Files
firestorm/Gameleap/code/mw4/Code/MW4/MissileWeapon.cpp
T
Cyd 2b8ca921cb Initial full mirror of c:\VWE (source + assets + toolchain + outputs) via Git LFS
Complete disaster-recovery snapshot: engine/game source, game data assets,
VC6 toolchain + DX SDKs, build outputs, deployed game, and _UNUSED archive.
Large binaries in Git LFS; text preserved byte-for-byte (core.autocrlf=false,
no eol attributes). See RECOVERY.md for the one-clone rebuild procedure.
2026-06-24 21:28:16 -05:00

680 lines
18 KiB
C++

//===========================================================================//
// File: MissileWeapon.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 "MissileWeapon.hpp"
#include "Missile.hpp"
#include "MWObject.hpp"
#include "MWMission.hpp"
#include "SubsystemClassData.hpp"
#include <adept\damageobject.hpp>
#include <Adept\Map.hpp>
#include <Adept\Site.hpp>
#include <MLR\MLRTexturePool.hpp>
// ngLog addition
#include "mech.hpp"
#if !defined(NO_LOG)
#include "nglog_mw4.hpp"
#endif // !defined(NO_LOG)
#include "MWApplication.hpp"
#include "sensor.hpp"
//#############################################################################
//############################### MissileWeapon #########################
//#############################################################################
MissileWeapon::ClassData*
MissileWeapon::DefaultData = NULL;
void MechWarrior4::MissileSecurityCheckStart()
{
_asm
{
nop
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MissileWeapon::InitializeClass()
{
Verify(!DefaultData);
DefaultData =
new ClassData(
MissileWeaponClassID,
"MechWarrior4::MissileWeapon",
ProjectileWeapon::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(
MissileWeapon__GameModel,
BurnTime,
burnTime,
Scalar
);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MissileWeapon::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MissileWeapon*
MissileWeapon::Make(
CreateMessage *message,
ReplicatorID *base_id
)
{
Check_Object(message);
gos_PushCurrentHeap(Heap);
MissileWeapon *new_entity =
new MissileWeapon(DefaultData, message, base_id, NULL);
gos_PopCurrentHeap();
Check_Object(new_entity);
return new_entity;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MissileWeapon::MissileWeapon(
ClassData *class_data,
CreateMessage *message,
ReplicatorID *base_id,
ElementRenderer::Element *element
):
ProjectileWeapon(class_data, message, base_id, element)
{
Check_Pointer(this);
Check_Object(message);
CommonCreation(message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MissileWeapon::CommonCreation(CreateMessage *message)
{
Check_Object(this);
Check_Object(message);
numberEffectedByAMS = 0;
rollAMSNumber = true;
m_lockedTargetEntityPartOffset = Stuff::Point3D::Identity;
// in Mercs, Arrow missiles can't be shot down by AMS
unaffectedByAMS = false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MissileWeapon::Respawn(Entity::CreateMessage *message)
{
Check_Object(this);
Check_Object(message);
ProjectileWeapon::Respawn(message);
CreateMessage *missile_message = Cast_Pointer(CreateMessage *, message);
CommonCreation(missile_message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
MissileWeapon::~MissileWeapon()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MissileWeapon::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;
Normal3D target_normal;
Motion3D initial_velocity = Motion3D::Identity;
Motion3D initial_acceleration = Motion3D::Identity;
LinearMatrix4D site_local_to_world;
//
//-------------------------
//Initialize the velocities
//-------------------------
//
Check_Object(sitePointer);
site_local_to_world = sitePointer->GetLocalToWorld();
initial_velocity.linearMotion.Multiply(
model->initialLinearVelocity,
site_local_to_world
);
initial_acceleration.linearMotion = model->initialLinearAcceleration;
ReplicatorID target_id;
target_id = ReplicatorID::Null;
Point3D target_point;
Entity *ray_source;
ray_source = targetQueryEntity.GetCurrent();
if(ray_source)
{
Check_Object(ray_source);
target_id = ray_source->GetReplicatorID();
// Point3D trans;
// targetQuery.m_line->FindEnd(&trans);
// target_point.MultiplyByInverse(
// trans,
// ray_source->GetLocalToWorld()
// );
target_point = m_lockedTargetEntityPartOffset;
}
else
{
targetQuery.m_line->FindEnd(&target_point);
}
//
//--------------------------------
//Create the Missile CreateMessage
//--------------------------------
//
int flags = WeaponMover::DefaultFlags;
if(numberEffectedByAMS > 0)
{
numberEffectedByAMS --;
flags |= Missile::DoesAMSDestroyFlag;
}
Missile::CreateMessage projectile_create_message(
sizeof(Missile::CreateMessage),
DefaultEventPriority,
CreateMessage::DefaultFlags,
class_ID,
flags,
model->projectileModelResource,
sitePointer->GetLocalToWorld(),
0.0f,
Missile::ExecutionStateEngine::SRMMotionState,
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,
target_id,
target_point,
model->burnTime
);
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)
{
vehicleInterface->PlayForce (VehicleInterface::FireMissleForce);
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MissileWeapon::PreCollisionExecute(Time till)
{
Check_Object(this);
PRECOLLISION_LOGIC("Subsystem::MissileWeapon");
const GameModel* model = GetGameModel();
Check_Object(model);
Check_Object(executionState);
int pre_state = executionState->GetState();
switch (pre_state)
{
case ExecutionStateEngine::ReloadingState:
{
if (model->reloadTime > 0.49f)
{
if ((!m_PlayedRechargeSound) && (GetTimeParameter(till) >= model->reloadTime-0.5f))
{
if (GetParentVehicle ())
if (GetParentVehicle()->vehicleInterface)
GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::RECHARGE_MISSILE);
m_PlayedRechargeSound = true;
}
}
}
}
ProjectileWeapon::PreCollisionExecute(till);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void MissileWeapon::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 Advance 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,
MissileDamageType,
Stuff::UnitVector3D(1,0,0),
Point3D (-1,-1,-1),
model->heatToDeal,
true,
model->itemID
);
target->Receive(&damage_message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MissileWeapon::Fire(CollisionQuery *query, Time time_locked, const Stuff::Point3D& lockedTargetEntityPartOffset)
{
Check_Object(this);
Check_Object(executionState);
const GameModel *model = GetGameModel();
Check_Object(model);
targetQuery.m_collisionMask = query->m_collisionMask;
targetQuery.m_material = query->m_material;
targetQuery.m_raySource = query->m_raySource;
m_lockedTargetEntityPartOffset = lockedTargetEntityPartOffset;
collisionLine = *query->m_line;
collisionNormal = *query->m_normal;
//
//--------------------------------------------------
//Check to see if we are locked on the entity or not
//--------------------------------------------------
//
// MSL 5.04 Corrects tracking of Missile from root of Building Object to target point
if(targetQuery.m_raySource)
{
if (targetQuery.m_raySource->IsDerivedFrom(Building::DefaultData) == true)
{
targetQuery.m_raySource = NULL;
}
}
//
// If this model doesn't have a lock time, it won't even lock for Narc's
//
if(Small_Enough(model->targetLockTime))
{
targetQuery.m_raySource = NULL;
}
else
{
// if we couldn't have locked legitimately yet, start with no lock
//
// .001 is a special value indicating immediate lock time. (SSRM's)
//
if (model->targetLockTime > time_locked && model->targetLockTime >= 0.0011f)
targetQuery.m_raySource = NULL;
// MSL 5.04 no tracking beyond maximum missile range
if (targetQuery.m_line->m_length > model->maxDistance)
targetQuery.m_raySource = NULL;
// MSL 5.04 no tracking within minimum missile range
// if (targetQuery.m_line->m_length < model->minDistance)
// targetQuery.m_raySource = NULL;
// MSL 5.04 no tracking if affected by ECM
if(targetQuery.m_raySource)
{
// MSL 5.05 Advance Mode
MWApplication *m_App;
m_App = MWApplication::GetInstance ();
m_AdvanceMode = m_App->GetLocalNetParams()->m_advancemodeOn;
if (m_AdvanceMode)
{
if (targetQuery.m_raySource->IsDerivedFrom(MWMover::DefaultData) == true)
{
MWMover* mover = Cast_Object(MWMover*,targetQuery.m_raySource);
MWObject *parent;
parent = mover->GetParentVehicle ();
if (parent)
{
MWObject *mobj = GetParentVehicle();
if(mobj && mobj->IsDerivedFrom(Mech::DefaultData))
{
Mech *mech = Cast_Object(Mech *, mobj);
if (mech->GetSensor()->GetSensorMode() == Sensor::PassiveMode)
{
targetQuery.m_raySource = NULL;
}
}
if (parent->GetSensor()->GetSensorMode() == Sensor::PassiveMode)
{
MWObject *mobj = GetParentVehicle();
if(mobj && mobj->IsDerivedFrom(Mech::DefaultData))
{
Mech *mech = Cast_Object(Mech *, mobj);
if (mech->DoesHaveBeagle())
{
if (targetQuery.m_line->m_length > 600.0f)
targetQuery.m_raySource = NULL;
}
else
{
if (targetQuery.m_line->m_length > 500.0f)
targetQuery.m_raySource = NULL;
}
}
}
if(parent->DoesHaveECM())
{
MWObject *mobj = GetParentVehicle();
if(mobj && mobj->IsDerivedFrom(Mech::DefaultData))
{
Mech *mech = Cast_Object(Mech *, mobj);
if (mech->DoesHaveBeagle())
{
if (targetQuery.m_line->m_length > 700.0f)
targetQuery.m_raySource = NULL;
}
else
{
if (targetQuery.m_line->m_length > 500.0f)
targetQuery.m_raySource = NULL;
}
}
}
}
}
}
}
bool does_have_lock = false;
if(targetQuery.m_raySource)
{
if(targetQuery.m_raySource == Map::GetInstance())
{
targetQuery.m_raySource = NULL;
}
else
{
does_have_lock = true;
if (targetQuery.m_raySource->IsDerivedFrom(MWMover::DefaultData) == true)
{
MWMover* mover = Cast_Object(MWMover*,targetQuery.m_raySource);
mover->NotifyProjectileApproaching(targetQuery.m_line->m_origin);
MWObject *parent;
parent = mover->GetParentVehicle ();
if (parent)
{
if(parent->GetAMS() && !unaffectedByAMS)
{
//
//-------------------------------------------------
//pre-determine how many missiles AMS will take out
//-------------------------------------------------
//
if ((rollAMSNumber) && (executionState->GetState() != ExecutionStateEngine::FiringState))
{
// MSL 5.02 AMS
// numberEffectedByAMS = 1;
const GameModel *model = GetGameModel();
Check_Object(model);
int upperLimit = model->numFire+1;
if (model->numFire > 1)
{
Max_Clamp(upperLimit, model->numFire); // stop 0 to 8 missile groups
}
else
{
Max_Clamp(upperLimit, model->numFire+1); // stop 0 to 8 missile groups
}
// Max_Clamp(upperLimit, model->numFire+1); // stop 0 to 8 missile groups
numberEffectedByAMS = Stuff::Random::GetLessThan(upperLimit);
}
}
}
}
}
}
//
//-------------------------------------------------------------
//If we don't have a lock then we need to check the narc status
//-------------------------------------------------------------
//
if(!does_have_lock)
{
if(Mission::GetInstance()->IsDerivedFrom(MWMission::DefaultData))
{
MWMission *temp_mission =
Cast_Object(MWMission *, Mission::GetInstance());
Entity *entity = (Entity *)temp_mission->GetNarc(GetParentVehicle()->GetAlignment());
if(entity)
{
targetQuery.m_raySource = entity;
m_lockedTargetEntityPartOffset = Point3D::Identity;
}
}
}
}
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)
Mech_CheckDamage(targetQuery.m_raySource, GetParentVehicle());
executionState->RequestState(ExecutionStateEngine::FiringState, &targetQuery);
}
else
{
#if 0
if (GetParentVehicle ()->vehicleInterface)
{
GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::OUT_OF_AMMO);
}
#endif
}
}
// MSL 5.03 Heat Jam
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;
}
}
}
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool MissileWeapon::IsAMSValid(Entity *target)
{
if (target == NULL)
return false;
if(target == Map::GetInstance())
return false;
// MSL 5.02 bug fix
// traverse parents looking for an MWObject
// (bug fix # 4718)
Entity *testEntity = target;
while (testEntity!=NULL)
{
// stop at the MWObject
if (testEntity->IsDerivedFrom(MWObject::DefaultData))
{
break;
}
testEntity = testEntity->GetParentEntity();
}
if (testEntity && testEntity->IsDerivedFrom(MWObject::DefaultData))
{
MWObject* mw_object = Cast_Object(MWObject *, testEntity);
if(mw_object->GetAMS())
{
return true;
}
}
return false;
}
void MechWarrior4::MissileSecurityCheckStop()
{
_asm
{
nop
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
MissileWeapon::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}