//===========================================================================// // File: Tank.cpp //---------------------------------------------------------------------------// // Date Who Modification // // -------- --- ---------------------------------------------------------- // // 09/09/98 JSE Inital base class based off of Shadowrun/Adept // // 09/23/98 BDB State engine update // //---------------------------------------------------------------------------// // Copyright (C) 1998, Fasa Interactive // // All Rights reserved worldwide // // This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL // //===========================================================================// #include "MW4Headers.hpp" #include "Tank.hpp" #include "gameinfo.hpp" #include "AI.hpp" #include "AI_UserConstants.hpp" #include "Mech.hpp" #include "MWDamageObject.hpp" #include #include "obstacle.hpp" // MSL 5.04 Tank Scorable #include "MWMission.hpp" #include #include #include //############################################################################# //############################### Tank ############################## //############################################################################# //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Tank::ClassData* Tank::DefaultData = NULL; DWORD MechWarrior4::Executed_Tank_Count = 0; //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Tank::InitializeClass() { Check_Object(MWMover::ExecutionStateEngine::DefaultData); Verify(!DefaultData); DefaultData = new ClassData( TankClassID, "MechWarrior4::Tank", BaseClass::DefaultData, 0, NULL, (Entity::Factory)Make, (Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage, ExecutionStateEngine::DefaultData, (Entity::GameModel::Factory)GameModel::ConstructGameModel, (Entity::GameModel::Factory)GameModel::ConstructOBBStream, (Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify, (Entity::GameModel::ModelWrite)GameModel::WriteToText, (Entity::GameModel::ModelSave)GameModel::SaveGameModel, AnimationStateEngine::Make ); Register_Object(DefaultData); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Tank::TerminateClass() { Unregister_Object(DefaultData); delete DefaultData; DefaultData = NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Tank* Tank::Make( CreateMessage *message, ReplicatorID *base_id ) { Check_Object(message); gos_PushCurrentHeap(Heap); Tank *new_entity = new Tank(DefaultData, message, base_id, NULL); gos_PopCurrentHeap(); Check_Object(new_entity); Check_Object(EntityManager::GetInstance()); EntityManager::GetInstance()->AddMover(new_entity); return new_entity; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Tank::Tank( ClassData *class_data, CreateMessage *message, ReplicatorID *base_id, ElementRenderer::Element *element ): Vehicle(class_data, message, base_id, element) { Check_Pointer(this); Check_Object(message); HookUpSubsystems(); CommonCreation(message); // MSL 5.04 Tank Scorable //should this tank be scored? m_scoreThisTank = false; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Tank::~Tank() { Check_Object(EntityManager::GetInstance()); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Tank::Reuse( const CreateMessage *message, ReplicatorID *base_id ) { Check_Object(this); Check_Object(message); STOP(("Not implemented")); BaseClass::Reuse(message, base_id); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Tank::CommonCreation(CreateMessage *message) { Check_Object(this); Check_Object(message); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Tank::Respawn(Entity::CreateMessage *message) { Check_Object(this); Check_Object(message); BaseClass::Respawn(message); CreateMessage *vehicle_message = Cast_Pointer(CreateMessage *, message); CommonCreation(vehicle_message); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Tank::PreCollisionExecute(Time till) { Check_Object(this); PRECOLLISION_LOGIC("Tank"); #if defined(LAB_ONLY) if (!instanceName) MWGameInfo::g_LastVehicle[0] = '\0'; else { strncpy(MWGameInfo::g_LastVehicle, instanceName, sizeof(MWGameInfo::g_LastVehicle)-1); MWGameInfo::g_LastVehicle[sizeof(MWGameInfo::g_LastVehicle)-1] = '\0'; } MWGameInfo::g_LastVehiclePos = GetLocalToWorld (); #endif // //------------------------------------------------------------- // Let the base class deal with setting up the motion variables //------------------------------------------------------------- // BaseClass::PreCollisionExecute(till); Set_Statistic(Executed_Tank_Count, Executed_Tank_Count+1); // //---------------------------------------------------------------------- // Any executing vehicle should not be in never execute state and should // always use post collision //---------------------------------------------------------------------- // Check_Object(executionState); int pre_state = executionState->GetState(); Verify(pre_state != ExecutionStateEngine::NeverExecuteState); // //------------------------------------------------------------------------- // Do the appropriate type of simulation. Animated motion state is for BRB //------------------------------------------------------------------------- // Scalar time_slice = GetTimeSlice(till); switch (pre_state) { // //---------------------------------------------------- // Always execute state is for the normal driving code //---------------------------------------------------- // case ExecutionStateEngine::DyingState: ComputeFrictionSpeed (time_slice); UpdateVehiclePosition (time_slice); break; case ExecutionStateEngine::DrivingMotionState: ComputeForwardSpeed(time_slice); UpdateVehiclePosition(time_slice); break; // //---------------------------------------------------- // Always execute state is for the normal driving code //---------------------------------------------------- // case ExecutionStateEngine::FlyingMotionState: ComputeForwardSpeed(time_slice); UpdateVehiclePosition(time_slice); break; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Tank::PostCollisionExecute (Stuff::Time time) { Check_Object(this); POSTCOLLISION_LOGIC("Tank"); lastParameterization = time; initialLocalToParent = GetLocalToParent(); initialWorldSpaceVelocity = worldSpaceVelocity, initialWorldSpaceAcceleration = worldSpaceAcceleration; int pre_state = executionState->GetState(); Verify(pre_state != ExecutionStateEngine::NeverExecuteState); switch (pre_state) { case ExecutionStateEngine::DyingState: // check for unit stopped falling and moving if ((localSpaceVelocity.linearMotion.z == 0.0f) && ((localSpaceVelocity.linearMotion.y == 0.0f))) { Verify (m_DeathDamageMode != -1); Verify (m_DeathDamageType != -1); const GameModel *model = GetGameModel(); Check_Object(model); DealSplashDamage(); SetDead (); Entity::ReactToDestruction(m_DeathDamageMode,m_DeathDamageType); // CreateEffect(model->secondaryDestroyedEffectResource, this); if(!m_deathEntity.GetCurrent()) { Entity *death_entity = CreateStaticHermitEntity(model->deathEntityResource); if(death_entity) { m_deathEntity.Remove(); m_deathEntity.Add(death_entity); } } executionState->RequestState(ExecutionStateEngine::NeverExecuteState); entityElement->SetAlwaysCullMode(); RemoveCollision(); lastParameterization = GetTimeSlice (time); initialLocalToParent = GetLocalToParent(); SyncMatrices(true); } break; } BaseClass::PostCollisionExecute (time); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // bool Tank::CollisionHandler( Stuff::LinearMatrix4D *new_position, Stuff::DynamicArrayOf *collisions ) { Check_Object(this); Check_Object(collisions); Verify(GetInterestLevel() != DormantInterestLevel); bool shoulddamage=true; // //---------------------------------------------------------- // If we are destroyed, delete the collision list and return //---------------------------------------------------------- // if (IsDestroyed()) { Verify(!newCollisions); delete collisions; return false; } // //-------------------------------- // Ask the AI about the collision //-------------------------------- // if (m_AI) { if (!m_AI->ReactToCollision(collisions,shoulddamage)) // ai will return false if collision should not occur { Verify(!newCollisions); delete collisions; return false; } } // //---------------------------------------- // Turn off collision if we are going fast //---------------------------------------- // #ifdef LAB_ONLY if (m_DebugFast) { Verify(!newCollisions); delete collisions; return false; } #endif // //----------------------- // Iterate the collisions //----------------------- // bool just_bump = true; for (int i=0; iGetLength(); ++i) { CollisionData *data = &(*collisions)[i]; Check_Pointer(data); // //--------------------------------------------------------------------- // Look to see if we have run into a collider, and if so, calculate the // relative velocity //--------------------------------------------------------------------- // Entity *other = data->m_otherEntity; if (other->IsACollider()) { just_bump = false; Adept::Mover *adeptmover = Cast_Object(Adept::Mover*, other); Vector3D rel_vel; rel_vel.Subtract(adeptmover->worldSpaceVelocity.linearMotion, worldSpaceVelocity.linearMotion); Scalar speed = rel_vel.GetLengthSquared(); MWObject *mover = Cast_Object(MWObject*, other); // //------------------------------- // If its a mech, get its ratings //------------------------------- // if (other->GetClassID() == MechClassID) { Mech *mech = Cast_Object(Mech*, mover); int mech_class; Scalar damage_received; Scalar mech_speed; if (mech->GetTonage() >= MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_heavy_tonage)) { damage_received = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_heavy_damage); mech_speed = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_heavy_building_speed); mech_class = HeavyCanWalkThruType; } else if (mech->GetTonage() >= MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_medium_tonage)) { damage_received = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_medium_damage); mech_speed = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_medium_building_speed); mech_class = MediumCanWalkThruType; } else { damage_received = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_light_damage); mech_speed = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_mech_light_building_speed); mech_class = AnythingCanWalkThruType; } // //---------------------- // See if it squishes us //---------------------- // if (GetWalkThruType() >= mech_class) { if (shoulddamage) { TakeDamageMessage damage_message( GetReplicatorID(), other->GetReplicatorID(), 100000.0f, // MSL 5.05 Advance Mode 0, RammingDamageFromAboveType, data->m_normal, data->m_worldIntersectionPoint, 0.0f, false, TakeDamageMessage::DefaultWeaponType ); Receive(&damage_message); } } // //----------------------------------------------- // See if we are going fast enough to get damaged //----------------------------------------------- // else if (speed >= mech_speed) { speed = Sqrt(speed)*mover->GetTonage(); // //------------------------ // Evaluate the DFA states //------------------------ // Scalar min_dfa_vel = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_dfa_speed); Entity::ExecutionStateEngine *engine = mech->executionState; bool their_dfa = mech->worldSpaceVelocity.linearMotion.y <= -min_dfa_vel && engine->GetState() == ExecutionStateEngine::FlyingMotionState; // //--------------------------------------- // Use the appropriate collision modifier //--------------------------------------- // char damage_type; if (their_dfa) { damage_type = RammingDamageFromAboveType; speed *= MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_dfa_damage_given); } else { damage_type = RammingDamageFromAheadType; speed *= damage_received; } // //---------------------------------- // Set the damage message to ourself //---------------------------------- // // MSL 5.04 Tank Collision Damage // MSL 5.06 Armor Mode MWApplication *m_App; m_App = MWApplication::GetInstance (); m_ArmorMode = m_App->GetLocalNetParams()->m_armormodeOn; if (shoulddamage) { if (m_ArmorMode) { TakeDamageMessage damage_message( GetReplicatorID(), other->GetReplicatorID(), 0.1f, m_ArmorMode, damage_type, data->m_normal, data->m_worldIntersectionPoint, 0.0f, false, TakeDamageMessage::DefaultWeaponType ); Receive(&damage_message); } else { TakeDamageMessage damage_message( GetReplicatorID(), other->GetReplicatorID(), speed, m_ArmorMode, damage_type, data->m_normal, data->m_worldIntersectionPoint, 0.0f, false, TakeDamageMessage::DefaultWeaponType ); Receive(&damage_message); } } } } } // //-------------------------------------------------------------- // If we've run into a static object, see if we can just kill it //-------------------------------------------------------------- // else { if (GetWalkThruType() <= other->GetWalkThruType()) { if (shoulddamage) { TakeDamageMessage damage_message( other->GetReplicatorID(), GetReplicatorID(), 100000.0f, // MSL 5.05 Advance Mode 0, RammingDamageFromAboveType, data->m_otherNormal, data->m_worldIntersectionPoint, 0.0f, false, TakeDamageMessage::DefaultWeaponType ); other->Receive(&damage_message); } } // //------------------------------ // Otherwise, just stop the tank //------------------------------ // else just_bump = false; } } // //------------------------------- // Slow down the tank as required //------------------------------- // Scalar slowdown = MW4AI::UserConstants::m_Instance->Get(MW4AI::UserConstants::collision_tank_slowdown); bool result = false; if (just_bump) { if (currentSpeedMPS > 0.0f) { currentSpeedMPS -= slowdown; Min_Clamp(currentSpeedMPS, 0.0f); } if (currentSpeedMPS < 0.0f) { currentSpeedMPS += slowdown; Max_Clamp(currentSpeedMPS, 0.0f); } } // //------------------------- // Otherwise, stop the tank //------------------------- // else { result = true; currentSpeedMPS = 0.0f; *new_position = GetLocalToParent(); } currentSpeedKPH = currentSpeedMPS * 3.6f; // //-------------------------------------------------------------------- // If the tank isn't executing, it can't move or retain its collisions //-------------------------------------------------------------------- // Entity::ExecutionStateEngine *engine = executionState; if (engine->GetState() == ExecutionStateEngine::NeverExecuteState || !IsUsingPostCollision()) { Verify(!newCollisions); delete collisions; return false; } newCollisions = collisions; return result; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Tank::TestInstance() const { Verify(IsDerivedFrom(DefaultData)); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Tank::ReactToDestruction(int damage_mode, int damage_type) { if (damage_mode != InternalDamageObject::DestructionDamageMode) { SPEW(("jerryeds", "DYING WITHOUT DEATH!!!")); SPEW(("daberger", "DYING WITHOUT DEATH!!!")); damage_mode = InternalDamageObject::DestructionDamageMode; } if ((localSpaceVelocity.linearMotion.z == 0.0f) && ((localSpaceVelocity.linearMotion.y == 0.0f))) { BaseClass::ReactToDestruction (damage_mode,damage_type); return; } switch(damage_mode) { case InternalDamageObject::DestructionDamageMode: { // MSL 5.04 Tank Scorable if (m_scoreThisTank) { Check_Object (Mission::GetInstance()); Mission::GetInstance()->ScoringReactToBuildingDeath (m_whoShotMeLast, GetReplicatorID()); } if(m_AI) { Check_Object(m_AI); m_AI->Die(); } else { MW4AI::g_Rect4DHash->RemovePermRect (this); } if((!IsDestroyed()) && (!m_SecondaryExplosion.GetCurrent ())) { m_DeathDamageMode = damage_mode; m_DeathDamageType = damage_type; const GameModel *model = GetGameModel(); SetDying (); Check_Object(model); Effect *eff; eff = CreateEffect(model->secondaryDestroyedEffectResource, this,true); if (eff != NULL) { Check_Object (eff); m_SecondaryExplosion.Add (eff); executionState->RequestState(ExecutionStateEngine::DyingState); } } } } }