//----------------------------------------------------------------------------- // V12 Engine // // Copyright (c) 2001 GarageGames.Com // Portions Copyright (c) 2001 by Sierra Online, Inc. //----------------------------------------------------------------------------- #include "game/projRepair.h" #include "core/bitStream.h" #include "console/consoleTypes.h" #include "game/shapeBase.h" #include "sim/netConnection.h" #include "dgl/dgl.h" #include "platformWIN32/platformGL.h" #include "scenegraph/sceneState.h" #include "math/mRandom.h" #include "dgl/splineUtil.h" #include "game/gameConnection.h" IMPLEMENT_CO_DATABLOCK_V1(RepairProjectileData); IMPLEMENT_CO_NETOBJECT_V1(RepairProjectile); //-------------------------------------------------------------------------- //-------------------------------------- // RepairProjectileData::RepairProjectileData() { beamRange = 10.0f; beamWidth = 0.15; numSegments = 20; texRepeat = 1.0/5.0; beamSpeed = 1.0; blurFreq = 10.0; blurLifetime = 1.0; cutoffAngle = 40.0; textureNames[0] = "special/redbump2"; textureNames[1] = "special/redflare"; } //-------------------------------------------------------------------------- RepairProjectileData::~RepairProjectileData() { } //-------------------------------------------------------------------------- bool RepairProjectileData::calculateAim(const Point3F& targetPos, const Point3F& /*targetVel*/, const Point3F& sourcePos, const Point3F& /*sourceVel*/, Point3F* outputVectorMin, F32* outputMinTime, Point3F* outputVectorMax, F32* outputMaxTime) { //make sure the source and target points are within range if ((targetPos - sourcePos).len() >= beamRange) return false; else { *outputVectorMin = targetPos - sourcePos; *outputMinTime = 0.001f; outputVectorMin->normalize(); *outputVectorMax = *outputVectorMin; *outputMaxTime = *outputMinTime; return true; } } //-------------------------------------------------------------------------- void RepairProjectileData::initPersistFields() { Parent::initPersistFields(); addField("beamRange", TypeF32, Offset(beamRange, RepairProjectileData)); addField("beamWidth", TypeF32, Offset(beamWidth, RepairProjectileData)); addField("beamSpeed", TypeF32, Offset(beamSpeed, RepairProjectileData)); addField("texRepeat", TypeF32, Offset(texRepeat, RepairProjectileData)); addField("textures", TypeString, Offset(textureNames, RepairProjectileData), RT_NUM_TEX); addField("numSegments", TypeS32, Offset(numSegments, RepairProjectileData)); addField("blurFreq", TypeF32, Offset(blurFreq, RepairProjectileData)); addField("blurLifetime", TypeF32, Offset(blurLifetime, RepairProjectileData)); addField("cutoffAngle", TypeF32, Offset(cutoffAngle, RepairProjectileData)); } //-------------------------------------------------------------------------- bool RepairProjectileData::onAdd() { if(!Parent::onAdd()) return false; if (beamRange < 2.0) { Con::warnf(ConsoleLogEntry::General, "RepairProjectileData(%s)::onAdd: beamRange must be >= 2", getName()); beamRange = 2.0; } return true; } //-------------------------------------------------------------------------- bool RepairProjectileData::preload( bool server, char errorBuffer[256] ) { if (Parent::preload(server, errorBuffer) == false) return false; if( server == false ) { for( int i=0; iwrite(beamRange); stream->write(beamWidth); stream->write(numSegments); stream->write(beamSpeed); stream->write(texRepeat); stream->write(blurFreq); stream->write(blurLifetime); stream->write(cutoffAngle); for( int i=0; iwriteString( textureNames[i] ); } } //-------------------------------------------------------------------------- void RepairProjectileData::unpackData(BitStream* stream) { Parent::unpackData(stream); stream->read(&beamRange); stream->read(&beamWidth); stream->read(&numSegments); stream->read(&beamSpeed); stream->read(&texRepeat); stream->read(&blurFreq); stream->read(&blurLifetime); stream->read(&cutoffAngle); for( int i=0; ireadSTString(); } } //-------------------------------------------------------------------------- //-------------------------------------- // RepairProjectile::RepairProjectile() { // Todo: ScopeAlways? mNetFlags.set(Ghostable); mElapsedTime = 0.0; mInitialHit = true; mTimeSinceLastBlur = 0.00; } //-------------------------------------------------------------------------- RepairProjectile::~RepairProjectile() { } //-------------------------------------------------------------------------- void RepairProjectile::initPersistFields() { Parent::initPersistFields(); addField("targetObject", TypeS32, Offset(mRepairingObjectId, RepairProjectile)); } //-------------------------------------------------------------------------- void RepairProjectile::consoleInit() { // } //-------------------------------------------------------------------------- bool RepairProjectile::onAdd() { if(!Parent::onAdd()) return false; if (isServerObject()) { AssertFatal(bool(mSourceObject), "Oh, crap."); ShapeBase* ptr; if (Sim::findObject(mRepairingObjectId, ptr)) { mRepairingObject = ptr; } else { if (mRepairingObjectId != -1) Con::errorf(ConsoleLogEntry::General, "Projectile::onAdd: mRepairingObjectId is invalid"); mRepairingObject = NULL; } mObjBox.min = mSourceObject->getPosition(); mObjBox.max = mSourceObject->getPosition() + Point3F( 1.0, 1.0, 1.0 ); resetWorldBox(); } else { if( bool( mSourceObject ) && bool( mRepairingObject ) ) { mSourceObject->getRenderMuzzlePoint(mSourceObjectSlot, &mCurrStartPoint); mSourceObject->getRenderMuzzleVector(mSourceObjectSlot, &mCurrStartDir); mDesiredEndPoint.set(0, 0, 0); RayInfo rayInfo; if( gClientContainer.castRay(mCurrStartPoint, mCurrStartPoint + (mCurrStartDir*mDataBlock->beamRange), mRepairingObject->getType(), &rayInfo) == true ) { if (rayInfo.object == (ShapeBase*)mRepairingObject) { mRepairingObject->getWorldBox().getCenter(&mDesiredEndPoint); mDesiredEndPoint = rayInfo.point - mDesiredEndPoint; } } mCurrEndPoint = mDesiredEndPoint; // resize object box of beam mObjBox.min = mCurrEndPoint; mObjBox.max = mCurrEndPoint; mObjBox.min.setMin(mCurrStartPoint); mObjBox.max.setMax(mCurrStartPoint); resetWorldBox(); } } addToScene(); return true; } //-------------------------------------------------------------------------- void RepairProjectile::onRemove() { removeFromScene(); // Parent::onRemove(); } //-------------------------------------------------------------------------- bool RepairProjectile::onNewDataBlock(GameBaseData* dptr) { mDataBlock = dynamic_cast(dptr); if (!mDataBlock || !Parent::onNewDataBlock(dptr)) return false; // Todo: Uncomment if this is a "leaf" class scriptOnNewDataBlock(); return true; } //-------------------------------------------------------------------------- U32 RepairProjectile::packUpdate(NetConnection* con, U32 mask, BitStream* stream) { U32 retMask = Parent::packUpdate(con, mask, stream); if (stream->writeFlag(mask & GameBase::InitialUpdateMask)) { // Have to write the source and the dest to the client, let's make sure it has a // ghost on the other side... S32 ghostIndexSource = con->getGhostIndex(mSourceObject); S32 ghostIndexDest = con->getGhostIndex(mRepairingObject); if (ghostIndexSource == -1 || ghostIndexDest == -1) { // One of the two isn't on the client. Crap. 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); } } else { } return retMask; } void RepairProjectile::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); mRepairingObjectId = stream->readRangedU32(0, NetConnection::MaxGhostCount); NetObject* pObject = con->resolveGhost(mSourceObjectId); if (pObject != NULL) mSourceObject = dynamic_cast(pObject); if (bool(mSourceObject) == false) Con::errorf(ConsoleLogEntry::General, "RepairProjectile::unpackUpdate: could not resolve source ghost properly on initial update"); pObject = con->resolveGhost(mRepairingObjectId); if (pObject != NULL) mRepairingObject = dynamic_cast(pObject); if (bool(mRepairingObject) == false) Con::errorf(ConsoleLogEntry::General, "RepairProjectile::unpackUpdate: could not resolve dest ghost properly on initial update"); } else { mSourceObjectId = 0; mSourceObjectSlot = 0; mRepairingObjectId = 0; mSourceObject = NULL; mRepairingObject = NULL; } } else { // Other } } //-------------------------------------------------------------------------- void RepairProjectile::advanceTime(F32 dt) { Parent::advanceTime(dt); if (!bool(mSourceObject) || !bool(mRepairingObject)) return; mElapsedTime += dt; mTimeSinceLastBlur += dt; // We start out with a good relative end point. // Our goal is to not invalidate that, but to update it if the object // points in a new direction... if( bool(mSourceObject) && bool(mRepairingObject) ) { mSourceObject->getRenderMuzzlePoint(mSourceObjectSlot, &mCurrStartPoint); mSourceObject->getRenderMuzzleVector(mSourceObjectSlot, &mCurrStartDir); RayInfo rayInfo; if (getContainer()->castRay(mCurrStartPoint, mCurrStartPoint + (mCurrStartDir*mDataBlock->beamRange), mRepairingObject->getType(), &rayInfo) == true) { if (rayInfo.object == (ShapeBase*)mRepairingObject) { mDesiredEndPoint = rayInfo.point; if( mInitialHit ) { mInitialHit = false; mCurrEndPoint = mDesiredEndPoint; } } } else { // No hit. Just use the old endpoint. } // resize object box of beam mObjBox.min = mCurrEndPoint; mObjBox.max = mCurrEndPoint; mObjBox.min.setMin(mCurrStartPoint); mObjBox.max.setMax(mCurrStartPoint); resetWorldBox(); } else { mCurrStartPoint.set(0, 0, 0); mCurrEndPoint.set(0, 0, 0); mObjBox.min.set(0, 0, 0); mObjBox.max.set(0, 0, 0); } MatrixF xform = getTransform(); setTransform(xform); Point3F endMoveVec = mDesiredEndPoint - mCurrEndPoint; endMoveVec *= 2.0 * dt; mCurrEndPoint += endMoveVec; updateBlur( dt ); } //-------------------------------------------------------------------------- // Update blur //-------------------------------------------------------------------------- void RepairProjectile::updateBlur( F32 dt ) { if( isServerObject() ) return; // update blur for( int j=0; j 1.0/mDataBlock->blurFreq ) { mTimeSinceLastBlur -= (1.0 / mDataBlock->blurFreq); Point3F cPoints[3]; cPoints[0] = mCurrStartPoint; cPoints[1] = mCurrStartPoint + mCurrStartDir * (mCurrStartPoint - mCurrEndPoint).len(); cPoints[2] = mCurrEndPoint; SplCtrlPts ctrlPoints; ctrlPoints.submitPoints( cPoints, 3 ); QuadPatch qPatch; qPatch.submitControlPoints( ctrlPoints ); for( int i=0; i mBlurList[i].lifetime ) { mBlurList[i].qPatch.submitControlPoints( ctrlPoints ); mBlurList[i].elapsedTime = 0.0; mBlurList[i].lifetime = mDataBlock->blurLifetime; mBlurList[i].startAlpha = 0.5;; break; } } } } //-------------------------------------------------------------------------- bool RepairProjectile::prepRenderImage(SceneState* state, const U32 stateKey, const U32 /*startZone*/, const bool /*modifyBaseState*/) { if (isLastState(state, stateKey)) return false; setLastState(state, stateKey); if (!bool(mSourceObject) || !bool(mRepairingObject)) 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::EndSort; state->setImageRefPoint(this, image); state->insertRenderImage(image); } return false; } //-------------------------------------------------------------------------- // Render the effect //-------------------------------------------------------------------------- void RepairProjectile::renderObject(SceneState* state, SceneRenderImage* /*sceneImage*/) { if( mCurrStartPoint.equal( mCurrEndPoint ) ) return; // make sure beam doesn't twist up in odd fashion if player turns rapidly VectorF actionDir = mCurrEndPoint - mCurrStartPoint; actionDir.normalize(); F32 angDiff = 90.0 - mDot( actionDir, mCurrStartDir ) * 90.0; if( angDiff > mDataBlock->cutoffAngle ) return; AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry"); RectI viewport; glMatrixMode(GL_PROJECTION); glPushMatrix(); dglGetViewport(&viewport); glMatrixMode(GL_MODELVIEW); // Uncomment this if this is a "simple" (non-zone managing) object state->setupObjectProjection(this); glDisable(GL_CULL_FACE); glDepthMask(GL_FALSE); glEnable(GL_BLEND); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE); glEnable(GL_TEXTURE_2D); glColor4f( 1.0, 1.0, 1.0, 1.0 ); if( canRenderFlare( state->getCameraPosition() ) ) { renderFlare( 0.6 ); } QuadPatch qPatch; Point3F cPoints[3]; cPoints[0] = mCurrStartPoint; cPoints[1] = mCurrStartPoint + mCurrStartDir * (mCurrStartPoint - mCurrEndPoint).len(); cPoints[2] = mCurrEndPoint; SplCtrlPts ctrlPoints; ctrlPoints.submitPoints( cPoints, 3 ); qPatch.submitControlPoints( ctrlPoints ); glBlendFunc(GL_SRC_ALPHA, GL_ONE); glBindTexture( GL_TEXTURE_2D, mDataBlock->textureList[RepairProjectileData::RT_BEAM].getGLName() ); glColor4f( 1.0, 1.0, 1.0, 0.75 ); SplineUtil::drawSplineBeam( state->getCameraPosition(), 20, 0.2, qPatch, -mElapsedTime * mDataBlock->beamSpeed, mDataBlock->texRepeat ); SplineUtil::SplineBeamInfo sbi; Point3F camPos = state->getCameraPosition(); sbi.camPos = &camPos; sbi.zeroAlphaStart = true; sbi.numSegments = mDataBlock->numSegments; sbi.uvOffset = -mElapsedTime * mDataBlock->beamSpeed; sbi.numTexRep = mDataBlock->texRepeat; for( int i=0; igetControllingClient(); if( gc ) { firstPerson = gc->isFirstPerson(); } VectorF dirFromGun = mCurrEndPoint - mCurrStartPoint; 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; mRepairingObject->disableCollision(); if( firstPerson ) { mSourceObject->disableCollision(); RayInfo rayInfo; rayHit = getContainer()->castRay( tweakedCamPos, mCurrEndPoint, mRepairingObject->getType(), &rayInfo ); mSourceObject->enableCollision(); } else { RayInfo rayInfo; rayHit = getContainer()->castRay( tweakedCamPos, mCurrEndPoint, mRepairingObject->getType(), &rayInfo ); } mRepairingObject->enableCollision(); return !rayHit; } //-------------------------------------------------------------------------- // Render flare //-------------------------------------------------------------------------- void RepairProjectile::renderFlare( F32 flareSize ) { Point3F pos = mCurrEndPoint; glDisable(GL_DEPTH_TEST); glBindTexture( GL_TEXTURE_2D, mDataBlock->textureList[RepairProjectileData::RT_FLARE].getGLName() ); glColor3f( 0.3333, 0.3333, 0.3333 ); dglDrawBillboard( pos, flareSize * 2.0, mElapsedTime * .15 * M_PI * 2.0 ); dglDrawBillboard( pos, flareSize * 1.7, -mElapsedTime * .10 * M_PI * 2.0 ); dglDrawBillboard( pos, flareSize * 1.82, mElapsedTime * .05 * M_PI * 2.0 ); glEnable(GL_DEPTH_TEST); }