//----------------------------------------------------------------------------- // Torque Game Engine // Copyright (C) GarageGames.com, Inc. //----------------------------------------------------------------------------- #include "dgl/dgl.h" #include "platform/platform.h" #include "core/dnet.h" #include "audio/audioDataBlock.h" #include "game/gameConnection.h" #include "game/moveManager.h" #include "console/consoleTypes.h" #include "core/bitStream.h" #include "ts/tsPartInstance.h" #include "ts/tsShapeInstance.h" #include "sceneGraph/sceneGraph.h" #include "sceneGraph/sceneState.h" #include "game/shadow.h" #include "game/fx/explosion.h" #include "game/shapeBase.h" #include "terrain/waterBlock.h" #include "game/debris.h" #include "terrain/sky.h" #include "game/physicalZone.h" #include "sceneGraph/detailManager.h" #include "math/mathUtils.h" #include "math/mMatrix.h" #include "math/mRandom.h" #include "platform/profiler.h" IMPLEMENT_CO_DATABLOCK_V1(ShapeBaseData); //---------------------------------------------------------------------------- // Timeout for non-looping sounds on a channel static SimTime sAudioTimeout = 500; bool ShapeBase::gRenderEnvMaps = true; F32 ShapeBase::sWhiteoutDec = 0.007; F32 ShapeBase::sDamageFlashDec = 0.007; U32 ShapeBase::sLastRenderFrame = 0; static const char *sDamageStateName[] = { // Index by enum ShapeBase::DamageState "Enabled", "Disabled", "Destroyed" }; //---------------------------------------------------------------------------- ShapeBaseData::ShapeBaseData() { shadowEnable = false; shadowCanMove = false; shadowCanAnimate = false; shapeName = ""; cloakTexName = ""; mass = 1; drag = 0; density = 1; maxEnergy = 0; maxDamage = 1.0; disabledLevel = 1.0; destroyedLevel = 1.0; repairRate = 0.0033; eyeNode = -1; shadowNode = -1; cameraNode = -1; damageSequence = -1; hulkSequence = -1; cameraMaxDist = 0; cameraMinDist = 0.2; cameraDefaultFov = 90.f; cameraMinFov = 5.f; cameraMaxFov = 120.f; emap = false; aiAvoidThis = false; isInvincible = false; renderWhenDestroyed = true; debris = NULL; debrisID = 0; debrisShapeName = NULL; explosion = NULL; explosionID = 0; underwaterExplosion = NULL; underwaterExplosionID = 0; firstPersonOnly = false; useEyePoint = false; observeThroughObject = false; computeCRC = false; // no shadows by default genericShadowLevel = 2.0f; noShadowLevel = 2.0f; inheritEnergyFromMount = false; for(U32 j = 0; j < NumHudRenderImages; j++) { hudImageNameFriendly[j] = 0; hudImageNameEnemy[j] = 0; hudRenderCenter[j] = false; hudRenderModulated[j] = false; hudRenderAlways[j] = false; hudRenderDistance[j] = false; hudRenderName[j] = false; } } static ShapeBaseData gShapeBaseDataProto; ShapeBaseData::~ShapeBaseData() { } bool ShapeBaseData::preload(bool server, char errorBuffer[256]) { if (!Parent::preload(server, errorBuffer)) return false; bool shapeError = false; // Resolve objects transmitted from server if (!server) { if( !explosion && explosionID != 0 ) { if( Sim::findObject( explosionID, explosion ) == false) { Con::errorf( ConsoleLogEntry::General, "ShapeBaseData::preload: Invalid packet, bad datablockId(explosion): 0x%x", explosionID ); } AssertFatal(!(explosion && ((explosionID < DataBlockObjectIdFirst) || (explosionID > DataBlockObjectIdLast))), "ShapeBaseData::preload: invalid explosion data"); } if( !underwaterExplosion && underwaterExplosionID != 0 ) { if( Sim::findObject( underwaterExplosionID, underwaterExplosion ) == false) { Con::errorf( ConsoleLogEntry::General, "ShapeBaseData::preload: Invalid packet, bad datablockId(underwaterExplosion): 0x%x", underwaterExplosionID ); } AssertFatal(!(underwaterExplosion && ((underwaterExplosionID < DataBlockObjectIdFirst) || (underwaterExplosionID > DataBlockObjectIdLast))), "ShapeBaseData::preload: invalid underwaterExplosion data"); } if( !debris && debrisID != 0 ) { Sim::findObject( debrisID, debris ); AssertFatal(!(debris && ((debrisID < DataBlockObjectIdFirst) || (debrisID > DataBlockObjectIdLast))), "ShapeBaseData::preload: invalid debris data"); } if( debrisShapeName && debrisShapeName[0] != '\0' && !bool(debrisShape) ) { debrisShape = ResourceManager->load(debrisShapeName); if( bool(debrisShape) == false ) { dSprintf(errorBuffer, 256, "ShapeBaseData::load: Couldn't load shape \"%s\"", debrisShapeName); return false; } else { if(!server && !debrisShape->preloadMaterialList() && NetConnection::filesWereDownloaded()) shapeError = true; TSShapeInstance* pDummy = new TSShapeInstance(debrisShape, !server); delete pDummy; } } } // if (shapeName && shapeName[0]) { S32 i; // Resolve shapename shape = ResourceManager->load(shapeName, computeCRC); if (!bool(shape)) { dSprintf(errorBuffer, 256, "ShapeBaseData: Couldn't load shape \"%s\"",shapeName); return false; } if(!server && !shape->preloadMaterialList() && NetConnection::filesWereDownloaded()) shapeError = true; if(computeCRC) { Con::printf("Validation required for shape: %s", shapeName); if(server) mCRC = shape.getCRC(); else if(mCRC != shape.getCRC()) { dSprintf(errorBuffer, 256, "Shape \"%s\" does not match version on server.",shapeName); return false; } } // Resolve details and camera node indexes. for (i = 0; i < shape->details.size(); i++) { char* name = (char*)shape->names[shape->details[i].nameIndex]; if (dStrstr((const char *)dStrlwr(name), "collision-")) { collisionDetails.push_back(i); collisionBounds.increment(); shape->computeBounds(collisionDetails.last(), collisionBounds.last()); shape->getAccelerator(collisionDetails.last()); if (!shape->bounds.isContained(collisionBounds.last())) { Con::warnf("Warning: shape %s collision detail %d (Collision-%d) bounds exceed that of shape.", shapeName, collisionDetails.size() - 1, collisionDetails.last()); collisionBounds.last() = shape->bounds; } else if (collisionBounds.last().isValidBox() == false) { Con::errorf("Error: shape %s-collision detail %d (Collision-%d) bounds box invalid!", shapeName, collisionDetails.size() - 1, collisionDetails.last()); collisionBounds.last() = shape->bounds; } // The way LOS works is that it will check to see if there is a LOS detail that matches // the the collision detail + 1 + MaxCollisionShapes (this variable name should change in // the future). If it can't find a matching LOS it will simply use the collision instead. // We check for any "unmatched" LOS's further down LOSDetails.increment(); char buff[128]; dSprintf(buff, sizeof(buff), "LOS-%d", i + 1 + MaxCollisionShapes); U32 los = shape->findDetail(buff); if (los == -1) LOSDetails.last() = i; else LOSDetails.last() = los; } } // Snag any "unmatched" LOS details for (i = 0; i < shape->details.size(); i++) { char* name = (char*)shape->names[shape->details[i].nameIndex]; if (dStrstr((const char *)dStrlwr(name), "los-")) { // See if we already have this LOS bool found = false; for (U32 j = 0; j < LOSDetails.size(); j++) { if (LOSDetails[j] == i) { found = true; break; } } if (!found) LOSDetails.push_back(i); } } debrisDetail = shape->findDetail("Debris-17"); eyeNode = shape->findNode("eye"); cameraNode = shape->findNode("cam"); if (cameraNode == -1) cameraNode = eyeNode; // Resolve mount point node indexes for (i = 0; i < NumMountPoints; i++) { char fullName[256]; dSprintf(fullName,sizeof(fullName),"mount%d",i); mountPointNode[i] = shape->findNode(fullName); } // find the AIRepairNode - hardcoded to be the last node in the array... mountPointNode[AIRepairNode] = shape->findNode("AIRepairNode"); // hulkSequence = shape->findSequence("Visibility"); damageSequence = shape->findSequence("Damage"); // F32 w = shape->bounds.len_y() / 2; if (cameraMaxDist < w) cameraMaxDist = w; } if(!server) { // grab all the hud images for(U32 i = 0; i < NumHudRenderImages; i++) { if(hudImageNameFriendly[i] && hudImageNameFriendly[i][0]) hudImageFriendly[i] = TextureHandle(hudImageNameFriendly[i], BitmapTexture); if(hudImageNameEnemy[i] && hudImageNameEnemy[i][0]) hudImageEnemy[i] = TextureHandle(hudImageNameEnemy[i], BitmapTexture); } } return !shapeError; } void ShapeBaseData::initPersistFields() { Parent::initPersistFields(); addGroup("Shadows"); addField("shadowEnable", TypeBool, Offset(shadowEnable, ShapeBaseData)); addField("shadowCanMove", TypeBool, Offset(shadowCanMove, ShapeBaseData)); addField("shadowCanAnimate", TypeBool, Offset(shadowCanAnimate, ShapeBaseData)); endGroup("Shadows"); addGroup("Render"); addField("shapeFile", TypeFilename, Offset(shapeName, ShapeBaseData)); addField("cloakTexture", TypeFilename, Offset(cloakTexName, ShapeBaseData)); addField("emap", TypeBool, Offset(emap, ShapeBaseData)); endGroup("Render"); addGroup("Destruction", "Parameters related to the destruction effects of this object."); addField("explosion", TypeExplosionDataPtr, Offset(explosion, ShapeBaseData)); addField("underwaterExplosion", TypeExplosionDataPtr, Offset(underwaterExplosion, ShapeBaseData)); addField("debris", TypeDebrisDataPtr, Offset(debris, ShapeBaseData)); addField("renderWhenDestroyed", TypeBool, Offset(renderWhenDestroyed, ShapeBaseData)); addField("debrisShapeName", TypeFilename, Offset(debrisShapeName, ShapeBaseData)); endGroup("Destruction"); addGroup("Physics"); addField("mass", TypeF32, Offset(mass, ShapeBaseData)); addField("drag", TypeF32, Offset(drag, ShapeBaseData)); addField("density", TypeF32, Offset(density, ShapeBaseData)); endGroup("Physics"); addGroup("Damage/Energy"); addField("maxEnergy", TypeF32, Offset(maxEnergy, ShapeBaseData)); addField("maxDamage", TypeF32, Offset(maxDamage, ShapeBaseData)); addField("disabledLevel", TypeF32, Offset(disabledLevel, ShapeBaseData)); addField("destroyedLevel", TypeF32, Offset(destroyedLevel, ShapeBaseData)); addField("repairRate", TypeF32, Offset(repairRate, ShapeBaseData)); addField("inheritEnergyFromMount", TypeBool, Offset(inheritEnergyFromMount, ShapeBaseData)); addField("isInvincible", TypeBool, Offset(isInvincible, ShapeBaseData)); endGroup("Damage/Energy"); addGroup("Camera"); addField("cameraMaxDist", TypeF32, Offset(cameraMaxDist, ShapeBaseData)); addField("cameraMinDist", TypeF32, Offset(cameraMinDist, ShapeBaseData)); addField("cameraDefaultFov", TypeF32, Offset(cameraDefaultFov, ShapeBaseData)); addField("cameraMinFov", TypeF32, Offset(cameraMinFov, ShapeBaseData)); addField("cameraMaxFov", TypeF32, Offset(cameraMaxFov, ShapeBaseData)); addField("firstPersonOnly", TypeBool, Offset(firstPersonOnly, ShapeBaseData)); addField("useEyePoint", TypeBool, Offset(useEyePoint, ShapeBaseData)); addField("observeThroughObject", TypeBool, Offset(observeThroughObject, ShapeBaseData)); endGroup("Camera"); // This hud code is going to get ripped out soon... addGroup("HUD", "@deprecated Likely to be removed soon."); addField("hudImageName", TypeFilename, Offset(hudImageNameFriendly, ShapeBaseData), NumHudRenderImages); addField("hudImageNameFriendly", TypeFilename, Offset(hudImageNameFriendly, ShapeBaseData), NumHudRenderImages); addField("hudImageNameEnemy", TypeFilename, Offset(hudImageNameEnemy, ShapeBaseData), NumHudRenderImages); addField("hudRenderCenter", TypeBool, Offset(hudRenderCenter, ShapeBaseData), NumHudRenderImages); addField("hudRenderModulated", TypeBool, Offset(hudRenderModulated, ShapeBaseData), NumHudRenderImages); addField("hudRenderAlways", TypeBool, Offset(hudRenderAlways, ShapeBaseData), NumHudRenderImages); addField("hudRenderDistance", TypeBool, Offset(hudRenderDistance, ShapeBaseData), NumHudRenderImages); addField("hudRenderName", TypeBool, Offset(hudRenderName, ShapeBaseData), NumHudRenderImages); endGroup("HUD"); addGroup("Misc"); addField("aiAvoidThis", TypeBool, Offset(aiAvoidThis, ShapeBaseData)); addField("computeCRC", TypeBool, Offset(computeCRC, ShapeBaseData)); endGroup("Misc"); } ConsoleMethod( ShapeBaseData, checkDeployPos, bool, 3, 3, "(Transform xform)") { if (bool(object->shape) == false) return false; Point3F pos(0, 0, 0); AngAxisF aa(Point3F(0, 0, 1), 0); dSscanf(argv[2],"%g %g %g %g %g %g %g", &pos.x,&pos.y,&pos.z,&aa.axis.x,&aa.axis.y,&aa.axis.z,&aa.angle); MatrixF mat; aa.setMatrix(&mat); mat.setColumn(3,pos); Box3F objBox = object->shape->bounds; Point3F boxCenter = (objBox.min + objBox.max) * 0.5; objBox.min = boxCenter + (objBox.min - boxCenter) * 0.9; objBox.max = boxCenter + (objBox.max - boxCenter) * 0.9; Box3F wBox = objBox; mat.mul(wBox); EarlyOutPolyList polyList; polyList.mNormal.set(0,0,0); polyList.mPlaneList.clear(); polyList.mPlaneList.setSize(6); polyList.mPlaneList[0].set(objBox.min,VectorF(-1,0,0)); polyList.mPlaneList[1].set(objBox.max,VectorF(0,1,0)); polyList.mPlaneList[2].set(objBox.max,VectorF(1,0,0)); polyList.mPlaneList[3].set(objBox.min,VectorF(0,-1,0)); polyList.mPlaneList[4].set(objBox.min,VectorF(0,0,-1)); polyList.mPlaneList[5].set(objBox.max,VectorF(0,0,1)); for (U32 i = 0; i < 6; i++) { PlaneF temp; mTransformPlane(mat, Point3F(1, 1, 1), polyList.mPlaneList[i], &temp); polyList.mPlaneList[i] = temp; } if (gServerContainer.buildPolyList(wBox, InteriorObjectType | StaticShapeObjectType, &polyList)) return false; return true; } ConsoleMethod(ShapeBaseData, getDeployTransform, const char *, 4, 4, "(Point3F pos, Point3F normal)") { Point3F normal; Point3F position; dSscanf(argv[2], "%g %g %g", &position.x, &position.y, &position.z); dSscanf(argv[3], "%g %g %g", &normal.x, &normal.y, &normal.z); normal.normalize(); VectorF xAxis; if( mFabs(normal.z) > mFabs(normal.x) && mFabs(normal.z) > mFabs(normal.y)) mCross( VectorF( 0, 1, 0 ), normal, &xAxis ); else mCross( VectorF( 0, 0, 1 ), normal, &xAxis ); VectorF yAxis; mCross( normal, xAxis, &yAxis ); MatrixF testMat(true); testMat.setColumn( 0, xAxis ); testMat.setColumn( 1, yAxis ); testMat.setColumn( 2, normal ); testMat.setPosition( position ); char *returnBuffer = Con::getReturnBuffer(256); Point3F pos; testMat.getColumn(3,&pos); AngAxisF aa(testMat); dSprintf(returnBuffer,256,"%g %g %g %g %g %g %g", pos.x,pos.y,pos.z,aa.axis.x,aa.axis.y,aa.axis.z,aa.angle); return returnBuffer; } void ShapeBaseData::packData(BitStream* stream) { Parent::packData(stream); if(stream->writeFlag(computeCRC)) stream->write(mCRC); stream->writeFlag(shadowEnable); stream->writeFlag(shadowCanMove); stream->writeFlag(shadowCanAnimate); stream->writeString(shapeName); stream->writeString(cloakTexName); if(stream->writeFlag(mass != gShapeBaseDataProto.mass)) stream->write(mass); if(stream->writeFlag(drag != gShapeBaseDataProto.drag)) stream->write(drag); if(stream->writeFlag(density != gShapeBaseDataProto.density)) stream->write(density); if(stream->writeFlag(maxEnergy != gShapeBaseDataProto.maxEnergy)) stream->write(maxEnergy); if(stream->writeFlag(cameraMaxDist != gShapeBaseDataProto.cameraMaxDist)) stream->write(cameraMaxDist); if(stream->writeFlag(cameraMinDist != gShapeBaseDataProto.cameraMinDist)) stream->write(cameraMinDist); cameraDefaultFov = mClampF(cameraDefaultFov, cameraMinFov, cameraMaxFov); if(stream->writeFlag(cameraDefaultFov != gShapeBaseDataProto.cameraDefaultFov)) stream->write(cameraDefaultFov); if(stream->writeFlag(cameraMinFov != gShapeBaseDataProto.cameraMinFov)) stream->write(cameraMinFov); if(stream->writeFlag(cameraMaxFov != gShapeBaseDataProto.cameraMaxFov)) stream->write(cameraMaxFov); stream->writeString( debrisShapeName ); stream->writeFlag(observeThroughObject); if( stream->writeFlag( debris != NULL ) ) { stream->writeRangedU32(packed? SimObjectId(debris): debris->getId(),DataBlockObjectIdFirst,DataBlockObjectIdLast); } stream->writeFlag(emap); stream->writeFlag(isInvincible); stream->writeFlag(renderWhenDestroyed); if( stream->writeFlag( explosion != NULL ) ) { stream->writeRangedU32( explosion->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); } if( stream->writeFlag( underwaterExplosion != NULL ) ) { stream->writeRangedU32( underwaterExplosion->getId(), DataBlockObjectIdFirst, DataBlockObjectIdLast ); } stream->writeFlag(inheritEnergyFromMount); stream->writeFlag(firstPersonOnly); stream->writeFlag(useEyePoint); } void ShapeBaseData::unpackData(BitStream* stream) { Parent::unpackData(stream); computeCRC = stream->readFlag(); if(computeCRC) stream->read(&mCRC); shadowEnable = stream->readFlag(); shadowCanMove = stream->readFlag(); shadowCanAnimate = stream->readFlag(); shapeName = stream->readSTString(); cloakTexName = stream->readSTString(); if(stream->readFlag()) stream->read(&mass); else mass = gShapeBaseDataProto.mass; if(stream->readFlag()) stream->read(&drag); else drag = gShapeBaseDataProto.drag; if(stream->readFlag()) stream->read(&density); else density = gShapeBaseDataProto.density; if(stream->readFlag()) stream->read(&maxEnergy); else maxEnergy = gShapeBaseDataProto.maxEnergy; if(stream->readFlag()) stream->read(&cameraMaxDist); else cameraMaxDist = gShapeBaseDataProto.cameraMaxDist; if(stream->readFlag()) stream->read(&cameraMinDist); else cameraMinDist = gShapeBaseDataProto.cameraMinDist; if(stream->readFlag()) stream->read(&cameraDefaultFov); else cameraDefaultFov = gShapeBaseDataProto.cameraDefaultFov; if(stream->readFlag()) stream->read(&cameraMinFov); else cameraMinFov = gShapeBaseDataProto.cameraMinFov; if(stream->readFlag()) stream->read(&cameraMaxFov); else cameraMaxFov = gShapeBaseDataProto.cameraMaxFov; debrisShapeName = stream->readSTString(); observeThroughObject = stream->readFlag(); if( stream->readFlag() ) { debrisID = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); } emap = stream->readFlag(); isInvincible = stream->readFlag(); renderWhenDestroyed = stream->readFlag(); if( stream->readFlag() ) { explosionID = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); } if( stream->readFlag() ) { underwaterExplosionID = stream->readRangedU32( DataBlockObjectIdFirst, DataBlockObjectIdLast ); } inheritEnergyFromMount = stream->readFlag(); firstPersonOnly = stream->readFlag(); useEyePoint = stream->readFlag(); } //---------------------------------------------------------------------------- //---------------------------------------------------------------------------- Chunker sTimeoutChunker; ShapeBase::CollisionTimeout* ShapeBase::sFreeTimeoutList = 0; //---------------------------------------------------------------------------- IMPLEMENT_CO_NETOBJECT_V1(ShapeBase); ShapeBase::ShapeBase() { mTypeMask |= ShapeBaseObjectType; mDrag = 0; mBuoyancy = 0; mWaterCoverage = 0; mLiquidType = 0; mLiquidHeight = 0.0f; //mControllingClient = 0; mControllingObject = 0; mGravityMod = 1.0; mAppliedForce.set(0, 0, 0); mTimeoutList = 0; mDataBlock = NULL; mShapeInstance = 0; mEnergy = 0; mRechargeRate = 0; mDamage = 0; mRepairRate = 0; mRepairReserve = 0; mDamageState = Enabled; mDamageThread = 0; mHulkThread = 0; mLastRenderFrame = 0; mLastRenderDistance = 0; mCloaked = false; mCloakLevel = 0.0; mMount.object = 0; mMount.link = 0; mMount.list = 0; mHidden = false; for (int a = 0; a < MaxSoundThreads; a++) { mSoundThread[a].play = false; mSoundThread[a].profile = 0; mSoundThread[a].sound = 0; } S32 i; for (i = 0; i < MaxScriptThreads; i++) { mScriptThread[i].sequence = -1; mScriptThread[i].thread = 0; mScriptThread[i].sound = 0; mScriptThread[i].state = Thread::Stop; mScriptThread[i].atEnd = false; mScriptThread[i].forward = true; } for (i = 0; i < MaxTriggerKeys; i++) mTrigger[i] = false; mDamageFlash = 0.0; mWhiteOut = 0.0; mInvincibleEffect = 0.0f; mInvincibleDelta = 0.0f; mInvincibleCount = 0.0f; mInvincibleSpeed = 0.0f; mInvincibleTime = 0.0f; mInvincibleFade = 0.1; mInvincibleOn = false; mIsControlled = false; mConvexList = new Convex; mCameraFov = 90.f; mShieldNormal.set(0, 0, 1); mFadeOut = true; mFading = false; mFadeVal = 1.0; mFadeTime = 1.0; mFadeElapsedTime = 0.0; mFadeDelay = 0.0; mFlipFadeVal = false; mLightTime = 0; damageDir.set(0, 0, 1); } ShapeBase::~ShapeBase() { delete mConvexList; mConvexList = NULL; AssertFatal(mMount.link == 0,"ShapeBase::~ShapeBase: An object is still mounted"); if( mShapeInstance && (mShapeInstance->getDebrisRefCount() == 0) ) { delete mShapeInstance; } CollisionTimeout* ptr = mTimeoutList; while (ptr) { CollisionTimeout* cur = ptr; ptr = ptr->next; cur->next = sFreeTimeoutList; sFreeTimeoutList = cur; } } //---------------------------------------------------------------------------- bool ShapeBase::onAdd() { if(!Parent::onAdd()) return false; // Resolve sounds that arrived in the initial update S32 i; for (i = 0; i < MaxSoundThreads; i++) updateAudioState(mSoundThread[i]); for (i = 0; i < MaxScriptThreads; i++) { Thread& st = mScriptThread[i]; if(st.thread) updateThread(st); } if (isClientObject()) { if(mDataBlock->cloakTexName != StringTable->insert("")) mCloakTexture = TextureHandle(mDataBlock->cloakTexName, MeshTexture, false); //one of the mounted images must have a light source... for (S32 i = 0; i < MaxMountedImages; i++) { ShapeBaseImageData* imageData = getMountedImage(i); if (imageData != NULL && imageData->lightType != ShapeBaseImageData::NoLight) { Sim::getLightSet()->addObject(this); break; } } } return true; } void ShapeBase::onRemove() { mConvexList->nukeList(); unmount(); Parent::onRemove(); // Stop any running sounds on the client if (isGhost()) for (S32 i = 0; i < MaxSoundThreads; i++) stopAudio(i); } void ShapeBase::onSceneRemove() { mConvexList->nukeList(); Parent::onSceneRemove(); } bool ShapeBase::onNewDataBlock(GameBaseData* dptr) { if (Parent::onNewDataBlock(dptr) == false) return false; mDataBlock = dynamic_cast(dptr); if (!mDataBlock) return false; setMaskBits(DamageMask); mDamageThread = 0; mHulkThread = 0; // Even if loadShape succeeds, there may not actually be // a shape assigned to this object. if (bool(mDataBlock->shape)) { delete mShapeInstance; mShapeInstance = new TSShapeInstance(mDataBlock->shape, isClientObject()); if (isClientObject()) mShapeInstance->cloneMaterialList(); mObjBox = mDataBlock->shape->bounds; resetWorldBox(); // Initialize the threads for (U32 i = 0; i < MaxScriptThreads; i++) { Thread& st = mScriptThread[i]; if (st.sequence != -1) { // TG: Need to see about supressing non-cyclic sounds // if the sequences were actived before the object was // ghosted. // TG: Cyclic animations need to have a random pos if // they were started before the object was ghosted. // If there was something running on the old shape, the thread // needs to be reset. Otherwise we assume that it's been // initialized either by the constructor or from the server. bool reset = st.thread != 0; st.thread = 0; setThreadSequence(i,st.sequence,reset); } } if (mDataBlock->damageSequence != -1) { mDamageThread = mShapeInstance->addThread(); mShapeInstance->setSequence(mDamageThread, mDataBlock->damageSequence,0); } if (mDataBlock->hulkSequence != -1) { mHulkThread = mShapeInstance->addThread(); mShapeInstance->setSequence(mHulkThread, mDataBlock->hulkSequence,0); } } if (isGhost() && mSkinNameHandle.isValidString() && mShapeInstance) { mShapeInstance->reSkin(mSkinNameHandle); mSkinHash = _StringTable::hashString(mSkinNameHandle.getString()); } // mEnergy = 0; mDamage = 0; mDamageState = Enabled; mRepairReserve = 0; updateMass(); updateDamageLevel(); updateDamageState(); mDrag = mDataBlock->drag; mCameraFov = mDataBlock->cameraDefaultFov; return true; } void ShapeBase::onDeleteNotify(SimObject* obj) { if (obj == getProcessAfter()) clearProcessAfter(); Parent::onDeleteNotify(obj); if (obj == mMount.object) unmount(); } void ShapeBase::onImpact(SceneObject* obj, VectorF vec) { if (!isGhost()) { char buff1[256]; char buff2[32]; dSprintf(buff1,sizeof(buff1),"%g %g %g",vec.x, vec.y, vec.z); dSprintf(buff2,sizeof(buff2),"%g",vec.len()); Con::executef(mDataBlock,5,"onImpact",scriptThis(), obj->getIdString(), buff1, buff2); } } void ShapeBase::onImpact(VectorF vec) { if (!isGhost()) { char buff1[256]; char buff2[32]; dSprintf(buff1,sizeof(buff1),"%g %g %g",vec.x, vec.y, vec.z); dSprintf(buff2,sizeof(buff2),"%g",vec.len()); Con::executef(mDataBlock,5,"onImpact",scriptThis(), "0", buff1, buff2); } } //---------------------------------------------------------------------------- void ShapeBase::processTick(const Move* move) { // Energy management if (mDamageState == Enabled && mDataBlock->inheritEnergyFromMount == false) { F32 store = mEnergy; mEnergy += mRechargeRate; if (mEnergy > mDataBlock->maxEnergy) mEnergy = mDataBlock->maxEnergy; else if (mEnergy < 0) mEnergy = 0; // Virtual setEnergyLevel is used here by some derived classes to // decide whether they really want to set the energy mask bit. if (mEnergy != store) setEnergyLevel(mEnergy); } // Repair management if (mDataBlock->isInvincible == false) { F32 store = mDamage; mDamage -= mRepairRate; mDamage = mClampF(mDamage, 0.f, mDataBlock->maxDamage); if (mRepairReserve > mDamage) mRepairReserve = mDamage; if (mRepairReserve > 0.0) { F32 rate = getMin(mDataBlock->repairRate, mRepairReserve); mDamage -= rate; mRepairReserve -= rate; } if (store != mDamage) { updateDamageLevel(); if (isServerObject()) { char delta[100]; dSprintf(delta,sizeof(delta),"%g",mDamage - store); setMaskBits(DamageMask); Con::executef(mDataBlock,3,"onDamage",scriptThis(),delta); } } } if (isServerObject()) { // Server only... advanceThreads(TickSec); updateServerAudio(); // update wet state setImageWetState(0, mWaterCoverage > 0.4); // more than 40 percent covered if(mFading) { F32 dt = TickMs / 1000.0; F32 newFadeET = mFadeElapsedTime + dt; if(mFadeElapsedTime < mFadeDelay && newFadeET >= mFadeDelay) setMaskBits(CloakMask); mFadeElapsedTime = newFadeET; if(mFadeElapsedTime > mFadeTime + mFadeDelay) { mFadeVal = F32(!mFadeOut); mFading = false; } } } // Advance images for (int i = 0; i < MaxMountedImages; i++) { if (mMountedImageList[i].dataBlock != NULL) updateImageState(i, TickSec); } // Call script on trigger state changes if (move && mDataBlock && isServerObject()) { for (S32 i = 0; i < MaxTriggerKeys; i++) { if (move->trigger[i] != mTrigger[i]) { mTrigger[i] = move->trigger[i]; char buf1[20],buf2[20]; dSprintf(buf1,sizeof(buf1),"%d",i); dSprintf(buf2,sizeof(buf2),"%d",(move->trigger[i]?1:0)); Con::executef(mDataBlock,4,"onTrigger",scriptThis(),buf1,buf2); } } } // Update the damage flash and the whiteout // if (mDamageFlash > 0.0) { mDamageFlash -= sDamageFlashDec; if (mDamageFlash <= 0.0) mDamageFlash = 0.0; } if (mWhiteOut > 0.0) { mWhiteOut -= sWhiteoutDec; if (mWhiteOut <= 0.0) mWhiteOut = 0.0; } } void ShapeBase::advanceTime(F32 dt) { // On the client, the shape threads and images are // advanced at framerate. advanceThreads(dt); updateAudioPos(); for (int i = 0; i < MaxMountedImages; i++) if (mMountedImageList[i].dataBlock) updateImageAnimation(i,dt); // Cloaking takes 0.5 seconds if (mCloaked && mCloakLevel != 1.0) { mCloakLevel += dt * 2; if (mCloakLevel >= 1.0) mCloakLevel = 1.0; } else if (!mCloaked && mCloakLevel != 0.0) { mCloakLevel -= dt * 2; if (mCloakLevel <= 0.0) mCloakLevel = 0.0; } if(mInvincibleOn) updateInvincibleEffect(dt); if(mFading) { mFadeElapsedTime += dt; if(mFadeElapsedTime > mFadeTime) { mFadeVal = F32(!mFadeOut); mFading = false; } else { mFadeVal = mFadeElapsedTime / mFadeTime; if(mFadeOut) mFadeVal = 1 - mFadeVal; } } } //---------------------------------------------------------------------------- //void ShapeBase::setControllingClient(GameConnection* client) //{ // mControllingClient = client; // // // piggybacks on the cloak update // setMaskBits(CloakMask); //} void ShapeBase::setControllingObject(ShapeBase* obj) { if (obj) { setProcessTick(false); // Even though we don't processTick, we still need to // process after the controller in case anyone is mounted // on this object. processAfter(obj); } else { setProcessTick(true); clearProcessAfter(); // Catch the case of the controlling object actually // mounted on this object. if (mControllingObject->mMount.object == this) mControllingObject->processAfter(this); } mControllingObject = obj; } ShapeBase* ShapeBase::getControlObject() { return 0; } void ShapeBase::setControlObject(ShapeBase*) { } bool ShapeBase::isFirstPerson() { // Always first person as far as the server is concerned. if (!isGhost()) return true; if (GameConnection* con = getControllingClient()) return con->getControlObject() == this && con->isFirstPerson(); return false; } // Camera: (in degrees) ------------------------------------------------------ F32 ShapeBase::getCameraFov() { return(mCameraFov); } F32 ShapeBase::getDefaultCameraFov() { return(mDataBlock->cameraDefaultFov); } bool ShapeBase::isValidCameraFov(F32 fov) { return((fov >= mDataBlock->cameraMinFov) && (fov <= mDataBlock->cameraMaxFov)); } void ShapeBase::setCameraFov(F32 fov) { mCameraFov = mClampF(fov, mDataBlock->cameraMinFov, mDataBlock->cameraMaxFov); } //---------------------------------------------------------------------------- static void scopeCallback(SceneObject* obj, void *conPtr) { NetConnection * ptr = reinterpret_cast(conPtr); if (obj->isScopeable()) ptr->objectInScope(obj); } void ShapeBase::onCameraScopeQuery(NetConnection *cr, CameraScopeQuery * query) { // update the camera query query->camera = this; // bool grabEye = true; if(GameConnection * con = dynamic_cast(cr)) { // get the fov from the connection (in deg) F32 fov; if (con->getControlCameraFov(&fov)) { query->fov = mDegToRad(fov/2); query->sinFov = mSin(query->fov); query->cosFov = mCos(query->fov); } } // failed to query the camera info? // if(grabEye) LH - always use eye as good enough, avoid camera animate { MatrixF eyeTransform; getEyeTransform(&eyeTransform); eyeTransform.getColumn(3, &query->pos); eyeTransform.getColumn(1, &query->orientation); } // grab the visible distance from the sky Sky * sky = gServerSceneGraph->getCurrentSky(); if(sky) query->visibleDistance = sky->getVisibleDistance(); else query->visibleDistance = 1000.f; // First, we are certainly in scope, and whatever we're riding is too... cr->objectInScope(this); if (isMounted()) cr->objectInScope(mMount.object); if (mSceneManager == NULL) { // Scope everything... gServerContainer.findObjects(0xFFFFFFFF,scopeCallback,cr); return; } // update the scenemanager mSceneManager->scopeScene(query->pos, query->visibleDistance, cr); // let the (game)connection do some scoping of its own (commandermap...) cr->doneScopingScene(); } //---------------------------------------------------------------------------- F32 ShapeBase::getEnergyLevel() { if (mDataBlock->inheritEnergyFromMount == false) return mEnergy; else if (isMounted()) { return getObjectMount()->getEnergyLevel(); } else { return 0.0f; } } F32 ShapeBase::getEnergyValue() { if (mDataBlock->inheritEnergyFromMount == false) { F32 maxEnergy = mDataBlock->maxEnergy; if ( maxEnergy > 0.f ) return (mEnergy / mDataBlock->maxEnergy); } else if (isMounted()) { F32 maxEnergy = getObjectMount()->mDataBlock->maxEnergy; if ( maxEnergy > 0.f ) return (getObjectMount()->getEnergyLevel() / maxEnergy); } return 0.0f; } void ShapeBase::setEnergyLevel(F32 energy) { if (mDataBlock->inheritEnergyFromMount == false) { if (mDamageState == Enabled) { mEnergy = (energy > mDataBlock->maxEnergy)? mDataBlock->maxEnergy: (energy < 0)? 0: energy; } } else { // Pass the set onto whatever we're mounted to... if (isMounted()) getObjectMount()->setEnergyLevel(energy); } } void ShapeBase::setDamageLevel(F32 damage) { if (!mDataBlock->isInvincible) { F32 store = mDamage; mDamage = mClampF(damage, 0.f, mDataBlock->maxDamage); if (store != mDamage) { updateDamageLevel(); if (isServerObject()) { setMaskBits(DamageMask); char delta[100]; dSprintf(delta,sizeof(delta),"%g",mDamage - store); Con::executef(mDataBlock,3,"onDamage",scriptThis(),delta); } } } } //---------------------------------------------------------------------------- static F32 sWaterDensity = 1; static F32 sWaterViscosity = 15; static F32 sWaterCoverage = 0; static U32 sWaterType = 0; static F32 sWaterHeight = 0.0f; static void waterFind(SceneObject* obj, void* key) { ShapeBase* shape = reinterpret_cast(key); WaterBlock* wb = dynamic_cast(obj); AssertFatal(wb != NULL, "Error, not a water block!"); if (wb == NULL) { sWaterCoverage = 0; return; } if (wb->isPointSubmergedSimple(shape->getPosition())) { const Box3F& wbox = obj->getWorldBox(); const Box3F& sbox = shape->getWorldBox(); sWaterType = wb->getLiquidType(); if (wbox.max.z < sbox.max.z) sWaterCoverage = (wbox.max.z - sbox.min.z) / (sbox.max.z - sbox.min.z); else sWaterCoverage = 1; sWaterViscosity = wb->getViscosity(); sWaterDensity = wb->getDensity(); sWaterHeight = wb->getSurfaceHeight(); } } void physicalZoneFind(SceneObject* obj, void *key) { ShapeBase* shape = reinterpret_cast(key); PhysicalZone* pz = dynamic_cast(obj); AssertFatal(pz != NULL, "Error, not a physical zone!"); if (pz == NULL || pz->testObject(shape) == false) { return; } if (pz->isActive()) { shape->mGravityMod *= pz->getGravityMod(); shape->mAppliedForce += pz->getForce(); } } void findRouter(SceneObject* obj, void *key) { if (obj->getTypeMask() & WaterObjectType) waterFind(obj, key); else if (obj->getTypeMask() & PhysicalZoneObjectType) physicalZoneFind(obj, key); else { AssertFatal(false, "Error, must be either water or physical zone here!"); } } void ShapeBase::updateContainer() { // Update container drag and buoyancy properties mDrag = mDataBlock->drag; mBuoyancy = 0; sWaterCoverage = 0; mGravityMod = 1.0; mAppliedForce.set(0, 0, 0); mContainer->findObjects(getWorldBox(), WaterObjectType|PhysicalZoneObjectType,findRouter,this); sWaterCoverage = mClampF(sWaterCoverage,0,1); mWaterCoverage = sWaterCoverage; mLiquidType = sWaterType; mLiquidHeight = sWaterHeight; if (mWaterCoverage >= 0.1f) { mDrag = mDataBlock->drag * sWaterViscosity * sWaterCoverage; mBuoyancy = (sWaterDensity / mDataBlock->density) * sWaterCoverage; } } //---------------------------------------------------------------------------- void ShapeBase::applyRepair(F32 amount) { // Repair increases the repair reserve if (amount > 0 && ((mRepairReserve += amount) > mDamage)) mRepairReserve = mDamage; } void ShapeBase::applyDamage(F32 amount) { if (amount > 0) setDamageLevel(mDamage + amount); } F32 ShapeBase::getDamageValue() { // Return a 0-1 damage value. return mDamage / mDataBlock->maxDamage; } void ShapeBase::updateDamageLevel() { if (mDamageThread) { // mDamage is already 0-1 on the client if (mDamage >= mDataBlock->destroyedLevel) { if (getDamageState() == Destroyed) mShapeInstance->setPos(mDamageThread, 0); else mShapeInstance->setPos(mDamageThread, 1); } else { mShapeInstance->setPos(mDamageThread, mDamage / mDataBlock->destroyedLevel); } } } //---------------------------------------------------------------------------- void ShapeBase::setDamageState(DamageState state) { if (mDamageState == state) return; const char* script = 0; const char* lastState = 0; if (!isGhost()) { if (state != getDamageState()) setMaskBits(DamageMask); lastState = getDamageStateName(); switch (state) { case Destroyed: { if (mDamageState == Enabled) setDamageState(Disabled); script = "onDestroyed"; break; } case Disabled: if (mDamageState == Enabled) script = "onDisabled"; break; case Enabled: script = "onEnabled"; break; } } mDamageState = state; if (mDamageState != Enabled) { mRepairReserve = 0; mEnergy = 0; } if (script) { // Like to call the scripts after the state has been intialize. // This should only end up being called on the server. Con::executef(mDataBlock,3,script,scriptThis(),lastState); } updateDamageState(); updateDamageLevel(); } bool ShapeBase::setDamageState(const char* state) { for (S32 i = 0; i < NumDamageStates; i++) if (!dStricmp(state,sDamageStateName[i])) { setDamageState(DamageState(i)); return true; } return false; } const char* ShapeBase::getDamageStateName() { return sDamageStateName[mDamageState]; } void ShapeBase::updateDamageState() { if (mHulkThread) { F32 pos = (mDamageState == Destroyed)? 1: 0; if (mShapeInstance->getPos(mHulkThread) != pos) { mShapeInstance->setPos(mHulkThread,pos); if (isClientObject()) mShapeInstance->animate(); } } } //---------------------------------------------------------------------------- void ShapeBase::blowUp() { Point3F center; mObjBox.getCenter(¢er); center += getPosition(); MatrixF trans = getTransform(); trans.setPosition( center ); // explode Explosion* pExplosion = NULL; if( pointInWater( (Point3F &)center ) && mDataBlock->underwaterExplosion ) { pExplosion = new Explosion; pExplosion->onNewDataBlock(mDataBlock->underwaterExplosion); } else { if (mDataBlock->explosion) { pExplosion = new Explosion; pExplosion->onNewDataBlock(mDataBlock->explosion); } } if( pExplosion ) { pExplosion->setTransform(trans); pExplosion->setInitialState(center, damageDir); if (pExplosion->registerObject() == false) { Con::errorf(ConsoleLogEntry::General, "ShapeBase(%s)::explode: couldn't register explosion", mDataBlock->getName() ); delete pExplosion; pExplosion = NULL; } } TSShapeInstance *debShape = NULL; if( mDataBlock->debrisShape.isNull() ) { return; } else { debShape = new TSShapeInstance( mDataBlock->debrisShape, true); } Vector< TSPartInstance * > partList; TSPartInstance::breakShape( debShape, 0, partList, NULL, NULL, 0 ); if( !mDataBlock->debris ) { mDataBlock->debris = new DebrisData; } // cycle through partlist and create debris pieces for( U32 i=0; isetPartInstance( partList[i] ); debris->init( center, randomDir ); debris->onNewDataBlock( mDataBlock->debris ); debris->setTransform( trans ); if( !debris->registerObject() ) { Con::warnf( ConsoleLogEntry::General, "Could not register debris for class: %s", mDataBlock->getName() ); delete debris; debris = NULL; } else { debShape->incDebrisRefCount(); } } damageDir.set(0, 0, 1); } //---------------------------------------------------------------------------- void ShapeBase::mountObject(ShapeBase* obj,U32 node) { // if (obj->mMount.object == this) // return; if (obj->mMount.object) obj->unmount(); // Since the object is mounting to us, nothing should be colliding with it for a while obj->mConvexList->nukeList(); obj->mMount.object = this; obj->mMount.node = (node >= 0 && node < ShapeBaseData::NumMountPoints)? node: 0; obj->mMount.link = mMount.list; mMount.list = obj; if (obj != getControllingObject()) obj->processAfter(this); obj->deleteNotify(this); obj->setMaskBits(MountedMask); obj->onMount(this,node); } void ShapeBase::unmountObject(ShapeBase* obj) { if (obj->mMount.object == this) { // Find and unlink the object for(ShapeBase **ptr = & mMount.list; (*ptr); ptr = &((*ptr)->mMount.link) ) { if(*ptr == obj) { *ptr = obj->mMount.link; break; } } if (obj != getControllingObject()) obj->clearProcessAfter(); obj->clearNotify(this); obj->mMount.object = 0; obj->mMount.link = 0; obj->setMaskBits(MountedMask); obj->onUnmount(this,obj->mMount.node); } } void ShapeBase::unmount() { if (mMount.object) mMount.object->unmountObject(this); } void ShapeBase::onMount(ShapeBase* obj,S32 node) { if (!isGhost()) { char buff1[32]; dSprintf(buff1,sizeof(buff1),"%d",node); Con::executef(mDataBlock,4,"onMount",scriptThis(),obj->scriptThis(),buff1); } } void ShapeBase::onUnmount(ShapeBase* obj,S32 node) { if (!isGhost()) { char buff1[32]; dSprintf(buff1,sizeof(buff1),"%d",node); Con::executef(mDataBlock,4,"onUnmount",scriptThis(),obj->scriptThis(),buff1); } } S32 ShapeBase::getMountedObjectCount() { S32 count = 0; for (ShapeBase* itr = mMount.list; itr; itr = itr->mMount.link) count++; return count; } ShapeBase* ShapeBase::getMountedObject(S32 idx) { if (idx >= 0) { S32 count = 0; for (ShapeBase* itr = mMount.list; itr; itr = itr->mMount.link) if (count++ == idx) return itr; } return 0; } S32 ShapeBase::getMountedObjectNode(S32 idx) { if (idx >= 0) { S32 count = 0; for (ShapeBase* itr = mMount.list; itr; itr = itr->mMount.link) if (count++ == idx) return itr->mMount.node; } return -1; } ShapeBase* ShapeBase::getMountNodeObject(S32 node) { for (ShapeBase* itr = mMount.list; itr; itr = itr->mMount.link) if (itr->mMount.node == node) return itr; return 0; } Point3F ShapeBase::getAIRepairPoint() { if (mDataBlock->mountPointNode[ShapeBaseData::AIRepairNode] < 0) return Point3F(0, 0, 0); MatrixF xf(true); getMountTransform(ShapeBaseData::AIRepairNode,&xf); Point3F pos(0, 0, 0); xf.getColumn(3,&pos); return pos; } //---------------------------------------------------------------------------- void ShapeBase::getEyeTransform(MatrixF* mat) { // Returns eye to world space transform S32 eyeNode = mDataBlock->eyeNode; if (eyeNode != -1) mat->mul(getTransform(), mShapeInstance->mNodeTransforms[eyeNode]); else *mat = getTransform(); } void ShapeBase::getRenderEyeTransform(MatrixF* mat) { // Returns eye to world space transform S32 eyeNode = mDataBlock->eyeNode; if (eyeNode != -1) mat->mul(getRenderTransform(), mShapeInstance->mNodeTransforms[eyeNode]); else *mat = getRenderTransform(); } void ShapeBase::getCameraTransform(F32* pos,MatrixF* mat) { // Returns camera to world space transform // Handles first person / third person camera position if (isServerObject() && mShapeInstance) mShapeInstance->animateNodeSubtrees(true); if (*pos != 0) { F32 min,max; Point3F offset; MatrixF eye,rot; getCameraParameters(&min,&max,&offset,&rot); getRenderEyeTransform(&eye); mat->mul(eye,rot); // Use the eye transform to orient the camera VectorF vp,vec; vp.x = vp.z = 0; vp.y = -(max - min) * *pos; eye.mulV(vp,&vec); // Use the camera node's pos. Point3F osp,sp; if (mDataBlock->cameraNode != -1) { mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp); // Scale the camera position before applying the transform const Point3F& scale = getScale(); osp.convolve( scale ); getRenderTransform().mulP(osp,&sp); } else getRenderTransform().getColumn(3,&sp); // Make sure we don't extend the camera into anything solid Point3F ep = sp + vec + offset; disableCollision(); if (isMounted()) getObjectMount()->disableCollision(); RayInfo collision; if (mContainer->castRay(sp, ep, (0xFFFFFFFF & ~(WaterObjectType | GameBaseObjectType | DefaultObjectType)), &collision) == true) { F32 veclen = vec.len(); F32 adj = (-mDot(vec, collision.normal) / veclen) * 0.1; F32 newPos = getMax(0.0f, collision.t - adj); if (newPos == 0.0f) eye.getColumn(3,&ep); else ep = sp + offset + (vec * newPos); } mat->setColumn(3,ep); if (isMounted()) getObjectMount()->enableCollision(); enableCollision(); } else { getRenderEyeTransform(mat); } } // void ShapeBase::getCameraTransform(F32* pos,MatrixF* mat) // { // // Returns camera to world space transform // // Handles first person / third person camera position // if (isServerObject() && mShapeInstance) // mShapeInstance->animateNodeSubtrees(true); // if (*pos != 0) { // F32 min,max; // Point3F offset; // MatrixF eye,rot; // getCameraParameters(&min,&max,&offset,&rot); // getRenderEyeTransform(&eye); // mat->mul(eye,rot); // // Use the eye transform to orient the camera // VectorF vp,vec; // vp.x = vp.z = 0; // vp.y = -(max - min) * *pos; // eye.mulV(vp,&vec); // // Use the camera node's pos. // Point3F osp,sp; // if (mDataBlock->cameraNode != -1) { // mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp); // getRenderTransform().mulP(osp,&sp); // } // else // getRenderTransform().getColumn(3,&sp); // // Make sure we don't extend the camera into anything solid // Point3F ep = sp + vec; // ep += offset; // disableCollision(); // if (isMounted()) // getObjectMount()->disableCollision(); // RayInfo collision; // if (mContainer->castRay(sp,ep,(0xFFFFFFFF & ~(WaterObjectType|ForceFieldObjectType|GameBaseObjectType|DefaultObjectType)),&collision)) { // *pos = collision.t *= 0.9; // if (*pos == 0) // eye.getColumn(3,&ep); // else // ep = sp + vec * *pos; // } // mat->setColumn(3,ep); // if (isMounted()) // getObjectMount()->enableCollision(); // enableCollision(); // } // else // { // getRenderEyeTransform(mat); // } // } // void ShapeBase::getRenderCameraTransform(F32* pos,MatrixF* mat) // { // // Returns camera to world space transform // // Handles first person / third person camera position // if (isServerObject() && mShapeInstance) // mShapeInstance->animateNodeSubtrees(true); // if (*pos != 0) { // F32 min,max; // Point3F offset; // MatrixF eye,rot; // getCameraParameters(&min,&max,&offset,&rot); // getRenderEyeTransform(&eye); // mat->mul(eye,rot); // // Use the eye transform to orient the camera // VectorF vp,vec; // vp.x = vp.z = 0; // vp.y = -(max - min) * *pos; // eye.mulV(vp,&vec); // // Use the camera node's pos. // Point3F osp,sp; // if (mDataBlock->cameraNode != -1) { // mShapeInstance->mNodeTransforms[mDataBlock->cameraNode].getColumn(3,&osp); // getRenderTransform().mulP(osp,&sp); // } // else // getRenderTransform().getColumn(3,&sp); // // Make sure we don't extend the camera into anything solid // Point3F ep = sp + vec; // ep += offset; // disableCollision(); // if (isMounted()) // getObjectMount()->disableCollision(); // RayInfo collision; // if (mContainer->castRay(sp,ep,(0xFFFFFFFF & ~(WaterObjectType|ForceFieldObjectType|GameBaseObjectType|DefaultObjectType)),&collision)) { // *pos = collision.t *= 0.9; // if (*pos == 0) // eye.getColumn(3,&ep); // else // ep = sp + vec * *pos; // } // mat->setColumn(3,ep); // if (isMounted()) // getObjectMount()->enableCollision(); // enableCollision(); // } // else // { // getRenderEyeTransform(mat); // } // } void ShapeBase::getCameraParameters(F32 *min,F32* max,Point3F* off,MatrixF* rot) { *min = mDataBlock->cameraMinDist; *max = mDataBlock->cameraMaxDist; off->set(0,0,0); rot->identity(); } //---------------------------------------------------------------------------- F32 ShapeBase::getDamageFlash() const { return mDamageFlash; } void ShapeBase::setDamageFlash(const F32 flash) { mDamageFlash = flash; if (mDamageFlash < 0.0) mDamageFlash = 0; else if (mDamageFlash > 1.0) mDamageFlash = 1.0; } //---------------------------------------------------------------------------- F32 ShapeBase::getWhiteOut() const { return mWhiteOut; } void ShapeBase::setWhiteOut(const F32 flash) { mWhiteOut = flash; if (mWhiteOut < 0.0) mWhiteOut = 0; else if (mWhiteOut > 1.5) mWhiteOut = 1.5; } //---------------------------------------------------------------------------- bool ShapeBase::onlyFirstPerson() const { return mDataBlock->firstPersonOnly; } bool ShapeBase::useObjsEyePoint() const { return mDataBlock->useEyePoint; } //---------------------------------------------------------------------------- F32 ShapeBase::getInvincibleEffect() const { return mInvincibleEffect; } void ShapeBase::setupInvincibleEffect(F32 time, F32 speed) { if(isClientObject()) { mInvincibleCount = mInvincibleTime = time; mInvincibleSpeed = mInvincibleDelta = speed; mInvincibleEffect = 0.0f; mInvincibleOn = true; mInvincibleFade = 1.0f; } else { mInvincibleTime = time; mInvincibleSpeed = speed; setMaskBits(InvincibleMask); } } void ShapeBase::updateInvincibleEffect(F32 dt) { if(mInvincibleCount > 0.0f ) { if(mInvincibleEffect >= ((0.3 * mInvincibleFade) + 0.05f) && mInvincibleDelta > 0.0f) mInvincibleDelta = -mInvincibleSpeed; else if(mInvincibleEffect <= 0.05f && mInvincibleDelta < 0.0f) { mInvincibleDelta = mInvincibleSpeed; mInvincibleFade = mInvincibleCount / mInvincibleTime; } mInvincibleEffect += mInvincibleDelta; mInvincibleCount -= dt; } else { mInvincibleEffect = 0.0f; mInvincibleOn = false; } } //---------------------------------------------------------------------------- void ShapeBase::setVelocity(const VectorF&) { } void ShapeBase::applyImpulse(const Point3F&,const VectorF&) { } //---------------------------------------------------------------------------- void ShapeBase::playAudio(U32 slot,AudioProfile* profile) { AssertFatal(slot < MaxSoundThreads,"ShapeBase::playSound: Incorrect argument"); Sound& st = mSoundThread[slot]; if (profile && (!st.play || st.profile != profile)) { setMaskBits(SoundMaskN << slot); st.play = true; st.profile = profile; updateAudioState(st); } } void ShapeBase::stopAudio(U32 slot) { AssertFatal(slot < MaxSoundThreads,"ShapeBase::stopSound: Incorrect argument"); Sound& st = mSoundThread[slot]; if (st.play) { st.play = false; setMaskBits(SoundMaskN << slot); updateAudioState(st); } } void ShapeBase::updateServerAudio() { // Timeout non-looping sounds for (int i = 0; i < MaxSoundThreads; i++) { Sound& st = mSoundThread[i]; if (st.play && st.timeout && st.timeout < Sim::getCurrentTime()) { clearMaskBits(SoundMaskN << i); st.play = false; } } } void ShapeBase::updateAudioState(Sound& st) { if (st.sound) { alxStop(st.sound); st.sound = 0; } if (st.play && st.profile) { if (isGhost()) { if (Sim::findObject(SimObjectId(st.profile), st.profile)) st.sound = alxPlay(st.profile, &getTransform()); else st.play = false; } else { // Non-looping sounds timeout on the server st.timeout = st.profile->mDescriptionObject->mDescription.mIsLooping? 0: Sim::getCurrentTime() + sAudioTimeout; } } else st.play = false; } void ShapeBase::updateAudioPos() { for (int i = 0; i < MaxSoundThreads; i++) if (AUDIOHANDLE sh = mSoundThread[i].sound) alxSourceMatrixF(sh, &getTransform()); } //---------------------------------------------------------------------------- bool ShapeBase::setThreadSequence(U32 slot,S32 seq,bool reset) { Thread& st = mScriptThread[slot]; if (st.thread && st.sequence == seq && st.state == Thread::Play) return true; if (seq < MaxSequenceIndex) { setMaskBits(ThreadMaskN << slot); st.sequence = seq; if (reset) { st.state = Thread::Play; st.atEnd = false; st.forward = true; } if (mShapeInstance) { if (!st.thread) st.thread = mShapeInstance->addThread(); mShapeInstance->setSequence(st.thread,seq,0); stopThreadSound(st); updateThread(st); } return true; } return false; } void ShapeBase::updateThread(Thread& st) { switch (st.state) { case Thread::Stop: mShapeInstance->setTimeScale(st.thread,1); mShapeInstance->setPos(st.thread,0); // Drop through to pause state case Thread::Pause: mShapeInstance->setTimeScale(st.thread,0); stopThreadSound(st); break; case Thread::Play: if (st.atEnd) { mShapeInstance->setTimeScale(st.thread,1); mShapeInstance->setPos(st.thread,st.forward? 1: 0); mShapeInstance->setTimeScale(st.thread,0); stopThreadSound(st); } else { mShapeInstance->setTimeScale(st.thread,st.forward? 1: -1); if (!st.sound) startSequenceSound(st); } break; } } bool ShapeBase::stopThread(U32 slot) { Thread& st = mScriptThread[slot]; if (st.sequence != -1 && st.state != Thread::Stop) { setMaskBits(ThreadMaskN << slot); st.state = Thread::Stop; updateThread(st); return true; } return false; } bool ShapeBase::pauseThread(U32 slot) { Thread& st = mScriptThread[slot]; if (st.sequence != -1 && st.state != Thread::Pause) { setMaskBits(ThreadMaskN << slot); st.state = Thread::Pause; updateThread(st); return true; } return false; } bool ShapeBase::playThread(U32 slot) { Thread& st = mScriptThread[slot]; if (st.sequence != -1 && st.state != Thread::Play) { setMaskBits(ThreadMaskN << slot); st.state = Thread::Play; updateThread(st); return true; } return false; } bool ShapeBase::setThreadDir(U32 slot,bool forward) { Thread& st = mScriptThread[slot]; if (st.sequence != -1) { if (st.forward != forward) { setMaskBits(ThreadMaskN << slot); st.forward = forward; st.atEnd = false; updateThread(st); } return true; } return false; } void ShapeBase::stopThreadSound(Thread& thread) { if (thread.sound) { } } void ShapeBase::startSequenceSound(Thread& thread) { if (!isGhost() || !thread.thread) return; stopThreadSound(thread); } void ShapeBase::advanceThreads(F32 dt) { for (U32 i = 0; i < MaxScriptThreads; i++) { Thread& st = mScriptThread[i]; if (st.thread) { if (!mShapeInstance->getShape()->sequences[st.sequence].isCyclic() && !st.atEnd && (st.forward? mShapeInstance->getPos(st.thread) >= 1.0: mShapeInstance->getPos(st.thread) <= 0)) { st.atEnd = true; updateThread(st); if (!isGhost()) { char slot[16]; dSprintf(slot,sizeof(slot),"%d",i); Con::executef(mDataBlock,3,"onEndSequence",scriptThis(),slot); } } mShapeInstance->advanceTime(dt,st.thread); } } } //---------------------------------------------------------------------------- TSShape const* ShapeBase::getShape() { return mShapeInstance? mShapeInstance->getShape(): 0; } void ShapeBase::calcClassRenderData() { // This is truly lame, but I didn't want to duplicate the whole preprender logic // in the player as well as the renderImage logic. DMM } bool ShapeBase::prepRenderImage(SceneState* state, const U32 stateKey, const U32 startZone, const bool modifyBaseState) { AssertFatal(modifyBaseState == false, "Error, should never be called with this parameter set"); AssertFatal(startZone == 0xFFFFFFFF, "Error, startZone should indicate -1"); if (isLastState(state, stateKey)) return false; setLastState(state, stateKey); if( ( getDamageState() == Destroyed ) && ( !mDataBlock->renderWhenDestroyed ) ) return false; // Select detail levels on mounted items // but... always draw the control object's mounted images // in high detail (I can't believe I'm commenting this hack :) F32 saveError = TSShapeInstance::smScreenError; GameConnection *con = GameConnection::getConnectionToServer(); bool fogExemption = false; ShapeBase *co = NULL; if(con && ( (co = con->getControlObject()) != NULL) ) { if(co == this || co->getObjectMount() == this) { TSShapeInstance::smScreenError = 0.001; fogExemption = true; } } if (state->isObjectRendered(this)) { mLastRenderFrame = sLastRenderFrame; // get shape detail and fog information...we might not even need to be drawn Point3F cameraOffset; getRenderTransform().getColumn(3,&cameraOffset); cameraOffset -= state->getCameraPosition(); F32 dist = cameraOffset.len(); if (dist < 0.01) dist = 0.01; F32 fogAmount = state->getHazeAndFog(dist,cameraOffset.z); F32 invScale = (1.0f/getMax(getMax(mObjScale.x,mObjScale.y),mObjScale.z)); if (mShapeInstance) DetailManager::selectPotentialDetails(mShapeInstance,dist,invScale); if (mShapeInstance) mShapeInstance->animate(); if ((fogAmount>0.99f && fogExemption == false) || (mShapeInstance && mShapeInstance->getCurrentDetail()<0) || (!mShapeInstance && !gShowBoundingBox)) { // no, don't draw anything return false; } for (U32 i = 0; i < MaxMountedImages; i++) { MountedImage& image = mMountedImageList[i]; if (image.dataBlock && image.shapeInstance) { DetailManager::selectPotentialDetails(image.shapeInstance,dist,invScale); if (mCloakLevel == 0.0f && image.shapeInstance->hasSolid() && mFadeVal == 1.0f) { ShapeImageRenderImage* rimage = new ShapeImageRenderImage; rimage->obj = this; rimage->mSBase = this; rimage->mIndex = i; rimage->isTranslucent = false; rimage->textureSortKey = (U32)(dsize_t)(image.dataBlock); state->insertRenderImage(rimage); } if ((mCloakLevel != 0.0f || mFadeVal != 1.0f || mShapeInstance->hasTranslucency()) || (mMount.object == NULL)) { ShapeImageRenderImage* rimage = new ShapeImageRenderImage; rimage->obj = this; rimage->mSBase = this; rimage->mIndex = i; rimage->isTranslucent = true; rimage->sortType = SceneRenderImage::Point; rimage->textureSortKey = (U32)(dsize_t)(image.dataBlock); state->setImageRefPoint(this, rimage); state->insertRenderImage(rimage); } } } TSShapeInstance::smScreenError = saveError; if (mCloakLevel == 0.0f && mShapeInstance->hasSolid() && mFadeVal == 1.0f) { SceneRenderImage* image = new SceneRenderImage; image->obj = this; image->isTranslucent = false; image->textureSortKey = mSkinHash ^ (U32)(dsize_t)(mDataBlock); state->insertRenderImage(image); } if ((mCloakLevel != 0.0f || mFadeVal != 1.0f || mShapeInstance->hasTranslucency()) || (mMount.object == NULL)) { SceneRenderImage* image = new SceneRenderImage; image->obj = this; image->isTranslucent = true; image->sortType = SceneRenderImage::Point; image->textureSortKey = mSkinHash ^ (U32)(dsize_t)(mDataBlock); state->setImageRefPoint(this, image); state->insertRenderImage(image); } calcClassRenderData(); } return false; } void ShapeBase::renderObject(SceneState* state, SceneRenderImage* image) { PROFILE_START(ShapeBaseRenderObject); AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on entry"); RectI viewport; dglGetViewport(&viewport); gClientSceneGraph->getLightManager()->sgSetupLights(this); glMatrixMode(GL_PROJECTION); glPushMatrix(); state->setupObjectProjection(this); // This is something of a hack, but since the 3space objects don't have a // clear conception of texels/meter like the interiors do, we're sorta // stuck. I can't even claim this is anything more scientific than eyeball // work. DMM F32 axis = (getObjBox().len_x() + getObjBox().len_y() + getObjBox().len_z()) / 3.0; F32 dist = (getRenderWorldBox().getClosestPoint(state->getCameraPosition()) - state->getCameraPosition()).len(); if (dist != 0) { F32 projected = dglProjectRadius(dist, axis) / 25; if (projected < (1.0 / 16.0)) { TextureManager::setSmallTexturesActive(true); } } // render shield effect if (mCloakLevel == 0.0f && mFadeVal == 1.0f) { if (image->isTranslucent == true) { TSShapeInstance::smNoRenderNonTranslucent = true; TSShapeInstance::smNoRenderTranslucent = false; } else { TSShapeInstance::smNoRenderNonTranslucent = false; TSShapeInstance::smNoRenderTranslucent = true; } } else { TSShapeInstance::smNoRenderNonTranslucent = false; TSShapeInstance::smNoRenderTranslucent = false; } TSMesh::setOverrideFade( mFadeVal ); ShapeImageRenderImage* shiri = dynamic_cast(image); if (shiri != NULL) { renderMountedImage(state, shiri); } else { renderImage(state, image); } TSMesh::setOverrideFade( 1.0 ); TSShapeInstance::smNoRenderNonTranslucent = false; TSShapeInstance::smNoRenderTranslucent = false; glMatrixMode(GL_PROJECTION); glPopMatrix(); glMatrixMode(GL_MODELVIEW); dglSetViewport(viewport); gClientSceneGraph->getLightManager()->sgResetLights(); // Debugging Bounding Box if (!mShapeInstance || gShowBoundingBox) { glDisable(GL_DEPTH_TEST); Point3F box; glPushMatrix(); dglMultMatrix(&getRenderTransform()); box = (mObjBox.min + mObjBox.max) * 0.5; glTranslatef(box.x,box.y,box.z); box = (mObjBox.max - mObjBox.min) * 0.5; glScalef(box.x,box.y,box.z); glColor3f(1, 0, 1); wireCube(Point3F(1,1,1),Point3F(0,0,0)); glPopMatrix(); glPushMatrix(); box = (mWorldBox.min + mWorldBox.max) * 0.5; glTranslatef(box.x,box.y,box.z); box = (mWorldBox.max - mWorldBox.min) * 0.5; glScalef(box.x,box.y,box.z); glColor3f(0, 1, 1); wireCube(Point3F(1,1,1),Point3F(0,0,0)); glPopMatrix(); for (U32 i = 0; i < MaxMountedImages; i++) { MountedImage& image = mMountedImageList[i]; if (image.dataBlock && image.shapeInstance) { MatrixF mat; glPushMatrix(); getRenderImageTransform(i,&mat); dglMultMatrix(&mat); glColor3f(1, 0, 1); wireCube(Point3F(0.05,0.05,0.05),Point3F(0,0,0)); glPopMatrix(); glPushMatrix(); getRenderMountTransform(i,&mat); dglMultMatrix(&mat); glColor3f(1, 0, 1); wireCube(Point3F(0.05,0.05,0.05),Point3F(0,0,0)); glPopMatrix(); glPushMatrix(); getRenderMuzzleTransform(i,&mat); dglMultMatrix(&mat); glColor3f(1, 0, 1); wireCube(Point3F(0.05,0.05,0.05),Point3F(0,0,0)); glPopMatrix(); } } glEnable(GL_DEPTH_TEST); } dglSetCanonicalState(); TextureManager::setSmallTexturesActive(false); AssertFatal(dglIsInCanonicalState(), "Error, GL not in canonical state on exit"); PROFILE_END(); } void ShapeBase::renderShadow(F32 dist, F32 fogAmount) { if(!mDataBlock->shadowEnable) return; shadows.sgRender(this, mShapeInstance, dist, fogAmount, mDataBlock->genericShadowLevel, mDataBlock->noShadowLevel, mDataBlock->shadowNode, mDataBlock->shadowCanMove, mDataBlock->shadowCanAnimate); } void ShapeBase::renderMountedImage(SceneState* state, ShapeImageRenderImage* rimage) { AssertFatal(rimage->mSBase == this, "Error, wrong image"); Point3F cameraOffset; getRenderTransform().getColumn(3,&cameraOffset); cameraOffset -= state->getCameraPosition(); F32 dist = cameraOffset.len(); F32 fogAmount = state->getHazeAndFog(dist,cameraOffset.z); // Mounted items PROFILE_START(ShapeBaseRenderMounted); MountedImage& image = mMountedImageList[rimage->mIndex]; if (image.dataBlock && image.shapeInstance && DetailManager::selectCurrentDetail(image.shapeInstance)) { MatrixF mat; getRenderImageTransform(rimage->mIndex, &mat); glPushMatrix(); dglMultMatrix(&mat); if (image.dataBlock->cloakable && mCloakLevel != 0.0) image.shapeInstance->setAlphaAlways(0.15 + (1 - mCloakLevel) * 0.85); else image.shapeInstance->setAlphaAlways(1.0); if (mCloakLevel == 0.0 && (image.dataBlock->emap && gRenderEnvMaps) && state->getEnvironmentMap().getGLName() != 0) { image.shapeInstance->setEnvironmentMap(state->getEnvironmentMap()); image.shapeInstance->setEnvironmentMapOn(true, 1.0); } else { image.shapeInstance->setEnvironmentMapOn(false, 1.0); } image.shapeInstance->setupFog(fogAmount,state->getFogColor()); image.shapeInstance->animate(); image.shapeInstance->render(); // easiest just to shut it off here. If we're cloaked on the next frame, // we don't want envmaps... image.shapeInstance->setEnvironmentMapOn(false, 1.0); glPopMatrix(); } PROFILE_END(); } void ShapeBase::renderImage(SceneState* state, SceneRenderImage* image) { glMatrixMode(GL_MODELVIEW); // Base shape F32 fogAmount = 0.0f; F32 dist = 0.0f; PROFILE_START(ShapeBaseRenderPrimary); if (mShapeInstance && DetailManager::selectCurrentDetail(mShapeInstance)) { glPushMatrix(); dglMultMatrix(&getRenderTransform()); glScalef(mObjScale.x,mObjScale.y,mObjScale.z); if (mCloakLevel != 0.0) { glMatrixMode(GL_TEXTURE); glPushMatrix(); static U32 shiftX = 0; static U32 shiftY = 0; shiftX = (shiftX + 1) % 128; shiftY = (shiftY + 1) % 127; glTranslatef(F32(shiftX) / 127.0, F32(shiftY)/126.0, 0); glMatrixMode(GL_MODELVIEW); mShapeInstance->setAlphaAlways(0.125 + (1 - mCloakLevel) * 0.875); mShapeInstance->setOverrideTexture(mCloakTexture); } else { mShapeInstance->setAlphaAlways(1.0); } if (mCloakLevel == 0.0 && (mDataBlock->emap && gRenderEnvMaps) && state->getEnvironmentMap().getGLName() != 0) { mShapeInstance->setEnvironmentMap(state->getEnvironmentMap()); mShapeInstance->setEnvironmentMapOn(true, 1.0); } else { mShapeInstance->setEnvironmentMapOn(false, 1.0); } Point3F cameraOffset; getRenderTransform().getColumn(3,&cameraOffset); cameraOffset -= state->getCameraPosition(); dist = cameraOffset.len(); fogAmount = state->getHazeAndFog(dist,cameraOffset.z); mShapeInstance->setupFog(fogAmount,state->getFogColor()); mShapeInstance->animate(); mShapeInstance->render(); mShapeInstance->setEnvironmentMapOn(false, 1.0); if (mCloakLevel != 0.0) { glMatrixMode(GL_TEXTURE); glPopMatrix(); mShapeInstance->clearOverrideTexture(); } glMatrixMode(GL_MODELVIEW); glPopMatrix(); } PROFILE_END(); PROFILE_START(ShapeBaseRenderShadow); // Shadow... if (mShapeInstance && mCloakLevel == 0.0 && mMount.object == NULL && image->isTranslucent == true) { // we are shadow enabled... renderShadow(dist,fogAmount); } PROFILE_END(); } //---------------------------------------------------------------------------- static ColorF cubeColors[8] = { ColorF(0, 0, 0), ColorF(1, 0, 0), ColorF(0, 1, 0), ColorF(0, 0, 1), ColorF(1, 1, 0), ColorF(1, 0, 1), ColorF(0, 1, 1), ColorF(1, 1, 1) }; static Point3F cubePoints[8] = { Point3F(-1, -1, -1), Point3F(-1, -1, 1), Point3F(-1, 1, -1), Point3F(-1, 1, 1), Point3F( 1, -1, -1), Point3F( 1, -1, 1), Point3F( 1, 1, -1), Point3F( 1, 1, 1) }; static U32 cubeFaces[6][4] = { { 0, 2, 6, 4 }, { 0, 2, 3, 1 }, { 0, 1, 5, 4 }, { 3, 2, 6, 7 }, { 7, 6, 4, 5 }, { 3, 7, 5, 1 } }; void ShapeBase::wireCube(const Point3F& size, const Point3F& pos) { glDisable(GL_CULL_FACE); for(int i = 0; i < 6; i++) { glBegin(GL_LINE_LOOP); for(int vert = 0; vert < 4; vert++) { int idx = cubeFaces[i][vert]; glVertex3f(cubePoints[idx].x * size.x + pos.x, cubePoints[idx].y * size.y + pos.y, cubePoints[idx].z * size.z + pos.z); } glEnd(); } } //---------------------------------------------------------------------------- bool ShapeBase::castRay(const Point3F &start, const Point3F &end, RayInfo* info) { if (mShapeInstance) { RayInfo shortest; shortest.t = 1e8; info->object = NULL; for (U32 i = 0; i < mDataBlock->LOSDetails.size(); i++) { mShapeInstance->animate(mDataBlock->LOSDetails[i]); if (mShapeInstance->castRay(start, end, info, mDataBlock->LOSDetails[i])) { info->object = this; if (info->t < shortest.t) shortest = *info; } } if (info->object == this) { // Copy out the shortest time... *info = shortest; return true; } } return false; } //---------------------------------------------------------------------------- bool ShapeBase::buildPolyList(AbstractPolyList* polyList, const Box3F &, const SphereF &) { if (mShapeInstance) { bool ret = false; polyList->setTransform(&mObjToWorld, mObjScale); polyList->setObject(this); for (U32 i = 0; i < mDataBlock->collisionDetails.size(); i++) { mShapeInstance->buildPolyList(polyList,mDataBlock->collisionDetails[i]); ret = true; } return ret; } return false; } void ShapeBase::buildConvex(const Box3F& box, Convex* convex) { if (mShapeInstance == NULL) return; // These should really come out of a pool mConvexList->collectGarbage(); Box3F realBox = box; mWorldToObj.mul(realBox); realBox.min.convolveInverse(mObjScale); realBox.max.convolveInverse(mObjScale); if (realBox.isOverlapped(getObjBox()) == false) return; for (U32 i = 0; i < mDataBlock->collisionDetails.size(); i++) { Box3F newbox = mDataBlock->collisionBounds[i]; newbox.min.convolve(mObjScale); newbox.max.convolve(mObjScale); mObjToWorld.mul(newbox); if (box.isOverlapped(newbox) == false) continue; // See if this hull exists in the working set already... Convex* cc = 0; CollisionWorkingList& wl = convex->getWorkingList(); for (CollisionWorkingList* itr = wl.wLink.mNext; itr != &wl; itr = itr->wLink.mNext) { if (itr->mConvex->getType() == ShapeBaseConvexType && (static_cast(itr->mConvex)->pShapeBase == this && static_cast(itr->mConvex)->hullId == i)) { cc = itr->mConvex; break; } } if (cc) continue; // Create a new convex. ShapeBaseConvex* cp = new ShapeBaseConvex; mConvexList->registerObject(cp); convex->addToWorkingList(cp); cp->mObject = this; cp->pShapeBase = this; cp->hullId = i; cp->box = mDataBlock->collisionBounds[i]; cp->transform = 0; cp->findNodeTransform(); } } //---------------------------------------------------------------------------- void ShapeBase::queueCollision(ShapeBase* obj, const VectorF& vec) { // Add object to list of collisions. SimTime time = Sim::getCurrentTime(); S32 num = obj->getId(); CollisionTimeout** adr = &mTimeoutList; CollisionTimeout* ptr = mTimeoutList; while (ptr) { if (ptr->objectNumber == num) { if (ptr->expireTime < time) { ptr->expireTime = time + CollisionTimeoutValue; ptr->object = obj; ptr->vector = vec; } return; } // Recover expired entries if (ptr->expireTime < time) { CollisionTimeout* cur = ptr; *adr = ptr->next; ptr = ptr->next; cur->next = sFreeTimeoutList; sFreeTimeoutList = cur; } else { adr = &ptr->next; ptr = ptr->next; } } // New entry for the object if (sFreeTimeoutList != NULL) { ptr = sFreeTimeoutList; sFreeTimeoutList = ptr->next; ptr->next = NULL; } else { ptr = sTimeoutChunker.alloc(); } ptr->object = obj; ptr->objectNumber = obj->getId(); ptr->vector = vec; ptr->expireTime = time + CollisionTimeoutValue; ptr->next = mTimeoutList; mTimeoutList = ptr; } void ShapeBase::notifyCollision() { // Notify all the objects that were just stamped during the queueing // process. SimTime expireTime = Sim::getCurrentTime() + CollisionTimeoutValue; for (CollisionTimeout* ptr = mTimeoutList; ptr; ptr = ptr->next) { if (ptr->expireTime == expireTime && ptr->object) { SimObjectPtr safePtr(ptr->object); SimObjectPtr safeThis(this); onCollision(ptr->object,ptr->vector); ptr->object = 0; if(!bool(safeThis)) return; if(bool(safePtr)) safePtr->onCollision(this,ptr->vector); if(!bool(safeThis)) return; } } } void ShapeBase::onCollision(ShapeBase* object,VectorF vec) { if (!isGhost()) { char buff1[256]; char buff2[32]; dSprintf(buff1,sizeof(buff1),"%g %g %g",vec.x, vec.y, vec.z); dSprintf(buff2,sizeof(buff2),"%g",vec.len()); Con::executef(mDataBlock,5,"onCollision",scriptThis(),object->scriptThis(), buff1, buff2); } } //-------------------------------------------------------------------------- bool ShapeBase::pointInWater( Point3F &point ) { SimpleQueryList sql; if (isServerObject()) gServerSceneGraph->getWaterObjectList(sql); else gClientSceneGraph->getWaterObjectList(sql); for (U32 i = 0; i < sql.mList.size(); i++) { WaterBlock* pBlock = dynamic_cast(sql.mList[i]); if (pBlock && pBlock->isPointSubmergedSimple( point )) return true; } return false; } //---------------------------------------------------------------------------- void ShapeBase::writePacketData(GameConnection *connection, BitStream *stream) { Parent::writePacketData(connection, stream); stream->write(getEnergyLevel()); stream->write(mRechargeRate); } void ShapeBase::readPacketData(GameConnection *connection, BitStream *stream) { Parent::readPacketData(connection, stream); F32 energy; stream->read(&energy); setEnergyLevel(energy); stream->read(&mRechargeRate); } U32 ShapeBase::getPacketDataChecksum(GameConnection *connection) { // just write the packet data into a buffer // then we can CRC the buffer. This should always let us // know when there is a checksum problem. static U8 buffer[1500] = { 0, }; BitStream stream(buffer, sizeof(buffer)); writePacketData(connection, &stream); U32 byteCount = stream.getPosition(); U32 ret = calculateCRC(buffer, byteCount, 0xFFFFFFFF); dMemset(buffer, 0, byteCount); return ret; } F32 ShapeBase::getUpdatePriority(CameraScopeQuery *camInfo, U32 updateMask, S32 updateSkips) { // If it's the scope object, must be high priority if (camInfo->camera == this) { // Most priorities are between 0 and 1, so this // should be something larger. return 10.0f; } if (camInfo->camera && (camInfo->camera->getType() & ShapeBaseObjectType)) { // see if the camera is mounted to this... // if it is, this should have a high priority if(((ShapeBase *) camInfo->camera)->getObjectMount() == this) return 10.0f; } return Parent::getUpdatePriority(camInfo, updateMask, updateSkips); } U32 ShapeBase::packUpdate(NetConnection *con, U32 mask, BitStream *stream) { U32 retMask = Parent::packUpdate(con, mask, stream); if (mask & InitialUpdateMask) { // mask off sounds that aren't playing S32 i; for (i = 0; i < MaxSoundThreads; i++) if (!mSoundThread[i].play) mask &= ~(SoundMaskN << i); // mask off threads that aren't running for (i = 0; i < MaxScriptThreads; i++) if (mScriptThread[i].sequence == -1) mask &= ~(ThreadMaskN << i); // mask off images that aren't updated for(i = 0; i < MaxMountedImages; i++) if(!mMountedImageList[i].dataBlock) mask &= ~(ImageMaskN << i); } if(!stream->writeFlag(mask & (NameMask | DamageMask | SoundMask | ThreadMask | ImageMask | CloakMask | MountedMask | InvincibleMask | ShieldMask | SkinMask))) return retMask; if (stream->writeFlag(mask & DamageMask)) { stream->writeFloat(mClampF(mDamage / mDataBlock->maxDamage, 0.f, 1.f), DamageLevelBits); stream->writeInt(mDamageState,NumDamageStateBits); stream->writeNormalVector( damageDir, 8 ); } if (stream->writeFlag(mask & ThreadMask)) { for (int i = 0; i < MaxScriptThreads; i++) { Thread& st = mScriptThread[i]; if (stream->writeFlag(st.sequence != -1 && (mask & (ThreadMaskN << i)))) { stream->writeInt(st.sequence,ThreadSequenceBits); stream->writeInt(st.state,2); stream->writeFlag(st.forward); stream->writeFlag(st.atEnd); } } } if (stream->writeFlag(mask & SoundMask)) { for (int i = 0; i < MaxSoundThreads; i++) { Sound& st = mSoundThread[i]; if (stream->writeFlag(mask & (SoundMaskN << i))) if (stream->writeFlag(st.play)) stream->writeRangedU32(st.profile->getId(),DataBlockObjectIdFirst, DataBlockObjectIdLast); } } if (stream->writeFlag(mask & ImageMask)) { for (int i = 0; i < MaxMountedImages; i++) if (stream->writeFlag(mask & (ImageMaskN << i))) { MountedImage& image = mMountedImageList[i]; if (stream->writeFlag(image.dataBlock)) stream->writeInt(image.dataBlock->getId() - DataBlockObjectIdFirst, DataBlockObjectIdBitSize); con->packStringHandleU(stream, image.skinNameHandle); stream->writeFlag(image.wet); stream->writeFlag(image.ammo); stream->writeFlag(image.loaded); stream->writeFlag(image.target); stream->writeFlag(image.triggerDown); stream->writeInt(image.fireCount,3); if (mask & InitialUpdateMask) stream->writeFlag(isImageFiring(i)); } } // Group some of the uncommon stuff together. if (stream->writeFlag(mask & (NameMask | ShieldMask | CloakMask | InvincibleMask | SkinMask))) { if (stream->writeFlag(mask & CloakMask)) { // cloaking stream->writeFlag( mCloaked ); // piggyback control update stream->writeFlag(bool(getControllingClient())); // fading if(stream->writeFlag(mFading && mFadeElapsedTime >= mFadeDelay)) { stream->writeFlag(mFadeOut); stream->write(mFadeTime); } else stream->writeFlag(mFadeVal == 1.0f); } if (stream->writeFlag(mask & NameMask)) { con->packStringHandleU(stream, mShapeNameHandle); } if (stream->writeFlag(mask & ShieldMask)) { stream->writeNormalVector(mShieldNormal, ShieldNormalBits); stream->writeFloat( getEnergyValue(), EnergyLevelBits ); } if (stream->writeFlag(mask & InvincibleMask)) { stream->write(mInvincibleTime); stream->write(mInvincibleSpeed); } if (stream->writeFlag(mask & SkinMask)) { con->packStringHandleU(stream, mSkinNameHandle); } } if (mask & MountedMask) { if (mMount.object) { S32 gIndex = con->getGhostIndex(mMount.object); if (stream->writeFlag(gIndex != -1)) { stream->writeFlag(true); stream->writeInt(gIndex,NetConnection::GhostIdBitSize); stream->writeInt(mMount.node,ShapeBaseData::NumMountPointBits); } else // Will have to try again later retMask |= MountedMask; } else // Unmount if this isn't the initial packet if (stream->writeFlag(!(mask & InitialUpdateMask))) stream->writeFlag(false); } else stream->writeFlag(false); return retMask; } void ShapeBase::unpackUpdate(NetConnection *con, BitStream *stream) { Parent::unpackUpdate(con, stream); mLastRenderFrame = sLastRenderFrame; // make sure we get a process after the event... if(!stream->readFlag()) return; if (stream->readFlag()) { mDamage = mClampF(stream->readFloat(DamageLevelBits) * mDataBlock->maxDamage, 0.f, mDataBlock->maxDamage); DamageState prevState = mDamageState; mDamageState = DamageState(stream->readInt(NumDamageStateBits)); stream->readNormalVector( &damageDir, 8 ); if (prevState != Destroyed && mDamageState == Destroyed && isProperlyAdded()) blowUp(); updateDamageLevel(); updateDamageState(); } if (stream->readFlag()) { for (S32 i = 0; i < MaxScriptThreads; i++) { if (stream->readFlag()) { Thread& st = mScriptThread[i]; U32 seq = stream->readInt(ThreadSequenceBits); st.state = stream->readInt(2); st.forward = stream->readFlag(); st.atEnd = stream->readFlag(); if (st.sequence != seq) setThreadSequence(i,seq,false); else updateThread(st); } } } if (stream->readFlag()) { for (S32 i = 0; i < MaxSoundThreads; i++) { if (stream->readFlag()) { Sound& st = mSoundThread[i]; if ((st.play = stream->readFlag()) == true) { st.profile = (AudioProfile*) stream->readRangedU32(DataBlockObjectIdFirst, DataBlockObjectIdLast); } if (isProperlyAdded()) updateAudioState(st); } } } if (stream->readFlag()) { for (int i = 0; i < MaxMountedImages; i++) { if (stream->readFlag()) { MountedImage& image = mMountedImageList[i]; ShapeBaseImageData* imageData = 0; if (stream->readFlag()) { SimObjectId id = stream->readInt(DataBlockObjectIdBitSize) + DataBlockObjectIdFirst; if (!Sim::findObject(id,imageData)) { con->setLastError("Invalid packet (mounted images)."); return; } } StringHandle skinDesiredNameHandle = con->unpackStringHandleU(stream); image.wet = stream->readFlag(); image.ammo = stream->readFlag(); image.loaded = stream->readFlag(); image.target = stream->readFlag(); image.triggerDown = stream->readFlag(); int count = stream->readInt(3); if ((image.dataBlock != imageData) || (image.skinNameHandle != skinDesiredNameHandle)) { setImage(i, imageData, skinDesiredNameHandle, image.loaded, image.ammo, image.triggerDown); } if (isProperlyAdded()) { // Normal processing if (count != image.fireCount) { image.fireCount = count; setImageState(i,getImageFireState(i),true); if( imageData && imageData->lightType == ShapeBaseImageData::WeaponFireLight ) { mLightTime = Sim::getCurrentTime(); } } updateImageState(i,0); } else { bool firing = stream->readFlag(); if(imageData) { // Initial state image.fireCount = count; if (firing) setImageState(i,getImageFireState(i),true); } } } } } if (stream->readFlag()) { if(stream->readFlag()) // Cloaked and control { setCloakedState(stream->readFlag()); mIsControlled = stream->readFlag(); if (( mFading = stream->readFlag()) == true) { mFadeOut = stream->readFlag(); if(mFadeOut) mFadeVal = 1.0f; else mFadeVal = 0; stream->read(&mFadeTime); mFadeDelay = 0; mFadeElapsedTime = 0; } else mFadeVal = F32(stream->readFlag()); } if (stream->readFlag()) { // NameMask mShapeNameHandle = con->unpackStringHandleU(stream); } if(stream->readFlag()) // ShieldMask { // Cloaking, Shield, and invul masking Point3F shieldNormal; stream->readNormalVector(&shieldNormal, ShieldNormalBits); F32 energyPercent = stream->readFloat(EnergyLevelBits); } if (stream->readFlag()) { // InvincibleMask F32 time, speed; stream->read(&time); stream->read(&speed); setupInvincibleEffect(time, speed); } if (stream->readFlag()) { // SkinMask StringHandle skinDesiredNameHandle = con->unpackStringHandleU(stream);; if (mSkinNameHandle != skinDesiredNameHandle) { mSkinNameHandle = skinDesiredNameHandle; if (mShapeInstance) { mShapeInstance->reSkin(mSkinNameHandle); if (mSkinNameHandle.isValidString()) { mSkinHash = _StringTable::hashString(mSkinNameHandle.getString()); } } } } } if (stream->readFlag()) { if (stream->readFlag()) { S32 gIndex = stream->readInt(NetConnection::GhostIdBitSize); ShapeBase* obj = dynamic_cast(con->resolveGhost(gIndex)); S32 node = stream->readInt(ShapeBaseData::NumMountPointBits); if(!obj) { con->setLastError("Invalid packet from server."); return; } obj->mountObject(this,node); } else unmount(); } } //-------------------------------------------------------------------------- void ShapeBase::forceUncloak(const char * reason) { AssertFatal(isServerObject(), "ShapeBase::forceUncloak: server only call"); if(!mCloaked) return; Con::executef(mDataBlock, 3, "onForceUncloak", scriptThis(), reason ? reason : ""); } void ShapeBase::setCloakedState(bool cloaked) { if (cloaked == mCloaked) return; if (isServerObject()) setMaskBits(CloakMask); // Have to do this for the client, if we are ghosted over in the initial // packet as cloaked, we set the state immediately to the extreme if (isProperlyAdded() == false) { mCloaked = cloaked; if (mCloaked) mCloakLevel = 1.0; else mCloakLevel = 0.0; } else { mCloaked = cloaked; } } //-------------------------------------------------------------------------- void ShapeBase::setHidden(bool hidden) { if (hidden != mHidden) { // need to set a mask bit to make the ghost manager delete copies of this object // hacky, but oh well. setMaskBits(CloakMask); if (mHidden) addToScene(); else removeFromScene(); mHidden = hidden; } } //-------------------------------------------------------------------------- void ShapeBaseConvex::findNodeTransform() { S32 dl = pShapeBase->mDataBlock->collisionDetails[hullId]; TSShapeInstance* si = pShapeBase->getShapeInstance(); TSShape* shape = si->getShape(); const TSShape::Detail* detail = &shape->details[dl]; const S32 subs = detail->subShapeNum; const S32 start = shape->subShapeFirstObject[subs]; const S32 end = start + shape->subShapeNumObjects[subs]; // Find the first object that contains a mesh for this // detail level. There should only be one mesh per // collision detail level. for (S32 i = start; i < end; i++) { const TSShape::Object* obj = &shape->objects[i]; if (obj->numMeshes && detail->objectDetailNum < obj->numMeshes) { nodeTransform = &si->mNodeTransforms[obj->nodeIndex]; return; } } return; } const MatrixF& ShapeBaseConvex::getTransform() const { // If the transform isn't specified, it's assumed to be the // origin of the shape. const MatrixF& omat = (transform != 0)? *transform: mObject->getTransform(); // Multiply on the mesh shape offset // tg: Returning this static here is not really a good idea, but // all this Convex code needs to be re-organized. if (nodeTransform) { static MatrixF mat; mat.mul(omat,*nodeTransform); return mat; } return omat; } Box3F ShapeBaseConvex::getBoundingBox() const { const MatrixF& omat = (transform != 0)? *transform: mObject->getTransform(); return getBoundingBox(omat, mObject->getScale()); } Box3F ShapeBaseConvex::getBoundingBox(const MatrixF& mat, const Point3F& scale) const { Box3F newBox = box; newBox.min.convolve(scale); newBox.max.convolve(scale); mat.mul(newBox); return newBox; } Point3F ShapeBaseConvex::support(const VectorF& v) const { TSShape::ConvexHullAccelerator* pAccel = pShapeBase->mShapeInstance->getShape()->getAccelerator(pShapeBase->mDataBlock->collisionDetails[hullId]); AssertFatal(pAccel != NULL, "Error, no accel!"); F32 currMaxDP = mDot(pAccel->vertexList[0], v); U32 index = 0; for (U32 i = 1; i < pAccel->numVerts; i++) { F32 dp = mDot(pAccel->vertexList[i], v); if (dp > currMaxDP) { currMaxDP = dp; index = i; } } return pAccel->vertexList[index]; } void ShapeBaseConvex::getFeatures(const MatrixF& mat, const VectorF& n, ConvexFeature* cf) { cf->material = 0; cf->object = mObject; TSShape::ConvexHullAccelerator* pAccel = pShapeBase->mShapeInstance->getShape()->getAccelerator(pShapeBase->mDataBlock->collisionDetails[hullId]); AssertFatal(pAccel != NULL, "Error, no accel!"); F32 currMaxDP = mDot(pAccel->vertexList[0], n); U32 index = 0; U32 i; for (i = 1; i < pAccel->numVerts; i++) { F32 dp = mDot(pAccel->vertexList[i], n); if (dp > currMaxDP) { currMaxDP = dp; index = i; } } const U8* emitString = pAccel->emitStrings[index]; U32 currPos = 0; U32 numVerts = emitString[currPos++]; for (i = 0; i < numVerts; i++) { cf->mVertexList.increment(); U32 index = emitString[currPos++]; mat.mulP(pAccel->vertexList[index], &cf->mVertexList.last()); } U32 numEdges = emitString[currPos++]; for (i = 0; i < numEdges; i++) { U32 ev0 = emitString[currPos++]; U32 ev1 = emitString[currPos++]; cf->mEdgeList.increment(); cf->mEdgeList.last().vertex[0] = ev0; cf->mEdgeList.last().vertex[1] = ev1; } U32 numFaces = emitString[currPos++]; for (i = 0; i < numFaces; i++) { cf->mFaceList.increment(); U32 plane = emitString[currPos++]; mat.mulV(pAccel->normalList[plane], &cf->mFaceList.last().normal); for (U32 j = 0; j < 3; j++) cf->mFaceList.last().vertex[j] = emitString[currPos++]; } } void ShapeBaseConvex::getPolyList(AbstractPolyList* list) { list->setTransform(&pShapeBase->getTransform(), pShapeBase->getScale()); list->setObject(pShapeBase); pShapeBase->mShapeInstance->animate(pShapeBase->mDataBlock->collisionDetails[hullId]); pShapeBase->mShapeInstance->buildPolyList(list,pShapeBase->mDataBlock->collisionDetails[hullId]); } //-------------------------------------------------------------------------- bool ShapeBase::isInvincible() { if( mDataBlock ) { return mDataBlock->isInvincible; } return false; } void ShapeBase::startFade( F32 fadeTime, F32 fadeDelay, bool fadeOut ) { setMaskBits(CloakMask); mFadeElapsedTime = 0; mFading = true; if(fadeDelay < 0) fadeDelay = 0; if(fadeTime < 0) fadeTime = 0; mFadeTime = fadeTime; mFadeDelay = fadeDelay; mFadeOut = fadeOut; mFadeVal = F32(mFadeOut); } //-------------------------------------------------------------------------- void ShapeBase::setShapeName(const char* name) { if (!isGhost()) { if (name[0] != '\0') { // Use tags for better network performance // Should be a tag, but we'll convert to one if it isn't. if (name[0] == StringTagPrefixByte) mShapeNameHandle = StringHandle(U32(dAtoi(name + 1))); else mShapeNameHandle = StringHandle(name); } else { mShapeNameHandle = StringHandle(); } setMaskBits(NameMask); } } void ShapeBase::setSkinName(const char* name) { if (!isGhost()) { if (name[0] != '\0') { // Use tags for better network performance // Should be a tag, but we'll convert to one if it isn't. if (name[0] == StringTagPrefixByte) { mSkinNameHandle = StringHandle(U32(dAtoi(name + 1))); } else { mSkinNameHandle = StringHandle(name); } } else { mSkinNameHandle = StringHandle(); } setMaskBits(SkinMask); } } //-------------------------------------------------------------------------- //---------------------------------------------------------------------------- ConsoleMethod( ShapeBase, setHidden, void, 3, 3, "(bool show)") { object->setHidden(dAtob(argv[2])); } ConsoleMethod( ShapeBase, isHidden, bool, 2, 2, "") { return object->isHidden(); } //---------------------------------------------------------------------------- ConsoleMethod( ShapeBase, playAudio, bool, 4, 4, "(int slot, AudioProfile ap)") { U32 slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxSoundThreads) { AudioProfile* profile; if (Sim::findObject(argv[3],profile)) { object->playAudio(slot,profile); return true; } } return false; } ConsoleMethod( ShapeBase, stopAudio, bool, 3, 3, "(int slot)") { U32 slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxSoundThreads) { object->stopAudio(slot); return true; } return false; } //---------------------------------------------------------------------------- ConsoleMethod( ShapeBase, playThread, bool, 3, 4, "(int slot, string sequenceName)") { U32 slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) { if (argc == 4) { if (object->getShape()) { S32 seq = object->getShape()->findSequence(argv[3]); if (seq != -1 && object->setThreadSequence(slot,seq)) return true; } } else if (object->playThread(slot)) return true; } return false; } ConsoleMethod( ShapeBase, setThreadDir, bool, 4, 4, "(int slot, bool isForward)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) { if (object->setThreadDir(slot,dAtob(argv[3]))) return true; } return false; } ConsoleMethod( ShapeBase, stopThread, bool, 3, 3, "(int slot)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) { if (object->stopThread(slot)) return true; } return false; } ConsoleMethod( ShapeBase, pauseThread, bool, 3, 3, "(int slot)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxScriptThreads) { if (object->pauseThread(slot)) return true; } return false; } //---------------------------------------------------------------------------- ConsoleMethod( ShapeBase, mountObject, bool, 4, 4, "( ShapeBase object, int slot )" "Mount ourselves on an object in the specified slot.") { ShapeBase *target; if (Sim::findObject(argv[2],target)) { S32 node = -1; dSscanf(argv[3],"%d",&node); if (node >= 0 && node < ShapeBaseData::NumMountPoints) object->mountObject(target,node); return true; } return false; } ConsoleMethod( ShapeBase, unmountObject, bool, 3, 3, "(ShapeBase obj)" "Unmount an object from ourselves.") { ShapeBase *target; if (Sim::findObject(argv[2],target)) { object->unmountObject(target); return true; } return false; } ConsoleMethod( ShapeBase, unmount, void, 2, 2, "Unmount from the currently mounted object if any.") { object->unmount(); } ConsoleMethod( ShapeBase, isMounted, bool, 2, 2, "Are we mounted?") { return object->isMounted(); } ConsoleMethod( ShapeBase, getObjectMount, S32, 2, 2, "Returns the ShapeBase we're mounted on.") { return object->isMounted()? object->getObjectMount()->getId(): 0; } ConsoleMethod( ShapeBase, getMountedObjectCount, S32, 2, 2, "") { return object->getMountedObjectCount(); } ConsoleMethod( ShapeBase, getMountedObject, S32, 3, 3, "(int slot)") { ShapeBase* mobj = object->getMountedObject(dAtoi(argv[2])); return mobj? mobj->getId(): 0; } ConsoleMethod( ShapeBase, getMountedObjectNode, S32, 3, 3, "(int node)") { return object->getMountedObjectNode(dAtoi(argv[2])); } ConsoleMethod( ShapeBase, getMountNodeObject, S32, 3, 3, "(int node)") { ShapeBase* mobj = object->getMountNodeObject(dAtoi(argv[2])); return mobj? mobj->getId(): 0; } //---------------------------------------------------------------------------- ConsoleMethod( ShapeBase, mountImage, bool, 4, 6, "(ShapeBaseImageData image, int slot, bool loaded=true, string skinTag=NULL)") { ShapeBaseImageData* imageData; if (Sim::findObject(argv[2],imageData)) { U32 slot = dAtoi(argv[3]); bool loaded = (argc == 5)? dAtob(argv[4]): true; StringHandle team; if(argc == 6) { if(argv[5][0] == StringTagPrefixByte) team = StringHandle(U32(dAtoi(argv[5]+1))); } if (slot >= 0 && slot < ShapeBase::MaxMountedImages) object->mountImage(imageData,slot,loaded,team); } return false; } ConsoleMethod( ShapeBase, unmountImage, bool, 3, 3, "(int slot)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxMountedImages) return object->unmountImage(slot); return false; } ConsoleMethod( ShapeBase, getMountedImage, S32, 3, 3, "(int slot)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxMountedImages) if (ShapeBaseImageData* data = object->getMountedImage(slot)) return data->getId(); return 0; } ConsoleMethod( ShapeBase, getPendingImage, S32, 3, 3, "(int slot)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxMountedImages) if (ShapeBaseImageData* data = object->getPendingImage(slot)) return data->getId(); return 0; } ConsoleMethod( ShapeBase, isImageFiring, bool, 3, 3, "(int slot)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxMountedImages) return object->isImageFiring(slot); return false; } ConsoleMethod( ShapeBase, isImageMounted, bool, 3, 3, "(ShapeBaseImageData db)") { ShapeBaseImageData* imageData; if (Sim::findObject(argv[2],imageData)) return object->isImageMounted(imageData); return false; } ConsoleMethod( ShapeBase, getMountSlot, S32, 3, 3, "(ShapeBaseImageData db)") { ShapeBaseImageData* imageData; if (Sim::findObject(argv[2],imageData)) return object->getMountSlot(imageData); return -1; } ConsoleMethod( ShapeBase, getImageSkinTag, S32, 3, 3, "(int slot)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxMountedImages) return object->getImageSkinTag(slot).getIndex(); return -1; } ConsoleMethod( ShapeBase, getImageState, const char*, 3, 3, "(int slot)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxMountedImages) return object->getImageState(slot); return "Error"; } ConsoleMethod( ShapeBase, getImageTrigger, bool, 3, 3, "(int slot)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxMountedImages) return object->getImageTriggerState(slot); return false; } ConsoleMethod( ShapeBase, setImageTrigger, bool, 4, 4, "(int slot, bool isTriggered)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxMountedImages) { object->setImageTriggerState(slot,dAtob(argv[3])); return object->getImageTriggerState(slot); } return false; } ConsoleMethod( ShapeBase, getImageAmmo, bool, 3, 3, "(int slot)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxMountedImages) return object->getImageAmmoState(slot); return false; } ConsoleMethod( ShapeBase, setImageAmmo, bool, 4, 4, "(int slot, bool hasAmmo)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxMountedImages) { bool ammo = dAtob(argv[3]); object->setImageAmmoState(slot,dAtob(argv[3])); return ammo; } return false; } ConsoleMethod( ShapeBase, getImageLoaded, bool, 3, 3, "(int slot)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxMountedImages) return object->getImageLoadedState(slot); return false; } ConsoleMethod( ShapeBase, setImageLoaded, bool, 4, 4, "(int slot, bool loaded)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxMountedImages) { bool loaded = dAtob(argv[3]); object->setImageLoadedState(slot, dAtob(argv[3])); return loaded; } return false; } ConsoleMethod( ShapeBase, getMuzzleVector, const char*, 3, 3, "(int slot)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxMountedImages) { VectorF v; object->getMuzzleVector(slot,&v); char* buff = Con::getReturnBuffer(100); dSprintf(buff,100,"%g %g %g",v.x,v.y,v.z); return buff; } return "0 1 0"; } ConsoleMethod( ShapeBase, getMuzzlePoint, const char*, 3, 3, "(int slot)") { int slot = dAtoi(argv[2]); if (slot >= 0 && slot < ShapeBase::MaxMountedImages) { Point3F p; object->getMuzzlePoint(slot,&p); char* buff = Con::getReturnBuffer(100); dSprintf(buff,100,"%g %g %g",p.x,p.y,p.z); return buff; } return "0 0 0"; } ConsoleMethod( ShapeBase, getSlotTransform, const char*, 3, 3, "(int slot)") { int slot = dAtoi(argv[2]); MatrixF xf(true); if (slot >= 0 && slot < ShapeBase::MaxMountedImages) object->getMountTransform(slot,&xf); Point3F pos; xf.getColumn(3,&pos); AngAxisF aa(xf); char* buff = Con::getReturnBuffer(200); dSprintf(buff,200,"%g %g %g %g %g %g %g", pos.x,pos.y,pos.z,aa.axis.x,aa.axis.y,aa.axis.z,aa.angle); return buff; } ConsoleMethod( ShapeBase, getAIRepairPoint, const char*, 2, 2, "Get the position at which the AI should stand to repair things.") { Point3F pos = object->getAIRepairPoint(); char* buff = Con::getReturnBuffer(200); dSprintf(buff,200,"%g %g %g", pos.x,pos.y,pos.z); return buff; } ConsoleMethod( ShapeBase, getVelocity, const char *, 2, 2, "") { const VectorF& vel = object->getVelocity(); char* buff = Con::getReturnBuffer(100); dSprintf(buff,100,"%g %g %g",vel.x,vel.y,vel.z); return buff; } ConsoleMethod( ShapeBase, setVelocity, bool, 3, 3, "(Vector3F vel)") { VectorF vel(0,0,0); dSscanf(argv[2],"%g %g %g",&vel.x,&vel.y,&vel.z); object->setVelocity(vel); return true; } ConsoleMethod( ShapeBase, applyImpulse, bool, 4, 4, "(Point3F Pos, VectorF vel)") { Point3F pos(0,0,0); VectorF vel(0,0,0); dSscanf(argv[2],"%g %g %g",&pos.x,&pos.y,&pos.z); dSscanf(argv[3],"%g %g %g",&vel.x,&vel.y,&vel.z); object->applyImpulse(pos,vel); return true; } ConsoleMethod( ShapeBase, getEyeVector, const char*, 2, 2, "") { MatrixF mat; object->getEyeTransform(&mat); VectorF v2; mat.getColumn(1,&v2); char* buff = Con::getReturnBuffer(100); dSprintf(buff, 100,"%g %g %g",v2.x,v2.y,v2.z); return buff; } ConsoleMethod( ShapeBase, getEyePoint, const char*, 2, 2, "") { MatrixF mat; object->getEyeTransform(&mat); Point3F ep; mat.getColumn(3,&ep); char* buff = Con::getReturnBuffer(100); dSprintf(buff, 100,"%g %g %g",ep.x,ep.y,ep.z); return buff; } ConsoleMethod( ShapeBase, getEyeTransform, const char*, 2, 2, "") { MatrixF mat; object->getEyeTransform(&mat); Point3F pos; mat.getColumn(3,&pos); AngAxisF aa(mat); char* buff = Con::getReturnBuffer(100); dSprintf(buff,100,"%g %g %g %g %g %g %g", pos.x,pos.y,pos.z,aa.axis.x,aa.axis.y,aa.axis.z,aa.angle); return buff; } ConsoleMethod( ShapeBase, setEnergyLevel, void, 3, 3, "(float level)") { object->setEnergyLevel(dAtof(argv[2])); } ConsoleMethod( ShapeBase, getEnergyLevel, F32, 2, 2, "") { return object->getEnergyLevel(); } ConsoleMethod( ShapeBase, getEnergyPercent, F32, 2, 2, "") { return object->getEnergyValue(); } ConsoleMethod( ShapeBase, setDamageLevel, void, 3, 3, "(float level)") { object->setDamageLevel(dAtof(argv[2])); } ConsoleMethod( ShapeBase, getDamageLevel, F32, 2, 2, "") { return object->getDamageLevel(); } ConsoleMethod( ShapeBase, getDamagePercent, F32, 2, 2, "") { return object->getDamageValue(); } ConsoleMethod( ShapeBase, setDamageState, bool, 3, 3, "(string state)") { return object->setDamageState(argv[2]); } ConsoleMethod( ShapeBase, getDamageState, const char*, 2, 2, "") { return object->getDamageStateName(); } ConsoleMethod( ShapeBase, isDestroyed, bool, 2, 2, "") { return object->isDestroyed(); } ConsoleMethod( ShapeBase, isDisabled, bool, 2, 2, "True if the state is not Enabled.") { return object->getDamageState() != ShapeBase::Enabled; } ConsoleMethod( ShapeBase, isEnabled, bool, 2, 2, "") { return object->getDamageState() == ShapeBase::Enabled; } ConsoleMethod( ShapeBase, applyDamage, void, 3, 3, "(float amt)") { object->applyDamage(dAtof(argv[2])); } ConsoleMethod( ShapeBase, applyRepair, void, 3, 3, "(float amt)") { object->applyRepair(dAtof(argv[2])); } ConsoleMethod( ShapeBase, setRepairRate, void, 3, 3, "(float amt)") { F32 rate = dAtof(argv[2]); if(rate < 0) rate = 0; object->setRepairRate(rate); } ConsoleMethod( ShapeBase, getRepairRate, F32, 2, 2, "") { return object->getRepairRate(); } ConsoleMethod( ShapeBase, setRechargeRate, void, 3, 3, "(float rate)") { object->setRechargeRate(dAtof(argv[2])); } ConsoleMethod( ShapeBase, getRechargeRate, F32, 2, 2, "") { return object->getRechargeRate(); } ConsoleMethod( ShapeBase, getControllingClient, S32, 2, 2, "Returns a GameConnection.") { if (GameConnection* con = object->getControllingClient()) return con->getId(); return 0; } ConsoleMethod( ShapeBase, getControllingObject, S32, 2, 2, "") { if (ShapeBase* con = object->getControllingObject()) return con->getId(); return 0; } // return true if can cloak, otherwise the reason why object cannot cloak ConsoleMethod( ShapeBase, canCloak, bool, 2, 2, "") { return true; } ConsoleMethod( ShapeBase, setCloaked, void, 3, 3, "(bool isCloaked)") { bool cloaked = dAtob(argv[2]); if (object->isServerObject()) object->setCloakedState(cloaked); } ConsoleMethod( ShapeBase, isCloaked, bool, 2, 2, "") { return object->getCloakedState(); } ConsoleMethod( ShapeBase, setDamageFlash, void, 3, 3, "(float lvl)") { F32 flash = dAtof(argv[2]); if (object->isServerObject()) object->setDamageFlash(flash); } ConsoleMethod( ShapeBase, getDamageFlash, F32, 2, 2, "") { return object->getDamageFlash(); } ConsoleMethod( ShapeBase, setWhiteOut, void, 3, 3, "(float flashLevel)") { F32 flash = dAtof(argv[2]); if (object->isServerObject()) object->setWhiteOut(flash); } ConsoleMethod( ShapeBase, getWhiteOut, F32, 2, 2, "") { return object->getWhiteOut(); } ConsoleMethod( ShapeBase, getCameraFov, F32, 2, 2, "") { if (object->isServerObject()) return object->getCameraFov(); return 0.0; } ConsoleMethod( ShapeBase, setCameraFov, void, 3, 3, "(float fov)") { if (object->isServerObject()) object->setCameraFov(dAtof(argv[2])); } ConsoleMethod( ShapeBase, setInvincibleMode, void, 4, 4, "(float time, float speed)") { object->setupInvincibleEffect(dAtof(argv[2]), dAtof(argv[3])); } ConsoleFunction(setShadowDetailLevel, void , 2, 2, "setShadowDetailLevel(val 0...1);") { argc; F32 val = dAtof(argv[1]); if (val < 0.0f) val = 0.0f; else if (val > 1.0f) val = 1.0f; if (mFabs(Shadow::getGlobalShadowDetailLevel()-val)<0.001f) return; // shadow details determined in two places: // 1. setGlobalShadowDetailLevel // 2. static shape header has some #defines that determine // at what level of shadow detail each type of // object uses a generic shadow or no shadow at all Shadow::setGlobalShadowDetailLevel(val); Con::setFloatVariable("$pref::Shadows", val); } ConsoleMethod( ShapeBase, startFade, void, 5, 5, "( int fadeTimeMS, int fadeDelayMS, bool fadeOut )") { U32 fadeTime; U32 fadeDelay; bool fadeOut; dSscanf(argv[2], "%d", &fadeTime ); dSscanf(argv[3], "%d", &fadeDelay ); fadeOut = dAtob(argv[4]); object->startFade( fadeTime / 1000.0, fadeDelay / 1000.0, fadeOut ); } ConsoleMethod( ShapeBase, setDamageVector, void, 3, 3, "(Vector3F origin)") { VectorF normal; dSscanf(argv[2], "%g %g %g", &normal.x, &normal.y, &normal.z); normal.normalize(); object->setDamageDir(VectorF(normal.x, normal.y, normal.z)); } ConsoleMethod( ShapeBase, setShapeName, void, 3, 3, "(string tag)") { object->setShapeName(argv[2]); } ConsoleMethod( ShapeBase, setSkinName, void, 3, 3, "(string tag)") { object->setSkinName(argv[2]); } ConsoleMethod( ShapeBase, getShapeName, const char*, 2, 2, "") { return object->getShapeName(); } ConsoleMethod( ShapeBase, getSkinName, const char*, 2, 2, "") { return object->getSkinName(); } //---------------------------------------------------------------------------- void ShapeBase::consoleInit() { Con::addVariable("SB::DFDec", TypeF32, &sDamageFlashDec); Con::addVariable("SB::WODec", TypeF32, &sWhiteoutDec); Con::addVariable("pref::environmentMaps", TypeBool, &gRenderEnvMaps); }