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

712 lines
19 KiB
C++

#include "MW4Headers.hpp"
#include "BombastWeapon.hpp"
#include "Vehicle.hpp"
#include "BeamEntity.hpp"
#include "GUIWeaponManager.hpp"
#include "VehicleInterface.hpp"
#include "SubsystemClassData.hpp"
#include <Adept\Map.hpp>
#include <Adept\EntityManager.hpp>
#include <Adept\Effect.hpp>
#include <Adept\Site.hpp>
#include <MLR\MLRTexturePool.hpp>
//#############################################################################
//########################## ExecutionStateEngine #######################
//#############################################################################
BombastWeapon::ExecutionStateEngine::ClassData*
BombastWeapon::ExecutionStateEngine::DefaultData = NULL;
const StateEngine::StateEntry
BombastWeapon::ExecutionStateEngine::StateEntries[]=
{
STATE_ENTRY(BombastWeapon__ExecutionStateEngine, Charging)
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BombastWeapon::ExecutionStateEngine::InitializeClass()
{
Check_Object(StateEngine::DefaultData);
Verify(!DefaultData);
DefaultData =
new ClassData(
BombastWeapon__ExecutionStateEngineClassID,
"BombastWeapon::ExecutionStateEngine",
BaseClass::DefaultData,
ELEMENTS(StateEntries), StateEntries,
(Entity::ExecutionStateEngine::Factory)Make,
(Entity::ExecutionStateEngine::FactoryRequest::Factory)
&FactoryRequest::ConstructFactoryRequest
);
Register_Object(DefaultData);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BombastWeapon::ExecutionStateEngine::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BombastWeapon::ExecutionStateEngine*
BombastWeapon::ExecutionStateEngine::Make(
BombastWeapon *weapon,
FactoryRequest *request
)
{
Check_Object(weapon);
Check_Object(request);
gos_PushCurrentHeap(Heap);
BombastWeapon::ExecutionStateEngine *engine =
new BombastWeapon::ExecutionStateEngine(DefaultData, weapon, request);
gos_PopCurrentHeap();
return engine;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
BombastWeapon::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 firing, see if this is the start of the bombast. If so,
// start the charging processes instead. Otherwise don't let anything
// fire until at least half a second has gone by
//--------------------------------------------------------------------
//
BombastWeapon *beam;
beam = Cast_Object(BombastWeapon*, owningEntity);
if (new_state == FiringState)
{
switch (currentState)
{
case NeverExecuteState:
new_state = ChargingState;
break;
case ChargingState:
if ((gos_GetElapsedTime() - beam->startChargeTime) <= 0.5f)
return currentState;
break;
case RechargingState:
return currentState;
}
}
//
//---------------------------------------------------------------------
// Otherwise, make sure we only enter charging state from never execute
//---------------------------------------------------------------------
//
else if ((new_state == ChargingState) && (currentState != NeverExecuteState))
return currentState;
//
//-------------------------------------------------------------------------
// We are calling weapon directly because we do not want beam functionality
//-------------------------------------------------------------------------
//
switch (BaseClass::BaseClass::RequestState(new_state, data))
{
case ChargingState:
{
beam->UsePostCollision();
beam->lastParameterization = gos_GetElapsedTime();
beam->startChargeTime = gos_GetElapsedTime();
beam->percentCharged = 0.25f;
beam->beamRechargeTime = 0.0f;
break;
}
case FiringState:
{
//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->LeaveNeverExecuteState();
beam->lastParameterization = gos_GetElapsedTime();
if(beam->guiWeapon.GetCurrent())
beam->guiWeapon.GetCurrent()->Reload();
beam->ApplyHeat();
beam->beamRechargeTime = (Scalar)(gos_GetElapsedTime() - beam->startChargeTime);
const BeamWeapon::GameModel *beam_model = beam->GetGameModel();
Clamp(beam->beamRechargeTime, 0.0f, beam_model->reloadTime);
}
break;
case NeverExecuteState:
case DestroyedState:
break;
default:
{
owningEntity->IgnorePostCollision();
break;
}
}
return currentState;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BombastWeapon::ExecutionStateEngine::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}
//#############################################################################
//############################### BombastWeapon #########################
//#############################################################################
BombastWeapon::ClassData*
BombastWeapon::DefaultData = NULL;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BombastWeapon::InitializeClass()
{
Verify(!DefaultData);
DefaultData =
new ClassData(
BombastWeaponClassID,
"MechWarrior4::BombastWeapon",
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(
BombastWeapon,
ExecutionState,
executionState,
BombastWeapon__ExecutionStateEngine
);
DIRECT_GAME_MODEL_ATTRIBUTE(
BombastWeapon__GameModel,
MaxChargeTime,
maxChargeTime,
Scalar
);
DIRECT_GAME_MODEL_ATTRIBUTE(
BombastWeapon__GameModel,
TimeForMaxCharge,
timeForMaxCharge,
Scalar
);
DIRECT_GAME_MODEL_ATTRIBUTE(
BombastWeapon__GameModel,
ChargeEffectResource,
chargeEffectResource,
ResourceID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
BombastWeapon__GameModel,
InitialChargeBeamResource,
initialChargeBeamResource,
ResourceID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
BombastWeapon__GameModel,
HalfChargeBeamResource,
halfChargeBeamResource,
ResourceID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
BombastWeapon__GameModel,
ThreeQuarterChargeBeamResource,
threeQuarterChargeBeamResource,
ResourceID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
BombastWeapon__GameModel,
FullChargeBeamResource,
fullChargeBeamResource,
ResourceID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
BombastWeapon__GameModel,
InitialChargeFlareResource,
initialChargeFlareResource,
ResourceID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
BombastWeapon__GameModel,
HalfChargeFlareResource,
halfChargeFlareResource,
ResourceID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
BombastWeapon__GameModel,
ThreeQuarterChargeFlareResource,
threeQuarterChargeFlareResource,
ResourceID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
BombastWeapon__GameModel,
FullChargeFlareResource,
fullChargeFlareResource,
ResourceID
);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BombastWeapon::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BombastWeapon*
BombastWeapon::Make(
CreateMessage *message,
ReplicatorID *base_id
)
{
gos_PushCurrentHeap(Heap);
BombastWeapon *new_entity =
new BombastWeapon(DefaultData, message, base_id, NULL);
gos_PopCurrentHeap();
Check_Object(new_entity);
return new_entity;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BombastWeapon::BombastWeapon(
ClassData *class_data,
CreateMessage *message,
ReplicatorID *base_id,
ElementRenderer::Element *element
):
BeamWeapon(class_data, message, base_id, element),
chargeEffect(NULL)
{
Check_Pointer(this);
Check_Object(message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BombastWeapon::~BombastWeapon()
{
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool BombastWeapon::ReadyToFire()
{
if (CanFireFromCurrentArm() == false)
{
return (false);
}
if (executionState->GetState()==ExecutionStateEngine::NeverExecuteState)
{
return true;
}
if ( executionState->GetState() == ExecutionStateEngine::ChargingState )
{
if ((gos_GetElapsedTime() - startChargeTime) > 0.5f)
{
return true;
}
}
return false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void BombastWeapon::PreCollisionExecute(Time till)
{
PRECOLLISION_LOGIC("Subsystem::Bombast");
Check_Object(executionState);
int pre_state = executionState->GetState();
switch (pre_state)
{
case ExecutionStateEngine::ChargingState:
{
if((GetTimeParameter(till) > 1.0f) && (percentCharged < 1.0f))
{
percentCharged = percentCharged + 0.25f;
lastParameterization = gos_GetElapsedTime();
}
#if 0
if((gos_GetElapsedTime() - startChargeTime) > model->maxChargeTime)
{
Verify(GetParentVehicle()->IsDerivedFrom(Vehicle::DefaultData));
Vehicle *vehicle = Cast_Object(Vehicle *, GetParentVehicle());
Check_Object(vehicle);
if(vehicle->vehicleInterface)
{
vehicle->vehicleInterface->targetQuery.m_raySource =
vehicle->vehicleInterface->targetEntityPart.GetCurrent();
Fire(&vehicle->vehicleInterface->targetQuery, 0.0f, Stuff::Point3D::Identity);
}
else
{
executionState->RequestState(ExecutionStateEngine::FiringState);
}
}
#endif
break;
}
}
BaseClass::PreCollisionExecute(till);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BombastWeapon::PostCollisionExecute(Time till)
{
Check_Object(this);
Check_Object(executionState);
POSTCOLLISION_LOGIC("Subsystem::Bombast");
int pre_state = executionState->GetState();
switch (pre_state)
{
case ExecutionStateEngine::ChargingState:
{
//
//--------------------------
//Create the charging effect
//--------------------------
//
if(!chargeEffect.GetCurrent())
CreateChargeEffect();
break;
}
default:
{
if(chargeEffect.GetCurrent())
chargeEffect.GetCurrent()->SentenceToDeathRow();
BaseClass::PostCollisionExecute(till);
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void BombastWeapon::CreateBeam()
{
const GameModel *model = GetGameModel();
Check_Object(model);
ResourceID beam_resource;
if(percentCharged <= 0.25f)
beam_resource = model->initialChargeBeamResource;
else if(percentCharged <= 0.50f)
beam_resource = model->halfChargeBeamResource;
else if(percentCharged <= 0.75)
beam_resource = model->threeQuarterChargeBeamResource;
else
beam_resource = model->fullChargeBeamResource;
Verify(beam_resource != ResourceID::Null);
ClassID class_ID;
class_ID = GetClassIDFromDataListID(beam_resource);
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)
{
targetQuery.m_line->m_length = model->maxDistance;
targetQuery.m_line->FindEnd(&target_point);
targetQuery.m_raySource = NULL;
}
else
{
if(targetQuery.m_raySource)
{
Check_Object(targetQuery.m_raySource);
target_id = targetQuery.m_raySource->GetReplicatorID();
targetQuery.m_line->FindEnd(&target_point);
target_normal = *targetQuery.m_normal;
}
else
{
targetQuery.m_line->FindEnd(&target_point);
}
}
Stuff::Scalar damage_amount, heat_amount;
damage_amount = percentCharged * model->damageAmount;
heat_amount = percentCharged * model->heatToDeal;
BeamEntity::CreateMessage beam_create_message(
sizeof(BeamEntity::CreateMessage),
DefaultEventPriority,
CreateMessage::DefaultFlags,
class_ID,
BeamEntity::DefaultFlags,
beam_resource,
sitePointer->GetLocalToWorld(),
0.0f,
ExecutionStateEngine::AlwaysExecuteState,
NameTable::NullObjectID,
Entity::DefaultAlignment,
inflicting_id,
site_name,
target_point,
target_id,
target_normal,
damage_amount,
heat_amount,
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
BombastWeapon::CreateChargeEffect()
{
Check_Object(this);
const GameModel *model = GetGameModel();
Check_Object(model);
if(model->chargeEffectResource == ResourceID::Null)
return;
Resource charge_resource(model->chargeEffectResource);
if(charge_resource.DoesResourceExist())
{
ClassID class_ID;
class_ID = GetClassIDFromDataListID(model->chargeEffectResource);
Verify(class_ID != NullClassID);
LinearMatrix4D effect_position = LinearMatrix4D::Identity;
UnitVector3D unit_forward;
Check_Object(sitePointer);
LinearMatrix4D site_position = sitePointer->GetLocalToWorld();
site_position.GetLocalForwardInWorld(&unit_forward);
effect_position.AlignLocalAxisToWorldVector(unit_forward, Y_Axis, X_Axis, Z_Axis);
Point3D muzzle_translation;
muzzle_translation = sitePointer->GetLocalToWorld();
effect_position.BuildTranslation(muzzle_translation);
Effect::CreateMessage effect_create_message(
sizeof(Effect::CreateMessage),
DefaultEventPriority,
CreateMessage::DefaultFlags,
class_ID,
Effect::DefaultFlags,
model->chargeEffectResource,
effect_position,
0.0f,
ExecutionStateEngine::AlwaysExecuteState,
NameTable::NullObjectID,
Entity::DefaultAlignment
);
MemoryStream stream(&effect_create_message, effect_create_message.messageLength);
Entity *entity;
ReplicatorID beam_id = Connection::Hermit->GetNextReplicatorID();
entity = CreateEntity(&stream, &beam_id, false);
Check_Object(Map::GetInstance());
Map::GetInstance()->AddChild(entity);
chargeEffect.Remove();
chargeEffect.Add(Cast_Object(Effect *, entity));
chargeEffect.GetCurrent()->SetFollowEntity(sitePointer);
chargeEffect.GetCurrent()->SyncMatrices(true);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BombastWeapon::CleanUpCreatedBeam()
{
Check_Object(this);
if(beamEntity.GetCurrent())
beamEntity.GetCurrent()->SentenceToDeathRow();
if(chargeEffect.GetCurrent())
chargeEffect.GetCurrent()->SentenceToDeathRow();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BombastWeapon::CreateMuzzleFlash()
{
Check_Object(this);
const GameModel *model = GetGameModel();
Check_Object(model);
ResourceID flare_resource;
if(percentCharged <= 0.25f)
flare_resource = model->initialChargeFlareResource;
else if(percentCharged <= 0.50f)
flare_resource = model->halfChargeFlareResource;
else if(percentCharged < 0.75)
flare_resource = model->threeQuarterChargeFlareResource;
else
flare_resource = model->fullChargeFlareResource;
Verify(flare_resource != ResourceID::Null);
// if(model->muzzleFlashResource == ResourceID::Null)
// return;
Resource muzzle_flash_resource(flare_resource);
if(muzzle_flash_resource.DoesResourceExist())
{
ClassID class_ID;
class_ID = GetClassIDFromDataListID(flare_resource);
Verify(class_ID != NullClassID);
LinearMatrix4D effect_position = LinearMatrix4D::Identity;
UnitVector3D unit_forward;
Check_Object(sitePointer);
LinearMatrix4D site_position = sitePointer->GetLocalToWorld();
site_position.GetLocalForwardInWorld(&unit_forward);
effect_position.AlignLocalAxisToWorldVector(unit_forward, Y_Axis, X_Axis, Z_Axis);
Point3D muzzle_translation;
muzzle_translation = sitePointer->GetLocalToWorld();
effect_position.BuildTranslation(muzzle_translation);
Effect::CreateMessage effect_create_message(
sizeof(Effect::CreateMessage),
DefaultEventPriority,
CreateMessage::DefaultFlags,
class_ID,
Effect::DefaultFlags,
flare_resource,
effect_position,
0.0f,
ExecutionStateEngine::AlwaysExecuteState,
NameTable::NullObjectID,
Entity::DefaultAlignment
);
MemoryStream stream(&effect_create_message, effect_create_message.messageLength);
Entity *entity;
ReplicatorID beam_id = Connection::Hermit->GetNextReplicatorID();
entity = CreateEntity(&stream, &beam_id, false);
Check_Object(Map::GetInstance());
Map::GetInstance()->AddChild(entity);
Effect *effect;
effect = Cast_Object(Effect *, entity);
effect->SetFollowEntity(sitePointer);
entity->SyncMatrices(true);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BombastWeapon::SetQuery(CollisionQuery *query)
{
Check_Object(this);
Check_Object(sitePointer);
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;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BombastWeapon::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}