//===========================================================================// // File: MissileWeapon.cpp //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // 05/18/99 DPB Created File //---------------------------------------------------------------------------// // Copyright (C) 1998, Fasa Interactive // // All Rights reserved worldwide // // This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // //===========================================================================// #include "MW4Headers.hpp" #include "MissileWeapon.hpp" #include "Missile.hpp" #include "MWObject.hpp" #include "MWMission.hpp" #include "SubsystemClassData.hpp" #include #include #include #include // ngLog addition #include "mech.hpp" #if !defined(NO_LOG) #include "nglog_mw4.hpp" #endif // !defined(NO_LOG) #include "MWApplication.hpp" #include "sensor.hpp" //############################################################################# //############################### MissileWeapon ######################### //############################################################################# MissileWeapon::ClassData* MissileWeapon::DefaultData = NULL; void MechWarrior4::MissileSecurityCheckStart() { _asm { nop } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void MissileWeapon::InitializeClass() { Verify(!DefaultData); DefaultData = new ClassData( MissileWeaponClassID, "MechWarrior4::MissileWeapon", ProjectileWeapon::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, (Subsystem::StreamCreate) CreateStream ); Register_Object(DefaultData); DIRECT_GAME_MODEL_ATTRIBUTE( MissileWeapon__GameModel, BurnTime, burnTime, Scalar ); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void MissileWeapon::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // MissileWeapon* MissileWeapon::Make( CreateMessage *message, ReplicatorID *base_id ) { Check_Object(message); gos_PushCurrentHeap(Heap); MissileWeapon *new_entity = new MissileWeapon(DefaultData, message, base_id, NULL); gos_PopCurrentHeap(); Check_Object(new_entity); return new_entity; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // MissileWeapon::MissileWeapon( ClassData *class_data, CreateMessage *message, ReplicatorID *base_id, ElementRenderer::Element *element ): ProjectileWeapon(class_data, message, base_id, element) { Check_Pointer(this); Check_Object(message); CommonCreation(message); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void MissileWeapon::CommonCreation(CreateMessage *message) { Check_Object(this); Check_Object(message); numberEffectedByAMS = 0; rollAMSNumber = true; m_lockedTargetEntityPartOffset = Stuff::Point3D::Identity; // in Mercs, Arrow missiles can't be shot down by AMS unaffectedByAMS = false; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void MissileWeapon::Respawn(Entity::CreateMessage *message) { Check_Object(this); Check_Object(message); ProjectileWeapon::Respawn(message); CreateMessage *missile_message = Cast_Pointer(CreateMessage *, message); CommonCreation(missile_message); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // MissileWeapon::~MissileWeapon() { } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void MissileWeapon::CreateProjectile(Time till) { Check_Object(this); const GameModel *model = GetGameModel(); Check_Object(model); if(model->projectileModelResource != ResourceID::Null) { // //--------------------------------- //Get the ClassID from the Resource //--------------------------------- // ClassID class_ID; class_ID = GetClassIDFromDataListID(model->projectileModelResource); Verify(class_ID != NullClassID); ReplicatorID site_id = ReplicatorID::Null; Normal3D target_normal; Motion3D initial_velocity = Motion3D::Identity; Motion3D initial_acceleration = Motion3D::Identity; LinearMatrix4D site_local_to_world; // //------------------------- //Initialize the velocities //------------------------- // Check_Object(sitePointer); site_local_to_world = sitePointer->GetLocalToWorld(); initial_velocity.linearMotion.Multiply( model->initialLinearVelocity, site_local_to_world ); initial_acceleration.linearMotion = model->initialLinearAcceleration; ReplicatorID target_id; target_id = ReplicatorID::Null; Point3D target_point; Entity *ray_source; ray_source = targetQueryEntity.GetCurrent(); if(ray_source) { Check_Object(ray_source); target_id = ray_source->GetReplicatorID(); // Point3D trans; // targetQuery.m_line->FindEnd(&trans); // target_point.MultiplyByInverse( // trans, // ray_source->GetLocalToWorld() // ); target_point = m_lockedTargetEntityPartOffset; } else { targetQuery.m_line->FindEnd(&target_point); } // //-------------------------------- //Create the Missile CreateMessage //-------------------------------- // int flags = WeaponMover::DefaultFlags; if(numberEffectedByAMS > 0) { numberEffectedByAMS --; flags |= Missile::DoesAMSDestroyFlag; } Missile::CreateMessage projectile_create_message( sizeof(Missile::CreateMessage), DefaultEventPriority, CreateMessage::DefaultFlags, class_ID, flags, model->projectileModelResource, sitePointer->GetLocalToWorld(), 0.0f, Missile::ExecutionStateEngine::SRMMotionState, NameTable::NullObjectID, Entity::DefaultAlignment, initial_velocity, initial_acceleration, model->damageAmount, model->heatToDeal, model->maxDistance, // MSL 5.03 Min Distance // model->minDistance, GetParentVehicle()->GetReplicatorID(), model->splashRadius, model->percentageOfDamageToDirectHit, model->minPercentageOfDamageToSphereHit, model->maxPercentageOfDamageToSphereHit, model->itemID, target_id, target_point, model->burnTime ); MemoryStream stream(&projectile_create_message, projectile_create_message.messageLength); Entity *entity; ReplicatorID projectile_id = Connection::Hermit->GetNextReplicatorID(); entity = CreateEntity(&stream, &projectile_id, false); Check_Object(entity); Map::GetInstance()->AddChild(entity); if (vehicleInterface) { vehicleInterface->PlayForce (VehicleInterface::FireMissleForce); } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void MissileWeapon::PreCollisionExecute(Time till) { Check_Object(this); PRECOLLISION_LOGIC("Subsystem::MissileWeapon"); const GameModel* model = GetGameModel(); Check_Object(model); Check_Object(executionState); int pre_state = executionState->GetState(); switch (pre_state) { case ExecutionStateEngine::ReloadingState: { if (model->reloadTime > 0.49f) { if ((!m_PlayedRechargeSound) && (GetTimeParameter(till) >= model->reloadTime-0.5f)) { if (GetParentVehicle ()) if (GetParentVehicle()->vehicleInterface) GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::RECHARGE_MISSILE); m_PlayedRechargeSound = true; } } } } ProjectileWeapon::PreCollisionExecute(till); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void MissileWeapon::QuickFire (Adept::Entity *target) { const GameModel *model = GetGameModel(); Check_Object(model); Check_Object(this); executionState->RequestState(ExecutionStateEngine::ReloadingState); if (target == 0) { return; } Check_Object(target); ReplicatorID inflicting_id = ReplicatorID::Null; inflicting_id = GetParentVehicle()->GetReplicatorID(); // MSL 5.06 Advance Mode MWApplication *m_App; m_App = MWApplication::GetInstance (); m_ArmorMode = m_App->GetLocalNetParams()->m_armormodeOn; Entity::TakeDamageMessage damage_message( target->GetReplicatorID(), inflicting_id, model->damageAmount, m_ArmorMode, MissileDamageType, Stuff::UnitVector3D(1,0,0), Point3D (-1,-1,-1), model->heatToDeal, true, model->itemID ); target->Receive(&damage_message); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void MissileWeapon::Fire(CollisionQuery *query, Time time_locked, const Stuff::Point3D& lockedTargetEntityPartOffset) { Check_Object(this); Check_Object(executionState); const GameModel *model = GetGameModel(); Check_Object(model); targetQuery.m_collisionMask = query->m_collisionMask; targetQuery.m_material = query->m_material; targetQuery.m_raySource = query->m_raySource; m_lockedTargetEntityPartOffset = lockedTargetEntityPartOffset; collisionLine = *query->m_line; collisionNormal = *query->m_normal; // //-------------------------------------------------- //Check to see if we are locked on the entity or not //-------------------------------------------------- // // MSL 5.04 Corrects tracking of Missile from root of Building Object to target point if(targetQuery.m_raySource) { if (targetQuery.m_raySource->IsDerivedFrom(Building::DefaultData) == true) { targetQuery.m_raySource = NULL; } } // // If this model doesn't have a lock time, it won't even lock for Narc's // if(Small_Enough(model->targetLockTime)) { targetQuery.m_raySource = NULL; } else { // if we couldn't have locked legitimately yet, start with no lock // // .001 is a special value indicating immediate lock time. (SSRM's) // if (model->targetLockTime > time_locked && model->targetLockTime >= 0.0011f) targetQuery.m_raySource = NULL; // MSL 5.04 no tracking beyond maximum missile range if (targetQuery.m_line->m_length > model->maxDistance) targetQuery.m_raySource = NULL; // MSL 5.04 no tracking within minimum missile range // if (targetQuery.m_line->m_length < model->minDistance) // targetQuery.m_raySource = NULL; // MSL 5.04 no tracking if affected by ECM if(targetQuery.m_raySource) { // MSL 5.05 Advance Mode MWApplication *m_App; m_App = MWApplication::GetInstance (); m_AdvanceMode = m_App->GetLocalNetParams()->m_advancemodeOn; if (m_AdvanceMode) { if (targetQuery.m_raySource->IsDerivedFrom(MWMover::DefaultData) == true) { MWMover* mover = Cast_Object(MWMover*,targetQuery.m_raySource); MWObject *parent; parent = mover->GetParentVehicle (); if (parent) { MWObject *mobj = GetParentVehicle(); if(mobj && mobj->IsDerivedFrom(Mech::DefaultData)) { Mech *mech = Cast_Object(Mech *, mobj); if (mech->GetSensor()->GetSensorMode() == Sensor::PassiveMode) { targetQuery.m_raySource = NULL; } } if (parent->GetSensor()->GetSensorMode() == Sensor::PassiveMode) { MWObject *mobj = GetParentVehicle(); if(mobj && mobj->IsDerivedFrom(Mech::DefaultData)) { Mech *mech = Cast_Object(Mech *, mobj); if (mech->DoesHaveBeagle()) { if (targetQuery.m_line->m_length > 600.0f) targetQuery.m_raySource = NULL; } else { if (targetQuery.m_line->m_length > 500.0f) targetQuery.m_raySource = NULL; } } } if(parent->DoesHaveECM()) { MWObject *mobj = GetParentVehicle(); if(mobj && mobj->IsDerivedFrom(Mech::DefaultData)) { Mech *mech = Cast_Object(Mech *, mobj); if (mech->DoesHaveBeagle()) { if (targetQuery.m_line->m_length > 700.0f) targetQuery.m_raySource = NULL; } else { if (targetQuery.m_line->m_length > 500.0f) targetQuery.m_raySource = NULL; } } } } } } } bool does_have_lock = false; if(targetQuery.m_raySource) { if(targetQuery.m_raySource == Map::GetInstance()) { targetQuery.m_raySource = NULL; } else { does_have_lock = true; if (targetQuery.m_raySource->IsDerivedFrom(MWMover::DefaultData) == true) { MWMover* mover = Cast_Object(MWMover*,targetQuery.m_raySource); mover->NotifyProjectileApproaching(targetQuery.m_line->m_origin); MWObject *parent; parent = mover->GetParentVehicle (); if (parent) { if(parent->GetAMS() && !unaffectedByAMS) { // //------------------------------------------------- //pre-determine how many missiles AMS will take out //------------------------------------------------- // if ((rollAMSNumber) && (executionState->GetState() != ExecutionStateEngine::FiringState)) { // MSL 5.02 AMS // numberEffectedByAMS = 1; const GameModel *model = GetGameModel(); Check_Object(model); int upperLimit = model->numFire+1; if (model->numFire > 1) { Max_Clamp(upperLimit, model->numFire); // stop 0 to 8 missile groups } else { Max_Clamp(upperLimit, model->numFire+1); // stop 0 to 8 missile groups } // Max_Clamp(upperLimit, model->numFire+1); // stop 0 to 8 missile groups numberEffectedByAMS = Stuff::Random::GetLessThan(upperLimit); } } } } } } // //------------------------------------------------------------- //If we don't have a lock then we need to check the narc status //------------------------------------------------------------- // if(!does_have_lock) { if(Mission::GetInstance()->IsDerivedFrom(MWMission::DefaultData)) { MWMission *temp_mission = Cast_Object(MWMission *, Mission::GetInstance()); Entity *entity = (Entity *)temp_mission->GetNarc(GetParentVehicle()->GetAlignment()); if(entity) { targetQuery.m_raySource = entity; m_lockedTargetEntityPartOffset = Point3D::Identity; } } } } if(DoesHaveAmmo()) { if(ReadyToFire()) { // ngLog addition #if !defined(NO_LOG) MWObject *mobj = GetParentVehicle(); if(mobj && mobj->IsDerivedFrom(Mech::DefaultData)) { Mech *mech = Cast_Object(Mech *, mobj); if(mech && mech->IsLanceMate()) { int p_id = mech->lancemateIndex; MWApplication::GetInstance()->lancemateConnectionData[p_id].wep_atts[GetGameModel()->itemID] += GetGameModel()->ammoPerShot; } else { int p_id = GetParentVehicle()->GetReplicatorID().connectionID; MWApplication::GetInstance()->servedConnectionData[p_id].wep_atts[GetGameModel()->itemID] += GetGameModel()->ammoPerShot; } } #endif // !defined(NO_LOG) #if !defined(NO_MR) if(GetParentVehicle()->IsPlayerVehicle()) { MWApplication::GetInstance()->SetMissionReviewWeaponFired(GetParentVehicle()->GetReplicatorID().connectionID); } #endif // !defined(NO_MR) Mech_CheckDamage(targetQuery.m_raySource, GetParentVehicle()); executionState->RequestState(ExecutionStateEngine::FiringState, &targetQuery); } else { #if 0 if (GetParentVehicle ()->vehicleInterface) { GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::OUT_OF_AMMO); } #endif } } // MSL 5.03 Heat Jam if(model->m_heatToJam > 0) { if (executionState->GetState() == ExecutionStateEngine::FiringState) { MWObject *mobj = GetParentVehicle(); if(mobj && mobj->IsDerivedFrom(Mech::DefaultData)) { Mech *mech = Cast_Object(Mech *, mobj); Check_Object(mech); float heatlevel = mech->m_heatManager->GetHeatPercentage() * 100.0f; float heattojam = model->m_heatToJam; // float heatjam = Stuff::Random::GetFraction(); float heatjam = (Stuff::Random::GetLessThan(100) * 100); // float heatjamlevel = (heatlevel - heattojam) / (100 - heattojam); if (heatlevel > heattojam) { if (heatjam < 20) { m_heatJamThisFrame = true; } } } } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool MissileWeapon::IsAMSValid(Entity *target) { if (target == NULL) return false; if(target == Map::GetInstance()) return false; // MSL 5.02 bug fix // traverse parents looking for an MWObject // (bug fix # 4718) Entity *testEntity = target; while (testEntity!=NULL) { // stop at the MWObject if (testEntity->IsDerivedFrom(MWObject::DefaultData)) { break; } testEntity = testEntity->GetParentEntity(); } if (testEntity && testEntity->IsDerivedFrom(MWObject::DefaultData)) { MWObject* mw_object = Cast_Object(MWObject *, testEntity); if(mw_object->GetAMS()) { return true; } } return false; } void MechWarrior4::MissileSecurityCheckStop() { _asm { nop } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void MissileWeapon::TestInstance() const { Verify(IsDerivedFrom(DefaultData)); }