Files
firestorm/Gameleap/code/mw4/Code/MW4/WeaponMover.cpp
T
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

1242 lines
34 KiB
C++

//===========================================================================//
// File: WeaponMover.cpp
//---------------------------------------------------------------------------//
// Date Who Modification //
// -------- --- ---------------------------------------------------------- //
// 09/09/98 JSE Inital base class based off of Shadowrun/Adept //
// 09/25/98 BDB WeaponMover based on Vehicle //
//---------------------------------------------------------------------------//
// Copyright (C) 1998, Fasa Interactive //
// All Rights reserved worldwide //
// This unpublished sourcecode is PROPRIETARY and CONFIDENTIAL //
//===========================================================================//
#include "MW4Headers.hpp"
#include "WeaponMover.hpp"
#include "Vehicle.hpp"
#include "DeathEntity.hpp"
#include "missile.hpp"
#include <Adept\Map.hpp>
#include <Adept\Effect.hpp>
#include <Adept\NameTable.hpp>
#include <Adept\EntityManager.hpp>
#include <Adept\Effect.hpp>
#include <gosFX\gosFX.hpp>
#include <ElementRenderer\Element.hpp>
#include "MWDamageObject.hpp"
#include <Adept\CollisionGrid.hpp>
#include <Adept\CollisionVolume.hpp>
#include "mwmission.hpp"
#include "bucket.hpp"
#include "MWApplication.hpp"
#include <Compost\CompostHeaders.hpp>
//#############################################################################
//############################### WeaponMover ###########################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
WeaponMover::ClassData*
WeaponMover::DefaultData = NULL;
DWORD MechWarrior4::Executed_WeaponMover_Count = 0;
void MechWarrior4::WeaponMoverSecurityCheckStart()
{
_asm
{
nop
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
WeaponMover::InitializeClass()
{
Check_Object(BaseClass::DefaultData);
Verify(!DefaultData);
DefaultData =
new ClassData(
WeaponMoverClassID,
"MechWarrior4::WeaponMover",
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
);
Register_Object(DefaultData);
INDIRECT_STATE_ATTRIBUTE(
WeaponMover,
ExecutionState,
executionState,
WeaponMover__ExecutionStateEngine
);
DIRECT_GAME_MODEL_ATTRIBUTE(
WeaponMover__GameModel,
MaxDistance,
maxDistance,
Scalar
);
// MSL 5.03 Min Distance
// DIRECT_GAME_MODEL_ATTRIBUTE(
// WeaponMover__GameModel,
// MinDistance,
// minDistance,
// Scalar
// );
DIRECT_GAME_MODEL_ATTRIBUTE(
WeaponMover__GameModel,
TrailResource,
trailResource,
ResourceID
);
CUSTOM_DIRECT_GAME_MODEL_ATTRIBUTE(
WeaponMover__GameModel,
DoesPlaceCrater,
doesPlaceCrater,
bool,
BoolClassID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
WeaponMover__GameModel,
HitEffectResource,
hitEffectResourceID,
ResourceID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
WeaponMover__GameModel,
SecondaryHitEffectResource,
secondaryHitEffectResourceID,
ResourceID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
WeaponMover__GameModel,
DamageDecalResource,
damageDecalResourceID,
ResourceID
);
DIRECT_GAME_MODEL_ATTRIBUTE(
WeaponMover__GameModel,
LightAmpFlareOut,
m_lightAmpFlareOut,
Scalar
);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
WeaponMover::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
WeaponMover*
WeaponMover::Make(
CreateMessage *message,
ReplicatorID *base_id
)
{
Check_Object(message);
gos_PushCurrentHeap(Heap);
Check_Object(EntityManager::GetInstance());
WeaponMover *new_entity;
Entity *entity = EntityManager::GetInstance()->RequestFromArmory(message->dataListID);
if (entity)
{
new_entity = Cast_Object(WeaponMover*, entity);
new_entity->Reuse(message, base_id);
}
else
new_entity = new WeaponMover(DefaultData, message, base_id, NULL);
gos_PopCurrentHeap();
Check_Object(new_entity);
return new_entity;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
WeaponMover::WeaponMover(
ClassData *class_data,
CreateMessage *message,
ReplicatorID *base_id,
ElementRenderer::Element *element
):
Mover(class_data, message, base_id, element),
splashedEntities(NULL),
trailEffect(NULL)
{
Check_Pointer(this);
Check_Object(message);
damageAmount = message->damageAmount;
heatAmount = message->heatAmount;
maxDistance = message->maxDistance;
// MSL 5.03 Min Distance
// minDistance = message->minDistance;
itemID = message->itemID;
Check_Object(entityElement);
const LinearMatrix4D &local_to_parent = entityElement->GetNewLocalToParent();
Check_Object(&local_to_parent);
startPoint = local_to_parent;
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);
}
Check_Object(Network::GetInstance());
Connection *connection = Network::GetInstance()->GetConnection(message->inflictingEntityID.connectionID);
Check_Object(connection);
Replicator *replicator;
replicator = connection->FindReplicator(message->inflictingEntityID);
Verify(replicator->IsDerivedFrom(MWObject::DefaultData));
inflictingVehicle = Cast_Object(MWObject*, replicator);
dontHit = inflictingVehicle;
const GameModel *model = GetGameModel();
Check_Object(model);
if(model->trailResource != ResourceID::Null)
{
CreateMissileTrail();
}
splashSize = 0;
splashRadius = message->splashRadius;
percentageOfDamageToDirectHit = message->percentageOfDamageToDirectHit;
minPercentageOfDamageToSphereHit = message->minPercentageOfDamageToSphereHit;
maxPercentageOfDamageToSphereHit = message->maxPercentageOfDamageToSphereHit;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
WeaponMover::~WeaponMover()
{
DESTRUCTOR("WeaponMover");
Check_Object(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
WeaponMover::Reuse(
const CreateMessage *message,
ReplicatorID *base_id
)
{
Check_Pointer(this);
Check_Object(message);
BaseClass::Reuse(message, base_id);
damageAmount = message->damageAmount;
maxDistance = message->maxDistance;
// MSL 5.03 Min Distance
// minDistance = message->minDistance;
Check_Object(entityElement);
const LinearMatrix4D &local_to_parent = entityElement->GetNewLocalToParent();
Check_Object(&local_to_parent);
startPoint = local_to_parent;
inflictingEntityID = message->inflictingEntityID;
Check_Object(Network::GetInstance());
Connection *connection = Network::GetInstance()->GetConnection(message->inflictingEntityID.connectionID);
Check_Object(connection);
Replicator *replicator;
replicator = connection->FindReplicator(message->inflictingEntityID);
Verify(replicator->IsDerivedFrom(MWObject::DefaultData));
inflictingVehicle = Cast_Object(MWObject*, replicator);
dontHit = inflictingVehicle;
const GameModel *model = GetGameModel();
Check_Object(model);
if(model->trailResource != ResourceID::Null)
{
CreateMissileTrail();
}
splashSize = 0;
splashRadius = message->splashRadius;
percentageOfDamageToDirectHit = message->percentageOfDamageToDirectHit;
minPercentageOfDamageToSphereHit = message->minPercentageOfDamageToSphereHit;
maxPercentageOfDamageToSphereHit = message->maxPercentageOfDamageToSphereHit;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void WeaponMover::SentenceToDeathRow()
{
Check_Object(this);
if(trailEffect.GetCurrent())
trailEffect.GetCurrent()->executionState->RequestState(Effect::ExecutionStateEngine::StoppingState);
BaseClass::SentenceToDeathRow();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
int
WeaponMover::GetExecutionSlot()
{
Check_Object(this);
return BuildingExecutionSlot;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
WeaponMover::PreCollisionExecute(Time till)
{
Check_Object(this);
PRECOLLISION_LOGIC("WeaponMover");
if (oldCollisions)
{
worldSpaceVelocity = Motion3D::Identity;
localSpaceVelocity = Motion3D::Identity;
worldSpaceAcceleration = Motion3D::Identity;
localSpaceAcceleration = Motion3D::Identity;
SentenceToDeathRow();
UsePostCollision();
BaseClass::PreCollisionExecute(till);
return;
}
Vector3D distance;
Point3D initial_position;
initial_position = startPoint;
Point3D pointStartThisFrame;
pointStartThisFrame = GetLocalToParent();
distance.Subtract(pointStartThisFrame, initial_position);
Scalar maxDistanceSquared = maxDistance * maxDistance;
// MSL 5.03 Min Distance
// Scalar minDistanceSquared = minDistance * minDistance;
// if(distance.GetLengthSquared() >= maxDistanceSquared)
// {
// Set_Statistic(Executed_WeaponMover_Count, Executed_WeaponMover_Count+1);
// UsePostCollision();
// SentenceToDeathRow();
// return;
// }
//
//----------------------------------------
// Let our base class set up the variables
//----------------------------------------
//
Set_Statistic(Executed_WeaponMover_Count, Executed_WeaponMover_Count+1);
UsePostCollision();
BaseClass::PreCollisionExecute(till);
Check_Object(executionState);
int execution_state = executionState->GetState();
if(execution_state == ExecutionStateEngine::StraightLineMotionState)
{
Point3D current_position;
ElementRenderer::Element *element = GetElement();
current_position = element->GetNewLocalToParent();
distance.Subtract(current_position, initial_position);
//
// We still need to run post collision this frame on anything within our
// range, we can't just skip in the same frame as we went to far so clamp
// the far point.
//
if(distance.GetLengthSquared() >= maxDistanceSquared)
{
//
// Clamp the end point
//
Vector3D direction;
direction.Normalize(distance);
Line3D line;
line.m_origin = initial_position;
line.m_direction = direction;
line.m_length = maxDistance+1.0f;
line.FindEnd(&pointStartThisFrame); // reusing variable
LinearMatrix4D new_local_to_parent;
new_local_to_parent = element->GetNewLocalToParent();
new_local_to_parent.BuildTranslation(pointStartThisFrame);
SetNewLocalToParent(new_local_to_parent);
}
}
BlowUpIfOffMap();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
#if defined(LAB_ONLY)
void WeaponMover::SyncMatrices(bool update_matrix)
{
SYNC_LOGIC("WeaponMover");
BaseClass::SyncMatrices(update_matrix);
}
#endif
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
WeaponMover::CollisionHandler(
Stuff::LinearMatrix4D *new_position,
Stuff::DynamicArrayOf<CollisionData> *collisions
)
{
Check_Object(this);
Check_Object(collisions);
Verify(collisions->GetLength() == 1);
Verify(EntityManager::GetInstance()->IsInPostCollisionExecution(this));
newCollisions = collisions;
Verify(GetInterestLevel() != DormantInterestLevel);
//
//-----------------------
// Time for us to blow up
//-----------------------
//
if (oldCollisions)
SentenceToDeathRow();
else
// make sure we're on the post collision chain
{
Verify(collisions->GetLength() == 1);
CollisionData *data = &(*collisions)[0];
const GameModel *model = GetGameModel();
Check_Object(model);
//
// Make sure we're not past our effective range. This is most important for missiles because burn time
// makes their distance hard to judge,and we want them to not do damage once they run out of fuel.
//
//
Vector3D distance;
Point3D initial_position;
initial_position = startPoint;
Point3D pointStartThisFrame;
pointStartThisFrame = data->m_worldIntersectionPoint;
distance.Subtract(pointStartThisFrame, initial_position);
Scalar maxDistanceSquared = (maxDistance + 1.5f) * (maxDistance + 1.5f);
// MSL 5.03 Min Distance
// Scalar minDistanceSquared = (minDistance + 1.5f) * (minDistance + 1.5f);
//
//---------------------------------
// Create the hit and damage decals
// Do this here since it causes problems in post collision
//---------------------------------
//
//
// We are not going to do ground hit effects if they are under water, but we will if it hits anything else
//
Check_Object (Map::GetInstance());
const Adept::Map__GameModel *mapModel = Map::GetInstance()->GetGameModel();
Check_Object(mapModel);
if(distance.GetLengthSquared() <= maxDistanceSquared)
{
if (data->m_worldIntersectionPoint.y >= ((mapModel->m_waterLevel) -.001f) ||
!data->m_otherEntity ||
!data->m_otherEntity->IsDerivedFrom(Map::DefaultData))
{
CreateHitEffect(
data->m_normal,
data->m_worldIntersectionPoint,
data->m_material
);
if(data->m_otherEntity)
{
if(!(data->m_otherEntity->IsDerivedFrom(Map::DefaultData)))
{
CreateSecondaryHitEffect(
data->m_normal,
data->m_worldIntersectionPoint,
data->m_material,
data->m_otherEntity
);
}
if((data->m_otherEntity->IsDerivedFrom(Map::DefaultData)) &&
(model->doesPlaceCrater))
{
CreateDamageDecal(
data->m_otherEntity,
data->m_normal,
data->m_worldIntersectionPoint
);
if(Compost::TerrainTextureLogistic::Instance)
{
const GameModel *model = GetGameModel();
Check_Object(model);
if(model->m_craterID >= 0)
{
Point3D local_to_world;
local_to_world = GetLocalToWorld();
Compost::TerrainTextureLogistic::Instance->AddFeature(
model->m_craterID, local_to_world.x, local_to_world.z);
}
}
} // if it's the map and we need a crater
else if(data->m_otherEntity->IsDerivedFrom(MWMover::DefaultData))
{
MWMover *hit_mover;
hit_mover = Cast_Object(MWMover *, data->m_otherEntity);
if(hit_mover->GetParentVehicle())
{
if(hit_mover->GetParentVehicle()->IsDerivedFrom(MWObject::DefaultData))
{
MWObject *hit_object;
hit_object = Cast_Object(MWObject *, hit_mover->GetParentVehicle());
const GameModel *model = GetGameModel();
Check_Object(model);
if(hit_object->vehicleInterface)
{
hit_object->vehicleInterface->SetLightAmpFlareOut(model->m_lightAmpFlareOut);
}
}
} // if it's the player mech
} // else (it's an object)
} // if we hit an object (non-map)
} // if we hit nothing (just in case) an object (non-map), or we hit above water
}
//
// if we just hit water then splash and continue
//
initialLocalToParent = GetLocalToWorld();
initialLocalToParent.BuildTranslation(data->m_worldIntersectionPoint);
if (data->m_otherEntity && data->m_material == WaterMaterial)
{
Point3D pointIntersection;
pointIntersection = data->m_worldIntersectionPoint;
Vector3D normalIntersection;
normalIntersection.Multiply(data->m_normal, -.001f);
pointIntersection += normalIntersection;
initialLocalToParent.BuildTranslation(pointIntersection);
delete newCollisions;
newCollisions = NULL;
//
// Take water collision off since we are now done with it.
//
/*
CollisionMask currentMask;
currentMask = GetCollisionMask();
currentMask &= (~Entity::WaterSurfaceFlag);
SetCollisionMask(currentMask);
*/
}
*new_position = initialLocalToParent;
SetNewLocalToParent(*new_position);
lastParameterization = gos_GetElapsedTime();
return true;
}
//
// Moved to actual post collision processing as per JM's request to make sure splash damage works correctly.
//
// else
// PostCollision(collisions);
return false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
WeaponMover::PostCollisionExecute(Stuff::Time till)
{
Check_Object(this);
POSTCOLLISION_LOGIC("WeaponMover");
Vector3D distance;
Point3D initial_position;
initial_position = startPoint;
Point3D pointStartThisFrame;
pointStartThisFrame = GetLocalToParent();
distance.Subtract(pointStartThisFrame, initial_position);
//
// Need to put on a slop factor of 1.5 just in case the last frame went past the max length
// and ended up clamping it to +1, we don't want to not apply damage under those circumstances.
//
Scalar maxDistanceSquared = (maxDistance) * (maxDistance);
Scalar maxDistanceSquaredForDamage = (maxDistance + 1.5f) * (maxDistance + 1.5f);
Scalar distanceSquared = distance.GetLengthSquared();
// MSL 5.03 Min Distance
// Scalar minDistanceSquared = (minDistance) * (minDistance);
// Scalar minDistanceSquaredForDamage = (minDistance + 1.5f) * (minDistance + 1.5f);
//
// [johnyo] According to JM we need to assign the splash damage during
// post collision, or else the splash search may not work.
//
// Don't apply damage if we are past our range.
//
if (distanceSquared >= maxDistanceSquared)
{
//
// There is a slop factor that needs to happen when we clamp the last frame of the motion to the
// max distance + 1.0
//
if (distanceSquared <= maxDistanceSquaredForDamage && newCollisions && !oldCollisions) // this would indicate we just hit things, and it's not time to die
{
PostCollision(newCollisions);
}
BaseClass::PostCollisionExecute(till);
//
// Missiles will continue their path and succomb to gravity and the ground eventually.
//
if (!this->IsDerivedFrom(Missile::DefaultData))
{
SentenceToDeathRow();
}
}
else
{
if (newCollisions && !oldCollisions) // this would indicate we just hit things, and it's not time to die
{
PostCollision(newCollisions);
}
BaseClass::PostCollisionExecute(till);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
WeaponMover::PostCollision(Stuff::DynamicArrayOf<CollisionData> *collisions)
{
Check_Object(this);
POSTCOLLISION_LOGIC("WeaponMover::Explode");
#ifdef _ARMOR // otherwise, this doesn't compile -- PAULTOZ
const GameModel *model = GetGameModel();
Check_Object(model);
#endif
Verify(collisions->GetLength() == 1);
CollisionData *data = &(*collisions)[0];
//This function should be called by all collision handlers that are derived
//From WeaponMover
//
//---------------------------------
// Apply damage to the thing we hit
//---------------------------------
//
if (splashRadius == 0.0f)
{
// MSL 5.06 Armor Mode
MWApplication *m_App;
m_App = MWApplication::GetInstance ();
m_ArmorMode = m_App->GetLocalNetParams()->m_armormodeOn;
Entity::TakeDamageMessage damage_message(
data->m_otherEntity->GetReplicatorID(),
inflictingEntityID,
damageAmount,
m_ArmorMode,
GetDamageType(), //Telling it that I am a projectile
data->m_normal,
data->m_worldIntersectionPoint,
heatAmount,
false,
itemID
);
data->m_otherEntity->Receive(&damage_message);
//data->m_otherEntity->Dispatch(&damage_message);
}
else
{
DealSplashDamage(data);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void WeaponMover::DealSplashDamage(const CollisionData *data)
{
Check_Object(this);
Check_Object(CollisionGrid::Instance);
POSTCOLLISION_LOGIC("WeaponMover::Splash");
extern g_nMR;
if (g_nMR == 2)
return;
Stuff::Sphere sphere;
//
// We need to apply direct damage before checking for splash damage radius. Otherwise if the
// collision info is a little off, or the collision radius is too small, no damage is done
//
// MSL 5.04 Building Missile Damage
// if we hit something deal a set percentage to that part
if (data->m_otherEntity != NULL && data->m_otherEntity != Map::GetInstance())
{
if (data->m_otherEntity->IsDerivedFrom(Building::DefaultData))
{
// MSL 5.06 Armor Mode
MWApplication *m_App;
m_App = MWApplication::GetInstance ();
m_ArmorMode = m_App->GetLocalNetParams()->m_armormodeOn;
Entity::TakeDamageMessage damage_message(
data->m_otherEntity->GetReplicatorID(),
inflictingEntityID,
damageAmount, //Do full damage
m_ArmorMode,
GetDamageType(), //Telling it that I am a projectile
data->m_normal,
data->m_worldIntersectionPoint,
heatAmount,
false,
itemID
);
data->m_otherEntity->Receive(&damage_message);
}
else
{
// MSL 5.06 Armor Mode
MWApplication *m_App;
m_App = MWApplication::GetInstance ();
m_ArmorMode = m_App->GetLocalNetParams()->m_armormodeOn;
Entity::TakeDamageMessage damage_message(
data->m_otherEntity->GetReplicatorID(),
inflictingEntityID,
damageAmount * percentageOfDamageToDirectHit, // Do percentage of full damage
m_ArmorMode,
GetDamageType(), //Telling it that I am a projectile
data->m_normal,
data->m_worldIntersectionPoint,
heatAmount * percentageOfDamageToDirectHit,
false,
itemID
);
data->m_otherEntity->Receive(&damage_message);
}
}
//
// The splash damage has to be applied to the collision location, not the current entity location
// While this is especially true for proximity teleports it's true of slow frame rates as well.
//sphere.center = GetLocalToWorld();
sphere.center = data->m_worldIntersectionPoint;
sphere.radius = splashRadius;
CollisionGrid::Instance->FindEntitiesWithin(sphere, (Adept::CollisionGrid::WithinCallback)SplashCallBack, this);
if (splashSize == 0)
return;
// iterate the list
ChainIteratorOf<Entity *> splashed_entities(&splashedEntities);
//
// First, iterate through the list once to clear the been hit flag so we don't do
// double/or more damage to multiple entities, connected to the same damage zones
//
Entity *hit_entity;
while((hit_entity = splashed_entities.ReadAndNext()) != NULL)
{
if (hit_entity->damageObject)
hit_entity->damageObject->ClearTakenDamageFromThisEvent();
}
// Set the damage object that received direct damage so it won't take more.
if (data->m_otherEntity && data->m_otherEntity->damageObject)
{
data->m_otherEntity->damageObject->SetTakenDamageFromThisEvent();
}
splashed_entities.First();
Scalar splash_damage = damageAmount * (1.0f - percentageOfDamageToDirectHit);
Scalar heat_damage = heatAmount * (1.0f - percentageOfDamageToDirectHit);
while((hit_entity = splashed_entities.GetCurrent()) != NULL)
{
if (hit_entity->damageObject)
{
//
// If there is a damage object verify we aren't hitting it twice
//
if (!hit_entity->damageObject->GetTakenDamageFromThisEvent())
{
hit_entity->damageObject->SetTakenDamageFromThisEvent() ;
}
else
{
splashed_entities.Remove();
continue;
}
}
if (data->m_otherEntity != hit_entity) // don't double deal
{
// MSL 5.06 Armor Mode
MWApplication *m_App;
m_App = MWApplication::GetInstance ();
m_ArmorMode = m_App->GetLocalNetParams()->m_armormodeOn;
Verify(splashSize != 0);
Adept::Entity__TakeDamageMessage message(
hit_entity->GetReplicatorID(),
inflictingEntityID,
splash_damage,
m_ArmorMode,
SplashDamageType,
data->m_normal,
data->m_worldIntersectionPoint,
heat_damage,
false,
itemID
);
hit_entity->Receive(&message);
}
splashed_entities.Remove();
}
splashSize = 0;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void WeaponMover::SplashCallBack(Entity *entity, Entity *original_caller, const Stuff::Sphere &sphere)
{
Check_Object(entity);
Check_Object(original_caller);
WeaponMover *weapon = Cast_Object( WeaponMover*, original_caller);
if (!entity->IsDestroyed())
{
CollisionVolume *volume = entity->GetHierarchicalVolume();
if (volume)
{
volume->FindSolidsWithin(
sphere,
(Adept::CollisionVolume::WithinCallback)SubSplashCallBack,
weapon,
entity,
entity->GetLocalToWorld()
);
}
else
{
weapon->splashedEntities.Add(entity);
++weapon->splashSize;
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void WeaponMover::SubSplashCallBack(CollisionVolume *solid, Entity *original_caller, const Stuff::Sphere &sphere)
{
Check_Object(solid);
Check_Object(original_caller);
WeaponMover *weapon = Cast_Object( WeaponMover*, original_caller);
weapon->splashedEntities.Add(solid->m_entity);
++weapon->splashSize;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
WeaponMover::CreateHitEffect(
Normal3D normal,
Point3D target_point,
BYTE hit_material
)
{
Check_Object(this);
const GameModel *model = GetGameModel();
Check_Object(model);
ResourceID hit_effect_resource_id = ResourceID::Null;
if((model->hitEffectMaterialTable != ResourceID::Null) &&
(hit_material >= 0) && (hit_material < MaterialCount))
{
Entity__GameModel::GetEffectResourceID(&hit_effect_resource_id,
model->hitEffectMaterialTable, hit_material
);
}
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(normal, Y_Axis, X_Axis, Z_Axis);
effect_position.BuildTranslation(target_point);
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);
Check_Object(entity);
Check_Object(Map::GetInstance());
Map::GetInstance()->AddChild(entity);
entity->SyncMatrices(true);
// Sound stuff here...
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
WeaponMover::CreateSecondaryHitEffect(
Normal3D normal,
Point3D target_point,
BYTE hit_material,
Entity *entity_hit
)
{
Check_Object(this);
const GameModel *model = GetGameModel();
Check_Object(model);
ResourceID hit_effect_resource_id = ResourceID::Null;
if((model->secondaryHitEffectMaterialTable != ResourceID::Null) &&
(hit_material >= 0) && (hit_material < MaterialCount))
{
Entity__GameModel::GetEffectResourceID(&hit_effect_resource_id,
model->secondaryHitEffectMaterialTable, hit_material
);
}
else
{
hit_effect_resource_id = model->secondaryHitEffectResourceID;
}
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(normal, Y_Axis, X_Axis, Z_Axis);
effect_position.BuildTranslation(target_point);
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);
Effect *entity;
ReplicatorID beam_id = Connection::Hermit->GetNextReplicatorID();
entity = Cast_Object(Effect *, CreateEntity(&stream, &beam_id, false));
Check_Object(entity);
Check_Object(Map::GetInstance());
Map::GetInstance()->AddChild(entity);
entity->SyncMatrices(true);
if(entity_hit)
{
// We need the local offset from the entity for where we hit.
LinearMatrix4D entity_hit_world_to_local;
entity_hit_world_to_local = entity_hit->GetLocalToWorld();
entity_hit_world_to_local.Invert();
LinearMatrix4D target_point_local_to_world(target_point);
LinearMatrix4D target_point_local_to_entity;
target_point_local_to_entity.Multiply(target_point_local_to_world, entity_hit_world_to_local);
entity->SetFollowEntity(entity_hit);
entity->PlaceOnEntity(target_point_local_to_entity);
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
WeaponMover::CreateDamageDecal(
Entity *hit_entity,
Normal3D normal,
Point3D target_point
)
{
Check_Object(this);
Check_Object(Map::GetInstance());
const Map__GameModel *model = Map::GetInstance()->GetGameModel();
Check_Object(model);
if (model->m_damageCraterResourceID != ResourceID::Null)
{
Resource damage_effect_resource(model->m_damageCraterResourceID);
Verify(damage_effect_resource.DoesResourceExist());
ClassID class_ID;
class_ID = GetClassIDFromDataListID(model->m_damageCraterResourceID);
Verify(class_ID != NullClassID);
LinearMatrix4D effect_position = LinearMatrix4D::Identity;
effect_position.AlignLocalAxisToWorldVector(normal, Y_Axis, X_Axis, Z_Axis);
effect_position.BuildTranslation(target_point);
DeathEntity::CreateMessage decal_create_message(
sizeof(DeathEntity::CreateMessage),
DefaultEventPriority,
CreateMessage::DefaultFlags,
class_ID,
DeathEntity::DefaultFlags,
model->m_damageCraterResourceID,
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(hit_entity);
hit_entity->AddChild(entity);
entity->SyncMatrices(true);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
WeaponMover::BlowUpIfOffMap()
{
Check_Object(this);
//
//--------------------------------------------------
//Need to check to see if we are moving off the edge
//--------------------------------------------------
//
ElementRenderer::Element *element = GetElement();
Check_Object(element);
Point3D new_translation;
new_translation = element->GetNewLocalToParent();
Check_Object(Map::GetInstance());
if(Map::GetInstance()->FindZone(new_translation) == -1 || HasTriedToMoveOffMapThisFrameFlag())
{
LinearMatrix4D current_local_to_world = LinearMatrix4D::Identity;
current_local_to_world = element->GetLocalToWorld();
SetNewLocalToParent(current_local_to_world);
SentenceToDeathRow();
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
WeaponMover::CreateMissileTrail()
{
Check_Pointer(this);
const GameModel *model = GetGameModel();
Check_Object(model);
Resource trail_resource(model->trailResource);
if(trail_resource.DoesResourceExist())
{
ClassID class_ID;
class_ID = GetClassIDFromDataListID(model->trailResource);
Verify(class_ID != NullClassID);
LinearMatrix4D initial_position = LinearMatrix4D::Identity;
initial_position.Multiply(gosFX::Effect_Against_Motion, GetElement()->GetNewLocalToParent());
Effect::CreateMessage effect_create_message(
sizeof(Effect::CreateMessage),
DefaultEventPriority,
CreateMessage::DefaultFlags,
class_ID,
Effect::DefaultFlags|Effect::LoopFlag,
model->trailResource,
initial_position,
0.0f,
ExecutionStateEngine::AlwaysExecuteState,
NameTable::NullObjectID,
Entity::DefaultAlignment
);
MemoryStream stream(&effect_create_message, effect_create_message.messageLength);
Entity *entity;
ReplicatorID trail_id = Connection::Hermit->GetNextReplicatorID();
entity = CreateEntity(&stream, &trail_id, false);
Check_Object(entity);
Check_Object(Map::GetInstance());
Map::GetInstance()->AddChild(entity);
trailEffect.Remove();
trailEffect.Add(Cast_Object(Effect *, entity));
trailEffect.GetCurrent()->SetFollowEntity(this);
trailEffect.GetCurrent()->executionState->RequestState(Effect::ExecutionStateEngine::RunningState);
entity->SyncMatrices(true);
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
char
WeaponMover::GetDamageType()
{
Check_Object(this);
if((itemID >= FirstBeamID) &&
(itemID <= LastBeamID))
return BeamDamageType;
if((itemID >= FirstMissileID) &&
(itemID <= LastMissileID))
return MissileDamageType;
return ProjectileDamageType;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
WeaponMover::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}
void MechWarrior4::WeaponMoverSecurityCheckStop()
{
_asm
{
nop
}
}