//===========================================================================// // File: BeamWeapon.cpp //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // 09/09/98 JSE Inital base class based off of Shadowrun/Adept // // 09/24/98 BDB Inital Weapon subsytem class based off of Subsystem // // 10/19/98 BDB Created LauchedWeapon subsytem based off of Weapon // //---------------------------------------------------------------------------// // Copyright (C) 1998, Fasa Interactive // // All Rights reserved worldwide // // This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // //===========================================================================// #include "MW4Headers.hpp" #include "BeamWeapon.hpp" #include "MWMover.hpp" #include "BeamEntity.hpp" #include "GUIWeaponManager.hpp" #include "SubsystemClassData.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 ####################### //############################################################################# BeamWeapon::ExecutionStateEngine::ClassData* BeamWeapon::ExecutionStateEngine::DefaultData = NULL; const StateEngine::StateEntry BeamWeapon::ExecutionStateEngine::StateEntries[]= { STATE_ENTRY(BeamWeapon__ExecutionStateEngine, Firing), STATE_ENTRY(BeamWeapon__ExecutionStateEngine, Recharging) }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BeamWeapon::ExecutionStateEngine::InitializeClass() { Check_Object(StateEngine::DefaultData); Verify(!DefaultData); DefaultData = new ClassData( BeamWeapon__ExecutionStateEngineClassID, "BeamWeapon::ExecutionStateEngine", BaseClass::DefaultData, ELEMENTS(StateEntries), StateEntries, (Entity::ExecutionStateEngine::Factory)Make, (Entity::ExecutionStateEngine::FactoryRequest::Factory) &FactoryRequest::ConstructFactoryRequest ); Register_Object(DefaultData); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BeamWeapon::ExecutionStateEngine::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BeamWeapon::ExecutionStateEngine* BeamWeapon::ExecutionStateEngine::Make( BeamWeapon *weapon, FactoryRequest *request ) { Check_Object(weapon); Check_Object(request); gos_PushCurrentHeap(Heap); BeamWeapon::ExecutionStateEngine *engine = new BeamWeapon::ExecutionStateEngine(DefaultData, weapon, request); gos_PopCurrentHeap(); return engine; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int BeamWeapon::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(new_state == FiringState && currentState == RechargingState) return currentState; // //--------------------- // Deal with our states //--------------------- // BeamWeapon *beam; beam = Cast_Object(BeamWeapon *, owningEntity); switch (BaseClass::RequestState(new_state, data)) { case FiringState: { // SPEW(("jackyc", "FIRING WEAPON - Weapon %d", beam->weaponID)); //We need to set up new target info here! beam->UsePostCollision(); Check_Object(EntityManager::GetInstance()); EntityManager::GetInstance()->RequestPostCollisionExecution(beam); // Start the sound beam->beamStartSFX = 1; beam->lastParameterization = gos_GetElapsedTime(); if(beam->guiWeapon.GetCurrent()) beam->guiWeapon.GetCurrent()->Reload(); beam->ApplyHeat(); } break; case NeverExecuteState: case DestroyedState: break; default: { owningEntity->IgnorePostCollision(); break; } } return currentState; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BeamWeapon::ExecutionStateEngine::TestInstance() const { Verify(IsDerivedFrom(DefaultData)); } //############################################################################# //############################### Weapon ################################ //############################################################################# BeamWeapon::ClassData* BeamWeapon::DefaultData = NULL; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BeamWeapon::InitializeClass() { Verify(!DefaultData); DefaultData = new ClassData( BeamWeaponClassID, "MechWarrior4::BeamWeapon", 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, (Subsystem::StreamCreate) CreateStream ); Register_Object(DefaultData); INDIRECT_STATE_ATTRIBUTE( BeamWeapon, ExecutionState, executionState, BeamWeapon__ExecutionStateEngine ); CUSTOM_DIRECT_ATTRIBUTE( BeamWeapon, BeamDistance, beamDistance, Scalar, ScalarClassID ); CUSTOM_DIRECT_ATTRIBUTE( BeamWeapon, BeamStartSFX, beamStartSFX, int, IntClassID ); DIRECT_GAME_MODEL_ATTRIBUTE( BeamWeapon__GameModel, FireDuration, fireDuration, Scalar ); DIRECT_GAME_MODEL_ATTRIBUTE( BeamWeapon__GameModel, BeamModelResource, beamModelResource, ResourceID ); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BeamWeapon::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BeamWeapon* BeamWeapon::Make( CreateMessage *message, ReplicatorID *base_id ) { gos_PushCurrentHeap(Heap); BeamWeapon *new_entity = new BeamWeapon(DefaultData, message, base_id, NULL); gos_PopCurrentHeap(); Check_Object(new_entity); return new_entity; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BeamWeapon::BeamWeapon( ClassData *class_data, CreateMessage *message, ReplicatorID *base_id, ElementRenderer::Element *element ): Weapon(class_data, message, base_id, element), targetQuery(NULL, NULL, CanBeShotFlag, NULL), beamEntity(NULL) { Check_Pointer(this); Check_Object(message); targetQuery.m_line = &collisionLine; targetQuery.m_normal = &collisionNormal; CommonCreation(message); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BeamWeapon::CommonCreation(CreateMessage *message) { Check_Object(this); Check_Object(message); fireRequest = false; accumlatedRechargeTime = 0.0f; accumlatedDuration = 0.0f; beamDistance = 0.01f; beamStartSFX = 0; beamEntity.Remove(); const GameModel *model = GetGameModel(); beamRechargeTime = model->reloadTime; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BeamWeapon::Respawn(Entity::CreateMessage *message) { Check_Object(this); Check_Object(message); BaseClass::Respawn(message); CreateMessage *beam_message = Cast_Pointer(CreateMessage *, message); CommonCreation(beam_message); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BeamWeapon::~BeamWeapon() { } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BeamWeapon::PreCollisionExecute(Time till) { const GameModel* model = GetGameModel(); PRECOLLISION_LOGIC("Subsystem::Beam"); Check_Object(executionState); int pre_state = executionState->GetState(); switch (pre_state) { case ExecutionStateEngine::FiringState: { UsePostCollision(); if ((GetTimeParameter(till) >= model->fireDuration) && (beamEntity.GetCurrent())) { // if(beamEntity.GetCurrent()) // { Check_Object(beamEntity.GetCurrent()); // if(!beamEntity.GetCurrent()->beamStatus) // { executionState->RequestState(ExecutionStateEngine::RechargingState); m_PlayedRechargeSound = false; lastParameterization = gos_GetElapsedTime(); Check_Object(beamEntity.GetCurrent()); beamEntity.GetCurrent()->SentenceToDeathRow(); // } } break; } case ExecutionStateEngine::RechargingState: { IgnorePostCollision(); beamEntity.Remove(); Scalar count = beamRechargeTime - GetTimeParameter(till); Min_Clamp(count, 0.0f); SetCount(count); if (beamRechargeTime > 0.49f) { if ((!m_PlayedRechargeSound) && (GetTimeParameter(till) >= beamRechargeTime-0.5f)) { if (GetParentVehicle ()) if (GetParentVehicle()->vehicleInterface) GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::RECHARGE_BEAM); m_PlayedRechargeSound = true; } } if (GetTimeParameter(till) >= beamRechargeTime) { executionState->RequestState(ExecutionStateEngine::NeverExecuteState); if(guiWeapon.GetCurrent()) guiWeapon.GetCurrent()->Ready(); } break; } } BaseClass::PreCollisionExecute(till); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BeamWeapon::PostCollisionExecute(Time till) { Check_Object(this); POSTCOLLISION_LOGIC("Subsystem::Beam"); if(!beamEntity.GetCurrent()) { CreateBeam(); CreateLightAmpFlareOut(); CreateMuzzleFlash(); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BeamWeapon::CreateBeam() { const GameModel *model = GetGameModel(); Check_Object(model); ClassID class_ID; class_ID = GetClassIDFromDataListID(model->beamModelResource); Verify(class_ID != NullClassID); Point3D target_point = Point3D::Identity; ReplicatorID target_id = ReplicatorID::Null; Normal3D target_normal(1.0f, 0.0f, 0.0f); Check_Object(sitePointer); const char *site_name = sitePointer->GetName(); ReplicatorID inflicting_id = ReplicatorID::Null; inflicting_id = GetParentVehicle()->GetReplicatorID(); if(targetQuery.m_line->m_length > model->maxDistance) { // SPEWALWAYS(("jackyc", "Create Beam - Length too long. NULL target")); targetQuery.m_line->m_length = model->maxDistance; targetQuery.m_line->FindEnd(&target_point); targetQuery.m_raySource = NULL; } else { if(targetQuery.m_raySource) { // SPEWALWAYS(("jackyc", "Create Beam - Using normal m_raySource target")); Check_Object(targetQuery.m_raySource); target_id = targetQuery.m_raySource->GetReplicatorID(); targetQuery.m_line->FindEnd(&target_point); target_normal = *targetQuery.m_normal; } else { // SPEWALWAYS(("jackyc", "Create Beam - NULL m_raySource target")); targetQuery.m_line->FindEnd(&target_point); } } BeamEntity::CreateMessage beam_create_message( sizeof(BeamEntity::CreateMessage), DefaultEventPriority, CreateMessage::DefaultFlags, class_ID, BeamEntity::DefaultFlags, model->beamModelResource, sitePointer->GetLocalToWorld(), 0.0f, ExecutionStateEngine::AlwaysExecuteState, NameTable::NullObjectID, Entity::DefaultAlignment, inflicting_id, site_name, target_point, target_id, target_normal, model->damageAmount, model->heatToDeal, targetQuery.m_material, model->maxDistance, // MSL 5.03 Min Distance // model->minDistance, model->itemID ); MemoryStream stream(&beam_create_message, beam_create_message.messageLength); Entity *entity; ReplicatorID beam_id = Connection::Hermit->GetNextReplicatorID(); entity = CreateEntity(&stream, &beam_id, false); Check_Object(entity); Verify(entity->IsDerivedFrom(BeamEntity::DefaultData)); beamEntity.Add(Cast_Object(BeamEntity *, entity)); Check_Object(Map::GetInstance()); Map::GetInstance()->AddChild(beamEntity.GetCurrent()); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BeamWeapon::QuickFire (Adept::Entity *target) { const GameModel *model = GetGameModel(); Check_Object(model); Check_Object(this); executionState->RequestState(ExecutionStateEngine::RechargingState); lastParameterization = gos_GetElapsedTime(); 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, BeamDamageType, Stuff::UnitVector3D(1,0,0), Point3D (-1,-1,-1), model->heatToDeal, true, model->itemID ); target->Receive(&damage_message); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BeamWeapon::Fire(CollisionQuery *query, Time time_locked, const Stuff::Point3D& lockedTargetEntityPartOffset) { //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; // ngLog addition if(ReadyToFire()) { #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, &targetQuery); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool BeamWeapon::ReadyToFire() { // if this is the local mech, always true in single player. if (GetReplicatorID().connectionID == Connection::Local->GetID()) { if (CanFireFromCurrentArm() == false) { return (false); } return (executionState->GetState()==ExecutionStateEngine::NeverExecuteState); } // SPEWALWAYS(("jackyc", "SERVER LAST RECHARGE START TIME - %f", lastParameterization)); // SPEWALWAYS(("jackyc", "SERVER CURRENT TIME - %f", gos_GetElapsedTime())); // SPEWALWAYS(("jackyc", "SERVER RECHARGE TIME - %f", (gos_GetElapsedTime() - lastParameterization))); // SPEWALWAYS(("jackyc", "FULL RECHARGE TIME - %f", beamRechargeTime)); // SPEWALWAYS(("jackyc", "RECHARGE EPSILON - %f", ((0.1f * beamRechargeTime) + (packet_time_diff * 0.001f)))); if (executionState->GetState()==ExecutionStateEngine::RechargingState) { if (gos_GetElapsedTime() - lastParameterization <= 0.1f) { // SPEWALWAYS(("jackyc", "RESETTING Recharge")); executionState->RequestState(ExecutionStateEngine::NeverExecuteState); } return true; } return (executionState->GetState()==ExecutionStateEngine::NeverExecuteState); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BeamWeapon::CleanUpCreatedBeam() { Check_Object(this); if(beamEntity.GetCurrent()) beamEntity.GetCurrent()->SentenceToDeathRow(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BeamWeapon::DestroySubsystem() { Check_Object(this); EnterNeverExecuteState(); IgnorePostCollision(); CleanUpCreatedBeam(); BaseClass::DestroySubsystem(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BeamWeapon::TestInstance() const { Verify(IsDerivedFrom(DefaultData)); }