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

829 lines
21 KiB
C++

#include "MW4Headers.hpp"
#include "BeamEntity.hpp"
#include <Adept\EntityClassData.hpp>
#include "MWPlayer.hpp"
#include "VehicleInterface.hpp"
#include <Adept\Effect.hpp>
#include <Adept\Map.hpp>
#include "MWMover.hpp"
#include "MWObject.hpp"
#include <Adept\NameTable.hpp>
#include "Decal.hpp"
#include "MWDamageObject.hpp"
#include <Adept\CollisionGrid.hpp>
#include <Adept\VideoRenderer.hpp>
#include <Adept\EntityManager.hpp>
#include <Adept\Site.hpp>
#include "mwmission.hpp"
#include "bucket.hpp"
#include "MWApplication.hpp"
//#############################################################################
//############################### BeamEntity ############################
//#############################################################################
BeamEntity::ClassData*
BeamEntity::DefaultData = NULL;
DWORD MechWarrior4::Executed_Beam_Count = 0;
#define HUNT_ADD_EFFECT_BUG
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamEntity::InitializeClass()
{
Verify(!DefaultData);
DefaultData =
new ClassData(
BeamEntityClassID,
"MechWarrior4::BeamEntity",
BaseClass::DefaultData,
0, NULL,
(Entity::Factory)Make,
NULL,
ExecutionStateEngine::DefaultData,
(Entity::GameModel::Factory)GameModel::ConstructGameModel,
NULL,
(Entity::GameModel::ReadAndVerifier)GameModel::ReadAndVerify,
(Entity::GameModel::ModelWrite)GameModel::WriteToText,
(Entity::GameModel::ModelSave)GameModel::SaveGameModel
);
Register_Object(DefaultData);
CUSTOM_DIRECT_ATTRIBUTE(
BeamEntity,
BeamDistance,
beamDistanceAttribute,
Scalar,
ScalarClassID
);
CUSTOM_DIRECT_ATTRIBUTE(
BeamEntity,
TargetPoint,
targetPoint,
Point3D,
Point3DClassID
);
CUSTOM_DIRECT_ATTRIBUTE(
BeamEntity,
BeamHitSFX,
beamHitSFX,
int,
IntClassID
);
CUSTOM_DIRECT_ATTRIBUTE(
BeamEntity,
BeamStatus,
beamStatus,
bool,
BoolClassID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
BeamEntity__GameModel,
HitEffectResource,
hitEffectResourceID,
ResourceID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
BeamEntity__GameModel,
DamageDecalResource,
damageDecalResourceID,
ResourceID
);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamEntity::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BeamEntity*
BeamEntity::Make(
CreateMessage *message,
ReplicatorID *base_id
)
{
gos_PushCurrentHeap(Heap);
Check_Object(EntityManager::GetInstance());
BeamEntity *new_entity;
Entity *entity = EntityManager::GetInstance()->RequestFromArmory(message->dataListID);
if (entity)
{
new_entity = Cast_Object(BeamEntity*, entity);
new_entity->Reuse(message, base_id);
}
else
new_entity = new BeamEntity(DefaultData, message, base_id, NULL);
gos_PopCurrentHeap();
Check_Object(new_entity);
return new_entity;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BeamEntity::BeamEntity(
ClassData *class_data,
CreateMessage *message,
ReplicatorID *base_id,
ElementRenderer::Element *element
):
Entity(class_data, message, base_id, element),
siteEntity(NULL),
targetEntity(NULL)
{
Check_Pointer(this);
Check_Object(message);
Check_Object(Connection::Local);
inflictingEntityID = message->inflictingEntityID;
if (MWApplication::GetInstance()->networkingFlag == true)
{
MWMission *mission;
Check_Object (MWMission::GetInstance());
mission = Cast_Object (MWMission *,MWMission::GetInstance());
mission->m_BucketManager->NotifyAction (CBucketManager::SHOTS_FIRED,inflictingEntityID,1);
}
// find the entity and connect to it
Check_Object(Network::GetInstance());
Connection *connection = Network::GetInstance()->GetConnection(inflictingEntityID.connectionID);
Check_Object(connection);
Replicator *replicator;
replicator = connection->FindReplicator(inflictingEntityID);
Verify(replicator->IsDerivedFrom(MWObject::DefaultData));
inflictingVehicle = Cast_Object(MWObject *, replicator);
Site *site = inflictingVehicle->m_sites.Find(message->siteName);
Check_Object(site);
siteEntity.Add(site);
targetOffset = Stuff::Point3D::Identity;
if(message->targetID != ReplicatorID::Null)
{
Check_Object(Network::GetInstance());
Connection *connection = Network::GetInstance()->GetConnection(message->targetID.connectionID);
if (connection)
{
Check_Object(connection);
Replicator *rep = connection->FindReplicator(message->targetID);
if (rep)
{
targetEntity.Add(Cast_Object(
Entity*,
rep
));
//
// If we have an entity we are locked on then targetPoint is the offset from the object itself
// we save this to use later in the raycast testing against where the object is now.
//
//
// Make targetOffset the difference from the target entity to the point
//
targetOffset.MultiplyByInverse(message->targetPoint, targetEntity.GetCurrent()->GetLocalToWorld());
}
}
}
targetPoint = message->targetPoint;
targetNormal = message->targetNormal;
damageAmount = message->damageAmount;
targetMaterial = message->targetMaterial;
heatAmount = message->heatAmount;
maxDistance = message->maxDistance;
// MSL 5.03 Min Distance
// minDistance = message->minDistance;
itemID = message->itemID;
Check_Object(siteEntity.GetCurrent());
beamHitSFX = 0;
CommonConstructor();
lastParameterization = gos_GetElapsedTime();
// firstFrame = true;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
BeamEntity::~BeamEntity()
{
DESTRUCTOR("BeamEntity");
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamEntity::Reuse(
const CreateMessage *message,
ReplicatorID *base_id
)
{
Check_Object(this);
BaseClass::Reuse(message, base_id);
Check_Pointer(this);
Check_Object(message);
Check_Object(Connection::Local);
// find the entity and connect to it
inflictingEntityID = message->inflictingEntityID;
// find the entity and connect to it
Check_Object(Network::GetInstance());
Connection *connection = Network::GetInstance()->GetConnection(inflictingEntityID.connectionID);
Check_Object(connection);
Replicator *replicator;
replicator = connection->FindReplicator(inflictingEntityID);
Verify(replicator->IsDerivedFrom(MWObject::DefaultData));
inflictingVehicle = Cast_Object(MWObject *, replicator);
Site *site = inflictingVehicle->m_sites.Find(message->siteName);
Check_Object(site);
siteEntity.Remove();
siteEntity.Add(site);
targetOffset = Stuff::Point3D::Identity;
if(message->targetID != ReplicatorID::Null)
{
Check_Object(Network::GetInstance());
Connection *connection = Network::GetInstance()->GetConnection(message->targetID.connectionID);
if (connection)
{
Check_Object(connection);
Replicator *rep = connection->FindReplicator(message->targetID);
if (rep)
{
targetEntity.Add(Cast_Object(
Entity*,
rep
));
//
// If we have an entity we are locked on then targetPoint is the offset from the object itself
// we save this to use later in the raycast testing against where the object is now.
//
//
// Make targetOffset the difference from the target entity to the point
//
targetOffset.MultiplyByInverse(message->targetPoint, targetEntity.GetCurrent()->GetLocalToWorld());
}
}
}
targetPoint = message->targetPoint;
targetNormal = message->targetNormal;
damageAmount = message->damageAmount;
targetMaterial = message->targetMaterial;
heatAmount = message->heatAmount;
maxDistance = message->maxDistance;
// MSL 5.03 Min Distance
// minDistance = message->minDistance;
Check_Object(siteEntity.GetCurrent());
beamHitSFX = 0;
CommonConstructor();
lastParameterization = gos_GetElapsedTime();
// firstFrame = true;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamEntity::CommonConstructor()
{
Check_Pointer(this);
rayCastDone = false;
lastParameterization = gos_GetElapsedTime();
if (siteEntity.GetCurrent())
{
ReplicatorID beam_hit_entity_id;
beam_hit_entity_id = MoveBeam();
//We want to make sure that what we are hitting now is what we thought we should hit..if not then no damage
if(targetEntity.GetCurrent())
{
//
// The hit effect will hang in empty space if we destroy something when we hit it. We should leave the destruction
// effect only in this case.
// This doesn't fix all cases since it could be destroyed as a result of another weapon
// this frame, and our hit effect will still be around.
//
//only damage if we are really hitting (nothing was in our way)
if(beam_hit_entity_id != ReplicatorID::Null)
InflictDamage(beam_hit_entity_id);
//
// Don't do hit effect under water unless we hit something
//
Check_Object (Map::GetInstance());
const Adept::Map__GameModel *mapModel = Map::GetInstance()->GetGameModel();
Check_Object(mapModel);
// if (!targetEntity.GetCurrent()->IsDestroyed())
// {
if (targetPoint.y >= ((mapModel->m_waterLevel) -.001f) ||
!targetEntity.GetCurrent()->IsDerivedFrom(Map::DefaultData))
{
CreateHitEffect();
PlaceDamageDecal();
}
// }
}
// else
// {
// MoveBeam();
// }
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamEntity::PostCollisionExecute(Time till)
{
Check_Pointer(this);
Set_Statistic(Executed_Beam_Count, Executed_Beam_Count+1);
POSTCOLLISION_LOGIC("Beam");
#if 0
// NO LONGER SUPPORTED!!!!!!!!!!!!!
if(GetTimeParameter(till) >= 0.5)
{
//
//------------------------------
//We need to update our position
//------------------------------
//
Check_Object(siteEntity);
MWObject *mw_object = siteEntity->GetParentVehicle();
Check_Object(mw_object);
mw_object->targetQuery->line->FindEnd(&targetPoint);
targetEntity = mw_object->targetQuery->raySource;
lastParameterization = gos_GetElapsedTime();
MoveBeam();
if(targetEntity)
{
CreateHitEffect();
PlaceDamageDecal();
InflictDamage();
}
}
else
#endif
#if 0
if(firstFrame)
{
firstFrame = false;
RendererComponentWeb *video_web = GetComponentWeb(VideoRenderer::Instance);
if(video_web)
{
Check_Object(video_web);
video_web->SendCommand(Renderer::StartEffectCommandID);
}
}
#endif
MoveBeam();
SyncMatrices(true);
BaseClass::PostCollisionExecute(till);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
ReplicatorID
BeamEntity::MoveBeam()
{
Check_Object(this);
ReplicatorID hit_entity_id = ReplicatorID::Null;
// if we haven't lost our master...
if (siteEntity.GetCurrent() == NULL)
return hit_entity_id;
Check_Object(siteEntity.GetCurrent());
Point3D new_translation;
new_translation = siteEntity.GetCurrent()->GetLocalToWorld();
if (!rayCastDone && targetEntity.GetCurrent())
{
Stuff::LinearMatrix4D tempLtW;
// Stuff::LinearMatrix4D ltw = Stuff::LinearMatrix4D::Identity;
// ltw.BuildTranslation(targetOffset);
tempLtW = targetEntity.GetCurrent()->GetLocalToWorld();
targetPoint.Multiply(targetOffset, tempLtW);
hit_entity_id = targetEntity.GetCurrent()->GetReplicatorID();
}
Line3D target_line_copy;
Vector3D target_vector;
target_vector.Subtract(targetPoint, new_translation);
Scalar beam_length = target_vector.GetLength();
beamDistanceAttribute = beam_length;
if(!rayCastDone)
{
rayCastDone = true;
Line3D target_line;
Normal3D target_normal;
target_line.SetOrigin(new_translation);
target_line.SetDirection(target_vector);
target_line.m_length = maxDistance;
Entity::CollisionQuery target_query(&target_line, &target_normal, (Entity::WalkerMask), inflictingVehicle);
Adept::Entity *entity_hit;
Check_Object(CollisionGrid::Instance);
entity_hit = CollisionGrid::Instance->ProjectLine(&target_query);
target_line_copy = *(target_query.m_line);
//
// If we don't hit anything with the cast, then we just leave the target point where it was last frame
//
// Not doing this check can cause a crash. If the beam goes through the "hit" object and the beam is pointed
// towards the end of the world, the m_line is extending off the map, and fails the FindZone() call.
//
if (entity_hit)
{
target_query.m_line->FindEnd(&targetPoint);
hit_entity_id = entity_hit->GetReplicatorID();
targetEntity.Remove();
targetEntity.Add(entity_hit);
}
PlayWaterEffectIfNecessary(target_line_copy);
}
LinearMatrix4D new_position = LinearMatrix4D::Identity;
new_position.BuildTranslation(new_translation);
ElementRenderer::Element *element = GetElement();
Check_Object(element);
element->SetLocalToParent(new_position);
NeedMatrixSync();
return hit_entity_id;
#if 0
LinearMatrix4D new_position;
Point3D new_translation;
new_translation = siteEntity->GetLocalToWorld();
new_position.BuildTranslation(new_translation);
Vector3D target_vector;
target_vector.Subtract(targetPoint, new_translation);
Scalar beam_length = target_vector.GetLength();
if(beam_length > maxDistance)
{
//
//----------
//Scale Beam
//----------
//
Scalar scale;
scale = maxDistance / beam_length;
target_vector *= scale;
}
YawPitchRange new_rotation;
new_rotation = target_vector;
YawPitchRoll new_yaw_pitch_roll = YawPitchRoll::Identity;
new_yaw_pitch_roll.yaw = new_rotation.yaw;
new_yaw_pitch_roll.pitch = new_rotation.pitch;
beamDistanceAttribute = new_rotation.range;
new_position.BuildRotation(new_yaw_pitch_roll);
ElementRenderer::Element *element = GetElement();
Check_Object(element);
element->SetLocalToParent(new_position);
NeedMatrixSync();
#endif
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamEntity::PlayWaterEffectIfNecessary(Line3D target_Line)
{
const GameModel * model = GetGameModel();
Check_Object(model);
Check_Object (Map::GetInstance());
const Adept::Map__GameModel *mapModel = Map::GetInstance()->GetGameModel();
Check_Object(mapModel);
//
// Because of certain assumptions at the lower level if you put in watersurface
// then you end up trying to collision when you get added to the zone, and that's
// a bad thing if you don't have a collision volume
//
// if (model->waterSurface)
// {
Stuff::Scalar product;
Plane water_Level;
water_Level.normal.x = 0.0f;
water_Level.normal.y = 1.0f;
water_Level.normal.z = 0.0f;
water_Level.offset = mapModel->m_waterLevel;
Stuff::Scalar to_water = target_Line.GetDistanceTo(water_Level, &product);
if (product<0.0f && to_water>=0.0f && to_water<=target_Line.m_length)
{
Line3D target_LineWaterIntersection;
target_LineWaterIntersection = target_Line;
target_LineWaterIntersection.m_length = to_water;
Point3D point_in_world;
target_LineWaterIntersection.FindEnd(&point_in_world);
//
// Make sure we didn't hit a plane off the map
//
if (Map::GetInstance()->FindZone(point_in_world) != -1)
{
ResourceID hit_effect_resource_id = ResourceID::Null;
if(model->hitEffectMaterialTable != ResourceID::Null)
{
Entity__GameModel::GetEffectResourceID(&hit_effect_resource_id,
model->hitEffectMaterialTable, WaterMaterial
);
}
// else
//{
// hit_effect_resource_id = model->hitEffectResourceID;
// }
if(hit_effect_resource_id != ResourceID::Null)
{
Resource hit_effect_resource(hit_effect_resource_id);
Verify(hit_effect_resource.DoesResourceExist());
ClassID class_ID;
class_ID = GetClassIDFromDataListID(hit_effect_resource_id);
Verify(class_ID != NullClassID);
LinearMatrix4D effect_position = LinearMatrix4D::Identity;
effect_position.AlignLocalAxisToWorldVector(water_Level.normal, Y_Axis, X_Axis, Z_Axis);
effect_position.BuildTranslation(point_in_world);
Effect::CreateMessage effect_create_message(
sizeof(Effect::CreateMessage),
DefaultEventPriority,
CreateMessage::DefaultFlags,
class_ID,
Effect::DefaultFlags,
hit_effect_resource_id,
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);
#ifdef HUNT_ADD_EFFECT_BUG
if (entity == NULL)
{
STOP(("FAILED CREATE ENTITY BEAM"));
}
#endif
Check_Object(entity);
Check_Object(Map::GetInstance());
Map::GetInstance()->AddChild(entity);
entity->SyncMatrices(true);
// play SFX
beamHitSFX = 1;
}
}
}
// }
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamEntity::CreateHitEffect()
{
Check_Object(this);
const GameModel *model = GetGameModel();
Check_Object(model);
ResourceID hit_effect_resource_id = ResourceID::Null;
if((model->hitEffectMaterialTable != ResourceID::Null) &&
(targetMaterial >= 0) && (targetMaterial < MaterialCount))
{
Entity__GameModel::GetEffectResourceID(&hit_effect_resource_id,
model->hitEffectMaterialTable, targetMaterial
);
}
else
{
hit_effect_resource_id = model->hitEffectResourceID;
}
if(hit_effect_resource_id != ResourceID::Null)
{
Resource hit_effect_resource(hit_effect_resource_id);
Verify(hit_effect_resource.DoesResourceExist());
ClassID class_ID;
class_ID = GetClassIDFromDataListID(hit_effect_resource_id);
Verify(class_ID != NullClassID);
LinearMatrix4D effect_position = LinearMatrix4D::Identity;
effect_position.AlignLocalAxisToWorldVector(targetNormal, Y_Axis, X_Axis, Z_Axis);
effect_position.BuildTranslation(targetPoint);
Effect::CreateMessage effect_create_message(
sizeof(Effect::CreateMessage),
DefaultEventPriority,
CreateMessage::DefaultFlags,
class_ID,
Effect::DefaultFlags,
hit_effect_resource_id,
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);
#ifdef HUNT_ADD_EFFECT_BUG
if (entity == NULL)
{
STOP(("FAILED CREATE ENTITY BEAM"));
}
#endif
Check_Object(entity);
Check_Object(Map::GetInstance());
Map::GetInstance()->AddChild(entity);
entity->SyncMatrices(true);
// play SFX
beamHitSFX = 1;
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamEntity::PlaceDamageDecal()
{
Check_Object(this);
const GameModel *model = GetGameModel();
Check_Object(model);
if (model->damageDecalResourceID != ResourceID::Null)
{
Resource damage_effect_resource(model->damageDecalResourceID);
Verify(damage_effect_resource.DoesResourceExist());
ClassID class_ID;
class_ID = GetClassIDFromDataListID(model->damageDecalResourceID);
Verify(class_ID != NullClassID);
LinearMatrix4D effect_position = LinearMatrix4D::Identity;
effect_position.AlignLocalAxisToWorldVector(targetNormal, Y_Axis, X_Axis, Z_Axis);
effect_position.BuildTranslation(targetPoint);
Decal::CreateMessage decal_create_message(
sizeof(Effect::CreateMessage),
DefaultEventPriority,
CreateMessage::DefaultFlags,
class_ID,
Decal::DefaultFlags,
model->damageDecalResourceID,
effect_position,
0.0f,
ExecutionStateEngine::NeverExecuteState,
NameTable::NullObjectID,
Entity::DefaultAlignment
);
MemoryStream stream(&decal_create_message, decal_create_message.messageLength);
Entity *entity;
ReplicatorID beam_id = Connection::Hermit->GetNextReplicatorID();
entity = CreateEntity(&stream, &beam_id, false);
Check_Object(entity);
Check_Object(targetEntity.GetCurrent());
targetEntity.GetCurrent()->AddChild(entity);
entity->SyncMatrices(true);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamEntity::InflictDamage(ReplicatorID damaged_entity)
{
Check_Object(this);
Check_Object(targetEntity.GetCurrent());
Check_Object(siteEntity.GetCurrent());
// MSL 5.06 Armor Mode
MWApplication *m_App;
m_App = MWApplication::GetInstance ();
m_ArmorMode = m_App->GetLocalNetParams()->m_armormodeOn;
Entity::TakeDamageMessage damage_message(
//targetEntity.GetCurrent()->GetReplicatorID(),
damaged_entity,
inflictingEntityID,
damageAmount,
m_ArmorMode,
BeamDamageType,
targetNormal,
targetPoint,
heatAmount,
false,
itemID
);
targetEntity.GetCurrent()->Receive(&damage_message);
//targetEntity->Dispatch(&damage_message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
BeamEntity::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}