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

434 lines
12 KiB
C++

#include "MW4Headers.hpp"
#include <Adept\Effect.hpp>
#include <Adept\Map.hpp>
#include "StickyMover.hpp"
#include <Adept\EntityManager.hpp>
//#############################################################################
//########################## ExecutionStateEngine #######################
//#############################################################################
StickyMover::ExecutionStateEngine::ClassData*
StickyMover::ExecutionStateEngine::DefaultData = NULL;
const StateEngine::StateEntry
StickyMover::ExecutionStateEngine::StateEntries[]=
{
STATE_ENTRY(StickyMover__ExecutionStateEngine, StickyExecution)
};
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
StickyMover::ExecutionStateEngine::InitializeClass()
{
Check_Object(StateEngine::DefaultData);
Verify(!DefaultData);
DefaultData =
new ClassData(
StickyMover__ExecutionStateEngineClassID,
"StickyMover::ExecutionStateEngine",
BaseClass::DefaultData,
ELEMENTS(StateEntries), StateEntries,
(Entity::ExecutionStateEngine::Factory)Make,
(Entity::ExecutionStateEngine::FactoryRequest::Factory)
&FactoryRequest::ConstructFactoryRequest
);
Register_Object(DefaultData);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
StickyMover::ExecutionStateEngine::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
StickyMover::ExecutionStateEngine*
StickyMover::ExecutionStateEngine::Make(
StickyMover *weapon,
FactoryRequest *request
)
{
Check_Object(weapon);
Check_Object(request);
gos_PushCurrentHeap(Heap);
StickyMover::ExecutionStateEngine *engine =
new StickyMover::ExecutionStateEngine(DefaultData, weapon, request);
gos_PopCurrentHeap();
return engine;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
StickyMover::ExecutionStateEngine::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}
//#############################################################################
//############################### StickyMover ############################
//#############################################################################
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
StickyMover::ClassData*
StickyMover::DefaultData = NULL;
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
StickyMover::InitializeClass()
{
Check_Object(StickyMover::ExecutionStateEngine::DefaultData);
Verify(!DefaultData);
DefaultData =
new ClassData(
StickyMoverClassID,
"MechWarrior4::StickyMover",
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);
DIRECT_GAME_MODEL_ATTRIBUTE(
StickyMover__GameModel,
ActiveTime,
activeTime,
Scalar
);
DIRECT_GAME_MODEL_ATTRIBUTE(
StickyMover__GameModel,
IdleLoopResource,
idleLoopResource,
ResourceID
);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
StickyMover::TerminateClass()
{
Unregister_Object(DefaultData);
delete DefaultData;
DefaultData = NULL;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
StickyMover*
StickyMover::Make(
CreateMessage *message,
ReplicatorID *base_id
)
{
Check_Object(message);
gos_PushCurrentHeap(Heap);
Check_Object(EntityManager::GetInstance());
StickyMover *new_entity;
Entity *entity = EntityManager::GetInstance()->RequestFromArmory(message->dataListID);
if (entity)
{
new_entity = Cast_Object(StickyMover*, entity);
new_entity->Reuse(message, base_id);
}
else
new_entity = new StickyMover(DefaultData, message, base_id, NULL);
gos_PopCurrentHeap();
Check_Object(new_entity);
return new_entity;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
StickyMover::StickyMover(
ClassData *class_data,
CreateMessage *message,
ReplicatorID *base_id,
ElementRenderer::Element *element
):
WeaponMover(class_data, message, base_id, element),
attachedEntity(this),
idleEffect(NULL)
{
Check_Pointer(this);
Check_Object(message);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
StickyMover::~StickyMover()
{
DESTRUCTOR("StickyMover");
Check_Object(this);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void StickyMover::SentenceToDeathRow()
{
Check_Object(this);
if(idleEffect.GetCurrent() != NULL)
{
idleEffect.GetCurrent()->SentenceToDeathRow();
}
BaseClass::SentenceToDeathRow ();
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
StickyMover::PreCollisionExecute(Time till)
{
Check_Object(this);
PRECOLLISION_LOGIC("Sticky Mover");
//MUST SKIP WEAPONMOVE PRE COLLISION SO COPY THIS STUFF HERE!
Vector3D distance;
Point3D initial_position;
initial_position = initialLocalToParent;
Point3D pointStartThisFrame;
pointStartThisFrame = GetLocalToParent();
distance.Subtract(pointStartThisFrame, initial_position);
Scalar maxDistanceSquared = maxDistance * maxDistance;
Set_Statistic(Executed_WeaponMover_Count, Executed_WeaponMover_Count+1);
UsePostCollision();
Mover::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();
//
//----------------------------------------------------
// Make sure we use post collision and call our parent
//----------------------------------------------------
//
switch(execution_state)
{
case ExecutionStateEngine::StickyExecutionState:
{
const GameModel *model = GetGameModel();
Check_Object(model);
if((GetTimeParameter(till) >= model->activeTime) ||
(attachedEntity.GetCurrent() == NULL) ||
(attachedEntity.GetCurrent()->IsDestroyed()))
{
SentenceToDeathRow();
}
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
bool
StickyMover::CollisionHandler(
Stuff::LinearMatrix4D *new_position,
Stuff::DynamicArrayOf<CollisionData> *collisions
)
{
Check_Object(this);
Check_Object(collisions);
Verify(GetInterestLevel() != DormantInterestLevel);
Verify(EntityManager::GetInstance()->IsInPostCollisionExecution(this));
//
//----------------------------------------------
//This is where we need to start the follow code
//----------------------------------------------
//
CollisionData *data = &(*collisions)[0];
Check_Pointer(data);
Check_Object(executionState);
executionState->RequestState(ExecutionStateEngine::StickyExecutionState);
*new_position = LinearMatrix4D::Identity;
new_position->AlignLocalAxisToWorldVector(data->m_normal, Y_Axis, X_Axis, Z_Axis);
new_position->BuildTranslation(data->m_worldIntersectionPoint);
SetNewLocalToParent(*new_position);
lastParameterization = gos_GetElapsedTime();
attachedEntity.Remove();
attachedEntity.Add(data->m_otherEntity);
SetFollowing();
entityOffset.MultiplyByInverse(data->m_worldIntersectionPoint, data->m_otherEntity->GetLocalToWorld());
if(trailEffect.GetCurrent())
trailEffect.GetCurrent()->executionState->RequestState(Effect::ExecutionStateEngine::StoppingState);
OnActiveTimeStart();
//
//--------------------------------------------------
//This is so the Narc will not block the target Mech
//--------------------------------------------------
//
SetCollisionMask(NeverCollidesMask);
CreateIdleEffect();
PostCollision(collisions);
Entity::ExecutionStateEngine *engine = executionState;
if (engine->GetState() == ExecutionStateEngine::NeverExecuteState)
{
Verify(!newCollisions);
delete collisions;
}
else
newCollisions = collisions;
return false;
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
StickyMover::PostCollisionExecute(Time till)
{
Check_Object(this);
POSTCOLLISION_LOGIC("StickyMover");
Check_Object(executionState);
int execution_state = executionState->GetState();
switch(execution_state)
{
case ExecutionStateEngine::StickyExecutionState:
{
Entity *attached_entity;
attached_entity = attachedEntity.GetCurrent();
if(attached_entity)
{
Check_Object(attached_entity);
LinearMatrix4D new_position;
Point3D new_location;
LinearMatrix4D entity_location;
UnitQuaternion rotation;
rotation = GetLocalToWorld();
entity_location = attached_entity->GetLocalToWorld();
new_location.Multiply(entityOffset, entity_location);
new_position.BuildTranslation(new_location);
new_position.BuildRotation(rotation);
SetNewLocalToParent(new_position);
SyncMatrices(true);
}
else
{
SentenceToDeathRow();
}
break;
}
}
BaseClass::PostCollisionExecute(till);
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
StickyMover::CreateIdleEffect()
{
Check_Object(this);
const GameModel *model = GetGameModel();
Check_Object(model);
if(model->idleLoopResource != ResourceID::Null)
{
Resource idle_resource(model->idleLoopResource);
if(idle_resource.DoesResourceExist())
{
ClassID class_ID;
class_ID = GetClassIDFromDataListID(model->idleLoopResource);
Verify(class_ID != NullClassID);
Effect::CreateMessage effect_create_message(
sizeof(Effect::CreateMessage),
DefaultEventPriority,
CreateMessage::DefaultFlags,
class_ID,
Effect::DefaultFlags|Effect::LoopFlag,
model->idleLoopResource,
GetElement()->GetNewLocalToParent(),
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);
Effect *effect = Cast_Object(Effect *, entity);
idleEffect.Add(effect);
effect->SetFollowEntity(this);
effect->executionState->RequestState(Effect::ExecutionStateEngine::RunningState);
entity->SyncMatrices(true);
}
}
}
//~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
//
void
StickyMover::TestInstance() const
{
Verify(IsDerivedFrom(DefaultData));
}