#include "MW4Headers.hpp" #include "BeamEntity.hpp" #include #include "MWPlayer.hpp" #include "VehicleInterface.hpp" #include #include #include "MWMover.hpp" #include "MWObject.hpp" #include #include "Decal.hpp" #include "MWDamageObject.hpp" #include #include #include #include #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)); }