#include "MW4Headers.hpp" #include "BombastWeapon.hpp" #include "Vehicle.hpp" #include "BeamEntity.hpp" #include "GUIWeaponManager.hpp" #include "VehicleInterface.hpp" #include "SubsystemClassData.hpp" #include #include #include #include #include //############################################################################# //########################## ExecutionStateEngine ####################### //############################################################################# BombastWeapon::ExecutionStateEngine::ClassData* BombastWeapon::ExecutionStateEngine::DefaultData = NULL; const StateEngine::StateEntry BombastWeapon::ExecutionStateEngine::StateEntries[]= { STATE_ENTRY(BombastWeapon__ExecutionStateEngine, Charging) }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BombastWeapon::ExecutionStateEngine::InitializeClass() { Check_Object(StateEngine::DefaultData); Verify(!DefaultData); DefaultData = new ClassData( BombastWeapon__ExecutionStateEngineClassID, "BombastWeapon::ExecutionStateEngine", BaseClass::DefaultData, ELEMENTS(StateEntries), StateEntries, (Entity::ExecutionStateEngine::Factory)Make, (Entity::ExecutionStateEngine::FactoryRequest::Factory) &FactoryRequest::ConstructFactoryRequest ); Register_Object(DefaultData); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BombastWeapon::ExecutionStateEngine::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BombastWeapon::ExecutionStateEngine* BombastWeapon::ExecutionStateEngine::Make( BombastWeapon *weapon, FactoryRequest *request ) { Check_Object(weapon); Check_Object(request); gos_PushCurrentHeap(Heap); BombastWeapon::ExecutionStateEngine *engine = new BombastWeapon::ExecutionStateEngine(DefaultData, weapon, request); gos_PopCurrentHeap(); return engine; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int BombastWeapon::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 firing, see if this is the start of the bombast. If so, // start the charging processes instead. Otherwise don't let anything // fire until at least half a second has gone by //-------------------------------------------------------------------- // BombastWeapon *beam; beam = Cast_Object(BombastWeapon*, owningEntity); if (new_state == FiringState) { switch (currentState) { case NeverExecuteState: new_state = ChargingState; break; case ChargingState: if ((gos_GetElapsedTime() - beam->startChargeTime) <= 0.5f) return currentState; break; case RechargingState: return currentState; } } // //--------------------------------------------------------------------- // Otherwise, make sure we only enter charging state from never execute //--------------------------------------------------------------------- // else if ((new_state == ChargingState) && (currentState != NeverExecuteState)) return currentState; // //------------------------------------------------------------------------- // We are calling weapon directly because we do not want beam functionality //------------------------------------------------------------------------- // switch (BaseClass::BaseClass::RequestState(new_state, data)) { case ChargingState: { beam->UsePostCollision(); beam->lastParameterization = gos_GetElapsedTime(); beam->startChargeTime = gos_GetElapsedTime(); beam->percentCharged = 0.25f; beam->beamRechargeTime = 0.0f; break; } case FiringState: { //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->LeaveNeverExecuteState(); beam->lastParameterization = gos_GetElapsedTime(); if(beam->guiWeapon.GetCurrent()) beam->guiWeapon.GetCurrent()->Reload(); beam->ApplyHeat(); beam->beamRechargeTime = (Scalar)(gos_GetElapsedTime() - beam->startChargeTime); const BeamWeapon::GameModel *beam_model = beam->GetGameModel(); Clamp(beam->beamRechargeTime, 0.0f, beam_model->reloadTime); } break; case NeverExecuteState: case DestroyedState: break; default: { owningEntity->IgnorePostCollision(); break; } } return currentState; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BombastWeapon::ExecutionStateEngine::TestInstance() const { Verify(IsDerivedFrom(DefaultData)); } //############################################################################# //############################### BombastWeapon ######################### //############################################################################# BombastWeapon::ClassData* BombastWeapon::DefaultData = NULL; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BombastWeapon::InitializeClass() { Verify(!DefaultData); DefaultData = new ClassData( BombastWeaponClassID, "MechWarrior4::BombastWeapon", 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( BombastWeapon, ExecutionState, executionState, BombastWeapon__ExecutionStateEngine ); DIRECT_GAME_MODEL_ATTRIBUTE( BombastWeapon__GameModel, MaxChargeTime, maxChargeTime, Scalar ); DIRECT_GAME_MODEL_ATTRIBUTE( BombastWeapon__GameModel, TimeForMaxCharge, timeForMaxCharge, Scalar ); DIRECT_GAME_MODEL_ATTRIBUTE( BombastWeapon__GameModel, ChargeEffectResource, chargeEffectResource, ResourceID ); DIRECT_GAME_MODEL_ATTRIBUTE( BombastWeapon__GameModel, InitialChargeBeamResource, initialChargeBeamResource, ResourceID ); DIRECT_GAME_MODEL_ATTRIBUTE( BombastWeapon__GameModel, HalfChargeBeamResource, halfChargeBeamResource, ResourceID ); DIRECT_GAME_MODEL_ATTRIBUTE( BombastWeapon__GameModel, ThreeQuarterChargeBeamResource, threeQuarterChargeBeamResource, ResourceID ); DIRECT_GAME_MODEL_ATTRIBUTE( BombastWeapon__GameModel, FullChargeBeamResource, fullChargeBeamResource, ResourceID ); DIRECT_GAME_MODEL_ATTRIBUTE( BombastWeapon__GameModel, InitialChargeFlareResource, initialChargeFlareResource, ResourceID ); DIRECT_GAME_MODEL_ATTRIBUTE( BombastWeapon__GameModel, HalfChargeFlareResource, halfChargeFlareResource, ResourceID ); DIRECT_GAME_MODEL_ATTRIBUTE( BombastWeapon__GameModel, ThreeQuarterChargeFlareResource, threeQuarterChargeFlareResource, ResourceID ); DIRECT_GAME_MODEL_ATTRIBUTE( BombastWeapon__GameModel, FullChargeFlareResource, fullChargeFlareResource, ResourceID ); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BombastWeapon::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BombastWeapon* BombastWeapon::Make( CreateMessage *message, ReplicatorID *base_id ) { gos_PushCurrentHeap(Heap); BombastWeapon *new_entity = new BombastWeapon(DefaultData, message, base_id, NULL); gos_PopCurrentHeap(); Check_Object(new_entity); return new_entity; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BombastWeapon::BombastWeapon( ClassData *class_data, CreateMessage *message, ReplicatorID *base_id, ElementRenderer::Element *element ): BeamWeapon(class_data, message, base_id, element), chargeEffect(NULL) { Check_Pointer(this); Check_Object(message); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // BombastWeapon::~BombastWeapon() { } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool BombastWeapon::ReadyToFire() { if (CanFireFromCurrentArm() == false) { return (false); } if (executionState->GetState()==ExecutionStateEngine::NeverExecuteState) { return true; } if ( executionState->GetState() == ExecutionStateEngine::ChargingState ) { if ((gos_GetElapsedTime() - startChargeTime) > 0.5f) { return true; } } return false; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BombastWeapon::PreCollisionExecute(Time till) { PRECOLLISION_LOGIC("Subsystem::Bombast"); Check_Object(executionState); int pre_state = executionState->GetState(); switch (pre_state) { case ExecutionStateEngine::ChargingState: { if((GetTimeParameter(till) > 1.0f) && (percentCharged < 1.0f)) { percentCharged = percentCharged + 0.25f; lastParameterization = gos_GetElapsedTime(); } #if 0 if((gos_GetElapsedTime() - startChargeTime) > model->maxChargeTime) { Verify(GetParentVehicle()->IsDerivedFrom(Vehicle::DefaultData)); Vehicle *vehicle = Cast_Object(Vehicle *, GetParentVehicle()); Check_Object(vehicle); if(vehicle->vehicleInterface) { vehicle->vehicleInterface->targetQuery.m_raySource = vehicle->vehicleInterface->targetEntityPart.GetCurrent(); Fire(&vehicle->vehicleInterface->targetQuery, 0.0f, Stuff::Point3D::Identity); } else { executionState->RequestState(ExecutionStateEngine::FiringState); } } #endif break; } } BaseClass::PreCollisionExecute(till); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BombastWeapon::PostCollisionExecute(Time till) { Check_Object(this); Check_Object(executionState); POSTCOLLISION_LOGIC("Subsystem::Bombast"); int pre_state = executionState->GetState(); switch (pre_state) { case ExecutionStateEngine::ChargingState: { // //-------------------------- //Create the charging effect //-------------------------- // if(!chargeEffect.GetCurrent()) CreateChargeEffect(); break; } default: { if(chargeEffect.GetCurrent()) chargeEffect.GetCurrent()->SentenceToDeathRow(); BaseClass::PostCollisionExecute(till); } } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BombastWeapon::CreateBeam() { const GameModel *model = GetGameModel(); Check_Object(model); ResourceID beam_resource; if(percentCharged <= 0.25f) beam_resource = model->initialChargeBeamResource; else if(percentCharged <= 0.50f) beam_resource = model->halfChargeBeamResource; else if(percentCharged <= 0.75) beam_resource = model->threeQuarterChargeBeamResource; else beam_resource = model->fullChargeBeamResource; Verify(beam_resource != ResourceID::Null); ClassID class_ID; class_ID = GetClassIDFromDataListID(beam_resource); 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) { targetQuery.m_line->m_length = model->maxDistance; targetQuery.m_line->FindEnd(&target_point); targetQuery.m_raySource = NULL; } else { if(targetQuery.m_raySource) { Check_Object(targetQuery.m_raySource); target_id = targetQuery.m_raySource->GetReplicatorID(); targetQuery.m_line->FindEnd(&target_point); target_normal = *targetQuery.m_normal; } else { targetQuery.m_line->FindEnd(&target_point); } } Stuff::Scalar damage_amount, heat_amount; damage_amount = percentCharged * model->damageAmount; heat_amount = percentCharged * model->heatToDeal; BeamEntity::CreateMessage beam_create_message( sizeof(BeamEntity::CreateMessage), DefaultEventPriority, CreateMessage::DefaultFlags, class_ID, BeamEntity::DefaultFlags, beam_resource, sitePointer->GetLocalToWorld(), 0.0f, ExecutionStateEngine::AlwaysExecuteState, NameTable::NullObjectID, Entity::DefaultAlignment, inflicting_id, site_name, target_point, target_id, target_normal, damage_amount, heat_amount, 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 BombastWeapon::CreateChargeEffect() { Check_Object(this); const GameModel *model = GetGameModel(); Check_Object(model); if(model->chargeEffectResource == ResourceID::Null) return; Resource charge_resource(model->chargeEffectResource); if(charge_resource.DoesResourceExist()) { ClassID class_ID; class_ID = GetClassIDFromDataListID(model->chargeEffectResource); Verify(class_ID != NullClassID); LinearMatrix4D effect_position = LinearMatrix4D::Identity; UnitVector3D unit_forward; Check_Object(sitePointer); LinearMatrix4D site_position = sitePointer->GetLocalToWorld(); site_position.GetLocalForwardInWorld(&unit_forward); effect_position.AlignLocalAxisToWorldVector(unit_forward, Y_Axis, X_Axis, Z_Axis); Point3D muzzle_translation; muzzle_translation = sitePointer->GetLocalToWorld(); effect_position.BuildTranslation(muzzle_translation); Effect::CreateMessage effect_create_message( sizeof(Effect::CreateMessage), DefaultEventPriority, CreateMessage::DefaultFlags, class_ID, Effect::DefaultFlags, model->chargeEffectResource, effect_position, 0.0f, ExecutionStateEngine::AlwaysExecuteState, NameTable::NullObjectID, Entity::DefaultAlignment ); MemoryStream stream(&effect_create_message, effect_create_message.messageLength); Entity *entity; ReplicatorID beam_id = Connection::Hermit->GetNextReplicatorID(); entity = CreateEntity(&stream, &beam_id, false); Check_Object(Map::GetInstance()); Map::GetInstance()->AddChild(entity); chargeEffect.Remove(); chargeEffect.Add(Cast_Object(Effect *, entity)); chargeEffect.GetCurrent()->SetFollowEntity(sitePointer); chargeEffect.GetCurrent()->SyncMatrices(true); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BombastWeapon::CleanUpCreatedBeam() { Check_Object(this); if(beamEntity.GetCurrent()) beamEntity.GetCurrent()->SentenceToDeathRow(); if(chargeEffect.GetCurrent()) chargeEffect.GetCurrent()->SentenceToDeathRow(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BombastWeapon::CreateMuzzleFlash() { Check_Object(this); const GameModel *model = GetGameModel(); Check_Object(model); ResourceID flare_resource; if(percentCharged <= 0.25f) flare_resource = model->initialChargeFlareResource; else if(percentCharged <= 0.50f) flare_resource = model->halfChargeFlareResource; else if(percentCharged < 0.75) flare_resource = model->threeQuarterChargeFlareResource; else flare_resource = model->fullChargeFlareResource; Verify(flare_resource != ResourceID::Null); // if(model->muzzleFlashResource == ResourceID::Null) // return; Resource muzzle_flash_resource(flare_resource); if(muzzle_flash_resource.DoesResourceExist()) { ClassID class_ID; class_ID = GetClassIDFromDataListID(flare_resource); Verify(class_ID != NullClassID); LinearMatrix4D effect_position = LinearMatrix4D::Identity; UnitVector3D unit_forward; Check_Object(sitePointer); LinearMatrix4D site_position = sitePointer->GetLocalToWorld(); site_position.GetLocalForwardInWorld(&unit_forward); effect_position.AlignLocalAxisToWorldVector(unit_forward, Y_Axis, X_Axis, Z_Axis); Point3D muzzle_translation; muzzle_translation = sitePointer->GetLocalToWorld(); effect_position.BuildTranslation(muzzle_translation); Effect::CreateMessage effect_create_message( sizeof(Effect::CreateMessage), DefaultEventPriority, CreateMessage::DefaultFlags, class_ID, Effect::DefaultFlags, flare_resource, effect_position, 0.0f, ExecutionStateEngine::AlwaysExecuteState, NameTable::NullObjectID, Entity::DefaultAlignment ); MemoryStream stream(&effect_create_message, effect_create_message.messageLength); Entity *entity; ReplicatorID beam_id = Connection::Hermit->GetNextReplicatorID(); entity = CreateEntity(&stream, &beam_id, false); Check_Object(Map::GetInstance()); Map::GetInstance()->AddChild(entity); Effect *effect; effect = Cast_Object(Effect *, entity); effect->SetFollowEntity(sitePointer); entity->SyncMatrices(true); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BombastWeapon::SetQuery(CollisionQuery *query) { Check_Object(this); Check_Object(sitePointer); 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; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void BombastWeapon::TestInstance() const { Verify(IsDerivedFrom(DefaultData)); }