#include "munga.h" #pragma hdrstop #include "explode.h" #include "mover.h" #include "boxsolid.h" #include "collasst.h" #include "damage.h" #include "random.h" #include "app.h" #include "objstrm.h" #include "hostmgr.h" #include "notation.h" #include "namelist.h" //############################################################################# //############################ Explosion ################################ //############################################################################# //############################################################################# // Shared Data Support // Derivation* Explosion::GetClassDerivations() { static Derivation classDerivations(Entity::GetClassDerivations(), "Explosion"); return &classDerivations; } Explosion::SharedData Explosion::DefaultData( Explosion::GetClassDerivations(), Explosion::GetMessageHandlers(), Explosion::GetAttributeIndex(), Explosion::StateCount, (Entity::MakeHandler)Explosion::Make ); void Explosion::ExplosionSimulation(Scalar) { Check(application); if ((Now() - creationTime) > explosionDuration) { CondemnToDeathRow(); } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void Explosion::SplashDamage ( EntityID inflicting, Damage &damage, Scalar collisionRadius, Vector3D offset, Entity * excluded) // Excluded should really be a set of entities! { Check(this); Check(entityHit); if (excluded) { Check(excluded); } Check_Pointer(&damage); Verify(0 < collisionRadius); // // The list of volumes we intersect // BoxedSolidCollisionList collision_list; collision_list.Reset(); // // Create the collision assistant to check movers against // CollisionAssistant *collision_assistant; collision_assistant = new CollisionAssistant(entityHit->GetInterestZoneID()); Register_Object(collision_assistant); Check(collision_assistant); BoxedSolidResource box; Check_Pointer(&box); box.solidType = BoxedSolid::YAxisCylinderType; box.materialType = BoxedSolid::ConcreteMaterial; const Vector3D min = Vector3D(-collisionRadius,-collisionRadius,-collisionRadius); const Vector3D max = Vector3D(collisionRadius,collisionRadius,collisionRadius); ExtentBox ext_box = ExtentBox(min,max); box.solidExtents = ext_box; box.sliceExtents = ext_box; BoxedSolidResource local_box; local_box.Instance(box,localOrigin); BoxedSolid *splash_volume = BoxedSolid::MakeBoxedSolid(&local_box, this, NULL); Register_Object(splash_volume); // //--------------------------------- // Test against the tangible movers //--------------------------------- // Check(collision_assistant); CollisionAssistant::MovingEntityIterator iterator(collision_assistant); Entity *entity; Mover *mover; Check(splash_volume); while ((entity = iterator.ReadAndNext()) != NULL) { // //------------------------------------------------------------------ // If we are checking against ourselves, skip it //------------------------------------------------------------------ // Check(entity); if (entity == this) { continue; } // //-------------------------------------------------- // If we have a mover class object, check against it //-------------------------------------------------- // if (entity->IsDerivedFrom(*Mover::GetClassDerivations())) { mover = Cast_Object(Mover *,entity); Check(mover); mover->CheckVolumeAgainstBoxedSolidChain(&collision_list, splash_volume); } } // //------------------------------------------------------------------------- // Test against the static world //------------------------------------------------------------------------- // InterestManager *interest_mgr = application->GetInterestManager(); Check(interest_mgr); InterestZone *zone = interest_mgr->GetInterestZone(interestZoneID); Check(zone); BoxedSolidTree* tree = zone->GetCollisionRoot(); Check(tree); tree->FindBoundingBoxesContaining( splash_volume, collision_list ); Check_Fpu(); Unregister_Object(collision_assistant); delete collision_assistant; Unregister_Object(splash_volume); delete splash_volume; // //------------------------------------------------------------------------- // Make a sorted list of all the entities from the collision list, // sorted by their distance from the explosion's origin. //------------------------------------------------------------------------- // VChainOf radius_chain(NULL,False); VChainIteratorOf radius_chain_iterator(radius_chain); Vector3D radius; Entity *target_entity; // for(int i = 0;i < collision_list.GetCollisionCount(); i++) { BoxedSolidCollision bsc = collision_list[i]; Check(&bsc); target_entity = (Entity*) ((bsc.GetTreeVolume())->GetOwningSimulation()); Check(target_entity); Verify(target_entity->IsDerivedFrom(Entity::GetClassDerivations())); if ((target_entity != entityHit) && (target_entity != this) && (target_entity != excluded)) { radius = Vector3D::Identity; radius.Subtract(target_entity->localOrigin.linearPosition,localOrigin.linearPosition); radius_chain.AddValue(target_entity, radius); } } // While remaining_bursts > 0, advance through list, pick a number of bursts // to inflict, and call SendDamage to the current entity. Damage some_damage = damage; int possible_targets = 0; int damageable_targets = 0; radius_chain_iterator.First(); while ((target_entity = (Entity*) radius_chain_iterator.GetCurrent()) != NULL) { Check(target_entity); radius = radius_chain_iterator.GetValue(); radius_chain_iterator.Next(); possible_targets++; if (target_entity->damageZones) { damageable_targets++; // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Set the damage data //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // some_damage.burstCount =damage.burstCount / (pow(radius.Length(), 1.2f)); Min_Clamp(some_damage.burstCount,1); #if DEBUG_LEVEL > 0 DEBUG_STREAM << "At radius " << radius.Length() << ", applied burstCount is " << some_damage.burstCount << std::endl << std::flush; DEBUG_STREAM << "Damage per burst is " << some_damage.damageAmount << std::endl << std::flush; #endif Vector3D force = Vector3D::Identity; force.Multiply(radius,localToWorld); some_damage.damageForce = force; Point3D origin_point = Point3D::Identity; origin_point.Add(localOrigin.linearPosition,offset); some_damage.impactPoint = origin_point; //some_damage.impactPoint.Add(origin_point,radius); // //------------------------------------------ // Send a damage message to the current target entity //------------------------------------------ // if (target_entity->GetEntityID() == inflicting) { DEBUG_STREAM << "Splash damage hurting inflicting entity." << std::endl << std::flush; } if (excluded && target_entity->GetEntityID() == excluded->GetEntityID() ) { DEBUG_STREAM << "Splash damage hurting excluded entity!" << std::endl << std::flush; } Entity::TakeDamageMessage take_damage( Entity::TakeDamageMessageID, sizeof(Entity::TakeDamageMessage), inflicting, -1, // We can't know what damage zone we hit some_damage ); target_entity->Dispatch(&take_damage); } } #if DEBUG_LEVEL >0 DEBUG_STREAM << "There were " << possible_targets << "entities within range, only " << damageable_targets << " had damage zones" << std::endl << std::flush; #endif Check_Fpu(); } //############################################################################# // Construction and Destruction // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Explosion::Explosion( Explosion::MakeMessage *creation_message, Explosion::SharedData &virtual_data ): Entity(creation_message, virtual_data) { Check_Pointer(this); Check_Pointer(creation_message); // //~~~~~~~~~~~~~~~~~~~~~~~ // zero out any rotation //~~~~~~~~~~~~~~~~~~~~~~~ // localOrigin.angularPosition = Quaternion::Identity; // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Get the explosion model resource //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ResourceDescription *resource_description; ModelResource *model_resource; Check(application); Check(application->GetResourceFile()); resource_description = application->GetResourceFile()->SearchList( resourceID, ResourceDescription::GameModelResourceType ); Check(resource_description); resource_description->Lock(); model_resource = (ModelResource*)resource_description->resourceAddress; Check_Pointer(model_resource); // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Get the creating entity and entity hit by explosion //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Check(application->GetHostManager()); entityHit = application->GetHostManager()->GetEntityPointer( creation_message->entityHitID ); Check(entityHit); if (creation_message->creatingEntityID != EntityID::Null) { creatingEntity = application->GetHostManager()->GetEntityPointer( creation_message->creatingEntityID ); Check(creatingEntity); } else { creatingEntity = NULL; } // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Calculate the localHitPosition in terms // local coordinate system of the entityHit //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // localHitPosition.MultiplyByInverse( localOrigin.linearPosition, entityHit->localToWorld ); // //~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Get the explosion duration //~~~~~~~~~~~~~~~~~~~~~~~~~~~ // explosionDuration = model_resource->explosionDuration; #if 0 // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // If an explosion resource table is being used then // use it to set the resource //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // if (model_resource->resourceTableObjectID != NullObjectID) { // // If the explosion hit us, set the material to our craft, // If the explosion hit an opponent, set the material to other craft, // If the explosion hit something else then get the material... // Enumeration material_type; Check(application); Entity *viewpoint_entity = application->GetViewpointEntity(); Check(viewpoint_entity); Verify(creation_message->entityHitID != EntityID::Null); // ECH 8/25/95 - Is this the best way to get our vehicle? if ( creation_message->entityHitID == viewpoint_entity->GetEntityID() ) { material_type = BoxedSolid::OurCraftMaterial; } else { // ECH 8/25/95 - Is this the best way to identify other vehicles? if ( entityHit != NULL && entityHit->GetPlayerLink() != NULL ) { material_type = BoxedSolid::OtherCraftMaterial; } else { // ECH 8/25/95 - How to get material? Just use this for now. material_type = BoxedSolid::ConcreteMaterial; } } // // Get resource table pointer, // Then set the appropriate resource ID // Plug *plug; ExplosionResourceTable *resource_table; ResourceDescription::ResourceID new_resource_ID; plug = PlugStreamManager::FindPlug(model_resource->resourceTableObjectID); resource_table = Cast_Object(ExplosionResourceTable*, plug); Check(resource_table); new_resource_ID = resource_table->FindResourceID(material_type); if (new_resource_ID != ResourceDescription::NullResourceID) { resourceID = new_resource_ID; } } #else // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // If an explosion resource table is being used then // use it to set the resource //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // if (model_resource->resourceTableObjectID != NullObjectID) { // // Get resource table pointer, // Then set the appropriate resource ID // Plug *plug; ExplosionResourceTable *resource_table; ResourceDescription::ResourceID new_resource_ID; plug = PlugStreamManager::FindPlug(model_resource->resourceTableObjectID); resource_table = Cast_Object(ExplosionResourceTable*, plug); Check(resource_table); new_resource_ID = resource_table->FindResourceID(entityHit); if (new_resource_ID != ResourceDescription::NullResourceID) { resourceID = new_resource_ID; } } #endif // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Set performance and entity validity //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // resource_description->Unlock(); SetPerformance(&Explosion::ExplosionSimulation); SetValidFlag(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Explosion* Explosion::Make(Explosion::MakeMessage *creation_message) { Check(application); HostManager *host_mgr = application->GetHostManager(); Check(host_mgr); Entity *entity = host_mgr->GetEntityPointer(creation_message->entityHitID); if (entity) { return new Explosion(creation_message); } Tell( "Warning: Explosion ignored - entity " << creation_message->entityHitID << " couldn't be found!\n" ); return NULL; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Explosion::~Explosion() { } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Logical Explosion::TestInstance() const { return IsDerivedFrom(*GetClassDerivations()); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // CreateModelResource // ResourceDescription::ResourceID Explosion::CreateModelResource( ResourceFile *resource_file, const char *model_name, NotationFile *model_file, const ResourceDirectories* ) { Check(resource_file); Check_Pointer(model_name); Check(model_file); ModelResource local_model; if( !model_file->GetEntry( "gamedata", "ExplosionDuration", &local_model.explosionDuration ) ) { std::cerr<GetEntry( "gamedata", "ExplosionResourceTable", &resource_table_name ) ) { // // Get the object ID and set it in the model resource // local_model.resourceTableObjectID = PlugStreamManager::FindPlugName(resource_table_name); if (local_model.resourceTableObjectID == NullObjectID) { std::cout << "resource_table_name == " << resource_table_name << "\n"; Fail("Explosion::CreateModelResource - ExplosionResourceTable not found"); } Verify(local_model.resourceTableObjectID != NullObjectID); } else { local_model.resourceTableObjectID = NullObjectID; } ResourceDescription *resource_description = resource_file->AddResource( model_name, ResourceDescription::GameModelResourceType, 1, ResourceDescription::Preload, &local_model, sizeof(ModelResource) ); Check(resource_description); return resource_description->resourceID; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Enumeration Explosion::GetInterestPriority(Entity *linked_entity) { Check(this); Check(linked_entity); return ( (linked_entity == creatingEntity) ? HighInterestPriority : LowInterestPriority ); } //############################################################################# //###################### ExplosionResourceTable ######################### //############################################################################# // //############################################################################# //############################################################################# // ExplosionResourceTable::ExplosionResourceTable(PlugStream *stream): Plug(stream), resourceIDSocket(NULL, True) { Check(stream); Check(&resourceIDSocket); CollectionSize table_size; *stream >> table_size; for (CollectionSize i = 0; i < table_size; i++) { Enumeration material_type; ResourceDescription::ResourceID resource_ID; ResourceIDPlug *resource_ID_Plug; *stream >> material_type >> resource_ID; resource_ID_Plug = new ResourceIDPlug(resource_ID); Register_Object(resource_ID_Plug); resourceIDSocket.AddValue(resource_ID_Plug, material_type); } } // //############################################################################# //############################################################################# // ExplosionResourceTable::~ExplosionResourceTable() { TableIteratorOf iterator(&resourceIDSocket); Check(&iterator); iterator.DeletePlugs(); } // //############################################################################# //############################################################################# // void ExplosionResourceTable::BuildFromPage( ObjectStream *stream, NameList *name_list, ClassID class_ID, ObjectID object_ID ) { Check(stream); Check(name_list); Plug::BuildFromPage(stream, name_list, class_ID, object_ID); // //-------------------------------------------------------------------------- // Write number of entries //-------------------------------------------------------------------------- // CollectionSize table_size = 0; NameList::Entry *entry; Check(name_list); entry = name_list->GetFirstEntry(); while (entry != NULL) { Check(entry); if (entry->IsName("table_entry")) table_size++; entry = entry->GetNextEntry(); } *stream << table_size; // //-------------------------------------------------------------------------- // Write entries //-------------------------------------------------------------------------- // #if DEBUG_LEVEL>0 CollectionSize i = 0; #endif Check(name_list); entry = name_list->GetFirstEntry(); while (entry != NULL) { Check(entry); if (entry->IsName("table_entry")) { CString table_entry, token; Enumeration material_type; ResourceDescription *resource_description; ResourceDescription::ResourceID resource_ID; Check_Pointer(entry->GetChar()); table_entry = entry->GetChar(); // // Material // Check_Pointer(table_entry.GetNthToken(0)); material_type = atol(table_entry.GetNthToken(0)); // // Resource // Check_Pointer(table_entry.GetNthToken(1)); Check(stream->GetResourceFile()); resource_description = stream->GetResourceFile()->FindResourceDescription( table_entry.GetNthToken(1), ResourceDescription::ModelListResourceType ); Check(resource_description); resource_ID = resource_description->resourceID; *stream << material_type << resource_ID; #if DEBUG_LEVEL>0 i++; #endif } entry = entry->GetNextEntry(); } #if DEBUG_LEVEL>0 Verify(i == table_size); #endif } // //############################################################################# //############################################################################# // Logical ExplosionResourceTable::TestInstance() const { Plug::TestInstance(); Check(&resourceIDSocket); return True; } // //############################################################################# //############################################################################# // ResourceDescription::ResourceID ExplosionResourceTable::FindResourceID(Enumeration material_type) { Check(this); ResourceIDPlug *resource_ID_Plug; if ((resource_ID_Plug = resourceIDSocket.Find(material_type)) != NULL) { Check(resource_ID_Plug); return resource_ID_Plug->GetItem(); } return ResourceDescription::NullResourceID; } // //############################################################################# //############################################################################# // ResourceDescription::ResourceID ExplosionResourceTable::FindResourceID(Entity *entity_hit) { Check(this); Check(entity_hit); // // If the explosion hit us, set the material to our craft, // If the explosion hit an opponent, set the material to other craft, // If the explosion hit something else then get the material... // Enumeration material_type; Entity *viewpoint_entity; Check(application); viewpoint_entity = application->GetViewpointEntity(); Check(viewpoint_entity); // ECH 8/25/95 - Is this the best way to get our vehicle? if (entity_hit->GetEntityID() == viewpoint_entity->GetEntityID()) { material_type = BoxedSolid::OurCraftMaterial; } // ECH 8/25/95 - Is this the best way to identify other vehicles? else if (entity_hit->GetPlayerLink() != NULL) { material_type = BoxedSolid::OtherCraftMaterial; } else { // ECH 8/25/95 - How to get material? Just use this for now. material_type = BoxedSolid::ConcreteMaterial; } // // Return the appropriate resource ID // return FindResourceID(material_type); }