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.
1119 lines
30 KiB
C++
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));
|
|
}
|
|
|