//===========================================================================// // File: Missile.cpp //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // 09/09/98 JSE Inital base class based off of Shadowrun/Adept // // 09/25/98 BDB WeaponMover based on Vehicle // //---------------------------------------------------------------------------// // Copyright (C) 1998, Fasa Interactive // // All Rights reserved worldwide // // This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // //===========================================================================// #include "MW4Headers.hpp" #include "Missile.hpp" #include "Vehicle.hpp" #include "Decal.hpp" #include "VehicleInterface.hpp" #include "MWMission.hpp" #include "MWDamageObject.hpp" #include #include #include #include #include #include //#define MISSILE_BUG "johnyo" //#define MISSILE_BUG "daberger" //#define TARGET_BUG "daberger" //############################################################################# //########################## ExecutionStateEngine ####################### //############################################################################# Missile::ExecutionStateEngine::ClassData* Missile::ExecutionStateEngine::DefaultData = NULL; DWORD MechWarrior4::Executed_Missile_Count = 0; const StateEngine::StateEntry Missile::ExecutionStateEngine::StateEntries[]= { STATE_ENTRY(Missile__ExecutionStateEngine, LRMMotion), STATE_ENTRY(Missile__ExecutionStateEngine, MRMMotion), STATE_ENTRY(Missile__ExecutionStateEngine, SRMMotion), STATE_ENTRY(Missile__ExecutionStateEngine, SSRMMotion), STATE_ENTRY(Missile__ExecutionStateEngine, BombMotion), STATE_ENTRY(Missile__ExecutionStateEngine, LongTomMotion) }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::ExecutionStateEngine::InitializeClass() { Check_Object(StateEngine::DefaultData); Verify(!DefaultData); DefaultData = new ClassData( Missile__ExecutionStateEngineClassID, "Missile::ExecutionStateEngine", BaseClass::DefaultData, ELEMENTS(StateEntries), StateEntries, (Entity::ExecutionStateEngine::Factory)Make, (Entity::ExecutionStateEngine::FactoryRequest::Factory) &FactoryRequest::ConstructFactoryRequest ); Register_Object(DefaultData); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::ExecutionStateEngine::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Missile::ExecutionStateEngine* Missile::ExecutionStateEngine::Make( Missile *weapon, FactoryRequest *request ) { Check_Object(weapon); Check_Object(request); gos_PushCurrentHeap(Heap); Missile::ExecutionStateEngine *engine = new Missile::ExecutionStateEngine(DefaultData, weapon, request); gos_PopCurrentHeap(); return engine; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::ExecutionStateEngine::TestInstance() const { Verify(IsDerivedFrom(DefaultData)); } //############################################################################# //############################### Missile ############################### //############################################################################# //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Missile::ClassData* Missile::DefaultData = NULL; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::InitializeClass() { Check_Object(Missile::ExecutionStateEngine::DefaultData); Verify(!DefaultData); DefaultData = new ClassData( MissileClassID, "MechWarrior4::Missile", BaseClass::DefaultData, 0, NULL, (Entity::Factory)Make, (Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage, ExecutionStateEngine::DefaultData, (Entity::GameModel::Factory)GameModel::ConstructGameModel, NULL, (Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify, (Entity::GameModel::ModelWrite)GameModel::WriteToText, (Entity::GameModel::ModelSave)GameModel::SaveGameModel ); Register_Object(DefaultData); DIRECT_GAME_MODEL_ATTRIBUTE( Missile__GameModel, MaxTurnAngle, maxTurnAngle, Scalar ); DIRECT_GAME_MODEL_ATTRIBUTE( Missile__GameModel, ThrusterAcceleration, thrusterAcceleration, Scalar ); DIRECT_GAME_MODEL_ATTRIBUTE( Missile__GameModel, ProximityFuseDistance, proximityFuseDistance, Scalar ); DIRECT_GAME_MODEL_ATTRIBUTE( Missile__GameModel, MaxLiveTime, maxLiveTime, Scalar ); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Missile* Missile::Make( CreateMessage *message, ReplicatorID *base_id ) { Check_Object(message); gos_PushCurrentHeap(Heap); Missile *new_entity; Entity *entity = EntityManager::GetInstance()->RequestFromArmory(message->dataListID); if (entity) { new_entity = Cast_Object(Missile*, entity); new_entity->Reuse(message, base_id); } else new_entity = new Missile(DefaultData, message, base_id, NULL); gos_PopCurrentHeap(); Check_Object(new_entity); return new_entity; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Missile::Missile( ClassData *class_data, CreateMessage *message, ReplicatorID *base_id, ElementRenderer::Element *element ): WeaponMover(class_data, message, base_id, element), targetEntity(NULL), targetMWObject(NULL) { Check_Pointer(this); Check_Object(message); targetEntity.Remove(); targetOffsetFromEntity.x = 0.0f; targetOffsetFromEntity.y = 0.0f; targetOffsetFromEntity.z = 0.0f; targetPoint = message->targetPoint; burnTime = message->burnTime; if(message->targetID != ReplicatorID::Null) { Check_Object(Network::GetInstance()); Connection *connection = Network::GetInstance()->GetConnection(message->targetID.connectionID); if (connection) { Check_Object(connection); Replicator *replicator; replicator = connection->FindReplicator(message->targetID); if (replicator) { Check_Object(replicator); targetEntity.Remove(); targetEntity.Add(Cast_Object(Entity*, replicator)); // // This won't account for rotation. I'm not sure how well this will work on a component that can rotate, since I // don't have a full transform to get to this point. // // only in the case of a locked entity, is targetPoint relative to the entity targetOffsetFromEntity = targetPoint; Stuff::Point3D tempPoint3D = targetPoint; targetPoint.Multiply(tempPoint3D, targetEntity.GetCurrent()->GetLocalToWorld()); Entity * parentEntity = TraceToParent(targetEntity.GetCurrent()); targetMWObject.Remove(); if(parentEntity && parentEntity->IsDerivedFrom(MWObject::DefaultData)) targetMWObject.Add(Cast_Object(MWObject*, parentEntity)); if(targetMWObject.GetCurrent()) { targetMWObject.GetCurrent()->NotifyOfMissileLock(); } } } } targetPosition = targetPoint; timeInAir = 0.0f; m_fAllowArcMissile = true; m_fTurnOffSeek = false; m_nMissCount = 0; srmPitchVector.x = 0.0f; srmPitchVector.y = -0.6f; srmPitchVector.z = 0.8f; srmYTargetOffset = 0.0f; doesAMSDestroyThisFrame = false; amsExplosionResource = ResourceID::Null; // // We are going to store this to fix the odd angles when frame rate dips // lastWorldSpaceVelocity = initialWorldSpaceVelocity; m_nDoubleTeleportPreventor = 0; GetTargetCollisionVolume(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Entity* Missile::GetFirstAcceptableParentEntity(Entity* entity) { // // Walk up the chain looking for the next acceptable entity // Entity * curEntity = entity; while (curEntity) { if (!curEntity->IsDestroyed()) { CollisionVolume *volume = curEntity->GetHierarchicalVolume(); if (!volume) { volume = curEntity->GetSolidVolume(); } if (volume) { // // This breaks if the volume above doesn't have anything at it's center since we can't guarantee this is // a valid local as we can with the original target passed in. // targetOffsetFromEntity = volume->m_localSpaceBounds.localToParent; return curEntity; } } curEntity = curEntity->GetParentEntity(); } return NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::GetTargetCollisionVolume() { // // Walk up the chain looking for something with a collision volume // Entity * curEntity = targetEntity.GetCurrent(); CollisionVolume *volume = NULL; while (curEntity) { volume = curEntity->GetHierarchicalVolume(); if (!volume) { volume = curEntity->GetSolidVolume(); } if (volume && !volume->IsDestroyed()) { // // If we have a solid volume we should check for the first child. // CollisionVolume * childvolume = volume; // // Recurse down to the last node // while (childvolume) { ChainIteratorOf children(&childvolume->m_children); while ((childvolume = children.ReadAndNext()) != NULL) { if (childvolume && !childvolume->IsDestroyed()) { volume = childvolume; childvolume = NULL; // to kick us out of here break; } } } // while childvolume break; } curEntity = curEntity->GetParentEntity(); } target_collision_volume = volume; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Missile::~Missile() { DESTRUCTOR("Missile"); Check_Object(this); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::Reuse( const CreateMessage *message, ReplicatorID *base_id ) { Check_Pointer(this); Check_Object(message); BaseClass::Reuse(message, base_id); targetEntity.Remove(); targetOffsetFromEntity.x = 0.0f; targetOffsetFromEntity.y = 0.0f; targetOffsetFromEntity.z = 0.0f; targetPoint = message->targetPoint; if(message->targetID != ReplicatorID::Null) { Check_Object(Network::GetInstance()); Connection *connection = Network::GetInstance()->GetConnection(message->targetID.connectionID); if (connection) { Check_Object(connection); Replicator *replicator; replicator = connection->FindReplicator(message->targetID); if (replicator) { Check_Object(replicator); targetEntity.Remove(); targetEntity.Add(Cast_Object(Entity*, replicator)); targetMWObject.Remove(); // // This won't account for rotation. I'm not sure how well this will work on a component that can rotate, since I // don't have a full transform to get to this point. // // only in the case of a locked entity, is targetPoint relative to the entity targetOffsetFromEntity = targetPoint; Stuff::Point3D tempPoint3D = targetPoint; targetPoint.Multiply(tempPoint3D, targetEntity.GetCurrent()->GetLocalToWorld()); Entity * parentEntity = TraceToParent(targetEntity.GetCurrent()); targetMWObject.Remove(); if(parentEntity && parentEntity->IsDerivedFrom(MWObject::DefaultData)) targetMWObject.Add(Cast_Object(MWObject*, parentEntity)); } } } targetPosition = targetPoint; timeInAir = 0.0f; m_fAllowArcMissile = true; m_fTurnOffSeek = false; m_nMissCount = 0; srmPitchVector.x = 0.0f; srmPitchVector.y = -0.6f; srmPitchVector.z = 0.8f; srmYTargetOffset = 0.0f; // // We are going to store this to fix the odd angles when frame rate dips // lastWorldSpaceVelocity = initialWorldSpaceVelocity; m_nDoubleTeleportPreventor = 0; GetTargetCollisionVolume(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::PreCollisionExecute(Stuff::Time till) { Check_Object(this); PRECOLLISION_LOGIC("Missile"); timeInAir += GetTimeSlice(till); const GameModel *model = GetGameModel(); Check_Object(model); // //---------------------------------------------------- // Make sure we use post collision and call our parent //---------------------------------------------------- // Set_Statistic(Executed_Missile_Count, Executed_Missile_Count+1); BaseClass::PreCollisionExecute(till); // //------------------------------- // Apply the right type of forces //------------------------------- // Check_Object(executionState); int execution_state = executionState->GetState(); switch(execution_state) { case ExecutionStateEngine::LRMMotionState: { LRMSeekerModel(till); SeekingThrusterSimulation(till); LinearDragMotionSimulation(till); ProximityCheck(till); break; } case ExecutionStateEngine::MRMMotionState: { MRMSeekerModel(till); SeekingThrusterSimulation(till); LinearDragMotionSimulation(till); break; } case ExecutionStateEngine::SRMMotionState: { SRMSeekerModel(till); // // SRM's used to work like bullets with a fixed speed out of the gun. To make them behave more consistenly // I am using the same code path as the others even though it is a little bit of overkill for their seek model. // SeekingThrusterSimulation(till); LinearDragMotionSimulation(till); break; } case ExecutionStateEngine::SSRMMotionState: { SSRMSeekerModel(till); SeekingThrusterSimulation(till); LinearDragMotionSimulation(till); break; } case ExecutionStateEngine::LongTomMotionState: { LongTomSeekerModel(); SeekingThrusterSimulation(till); LinearDragMotionSimulation(till); break; } case ExecutionStateEngine::BombMotionState: { if(timeInAir >= model->maxLiveTime) { SentenceToDeathRow(); return; } SRMSeekerModel(till); // // SRM's used to work like bullets with a fixed speed out of the gun. To make them behave more consistenly // I am using the same code path as the others even though it is a little bit of overkill for their seek model. // SeekingThrusterSimulation(till); LinearDragMotionSimulation(till); break; } default: { // STOP(("This is not a valid state for Missile")); } } // // If the missile is not a seeking missile or not locked, let it seek one frame to get direction // then stop seeking from the next frame on. Otherwise when it gets where it was // going, even non-locked, it will turn around and try to come back // if (!m_fTurnOffSeek && !targetEntity.GetCurrent() && Small_Enough(targetPoint.y - targetPosition.y)) { Point3D local_direction; Point3D local_to_world_position; local_to_world_position = GetLocalToWorld(); local_direction.Subtract( //targetPosition, targetPoint, local_to_world_position ); UnitVector3D direction_to_target; Stuff::Scalar direction_to_target_length = local_direction.GetLengthSquared(); if (Small_Enough(direction_to_target_length)) { goto ExitFunction; } // // Normalize // direction_to_target_length = 1.0f / Sqrt(direction_to_target_length); direction_to_target.x = local_direction.x * direction_to_target_length; direction_to_target.y = local_direction.y * direction_to_target_length; direction_to_target.z = local_direction.z * direction_to_target_length; Stuff::Scalar differenceTargetAngleToPreviousVelocity; UnitVector3D current_direction; Stuff::Scalar current_direction_length = worldSpaceVelocity.linearMotion.GetLengthSquared(); if (Small_Enough(current_direction_length)) { goto ExitFunction; } // // Normalize // current_direction_length = 1.0f / Sqrt(current_direction_length); current_direction.x = worldSpaceVelocity.linearMotion.x * current_direction_length; current_direction.y = worldSpaceVelocity.linearMotion.y * current_direction_length; current_direction.z = worldSpaceVelocity.linearMotion.z * current_direction_length; differenceTargetAngleToPreviousVelocity = direction_to_target * current_direction; // If we are aimed close enough the target, and we are physically close enough, then just assume we are at the target if (differenceTargetAngleToPreviousVelocity >= 0.999f) { m_fTurnOffSeek = true; // // Aim right at the spot now // Point3D local_direction; Point3D local_to_world_position; local_to_world_position = GetLocalToWorld(); local_direction.Subtract( targetPosition, local_to_world_position ); LinearMatrix4D aligned_matrix = entityElement->GetNewLocalToParent(); aligned_matrix.AlignLocalAxisToWorldVector(local_direction, Z_Axis, X_Axis, Y_Axis); SetNewLocalToParent(aligned_matrix); Stuff::Scalar linearMotionSpeed = localSpaceVelocity.linearMotion.GetApproximateLength(); localSpaceVelocity.angularMotion.Zero(); localSpaceVelocity.linearMotion.z = linearMotionSpeed; localSpaceVelocity.linearMotion.y = 0.0f; localSpaceVelocity.linearMotion.x = 0.0f; // reuse for acceleration linearMotionSpeed = localSpaceAcceleration.linearMotion.GetApproximateLength(); localSpaceAcceleration.linearMotion.z = linearMotionSpeed; localSpaceAcceleration.linearMotion.y = 0.0f; localSpaceAcceleration.linearMotion.x = 0.0f; // localSpaceVelocity.angularMotion.Zero(); // make sure we don't end up in a spin when we stop orienting. /* // // In order to preserve it's current acceleration, but make sure it hits the target we need to // find the normalized direction that we want to go ( // Stuff::Scalar current_acceleration_length = worldSpaceAcceleration.linearMotion.GetApproximateLength(); worldSpaceAcceleration.linearMotion.Multiply(current_direction, current_acceleration_length); current_acceleration_length = localSpaceAcceleration.linearMotion.GetApproximateLength(); localSpaceAcceleration.linearMotion.Multiply(current_direction, current_acceleration_length); */ // // We need to change the target point to the extreme distance of the current direction, then we can // still seek as far as the thruster model is concerned. If we don't do this, then the missiles // will be affected by torque and have problems hitting the target. // Stuff::Point3D current_location; current_location = GetLocalToWorld(); Stuff::Line3D target_line(current_location, direction_to_target, 1000.0f); target_line.FindEnd(&targetPoint); targetPosition = targetPoint; // localSpaceAcceleration.angularMotion.Zero(); // worldSpaceVelocity.angularMotion.Zero(); // make sure we don't end up in a spin when we stop orienting. // worldSpaceAcceleration.angularMotion.Zero(); } } ExitFunction: ; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::LinearDragMotionSimulation(Stuff::Time till) { // // Before doing the real math, see if we have any easy outs... // if (m_fTurnOffSeek || timeInAir >= burnTime) { BaseClass::LinearDragMotionSimulation(till); return; } // // Has the last slice of time been greater than the time it would take to turn to that angle? // If so, just aim it at the target; Point3D local_direction; Point3D local_to_world_position; local_to_world_position = GetLocalToWorld(); local_direction.Subtract( targetPosition, local_to_world_position ); UnitVector3D new_direction; Stuff::Scalar new_direction_length = local_direction.GetLengthSquared(); if (Small_Enough(new_direction_length)) { BaseClass::LinearDragMotionSimulation(till); return; } // // Normalize // new_direction_length = 1.0f / Sqrt(new_direction_length); new_direction.x = local_direction.x * new_direction_length; new_direction.y = local_direction.y * new_direction_length; new_direction.z = local_direction.z * new_direction_length; Stuff::Scalar differenceTargetAngleToPreviousVelocity; UnitVector3D previousDirection; Stuff::Scalar previousDirection_length = worldSpaceVelocity.linearMotion.GetLengthSquared(); if (Small_Enough(previousDirection_length)) { BaseClass::LinearDragMotionSimulation(till); return; } // // Normalize // previousDirection_length = 1.0f / Sqrt(previousDirection_length); previousDirection.x = worldSpaceVelocity.linearMotion.x * previousDirection_length; previousDirection.y = worldSpaceVelocity.linearMotion.y * previousDirection_length; previousDirection.z = worldSpaceVelocity.linearMotion.z * previousDirection_length; differenceTargetAngleToPreviousVelocity = new_direction * previousDirection; // If we are aimed close enough the target, and we are physically close enough, then just assume we are at the target lastWorldSpaceVelocity = worldSpaceVelocity; BaseClass::LinearDragMotionSimulation(till); UnitVector3D currentDirection; Stuff::Scalar currentDirection_length = worldSpaceVelocity.linearMotion.GetLengthSquared(); if (Small_Enough(currentDirection_length)) return; // // Normalize // currentDirection_length = 1.0f / Sqrt(currentDirection_length); currentDirection.x = worldSpaceVelocity.linearMotion.x * currentDirection_length; currentDirection.y = worldSpaceVelocity.linearMotion.y * currentDirection_length; currentDirection.z = worldSpaceVelocity.linearMotion.z * currentDirection_length; Stuff::Scalar differencePreviousVelocityToCurrentVelocity; differencePreviousVelocityToCurrentVelocity = previousDirection * currentDirection; if (differencePreviousVelocityToCurrentVelocity < differenceTargetAngleToPreviousVelocity) { // // If this is true then the missile is oversteering. What this translates to is that the missile // turned more than was necessary last frame to end up pointing at the target. This is easy to do // on slower frame rates since the momentum carries them past their target during the long break between // frames. In order to compensate for this we use the following logic: // if (missile turned passed target last frame) // point missile at the target // kill the angular motion and acceleration, so it stays pointing at it for the next frame. // // // Some variables already defined at this point: // new_direction - this is the normalized world space direction to the target prior to // simulating the linear drag/propulsion calculations // currentDirection - this is the direction the missile is moving in after the simulation // cheating since the missiles parent should be the world, we aren't multiplying by it's parent local_to_world_position = GetLocalToWorld(); local_direction.Subtract( targetPosition, local_to_world_position ); LinearMatrix4D aligned_matrix = entityElement->GetNewLocalToParent(); aligned_matrix.AlignLocalAxisToWorldVector(local_direction, Z_Axis, X_Axis, Y_Axis); SetNewLocalToParent(aligned_matrix); Stuff::Scalar linearMotionSpeed = localSpaceVelocity.linearMotion.GetApproximateLength(); localSpaceVelocity.angularMotion.Zero(); localSpaceVelocity.linearMotion.z = linearMotionSpeed; localSpaceVelocity.linearMotion.y = 0.0f; localSpaceVelocity.linearMotion.x = 0.0f; // reuse for acceleration linearMotionSpeed = localSpaceAcceleration.linearMotion.GetApproximateLength(); localSpaceAcceleration.linearMotion.z = linearMotionSpeed; localSpaceAcceleration.linearMotion.y = 0.0f; localSpaceAcceleration.linearMotion.x = 0.0f; // // new_direction is in world space // /* Stuff::Point3D p1, p2; p1 = new_direction; p2.MultiplyByInverse(p1, entityElement->GetNewLocalToParent()); // Stuff::Point3D p2; // p2 = currentDirection; // Stuff::Point3D p3; // p3.Subtract(p1, p2); // LinearMatrix4D d; // d.BuildRotation(p3); // d.BuildTranslation(Point3D::Identity); Stuff::LinearMatrix4D new_local_to_parent; // new_local_to_parent.Multiply(entityElement->GetNewLocalToParent(), d); // p1 = entityElement->GetNewLocalToParent(); // new_local_to_parent.BuildTranslation(p1); // new_local_to_parent.Normalize(); new_local_to_parent.BuildRotation(p2); p1 = entityElement->GetNewLocalToParent(); new_local_to_parent.BuildTranslation(p1); SetNewLocalToParent(new_local_to_parent); // new_local_to_parent.BuildTranslation(new_translation); /* localSpaceAcceleration.angularMotion.Zero(); worldSpaceAcceleration.angularMotion.Zero(); localSpaceAcceleration.linearMotion.x = 0.0f; localSpaceAcceleration.linearMotion.y = 0.0f; */ // // In order to preserve it's current acceleration, but make sure it hits the target we need to // find the normalized direction that we want to go ( // /* Stuff::Scalar current_acceleration_length = worldSpaceAcceleration.linearMotion.GetApproximateLength(); worldSpaceAcceleration.linearMotion.Multiply(new_direction, current_acceleration_length); current_acceleration_length = localSpaceAcceleration.linearMotion.GetApproximateLength(); localSpaceAcceleration.linearMotion.Multiply(new_direction, current_acceleration_length); */ } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::ProximityCheck(Stuff::Time till) { const GameModel *model = GetGameModel(); Point3D targetDistance; Point3D local_to_parent_position; Entity *target_entity = targetEntity.GetCurrent(); Point3D targetLocalToOurParent; if (!GetParentEntity()) return; // // To avoid missiles chasing dead things we need to make sure we cut the gas when this is detected // // make sure we burn a little at least (.5) if (target_entity && target_entity->IsDestroyed() && timeInAir < burnTime/* && timeInAir > 0.5f*/) { m_nMissCount++; // this makes sure we run for at least a couple of frames to orient the missile correctly // This should no longer be needed since we'll just turn off seeking // timeInAir = model->burnTime; } if(timeInAir >= burnTime) { return; } // // Don't do // if (!target_entity || (target_entity->IsDerivedFrom(Map::DefaultData)) || m_fTurnOffSeek) { return; } LinearMatrix4D world_to_entity = LinearMatrix4D::Identity; world_to_entity.Invert(target_entity->GetLocalToWorld()); ElementRenderer::Element *element = target_entity->GetElement(); Check_Object(element); LinearMatrix4D world_to_new_world = LinearMatrix4D::Identity; world_to_new_world.Multiply(world_to_entity, element->GetNewLocalToParent()); LinearMatrix4D targetPositionMatrix = LinearMatrix4D::Identity; // world_to_entity being reused as a temp. targetPositionMatrix.BuildTranslation(targetPosition); world_to_entity.Multiply(targetPositionMatrix, world_to_new_world); targetPosition = world_to_entity; targetLocalToOurParent.MultiplyByInverse(targetPosition, GetParentEntity()->GetLocalToWorld()); local_to_parent_position = GetLocalToParent(); targetDistance.Subtract( targetLocalToOurParent, local_to_parent_position ); Stuff::Scalar distanceSquared = targetDistance.GetLengthSquared(); if ((m_nDoubleTeleportPreventor < 5) && target_entity && (target_entity->IsDerivedFrom(MWObject::DefaultData)) && (distanceSquared < model->proximityFuseDistance)) { /* // Since it's going to die, we don't need to know about the orientation // // Can I call the CollisionHandler directly from here? Entity::CollisionData collision_data; UnitVector3D normal; local_to_world_position.Negate(targetDistance); // reusing local_to_world_position as temp normal.Normalize(local_to_world_position); collision_data.m_normal = normal; collision_data.m_otherEntity = target_entity; collision_data.m_otherVolume = NULL; collision_data.m_volume = NULL; collision_data.m_material = collision_query.m_material; collision_data.m_timeSlice = (line.m_length * t) * collider->GetTimeSlice(till); line.FindEnd(&collision_data.m_worldIntersectionPoint); DynamicArrayOf *array = new DynamicArrayOf(1); (*array)[0] = collision_data; PostCollision(); SentenceToDeathRow(); */ // // If due to a data error (collision volume that doesn't have geometry in center) or some other mishap, we need to prevent // the missile from continually teleporting. // // m_nDoubleTeleportPreventor++; LinearMatrix4D targetDirectionLocalToWorld = LinearMatrix4D::Identity; // // Try going just a little bit through the point we are trying to hit to maximize collision chances // targetDirectionLocalToWorld.BuildTranslation(targetLocalToOurParent); targetDirectionLocalToWorld.AlignLocalAxisToWorldVector(targetDistance, Z_Axis, Y_Axis, X_Axis); SetNewLocalToParent(targetDirectionLocalToWorld); // // We have to immediately sync, or we have problems with splash damage location. // ** Seeing if I can do this as part of the damage handler instead // SyncMatrices(true); } else if (m_nDoubleTeleportPreventor >= 5) { // // Let it get out of range before it tries to teleport again. // if (distanceSquared > (model->proximityFuseDistance + 1.0f)) m_nDoubleTeleportPreventor = 0; m_nMissCount++; } } /* //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::ProximityCheck(Stuff::Time till) { const GameModel *model = GetGameModel(); Point3D targetDistance; Point3D local_to_parent_position; Point3D targetLocalToOurParent; if (!GetParentEntity()) return; targetLocalToOurParent.MultiplyByInverse(targetPosition, GetParentEntity()->GetLocalToWorld()); local_to_parent_position = GetLocalToParent(); targetDistance.Subtract( targetLocalToOurParent, local_to_parent_position ); Entity *target_entity = targetEntity.GetCurrent(); Stuff::Scalar distanceSquared = targetDistance.GetLengthSquared(); if (target_entity && (target_entity->IsDerivedFrom(MWObject::DefaultData)) && (distanceSquared < model->proximityFuseDistance)) { LinearMatrix4D targetDirectionLocalToWorld = LinearMatrix4D::Identity; targetDirectionLocalToWorld.BuildTranslation(targetLocalToOurParent); // Since it's going to die, we don't need to know about the orientation // // Can I call the CollisionHandler directly from here? Entity::CollisionData collision_data; UnitVector3D normal; Stuff::Scalar new_direction_length_inverse_sqrt = targetDistance.GetLengthSquared(); Stuff::Scalar new_direction_length = Sqrt(new_direction_length_inverse_sqrt); if (Small_Enough(new_direction_length)) { normal = UnitVector3D::Identity; // // Keep our current direction in this case // targetDirectionLocalToWorld.BuildRotation(worldSpaceVelocity.linearMotion); } else { // // Normalize // new_direction_length_inverse_sqrt = 1.0f / Sqrt(new_direction_length); normal.x = -targetDistance.x * new_direction_length_inverse_sqrt; normal.y = -targetDistance.y * new_direction_length_inverse_sqrt; normal.z = -targetDistance.z * new_direction_length_inverse_sqrt; // // If we have a true line, align our direction to it // targetDirectionLocalToWorld.AlignLocalAxisToWorldVector(targetDistance, Z_Axis, Y_Axis, X_Axis); } // // We now have our position, and possibly our rotation so commit it. // SetNewLocalToParent(targetDirectionLocalToWorld); collision_data.m_normal = normal; if (target_collision_volume && !target_collision_volume->IsDestroyed()) { collision_data.m_otherVolume = target_collision_volume; collision_data.m_material = target_collision_volume->m_material; } else { collision_data.m_otherVolume = NULL; collision_data.m_material = MechWarrior4::MaterialCount; } collision_data.m_otherEntity = target_entity; collision_data.m_volume = NULL; collision_data.m_timeSlice = GetTimeSlice(till); collision_data.m_worldIntersectionPoint = targetPosition; DynamicArrayOf *array = new DynamicArrayOf(1); (*array)[0] = collision_data; PostCollision(array); SentenceToDeathRow(); } } */ //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool Missile::CollisionHandler( Stuff::LinearMatrix4D *new_position, Stuff::DynamicArrayOf *collisions ) { Check_Object(this); Check_Object(collisions); Verify(collisions->GetLength() == 1); Verify(EntityManager::GetInstance()->IsInPostCollisionExecution(this)); return BaseClass::CollisionHandler(new_position, collisions); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::SeekingThrusterSimulation(Stuff::Time till) { Check_Object(this); const GameModel *model = GetGameModel(); Check_Object(model); #if defined(MISSILE_BUG) SPEW((MISSILE_BUG, "Position: %f %f %f", GetLocalToWorld()(3,0), GetLocalToWorld()(3,1), GetLocalToWorld()(3,2))); SPEW((MISSILE_BUG, "LocalSpaceVelocityLinearMotion: %f %f %f", localSpaceVelocity.linearMotion.x, localSpaceVelocity.linearMotion.y, localSpaceVelocity.linearMotion.z)); SPEW((MISSILE_BUG, "LocalSpaceVelocityAngularMotion: %f %f %f", localSpaceVelocity.angularMotion.x, localSpaceVelocity.angularMotion.y, localSpaceVelocity.angularMotion.z)); #endif #if defined(MISSILE_BUG) SPEW((MISSILE_BUG, "TargetPosition: %f %f %f", targetPosition.x, targetPosition.y, targetPosition.z)); #endif #if defined(TARGET_BUG) SPEW((TARGET_BUG, "TargetPosition: %f %f %f", targetPosition.x, targetPosition.y, targetPosition.z)); #endif if(timeInAir <= burnTime) { UnitVector3D new_direction; // //----------------------------- //Find direction in local space //----------------------------- // Stuff::Point3D local_direction; Stuff::Point3D temp_target_location; temp_target_location = targetPosition; // Stuff::Scalar timeSlice = GetTimeParameter(till); // local_direction.MultiplyByInverse( // temp_target_location, // GetLocalToWorld() // ); //new_direction = UnitVector3D::Identity; // local_direction.y = 0; // Stuff::Scalar new_direction_length = local_direction.GetLengthSquared(); // // Gravity isn't supposedly acting on these objects but they seem to draw up short. // // Stuff::Scalar gravity_compensation_factor = new_direction_length; // Max_Clamp(gravity_compensation_factor, 2.0f); // temp_target_location.y += (g_Gravity * timeSlice * timeSlice) * gravity_compensation_factor; // // Re-get the direction since we are shifting the target position // local_direction.MultiplyByInverse( temp_target_location, GetLocalToWorld() ); #if defined(MISSILE_BUG) SPEW((MISSILE_BUG, "TargetPositionInLocalSpace: %f %f %f", local_direction.x, local_direction.y, local_direction.z)); #endif Stuff::Scalar new_direction_length = local_direction.GetLengthSquared(); Vector3D local_steer_direction; // if (!m_fTurnOffSeek) // { if (Small_Enough(new_direction_length)) { new_direction = Vector3D::Forward; } else { // // Normalize // new_direction_length = 1.0f / Sqrt(new_direction_length); new_direction.x = local_direction.x * new_direction_length; new_direction.y = local_direction.y * new_direction_length; new_direction.z = local_direction.z * new_direction_length; } // //------------------------------------------- //Find the direction that we want to steer to //------------------------------------------- // local_steer_direction.Cross( Vector3D::Forward, new_direction ); // //---------------------------------- //We need to find the degree to turn //---------------------------------- // Scalar degree_to_turn; Scalar sine = 0.0f; if (!Small_Enough(local_steer_direction)) { sine = local_steer_direction.GetLength(); } if(Small_Enough(sine)) { degree_to_turn = 0.0f; local_steer_direction = Vector3D::Identity; } else { if(local_steer_direction.y < 0.0f) { sine = -sine; } degree_to_turn = Arctan(sine, new_direction.z); #if defined(MISSILE_BUG) SPEW((MISSILE_BUG, "RadianToTurn: %f", degree_to_turn)); #endif // // We need to clamp the turn, but it has to be clamped based on the time slice, it can't just be clamped a fixed amount per frame // or the slower frame rates won't be able to turn well. // Stuff::Scalar timeSlice = GetTimeParameter(till); Scalar max_angle = model->maxTurnAngle * timeSlice ; Clamp(degree_to_turn, -max_angle, max_angle); #if defined(MISSILE_BUG) SPEW((MISSILE_BUG, "ClampedRadianToTurn: %f", degree_to_turn)); #endif degree_to_turn /= sine; local_steer_direction *= degree_to_turn; } #if defined(MISSILE_BUG) SPEW((MISSILE_BUG, "AxisOfRotation: %f %f %f", local_steer_direction.x, local_steer_direction.y, local_steer_direction.z)); #endif // //------------------------------------------- //Negate the z value of the turn direction to //------------------------------------------- // LinearMatrix4D force_to_parent = LinearMatrix4D::Identity; force_to_parent.BuildRotation(local_steer_direction); UnitVector3D force_forward; force_to_parent.GetLocalForwardInWorld(&force_forward); force_forward.x = -force_forward.x; force_forward.y = -force_forward.y; // force_forward.z = -force_forward.z; local_steer_direction.Multiply( force_forward, model->thrusterAcceleration ); #if defined(MISSILE_BUG) SPEW((MISSILE_BUG, "LocalForce: %f %f %f\n", local_steer_direction.x, local_steer_direction.y, local_steer_direction.z)); #endif ApplyLocalAcceleration( local_steer_direction, Vector3D::Backward ); // // If Dot Product of the angles between current direction and the direction to target // are small enough, then kill the angular motion. It's a cheat to prevent over // correction. // Stuff::Scalar differenceTargetAngleToActualAngle; local_direction.MultiplyByInverse( temp_target_location, GetLocalToWorld()); new_direction_length = local_direction.GetLengthSquared(); if (Small_Enough(new_direction_length)) return; // // Normalize // new_direction_length = 1.0f / Sqrt(new_direction_length); new_direction.x = local_direction.x * new_direction_length; new_direction.y = local_direction.y * new_direction_length; new_direction.z = local_direction.z * new_direction_length; differenceTargetAngleToActualAngle = new_direction * UnitVector3D::Forward; if ( differenceTargetAngleToActualAngle >= 0.985f) { localSpaceVelocity.angularMotion.Zero(); localSpaceAcceleration.angularMotion.Zero(); Stuff::Scalar speed = localSpaceVelocity.linearMotion.GetApproximateLength(); localSpaceVelocity.linearMotion.Multiply(new_direction, speed); /* LinearMatrix4D targetDirectionLocalToWorld; targetDirectionLocalToWorld.BuildRotation(new_direction); LinearMatrix4D localToParent; LinearMatrix4D oldLocalToWorld; oldLocalToWorld = parentEntity->GetLocalToWorld(); localToParent.Multiply(targetDirectionLocalToWorld, oldLocalToWorld.Invert()); SetNewLocalToParent(localToParent); */ } // } /* else { Stuff::UnitVector3D current_direction; GetLocalToWorld().GetLocalForwardInWorld(¤t_direction); /* Stuff::Scalar new_direction_length = current_direction.GetLengthSquared(); if (Small_Enough(new_direction_length)) return; // // Normalize // new_direction_length = 1.0f / Sqrt(new_direction_length); new_direction.x = current_direction.x * new_direction_length; new_direction.y = current_direction.y * new_direction_length; new_direction.z = current_direction.z * new_direction_length; // * / current_direction.x = -current_direction.x; current_direction.y = -current_direction.y; local_steer_direction.Multiply( current_direction, model->thrusterAcceleration ); localSpaceAcceleration.linearMotion += local_steer_direction; ApplyLocalAcceleration( local_steer_direction, Vector3D::Backward ); // Stuff::Scalar speed = localSpaceVelocity.linearMotion.GetApproximateLength(); // localSpaceVelocity.linearMotion.Multiply(new_direction, speed); } */ } else { // //------------------ //We are out of fuel //------------------ // #if defined(MISSILE_BUG) SPEW((MISSILE_BUG, "We are out of fuel")); #endif localSpaceDrag = Motion3D::Identity; UnitVector3D local_down; GetLocalToWorld().GetWorldDownInLocal(&local_down); localSpaceAcceleration.linearMotion.Multiply( local_down, g_Gravity ); if(trailEffect.GetCurrent()) trailEffect.GetCurrent()->executionState->RequestState(Effect::ExecutionStateEngine::StoppingState); #if defined(MISSILE_BUG) UnitVector3D local_forward,local_up; GetLocalToWorld().GetLocalForwardInWorld(&local_forward); GetLocalToWorld().GetLocalUpInWorld(&local_up); SPEW((MISSILE_BUG, "Local Down: %f %f %f", local_down.x, local_down.y, local_down.z)); SPEW((MISSILE_BUG, "Local Forward in Local: %f %f %f", local_forward.x, local_forward.y, local_forward.z)); SPEW((MISSILE_BUG, "Local Up in Local: %f %f %f", local_up.x, local_up.y, local_up.z)); SPEW((MISSILE_BUG, "WorldSpaceVelocityLinearMotion: %f %f %f\n", worldSpaceVelocity.linearMotion.x, worldSpaceVelocity.linearMotion.y, worldSpaceVelocity.linearMotion.z)); #endif } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::PostCollisionExecute(Stuff::Time till) { Check_Object(this); Verify(Close_Enough(GetLocalToParent(), GetLocalToWorld())); POSTCOLLISION_LOGIC("Missile"); lastParameterization = till; initialLocalToParent = GetLocalToParent(); initialWorldSpaceVelocity = worldSpaceVelocity, initialWorldSpaceAcceleration = worldSpaceAcceleration; if(doesAMSDestroyThisFrame) { CreateEffect(amsExplosionResource); SentenceToDeathRow(); } BaseClass::PostCollisionExecute(till); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::LRMSeekerModel(Stuff::Time till) { Check_Object(this); if (m_fTurnOffSeek) { localSpaceVelocity.angularMotion.Zero(); localSpaceAcceleration.angularMotion.Zero(); return; // leave the current direction alone since we're done turning } // // Don't continually circle, but just burn off fuel in the current direction instead. // if (m_nMissCount > 1) { m_fTurnOffSeek = true; localSpaceVelocity.angularMotion.Zero(); localSpaceAcceleration.angularMotion.Zero(); return; } Point3D local_to_world; local_to_world = GetLocalToWorld(); CalculateTargetPosition(&targetPosition, till); targetPoint = targetPosition; // // Decide whether or not to make this missile arc in it's path. // Stuff::Scalar diffY = (targetPosition.y - local_to_world.y); if (diffY <= 50.0f) { Vector3D local_direction; local_direction.Subtract( targetPosition, local_to_world ); Scalar true_distance = local_direction.GetApproximateLength(); local_direction.y = 0; Scalar arc_value; arc_value = local_direction.GetLength(); // //------------------------------------------------------------ //Right here we need to tell the target how far we are from it //------------------------------------------------------------ // if((true_distance < 50.0f) && (DoesAMSDestroy())) { ConveyDistanceToTarget(true_distance); } arc_value -= 200.0f; if(m_fAllowArcMissile && arc_value > 0.0f) { arc_value /= 2.0f; //reduced the angle since our curve code might get in the way. Max_Clamp(arc_value, 150.0f); targetPosition.y += ((1.0f / (timeInAir + 0.5f)) * arc_value); // targetPosition.y += arc_value; } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::MRMSeekerModel(Stuff::Time till) { Check_Object(this); if (m_fTurnOffSeek) { localSpaceVelocity.angularMotion.Zero(); localSpaceAcceleration.angularMotion.Zero(); return; // leave the current direction alone since we're done turning } Point3D local_to_world; local_to_world = GetLocalToWorld(); CalculateTargetPosition(&targetPosition, till); if((targetPosition.y - local_to_world.y) <= 50.0) { Vector3D local_direction; local_direction.Subtract( targetPosition, local_to_world ); Scalar true_distance = local_direction.GetApproximateLength(); /* local_direction.y = 0; Scalar arc_value; arc_value = local_direction.GetLength(); arc_value -= 140.0f; */ // //------------------------------------------------------------ //Right here we need to tell the target how far we are from it //------------------------------------------------------------ // if((true_distance < 50.0f) && (DoesAMSDestroy())) { ConveyDistanceToTarget(true_distance); } /* if(arc_value > 0.0f) { arc_value /= 3; Max_Clamp(arc_value, 120.0f); targetPosition.y += arc_value; } */ } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::LongTomSeekerModel() { Check_Object(this); Point3D local_to_world; local_to_world = GetLocalToWorld(); if((targetPosition.y - local_to_world.y) <= 50.0) { Vector3D local_direction; local_direction.Subtract( targetPosition, local_to_world ); local_direction.y = 0; Scalar arc_value; arc_value = local_direction.GetLength(); arc_value -= 140.0f; if(arc_value > 0.0f) { arc_value /= 3; Max_Clamp(arc_value, 120.0f); targetPosition.y += arc_value; } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::SSRMSeekerModel(Stuff::Time till) { Check_Object(this); if (m_fTurnOffSeek) { localSpaceVelocity.angularMotion.Zero(); localSpaceAcceleration.angularMotion.Zero(); return; // leave the current direction alone since we're done turning } Point3D local_to_world; local_to_world = GetLocalToWorld(); CalculateTargetPosition(&targetPosition, till); Point3D origin; origin = GetLocalToWorld(); Vector3D local_direction; local_direction.Subtract( targetPosition, origin ); Scalar true_distance = local_direction.GetApproximateLength(); // //------------------------------------------------------------ //Right here we need to tell the target how far we are from it //------------------------------------------------------------ // if((true_distance < 50.0f) && (DoesAMSDestroy())) { ConveyDistanceToTarget(true_distance); } #if 0 if(local_direction.GetLength() > 80.0f) { // //-------------------------------------------- //Now we want to cast a ray towards the ground //-------------------------------------------- // Vector3D ray_direction; ray_direction.Multiply(srmPitchVector, GetLocalToWorld()); Stuff::Line3D line; line.length = 20.0f; line.direction = ray_direction; line.origin = origin; // // Prepare query // Stuff::Normal3D normal; CollisionQuery query(&line, &normal, CanBeShotFlag, this); // // Cast ray // Check_Object(CollisionGrid::Instance); Entity *entity_hit = CollisionGrid::Instance->ProjectLine(&query); if(entity_hit) { if(entity_hit != Map::GetInstance()) { SPEW(("daberger", "I did not hit the map, and I hit %s", (const char *)entity_hit->instanceName)); srmYTargetOffset += (20 - line.length); SPEW(("daberger", "We are adding to the offset %f", srmYTargetOffset)); } else { SPEW(("daberger", "I hit the fucking Map")); srmYTargetOffset = (20 - line.length); } } else { srmYTargetOffset = 0.0f; SPEW(("daberger", "We have cleared the offset")); } targetPoint.y += srmYTargetOffset; SPEW(("daberger", "The Y target is %f\n", targetPoint.y)); } #endif } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::SRMSeekerModel(Stuff::Time till) { Check_Object(this); if (m_fTurnOffSeek) { localSpaceVelocity.angularMotion.Zero(); localSpaceAcceleration.angularMotion.Zero(); return; // leave the current direction alone since we're done turning } /* if(timeInAir <= burnTime) { Mover::StraightLineMotionSimulation(till); } else { localSpaceDrag = Motion3D::Identity; UnitVector3D local_down; GetLocalToWorld().GetWorldDownInLocal(&local_down); localSpaceAcceleration.linearMotion.Multiply( local_down, g_Gravity ); LinearDragMotionSimulation(till); if(trailEffect.GetCurrent()) trailEffect.GetCurrent()->executionState->RequestState(Effect::ExecutionStateEngine::StoppingState); } */ } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::ConveyDistanceToTarget(Stuff::Scalar distance) { Check_Object(this); if(targetMWObject.GetCurrent() == NULL) return; Point3D local_to_world; local_to_world = GetLocalToWorld(); amsExplosionResource = targetMWObject.GetCurrent()->ReactToMissileApproach(distance, local_to_world); if(amsExplosionResource != ResourceID::Null) doesAMSDestroyThisFrame = true; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::CreateEffect(const Adept::ResourceID& effect_id) { if (effect_id == ResourceID::Null) return; Resource resource(effect_id); Verify(resource.DoesResourceExist()); ClassID class_ID; class_ID = Entity::GetClassIDFromDataListID(effect_id); Verify(class_ID != NullClassID); LinearMatrix4D effect_location = GetLocalToWorld(); Effect::CreateMessage effect_create_message( sizeof(Effect::CreateMessage), DefaultEventPriority, Entity::CreateMessage::DefaultFlags, class_ID, Effect::DefaultFlags, effect_id, effect_location, 0.0f, Entity::ExecutionStateEngine::AlwaysExecuteState, NameTable::NullObjectID, Entity::DefaultAlignment ); MemoryStream stream(&effect_create_message, effect_create_message.messageLength); Effect *entity; ReplicatorID effect_rep_id = Connection::Hermit->GetNextReplicatorID(); entity = Cast_Object(Effect *,Entity::CreateEntity(&stream, &effect_rep_id, false)); Check_Object(entity); Check_Object(Map::GetInstance()); Map::GetInstance()->AddChild(entity); entity->SyncMatrices(true); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Missile::TestInstance() const { Verify(IsDerivedFrom(DefaultData)); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Stuff::Point3D& Missile::CalculateTargetPosition(Stuff::Point3D *new_position, Time till) { Check_Object(this); Check_Pointer(new_position); // //------------------------------------------------------------------- // If we are targeted on something that isn't the map, figure out its // current position //------------------------------------------------------------------- // Entity *target_entity = targetEntity.GetCurrent(); MWObject * target_mwobject = targetMWObject.GetCurrent(); if((target_entity) && (!target_entity->IsDerivedFrom(Map::DefaultData))) { if (target_entity->IsDestroyed()) { target_collision_volume = NULL; target_entity = GetFirstAcceptableParentEntity(target_entity); if (target_entity) { Check_Object(target_entity); targetEntity.Remove(); targetEntity.Add(target_entity); } // // If we don't have anything valid to aim at, then just find a spot on the ground below the object we were // previously aiming at // else { targetEntity.Remove(); // // Cast a ray down from the last known position. // Vector3D ray_direction = Vector3D::Down; Stuff::Line3D line; line.m_length = 1000.0f; line.m_direction = ray_direction; line.m_origin = *new_position; line.m_origin.y += 5.0f; // // Prepare query // Stuff::Normal3D normal; CollisionQuery query(&line, &normal, CanBeShotFlag, this); CollisionGrid::Instance->ProjectLine(&query); line.FindEnd(new_position); } } if (target_entity && !target_entity->IsDestroyed()) { // // Rather than indirectly getting the collision volume, I'll just get where the original raycast hit // relative to the entity that we are tracking. // Stuff::Point3D target_local_to_world; (*new_position).Multiply(targetOffsetFromEntity, target_entity->GetLocalToWorld()); // //------------------------------------------------------------------ // If it is an MWObject, look for the center torso damage object and // seek its collision volume //------------------------------------------------------------------ // // // Make sure our current target_collision_volume is valid first... // // if (!target_collision_volume || target_collision_volume->IsDestroyed()) // { // GetTargetCollisionVolume(); // } // // Aim at the targetOffset in this case. // // if (target_collision_volume) // we didn't find one previously // { // *new_position = target_collision_volume->m_worldSpaceBounds.localToParent; // } // else // { // *new_position = target_entity->GetLocalToWorld(); // } // if (target_mwobject ) { Check_Object(target_mwobject); // //--------------------------------------------------------------- // If this is a vehicle, we use dead reckoning to figure out // the best point along its potential path for us to hit it, // given our two relative speeds //--------------------------------------------------------------- // if (target_mwobject->IsDerivedFrom(Vehicle::DefaultData)) { // Stuff::Point3D hit_point_offset; Stuff::LinearMatrix4D mw_object_ltw; Stuff::LinearMatrix4D mw_object_ltw_inverse; Stuff::LinearMatrix4D target_ltp_mwobject; mw_object_ltw = target_mwobject->GetLocalToWorld(); mw_object_ltw_inverse.Invert(mw_object_ltw); target_ltp_mwobject.Multiply(target_entity->GetLocalToWorld(), mw_object_ltw_inverse); // // If we use a fixed offset like this is it true when we rotate in the prediction? // Probably since we are only guessing at linear motion... // hit_point_offset.MultiplyByInverse(*new_position,(Stuff::Point3D)mw_object_ltw); Stuff::Scalar my_velocity(worldSpaceVelocity.linearMotion.GetApproximateLength()); Vehicle* v = Cast_Object(Vehicle*,target_mwobject); Check_Object(v); Stuff::Point3D distanceToTarget; distanceToTarget.Subtract((Stuff::Point3D)v->GetLocalToWorld(), (Stuff::Point3D)GetLocalToWorld()); if (!Small_Enough(distanceToTarget.GetLengthSquared())) { Stuff::Scalar time = TimeToCollide(v->GetLocalToWorld(), v->currentSpeedMPS, (Stuff::Point3D)GetLocalToWorld(), my_velocity); Stuff::Scalar timeSlice = GetTimeParameter(till); if (time < 0) { time = 0.0f; // negative time is bad; it means we will never hit -- so we clamp it at 0 minimum } else if (time > 2) { time = 2.0f; // no point predicting more than, say, 2 seconds in advance } time += timeSlice; // compensate for the last frame. v->EstimateFuturePosition(new_position,time); // // Right now the missiles won't alter their target based on any animation information. Jerry needs to work with me // to find a cheap method for finding out this info. We may not have a cheaper way than storing the index and querying // the array every frame // /* ElementRenderer::Element *element = target_mwobject->animationArray[vehiclerootJointIndex]->GetElement(); Check_Object(element); // // Multiply by the root's localtoparent // camera_local_to_torso.Multiply(tempJumpMatrix, element->GetLocalToParent()); */ mw_object_ltw.BuildTranslation(*new_position); mw_object_ltw_inverse.Multiply(target_ltp_mwobject, mw_object_ltw); (*new_position).Multiply(targetOffsetFromEntity, mw_object_ltw_inverse); } // we were able to do a prediction (!smallenough) } // it's a vehicle } // if target_mwobject has anything in it // //---------------------------------------------------------- // The object is something else, so just seek it where it is //---------------------------------------------------------- // // else // { // *new_position = target_entity->GetLocalToWorld(); // } } // is the entity we currently have !destroyed } // if we have an enitity and it's not the map // //--------------------------------------------------- // If we hit the map, just fly to where we where told //--------------------------------------------------- // else { *new_position = targetPoint; } #if defined(MISSILE_BUG) SPEW((MISSILE_BUG, "EstimatedTargetPosition: %f %f %f", new_position->x, new_position->y, new_position->z)); #endif return *new_position; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Entity* Missile::TraceToParent(Entity *entity) { if(!entity) return NULL; if(entity->IsDerivedFrom(MWMover::DefaultData)) { MWMover *mover; mover = Cast_Object(MWMover *, entity); if(mover->myParentVehicle) return mover->myParentVehicle; } return entity; }