//=========================================================================== // Copyright (C) 2002 Radical Entertainment Ltd. All rights reserved. // // Component: ActorDSG // // Description: ActorDSG is a statepropdsg that has an id that will indicate the // relationship between an object and the objects that it can shoot // // Authors: Michael Riegger // //=========================================================================== //--------------------------------------------------------------------------- // Includes //=========================================================================== #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include //=========================================================================== // Local Constants, Typedefs, and Macros //=========================================================================== unsigned int ActorDSG::s_IDCounter = 0; static const int REMOVE_FROM_WORLD = 0; static const int SPAWN_5_COINS = 1; static const int REMOVE_COLLISION_VOLUME = 2; static const int FIRE_ENERGY_BOLT = 3; static const int KILL_SPEED = 4; static const int SPAWN_10_COINS = 5; static const int SPAWN_15_COINS = 6; static const int SPAWN_20_COINS = 7; // States for the shield static const unsigned int SHIELD_STATE_POWER_UP = 0; static const unsigned int SHIELD_STATE_IDLE = 1; static const unsigned int SHIELD_STATE_HIT = 2; static const unsigned int SHIELD_STATE_DESTROYED = 3; // States for the tractor beam static const unsigned int TRACTOR_BEAM_ACTIVATE = 0; static const unsigned int TRACTOR_BEAM_ON = 1; static const unsigned int TRACTOR_BEAM_DEACTIVATE = 2; static const unsigned int TRACTOR_BEAM_OFF = 3; //=========================================================================== // Global Data, Local Data, Local Classes //=========================================================================== class CStateProp; static tUID s_UfoName = tName::MakeUID("spaceship"); //=========================================================================== // Member Functions //=========================================================================== //=========================================================================== // Debugging / Watching //=========================================================================== static float AMB_RED_SCALE = 2.0f; static float AMB_GREEN_SCALE = 2.0f; static float AMB_BLUE_SCALE = 2.0f; static float DIR_RED_SCALE = 1.0f; static float DIR_GREEN_SCALE = 1.0f; static float DIR_BLUE_SCALE = 1.0f; ActorDSG::ActorDSG(): m_ID( ++s_IDCounter ), m_ShieldProp( NULL ), m_TractorBeamProp( NULL ), m_ShieldEnabled( false ), mPhysicsProperties( NULL ) { } ActorDSG::~ActorDSG() { if ( m_ShieldProp != NULL ) { m_ShieldProp->Release(); m_ShieldProp = NULL; } if ( m_TractorBeamProp != NULL ) { m_TractorBeamProp->ReleaseVerified(); m_TractorBeamProp = NULL; } if ( mPhysicsProperties != NULL ) { mPhysicsProperties->Release(); } } sim::Solving_Answer ActorDSG::PreReactToCollision( sim::SimState* pCollidedObj, sim::Collision& inCollision ) { // Check to see that we aren't going to collide with an object that has the same ID // that we do if ( pCollidedObj->mAIRefIndex == PhysicsAIRef::ActorStateProp ) { rAssert( dynamic_cast< ActorDSG* >( static_cast< tRefCounted* >( pCollidedObj->mAIRefPointer) ) ); ActorDSG* pStatePropDSG = static_cast< ActorDSG* >( pCollidedObj->mAIRefPointer ); if ( pStatePropDSG->GetID() == GetID() ) { return sim::Solving_Aborted; } } HandleEvent( StatePropDSG::FEATHER_TOUCH ); return sim::Solving_Continue; } void ActorDSG::SetRank(rmt::Vector& irRefPosn, rmt::Vector& mViewVector) { if ( mpStateProp->GetUID() != s_UfoName ) { IEntityDSG::SetRank( irRefPosn, mViewVector ); } else { // Final hack to make the UFO always draw last // The UFO has a *huge* bounding sphere that makes it // always drawn after the vehicle explosion, cutting it // off nastily, lets fix it and force the thing to be drawn first mRank = FLT_MAX; } } sim::Solving_Answer ActorDSG::PostReactToCollision(rmt::Vector& impulse, sim::Collision& inCollision) { // Calculate impulse float impulseMagSqr = impulse.MagnitudeSqr(); // If the shield is on, impulse has to be above an very high number to cause it be // be destroyed const float FORCE_REQUIRED_TO_BREAK_SHIELD = 1600000; const float FORCE_REQUIRE_TO_DESTROY = 800000; if ( m_ShieldEnabled ) { if ( impulseMagSqr > FORCE_REQUIRED_TO_BREAK_SHIELD ) { DestroyShield(); } else { PlayShieldGettingHit(); } } else { if ( impulseMagSqr > FORCE_REQUIRE_TO_DESTROY ) { HandleEvent( DESTROYED ); } } AddToSimulation(); return CollisionEntityDSG::PostReactToCollision(impulse, inCollision); } void ActorDSG::ApplyForce( const rmt::Vector& direction, float force ) { // Force over a certain const float SHIELD_BREAKING_FORCE = 400.0f; const float FORCE_REQUIRED_TO_DESTROY = 400.0f; if ( m_ShieldEnabled ) { if ( force < SHIELD_BREAKING_FORCE ) { PlayShieldGettingHit(); } else { DestroyShield(); } } else { if ( force >= FORCE_REQUIRED_TO_DESTROY ) { DestroyShield(); HandleEvent( StatePropDSG::DESTROYED ); DynaPhysDSG::ApplyForce( direction, force ); } } } void ActorDSG::RecieveEvent( int callback , CStateProp* stateProp ) { switch ( callback ) { case FIRE_ENERGY_BOLT: { tName name = "waspray"; GetActorManager()->FireProjectile( name.GetUID(), mPosn, -mTransform.Row(2), m_ID ); } break; case REMOVE_FROM_WORLD: GetActorManager()->RemoveActorByDSGPointer( this, true ); break; default: StatePropDSG::RecieveEvent( callback, stateProp ); break; }; } void ActorDSG::HandleEvent( StatePropDSG::Event event ) { // If a shield is enabled, then only destroyed events should get StatePropDSG::HandleEvent( event ); } bool ActorDSG::LoadShield( const char* statePropName ) { bool loadedOk = false; CStatePropData* statePropData = p3d::find< CStatePropData > ( statePropName ); if ( statePropData != NULL ) { CStateProp* stateprop = CStateProp::CreateStateProp( statePropData, 0 ); if ( stateprop != NULL ) { m_ShieldEnabled = true; tRefCounted::Assign( m_ShieldProp, stateprop); loadedOk = true; SetPhysicsProperties( 100.0f, 0.8f, 1.15f, 0.0f ); } } return loadedOk; } bool ActorDSG::LoadTractorBeam( const char* statePropName ) { bool loadedOk = false; CStatePropData* statePropData = p3d::find< CStatePropData > ( statePropName ); if ( statePropData != NULL ) { CStateProp* stateprop = CStateProp::CreateStateProp( statePropData, TRACTOR_BEAM_OFF ); if ( stateprop != NULL ) { tRefCounted::Assign( m_TractorBeamProp, stateprop); loadedOk = true; } } return loadedOk; } void ActorDSG::ActivateTractorBeam() { if ( m_TractorBeamProp != NULL ) { if ( m_TractorBeamProp->GetState() == TRACTOR_BEAM_OFF ) { m_TractorBeamProp->SetState( TRACTOR_BEAM_ACTIVATE ); } } } void ActorDSG::DeactivateTractorBeam() { if ( m_TractorBeamProp != NULL ) { if ( m_TractorBeamProp->GetState() == TRACTOR_BEAM_ON || m_TractorBeamProp->GetState() == TRACTOR_BEAM_ACTIVATE ) { m_TractorBeamProp->SetState( TRACTOR_BEAM_DEACTIVATE ); } } } bool ActorDSG::IsTractorBeamOn()const { if ( m_TractorBeamProp == NULL ) return false; return m_TractorBeamProp->GetState() != TRACTOR_BEAM_OFF; } void ActorDSG::Display() { // Have the rad debugging functions been attached yet? // Check, and if not, add them once #ifndef RAD_RELEASE static bool radattached = false; if ( radattached == false ) { radDbgWatchAddFloat( &AMB_RED_SCALE, "Amb Red Scale", "Wasp lighting", NULL, NULL, 0, 4.0f ); radDbgWatchAddFloat( &AMB_GREEN_SCALE, "Amb Green Scale", "Wasp lighting", NULL, NULL, 0, 4.0f ); radDbgWatchAddFloat( &AMB_BLUE_SCALE, "Amb Blue Scale", "Wasp lighting", NULL, NULL, 0, 4.0f ); radDbgWatchAddFloat( &DIR_RED_SCALE, "Dir Red Scale", "Wasp lighting", NULL, NULL, 0, 4.0f ); radDbgWatchAddFloat( &DIR_GREEN_SCALE, "Dir Green Scale", "Wasp lighting", NULL, NULL, 0, 4.0f ); radDbgWatchAddFloat( &DIR_BLUE_SCALE, "Dir Blue Scale", "Wasp lighting", NULL, NULL, 0, 4.0f ); radattached = true; } #endif // Aryan and Yayoi want the beecamera brightened artificially // There doesnt appear to be a nice + quick solution // Do a quick UID compare then brighten the level lights // We will reset them after drawing is finished static tUID beecameraUID = tName::MakeUID( "beecamera" ); tColour origColours[MAX_VIEW_LIGHTS]; tColour origAmbient; if ( mpStateProp->GetUID() == beecameraUID ) { tView* view = p3d::context->GetView(); for ( unsigned int i = 0 ; i < MAX_VIEW_LIGHTS ; i ++ ) { tLight* light = view->GetLight( i ); if ( light ) { origColours[i] = light->GetColour(); int r = (int)( (float) origColours[i].Red() * DIR_RED_SCALE ); int g = (int)( (float) origColours[i].Green() * DIR_GREEN_SCALE ); int b = (int)( (float) origColours[i].Blue() * DIR_BLUE_SCALE ); if ( r > 255 ) r = 255; if ( g > 255 ) g = 255; if ( b > 255 ) b = 255; tColour newColour( r, g, b ); light->SetColour( newColour ); } } origAmbient = p3d::pddi->GetAmbientLight(); int r = (int)( (float) origAmbient.Red() * AMB_RED_SCALE ); int g = (int)( (float) origAmbient.Green() * AMB_GREEN_SCALE ); int b = (int)( (float) origAmbient.Blue() * AMB_BLUE_SCALE ); if ( r > 255 ) r = 255; if ( g > 255 ) g = 255; if ( b > 255 ) b = 255; tColour scaledAmbient( r, g, b ); p3d::pddi->SetAmbientLight( scaledAmbient ); } #ifdef PROFILER_ENABLED char profileName[] = " ActorDSG Display"; #endif DSG_BEGIN_PROFILE(profileName) StatePropDSG::Display(); if ( m_ShieldProp != NULL ) { // Draw the shield p3d::stack->PushMultiply( mTransform ); m_ShieldProp->UpdateFrameControllersForRender(); m_ShieldProp->GetDrawable()->Display(); p3d::stack->Pop(); } if ( m_TractorBeamProp != NULL ) { // Draw the tractor beam p3d::stack->PushMultiply( mTransform ); m_TractorBeamProp->Display(); p3d::stack->Pop(); } // Reset the level lights for the beecamera if ( mpStateProp->GetUID() == beecameraUID ) { tView* view = p3d::context->GetView(); for ( unsigned int i = 0 ; i < MAX_VIEW_LIGHTS ; i ++ ) { tLight* light = view->GetLight( i ); if ( light ) { light->SetColour( origColours[i] ); } } // Reset the ambient p3d::pddi->SetAmbientLight( origAmbient ); } DSG_END_PROFILE(profileName) } void ActorDSG::AdvanceAnimation( float timeInMS ) { if ( m_ShieldProp ) { m_ShieldProp->Update( timeInMS ); } if ( m_TractorBeamProp ) { m_TractorBeamProp->Update( timeInMS ); } StatePropDSG::AdvanceAnimation( timeInMS ); } void ActorDSG::DestroyShield() { if ( m_ShieldProp != NULL ) { m_ShieldEnabled = false; m_ShieldProp->SetState( SHIELD_STATE_DESTROYED ); } } void ActorDSG::PlayShieldGettingHit() { if ( m_ShieldProp != NULL ) { if ( m_ShieldProp->GetState() != SHIELD_STATE_HIT ) { m_ShieldProp->SetState( SHIELD_STATE_HIT ); } } } void ActorDSG::RestoreShield() { if ( m_ShieldProp ) { m_ShieldEnabled = true; m_ShieldProp->SetState( SHIELD_STATE_POWER_UP ); } } void ActorDSG::SetPhysicsProperties( float mass, float friction, float rest, float tang ) { if ( mPhysicsProperties == NULL ) return; // We want to grab the volume so we can compute the density via the given mass // and the simstate's volume sim::SimStateArticulated* pSimStateArt = ( sim::SimStateArticulated* )( mpSimStateObj ); sim::ArticulatedPhysicsObject* physicsObject = static_cast< sim::ArticulatedPhysicsObject*>( pSimStateArt->GetSimulatedObject( -1 )); float volume = physicsObject->GetVolume(); rAssert( volume != 0 ); float density = mass / volume; mPhysicsProperties->SetRestCoeffCGS( rest ); mPhysicsProperties->SetFrictCoeffCGS( friction ); mPhysicsProperties->SetTangRestCoeffCGS( tang ); mPhysicsProperties->SetDensityCGS( density ); mpSimStateObj->SetPhysicsProperties( mPhysicsProperties ); } void ActorDSG::LoadSetup( CStatePropData* statePropData, int startState, const rmt::Matrix& transform, CollisionAttributes* ipCollAttr, bool isMoveable, bool isDynaLoaded, tEntityStore* ipSearchStore ) { HeapMgr()->PushHeap( GMA_LEVEL_OTHER ); sim::PhysicsProperties* physicsProperties = new sim::PhysicsProperties; StatePropDSG::LoadSetup( statePropData, startState, transform, ipCollAttr, isDynaLoaded, ipSearchStore ); tRefCounted::Assign( mPhysicsProperties, physicsProperties ); HeapMgr()->PopHeap( GMA_LEVEL_OTHER ); } void ActorDSG::GenerateCoins( int numCoins ) { // Generate coins, but take into account the fact that this object might be hovering // over the ground. // So generate coins at the top of the object below the stateprop // First find the ground plane bool foundPlane; rmt::Vector deepestPosn, deepestNormal, groundPlaneNormal, groundPlanePosn; rmt::Vector searchPosn = rPosition(); searchPosn.y += 100.0f; GetIntersectManager()->FindIntersection( searchPosn, foundPlane, groundPlaneNormal, groundPlanePosn ); if ( foundPlane == false ) { groundPlanePosn = rPosition(); groundPlanePosn.y -= 30.0f; } // Check to see if there any static objects directly underneath us // find the midpoint between the current position and the ground position rmt::Vector midpoint = ( groundPlanePosn + rPosition() ) * 0.5f; float radius = (rPosition().y - groundPlanePosn.y ); ReserveArray< StaticPhysDSG* > spList(200); GetIntersectManager()->FindStaticPhysElems( midpoint, radius , spList ); // Iterate through the static phys list and add them to an intersection list IntersectionList staticList; for ( int i = 0 ; i < spList.mUseSize; i++ ) { staticList.AddStatic( spList[i]->GetSimState() ); } // Now query our list using subcollision volume accuracy to pinpoint the precise location // of intersection rmt::Vector staticIntersection; rmt::Vector start = rPosition(); rmt::Vector end = groundPlanePosn; if ( staticList.TestIntersectionStatics( start, end, &staticIntersection )) { // There is a static in the way. spawn teh coins on top of this object GetCoinManager()->SpawnCoins( numCoins, rPosition(), staticIntersection.y ); } else { // The wasp is hovering above solid ground. Spawn the coins on the ground GetCoinManager()->SpawnCoins( numCoins, rPosition(), groundPlanePosn.y ); } } int ActorDSG::CastsShadow() { // Dont cast a shadow in the first (fade in) and last(exploded) states int castsShadow; unsigned int statepropState = GetState(); if ( statepropState == ActorEnum::eFadeIn || statepropState == ActorEnum::eDestroyed ) castsShadow = 0; else castsShadow = StaticPhysDSG::CastsShadow(); return castsShadow; }