716 lines
21 KiB
C++
Executable File
716 lines
21 KiB
C++
Executable File
//-----------------------------------------------------------------------------
|
|
// Torque Game Engine
|
|
// Copyright (C) GarageGames.com, Inc.
|
|
//-----------------------------------------------------------------------------
|
|
|
|
#include "dgl/dgl.h"
|
|
#include "console/consoleTypes.h"
|
|
#include "math/mathIO.h"
|
|
#include "math/mRandom.h"
|
|
#include "sceneGraph/sceneState.h"
|
|
#include "audio/audioDataBlock.h"
|
|
#include "game/fx/weatherLightning.h"
|
|
|
|
IMPLEMENT_CO_CLIENTEVENT_V1(WeatherLightningStrikeEvent);
|
|
IMPLEMENT_CO_DATABLOCK_V1(WeatherLightningData);
|
|
IMPLEMENT_CO_NETOBJECT_V1(WeatherLightning);
|
|
|
|
MRandomLCG sgRandomGen;
|
|
|
|
S32 QSORT_CALLBACK cmpWLSounds(const void* p1, const void* p2)
|
|
{
|
|
U32 i1 = *((const S32*)p1);
|
|
U32 i2 = *((const S32*)p2);
|
|
|
|
if (i1 < i2) {
|
|
return 1;
|
|
} else if (i1 > i2) {
|
|
return -1;
|
|
} else {
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
S32 QSORT_CALLBACK cmpWLTextures(const void* t1, const void* t2)
|
|
{
|
|
StringTableEntry ta = *(StringTableEntry*)t1;
|
|
StringTableEntry tb = *(StringTableEntry*)t2;
|
|
|
|
if(ta && ta[0] != '\0')
|
|
{
|
|
if(tb && tb[0] != '\0')
|
|
return dStricmp(ta, tb);
|
|
else
|
|
return -1;
|
|
}
|
|
else
|
|
{
|
|
if(tb && tb[0] != '\0')
|
|
return 1;
|
|
else
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
WeatherLightningStrikeEvent::WeatherLightningStrikeEvent()
|
|
{
|
|
mLightning = NULL;
|
|
}
|
|
|
|
WeatherLightningStrikeEvent::~WeatherLightningStrikeEvent()
|
|
{
|
|
|
|
}
|
|
|
|
void WeatherLightningStrikeEvent::pack(NetConnection* con, BitStream* stream)
|
|
{
|
|
if(!mLightning)
|
|
{
|
|
stream->writeFlag(false);
|
|
return;
|
|
}
|
|
|
|
S32 ghostIndex = con->getGhostIndex(mLightning);
|
|
if(ghostIndex == -1)
|
|
{
|
|
stream->writeFlag(false);
|
|
return;
|
|
}
|
|
|
|
stream->writeFlag(true);
|
|
stream->writeRangedU32(U32(ghostIndex), 0, NetConnection::MaxGhostCount);
|
|
stream->writeFloat(mStart.x, PositionalBits);
|
|
stream->writeFloat(mStart.y, PositionalBits);
|
|
}
|
|
|
|
void WeatherLightningStrikeEvent::unpack(NetConnection* con, BitStream* stream)
|
|
{
|
|
if(!stream->readFlag())
|
|
return;
|
|
S32 ghostIndex = stream->readRangedU32(0, NetConnection::MaxGhostCount);
|
|
mLightning = NULL;
|
|
NetObject* pObject = con->resolveGhost(ghostIndex);
|
|
if(pObject)
|
|
mLightning = dynamic_cast<WeatherLightning*>(pObject);
|
|
|
|
mStart.x = stream->readFloat(PositionalBits);
|
|
mStart.y = stream->readFloat(PositionalBits);
|
|
}
|
|
|
|
void WeatherLightningStrikeEvent::process(NetConnection*)
|
|
{
|
|
if (mLightning)
|
|
mLightning->processEvent(this);
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
WeatherLightningData::WeatherLightningData()
|
|
{
|
|
dMemset(strikeTextureNames, 0, sizeof(strikeTextureNames));
|
|
dMemset(flashTextureNames, 0, sizeof(flashTextureNames));
|
|
dMemset(fuzzyTextureNames, 0, sizeof(fuzzyTextureNames));
|
|
|
|
dMemset(strikeTextures, 0, sizeof(strikeTextures));
|
|
dMemset(flashTextures, 0, sizeof(flashTextures));
|
|
dMemset(fuzzyTextures, 0, sizeof(fuzzyTextures));
|
|
|
|
strikeSoundId = -1;
|
|
strikeSound = NULL_AUDIOHANDLE;
|
|
for(U32 i = 0; i < MaxSounds; i++)
|
|
{
|
|
thunderSoundIds[i] = -1;
|
|
thunderSounds[i] = NULL_AUDIOHANDLE;
|
|
}
|
|
}
|
|
|
|
WeatherLightningData::~WeatherLightningData()
|
|
{
|
|
//
|
|
};
|
|
|
|
void WeatherLightningData::initPersistFields()
|
|
{
|
|
Parent::initPersistFields();
|
|
|
|
addField("strikeTextures", TypeFilename, Offset(strikeTextureNames, WeatherLightningData), MaxStrikeTextures);
|
|
addField("flashTextures", TypeFilename, Offset(flashTextureNames, WeatherLightningData), MaxFlashTextures);
|
|
addField("fuzzyTextures", TypeFilename, Offset(fuzzyTextureNames, WeatherLightningData), MaxFuzzyTextures);
|
|
|
|
addField("strikeSound", TypeAudioProfilePtr, Offset(strikeSound, WeatherLightningData));
|
|
addField("thunderSounds", TypeAudioProfilePtr, Offset(thunderSounds, WeatherLightningData), MaxSounds);
|
|
}
|
|
|
|
bool WeatherLightningData::onAdd()
|
|
{
|
|
if(!Parent::onAdd())
|
|
return false;
|
|
|
|
if(!strikeSound && strikeSoundId != -1)
|
|
{
|
|
if(Sim::findObject(strikeSoundId, strikeSound) == false)
|
|
Con::errorf(ConsoleLogEntry::General, "WeatherLightningData::onAdd: Invalid packet, bad datablockId(sound: %d", strikeSound);
|
|
}
|
|
|
|
for(U32 i = 0; i < MaxSounds; i++)
|
|
{
|
|
if(!thunderSounds[i] && thunderSoundIds[i] != -1)
|
|
{
|
|
if(Sim::findObject(thunderSoundIds[i], thunderSounds[i]) == false)
|
|
Con::errorf(ConsoleLogEntry::General, "WeahterLightningData::onAdd: Invalid packet, bad datablockId(sound: %d", thunderSounds[i]);
|
|
}
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
bool WeatherLightningData::preload(bool server, char errorBuffer[256])
|
|
{
|
|
if(Parent::preload(server, errorBuffer) == false)
|
|
return false;
|
|
|
|
dQsort(strikeTextureNames, MaxStrikeTextures, sizeof(StringTableEntry), cmpWLTextures);
|
|
dQsort(flashTextureNames, MaxFlashTextures, sizeof(StringTableEntry), cmpWLTextures);
|
|
dQsort(fuzzyTextureNames, MaxFuzzyTextures, sizeof(StringTableEntry), cmpWLTextures);
|
|
|
|
if(!server)
|
|
{
|
|
for(numStrikes = 0; numStrikes < MaxStrikeTextures; numStrikes++)
|
|
{
|
|
if(strikeTextureNames[numStrikes] && strikeTextureNames[numStrikes][0] != '\0')
|
|
strikeTextures[numStrikes] = TextureHandle(strikeTextureNames[numStrikes], MeshTexture);
|
|
else
|
|
break;
|
|
}
|
|
for(numFlashes = 0; numFlashes < MaxFlashTextures && flashTextureNames[numFlashes] != NULL; numFlashes++)
|
|
{
|
|
if(flashTextureNames[numFlashes] && flashTextureNames[numFlashes][0] != '\0')
|
|
flashTextures[numFlashes] = TextureHandle(flashTextureNames[numFlashes], MeshTexture);
|
|
else
|
|
break;
|
|
}
|
|
for(numFuzzes = 0; numFuzzes < MaxFuzzyTextures && fuzzyTextureNames[numFuzzes] != NULL; numFuzzes++)
|
|
{
|
|
if(fuzzyTextureNames[numFuzzes] && fuzzyTextureNames[numFuzzes][0] != '\0')
|
|
fuzzyTextures[numFuzzes] = TextureHandle(fuzzyTextureNames[numFuzzes], MeshTexture);
|
|
else
|
|
break;
|
|
}
|
|
}
|
|
|
|
dQsort(thunderSounds, MaxSounds, sizeof(AudioProfile*), cmpWLSounds);
|
|
for(numSounds = 0; numSounds < MaxSounds && thunderSounds[numSounds] != NULL_AUDIOHANDLE; numSounds++) {
|
|
//
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WeatherLightningData::packData(BitStream* stream)
|
|
{
|
|
Parent::packData(stream);
|
|
|
|
U32 i;
|
|
for (i = 0; i < MaxStrikeTextures; i++)
|
|
stream->writeString(strikeTextureNames[i]);
|
|
for(i = 0; i < MaxFlashTextures; i++)
|
|
stream->writeString(flashTextureNames[i]);
|
|
for(i = 0; i < MaxFuzzyTextures; i++)
|
|
stream->writeString(fuzzyTextureNames[i]);
|
|
|
|
if(stream->writeFlag(strikeSound != NULL_AUDIOHANDLE))
|
|
stream->writeRangedU32(strikeSound->getId(), DataBlockObjectIdFirst,
|
|
DataBlockObjectIdLast);
|
|
|
|
for(i = 0; i < MaxSounds; i++)
|
|
{
|
|
if(stream->writeFlag(thunderSounds[i] != NULL_AUDIOHANDLE))
|
|
stream->writeRangedU32(thunderSounds[i]->getId(), DataBlockObjectIdFirst,
|
|
DataBlockObjectIdLast);
|
|
}
|
|
}
|
|
|
|
void WeatherLightningData::unpackData(BitStream* stream)
|
|
{
|
|
Parent::unpackData(stream);
|
|
|
|
U32 i;
|
|
for(i = 0; i < MaxStrikeTextures; i++)
|
|
strikeTextureNames[i] = stream->readSTString();
|
|
for(i = 0; i < MaxFlashTextures; i++)
|
|
flashTextureNames[i] = stream->readSTString();
|
|
for(i = 0; i < MaxFuzzyTextures; i++)
|
|
fuzzyTextureNames[i] = stream->readSTString();
|
|
|
|
if(stream->readFlag())
|
|
strikeSoundId = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
|
|
else
|
|
strikeSoundId = -1;
|
|
|
|
for(i = 0; i < MaxSounds; i++)
|
|
{
|
|
if(stream->readFlag())
|
|
thunderSoundIds[i] = stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast);
|
|
else
|
|
thunderSoundIds[i] = -1;
|
|
}
|
|
}
|
|
|
|
//--------------------------------------------------------------------------
|
|
|
|
WeatherLightning::WeatherLightning()
|
|
{
|
|
mNetFlags.set(Ghostable | ScopeAlways);
|
|
mTypeMask |= StaticObjectType|EnvironmentObjectType;
|
|
|
|
lastThink = 0;
|
|
strikesPerMinute = 9;
|
|
boltDeathAge = 1.5;
|
|
}
|
|
|
|
WeatherLightning::~WeatherLightning()
|
|
{
|
|
//
|
|
}
|
|
|
|
void WeatherLightning::initPersistFields()
|
|
{
|
|
Parent::initPersistFields();
|
|
|
|
addNamedField(strikesPerMinute, TypeS32, WeatherLightning);
|
|
addNamedField(boltDeathAge, TypeF32, WeatherLightning);
|
|
}
|
|
|
|
bool WeatherLightning::onAdd()
|
|
{
|
|
if(!Parent::onAdd())
|
|
return false;
|
|
|
|
mObjBox.min.set( -0.5, -0.5, -0.5 );
|
|
mObjBox.max.set( 0.5, 0.5, 0.5 );
|
|
resetWorldBox();
|
|
|
|
addToScene();
|
|
return true;
|
|
}
|
|
|
|
void WeatherLightning::onRemove()
|
|
{
|
|
while(mActiveBolts.size())
|
|
{
|
|
WeatherLightningBolt* bolt = mActiveBolts[0];
|
|
delete bolt;
|
|
mActiveBolts.erase_fast(U32(0));
|
|
}
|
|
while(mSoundEvents.size())
|
|
mSoundEvents.erase_fast(U32(0));
|
|
|
|
removeFromScene();
|
|
Parent::onRemove();
|
|
}
|
|
|
|
bool WeatherLightning::onNewDataBlock(GameBaseData* dptr)
|
|
{
|
|
mDataBlock = dynamic_cast<WeatherLightningData*>(dptr);
|
|
if(!mDataBlock || !Parent::onNewDataBlock(dptr))
|
|
return false;
|
|
|
|
scriptOnNewDataBlock();
|
|
return true;
|
|
}
|
|
|
|
bool WeatherLightning::prepRenderImage(SceneState* state, const U32 stateKey, const U32, const bool)
|
|
{
|
|
if(isLastState(state, stateKey))
|
|
return false;
|
|
setLastState(state, stateKey);
|
|
|
|
// 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::EndSort;
|
|
state->insertRenderImage(image);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void WeatherLightningBolt::render(const Point3F &camPos)
|
|
{
|
|
Point3F perpVec;
|
|
Point3F lightUp = startPoint - endPoint;
|
|
mCross(camPos - endPoint, lightUp, &perpVec);
|
|
perpVec.normalize();
|
|
|
|
Point3F frontVec;
|
|
mCross(perpVec, lightUp, &frontVec);
|
|
frontVec.normalize();
|
|
|
|
glEnable(GL_DEPTH_TEST);
|
|
glDepthMask(GL_FALSE);
|
|
glDepthFunc(GL_LEQUAL);
|
|
|
|
//
|
|
// strike texture
|
|
//
|
|
|
|
// setup alpha value
|
|
F32 strikeAlpha;
|
|
if(currentAge < (strikeTime / 3.0))
|
|
{
|
|
strikeAlpha = currentAge / (strikeTime / 3.0);
|
|
strikeAlpha = mPow(strikeAlpha, F32(1.0 / 3.0));
|
|
}
|
|
else if(currentAge < (2.0 * strikeTime / 3.0))
|
|
strikeAlpha = 1.0;
|
|
else
|
|
strikeAlpha = 1.0 - ((currentAge - (2.0 * strikeTime / 3.0)) / (strikeTime / 3.0));
|
|
glColor4f(1.0f, 1.0f, 1.0f, strikeAlpha);
|
|
|
|
// generate texture coords
|
|
Point3F points[4];
|
|
F32 width = ((startPoint.z - endPoint.z) * 0.125f);
|
|
points[0] = startPoint - perpVec * width;
|
|
points[1] = startPoint + perpVec * width;
|
|
points[2] = endPoint + perpVec * width;
|
|
points[3] = endPoint - perpVec * width;
|
|
|
|
// bind and draw texture
|
|
glBindTexture(GL_TEXTURE_2D, strikeTexture->getGLName());
|
|
glBegin(GL_TRIANGLE_FAN);
|
|
glTexCoord2f(0, 0); glVertex3fv(points[0]);
|
|
glTexCoord2f(0, 1); glVertex3fv(points[1]);
|
|
glTexCoord2f(1, 1); glVertex3fv(points[2]);
|
|
glTexCoord2f(1, 0); glVertex3fv(points[3]);
|
|
//glTexCoord2f(0, 0); glVertex3fv(points[0]);
|
|
//glTexCoord2f(1, 0); glVertex3fv(points[1]);
|
|
//glTexCoord2f(1, 1); glVertex3fv(points[2]);
|
|
//glTexCoord2f(0, 1); glVertex3fv(points[3]);
|
|
glEnd();
|
|
|
|
//
|
|
// fuzzy texture
|
|
//
|
|
|
|
// setup alpha value
|
|
F32 constAlpha;
|
|
if(currentAge < strikeTime / 2.0)
|
|
constAlpha = currentAge / (strikeTime / 2.0);
|
|
else if(currentAge < (2.0 * strikeTime / 3.0))
|
|
constAlpha = 1.0 - ((currentAge - (strikeTime / 2.0)) / (strikeTime / 6.0));
|
|
else
|
|
constAlpha = 0.0;
|
|
glColor4f(1.0, 1.0, 1.0, constAlpha);
|
|
|
|
// generate texture coords
|
|
width *= 4;
|
|
points[0] = startPoint - perpVec * width;
|
|
points[1] = startPoint + perpVec * width;
|
|
points[2] = endPoint + perpVec * width;
|
|
points[3] = endPoint - perpVec * width;
|
|
|
|
if(constAlpha != 0.0)
|
|
{
|
|
// bind and draw texture
|
|
glBindTexture(GL_TEXTURE_2D, fuzzyTexture->getGLName());
|
|
glBegin(GL_TRIANGLE_FAN);
|
|
glTexCoord2f(0, 0); glVertex3fv(points[0]);
|
|
glTexCoord2f(0, 1); glVertex3fv(points[1]);
|
|
glTexCoord2f(1, 1); glVertex3fv(points[2]);
|
|
glTexCoord2f(1, 0); glVertex3fv(points[3]);
|
|
//glTexCoord2f(0, 0); glVertex3fv(points[0]);
|
|
//glTexCoord2f(1, 0); glVertex3fv(points[1]);
|
|
//glTexCoord2f(1, 1); glVertex3fv(points[2]);
|
|
//glTexCoord2f(0, 1); glVertex3fv(points[3]);
|
|
glEnd();
|
|
|
|
glDepthMask(GL_TRUE);
|
|
}
|
|
|
|
//
|
|
// flash texture
|
|
//
|
|
|
|
// setup alpha value
|
|
glColor4f(1.0f, 1.0f, 1.0f, strikeAlpha);
|
|
|
|
// generate texture coords
|
|
points[0] = startPoint - perpVec * width + frontVec * width;
|
|
points[1] = startPoint - perpVec * width - frontVec * width;
|
|
points[2] = startPoint + perpVec * width - frontVec * width;
|
|
points[3] = startPoint + perpVec * width + frontVec * width;
|
|
|
|
// bind and draw texture
|
|
glBindTexture(GL_TEXTURE_2D, flashTexture->getGLName());
|
|
glBegin(GL_TRIANGLE_FAN);
|
|
glTexCoord2f(0, 0); glVertex3fv(points[0]);
|
|
glTexCoord2f(0, 1); glVertex3fv(points[1]);
|
|
glTexCoord2f(1, 1); glVertex3fv(points[2]);
|
|
glTexCoord2f(1, 0); glVertex3fv(points[3]);
|
|
//glTexCoord2f(0, 0); glVertex3fv(points[0]);
|
|
//glTexCoord2f(1, 0); glVertex3fv(points[1]);
|
|
//glTexCoord2f(1, 1); glVertex3fv(points[2]);
|
|
//glTexCoord2f(0, 1); glVertex3fv(points[3]);
|
|
glEnd();
|
|
|
|
//
|
|
// finished
|
|
//
|
|
glDepthMask(GL_TRUE);
|
|
}
|
|
|
|
void WeatherLightning::renderObject(SceneState* state, SceneRenderImage*)
|
|
{
|
|
if(mActiveBolts.size())
|
|
{
|
|
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry");
|
|
|
|
RectI viewport;
|
|
F64 farPlane;
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPushMatrix();
|
|
|
|
dglGetViewport(&viewport);
|
|
farPlane = state->getFarPlane();
|
|
const Point3F &camPos = state->getCameraPosition();
|
|
|
|
// adjust far clip plane
|
|
F64 distance = (getPosition() - camPos).lenSquared();
|
|
state->setFarPlane(getMax(farPlane, distance));
|
|
|
|
state->setupObjectProjection(this);
|
|
|
|
glDisable(GL_CULL_FACE);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glEnable(GL_BLEND);
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE);
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
|
|
|
|
for(U32 i = 0; i < mActiveBolts.size(); i++)
|
|
mActiveBolts[i]->render(camPos);
|
|
|
|
glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE);
|
|
glDisable(GL_BLEND);
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glPopMatrix();
|
|
glMatrixMode(GL_MODELVIEW);
|
|
|
|
state->setFarPlane(farPlane);
|
|
dglSetViewport(viewport);
|
|
|
|
AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit");
|
|
}
|
|
}
|
|
|
|
void WeatherLightning::processTick(const Move *move)
|
|
{
|
|
Parent::processTick(move);
|
|
|
|
if (isServerObject())
|
|
{
|
|
S32 msBetweenStrikes = (S32)(60.0 / strikesPerMinute * 1000.0);
|
|
|
|
lastThink += TickMs;
|
|
if( lastThink > msBetweenStrikes )
|
|
{
|
|
strikeRandomPoint();
|
|
lastThink -= msBetweenStrikes;
|
|
}
|
|
}
|
|
}
|
|
|
|
void WeatherLightning::advanceTime(F32 dt)
|
|
{
|
|
Parent::advanceTime(dt);
|
|
|
|
U32 i;
|
|
|
|
// loop through and erase any dead bolts
|
|
for(i = 0; i < mActiveBolts.size();)
|
|
{
|
|
WeatherLightningBolt* bolt = mActiveBolts[i];
|
|
bolt->currentAge += dt;
|
|
if(bolt->currentAge > bolt->deathAge)
|
|
{
|
|
delete bolt;
|
|
mActiveBolts.erase_fast(i);
|
|
continue;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
|
|
// loop through and find any pending sound events
|
|
for(i = 0; i < mSoundEvents.size();)
|
|
{
|
|
SoundEvent *sEvent = &mSoundEvents[i];
|
|
sEvent->time -= dt;
|
|
if(sEvent->time <= 0.0)
|
|
{
|
|
// fire off the sound
|
|
if(sEvent->soundBlockId != -1)
|
|
alxPlay(mDataBlock->thunderSounds[sEvent->soundBlockId], &sEvent->position);
|
|
mSoundEvents.erase_fast(i);
|
|
continue;
|
|
}
|
|
|
|
i++;
|
|
}
|
|
}
|
|
|
|
void WeatherLightning::strikeRandomPoint()
|
|
{
|
|
// choose random strike point within object bounds
|
|
Point2F strikePoint;
|
|
strikePoint.x = sgRandomGen.randF( 0.0, 1.0 );
|
|
strikePoint.y = sgRandomGen.randF( 0.0, 1.0 );
|
|
|
|
SimGroup* pClientGroup = Sim::getClientGroup();
|
|
for (SimGroup::iterator itr = pClientGroup->begin(); itr != pClientGroup->end(); itr++)
|
|
{
|
|
NetConnection* nc = static_cast<NetConnection*>(*itr);
|
|
|
|
WeatherLightningStrikeEvent* wlEvent = new WeatherLightningStrikeEvent;
|
|
wlEvent->mLightning = this;
|
|
wlEvent->mStart = strikePoint;
|
|
|
|
nc->postNetEvent(wlEvent);
|
|
}
|
|
}
|
|
|
|
TextureHandle* WeatherLightning::getRandomStrike()
|
|
{
|
|
U32 strike = (U32)(mCeil(mDataBlock->numStrikes * sgRandomGen.randF()) - 1.0f);
|
|
return &mDataBlock->strikeTextures[strike];
|
|
}
|
|
|
|
TextureHandle* WeatherLightning::getRandomFlash()
|
|
{
|
|
U32 flash = (U32)(mCeil(mDataBlock->numFlashes * sgRandomGen.randF()) - 1.0f);
|
|
return &mDataBlock->flashTextures[flash];
|
|
}
|
|
|
|
TextureHandle* WeatherLightning::getRandomFuzzy()
|
|
{
|
|
U32 fuzzy = (U32)(mCeil(mDataBlock->numFuzzes * sgRandomGen.randF()) - 1.0f);
|
|
return &mDataBlock->fuzzyTextures[fuzzy];
|
|
}
|
|
|
|
S32 WeatherLightning::getRandomSound()
|
|
{
|
|
U32 sound = (U32)(mCeil(mDataBlock->numSounds * sgRandomGen.randF()) - 1.0f);
|
|
if(mDataBlock->thunderSounds[sound] != NULL_AUDIOHANDLE)
|
|
return sound;
|
|
|
|
return -1;
|
|
}
|
|
|
|
void WeatherLightning::processEvent(WeatherLightningStrikeEvent* wlEvent)
|
|
{
|
|
AssertFatal(wlEvent->mStart.x >= 0 && wlEvent->mStart.x <= 1.0, "Out of bounds coord!");
|
|
|
|
mActiveBolts.push_back(new WeatherLightningBolt);
|
|
WeatherLightningBolt* bolt = mActiveBolts.last();
|
|
|
|
Point3F strikePoint(0.0, 0.0, 0.0);
|
|
strikePoint.x = wlEvent->mStart.x;
|
|
strikePoint.y = wlEvent->mStart.y;
|
|
strikePoint *= mObjScale;
|
|
strikePoint += getPosition();
|
|
strikePoint += Point3F( -mObjScale.x * 0.5, -mObjScale.y * 0.5, 0.0 );
|
|
|
|
RayInfo rayInfo;
|
|
Point3F start = strikePoint;
|
|
start.z = mObjScale.z * 0.5 + getPosition().z;
|
|
strikePoint.z += -mObjScale.z * 0.5;
|
|
bool rayHit = gClientContainer.castRay(start, strikePoint, (STATIC_COLLISION_MASK | WaterObjectType), &rayInfo);
|
|
if(rayHit)
|
|
strikePoint.z = rayInfo.point.z;
|
|
|
|
F32 height = mObjScale.z * 0.5 + getPosition().z;
|
|
|
|
bolt->startPoint = Point3F(strikePoint.x, strikePoint.y, height);
|
|
bolt->endPoint = strikePoint;
|
|
|
|
bolt->currentAge = 0.0f;
|
|
bolt->deathAge = boltDeathAge;
|
|
bolt->strikeTime = 0.35;
|
|
|
|
bolt->strikeTexture = getRandomStrike();
|
|
bolt->flashTexture = getRandomFlash();
|
|
bolt->fuzzyTexture = getRandomFuzzy();
|
|
|
|
// setup a thunder sound event
|
|
Point3F listener;
|
|
alxGetListenerPoint3F(AL_POSITION, &listener);
|
|
|
|
mSoundEvents.increment();
|
|
SoundEvent& sEvent = mSoundEvents.last();
|
|
|
|
// find the length to the closest point on the bolt
|
|
Point3F dHat = bolt->startPoint - bolt->endPoint;
|
|
F32 boltLength = dHat.len();
|
|
dHat /= boltLength;
|
|
F32 distAlong = mDot((listener - bolt->endPoint), dHat);
|
|
|
|
Point3F contactPoint;
|
|
if(distAlong >= boltLength)
|
|
contactPoint = bolt->startPoint;
|
|
else if(distAlong <= 0.0)
|
|
contactPoint = bolt->endPoint;
|
|
else
|
|
contactPoint = bolt->endPoint + dHat * distAlong;
|
|
|
|
F32 delayDist = (listener - contactPoint).len();
|
|
U32 delayTime = U32((delayDist / 330.0f) * 100.0f);
|
|
|
|
MatrixF trans(true);
|
|
trans.setPosition(contactPoint);
|
|
|
|
sEvent.soundBlockId = getRandomSound();
|
|
sEvent.position = trans;
|
|
sEvent.time = delayTime;
|
|
|
|
// play strike sound
|
|
trans.setPosition(strikePoint);
|
|
if(mDataBlock->strikeSound)
|
|
alxPlay(mDataBlock->strikeSound, &trans);
|
|
}
|
|
|
|
U32 WeatherLightning::packUpdate(NetConnection *conn, U32 mask, BitStream *stream)
|
|
{
|
|
U32 retMask = Parent::packUpdate(conn, mask, stream);
|
|
|
|
// Only write data if this is the initial packet or we've been inspected.
|
|
if (stream->writeFlag(mask & (InitialUpdateMask | ExtendedInfoMask)))
|
|
{
|
|
// Initial update
|
|
mathWrite(*stream, getPosition());
|
|
mathWrite(*stream, mObjScale);
|
|
}
|
|
|
|
return retMask;
|
|
}
|
|
|
|
void WeatherLightning::unpackUpdate(NetConnection *conn, BitStream *stream)
|
|
{
|
|
Parent::unpackUpdate(conn, stream);
|
|
|
|
if (stream->readFlag())
|
|
{
|
|
// Initial update
|
|
Point3F pos;
|
|
mathRead(*stream, &pos);
|
|
setPosition( pos );
|
|
|
|
mathRead(*stream, &mObjScale);
|
|
}
|
|
}
|