//===========================================================================// // File: ProjectileWeapon.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 "ProjectileWeapon.hpp" #include "GUIWeaponManager.hpp" #include "Vehicle.hpp" #include "WeaponMover.hpp" #include "SubsystemClassData.hpp" #include "HeatManager.hpp" #include #include #include #include #include // ngLog addition #include "mech.hpp" #if !defined(NO_LOG) #include "nglog_mw4.hpp" #endif // !defined(NO_LOG) #include "MWApplication.hpp" //############################################################################# //########################## ExecutionStateEngine ####################### //############################################################################# ProjectileWeapon::ExecutionStateEngine::ClassData* ProjectileWeapon::ExecutionStateEngine::DefaultData = NULL; const StateEngine::StateEntry ProjectileWeapon::ExecutionStateEngine::StateEntries[]= { STATE_ENTRY(ProjectileWeapon__ExecutionStateEngine, Firing), STATE_ENTRY(ProjectileWeapon__ExecutionStateEngine, Reloading), STATE_ENTRY(ProjectileWeapon__ExecutionStateEngine, Jammed), STATE_ENTRY(ProjectileWeapon__ExecutionStateEngine, HeatJammed), STATE_ENTRY(ProjectileWeapon__ExecutionStateEngine, AmmoFire), STATE_ENTRY(ProjectileWeapon__ExecutionStateEngine, Recharging) }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::ExecutionStateEngine::InitializeClass() { Check_Object(StateEngine::DefaultData); Verify(!DefaultData); DefaultData = new ClassData( ProjectileWeapon__ExecutionStateEngineClassID, "ProjectileWeapon::ExecutionStateEngine", Weapon::ExecutionStateEngine::DefaultData, ELEMENTS(StateEntries), StateEntries, (Entity::ExecutionStateEngine::Factory)Make, (Entity::ExecutionStateEngine::FactoryRequest::Factory) &FactoryRequest::ConstructFactoryRequest ); Register_Object(DefaultData); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::ExecutionStateEngine::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ProjectileWeapon::ExecutionStateEngine* ProjectileWeapon::ExecutionStateEngine::Make( ProjectileWeapon *weapon, FactoryRequest *request ) { Check_Object(weapon); Check_Object(request); gos_PushCurrentHeap(Heap); ProjectileWeapon::ExecutionStateEngine *engine = new ProjectileWeapon::ExecutionStateEngine(DefaultData, weapon, request); gos_PopCurrentHeap(); return engine; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int ProjectileWeapon::ExecutionStateEngine::RequestState( int new_state, void* data ) { Check_Object(this); Check_Object(owningEntity); // //------------------------------ // Quick exit if no state change //------------------------------ // if (new_state == currentState) { return currentState; } // //------------------------------------------------------- // If we are currently reloading, ignore a firing request //------------------------------------------------------- // if((currentState == AmmoFireState) && (new_state == FiringState || new_state == RechargingState || new_state == ReloadingState || new_state == JammedState || new_state == HeatJammedState)) { return currentState; } if((currentState == ReloadingState) && new_state == FiringState) { return currentState; } // MSL 5.03 Weapon Jammed if((currentState == JammedState || currentState == HeatJammedState) && (new_state == FiringState || new_state == RechargingState || new_state == ReloadingState)) { return currentState; } if(currentState == NeverExecuteState && new_state == FiringState) { // Set the initial time of fire ProjectileWeapon* owner = Cast_Object( ProjectileWeapon*, owningEntity ); owner->m_fireStartTime = owner->m_fireEndTime = gos_GetElapsedTime(); } // MSL 5.03 Ammo Bay Fire if(new_state == AmmoFireState) { ProjectileWeapon* owner = Cast_Object( ProjectileWeapon*, owningEntity ); Check_Object(owner); owner->StartFireState(); } // MSL 5.03 Weapon Jammed if(new_state == JammedState) { ProjectileWeapon* owner = Cast_Object( ProjectileWeapon*, owningEntity ); Check_Object(owner); owner->StartJamState(); } // MSL 5.03 Weapon Heat Jammed if(new_state == HeatJammedState) { ProjectileWeapon* owner = Cast_Object( ProjectileWeapon*, owningEntity ); Check_Object(owner); owner->StartHeatJamState(); } // MSL 5.03 Weapon Jammed if(new_state == RechargingState) { ProjectileWeapon* owner = Cast_Object( ProjectileWeapon*, owningEntity ); Check_Object(owner); owner->m_enterRecharge = gos_GetElapsedTime(); } // // //--------------------- // Deal with our states //--------------------- // ProjectileWeapon *weapon; weapon = Cast_Object(ProjectileWeapon *, owningEntity); switch (BaseClass::RequestState(new_state, data)) { case FiringState: { weapon->targetQueryEntity.Remove(); if(weapon->targetQuery.m_raySource) weapon->targetQueryEntity.Add(weapon->targetQuery.m_raySource); weapon->UsePostCollision(); Check_Object(EntityManager::GetInstance()); EntityManager::GetInstance()->RequestPostCollisionExecution(weapon); weapon->lastExecuted = gos_GetElapsedTime(); weapon->fireRequest = true; if(weapon->guiWeapon.GetCurrent()) weapon->guiWeapon.GetCurrent()->Reload(); weapon->ApplyHeat(); break; } case ReloadingState: { weapon->IgnorePostCollision(); weapon->lastParameterization = gos_GetElapsedTime(); break; } // case AmmoFireState: // { // weapon->UsePostCollision(); // Check_Object(EntityManager::GetInstance()); // EntityManager::GetInstance()->RequestPostCollisionExecution(weapon); // break; // } }; return currentState; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::ExecutionStateEngine::TestInstance() const { Verify(IsDerivedFrom(DefaultData)); } //############################################################################# //############################### Weapon ############################## //############################################################################# ProjectileWeapon::ClassData* ProjectileWeapon::DefaultData = NULL; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::InitializeClass() { Verify(!DefaultData); DefaultData = new ClassData( ProjectileWeaponClassID, "MechWarrior4::ProjectileWeapon", Weapon::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); INDIRECT_STATE_ATTRIBUTE( ProjectileWeapon, ExecutionState, executionState, ProjectileWeapon__ExecutionStateEngine ); CUSTOM_DIRECT_ATTRIBUTE( ProjectileWeapon, ProjectileStartSFX, projectileStartSFX, int, IntClassID ); DIRECT_GAME_MODEL_ATTRIBUTE( ProjectileWeapon__GameModel, FireRate, fireRate, Scalar ); DIRECT_GAME_MODEL_ATTRIBUTE( ProjectileWeapon__GameModel, InitialLinearVelocity, initialLinearVelocity, Vector3D ); DIRECT_GAME_MODEL_ATTRIBUTE( ProjectileWeapon__GameModel, InitialLinearAcceleration, initialLinearAcceleration, Vector3D ); DIRECT_GAME_MODEL_ATTRIBUTE( ProjectileWeapon__GameModel, ProjectileModelResource, projectileModelResource, ResourceID ); DIRECT_GAME_MODEL_ATTRIBUTE( ProjectileWeapon__GameModel, TargetLockTime, targetLockTime, Scalar ); DIRECT_GAME_MODEL_ATTRIBUTE( ProjectileWeapon__GameModel, TimeToPotentialJam, m_timeToPotentialJam, Scalar ); DIRECT_GAME_MODEL_ATTRIBUTE( ProjectileWeapon__GameModel, TimeToJam, m_timeToJam, Scalar ); DIRECT_GAME_MODEL_ATTRIBUTE( ProjectileWeapon__GameModel, TimeToUnjam, m_timeToUnjam, Scalar ); // MSL 5.03 Weapon Jammed DIRECT_GAME_MODEL_ATTRIBUTE( ProjectileWeapon__GameModel, HeatToJam, m_heatToJam, Scalar ); DIRECT_GAME_MODEL_ATTRIBUTE( ProjectileWeapon__GameModel, HeatToUnjam, m_heatToUnjam, Scalar ); // MSL 5.03 Ammo Bay Fire DIRECT_GAME_MODEL_ATTRIBUTE( ProjectileWeapon__GameModel, TimeToAmmoBay, m_timeToAmmoBay, Scalar ); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ProjectileWeapon* ProjectileWeapon::Make( CreateMessage *message, ReplicatorID *base_id ) { Check_Object(message); gos_PushCurrentHeap(Heap); ProjectileWeapon *new_entity = new ProjectileWeapon(DefaultData, message, base_id, NULL); gos_PopCurrentHeap(); Check_Object(new_entity); return new_entity; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ProjectileWeapon::ProjectileWeapon( ClassData *class_data, CreateMessage *message, ReplicatorID *base_id, ElementRenderer::Element *element ): Weapon(class_data, message, base_id, element), targetQuery(NULL, NULL, CanBeShotFlag, NULL), targetQueryEntity(NULL), m_jamStartTime( 0 ), m_fireStartTime( 0 ), m_fireEndTime( 0 ), m_ammofireStartTime( 0 ) { Check_Pointer(this); Check_Object(message); m_jamThisFrame = false; m_heatJamThisFrame = false; targetQuery.m_line = &collisionLine; targetQuery.m_normal = &collisionNormal; CommonCreation(message); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::Respawn(Entity::CreateMessage *message) { Check_Object(this); Check_Object(message); Weapon::Respawn(message); CreateMessage *projectile_message = Cast_Pointer(CreateMessage *, message); CommonCreation(projectile_message); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::CommonCreation(CreateMessage *message) { Check_Object(this); Check_Object(message); projectileStartSFX = 0; fireRequest = false; projectilesFired = 0; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ProjectileWeapon::~ProjectileWeapon() { } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::PreCollisionExecute(Time till) { Check_Object(this); PRECOLLISION_LOGIC("Subsystem::Projectile"); const GameModel* model = GetGameModel(); Check_Object(model); Check_Object(executionState); Stuff::Scalar timeSlice = GetTimeSlice( till ); int pre_state = executionState->GetState(); switch (pre_state) { case ExecutionStateEngine::FiringState: { UsePostCollision(); m_PlayedRechargeSound = false; break; } case ExecutionStateEngine::ReloadingState: { Scalar count = model->reloadTime - GetTimeParameter(till); Min_Clamp(count, 0.0f); SetCount(count); if (model->reloadTime > 0.49f) { if ((!m_PlayedRechargeSound) && (GetTimeParameter(till) >= model->reloadTime-0.5f)) { if (GetParentVehicle ()) if (GetParentVehicle()->vehicleInterface) { if ((model->itemID == W_ClanERPPC) || (model->itemID == W_PPC) || (model->itemID == W_ERPPC)) GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::RECHARGE_PPC); else if (!((model->itemID == W_MachineGun) || (model->itemID == W_ClanMachineGun))) GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::RECHARGE_PROJECTILE); } m_PlayedRechargeSound = true; } } // MSL 5.03 Weapon Jammed if(GetTimeParameter(till) > model->reloadTime) { // if(model->m_timeToJam > 0) // { // executionState->RequestState(ExecutionStateEngine::RechargingState); // if(guiWeapon.GetCurrent()) // guiWeapon.GetCurrent()->Ready(); // m_PlayedRechargeSound = false; // } // else // { executionState->RequestState(ExecutionStateEngine::NeverExecuteState); if(guiWeapon.GetCurrent()) guiWeapon.GetCurrent()->Ready(); m_PlayedRechargeSound = false; // } } IgnorePostCollision(); break; } // MSL 5.03 Weapon Jammed case ExecutionStateEngine::RechargingState: { // In order to make the server and client agree on cumulative time more, I'll put a buffer in. // but if we don't get a new fire request with .25 seconds of entering this state we'll assume recharge. const GameModel* model = GetGameModel(); Check_Object(model); Check_Object(executionState); if(model->m_timeToJam > 0) { if ( (gos_GetElapsedTime() - m_enterRecharge ) >= 0.25f) { m_fireStartTime += timeSlice*2; m_fireEndTime += timeSlice; double cumulativeFiringTime = m_fireEndTime - m_fireStartTime; if ( cumulativeFiringTime <= 0 ) { executionState->RequestState(ExecutionStateEngine::NeverExecuteState); // if(guiWeapon.GetCurrent()) // guiWeapon.GetCurrent()->Ready(); } } } break; } // MSL 5.03 Weapon Jammed case ExecutionStateEngine::JammedState: { const GameModel* model = GetGameModel(); Check_Object(model); Check_Object(executionState); if(model->m_timeToJam > 0) { if( gos_GetElapsedTime() - m_jamStartTime > model->m_timeToUnjam ) { executionState->RequestState(ExecutionStateEngine::NeverExecuteState); if(guiWeapon.GetCurrent()) guiWeapon.GetCurrent()->Ready(); m_jamThisFrame = false; } else { // Update the gui time if(guiWeapon.GetCurrent()) { guiWeapon.GetCurrent()->SetJamCount( (Stuff::Scalar)(gos_GetElapsedTime() - m_jamStartTime) ); } } IgnorePostCollision(); } break; } // MSL 5.03 Weapon Jammed case ExecutionStateEngine::HeatJammedState: { const GameModel* model = GetGameModel(); Check_Object(model); Check_Object(executionState); if(model->m_heatToUnjam > 0) { MWObject *mobj = GetParentVehicle(); if(mobj && mobj->IsDerivedFrom(Mech::DefaultData)) { Mech *mech = Cast_Object(Mech *, mobj); Check_Object(mech); if ((mech->m_heatManager->GetHeatPercentage() * 100.0f) <= model->m_heatToUnjam) { executionState->RequestState(ExecutionStateEngine::NeverExecuteState); if(guiWeapon.GetCurrent()) guiWeapon.GetCurrent()->Ready(); m_heatJamThisFrame = false; } IgnorePostCollision(); } } break; } // MSL 5.04 Ammo Bay Fire case ExecutionStateEngine::AmmoFireState: { const GameModel* model = GetGameModel(); Check_Object(model); Check_Object(executionState); if(model->m_timeToAmmoBay > 0) { MWObject *mobj = GetParentVehicle(); if(mobj && mobj->IsDerivedFrom(Mech::DefaultData)) { if (DoesHaveAmmo()) { if( gos_GetElapsedTime() - m_ammofireStartTime > model->m_timeToAmmoBay ) { Check_Object(this); int ammo_count = GetAmmoCount(); float damage_count = ammo_count * model->damageAmount * model->numFire; // MSL 5.04 Ammo Bay Fire SetAmmoCount(0); executionState->RequestState(ExecutionStateEngine::DestroyedState); } else { // Update the gui time if(guiWeapon.GetCurrent()) { guiWeapon.GetCurrent()->SetFireCount( (Stuff::Scalar)(gos_GetElapsedTime() - m_ammofireStartTime) ); } } } } else { executionState->RequestState(ExecutionStateEngine::NeverExecuteState); // if(guiWeapon.GetCurrent()) // guiWeapon.GetCurrent()->Ready(); } UsePostCollision(); } break; } } Weapon::PreCollisionExecute(till); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::PostCollisionExecute(Time till) { Check_Object(this); POSTCOLLISION_LOGIC("Subsystem::Projectile"); const GameModel *model = GetGameModel(); Check_Object(model); Check_Object(executionState); // MSL 5.03 Weapon Jammed bool jammed = false; MWApplication *m_App; m_App = MWApplication::GetInstance (); if (m_App->GetLocalNetParams()->m_weaponjamOn) { if ( executionState->GetState() == ExecutionStateEngine::FiringState ) { if (m_jamThisFrame) { executionState->RequestState(ExecutionStateEngine::JammedState); jammed = true; } if (m_heatJamThisFrame) { executionState->RequestState(ExecutionStateEngine::HeatJammedState); jammed = true; } } } m_heatJamThisFrame = false; m_jamThisFrame = false; if (jammed) { if (GetParentVehicle ()) { if (GetParentVehicle()->vehicleInterface) { GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::ZOOM_FAIL); } } return; } if(fireRequest) { if(!DoesHaveAmmo()) { executionState->RequestState(ExecutionStateEngine::NeverExecuteState); if(guiWeapon.GetCurrent()) guiWeapon.GetCurrent()->Ready(); IgnorePostCollision(); } else { if((GetTimeParameter(till) > model->fireRate) || (projectilesFired == 0)) { lastParameterization = gos_GetElapsedTime(); CreateProjectile(till); CreateMuzzleFlash(); CreateEjectEffect(); CreateLightAmpFlareOut(); projectilesFired ++; projectileStartSFX = 1; if(projectilesFired >= model->numFire) { fireRequest = false; projectilesFired = 0; Check_Object(executionState); executionState->RequestState(ExecutionStateEngine::ReloadingState); UseAmmo(); } } } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::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; ReplicatorID target_id = ReplicatorID::Null; Normal3D target_normal; Motion3D initial_velocity = Motion3D::Identity; Motion3D initial_acceleration = Motion3D::Identity; Point3D site_local_to_world; // //------------------------- //Initialize the velocities //------------------------- // Check_Object(sitePointer); site_local_to_world = sitePointer->GetLocalToWorld(); Vector3D projectile_direction; Point3D target_point; targetQuery.m_line->FindEnd(&target_point); projectile_direction.Subtract( target_point, site_local_to_world ); Check_Object(sitePointer); site_local_to_world = sitePointer->GetLocalToWorld(); LinearMatrix4D direction_matrix = LinearMatrix4D::Identity; direction_matrix.BuildTranslation((Stuff::Point3D)site_local_to_world); direction_matrix.AlignLocalAxisToWorldVector(projectile_direction, Z_Axis, Y_Axis, X_Axis); Vector3D initial_scale = model->initialLinearVelocity; if(GetParentAsVehicle()) { initial_scale.z += GetParentAsVehicle()->currentSpeedMPS; } initial_velocity.linearMotion.Multiply( initial_scale, direction_matrix ); initial_acceleration.linearMotion = model->initialLinearAcceleration; // //------------------------------------ //Create the WeaponMover CreateMessage //------------------------------------ // WeaponMover::CreateMessage projectile_create_message( sizeof(WeaponMover::CreateMessage), DefaultEventPriority, CreateMessage::DefaultFlags, class_ID, WeaponMover::DefaultFlags, model->projectileModelResource, direction_matrix, 0.0f, WeaponMover::ExecutionStateEngine::StraightLineMotionState, 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 ); 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) { if (!((model->itemID == W_MachineGun) || (model->itemID == W_ClanMachineGun))) vehicleInterface->PlayForce (VehicleInterface::FireProjectileForce); } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::Fire(Adept::Entity::CollisionQuery *query, Time time_locked, const Stuff::Point3D& lockedTargetEntityPartOffset) { Check_Object(this); Check_Object(executionState); //copy the query targetQuery.m_collisionMask = query->m_collisionMask; targetQuery.m_material = query->m_material; targetQuery.m_raySource = query->m_raySource; collisionLine = *query->m_line; collisionNormal = *query->m_normal; 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) } executionState->RequestState(ExecutionStateEngine::FiringState); } else { #if 0 if (GetParentVehicle ()->vehicleInterface) { GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::OUT_OF_AMMO); } #endif } // MSL 5.03 Weapon Jammed const GameModel* model = GetGameModel(); Check_Object(model); Check_Object(executionState); 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; } } } } } // MSL 5.03 Weapon Jammed if(model->m_timeToJam > 0) { if (executionState->GetState() == ExecutionStateEngine::FiringState) { Check_Object(GetParentVehicle()); bool fServer = Network::GetInstance()->AmIServer(); bool fLocalPlayer = (GetReplicatorID().connectionID == Connection::Local->GetID()); if (fServer || fLocalPlayer) { // if we fired, check for jam m_fireEndTime = gos_GetElapsedTime(); double cumulativeFiringTime = m_fireEndTime - m_fireStartTime; // // For servers, the rules are going to be different. If the fire time is at the absolute max, plus a buffer for lag // then we will gaurantee a jam on the server, and have it refresh. // // This allows for some tolerance of various net traffic since we'd still prefer that the // client handle this, but puts a cap on what we tolerate from a potential cheater. if ( fLocalPlayer) { if ( cumulativeFiringTime >= model->m_timeToPotentialJam) { Stuff::Scalar idPermutation = (replicatorID.localID % 3)/3.0f; // have weapon's ID permutate it's jam time Stuff::Scalar r = (Stuff::Scalar)fmod( gos_GetElapsedTime()+idPermutation, 1.0 ); Stuff::Scalar e = (Stuff::Scalar)( ( cumulativeFiringTime - model->m_timeToPotentialJam ) / model->m_timeToJam ); // Potential to jam: if ( r < e ) { m_jamThisFrame = true; } } } else if (fServer) { if (cumulativeFiringTime >= (model->m_timeToJam + model->m_timeToPotentialJam + 2.0f)) { m_jamThisFrame = true; } } } } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::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 Armor 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, ProjectileDamageType, Stuff::UnitVector3D(1,0,0), Point3D (-1,-1,-1), model->heatToDeal, true, model->itemID ); target->Receive(&damage_message); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool ProjectileWeapon::ReadyToFire() { Check_Object(this); Check_Object(executionState); // if this is the local mech, always true in single player. if (GetReplicatorID().connectionID == Connection::Local->GetID()) { if (CanFireFromCurrentArm() == false) { return (false); } bool result; result = executionState->GetState()==ExecutionStateEngine::NeverExecuteState && DoesHaveAmmo(); return result; } if (executionState->GetState()==ExecutionStateEngine::ReloadingState) { if (gos_GetElapsedTime() - lastParameterization <= 0.1f) { executionState->RequestState(ExecutionStateEngine::NeverExecuteState); } return DoesHaveAmmo(); } bool result; result = executionState->GetState()==ExecutionStateEngine::NeverExecuteState && DoesHaveAmmo(); return result; } // MSL 5.03 Ammo Bay Fire //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::StartFireState() { const GameModel *model = GetGameModel(); Check_Object(model); m_ammofireStartTime = gos_GetElapsedTime(); if ( guiWeapon.GetCurrent() ) { guiWeapon.GetCurrent()->RecoverFromFire(); guiWeapon.GetCurrent()->SetFireCount( model->m_timeToAmmoBay ); } } // MSL 5.03 Weapon Jammed //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::StartJamState() { const GameModel *model = GetGameModel(); Check_Object(model); m_jamStartTime = gos_GetElapsedTime(); if ( guiWeapon.GetCurrent() ) { guiWeapon.GetCurrent()->RecoverFromJam(); guiWeapon.GetCurrent()->SetJamCount( model->m_timeToUnjam ); } } // MSL 5.03 Weapon Jammed //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::StartHeatJamState() { // const GameModel *model = GetGameModel(); // Check_Object(model); // m_jamStartTime = gos_GetElapsedTime(); if ( guiWeapon.GetCurrent() ) { guiWeapon.GetCurrent()->RecoverFromJam(); // guiWeapon.GetCurrent()->SetJamCount( model->m_timeToUnjam ); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::DestroySubsystem() { Check_Object(this); Weapon::DestroySubsystem(); EnterNeverExecuteState(); IgnorePostCollision(); CleanUpProjectiles(); ClearAmmo(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::CleanUpProjectiles() { Check_Object(this); fireRequest = false; projectilesFired = 0; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void ProjectileWeapon::TestInstance() const { Verify(IsDerivedFrom(DefaultData)); }