//===========================================================================// // 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 #include #include #include #include #include #include #include "MWDamageObject.hpp" #include #include #include "mwmission.hpp" #include "bucket.hpp" #include "MWApplication.hpp" #include //############################################################################# //############################### 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 *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 *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 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 } }