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

648 lines
17 KiB
C++

//===========================================================================//
// File: BeamWeapon.cpp
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// 09/09/98 JSE Inital base class based off of Shadowrun/Adept //
// 09/24/98 BDB Inital Weapon subsytem class based off of Subsystem //
// 10/19/98 BDB Created LauchedWeapon subsytem based off of Weapon //
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Fasa Interactive //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#include "MW4Headers.hpp"
#include "BeamWeapon.hpp"
#include "MWMover.hpp"
#include "BeamEntity.hpp"
#include "GUIWeaponManager.hpp"
#include "SubsystemClassData.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 #######################
//#############################################################################
BeamWeapon::ExecutionStateEngine::ClassData*
BeamWeapon::ExecutionStateEngine::DefaultData = NULL;
const StateEngine::StateEntry
BeamWeapon::ExecutionStateEngine::StateEntries[]=
{
STATE_ENTRY(BeamWeapon__ExecutionStateEngine, Firing),
STATE_ENTRY(BeamWeapon__ExecutionStateEngine, Recharging)
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamWeapon::ExecutionStateEngine::InitializeClass()
{
Check_Object(StateEngine::DefaultData);
Verify(!DefaultData);
DefaultData =
new ClassData(
BeamWeapon__ExecutionStateEngineClassID,
"BeamWeapon::ExecutionStateEngine",
BaseClass::DefaultData,
ELEMENTS(StateEntries), StateEntries,
(Entity::ExecutionStateEngine::Factory)Make,
(Entity::ExecutionStateEngine::FactoryRequest::Factory)
&FactoryRequest::ConstructFactoryRequest
);
Register_Object(DefaultData);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamWeapon::ExecutionStateEngine::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BeamWeapon::ExecutionStateEngine*
BeamWeapon::ExecutionStateEngine::Make(
BeamWeapon *weapon,
FactoryRequest *request
)
{
Check_Object(weapon);
Check_Object(request);
gos_PushCurrentHeap(Heap);
BeamWeapon::ExecutionStateEngine *engine =
new BeamWeapon::ExecutionStateEngine(DefaultData, weapon, request);
gos_PopCurrentHeap();
return engine;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
BeamWeapon::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(new_state == FiringState && currentState == RechargingState)
return currentState;
//
//---------------------
// Deal with our states
//---------------------
//
BeamWeapon *beam;
beam = Cast_Object(BeamWeapon *, owningEntity);
switch (BaseClass::RequestState(new_state, data))
{
case FiringState:
{
// SPEW(("jackyc", "FIRING WEAPON - Weapon %d", beam->weaponID));
//We need to set up new target info here!
beam->UsePostCollision();
Check_Object(EntityManager::GetInstance());
EntityManager::GetInstance()->RequestPostCollisionExecution(beam);
// Start the sound
beam->beamStartSFX = 1;
beam->lastParameterization = gos_GetElapsedTime();
if(beam->guiWeapon.GetCurrent())
beam->guiWeapon.GetCurrent()->Reload();
beam->ApplyHeat();
}
break;
case NeverExecuteState:
case DestroyedState:
break;
default:
{
owningEntity->IgnorePostCollision();
break;
}
}
return currentState;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamWeapon::ExecutionStateEngine::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}
//#############################################################################
//############################### Weapon ################################
//#############################################################################
BeamWeapon::ClassData*
BeamWeapon::DefaultData = NULL;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamWeapon::InitializeClass()
{
Verify(!DefaultData);
DefaultData =
new ClassData(
BeamWeaponClassID,
"MechWarrior4::BeamWeapon",
BaseClass::DefaultData,
0, NULL,
(Entity::Factory)Make,
(Entity::CreateMessage::Factory)CreateMessage::ConstructCreateMessage,
ExecutionStateEngine::DefaultData,
(Entity::GameModel::Factory)GameModel::ConstructGameModel,
NULL,
(Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
(Entity::GameModel::ModelWrite)GameModel::WriteToText,
(Entity::GameModel::ModelSave)GameModel::SaveGameModel,
(Subsystem::StreamCreate) CreateStream
);
Register_Object(DefaultData);
INDIRECT_STATE_ATTRIBUTE(
BeamWeapon,
ExecutionState,
executionState,
BeamWeapon__ExecutionStateEngine
);
CUSTOM_DIRECT_ATTRIBUTE(
BeamWeapon,
BeamDistance,
beamDistance,
Scalar,
ScalarClassID
);
CUSTOM_DIRECT_ATTRIBUTE(
BeamWeapon,
BeamStartSFX,
beamStartSFX,
int,
IntClassID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
BeamWeapon__GameModel,
FireDuration,
fireDuration,
Scalar
);
DIRECT_GAME_MODEL_ATTRIBUTE(
BeamWeapon__GameModel,
BeamModelResource,
beamModelResource,
ResourceID
);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamWeapon::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BeamWeapon*
BeamWeapon::Make(
CreateMessage *message,
ReplicatorID *base_id
)
{
gos_PushCurrentHeap(Heap);
BeamWeapon *new_entity =
new BeamWeapon(DefaultData, message, base_id, NULL);
gos_PopCurrentHeap();
Check_Object(new_entity);
return new_entity;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BeamWeapon::BeamWeapon(
ClassData *class_data,
CreateMessage *message,
ReplicatorID *base_id,
ElementRenderer::Element *element
):
Weapon(class_data, message, base_id, element),
targetQuery(NULL, NULL, CanBeShotFlag, NULL),
beamEntity(NULL)
{
Check_Pointer(this);
Check_Object(message);
targetQuery.m_line = &collisionLine;
targetQuery.m_normal = &collisionNormal;
CommonCreation(message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamWeapon::CommonCreation(CreateMessage *message)
{
Check_Object(this);
Check_Object(message);
fireRequest = false;
accumlatedRechargeTime = 0.0f;
accumlatedDuration = 0.0f;
beamDistance = 0.01f;
beamStartSFX = 0;
beamEntity.Remove();
const GameModel *model = GetGameModel();
beamRechargeTime = model->reloadTime;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamWeapon::Respawn(Entity::CreateMessage *message)
{
Check_Object(this);
Check_Object(message);
BaseClass::Respawn(message);
CreateMessage *beam_message = Cast_Pointer(CreateMessage *, message);
CommonCreation(beam_message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BeamWeapon::~BeamWeapon()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void BeamWeapon::PreCollisionExecute(Time till)
{
const GameModel* model = GetGameModel();
PRECOLLISION_LOGIC("Subsystem::Beam");
Check_Object(executionState);
int pre_state = executionState->GetState();
switch (pre_state)
{
case ExecutionStateEngine::FiringState:
{
UsePostCollision();
if ((GetTimeParameter(till) >= model->fireDuration) &&
(beamEntity.GetCurrent()))
{
// if(beamEntity.GetCurrent())
// {
Check_Object(beamEntity.GetCurrent());
// if(!beamEntity.GetCurrent()->beamStatus)
// {
executionState->RequestState(ExecutionStateEngine::RechargingState);
m_PlayedRechargeSound = false;
lastParameterization = gos_GetElapsedTime();
Check_Object(beamEntity.GetCurrent());
beamEntity.GetCurrent()->SentenceToDeathRow();
// }
}
break;
}
case ExecutionStateEngine::RechargingState:
{
IgnorePostCollision();
beamEntity.Remove();
Scalar count = beamRechargeTime - GetTimeParameter(till);
Min_Clamp(count, 0.0f);
SetCount(count);
if (beamRechargeTime > 0.49f)
{
if ((!m_PlayedRechargeSound) && (GetTimeParameter(till) >= beamRechargeTime-0.5f))
{
if (GetParentVehicle ())
if (GetParentVehicle()->vehicleInterface)
GetParentVehicle ()->vehicleInterface->ReactToEvent (VehicleInterface::RECHARGE_BEAM);
m_PlayedRechargeSound = true;
}
}
if (GetTimeParameter(till) >= beamRechargeTime)
{
executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
if(guiWeapon.GetCurrent())
guiWeapon.GetCurrent()->Ready();
}
break;
}
}
BaseClass::PreCollisionExecute(till);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamWeapon::PostCollisionExecute(Time till)
{
Check_Object(this);
POSTCOLLISION_LOGIC("Subsystem::Beam");
if(!beamEntity.GetCurrent())
{
CreateBeam();
CreateLightAmpFlareOut();
CreateMuzzleFlash();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void BeamWeapon::CreateBeam()
{
const GameModel *model = GetGameModel();
Check_Object(model);
ClassID class_ID;
class_ID = GetClassIDFromDataListID(model->beamModelResource);
Verify(class_ID != NullClassID);
Point3D target_point = Point3D::Identity;
ReplicatorID target_id = ReplicatorID::Null;
Normal3D target_normal(1.0f, 0.0f, 0.0f);
Check_Object(sitePointer);
const char *site_name = sitePointer->GetName();
ReplicatorID inflicting_id = ReplicatorID::Null;
inflicting_id = GetParentVehicle()->GetReplicatorID();
if(targetQuery.m_line->m_length > model->maxDistance)
{
// SPEWALWAYS(("jackyc", "Create Beam - Length too long. NULL target"));
targetQuery.m_line->m_length = model->maxDistance;
targetQuery.m_line->FindEnd(&target_point);
targetQuery.m_raySource = NULL;
}
else
{
if(targetQuery.m_raySource)
{
// SPEWALWAYS(("jackyc", "Create Beam - Using normal m_raySource target"));
Check_Object(targetQuery.m_raySource);
target_id = targetQuery.m_raySource->GetReplicatorID();
targetQuery.m_line->FindEnd(&target_point);
target_normal = *targetQuery.m_normal;
}
else
{
// SPEWALWAYS(("jackyc", "Create Beam - NULL m_raySource target"));
targetQuery.m_line->FindEnd(&target_point);
}
}
BeamEntity::CreateMessage beam_create_message(
sizeof(BeamEntity::CreateMessage),
DefaultEventPriority,
CreateMessage::DefaultFlags,
class_ID,
BeamEntity::DefaultFlags,
model->beamModelResource,
sitePointer->GetLocalToWorld(),
0.0f,
ExecutionStateEngine::AlwaysExecuteState,
NameTable::NullObjectID,
Entity::DefaultAlignment,
inflicting_id,
site_name,
target_point,
target_id,
target_normal,
model->damageAmount,
model->heatToDeal,
targetQuery.m_material,
model->maxDistance,
// MSL 5.03 Min Distance
// model->minDistance,
model->itemID
);
MemoryStream stream(&beam_create_message, beam_create_message.messageLength);
Entity *entity;
ReplicatorID beam_id = Connection::Hermit->GetNextReplicatorID();
entity = CreateEntity(&stream, &beam_id, false);
Check_Object(entity);
Verify(entity->IsDerivedFrom(BeamEntity::DefaultData));
beamEntity.Add(Cast_Object(BeamEntity *, entity));
Check_Object(Map::GetInstance());
Map::GetInstance()->AddChild(beamEntity.GetCurrent());
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void BeamWeapon::QuickFire (Adept::Entity *target)
{
const GameModel *model = GetGameModel();
Check_Object(model);
Check_Object(this);
executionState->RequestState(ExecutionStateEngine::RechargingState);
lastParameterization = gos_GetElapsedTime();
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,
BeamDamageType,
Stuff::UnitVector3D(1,0,0),
Point3D (-1,-1,-1),
model->heatToDeal,
true,
model->itemID
);
target->Receive(&damage_message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void BeamWeapon::Fire(CollisionQuery *query,
Time time_locked,
const Stuff::Point3D& lockedTargetEntityPartOffset)
{
//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;
// ngLog addition
if(ReadyToFire()) {
#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, &targetQuery);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool BeamWeapon::ReadyToFire()
{
// if this is the local mech, always true in single player.
if (GetReplicatorID().connectionID == Connection::Local->GetID())
{
if (CanFireFromCurrentArm() == false)
{
return (false);
}
return (executionState->GetState()==ExecutionStateEngine::NeverExecuteState);
}
// SPEWALWAYS(("jackyc", "SERVER LAST RECHARGE START TIME - %f", lastParameterization));
// SPEWALWAYS(("jackyc", "SERVER CURRENT TIME - %f", gos_GetElapsedTime()));
// SPEWALWAYS(("jackyc", "SERVER RECHARGE TIME - %f", (gos_GetElapsedTime() - lastParameterization)));
// SPEWALWAYS(("jackyc", "FULL RECHARGE TIME - %f", beamRechargeTime));
// SPEWALWAYS(("jackyc", "RECHARGE EPSILON - %f", ((0.1f * beamRechargeTime) + (packet_time_diff * 0.001f))));
if (executionState->GetState()==ExecutionStateEngine::RechargingState)
{
if (gos_GetElapsedTime() - lastParameterization <= 0.1f)
{
// SPEWALWAYS(("jackyc", "RESETTING Recharge"));
executionState->RequestState(ExecutionStateEngine::NeverExecuteState);
}
return true;
}
return (executionState->GetState()==ExecutionStateEngine::NeverExecuteState);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamWeapon::CleanUpCreatedBeam()
{
Check_Object(this);
if(beamEntity.GetCurrent())
beamEntity.GetCurrent()->SentenceToDeathRow();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamWeapon::DestroySubsystem()
{
Check_Object(this);
EnterNeverExecuteState();
IgnorePostCollision();
CleanUpCreatedBeam();
BaseClass::DestroySubsystem();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamWeapon::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}