//===========================================================================// // File: JumpJet.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 "JumpJet.hpp" #include "Mech.hpp" #include "VehicleInterface.hpp" #include "SubsystemClassData.hpp" #include #include #include #include #include "ai_userconstants.hpp" //############################################################################# //########################## ExecutionStateEngine ####################### //############################################################################# JumpJet::ExecutionStateEngine::ClassData* JumpJet::ExecutionStateEngine::DefaultData = NULL; const StateEngine::StateEntry JumpJet::ExecutionStateEngine::StateEntries[]= { STATE_ENTRY(JumpJet__ExecutionStateEngine, Jumping), STATE_ENTRY(JumpJet__ExecutionStateEngine, Recharging) }; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void JumpJet::ExecutionStateEngine::InitializeClass() { Check_Object(StateEngine::DefaultData); Verify(!DefaultData); DefaultData = new ClassData( JumpJet__ExecutionStateEngineClassID, "JumpJet::ExecutionStateEngine", BaseClass::DefaultData, ELEMENTS(StateEntries), StateEntries, (Entity::ExecutionStateEngine::Factory)Make, (Entity::ExecutionStateEngine::FactoryRequest::Factory) &FactoryRequest::ConstructFactoryRequest ); Register_Object(DefaultData); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void JumpJet::ExecutionStateEngine::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // JumpJet::ExecutionStateEngine* JumpJet::ExecutionStateEngine::Make( JumpJet *jump_jet, FactoryRequest *request ) { Check_Object(jump_jet); Check_Object(request); gos_PushCurrentHeap(Heap); JumpJet::ExecutionStateEngine *engine = new JumpJet::ExecutionStateEngine(DefaultData, jump_jet, request); gos_PopCurrentHeap(); return engine; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void MechWarrior4::JumpJetSecurityCheckStart() { _asm { nop } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // int JumpJet::ExecutionStateEngine::RequestState( int new_state, void* data ) { Check_Object(this); Check_Object(owningEntity); // //------------------------------------------- // If there is no change of state just return //------------------------------------------- // if (new_state == currentState) return currentState; // //--------------------------------------------------- // If we have less than one second charge, don't jump //--------------------------------------------------- // JumpJet *jump_jet; jump_jet = Cast_Object(JumpJet*, owningEntity); if (new_state == JumpingState && DECRYPT(jump_jet->m_currentEncryptedCharge) < 0.25f) return currentState; if(new_state == DestroyedState) return currentState; // //-------------------------------------------------------------------- // Look for the transition type. Our base class should handle destroy // and never execute commands, but make sure that we only go into // zero-g state from jumping state //-------------------------------------------------------------------- // switch (BaseClass::RequestState(new_state, data)) { case JumpingState: { jump_jet->UsePostCollision(); jump_jet->lastExecuted = gos_GetElapsedTime(); jump_jet->m_needToCreateEffects = true; // //---------------------------------------------------------------- // If the mech is walking right now, we launch the initial effects //---------------------------------------------------------------- // Check_Object(jump_jet->m_parentMech); Mech::ExecutionStateEngine *engine = Cast_Object(Mech::ExecutionStateEngine *, jump_jet->m_parentMech->executionState); Check_Object(engine); if (engine->GetState() == engine->DrivingMotionState) jump_jet->m_needToCreateInitialEffects = true; jump_jet->lastParameterization = gos_GetElapsedTime(); } break; case NeverExecuteState: if (oldState != RechargingState) jump_jet->CleanUpEffects(); jump_jet->m_needToCreateEffects = false; jump_jet->m_needToCreateInitialEffects = false; jump_jet->lastParameterization = gos_GetElapsedTime(); break; case RechargingState: if (oldState != NeverExecuteState) jump_jet->CleanUpEffects(); jump_jet->m_needToCreateEffects = false; jump_jet->m_needToCreateInitialEffects = false; jump_jet->lastParameterization = gos_GetElapsedTime(); break; // //------------------------------------------------------------------ // Our base class has handled destruction, so don't do anything more //------------------------------------------------------------------ // case DestroyedState: break; default: STOP(("Shouldn't be here")); break; }; return currentState; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void JumpJet::ExecutionStateEngine::TestInstance() const { Verify(IsDerivedFrom(DefaultData)); } //############################################################################# //############################### JumpJet ############################### //############################################################################# JumpJet::ClassData* JumpJet::DefaultData = NULL; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void JumpJet::InitializeClass() { Verify(!DefaultData); DefaultData = new ClassData( JumpJetClassID, "MechWarrior4::JumpJet", 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); DIRECT_GAME_MODEL_ATTRIBUTE( JumpJet__GameModel, RechargeTime, m_rechargeTime, Scalar ); DIRECT_GAME_MODEL_ATTRIBUTE( JumpJet__GameModel, BurnTime, m_burnTime, Scalar, ); DIRECT_GAME_MODEL_ATTRIBUTE( JumpJet__GameModel, FlightTime, m_flightTime, Scalar, ); DIRECT_GAME_MODEL_ATTRIBUTE( JumpJet__GameModel, SlipDistance, m_slipDistance, Scalar, ); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // //moved it so it won't be in the same spot in the new executable void JumpJet::PreCollisionExecute(Time till) { Check_Object(this); Check_Object(m_parentMech); PRECOLLISION_LOGIC("Subsystem::JumpJet"); Stuff::Scalar current_charge = DECRYPT(m_currentEncryptedCharge); // //--------------------- // Figure out our state //--------------------- // Scalar time_slice = GetTimeSlice(till); const GameModel* model = GetGameModel(); Check_Object(model); Check_Object(executionState); int pre_state = executionState->GetState(); Verify(pre_state != ExecutionStateEngine::NeverExecuteState); // //--------------------------------------------------------------------- // If we are jumping, deal with applying the force if we are not out of // power //--------------------------------------------------------------------- // switch (pre_state) { case ExecutionStateEngine::JumpingState: // only the master is allowed to do decrement the charge // if (!m_EffectOnly) // { // everyone is allowed to decrement the charge... current_charge -= time_slice; SetCurrentCharge(current_charge); if (current_charge <= 0.0f) { current_charge = 0.0f; SetCurrentCharge(current_charge); executionState->RequestState(ExecutionStateEngine::RechargingState); } else { Vector3D &accel = m_parentMech->localSpaceAcceleration.linearMotion; Vector3D &velocity = m_parentMech->localSpaceVelocity.linearMotion; if (velocity.z > 0.0f && m_parentMech->speedDemand <= 0.0f) accel.z += m_parentMech->speedDemand*m_slide; else accel.y += m_acceleration; if (MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::flying_turn_rate) == 0.0f) accel.x += m_parentMech->yawDemand * m_slide; } // } break; // //----------------- // Start recharging //----------------- // case ExecutionStateEngine::RechargingState: // but only the master/server is allowed to increment the jump charge // if (GetReplicatorMode() == ServerMasterMode || GetReplicatorMode() == MasterMode) // { Verify(!m_leftJumpJetEffect.GetCurrent()); Verify(!m_rightJumpJetEffect.GetCurrent()); current_charge += time_slice * m_rechargeRate; Max_Clamp(current_charge, model->m_burnTime); SetCurrentCharge(current_charge); // } if(current_charge == model->m_burnTime) executionState->RequestState(ExecutionStateEngine::NeverExecuteState); break; } SPEWALWAYS(("jerryeds", "JUMP FUEL: %f", current_charge)); // //------------------------------------ // Let our parent deal with things now //------------------------------------ // BaseClass::PreCollisionExecute(till); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void JumpJet::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // JumpJet* JumpJet::Make( CreateMessage *message, ReplicatorID *base_id ) { Check_Object(message); gos_PushCurrentHeap(Heap); JumpJet *new_entity = new JumpJet(DefaultData, message, base_id, NULL); gos_PopCurrentHeap(); Check_Object(new_entity); return new_entity; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // JumpJet::JumpJet( ClassData *class_data, CreateMessage *message, ReplicatorID *base_id, ElementRenderer::Element *element ): Subsystem(class_data, message, base_id, element), m_rightJumpJetEffect(NULL), m_leftJumpJetEffect(NULL), m_initialRightJumpJetEffect(NULL), m_initialLeftJumpJetEffect(NULL) { Check_Pointer(this); Check_Object(message); CommonCreation(message); m_jumpJetEffectResource = ResourceID::Null; m_initialJumpJetEffectResource = ResourceID::Null; m_leftJumpJetSite = NULL; m_rightJumpJetSite = NULL; m_parentMech = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void JumpJet::Respawn(Entity::CreateMessage *message) { Check_Object(this); Check_Object(message); BaseClass::Respawn(message); CreateMessage *jump_jet_message = Cast_Pointer(CreateMessage *, message); CommonCreation(jump_jet_message); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void JumpJet::CommonCreation(CreateMessage *message) { Check_Object(this); Check_Object(message); const GameModel *model = GetGameModel(); Check_Object(model); m_needToCreateEffects = false; m_needToCreateInitialEffects = false; m_currentEncryptedCharge = ENCRYPT(model->m_burnTime); Verify(model->m_burnTime > 0); Verify(model->m_rechargeTime > 0); m_rechargeRate = model->m_burnTime / model->m_rechargeTime; // //------------------------------------------------------------ // Now calculate the acceleration needed to give these numbers //------------------------------------------------------------ // Scalar temp = model->m_flightTime - model->m_burnTime; Verify(temp > 0.0f); m_acceleration = g_Gravity*temp*temp / (2.0f*model->m_flightTime - model->m_burnTime); Verify(m_acceleration > g_Gravity); // //--------------------------------- // Figure out our side acceleration //--------------------------------- // Verify(model->m_flightTime >= 0.0f); m_slide = model->m_slipDistance / (model->m_burnTime*(model->m_flightTime - 0.5f*model->m_burnTime)); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // JumpJet::~JumpJet() { } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Stuff::Scalar JumpJet::CalcBurnTime( Stuff::Scalar distance, Stuff::Scalar heightdelta, Stuff::Scalar forwardvel ) { Stuff::Scalar burn,upvel,downvel; Stuff::Scalar num,denom,ds; if (forwardvel == 0.0f) return FLT_MAX; ds = distance / forwardvel; upvel = m_acceleration; downvel = g_Gravity; num = ((upvel+downvel) * ((upvel*ds*ds) - (2.0f*heightdelta))); denom = upvel+downvel; if (num <= SMALL) return FLT_MAX; num = Sqrt(num); Verify (denom != 0.0f); burn = ds - (num/denom); return burn; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void MechWarrior4::JumpJetSecurityCheckStop() { _asm { nop } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void JumpJet::PostCollisionExecute(Time till) { Check_Object(this); POSTCOLLISION_LOGIC("Subsystem::JumpJet"); if(m_needToCreateInitialEffects) { CreateInitialJumpEffects(); m_needToCreateInitialEffects = false; } if(m_needToCreateEffects) { CreateJumpJetEffects(); m_needToCreateEffects = false; } IgnorePostCollision(); BaseClass::PostCollisionExecute(till); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void JumpJet::CreateJumpJetEffects() { Check_Object(this); Check_Object(m_parentMech); Check_Object(m_leftJumpJetSite); Check_Object(m_rightJumpJetSite); Verify(executionState->GetState() == ExecutionStateEngine::JumpingState); Effect *jump_jet_effect; if(m_leftJumpJetEffect.GetCurrent() == NULL) { jump_jet_effect = m_parentMech->CreateLoopingEffect( m_jumpJetEffectResource, m_leftJumpJetSite, true ); Check_Object(jump_jet_effect); jump_jet_effect->BecomeInteresting (true); jump_jet_effect->executionState->RequestState(Effect::ExecutionStateEngine::RunningState); m_leftJumpJetEffect.Add(jump_jet_effect); } if(m_rightJumpJetEffect.GetCurrent() == NULL) { jump_jet_effect = m_parentMech->CreateLoopingEffect( m_jumpJetEffectResource, m_rightJumpJetSite, true ); Check_Object(jump_jet_effect); jump_jet_effect->BecomeInteresting (true); jump_jet_effect->executionState->RequestState(Effect::ExecutionStateEngine::RunningState); m_rightJumpJetEffect.Add(jump_jet_effect); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void JumpJet::CreateInitialJumpEffects() { Check_Object(this); Check_Object(m_parentMech); Check_Object(m_leftJumpJetSite); Check_Object(m_rightJumpJetSite); Verify(executionState->GetState() == ExecutionStateEngine::JumpingState); Effect *jump_jet_effect; if(m_initialLeftJumpJetEffect.GetCurrent() == NULL) { jump_jet_effect = m_parentMech->CreateEffect( m_initialJumpJetEffectResource, m_leftJumpJetSite, true ); if(jump_jet_effect) m_initialLeftJumpJetEffect.Add(jump_jet_effect); } if(m_initialRightJumpJetEffect.GetCurrent() == NULL) { jump_jet_effect = m_parentMech->CreateEffect( m_initialJumpJetEffectResource, m_rightJumpJetSite, true ); if(jump_jet_effect) m_initialRightJumpJetEffect.Add(jump_jet_effect); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool JumpJet::ConnectSubsystem() { Check_Object(this); Verify(GetParentVehicle()->IsDerivedFrom(Mech::DefaultData)); m_parentMech = Cast_Pointer(Mech*, GetParentVehicle()); const Mech::GameModel *mech_model = m_parentMech->GetGameModel(); Check_Object(mech_model); bool return_value = false; if(m_parentMech->DoesHaveAvailableTonage(mech_model->m_jumpJetTonnage)) return_value = BaseClass::ConnectSubsystem(); if(return_value) m_parentMech->AddTonage(mech_model->m_jumpJetTonnage); return return_value; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // // bool JumpJet::DisconnectSubsystem() { Check_Object(this); Verify(GetParentVehicle()->IsDerivedFrom(Mech::DefaultData)); m_parentMech = Cast_Pointer(Mech*, GetParentVehicle()); const Mech::GameModel *mech_model = m_parentMech->GetGameModel(); Check_Object(mech_model); bool return_value = false; return_value = BaseClass::DisconnectSubsystem(); if(return_value) m_parentMech->SubtractTonage(mech_model->m_jumpJetTonnage); return return_value; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool JumpJet::TakeCriticalHit() { Check_Object(this); //Don't do anything here return false; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void JumpJet::DestroySubsystem() { Check_Object(this); #if 0 BaseClass::DestroySubsystem(); const Mech::GameModel *mech_model = m_parentMech->GetGameModel(); Check_Object(mech_model); m_parentMech->SubtractTonage(mech_model->m_jumpJetTonnage); EnterNeverExecuteState(); IgnorePostCollision(); CleanUpEffects(); #endif } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void JumpJet::CleanUpEffects() { Check_Object(this); if(m_leftJumpJetEffect.GetCurrent()) { m_leftJumpJetEffect.GetCurrent()->executionState->RequestState( Effect::ExecutionStateEngine::StoppingState); m_leftJumpJetEffect.Remove(); } if(m_rightJumpJetEffect.GetCurrent()) { m_rightJumpJetEffect.GetCurrent()->executionState->RequestState( Effect::ExecutionStateEngine::StoppingState); m_rightJumpJetEffect.Remove(); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void JumpJet::TestInstance() const { Verify(IsDerivedFrom(DefaultData)); }