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.
434 lines
12 KiB
C++
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));
|
|
}
|