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

925 lines
31 KiB
C++

//-----------------------------------------------------------------------------
// V12 Engine
//
// Copyright (c) 2001 GarageGames.Com
// Portions Copyright (c) 2001 by Sierra Online, Inc.
//-----------------------------------------------------------------------------
#include "dgl/dgl.h"
#include "scenegraph/sceneState.h"
#include "scenegraph/sceneGraph.h"
#include "console/consoleTypes.h"
#include "core/bitStream.h"
#include "game/particleEngine.h"
#include "game/explosion.h"
#include "game/shapeBase.h"
#include "ts/tsShapeInstance.h"
#include "game/projectile.h"
#include "audio/audio.h"
#include "sim/decalManager.h"
#include "game/splash.h"
#include "terrain/waterBlock.h"
#include "math/mathUtils.h"
IMPLEMENT_CO_DATABLOCK_V1(ProjectileData);
IMPLEMENT_CO_NETOBJECT_V1(Projectile);
const U32 Projectile::csmStaticCollisionMask = TerrainObjectType |
InteriorObjectType |
ForceFieldObjectType |
StaticObjectType;
const U32 Projectile::csmDynamicCollisionMask = PlayerObjectType |
VehicleObjectType |
StationObjectType |
GeneratorObjectType |
SensorObjectType |
DamagableItemObjectType |
TurretObjectType;
const U32 Projectile::csmDamageableMask = Projectile::csmDynamicCollisionMask;
U32 Projectile::smProjectileWarpTicks = 5;
//--------------------------------------------------------------------------
//--------------------------------------
//
ProjectileData::ProjectileData()
{
projectileShapeName = NULL;
emitterDelay = -1;
velInheritFactor = 1.0;
isBallistic = false;
directDamage = 0.0f;
hasDamageRadius = false;
indirectDamage = 0.0f;
damageRadius = 0.0f;
radiusDamageType = 0;
kickBackStrength = 0.0;
baseEmitter = NULL;
delayEmitter = NULL;
bubbleEmitter = NULL;
explosion = NULL;
underwaterExplosion = NULL;
splash = NULL;
sound = NULL;
wetFireSound = NULL;
fireSound = NULL;
baseEmitterId = 0;
delayEmitterId = 0;
bubbleEmitterId = 0;
explosionId = 0;
underwaterExplosionId = 0;
splashId = 0;
soundId = 0;
wetFireSoundId = 0;
fireSoundId = 0;
hasLight = false;
lightRadius = 1;
lightColor.set(1, 1, 1);
hasLightUnderwaterColor = false;
underWaterLightColor.set(1, 1, 1);
explodeOnWaterImpact = false;
depthTolerance = 5.0;
bubbleEmitTime = 0.5;
for (U32 i = 0; i < 6; i++) {
decalData[i] = NULL;
decalID[i] = 0;
}
numDecals = 0;
faceViewer = false;
scale.set( 1.0, 1.0, 1.0 );
}
ProjectileData::~ProjectileData()
{
//
}
//--------------------------------------------------------------------------
IMPLEMENT_GETDATATYPE(ProjectileData)
IMPLEMENT_SETDATATYPE(ProjectileData)
void ProjectileData::initPersistFields()
{
Parent::initPersistFields();
Con::registerType(TypeProjectileDataPtr, sizeof(ProjectileData*),
REF_GETDATATYPE(ProjectileData),
REF_SETDATATYPE(ProjectileData));
addField("projectileShapeName", TypeString, Offset(projectileShapeName, ProjectileData));
addField("emitterDelay", TypeS32, Offset(emitterDelay, ProjectileData));
addField("velInheritFactor", TypeF32, Offset(velInheritFactor, ProjectileData));
addField("directDamage", TypeF32, Offset(directDamage, ProjectileData));
addField("hasDamageRadius", TypeBool, Offset(hasDamageRadius, ProjectileData));
addField("indirectDamage", TypeF32, Offset(indirectDamage, ProjectileData));
addField("damageRadius", TypeF32, Offset(damageRadius, ProjectileData));
addField("radiusDamageType", TypeS32, Offset(radiusDamageType, ProjectileData));
addField("kickBackStrength", TypeF32, Offset(kickBackStrength, ProjectileData));
addField("baseEmitter", TypeParticleEmitterDataPtr, Offset(baseEmitter, ProjectileData));
addField("delayEmitter", TypeParticleEmitterDataPtr, Offset(delayEmitter, ProjectileData));
addField("bubbleEmitter", TypeParticleEmitterDataPtr, Offset(bubbleEmitter, ProjectileData));
addField("explosion", TypeExplosionDataPtr, Offset(explosion, ProjectileData));
addField("underwaterExplosion", TypeExplosionDataPtr, Offset(underwaterExplosion, ProjectileData));
addField("splash", TypeSplashDataPtr, Offset(splash, ProjectileData));
addField("sound", TypeAudioProfilePtr, Offset(sound, ProjectileData));
addField("wetFireSound", TypeAudioProfilePtr, Offset(wetFireSound, ProjectileData));
addField("fireSound", TypeAudioProfilePtr, Offset(fireSound, ProjectileData));
addField("hasLight", TypeBool, Offset(hasLight, ProjectileData));
addField("lightRadius", TypeF32, Offset(lightRadius, ProjectileData));
addField("lightColor", TypeColorF, Offset(lightColor, ProjectileData));
addField("hasLightUnderwaterColor", TypeBool, Offset(hasLightUnderwaterColor, ProjectileData));
addField("underWaterLightColor", TypeColorF, Offset(underWaterLightColor, ProjectileData));
addField("explodeOnWaterImpact", TypeBool, Offset(explodeOnWaterImpact, ProjectileData));
addField("depthTolerance", TypeF32, Offset(depthTolerance, ProjectileData));
addField("decalData", TypeDecalDataPtr, Offset(decalData, ProjectileData), 6);
addField("bubbleEmitTime", TypeF32, Offset(bubbleEmitTime, ProjectileData));
addField("faceViewer", TypeBool, Offset(faceViewer, ProjectileData));
addField("scale", TypePoint3F, Offset(scale, ProjectileData));
}
//--------------------------------------------------------------------------
bool ProjectileData::onAdd()
{
if(!Parent::onAdd())
return false;
if (!baseEmitter && baseEmitterId != 0)
if (Sim::findObject(baseEmitterId, baseEmitter) == false)
Con::errorf(ConsoleLogEntry::General, "ProjectileData::onAdd: Invalid packet, bad datablockId(baseEmitter): %d", baseEmitterId);
if (!delayEmitter && delayEmitterId != 0)
if (Sim::findObject(delayEmitterId, delayEmitter) == false)
Con::errorf(ConsoleLogEntry::General, "ProjectileData::onAdd: Invalid packet, bad datablockId(delayEmitter): %d", delayEmitterId);
if (!bubbleEmitter && bubbleEmitterId != 0)
if (Sim::findObject(bubbleEmitterId, bubbleEmitter) == false)
Con::errorf(ConsoleLogEntry::General, "ProjectileData::onAdd: Invalid packet, bad datablockId(bubbleEmitter): %d", bubbleEmitterId);
if (!explosion && explosionId != 0)
if (Sim::findObject(explosionId, explosion) == false)
Con::errorf(ConsoleLogEntry::General, "ProjectileData::onAdd: Invalid packet, bad datablockId(explosion): %d", explosionId);
if (!underwaterExplosion && underwaterExplosionId != 0)
if (Sim::findObject(underwaterExplosionId, underwaterExplosion) == false)
Con::errorf(ConsoleLogEntry::General, "ProjectileData::onAdd: Invalid packet, bad datablockId(underwaterExplosion): %d", underwaterExplosionId);
if (!splash && splashId != 0)
if (Sim::findObject(splashId, splash) == false)
Con::errorf(ConsoleLogEntry::General, "ProjectileData::onAdd: Invalid packet, bad datablockId(splash): %d", splashId);
if (!sound && soundId != 0)
if (Sim::findObject(soundId, sound) == false)
Con::errorf(ConsoleLogEntry::General, "ProjectileData::onAdd: Invalid packet, bad datablockId(sound): %d", soundId);
if (!wetFireSound && wetFireSoundId != 0)
if (Sim::findObject(wetFireSoundId, wetFireSound) == false)
Con::errorf(ConsoleLogEntry::General, "ProjectileData::onAdd: Invalid packet, bad datablockId(wetFireSound): %d", wetFireSoundId);
if (!fireSound && fireSoundId != 0)
if (Sim::findObject(fireSoundId, fireSound) == false)
Con::errorf(ConsoleLogEntry::General, "ProjectileData::onAdd: Invalid packet, bad datablockId(fireSound): %d", fireSoundId);
if (emitterDelay < 0)
emitterDelay = -1;
if (directDamage < 0.0) {
Con::warnf(ConsoleLogEntry::General, "ProjectileData(%s)::onAdd: directDamage < 0.0", getName());
directDamage = 0.0;
}
if (damageRadius < 0.0) {
Con::warnf(ConsoleLogEntry::General, "ProjectileData(%s)::onAdd: damageRadius < 0.0", getName());
damageRadius = 0.0;
}
if (indirectDamage < 0.0) {
Con::warnf(ConsoleLogEntry::General, "ProjectileData(%s)::onAdd: indirectDamage < 0.0", getName());
indirectDamage = 0.0;
}
if ((damageRadius == 0.0 || indirectDamage == 0.0) && hasDamageRadius == true) {
Con::warnf(ConsoleLogEntry::General, "ProjectileData(%s)::onAdd: (damageRadius || indirectDamage) == 0 && hasDamageRadius == true", getName());
hasDamageRadius = false;
}
if (velInheritFactor < 0.0f || velInheritFactor > 1.0f) {
Con::warnf(ConsoleLogEntry::General, "ProjectileData(%s)::onAdd: velInheritFactor out of range", getName());
velInheritFactor = velInheritFactor < 0.0f ? 0.0f : 1.0f;
}
if (kickBackStrength < 0.0f) {
Con::warnf(ConsoleLogEntry::General, "ProjectileData(%s)::onAdd: kickBackStrength must be >= 0", getName());
kickBackStrength = 0.0f;
}
if (hasLight == true) {
if (lightRadius < 1 || lightRadius > 20) {
Con::warnf(ConsoleLogEntry::General, "ProjectileData(%s)::onAdd: lightRadius must be in range [1, 20]", getName());
lightRadius = lightRadius < 1 ? 1.0 : 20.0;
}
}
lightColor.clamp();
underWaterLightColor.clamp();
return true;
}
bool ProjectileData::preload(bool server, char errorBuffer[256])
{
if (Parent::preload(server, errorBuffer) == false)
return false;
if (projectileShapeName && projectileShapeName[0] != '\0') {
char fullName[256];
dSprintf(fullName, sizeof(fullName), "shapes/%s", projectileShapeName);
projectileShape = ResourceManager->load(fullName);
if (bool(projectileShape) == false) {
dSprintf(errorBuffer, sizeof(errorBuffer), "ProjectileData::load: Couldn't load shape \"%s\"", projectileShapeName);
return false;
}
ambientSeq = projectileShape->findSequence("ambient");
}
if (bool(projectileShape)) {
TSShapeInstance* pDummy = new TSShapeInstance(projectileShape, !server);
delete pDummy;
}
for (U32 i = 0; i < 6; i++) {
if( !decalData && decalID != 0 )
{
if( !Sim::findObject( decalID[i], decalData[i] ) )
{
Con::errorf( ConsoleLogEntry::General, "ProjectileData::preload Invalid packet, bad datablockId(decalData): 0x%x", decalID[i]);
}
}
if (!server && decalData[i])
numDecals++;
}
if (!server) {
// DMM: order the decals so the non-null ones are in the front...
}
return true;
}
bool ProjectileData::calculateAim(const Point3F&,
const Point3F&,
const Point3F&,
const Point3F&,
Point3F*, F32*,
Point3F*, F32*)
{
//AssertFatal(false, "Projectile::calculateAim: this function is (essentially) pure virtual. Should never be called");
Con::warnf(ConsoleLogEntry::General, "Projectile::calculateAim: this function is (essentially) pure virtual. Should never be called");
return false;
}
//--------------------------------------------------------------------------
void ProjectileData::packData(BitStream* stream)
{
Parent::packData(stream);
stream->writeString(projectileShapeName);
stream->write(emitterDelay);
stream->write(bubbleEmitTime);
stream->writeFlag(faceViewer);
if(stream->writeFlag(scale.x != 1 || scale.y != 1 || scale.z != 1))
{
stream->write(scale.x);
stream->write(scale.y);
stream->write(scale.z);
}
if (stream->writeFlag(baseEmitter != NULL))
stream->writeRangedU32(baseEmitter->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
if (stream->writeFlag(delayEmitter != NULL))
stream->writeRangedU32(delayEmitter->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
if (stream->writeFlag(bubbleEmitter != NULL))
stream->writeRangedU32(bubbleEmitter->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
if (stream->writeFlag(explosion != NULL))
stream->writeRangedU32(explosion->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
if (stream->writeFlag(underwaterExplosion != NULL))
stream->writeRangedU32(underwaterExplosion->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
if (stream->writeFlag(splash != NULL))
stream->writeRangedU32(splash->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
if (stream->writeFlag(sound != NULL))
stream->writeRangedU32(sound->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
if (stream->writeFlag(wetFireSound != NULL))
stream->writeRangedU32(wetFireSound->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
if (stream->writeFlag(fireSound != NULL))
stream->writeRangedU32(fireSound->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
for (U32 i = 0; i < 6; i++) {
if (stream->writeFlag(decalData[i] != NULL))
stream->writeRangedU32(decalData[i]->getId(), DataBlockObjectIdFirst,
DataBlockObjectIdLast);
}
if(stream->writeFlag(hasLight))
{
stream->writeFloat(lightRadius/20.0, 8);
stream->writeFloat(lightColor.red,7);
stream->writeFloat(lightColor.green,7);
stream->writeFloat(lightColor.blue,7);
}
if(stream->writeFlag(hasLightUnderwaterColor))
{
stream->writeFloat(underWaterLightColor.red,7);
stream->writeFloat(underWaterLightColor.green,7);
stream->writeFloat(underWaterLightColor.blue,7);
}
stream->write(explodeOnWaterImpact);
stream->write(depthTolerance);
}
void ProjectileData::unpackData(BitStream* stream)
{
Parent::unpackData(stream);
projectileShapeName = stream->readSTString();
stream->read(&emitterDelay);
stream->read(&bubbleEmitTime);
faceViewer = stream->readFlag();
if(stream->readFlag())
{
stream->read(&scale.x);
stream->read(&scale.y);
stream->read(&scale.z);
}
else
scale.set(1,1,1);
if (stream->readFlag())
baseEmitterId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
baseEmitterId = 0;
if (stream->readFlag())
delayEmitterId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
delayEmitterId = 0;
if (stream->readFlag())
bubbleEmitterId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
bubbleEmitterId = 0;
if (stream->readFlag())
explosionId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
explosionId = 0;
if (stream->readFlag())
underwaterExplosionId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
underwaterExplosionId = 0;
if (stream->readFlag())
splashId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
splashId = 0;
if (stream->readFlag())
soundId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
soundId = 0;
if (stream->readFlag())
wetFireSoundId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
wetFireSoundId = 0;
if (stream->readFlag())
fireSoundId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
else
fireSoundId = 0;
for (U32 i = 0; i < 6; i++) {
if (stream->readFlag()) {
decalID[i] = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
}
}
hasLight = stream->readFlag();
if(hasLight)
{
lightRadius = stream->readFloat(8) * 20;
lightColor.red = stream->readFloat(7);
lightColor.green = stream->readFloat(7);
lightColor.blue = stream->readFloat(7);
}
hasLightUnderwaterColor = stream->readFlag();
if(hasLightUnderwaterColor)
{
underWaterLightColor.red = stream->readFloat(7);
underWaterLightColor.green = stream->readFloat(7);
underWaterLightColor.blue = stream->readFloat(7);
}
stream->read(&explodeOnWaterImpact);
stream->read(&depthTolerance);
}
//--------------------------------------------------------------------------
//--------------------------------------
//
Projectile::Projectile()
{
// Todo: ScopeAlways?
mNetFlags.set(Ghostable);
mTypeMask |= ProjectileObjectType;
mInitialPosition.set(0, 0, 0);
mInitialDirection.set(0, 0, 1);
mSourceObjectId = -1;
mVehicleObjectId = -1;
mSourceObjectSlot = -1;
mCurrTick = 0;
mProjectileShape = NULL;
mAmbientThread = NULL;
mHidden = false;
mFadeValue = 1.0;
mPlayedSplash = false;
mBaseEmitter = NULL;
mDelayEmitter = NULL;
mBubbleEmitter = NULL;
mBubbleEmitterTime = U32( -1 );
mUseUnderwaterLight = false;
mProjectileSound = NULL_AUDIOHANDLE;
}
Projectile::~Projectile()
{
delete mProjectileShape;
mProjectileShape = NULL;
}
//--------------------------------------------------------------------------
void Projectile::initPersistFields()
{
Parent::initPersistFields();
addField("initialPosition", TypePoint3F, Offset(mInitialPosition, Projectile));
addField("initialDirection", TypePoint3F, Offset(mInitialDirection, Projectile));
addField("sourceObject", TypeS32, Offset(mSourceObjectId, Projectile));
addField("vehicleObject", TypeS32, Offset(mVehicleObjectId, Projectile));
addField("sourceSlot", TypeS32, Offset(mSourceObjectSlot, Projectile));
}
void Projectile::consoleInit()
{
//
}
bool Projectile::calculateImpact(float,
Point3F& pointOfImpact,
float& impactTime)
{
Con::warnf(ConsoleLogEntry::General, "Projectile::calculateImpact: this function is (essentially) pure virtual. Should never be called");
impactTime = 0;
pointOfImpact.set(0, 0, 0);
return false;
}
//--------------------------------------------------------------------------
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 || mVehicleObject == 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;
}
mVehicleObject = (Sim::findObject(mVehicleObjectId, ptr)) ? ptr : NULL;
// If we're on the server, we need to inherit some of our parent's velocity
//
Point3F sourceObjectVel(0, 0, 0);
if (bool(mSourceObject))
{
Point3F velocity = mSourceObject->getVelocity();
if(bool(mVehicleObject))
velocity = mVehicleObject->getVelocity();
sourceObjectVel = velocity * mDataBlock->velInheritFactor;
}
F32 len = sourceObjectVel.len();
mExcessVel = U32(len + 0.5f);
if (mExcessVel != 0)
mExcessDir = sourceObjectVel / len;
else
mExcessDir.set(0, 0, 1);
mExcessDirDumb = BitStream::dumbDownNormal(mExcessDir, ExcessVelDirBits);
mCurrTick = 0;
} else {
if (bool(mDataBlock->projectileShape)) {
mProjectileShape = new TSShapeInstance(mDataBlock->projectileShape, isClientObject());
if (mDataBlock->ambientSeq != -1) {
mAmbientThread = mProjectileShape->addThread();
mProjectileShape->setTimeScale(mAmbientThread, 1);
mProjectileShape->setSequence(mAmbientThread, mDataBlock->ambientSeq, 0);
}
}
if (mDataBlock->baseEmitter != NULL) {
ParticleEmitter* pEmitter = new ParticleEmitter;
pEmitter->onNewDataBlock(mDataBlock->baseEmitter);
if (pEmitter->registerObject() == false) {
Con::warnf(ConsoleLogEntry::General, "Could not register base emitter for particle of class: %s", mDataBlock->getName());
delete pEmitter;
pEmitter = NULL;
}
mBaseEmitter = pEmitter;
}
if (mDataBlock->delayEmitter != NULL) {
ParticleEmitter* pEmitter = new ParticleEmitter;
pEmitter->onNewDataBlock(mDataBlock->delayEmitter);
if (pEmitter->registerObject() == false) {
Con::warnf(ConsoleLogEntry::General, "Could not register delay emitter for particle of class: %s", mDataBlock->getName());
delete pEmitter;
pEmitter = NULL;
}
mDelayEmitter = pEmitter;
}
if (mDataBlock->bubbleEmitter != NULL) {
ParticleEmitter* pEmitter = new ParticleEmitter;
pEmitter->onNewDataBlock(mDataBlock->bubbleEmitter);
if (pEmitter->registerObject() == false) {
Con::warnf(ConsoleLogEntry::General, "Could not register delay emitter for particle of class: %s", mDataBlock->getName());
delete pEmitter;
pEmitter = NULL;
}
mBubbleEmitter = pEmitter;
}
if (mDataBlock->hasLight == true)
Sim::getLightSet()->addObject(this);
mBubbleEmitterTime = mDataBlock->bubbleEmitTime;
}
mSourceIdTimeoutTicks = SourceIdTimeoutTicks;
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();
mLastPos = getPosition();
return true;
}
void Projectile::onRemove()
{
if (bool(mBaseEmitter)) {
mBaseEmitter->deleteWhenEmpty();
mBaseEmitter = NULL;
}
if (bool(mDelayEmitter)) {
mDelayEmitter->deleteWhenEmpty();
mDelayEmitter = NULL;
}
if (bool(mBubbleEmitter)) {
mBubbleEmitter->deleteWhenEmpty();
mBubbleEmitter = NULL;
}
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->hasLightUnderwaterColor && mUseUnderwaterLight)
{
mLight.mColor = mDataBlock->underWaterLightColor;
}
else
{
mLight.mColor = mDataBlock->lightColor;
}
lightManager->addLight(&mLight);
}
}
//----------------------------------------------------------------------------
void Projectile::processTick(const Move* move)
{
Parent::processTick(move);
mCurrTick++;
if (mSourceIdTimeoutTicks)
mSourceIdTimeoutTicks--;
}
void Projectile::advanceTime(F32 dt)
{
Parent::advanceTime(dt);
mLastPos = getRenderPosition();
if (mAmbientThread)
mProjectileShape->advanceTime(dt, mAmbientThread);
}
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();
}
if (bool(mBaseEmitter)) {
mBaseEmitter->emitParticles(from, to,
axis, vel,
ms);
}
if (bool(mDelayEmitter) && mCurrTick > (mDataBlock->emitterDelay / TickMs)) {
mDelayEmitter->emitParticles(from, to,
axis, vel,
ms);
}
}
void Projectile::updateSound(const Point3F& pos, const Point3F& vel, const bool active)
{
AssertFatal(isClientObject(), "Error, server projectiles play no sounds!");
if (mDataBlock->sound == NULL)
return;
MatrixF xForm(true);
xForm.setColumn(3, pos);
if (active == true && mProjectileSound == NULL_AUDIOHANDLE) {
mProjectileSound = alxPlay(mDataBlock->sound, &xForm, &vel);
return;
} else if (active == false && mProjectileSound != NULL_AUDIOHANDLE) {
alxStop(mProjectileSound);
mProjectileSound = NULL_AUDIOHANDLE;
}
if (mProjectileSound != NULL_AUDIOHANDLE) {
alxSourceMatrixF(mProjectileSound, &xForm);
// alxSourcePoint3F(mProjectileSound, AL_VELOCITY, &vel);
}
}
//--------------------------------------------------------------------------
void Projectile::onCollision(const Point3F& hitPosition,
const Point3F& hitNormal,
SceneObject* hitObject)
{
if (!isClientObject() && hitObject != NULL) {
char *posArg = Con::getArgBuffer(64);
char *normalArg = Con::getArgBuffer(64);
dSprintf(posArg, 64, "%f %f %f", hitPosition.x, hitPosition.y, hitPosition.z);
dSprintf(normalArg, 64, "%f %f %f", hitNormal.x, hitNormal.y, hitNormal.z);
Con::executef(mDataBlock, 6, "onCollision",
Con::getIntArg(getId()),
Con::getIntArg(hitObject->getId()),
Con::getFloatArg(mFadeValue),
posArg,
normalArg);
}
}
//--------------------------------------------------------------------------
void Projectile::prepModelView(SceneState* state)
{
Point3F targetVector;
if( mDataBlock->faceViewer )
{
targetVector = state->getCameraPosition() - getRenderPosition();
targetVector.normalize();
MatrixF explOrient = MathUtils::createOrientFromDir( targetVector );
explOrient.setPosition( getRenderPosition() );
dglMultMatrix( &explOrient );
}
else
{
dglMultMatrix( &getRenderTransform() );
}
}
//--------------------------------------------------------------------------
bool Projectile::prepRenderImage(SceneState* state, const U32 stateKey,
const U32 /*startZone*/, const bool /*modifyBaseState*/)
{
if (isLastState(state, stateKey))
return false;
setLastState(state, stateKey);
if (mHidden == true || mFadeValue <= (1.0/255.0))
return false;
// This should be sufficient for most objects that don't manage zones, and
// don't need to return a specialized RenderImage...
if (state->isObjectRendered(this)) {
SceneRenderImage* image = new SceneRenderImage;
image->obj = this;
image->isTranslucent = true;
image->sortType = SceneRenderImage::Point;
state->setImageRefPoint(this, image);
// For projectiles, the datablock pointer is a good enough sort key, since they aren't
// skinned at all...
image->textureSortKey = U32(mDataBlock);
state->insertRenderImage(image);
}
return false;
}
void Projectile::renderObject(SceneState* state, SceneRenderImage*)
{
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->setupObjectProjection(this);
glMatrixMode(GL_MODELVIEW);
glPushMatrix();
prepModelView( state );
glScalef( mDataBlock->scale.x, mDataBlock->scale.y, mDataBlock->scale.z );
AssertFatal(mProjectileShape != NULL,
"Projectile::renderObject: Error, projectile shape should always be present in renderObject");
mProjectileShape->selectCurrentDetail();
mProjectileShape->animate();
Point3F cameraOffset;
mObjToWorld.getColumn(3,&cameraOffset);
cameraOffset -= state->getCameraPosition();
F32 fogAmount = state->getHazeAndFog(cameraOffset.len(),cameraOffset.z);
if (mFadeValue == 1.0) {
mProjectileShape->setupFog(fogAmount, state->getFogColor());
} else {
mProjectileShape->setupFog(0.0, state->getFogColor());
mProjectileShape->setAlphaAlways(mFadeValue * (1.0 - fogAmount));
}
mProjectileShape->render();
glDisable(GL_BLEND);
glDisable(GL_TEXTURE_2D);
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
glMatrixMode(GL_MODELVIEW);
glPopMatrix();
glMatrixMode(GL_PROJECTION);
glPopMatrix();
glMatrixMode(GL_MODELVIEW);
dglSetViewport(viewport);
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
}
//--------------------------------------------------------------------------
bool Projectile::pointInWater( Point3F &point, F32 *waterHeight )
{
SimpleQueryList sql;
if (isServerObject())
gServerSceneGraph->getWaterObjectList(sql);
else
gClientSceneGraph->getWaterObjectList(sql);
for (U32 i = 0; i < sql.mList.size(); i++)
{
WaterBlock* pBlock = dynamic_cast<WaterBlock*>(sql.mList[i]);
if (pBlock && pBlock->isPointSubmergedSimple( point ))
{
if(pBlock->isLava(pBlock->getLiquidType()))
return false;
if( waterHeight )
{
*waterHeight = pBlock->getSurfaceHeight();
}
return true;
}
}
return false;
}
//--------------------------------------------------------------------------
void Projectile::createSplash( Point3F &pos )
{
if( mDataBlock->splash && !mPlayedSplash )
{
MatrixF trans = getTransform();
trans.setPosition( pos );
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;
mPlayedSplash = true;
}
}