Files
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

1119 lines
30 KiB
C++

//===========================================================================//
// File: ProjectileWeapon.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 "ProjectileWeapon.hpp"
#include "GUIWeaponManager.hpp"
#include "Vehicle.hpp"
#include "WeaponMover.hpp"
#include "SubsystemClassData.hpp"
#include "HeatManager.hpp"
#include <Adept\Map.hpp>
#include <Adept\Site.hpp>
#include <Adept\EntityManager.hpp>
#include <adept\damageobject.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"
//#############################################################################
//########################## ExecutionStateEngine #######################
//#############################################################################
ProjectileWeapon::ExecutionStateEngine::ClassData*
ProjectileWeapon::ExecutionStateEngine::DefaultData = NULL;
const StateEngine::StateEntry
ProjectileWeapon::ExecutionStateEngine::StateEntries[]=
{
STATE_ENTRY(ProjectileWeapon__ExecutionStateEngine, Firing),
STATE_ENTRY(ProjectileWeapon__ExecutionStateEngine, Reloading),
STATE_ENTRY(ProjectileWeapon__ExecutionStateEngine, Jammed),
STATE_ENTRY(ProjectileWeapon__ExecutionStateEngine, HeatJammed),
STATE_ENTRY(ProjectileWeapon__ExecutionStateEngine, AmmoFire),
STATE_ENTRY(ProjectileWeapon__ExecutionStateEngine, Recharging)
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ProjectileWeapon::ExecutionStateEngine::InitializeClass()
{
Check_Object(StateEngine::DefaultData);
Verify(!DefaultData);
DefaultData =
new ClassData(
ProjectileWeapon__ExecutionStateEngineClassID,
"ProjectileWeapon::ExecutionStateEngine",
Weapon::ExecutionStateEngine::DefaultData,
ELEMENTS(StateEntries), StateEntries,
(Entity::ExecutionStateEngine::Factory)Make,
(Entity::ExecutionStateEngine::FactoryRequest::Factory)
&FactoryRequest::ConstructFactoryRequest
);
Register_Object(DefaultData);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ProjectileWeapon::ExecutionStateEngine::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ProjectileWeapon::ExecutionStateEngine*
ProjectileWeapon::ExecutionStateEngine::Make(
ProjectileWeapon *weapon,
FactoryRequest *request
)
{
Check_Object(weapon);
Check_Object(request);
gos_PushCurrentHeap(Heap);
ProjectileWeapon::ExecutionStateEngine *engine =
new ProjectileWeapon::ExecutionStateEngine(DefaultData, weapon, request);
gos_PopCurrentHeap();
return engine;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
ProjectileWeapon::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((currentState == AmmoFireState) && (new_state == FiringState || new_state == RechargingState || new_state == ReloadingState || new_state == JammedState || new_state == HeatJammedState))
{
return currentState;
}
if((currentState == ReloadingState) && new_state == FiringState)
{
return currentState;
}
// MSL 5.03 Weapon Jammed
if((currentState == JammedState || currentState == HeatJammedState) && (new_state == FiringState || new_state == RechargingState || new_state == ReloadingState))
{
return currentState;
}
if(currentState == NeverExecuteState && new_state == FiringState)
{
// Set the initial time of fire
ProjectileWeapon* owner = Cast_Object( ProjectileWeapon*, owningEntity );
owner->m_fireStartTime = owner->m_fireEndTime = gos_GetElapsedTime();
}
// MSL 5.03 Ammo Bay Fire
if(new_state == AmmoFireState)
{
ProjectileWeapon* owner = Cast_Object( ProjectileWeapon*, owningEntity );
Check_Object(owner);
owner->StartFireState();
}
// MSL 5.03 Weapon Jammed
if(new_state == JammedState)
{
ProjectileWeapon* owner = Cast_Object( ProjectileWeapon*, owningEntity );
Check_Object(owner);
owner->StartJamState();
}
// MSL 5.03 Weapon Heat Jammed
if(new_state == HeatJammedState)
{
ProjectileWeapon* owner = Cast_Object( ProjectileWeapon*, owningEntity );
Check_Object(owner);
owner->StartHeatJamState();
}
// MSL 5.03 Weapon Jammed
if(new_state == RechargingState)
{
ProjectileWeapon* owner = Cast_Object( ProjectileWeapon*, owningEntity );
Check_Object(owner);
owner->m_enterRecharge = gos_GetElapsedTime();
}
//
//
//---------------------
// Deal with our states
//---------------------
//
ProjectileWeapon *weapon;
weapon = Cast_Object(ProjectileWeapon *, owningEntity);
switch (BaseClass::RequestState(new_state, data))
{
case FiringState:
{
weapon->targetQueryEntity.Remove();
if(weapon->targetQuery.m_raySource)
weapon->targetQueryEntity.Add(weapon->targetQuery.m_raySource);
weapon->UsePostCollision();
Check_Object(EntityManager::GetInstance());
EntityManager::GetInstance()->RequestPostCollisionExecution(weapon);
weapon->lastExecuted = gos_GetElapsedTime();
weapon->fireRequest = true;
if(weapon->guiWeapon.GetCurrent())
weapon->guiWeapon.GetCurrent()->Reload();
weapon->ApplyHeat();
break;
}
case ReloadingState:
{
weapon->IgnorePostCollision();
weapon->lastParameterization = gos_GetElapsedTime();
break;
}
// case AmmoFireState:
// {
// weapon->UsePostCollision();
// Check_Object(EntityManager::GetInstance());
// EntityManager::GetInstance()->RequestPostCollisionExecution(weapon);
// break;
// }
};
return currentState;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ProjectileWeapon::ExecutionStateEngine::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}
//#############################################################################
//############################### Weapon ##############################
//#############################################################################
ProjectileWeapon::ClassData*
ProjectileWeapon::DefaultData = NULL;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ProjectileWeapon::InitializeClass()
{
Verify(!DefaultData);
DefaultData =
new ClassData(
ProjectileWeaponClassID,
"MechWarrior4::ProjectileWeapon",
Weapon::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(
ProjectileWeapon,
ExecutionState,
executionState,
ProjectileWeapon__ExecutionStateEngine
);
CUSTOM_DIRECT_ATTRIBUTE(
ProjectileWeapon,
ProjectileStartSFX,
projectileStartSFX,
int,
IntClassID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
ProjectileWeapon__GameModel,
FireRate,
fireRate,
Scalar
);
DIRECT_GAME_MODEL_ATTRIBUTE(
ProjectileWeapon__GameModel,
InitialLinearVelocity,
initialLinearVelocity,
Vector3D
);
DIRECT_GAME_MODEL_ATTRIBUTE(
ProjectileWeapon__GameModel,
InitialLinearAcceleration,
initialLinearAcceleration,
Vector3D
);
DIRECT_GAME_MODEL_ATTRIBUTE(
ProjectileWeapon__GameModel,
ProjectileModelResource,
projectileModelResource,
ResourceID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
ProjectileWeapon__GameModel,
TargetLockTime,
targetLockTime,
Scalar
);
DIRECT_GAME_MODEL_ATTRIBUTE(
ProjectileWeapon__GameModel,
TimeToPotentialJam,
m_timeToPotentialJam,
Scalar
);
DIRECT_GAME_MODEL_ATTRIBUTE(
ProjectileWeapon__GameModel,
TimeToJam,
m_timeToJam,
Scalar
);
DIRECT_GAME_MODEL_ATTRIBUTE(
ProjectileWeapon__GameModel,
TimeToUnjam,
m_timeToUnjam,
Scalar
);
// MSL 5.03 Weapon Jammed
DIRECT_GAME_MODEL_ATTRIBUTE(
ProjectileWeapon__GameModel,
HeatToJam,
m_heatToJam,
Scalar
);
DIRECT_GAME_MODEL_ATTRIBUTE(
ProjectileWeapon__GameModel,
HeatToUnjam,
m_heatToUnjam,
Scalar
);
// MSL 5.03 Ammo Bay Fire
DIRECT_GAME_MODEL_ATTRIBUTE(
ProjectileWeapon__GameModel,
TimeToAmmoBay,
m_timeToAmmoBay,
Scalar
);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ProjectileWeapon::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ProjectileWeapon*
ProjectileWeapon::Make(
CreateMessage *message,
ReplicatorID *base_id
)
{
Check_Object(message);
gos_PushCurrentHeap(Heap);
ProjectileWeapon *new_entity =
new ProjectileWeapon(DefaultData, message, base_id, NULL);
gos_PopCurrentHeap();
Check_Object(new_entity);
return new_entity;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ProjectileWeapon::ProjectileWeapon(
ClassData *class_data,
CreateMessage *message,
ReplicatorID *base_id,
ElementRenderer::Element *element
):
Weapon(class_data, message, base_id, element),
targetQuery(NULL, NULL, CanBeShotFlag, NULL),
targetQueryEntity(NULL),
m_jamStartTime( 0 ),
m_fireStartTime( 0 ),
m_fireEndTime( 0 ),
m_ammofireStartTime( 0 )
{
Check_Pointer(this);
Check_Object(message);
m_jamThisFrame = false;
m_heatJamThisFrame = false;
targetQuery.m_line = &collisionLine;
targetQuery.m_normal = &collisionNormal;
CommonCreation(message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ProjectileWeapon::Respawn(Entity::CreateMessage *message)
{
Check_Object(this);
Check_Object(message);
Weapon::Respawn(message);
CreateMessage *projectile_message = Cast_Pointer(CreateMessage *, message);
CommonCreation(projectile_message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ProjectileWeapon::CommonCreation(CreateMessage *message)
{
Check_Object(this);
Check_Object(message);
projectileStartSFX = 0;
fireRequest = false;
projectilesFired = 0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ProjectileWeapon::~ProjectileWeapon()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ProjectileWeapon::PreCollisionExecute(Time till)
{
Check_Object(this);
PRECOLLISION_LOGIC("Subsystem::Projectile");
const GameModel* model = GetGameModel();
Check_Object(model);
Check_Object(executionState);
Stuff::Scalar timeSlice = GetTimeSlice( till );
int pre_state = executionState->GetState();
switch (pre_state)
{
case ExecutionStateEngine::FiringState:
{
UsePostCollision();
m_PlayedRechargeSound = false;
break;
}
case ExecutionStateEngine::ReloadingState:
{
Scalar count = model->reloadTime - GetTimeParameter(till);
Min_Clamp(count, 0.0f);
SetCount(count);
if (model->reloadTime > 0.49f)
{
if ((!m_PlayedRechargeSound) && (GetTimeParameter(till) >= model->reloadTime-0.5f))
{
if (GetParentVehicle ())
if (GetParentVehicle()->vehicleInterface)
{
if ((model->itemID == W_ClanERPPC) || (model->itemID == W_PPC) || (model->itemID == W_ERPPC))
GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::RECHARGE_PPC);
else if (!((model->itemID == W_MachineGun) || (model->itemID == W_ClanMachineGun)))
GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::RECHARGE_PROJECTILE);
}
m_PlayedRechargeSound = true;
}
}
// MSL 5.03 Weapon Jammed
if(GetTimeParameter(till) > model->reloadTime)
{
// if(model->m_timeToJam > 0)
// {
// executionState->RequestState(ExecutionStateEngine::RechargingState);
// if(guiWeapon.GetCurrent())
// guiWeapon.GetCurrent()->Ready();
// m_PlayedRechargeSound = false;
// }
// else
// {
executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
if(guiWeapon.GetCurrent())
guiWeapon.GetCurrent()->Ready();
m_PlayedRechargeSound = false;
// }
}
IgnorePostCollision();
break;
}
// MSL 5.03 Weapon Jammed
case ExecutionStateEngine::RechargingState:
{
// In order to make the server and client agree on cumulative time more, I'll put a buffer in.
// but if we don't get a new fire request with .25 seconds of entering this state we'll assume recharge.
const GameModel* model = GetGameModel();
Check_Object(model);
Check_Object(executionState);
if(model->m_timeToJam > 0)
{
if ( (gos_GetElapsedTime() - m_enterRecharge ) >= 0.25f)
{
m_fireStartTime += timeSlice*2;
m_fireEndTime += timeSlice;
double cumulativeFiringTime = m_fireEndTime - m_fireStartTime;
if ( cumulativeFiringTime <= 0 )
{
executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
// if(guiWeapon.GetCurrent())
// guiWeapon.GetCurrent()->Ready();
}
}
}
break;
}
// MSL 5.03 Weapon Jammed
case ExecutionStateEngine::JammedState:
{
const GameModel* model = GetGameModel();
Check_Object(model);
Check_Object(executionState);
if(model->m_timeToJam > 0)
{
if( gos_GetElapsedTime() - m_jamStartTime > model->m_timeToUnjam )
{
executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
if(guiWeapon.GetCurrent())
guiWeapon.GetCurrent()->Ready();
m_jamThisFrame = false;
}
else
{
// Update the gui time
if(guiWeapon.GetCurrent())
{
guiWeapon.GetCurrent()->SetJamCount( (Stuff::Scalar)(gos_GetElapsedTime() - m_jamStartTime) );
}
}
IgnorePostCollision();
}
break;
}
// MSL 5.03 Weapon Jammed
case ExecutionStateEngine::HeatJammedState:
{
const GameModel* model = GetGameModel();
Check_Object(model);
Check_Object(executionState);
if(model->m_heatToUnjam > 0)
{
MWObject *mobj = GetParentVehicle();
if(mobj && mobj->IsDerivedFrom(Mech::DefaultData))
{
Mech *mech = Cast_Object(Mech *, mobj);
Check_Object(mech);
if ((mech->m_heatManager->GetHeatPercentage() * 100.0f) <= model->m_heatToUnjam)
{
executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
if(guiWeapon.GetCurrent())
guiWeapon.GetCurrent()->Ready();
m_heatJamThisFrame = false;
}
IgnorePostCollision();
}
}
break;
}
// MSL 5.04 Ammo Bay Fire
case ExecutionStateEngine::AmmoFireState:
{
const GameModel* model = GetGameModel();
Check_Object(model);
Check_Object(executionState);
if(model->m_timeToAmmoBay > 0)
{
MWObject *mobj = GetParentVehicle();
if(mobj && mobj->IsDerivedFrom(Mech::DefaultData))
{
if (DoesHaveAmmo())
{
if( gos_GetElapsedTime() - m_ammofireStartTime > model->m_timeToAmmoBay )
{
Check_Object(this);
int ammo_count = GetAmmoCount();
float damage_count = ammo_count * model->damageAmount * model->numFire;
// MSL 5.04 Ammo Bay Fire
SetAmmoCount(0);
executionState->RequestState(ExecutionStateEngine::DestroyedState);
}
else
{
// Update the gui time
if(guiWeapon.GetCurrent())
{
guiWeapon.GetCurrent()->SetFireCount( (Stuff::Scalar)(gos_GetElapsedTime() - m_ammofireStartTime) );
}
}
}
}
else
{
executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
// if(guiWeapon.GetCurrent())
// guiWeapon.GetCurrent()->Ready();
}
UsePostCollision();
}
break;
}
}
Weapon::PreCollisionExecute(till);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ProjectileWeapon::PostCollisionExecute(Time till)
{
Check_Object(this);
POSTCOLLISION_LOGIC("Subsystem::Projectile");
const GameModel *model = GetGameModel();
Check_Object(model);
Check_Object(executionState);
// MSL 5.03 Weapon Jammed
bool jammed = false;
MWApplication *m_App;
m_App = MWApplication::GetInstance ();
if (m_App->GetLocalNetParams()->m_weaponjamOn)
{
if ( executionState->GetState() == ExecutionStateEngine::FiringState )
{
if (m_jamThisFrame)
{
executionState->RequestState(ExecutionStateEngine::JammedState);
jammed = true;
}
if (m_heatJamThisFrame)
{
executionState->RequestState(ExecutionStateEngine::HeatJammedState);
jammed = true;
}
}
}
m_heatJamThisFrame = false;
m_jamThisFrame = false;
if (jammed)
{
if (GetParentVehicle ())
{
if (GetParentVehicle()->vehicleInterface)
{
GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::ZOOM_FAIL);
}
}
return;
}
if(fireRequest)
{
if(!DoesHaveAmmo())
{
executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
if(guiWeapon.GetCurrent())
guiWeapon.GetCurrent()->Ready();
IgnorePostCollision();
}
else
{
if((GetTimeParameter(till) > model->fireRate) ||
(projectilesFired == 0))
{
lastParameterization = gos_GetElapsedTime();
CreateProjectile(till);
CreateMuzzleFlash();
CreateEjectEffect();
CreateLightAmpFlareOut();
projectilesFired ++;
projectileStartSFX = 1;
if(projectilesFired >= model->numFire)
{
fireRequest = false;
projectilesFired = 0;
Check_Object(executionState);
executionState->RequestState(ExecutionStateEngine::ReloadingState);
UseAmmo();
}
}
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ProjectileWeapon::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;
ReplicatorID target_id = ReplicatorID::Null;
Normal3D target_normal;
Motion3D initial_velocity = Motion3D::Identity;
Motion3D initial_acceleration = Motion3D::Identity;
Point3D site_local_to_world;
//
//-------------------------
//Initialize the velocities
//-------------------------
//
Check_Object(sitePointer);
site_local_to_world = sitePointer->GetLocalToWorld();
Vector3D projectile_direction;
Point3D target_point;
targetQuery.m_line->FindEnd(&target_point);
projectile_direction.Subtract(
target_point,
site_local_to_world
);
Check_Object(sitePointer);
site_local_to_world = sitePointer->GetLocalToWorld();
LinearMatrix4D direction_matrix = LinearMatrix4D::Identity;
direction_matrix.BuildTranslation((Stuff::Point3D)site_local_to_world);
direction_matrix.AlignLocalAxisToWorldVector(projectile_direction, Z_Axis, Y_Axis, X_Axis);
Vector3D initial_scale = model->initialLinearVelocity;
if(GetParentAsVehicle())
{
initial_scale.z += GetParentAsVehicle()->currentSpeedMPS;
}
initial_velocity.linearMotion.Multiply(
initial_scale,
direction_matrix
);
initial_acceleration.linearMotion = model->initialLinearAcceleration;
//
//------------------------------------
//Create the WeaponMover CreateMessage
//------------------------------------
//
WeaponMover::CreateMessage projectile_create_message(
sizeof(WeaponMover::CreateMessage),
DefaultEventPriority,
CreateMessage::DefaultFlags,
class_ID,
WeaponMover::DefaultFlags,
model->projectileModelResource,
direction_matrix,
0.0f,
WeaponMover::ExecutionStateEngine::StraightLineMotionState,
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
);
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)
{
if (!((model->itemID == W_MachineGun) || (model->itemID == W_ClanMachineGun)))
vehicleInterface->PlayForce (VehicleInterface::FireProjectileForce);
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ProjectileWeapon::Fire(Adept::Entity::CollisionQuery *query, Time time_locked, const Stuff::Point3D& lockedTargetEntityPartOffset)
{
Check_Object(this);
Check_Object(executionState);
//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;
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)
}
executionState->RequestState(ExecutionStateEngine::FiringState);
}
else
{
#if 0
if (GetParentVehicle ()->vehicleInterface)
{
GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::OUT_OF_AMMO);
}
#endif
}
// MSL 5.03 Weapon Jammed
const GameModel* model = GetGameModel();
Check_Object(model);
Check_Object(executionState);
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;
}
}
}
}
}
// MSL 5.03 Weapon Jammed
if(model->m_timeToJam > 0)
{
if (executionState->GetState() == ExecutionStateEngine::FiringState)
{
Check_Object(GetParentVehicle());
bool fServer = Network::GetInstance()->AmIServer();
bool fLocalPlayer = (GetReplicatorID().connectionID == Connection::Local->GetID());
if (fServer || fLocalPlayer)
{
// if we fired, check for jam
m_fireEndTime = gos_GetElapsedTime();
double cumulativeFiringTime = m_fireEndTime - m_fireStartTime;
//
// For servers, the rules are going to be different. If the fire time is at the absolute max, plus a buffer for lag
// then we will gaurantee a jam on the server, and have it refresh.
//
// This allows for some tolerance of various net traffic since we'd still prefer that the
// client handle this, but puts a cap on what we tolerate from a potential cheater.
if ( fLocalPlayer)
{
if ( cumulativeFiringTime >= model->m_timeToPotentialJam)
{
Stuff::Scalar idPermutation = (replicatorID.localID % 3)/3.0f; // have weapon's ID permutate it's jam time
Stuff::Scalar r = (Stuff::Scalar)fmod( gos_GetElapsedTime()+idPermutation, 1.0 );
Stuff::Scalar e = (Stuff::Scalar)(
( cumulativeFiringTime - model->m_timeToPotentialJam )
/ model->m_timeToJam );
// Potential to jam:
if ( r < e )
{
m_jamThisFrame = true;
}
}
}
else if (fServer)
{
if (cumulativeFiringTime >= (model->m_timeToJam + model->m_timeToPotentialJam + 2.0f))
{
m_jamThisFrame = true;
}
}
}
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ProjectileWeapon::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 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,
ProjectileDamageType,
Stuff::UnitVector3D(1,0,0),
Point3D (-1,-1,-1),
model->heatToDeal,
true,
model->itemID
);
target->Receive(&damage_message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
ProjectileWeapon::ReadyToFire()
{
Check_Object(this);
Check_Object(executionState);
// if this is the local mech, always true in single player.
if (GetReplicatorID().connectionID == Connection::Local->GetID())
{
if (CanFireFromCurrentArm() == false)
{
return (false);
}
bool result;
result = executionState->GetState()==ExecutionStateEngine::NeverExecuteState &&
DoesHaveAmmo();
return result;
}
if (executionState->GetState()==ExecutionStateEngine::ReloadingState)
{
if (gos_GetElapsedTime() - lastParameterization <= 0.1f)
{
executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
}
return DoesHaveAmmo();
}
bool result;
result = executionState->GetState()==ExecutionStateEngine::NeverExecuteState &&
DoesHaveAmmo();
return result;
}
// MSL 5.03 Ammo Bay Fire
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ProjectileWeapon::StartFireState()
{
const GameModel *model = GetGameModel();
Check_Object(model);
m_ammofireStartTime = gos_GetElapsedTime();
if ( guiWeapon.GetCurrent() )
{
guiWeapon.GetCurrent()->RecoverFromFire();
guiWeapon.GetCurrent()->SetFireCount( model->m_timeToAmmoBay );
}
}
// MSL 5.03 Weapon Jammed
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ProjectileWeapon::StartJamState()
{
const GameModel *model = GetGameModel();
Check_Object(model);
m_jamStartTime = gos_GetElapsedTime();
if ( guiWeapon.GetCurrent() )
{
guiWeapon.GetCurrent()->RecoverFromJam();
guiWeapon.GetCurrent()->SetJamCount( model->m_timeToUnjam );
}
}
// MSL 5.03 Weapon Jammed
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void ProjectileWeapon::StartHeatJamState()
{
// const GameModel *model = GetGameModel();
// Check_Object(model);
// m_jamStartTime = gos_GetElapsedTime();
if ( guiWeapon.GetCurrent() )
{
guiWeapon.GetCurrent()->RecoverFromJam();
// guiWeapon.GetCurrent()->SetJamCount( model->m_timeToUnjam );
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ProjectileWeapon::DestroySubsystem()
{
Check_Object(this);
Weapon::DestroySubsystem();
EnterNeverExecuteState();
IgnorePostCollision();
CleanUpProjectiles();
ClearAmmo();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ProjectileWeapon::CleanUpProjectiles()
{
Check_Object(this);
fireRequest = false;
projectilesFired = 0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
ProjectileWeapon::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}