#include "rp.h" #pragma hdrstop #include "vtv.h" #include "vtvmppr.h" #include "vtvpwr.h" #include "weapsys.h" #include "thruster.h" #include "booster.h" #include "chute.h" #include "rpplayer.h" #include "..\munga\door.h" #include "..\munga\line.h" #include "..\munga\random.h" #include "..\munga\fileutil.h" #include "..\munga\boxsolid.h" #include "..\munga\app.h" #include "..\munga\hostmgr.h" #include "..\munga\namelist.h" //############################################################################# // Shared Data Support // Derivation* VTV::GetClassDerivations() { static Derivation classDerivations(JointedMover::GetClassDerivations(), "VTV"); return &classDerivations; } VTV::SharedData VTV::DefaultData( VTV::GetClassDerivations(), VTV::MessageHandlers, VTV::GetAttributeIndex(), VTV::StateCount, (Entity::MakeHandler)VTV::Make ); //############################################################################# // Message Support // const VTV::HandlerEntry VTV::MessageHandlerEntries[]= { MESSAGE_ENTRY(VTV, TakeDamage), MESSAGE_ENTRY(VTV, Kavorkian), MESSAGE_ENTRY(VTV, MarkSpot), MESSAGE_ENTRY(VTV, RestoreToSpot), MESSAGE_ENTRY(VTV, ZoomIn), MESSAGE_ENTRY(VTV, ZoomOut), MESSAGE_ENTRY(VTV, PlayerLink) }; VTV::MessageHandlerSet VTV::MessageHandlers( ELEMENTS(VTV::MessageHandlerEntries), VTV::MessageHandlerEntries, JointedMover::GetMessageHandlers() ); //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VTV::PlayerLinkMessageHandler(PlayerLinkMessage *message) { Entity::PlayerLinkMessageHandler(message); RPPlayer *rp_player = Cast_Object(RPPlayer*, playerLink); rp_player->SetPlayerVehicle(this); // //---------------------------------------------------------------------- // If our player is a runner, add two boosters to each booster subsystem //---------------------------------------------------------------------- // switch (rp_player->playerType) { case RPPlayer::RunnerPlayerType: { Check_Pointer(subsystemArray); for (int i=0; iGetClassID() == BoosterClassID) { Booster *booster = (Booster*)subsystemArray[i]; booster->maxBoosters += 2; booster->boostersRemaining += 2; } } } } goto Is_A_Scorer; case RPPlayer::GeneralPlayerType: case RPPlayer::CrusherPlayerType: case RPPlayer::BlockerPlayerType: Is_A_Scorer: rp_player->SetScoringPlayerFlag(); break; } Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VTV::TakeDamageMessageHandler(TakeDamageMessage *message) { Check(this); Check(message); // //------------------------------------------------- // Check If the damage was caused by another Player //------------------------------------------------- // Check(application); HostManager *host = application->GetHostManager(); Check(host); Entity *offensive_entity = Cast_Object( Entity*, host->GetEntityPointer(message->inflictingEntity) ); // //------------------------------------ // make sure we can enter something... //------------------------------------ // if (offensive_entity) { Check(offensive_entity); RPPlayer *offensive_player = (RPPlayer*)offensive_entity->GetPlayerLink(); if (offensive_player) { // //--------------------------------------------------------------- // Notify this player that damaged was caused by offensive_player //--------------------------------------------------------------- // Check(offensive_player); RPPlayer::OffensivePlayer *shooter = new RPPlayer::OffensivePlayer( offensive_player, message->damageData.damageType, Now() ); Register_Object(shooter); RPPlayer *our_player = (RPPlayer*)GetPlayerLink(); Check(our_player); our_player->AddOffensivePlayer(*shooter); } } // //-------------------------------------------------------------------- // We we have received a collision packet, we should calculate our own // damage loss //-------------------------------------------------------------------- // switch (message->damageData.damageType) { case Damage::CollisionDamageType: { // //----------------------------------------------------------- // Find out who hit us, and get our velocity relative to them //----------------------------------------------------------- // Check(application); HostManager *host = application->GetHostManager(); Check(host); Mover *mover = (Mover*)host->GetEntityPointer(message->inflictingEntity); if (mover == NULL) { // // The inflicting entity is gone - its pod left the race // mid-mission (consumer multiplayer; arcade pods never // left). Nothing to bounce against. // Check_Fpu(); return; } Verify(mover->IsDerivedFrom(Mover::GetClassDerivations())); Vector3D v; v.Subtract(worldLinearVelocity, mover->worldLinearVelocity); // //----------------------------------------------------------------------- // The normal to bounce against is passed via the damage force, so see if // it opposes our motion. If not, we can ignore this message //----------------------------------------------------------------------- // if (v*message->damageData.surfaceNormal <= 0.0f) { Check_Fpu(); return; } // //---------------------------------------------------------------------- // Otherwise, do a dynamic bounce, and apply the damage to ourselves. // We save the original positions of the vehicles because we do not know // what time slice to use //---------------------------------------------------------------------- // Point3D old_us = localOrigin.linearPosition, old_them = mover->localOrigin.linearPosition; Scalar elasticity = elasticityCoefficient; message->damageData.surfaceNormal.x = -message->damageData.surfaceNormal.x; message->damageData.surfaceNormal.y = -message->damageData.surfaceNormal.y; message->damageData.surfaceNormal.z = -message->damageData.surfaceNormal.z; message->damageData.damageAmount = DynamicBounce( mover, 0.1f, 1.0f, message->damageData.surfaceNormal, &elasticity ); localOrigin.linearPosition = old_us; mover->localOrigin.linearPosition = old_them; break; } default: { message->damageData.damageForce /= moverMass; Check_Fpu(); worldLinearVelocity += message->damageData.damageForce; break; } } Entity::TakeDamageMessageHandler(message); Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VTV::KavorkianMessageHandler( ReceiverDataMessageOf *message ) { Check(this); Check(message); if (message->dataContents > 0) { Damage damage; Check_Pointer(damageZones); Check(*damageZones); damage.damageType = Damage::ExplosiveDamageType; damage.damageAmount = 1.0f / (*damageZones)->damageScale[Damage::ExplosiveDamageType]; Check_Fpu(); damage.damageForce = Vector3D::Identity; (*damageZones)->TakeDamage(damage); } Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VTV::MarkSpotMessageHandler( ReceiverDataMessageOf *message ) { Check(this); Check(message); if (message->dataContents > 0) { savedOrigin = localOrigin; } Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VTV::RestoreToSpotMessageHandler( ReceiverDataMessageOf *message ) { Check(this); Check(message); if (message->dataContents > 0) { Reset(savedOrigin, VTV::RegularReset); } Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VTV::ZoomInMessageHandler( ReceiverDataMessageOf *message ) { Check(this); Check(message); if (message->dataContents > 0) { targetRangeExponent; if (--targetRangeExponent < 0.0f) { targetRangeExponent = 0.0f; } } Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VTV::ZoomOutMessageHandler( ReceiverDataMessageOf *message ) { Check(this); Check(message); if (message->dataContents > 0) { if (++targetRangeExponent > 5.0f) { targetRangeExponent = 5.0f; } } Check_Fpu(); } //############################################################################# // Attribute Support // const VTV::IndexEntry VTV::AttributePointers[]= { ATTRIBUTE_ENTRY(VTV, HeightAboveTerrain, heightAboveTerrain), ATTRIBUTE_ENTRY(VTV, ThrusterPitch, thrusterPitch), ATTRIBUTE_ENTRY(VTV, CollisionState, collisionState), ATTRIBUTE_ENTRY(VTV, CollisionMaterialType, collisionMaterialType), ATTRIBUTE_ENTRY(VTV, CollisionNormal, collisionNormal), ATTRIBUTE_ENTRY(VTV, CollisionSpeed, collisionSpeed), ATTRIBUTE_ENTRY(VTV, EyepointRotation, eyepointRotation), ATTRIBUTE_ENTRY(VTV, BoosterOn, boosterOn), ATTRIBUTE_ENTRY(VTV, BoosterScale, boosterScale), ATTRIBUTE_ENTRY(VTV, BoosterSmokeDensity, boosterSmokeDensity), ATTRIBUTE_ENTRY(VTV, ForwardVelocity, forwardVelocity), ATTRIBUTE_ENTRY(VTV, CompassHeading, compassHeading), ATTRIBUTE_ENTRY(VTV, TargetReticle, targetReticle), ATTRIBUTE_ENTRY(VTV, NavigationRange, navigationRange), ATTRIBUTE_ENTRY(VTV, NavigationLinearPosition,navigationLinearPosition), ATTRIBUTE_ENTRY(VTV, NavigationAngularPosition,navigationAngularPosition), ATTRIBUTE_ENTRY(VTV, ScoreState, scoreState), ATTRIBUTE_ENTRY(VTV, ScoreType, scoreType), ATTRIBUTE_ENTRY(VTV, HornBlast, hornBlast) // HACK - ECH 7/31/95 - For replicants }; VTV::AttributeIndexSet& VTV::GetAttributeIndex() { static VTV::AttributeIndexSet attributeIndex(ELEMENTS(VTV::AttributePointers), VTV::AttributePointers, JointedMover::GetAttributeIndex() ); return attributeIndex; } //############################################################################# // Model support // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VTV::SetMappingSubsystem(VTVControlsMapper *mapper) { Check(this); Check(mapper); Check_Pointer(subsystemArray); if (subsystemArray[ControlsMapperSubsystem]) { Unregister_Object(subsystemArray[ControlsMapperSubsystem]); delete subsystemArray[ControlsMapperSubsystem]; } subsystemArray[ControlsMapperSubsystem] = mapper; Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Logical VTV::BoosterOn() { Check(this); for(int ii=BasicSubsystemCount;iiGetClassID() == BoosterClassID) { Booster *booster = (Booster*) subsystem; Check(booster); if (booster->GetSimulationState() == Booster::BoosterBurning) { Check_Fpu(); return True; } } } } Check_Fpu(); return False; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VTV::MoveAndCollide(Scalar time_slice) { Check(this); JointSubsystem *joint_subsystem = GetJointSubsystem(); Check(joint_subsystem); Verify(joint_subsystem->GetJointCount()); VTVControlsMapper *mapper = Cast_Object( VTVControlsMapper*, GetSubsystem(VTV::ControlsMapperSubsystem) ); Check(mapper); // //------------------------------------------------------------------------- // Figure out the average height and thruster rotation. While doing this, // calculate the differences in the corner forces so that we can figure out // the roll and pitch accelerations due to terrain //------------------------------------------------------------------------- // heightAboveTerrain = 0.0f; thrusterPitch.rotationAmount = 0.0f; Vector3D difference = Vector3D::Identity; int valid_thruster=0; for (int i=0; icurrentHeight; switch (joint_subsystem->GetJoint(valid_thruster)->GetJointType()) { case Joint::BallJointType: thrusterPitch.rotationAmount += joint_subsystem->GetJoint(valid_thruster)-> GetEulerAngles().pitch; break; case Joint::HingeXJointType: thrusterPitch.rotationAmount += joint_subsystem->GetJoint(valid_thruster)-> GetHinge().rotationAmount; break; } ++valid_thruster; // //---------------------------------------------------------------- // Generate the pseudoforce. Ignore the center engines of the VTV //---------------------------------------------------------------- // if ((i == 2 || i == 3) && joint_subsystem->GetJointCount() > 4) { continue; } if (thruster->thrusterOffset.x > 0.0f) { difference.z -= thruster->currentHeight; } else { difference.z += thruster->currentHeight; } if (thruster->thrusterOffset.z > 0.0f) { difference.x += thruster->currentHeight; } else { difference.x -= thruster->currentHeight; } } } heightAboveTerrain /= joint_subsystem->GetJointCount(); thrusterPitch.rotationAmount /= joint_subsystem->GetJointCount(); Check_Fpu(); // //--------------------------------------------------- // If we are not burning, follow the terrain and turn //--------------------------------------------------- // if (GetSimulationState() != BurningState) { difference *= angularSpringFactor / (15.0f + 2.0f*heightAboveTerrain); Check_Fpu(); // //------------------------------------------------------------------- // If this is a two-engine VTV, try to straighten out the pitch value //------------------------------------------------------------------- // if (joint_subsystem->GetJointCount() == 2) { Scalar len = localToWorld(2,1)*localToWorld(2,1) + localToWorld(1,1)*localToWorld(1,1); if (!Small_Enough(len)) { difference.x = 3.0f*localToWorld(2,1) / Sqrt(len); Check_Fpu(); } difference.z *= 0.5f; } else { difference *= 0.25f; } if (groundEffectDomainSquared) { difference.z -= bankFactor * mapper->powerDemand.x; localAcceleration.angularMotion += difference; // //------------------------------------- // Stabilize the craft at high altitude //------------------------------------- // if (heightAboveTerrain > 10.0f) { Vector3D world_g = Vector3D::Identity, local_g, torque; world_g.y = -GetEnvironment()->gravityConstant; local_g.MultiplyByInverse(world_g, localToWorld); world_g.y = -1.0f; torque.Cross(world_g, local_g); torque *= momentOfInertia; localAcceleration.angularMotion += torque; } } // //--------------------------------------- // Turn the VTV as required by the mapper //--------------------------------------- // Vector3D vel; vel.Multiply(localVelocity.angularMotion, localToWorld); Scalar step = mapper->angularVelocityDemand.y - vel.y; difference = Vector3D::Identity; if (step > 0.0f) { if (maxAngularAcceleration.y * time_slice > step) { difference.y += step / time_slice; Check_Fpu(); } else { difference.y += maxAngularAcceleration.y; } } else if (step < 0.0f) { if (maxAngularAcceleration.y * time_slice > -step) { difference.y += step / time_slice; Check_Fpu(); } else { difference.y -= maxAngularAcceleration.y; } } vel.MultiplyByInverse(difference,localToWorld); localAcceleration.angularMotion += vel; } // //-------------------------------------------------------------------- // Apply the standard air resistance and gravity, then project the VTV // motion through the time slice //-------------------------------------------------------------------- // ApplyAirResistanceAndGravity(); Vector3D old_acceleration = worldLinearAcceleration; worldLinearAcceleration.x *= ZIPPY; worldLinearAcceleration.z *= ZIPPY; Point3D old_position(localOrigin.linearPosition); ApplyWorldAccelerations(time_slice); // //------------------------------------------------------------------------- // We have now moved the VTV. We should now update our collision volume to // the new location and check it against the tree //------------------------------------------------------------------------- // Vector3D zippy_accel = worldLinearAcceleration, impact_velocity = localVelocity.linearMotion; worldLinearAcceleration = old_acceleration; MoveCollisionVolume(); // //------------------------------------------- // If we are going fast enough, do a ray-cast //------------------------------------------- // BoxedSolidCollisionList* collision_list = AllocateCollisionList(); Line line; line = old_position; Vector3D step; step.Subtract(localOrigin.linearPosition, old_position); if (step.LengthSquared() > SMALL) { line = step; Scalar diameter = collisionTemplate->maxX - collisionTemplate->minX; if (diameter < line.length) { CollideCenterOfMotion(&line, collision_list); } } if (!collision_list->GetCollisionCount()) { GetCurrentCollisions(collision_list); } // //------------------------------- // Process each of the collisions //------------------------------- // collisionTemporaryState = NoCollisionState; Damage collision_damage; ProcessCollisionList( collision_list, time_slice, old_position, &collision_damage ); // //------------------------- // Set Collision attributes //------------------------- // if (collision_damage.damageAmount > 0.0f) { BoxedSolid *boxed_solid; Check(&(*lastCollisionList)[0]); boxed_solid = (*lastCollisionList)[0].GetTreeVolume(); Check(boxed_solid); collisionMaterialType = boxed_solid->materialType; collisionNormal.MultiplyByInverse( collision_damage.surfaceNormal, localToWorld ); collisionSpeed = -(collisionNormal * impact_velocity); if (GetSimulationState() != BurningState) { collisionState.SetState(collisionTemporaryState); } // //--------------------------------------------------------------------- // Take the damage, allowing any slope less than 45 degrees relative to // the keel to take one-tenth the damage //--------------------------------------------------------------------- // if (collisionNormal.y > 0.7f) { collision_damage.damageAmount *= bottomArmorScale; } Check_Pointer(damageZones); Check(*damageZones); //TODO: IMPORTANT COLLISION CODE HERE (*damageZones)->TakeDamage(collision_damage); // //------------------------------------------------------------------ // If the VTV is burning, apply the world-space force vectors to the // craft as torque. The spinning will start next frame //------------------------------------------------------------------ // if (GetSimulationState() == BurningState) { Vector3D force, torque; Point3D moment; force.MultiplyByInverse(collision_damage.damageForce, localToWorld); moment.MultiplyByInverse( (Vector3D)collision_damage.impactPoint, localToWorld ); torque.Cross(moment, force); torque *= momentOfInertia; Clamp(torque.x, -20.0f, 20.0f); Clamp(torque.y, -20.0f, 20.0f); Clamp(torque.z, -20.0f, 20.0f); localVelocity.angularMotion += torque; } } else { collisionState.SetState(NoCollisionState); } // //------------------------------------------ // Check to see if we should blow up the VTV //------------------------------------------ // { InterestManager *interest_mgr = application->GetInterestManager(); Check(interest_mgr); InterestZone *zone = interest_mgr->GetInterestZone(interestZoneID); Check(zone); BoundingBoxTree *tree = zone->GetExistanceRoot(); Check(tree); Check_Pointer(damageZones); Check(*damageZones); insideWorld = tree->FindBoundingBoxContaining(localOrigin.linearPosition) != NULL; if (!insideWorld && (*damageZones)->damageLevel < 1.0f) { collision_damage.damageType = Damage::ExplosiveDamageType; collision_damage.damageAmount = 1.0f / (*damageZones)->damageScale[Damage::ExplosiveDamageType]; Check_Fpu(); collision_damage.damageForce = Vector3D::Identity; Check_Pointer(damageZones); Check(*damageZones); (*damageZones)->TakeDamage(collision_damage); localVelocity.angularMotion.x += 8.0f * Random - 4.0f; localVelocity.angularMotion.y += 8.0f * Random - 4.0f; localVelocity.angularMotion.z += 8.0f * Random - 4.0f; } } worldLinearAcceleration = zippy_accel; if (collisionState.GetState() == RestState) { worldLinearAcceleration = Vector3D::Identity; } UpdateLocalMotion(); // //------------------------------------------------- // Update the stuff that the secondary screen wants //------------------------------------------------- // compassHeading = localToWorld; forwardVelocity = -3.6f * localVelocity.linearMotion.z; if (targetRangeExponent > currentRangeExponent) { currentRangeExponent += time_slice; if (currentRangeExponent > targetRangeExponent) { currentRangeExponent = targetRangeExponent; } navigationRange = 250.0f * pow(2.0f, currentRangeExponent); } else if (targetRangeExponent < currentRangeExponent) { currentRangeExponent -= time_slice; if (currentRangeExponent < targetRangeExponent) { currentRangeExponent = targetRangeExponent; } navigationRange = 250.0f * pow(2.0f, currentRangeExponent); } Check_Fpu(); // //------------------------------------------------------------------------ // Run the dead reckoner, and then see how far apart the two positions are //------------------------------------------------------------------------ // (this->*deadReckoner)(); Vector3D error; error.Subtract( projectedOrigin.linearPosition, localOrigin.linearPosition ); Quaternion angular_deviation; angular_deviation.Subtract( projectedOrigin.angularPosition, localOrigin.angularPosition ); if ( error.LengthSquared() > 0.04f || Abs(angular_deviation.w) < 0.997f || collisionState.GetState() != collisionState.GetOldState() || lastPerformance - lastUpdate > 2.0f ) { ForceUpdate(); } Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VTV::PerformAndWatch( const Time &till, MemoryStream *update_stream ) { Check(this); Check(&till); if (GetInstance() != ReplicantInstance) { boosterOn = False; boosterScale = Vector3D::Identity; boosterSmokeDensity = 0.0f; } JointedMover::PerformAndWatch(till, update_stream); Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VTV::WriteUpdateRecord(Simulation::UpdateRecord *message, int update_model) { Check(this); Check_Pointer(message); JointedMover::WriteUpdateRecord(message, update_model); UpdateRecord *record = (UpdateRecord*)message; record->recordLength = sizeof(*record); record->collisionState = collisionState.GetState(); record->collisionMaterialType = collisionMaterialType; record->collisionNormal = collisionNormal; record->collisionSpeed = collisionSpeed; record->boosterOn = boosterOn; record->boosterScale = boosterScale; record->boosterSmokeDensity = boosterSmokeDensity; record->hornBlast = hornBlast; // HACK - ECH 7/31/95 - For replicants Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VTV::ReadUpdateRecord(Simulation::UpdateRecord *message) { Check(this); Check_Pointer(message); JointedMover::ReadUpdateRecord(message); UpdateRecord *record = (UpdateRecord*)message; collisionState.SetState(record->collisionState); collisionMaterialType = record->collisionMaterialType; collisionNormal = record->collisionNormal; collisionSpeed = record->collisionSpeed; boosterOn = record->boosterOn; boosterScale = record->boosterScale; boosterSmokeDensity = record->boosterSmokeDensity; hornBlast = record->hornBlast; // HACK - ECH 7/31/95 - For replicants if ( GetSimulationState() == DefaultState && simulationState.GetOldState() == BurningState ) { localOrigin = updateOrigin; if (IsCollisionVolume()) { MoveCollisionVolume(); } } Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VTV::ProcessCollision( Scalar time_slice, BoxedSolidCollision &collision, const Point3D &old_position, Damage *damage ) { Scalar penetration, r, elasticity = elasticityCoefficient, friction = frictionCoefficient, added_damage = 0.0f; Door *door = NULL; // //----------------------------------------------------------------------- // Find out if we have hit a mover or something static. Moving collision // volumes must have an owning entity //----------------------------------------------------------------------- // BoxedSolid *box = collision.GetTreeVolume(); Check(box); Simulation *sim = box->GetOwningSimulation(); if (!sim) { goto Static_Bounce; } if (sim->IsDerivedFrom(*Mover::GetClassDerivations())) { Mover *mover = (Mover*)sim; // //-------------------------------------------------------------------- // If we hit a mover, make sure that the collision is valid, and if it // is, calculate the energy_loss //-------------------------------------------------------------------- // if ( !collisionVolume->ProcessCollision( collision, worldLinearVelocity, lastCollisionList, &damage->surfaceNormal, &penetration ) ) { Check_Fpu(); return; } // //-------------------------------------------------------------- // Make sure that we are not rearending somebody going faster... //-------------------------------------------------------------- // Vector3D rel_v; rel_v.Subtract(worldLinearVelocity, mover->worldLinearVelocity); if (rel_v * damage->surfaceNormal >= -SMALL) { Check_Fpu(); return; } // //--------------------------------- // Calculate the moving energy loss //--------------------------------- // Max_Clamp(penetration, time_slice); r = penetration / time_slice; Check_Fpu(); damage->damageAmount = DynamicBounce( mover, time_slice, r, damage->surfaceNormal, &elasticity ); // //-------------------------------------------------------------------- // If we hit another VTV, we will have to send it a damage message and // enter it into the queue of guys what hit us in the last two seconds //-------------------------------------------------------------------- // if (mover->GetClassID() == VTVClassID) { TakeDamageMessage message( TakeDamageMessageID, sizeof(TakeDamageMessage), GetEntityID(), 0, *damage ); mover->Dispatch(&message); RPPlayer *offensive_player = (RPPlayer*)mover->GetPlayerLink(); if (offensive_player) { Check(offensive_player); RPPlayer::OffensivePlayer *shooter = new RPPlayer::OffensivePlayer( offensive_player, message.damageData.damageType, lastPerformance ); Register_Object(shooter); RPPlayer *our_player = (RPPlayer*)GetPlayerLink(); Check(our_player); our_player->AddOffensivePlayer(*shooter); } } // //------------------------------------- // Set up the collision state correctly //------------------------------------- // goto Set_Collision_State; } // //-------------------------------------------------------------------------- // If we hit a moving door, adjust our relative velocity to the door. If we // get hit by both doors, CRUSH the vtv!!! //-------------------------------------------------------------------------- // else if (sim->IsDerivedFrom(*Door::GetClassDerivations())) { door = (Door*)sim; Check(door); worldLinearVelocity += door->currentVelocity; } //-------------------------------------------------------------------- // If we really have a collision, do a static bounce off of the normal // generated //-------------------------------------------------------------------- // Static_Bounce: if ( collisionVolume->ProcessCollision( collision, worldLinearVelocity, lastCollisionList, &damage->surfaceNormal, &penetration ) ) { Max_Clamp(penetration, time_slice); r = penetration / time_slice; Check_Fpu(); damage->damageAmount = StaticBounce( old_position, time_slice, r, damage->surfaceNormal, &elasticity, minimumBounceSpeed, &friction ) + added_damage; // //---------------------------------------- // If we have hit a door, look for a crush //---------------------------------------- // if (door) { if ( lastPerformance - lastDoorHit < 0.1f && doorHitNormal*damage->surfaceNormal < -0.7f && damageZones[0]->damageLevel < 1.0f ) { damageZones[0]->damageLevel = 1.0f; } doorHitNormal = damage->surfaceNormal; lastDoorHit = lastPerformance; } // //-------------------------- // Determine collision state //-------------------------- // Set_Collision_State: if (!elasticity) { if (!friction) { if (collisionTemporaryState == NoCollisionState) { collisionTemporaryState = RestState; } } else if (collisionTemporaryState != InitialHitState) { collisionTemporaryState = SlideState; } } else { collisionTemporaryState = InitialHitState; } } if (door) { worldLinearVelocity -= door->currentVelocity; } Check_Fpu(); } //############################################################################# // Scoring support // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // HACK - ECH 7/6/95 - Allow the player vehicle to respond to score // messages, allows attribute system to be used for scoring // void VTV::RespondToScoreMessage(Message *message) { Check(this); Check(message); RPPlayer::ScoreMessage *score_message = Cast_Object(RPPlayer::ScoreMessage*, message); // // Set the score state and toggle the state to trigger the watchers // Check(score_message); scoreType = score_message->scoreType; Verify( score_message->scoreType > RPPlayer::NullPointType && score_message->scoreType < RPPlayer::PointTypeCount ); scoreState.SetState(score_message->scoreType); scoreState.SetState(RPPlayer::NullPointType); Check_Fpu(); } //############################################################################# // Construction and Destruction // //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // VTV::VTV( VTV::MakeMessage *creation_message, VTV::SharedData &virtual_data ): JointedMover(creation_message, virtual_data), collisionState(CollisionStateCount), collisionNormal(0.0f, 0.0f, 1.0f), scoreState(RPPlayer::PointTypeCount) { SetValidFlag(); // //------------------------ // Setup the initial state //------------------------ // Check(application); Check(application->GetInterestManager()); InterestZone *interest_zone = application->GetInterestManager()->GetInterestZone(interestZoneID); Check(interest_zone); localEnvironment = interest_zone->GetEnvironment(); Check(localEnvironment); // //---------------- // Init attributes //---------------- // collisionMaterialType = BoxedSolid::SteelMaterial; // collisionNormal = Normal::Identity; // Fails TestInstance collisionSpeed = 0.0f; collisionState.SetState(NoCollisionState); scoreState.SetState(RPPlayer::NullPointType); scoreType = -1; boosterOn = False; boosterScale = Vector3D::Identity; boosterSmokeDensity = 0.0f; doorHitNormal = Vector3D::Identity; lastDoorHit = Time::Null; heightAboveTerrain = 0.0f; forwardVelocity = 0.0f; hornBlast = -1; insideWorld = True; thrusterAngle = 0.0f; // //----------------------------- // Set the correct motion model //----------------------------- // BoxedSolid::Material material; SetDeadReckoner(&VTV::AcceleratedDeadReckoner); if (GetInstance() == ReplicantInstance) { SetPerformance(&VTV::DeadReckon); material = BoxedSolid::OtherCraftMaterial; } else { SetPerformance(&VTV::MoveAndCollide); material = BoxedSolid::OurCraftMaterial; } // //----------------------------- // Initialize the VTV variables //----------------------------- // ResourceFile *res_file = application->GetResourceFile(); ResourceDescription *res = res_file->SearchList( resourceID, ResourceDescription::GameModelResourceType ); Check(res); res->Lock(); ModelResource *model_resource = (ModelResource*)res->resourceAddress; Check_Pointer(model_resource); groundEffectDomainSquared = model_resource->groundEffectDomainSquared; groundEffectRange = model_resource->groundEffectRange; maxAngularAcceleration = model_resource->maxAngularAcceleration; maxYawVelocity = model_resource->maxYawVelocity; angularSpringFactor = model_resource->angularSpringFactor; bankFactor = model_resource->bankFactor; maxImpactSpeed = model_resource->maxImpactSpeed; bottomArmorScale = model_resource->bottomArmorScale; Scalar death_speed_km = model_resource->deathSpeed; // // Convert to Meters/Second // deathSpeed = death_speed_km/3.6f; deathScoreLoss = model_resource->deathScoreLoss; powerDive = model_resource->powerDive; res->Unlock(); deathConstant = (2000 * deathScoreLoss) / ( (1 - (elasticityCoefficient * elasticityCoefficient)) * (deathSpeed * deathSpeed) * moverMass ); Check_Fpu(); thrusterPitch.rotationAmount = 0.0f; thrusterPitch.axisNumber = X_Axis; compassHeading = YawPitchRoll::Identity; eyepointRotation = EulerAngles::Identity; navigationRange = 1000.0; // initial range = 1km currentRangeExponent = 3.0f; targetRangeExponent = 3.0f; Str_Copy(vehicleBadge, creation_message->vehicleBadge, sizeof(vehicleBadge)); Str_Copy(vehicleColor, creation_message->vehicleColor, sizeof(vehicleColor)); // //------------------------------------------------- // Set up secondary display variables // The indirection is required by the camera ship, // but may be exploited for viewing the nav // display at an arbitrary location, if desired. //------------------------------------------------- // If 'navigationLinearPosition' is NULL, it will // extract the position from the entity directly. // Not so for 'navigationAngularPosition': if it // is NULL, the display will not 'turn' with the // vehicle. It must be explicitly set if used. //------------------------------------------------- // navigationLinearPosition = NULL; navigationAngularPosition = &localOrigin.angularPosition; // //--------------------------------------------------- // Make sure to correctly set the material of the VTV //--------------------------------------------------- // collisionVolume->materialType = material; // //------------------------------------------------------------------------ // Setup the subsystems. The mapper subsystem will be left blank, so that // the caller can hook in the appropriate mapper //------------------------------------------------------------------------ // res = application->GetResourceFile()->SearchList( resourceID, ResourceDescription::SubsystemModelStreamResourceType ); Check(res); res->Lock(); Check_Pointer(res->resourceAddress); MemoryStream subsystems(res->resourceAddress, res->resourceSize); subsystemCount = *(int*)subsystems.GetPointer() + BasicSubsystemCount; subsystems.AdvancePointer(sizeof(int)); Verify(!subsystemArray); subsystemArray = new (Subsystem (*[subsystemCount])); Register_Pointer(subsystemArray); if (GetInstance() == ReplicantInstance) { VTVControlsMapper::SubsystemResource control_subsystem_resource; Str_Copy( control_subsystem_resource.subsystemName, "ControlsMapper", sizeof(control_subsystem_resource.subsystemName) ); control_subsystem_resource.classID = TrivialSubsystemClassID; control_subsystem_resource.subsystemModelSize = sizeof(control_subsystem_resource); control_subsystem_resource.segmentIndex = -1; control_subsystem_resource.subsystemFlags = DontExecuteFlag; subsystemArray[ControlsMapperSubsystem] = new VTVControlsMapper( this, VTV::ControlsMapperSubsystem, &control_subsystem_resource, VTVControlsMapper::DefaultData ); Register_Object(subsystemArray[ControlsMapperSubsystem]); } else { subsystemArray[ControlsMapperSubsystem] = NULL; } res->Unlock(); // //-------------------------------------------------------------------- // Make the power subsystem. All VTVs have one in the designated slot //-------------------------------------------------------------------- // VTVPower::SubsystemResource power_subsystem_resource; Str_Copy( power_subsystem_resource.subsystemName, "Power", sizeof(power_subsystem_resource.subsystemName) ); power_subsystem_resource.classID = TrivialSubsystemClassID; power_subsystem_resource.subsystemModelSize = sizeof(power_subsystem_resource); power_subsystem_resource.maxAcceleration = model_resource->maxAcceleration; power_subsystem_resource.segmentIndex = -1; power_subsystem_resource.subsystemFlags = 0; subsystemArray[PowerSubsystem] = new VTVPower(this, PowerSubsystem, &power_subsystem_resource); Register_Object(subsystemArray[PowerSubsystem]); // //--------------------------------------------------------------- // Make the thruster subsystems. We can handle up to 6 thrusters //--------------------------------------------------------------- // Thruster::SubsystemResource thruster_subsystem_resource; Str_Copy( thruster_subsystem_resource.subsystemName, "Thruster?", sizeof(thruster_subsystem_resource.subsystemName) ); thruster_subsystem_resource.classID = TrivialSubsystemClassID; thruster_subsystem_resource.subsystemModelSize = sizeof(thruster_subsystem_resource); thruster_subsystem_resource.subsystemFlags = 0; thruster_subsystem_resource.primeThruster = True; // // Initialize the thruster subsystems from the jointSubystem // Information // #if DEBUG_LEVEL>0 JointSubsystem *joint_subsystem = GetJointSubsystem(); Check(joint_subsystem); Verify(joint_subsystem->GetJointCount() <= MAX_THRUSTERS); #endif int i; for (i=0; ijointIndex[i] == -1 || model_resource->segmentIndex[i] == -1 ) { subsystemArray[Thruster1Subsystem + i] = NULL; continue; } // // Append thruster number to subsystem name // thruster_subsystem_resource.subsystemName[8] = (char)(i + '0'); // // assign joint and segment Indicies // thruster_subsystem_resource.jointIndex = model_resource->jointIndex[i]; thruster_subsystem_resource.segmentIndex = model_resource->segmentIndex[i]; // // Get maxThrusterRotation Rate // thruster_subsystem_resource.maxThrusterRotationRate = model_resource->maxThrusterRotationRate; // // Construct the thruster Subsystem // subsystemArray[Thruster1Subsystem + i] = new Thruster( this, Thruster1Subsystem+i, &thruster_subsystem_resource); Register_Object(subsystemArray[Thruster1Subsystem + i]); thruster_subsystem_resource.primeThruster = False; } // // Add the JointSubsystem to the subsystemArray // subsystemArray[JointSubsystemID] = GetJointSubsystem(); // //--------------------------------------------------- // Read and process the subsystem array specification //--------------------------------------------------- // for (i=BasicSubsystemCount; isubsystemFlags & Subsystem::DontReplicateFlag) || GetInstance() != ReplicantInstance ) { switch (subsystem_resource->classID) { case RegisteredClass::BoosterClassID: subsystemArray[i] = new Booster( this, i, (Booster::SubsystemResource*)subsystem_resource ); break; case RegisteredClass::ChuteClassID: subsystemArray[i] = new Chute(this, i, (Chute::SubsystemResource*)subsystem_resource); break; case RegisteredClass::RivetGunClassID: subsystemArray[i] = new RivetGun(this, i, (RivetGun::SubsystemResource*)subsystem_resource); break; case RegisteredClass::LaserDrillClassID: subsystemArray[i] = new LaserGun(this, i, (LaserGun::SubsystemResource*)subsystem_resource); break; case RegisteredClass::DemolitionPackDropperClassID: subsystemArray[i] = new DemolitionPackDropper( this, i, (DemolitionPackDropper::SubsystemResource*)subsystem_resource); break; } Register_Object(subsystemArray[i]); } else { subsystemArray[i] = NULL; } subsystems.AdvancePointer(subsystem_resource->subsystemModelSize); } // // Read In DamageZones and Create DamageZoneID's // ResourceDescription *dmg_res = application->GetResourceFile()->SearchList( resourceID, ResourceDescription::DamageZoneStreamResourceType ); Check(dmg_res); dmg_res->Lock(); DynamicMemoryStream damage_zone_stream( dmg_res->resourceAddress, dmg_res->resourceSize ); // //------------------------------------- // skip over damageZoneCount in Stream //------------------------------------- // damage_zone_stream.AdvancePointer(sizeof(damageZoneCount)); // // Allocate damageZones Array // for(int ii=0;iiUnlock(); // //-------------------- // Init the reticle //-------------------- // targetReticle.reticlePosition = model_resource->reticleResource.reticlePosition; Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // VTV::~VTV() { Check(this); Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VTV::Reset(const Origin& new_origin, int reset_command) { Check(this); Check(&new_origin); localOrigin = new_origin; localVelocity = Motion::Identity; localAcceleration = Motion::Identity; worldLinearVelocity = Vector3D::Identity; worldLinearAcceleration = Vector3D::Identity; localToWorld = localOrigin; SetSimulationState(VTV::DefaultState); damageZones[0]->damageLevel = 0.0f; collisionState.SetState(NoCollisionState); boosterOn = False; boosterScale = Vector3D::Identity; boosterSmokeDensity = 0.0f; // //------------------------- // Reset subsystems //------------------------- // for(int ii=BasicSubsystemCount;iiDeathReset(reset_command); } } Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // void VTV::DeathShutdown(int shutdown_command) { // //------------------------- // Shutdown subsystems //------------------------- // for(int ii=BasicSubsystemCount;iiDeathShutdown(shutdown_command); } Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ResourceDescription::ResourceID VTV::CreateModelResource( ResourceFile *resource_file, const char *model_name, NotationFile *model_file, const ResourceDirectories *directories, ModelResource *model ) { Check(resource_file); Check_Pointer(model_name); Check(model_file); Check_Pointer(directories); // //----------------------------------------------------------------------- // If we were not provided a buffer to write the model data into, we must // create it ourselves. Then make sure that the model stuff is read in //----------------------------------------------------------------------- // ModelResource *local_model = model; if (!local_model) { local_model = new ModelResource; Register_Pointer(local_model); } if ( JointedMover::CreateModelResource( resource_file, model_name, model_file, directories, local_model ) == -1 ) { Dump_And_Die: if (!model) { Unregister_Pointer(local_model); delete local_model; } Check_Fpu(); return -1; } if ( Reticle::CreateModelResource( resource_file, model_name, model_file, directories, &local_model->reticleResource ) == -1 ) { goto Dump_And_Die; } // //----------------------------- // Read in the max acceleration //----------------------------- // if ( !model_file->GetEntry( "gamedata", "MaxAcceleration", &local_model->maxAcceleration ) ) { DEBUG_STREAM << model_name << " missing MaxAcceleration!\n" << std::flush; goto Dump_And_Die; } if ( !model_file->GetEntry( "gamedata", "MaxImpactSpeed", &local_model->maxImpactSpeed ) ) { DEBUG_STREAM << model_name << " missing MaxImpactSpeed!\n" << std::flush; goto Dump_And_Die; } // //------------------------- // Read in the bottom armor //------------------------- // local_model->bottomArmorScale = 0.1f; model_file->GetEntry( "gamedata", "BottomArmorScale", &local_model->bottomArmorScale ); // //------------------------- // Read in the bottom armor //------------------------- // local_model->powerDive = 0.0f; model_file->GetEntry( "gamedata", "PowerDive", &local_model->powerDive ); if (local_model->powerDive > 0.0 || local_model->powerDive < -1.0f) { DEBUG_STREAM << model_name << " has an invalid powerDive!\n" << std::flush; goto Dump_And_Die; } // //----------------------------------- // Read in the thruster rotation rate //----------------------------------- // if ( !model_file->GetEntry( "gamedata", "MaxThrusterRotationRate", &local_model->maxThrusterRotationRate ) ) { DEBUG_STREAM << model_name << " missing MaxThrusterRotationRate!\n" << std::flush; goto Dump_And_Die; } // //-------------------------------- // Read in the ground effect stuff //-------------------------------- // if ( !model_file->GetEntry( "gamedata", "GroundEffectDomainSquared", &local_model->groundEffectDomainSquared ) ) { DEBUG_STREAM << model_name << " missing GroundEffectDomainSquared!\n" << std::flush; goto Dump_And_Die; } if ( !model_file->GetEntry( "gamedata", "GroundEffectRange", &local_model->groundEffectRange ) ) { DEBUG_STREAM << model_name << " missing GroundEffectRange!\n" << std::flush; goto Dump_And_Die; } // //---------------------------------------- // Read in the angular acceleration limits //---------------------------------------- // const char *entry; if ( !model_file->GetEntry( "gamedata", "MaxAngularAcceleration", &entry ) ) { DEBUG_STREAM << model_name << " missing MaxAngularAcceleration!\n" << std::flush; goto Dump_And_Die; } sscanf( entry, "%f %f %f", &local_model->maxAngularAcceleration.x, &local_model->maxAngularAcceleration.y, &local_model->maxAngularAcceleration.z ); if ( !model_file->GetEntry( "gamedata", "AngularSpringFactor", &local_model->angularSpringFactor ) ) { DEBUG_STREAM << model_name << " missing AngularSpringFactor!\n" << std::flush; goto Dump_And_Die; } if ( !model_file->GetEntry( "gamedata", "DeathSpeed", &local_model->deathSpeed ) ) { DEBUG_STREAM << model_name << " missing DeathSpeed!\n" << std::flush; goto Dump_And_Die; } if ( !model_file->GetEntry( "gamedata", "DeathScoreLoss", &local_model->deathScoreLoss ) ) { DEBUG_STREAM << model_name << " missing DeathScoreLoss!\n" << std::flush; goto Dump_And_Die; } if ( !model_file->GetEntry( "gamedata", "ElasticityCoefficient", &local_model->elasticityCoefficient ) ) { DEBUG_STREAM << model_name << " missing ElasticityCoefficient!\n" << std::flush; goto Dump_And_Die; } if ( !model_file->GetEntry( "gamedata", "MaxYawVelocity", &local_model->maxYawVelocity ) ) { DEBUG_STREAM << model_name << " missing MaxYawVelocity!\n" << std::flush; goto Dump_And_Die; } local_model->maxYawVelocity *= RAD_PER_DEG; if ( !model_file->GetEntry( "gamedata", "BankFactor", &local_model->bankFactor ) ) { DEBUG_STREAM << model_name << " missing BankFactor!\n" << std::flush; goto Dump_And_Die; } // // Read in Segment and Joint information from the .skl file // for the thrusters // //------------------- // Find the .skl file //------------------- // const char *skl_entry; if ( !model_file->GetEntry( "video", "skeleton", &skl_entry ) ) { DEBUG_STREAM << model_name << " is missing .skl file specification!\n" << std::flush; return -1; } char *skl_filename = MakePathedFilename(directories->videoDirectory, skl_entry); Register_Pointer(skl_filename); NotationFile *skl_file = new NotationFile(skl_filename); Register_Object(skl_file); if (!skl_file->PageCount()) { DEBUG_STREAM << skl_filename << " is empty or missing!\n" << std::flush; Dump_And_Die_1: Unregister_Pointer(skl_filename); delete[] skl_filename; Unregister_Object(skl_file); delete skl_file; Check_Fpu(); return ResourceDescription::NullResourceID; } // // Make a pagelist of all the segments // NameList *segment_pages = skl_file->MakePageList(); Register_Object(segment_pages); NameList::Entry *segment_entry = segment_pages->GetFirstEntry(); int segment_count=0; int joint_count=0; // // Initialize the thruster Model Resource info to No Thruster! // for(int ii=0;iijointIndex[ii] = -1; local_model->segmentIndex[ii] = -1; } while(segment_entry) { char entry[32]; Str_Copy(entry, segment_entry->GetName(), sizeof(entry)); // // // Skip LAB_ONLY and Damage_Zones if( (strcmp(entry, "LAB_ONLY") == 0) || (strcmp(entry,"DamageZones") ==0) ) { segment_entry = segment_entry->GetNextEntry(); continue; } if (strncmp(entry,"joint",5)==0) { if (!stricmp(entry,"jointrfeng")) { local_model->jointIndex[0] = joint_count; local_model->segmentIndex[0] = segment_count; } else if (!stricmp(entry,"jointlfeng")) { local_model->jointIndex[1] = joint_count; local_model->segmentIndex[1] = segment_count; } else if (!stricmp(entry,"jointreng") || !stricmp(entry,"jointrmeng")) { local_model->jointIndex[2] = joint_count; local_model->segmentIndex[2] = segment_count; } else if (!stricmp(entry,"jointleng") || !stricmp(entry,"jointlmeng")) { local_model->jointIndex[3] = joint_count; local_model->segmentIndex[3] = segment_count; } else if (!strcmp(entry,"jointrbeng")) { local_model->jointIndex[4] = joint_count; local_model->segmentIndex[4] = segment_count; } else if (!strcmp(entry,"jointlbeng")) { local_model->jointIndex[5] = joint_count; local_model->segmentIndex[5] = segment_count; } ++joint_count; } ++segment_count; segment_entry = segment_entry->GetNextEntry(); } // //------------------------------------------------------------------------- // If we created the model buffer, then we have the responsibility to write // it out to the resource file //------------------------------------------------------------------------- // if (!model) { ResourceDescription *new_res = resource_file->AddResource( model_name, ResourceDescription::GameModelResourceType, 1, ResourceDescription::Preload, local_model, sizeof(*local_model) ); Unregister_Pointer(local_model); delete local_model; Unregister_Pointer(skl_filename); delete skl_filename; Unregister_Object(skl_file); delete skl_file; Unregister_Object(segment_pages); delete segment_pages; Check(new_res); Check_Fpu(); return new_res->resourceID; } else { Unregister_Pointer(local_model); delete local_model; Unregister_Pointer(skl_filename); delete skl_filename; Unregister_Object(skl_file); delete skl_file; Unregister_Object(segment_pages); delete segment_pages; Check_Fpu(); return 0; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ResourceDescription::ResourceID VTV::CreateSubsystemStream( ResourceFile *resource_file, const char *model_name, NotationFile *model_file, const ResourceDirectories *directories ) { Check(resource_file); Check_Pointer(model_name); Check(model_file); Check_Pointer(directories); // //------------------- // Find the .sub file //------------------- // const char *entry_data; if ( !model_file->GetEntry( "gamedata", "Subsystems", &entry_data ) ) { DEBUG_STREAM << model_name << " is missing .sub file specification!\n" << std::flush; Check_Fpu(); return -1; } char *filename = MakePathedFilename(directories->modelDirectory, entry_data); Register_Pointer(filename); NotationFile *sys_file = new NotationFile(filename); Register_Object(sys_file); if (!sys_file->PageCount()) { DEBUG_STREAM << filename << " cannot be found!\n" << std::flush; Unregister_Pointer(filename); delete filename; Unregister_Object(sys_file); delete sys_file; Check_Fpu(); return -1; } // //------------------------------------------------------- // Figure out how many subsystems will have to be created //------------------------------------------------------- // NameList *namelist = sys_file->MakePageList(); Register_Object(namelist); NameList::Entry *entry = namelist->GetFirstEntry(); int count = 0; while (entry) { ++count; entry = entry->GetNextEntry(); } // //--------------------------------------------------------------------- // Step through each of the pages, and create an entry in the subsystem // stream //--------------------------------------------------------------------- // int *subsystems = new int[sizeof(RivetGun::SubsystemResource)*count]; Register_Pointer(subsystems); MemoryStream stream(subsystems,sizeof(RivetGun::SubsystemResource)*count*sizeof(int)); *(int*)stream.GetPointer() = count; stream.AdvancePointer(sizeof(int)); entry = namelist->GetFirstEntry(); while (entry) { const char *subsystem_name = entry->GetName(), *type; if ( !sys_file->GetEntry( subsystem_name, "Type", &type ) ) { DEBUG_STREAM << model_name << ':' << subsystem_name << " missing Type!\n" << std::flush; Dump_And_Die: Unregister_Pointer(subsystems); delete[] subsystems; Unregister_Object(namelist); delete namelist; Unregister_Pointer(filename); delete filename; Unregister_Object(sys_file); delete sys_file; return -1; } Subsystem::SubsystemResource *subsystem_resource = (Subsystem::SubsystemResource*)stream.GetPointer(); if (!strcmp(type, "BoosterClassID")) { if ( Booster::CreateStreamedSubsystem( model_file, model_name, subsystem_name, (Booster::SubsystemResource*)subsystem_resource, sys_file, directories ) == -1 ) { goto Dump_And_Die; } } else if (!strcmp(type, "ChuteClassID")) { if ( Chute::CreateStreamedSubsystem( model_file, model_name, subsystem_name, (Chute::SubsystemResource*)subsystem_resource, sys_file, directories ) == -1 ) { goto Dump_And_Die; } } else if (!strcmp(type, "RivetGunClassID")) { if ( RivetGun::CreateStreamedSubsystem( model_file, model_name, subsystem_name, (RivetGun::SubsystemResource*)subsystem_resource, sys_file, resource_file, directories ) == -1 ) { goto Dump_And_Die; } } else if (!strcmp(type, "DemolitionPackDropperClassID")) { if ( DemolitionPackDropper::CreateStreamedSubsystem( model_file, model_name, subsystem_name, (DemolitionPackDropper::SubsystemResource*)subsystem_resource, sys_file, resource_file, directories ) == -1 ) { goto Dump_And_Die; } } else if (!strcmp(type, "LaserDrillClassID")) { if ( LaserGun::CreateStreamedSubsystem( model_file, model_name, subsystem_name, (LaserGun::SubsystemResource*)subsystem_resource, sys_file, resource_file, directories ) == -1 ) { goto Dump_And_Die; } } else { DEBUG_STREAM << model_name << ':' << subsystem_name << " has an unknown component type of " << type << std::endl << std::flush; goto Dump_And_Die; } stream.AdvancePointer(subsystem_resource->subsystemModelSize); entry = entry->GetNextEntry(); } // //-------------------------------------------------------------------- // Write the stream out to disk. Size is equal to the byte difference // between stream and subsystems //-------------------------------------------------------------------- // ResourceDescription *new_res = resource_file->AddResource( model_name, ResourceDescription::SubsystemModelStreamResourceType, 1, ResourceDescription::Preload, subsystems, (char*)stream.GetPointer() - (char*)subsystems ); Check(new_res); // //------------------------- // Clean up after ourselves //------------------------- // Unregister_Pointer(subsystems); delete[] subsystems; Unregister_Object(namelist); delete namelist; Unregister_Pointer(filename); delete filename; Unregister_Object(sys_file); delete sys_file; Check_Fpu(); return new_res->resourceID; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Simulation::SharedData* VTV::GetSubsystemSharedData(const char *name) { Check_Pointer(name); Check_Fpu(); if (!strcmp(name, "")) { return &VTV::DefaultData; } else if (!strcmp(name, "BoosterClassID")) { return &Booster::DefaultData; } else if (!strcmp(name, "ChuteClassID")) { return &Chute::DefaultData; } else if (!strcmp(name, "Power")) { return &VTVPower::DefaultData; } else if (!strcmp(name, "ControlsMapper")) { return &VTVControlsMapper::DefaultData; } else if (!strcmp(name, "RivetGunClassID")) { return &RivetGun::DefaultData; } else if (!strcmp(name, "LaserDrillClassID")) { return &LaserGun::DefaultData; } else if (!strcmp(name, "DemolitionPackDropperClassID")) { return &DemolitionPackDropper::DefaultData; } else { return NULL; } } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ResourceDescription::ResourceID VTV::CreateControlMappingStream( const char* mapping_name, NotationFile *mapping_file, FindNameFunction find_name, ResourceFile *resource_file, const char *model_name, NotationFile *model_file, const ResourceDirectories *directories, PlatformTool *current_tool ) { Check(mapping_file); Check(resource_file); Check_Pointer(model_name); Check(model_file); Check_Pointer(directories); NameList *namelist = mapping_file->MakePageList(); Register_Object(namelist); NameList::Entry *entry = namelist->GetFirstEntry(); int count=mapping_file->PageCount(); if (!count) { DEBUG_STREAM << model_name << " is missing control mappings!\n" << std::flush; Unwind_1: Unregister_Object(namelist); delete namelist; Check_Fpu(); return -1; } int *stream = (int*)new ControlsMapping[(count+1)*6]; Register_Pointer(stream); int *mappings = stream; *mappings++ = count; int real_count = 0; // //---------------------------------- // For each page, create one mapping //---------------------------------- // for (int i=0; iGetName(); if ( !mapping_file->GetEntry( page_name, "IOMapping", &control_name ) ) { DEBUG_STREAM << model_name << ':' << page_name << " has no IOMapping !\n" << std::flush; Unwind_2: Unregister_Pointer(stream); delete stream; goto Unwind_1; } if (!(*find_name)(control_name, mapping)) { DEBUG_STREAM << model_name << ':' << control_name << " does not exist!\n" << std::flush; goto Unwind_2; } // //-------------------------------------------------------- // Now, find the right subsystem to attach this control to //-------------------------------------------------------- // const char *subsystem_name; mapping->subsystemID = Entity::EntitySubsystemID; char subsystem_type[80]; *subsystem_type = '\0'; if ( mapping_file->GetEntry( page_name, "Subsystem", &subsystem_name ) ) { // //-------------------------------------------------- // See if this is one of the always there subsystems //-------------------------------------------------- // if (!strcmp(subsystem_name, "ControlsMapper")) { mapping->subsystemID = ControlsMapperSubsystem; Str_Copy(subsystem_type, subsystem_name, sizeof(subsystem_type)); } else if (!strcmp(subsystem_name, "Power")) { mapping->subsystemID = PowerSubsystem; Str_Copy(subsystem_type, subsystem_name, sizeof(subsystem_type)); } else if (!strncmp(subsystem_name, "Thruster", 8)) { mapping->subsystemID = Thruster1Subsystem + (subsystem_name[8] - '0'); Str_Copy(subsystem_type, subsystem_name, sizeof(subsystem_type)); } // //-------------------------------------------------------------- // Otherwise, we have to open up the subsystem file and find the // matching page, keeping track of the count //-------------------------------------------------------------- // else { const char *entry_data; model_file->GetEntry("gamedata", "Subsystems", &entry_data); char *filename = MakePathedFilename(directories->modelDirectory, entry_data); Register_Pointer(filename); NotationFile *sys_file = new NotationFile( filename, NotationFile::CleanListMode|NotationFile::IgnoreCaseMode ); Register_Object(sys_file); NameList *sublist = sys_file->MakePageList(); Register_Object(sublist); NameList::Entry *subsystem_entry = sublist->GetFirstEntry(); // //----------------------------------------------------------------- // Look through all the pages of the subsystem file, looking for // our subsystem. When we find it, remember what number it was and // what type it was //----------------------------------------------------------------- // mapping->subsystemID = BasicSubsystemCount; while (subsystem_entry) { if (!strcmp(subsystem_entry->GetName(), subsystem_name)) { if (!sys_file->GetEntry(subsystem_name, "Type", &entry_data)) { DEBUG_STREAM << model_name << ':' << subsystem_name << " does not have a 'Type' in file '" << filename << "'!\n" << std::flush; } Str_Copy(subsystem_type, entry_data, sizeof(subsystem_type)); subsystem_name = entry_data; break; } subsystem_entry = subsystem_entry->GetNextEntry(); ++mapping->subsystemID; } Unregister_Pointer(filename); delete filename; if (!subsystem_entry) { DEBUG_STREAM << model_name << ":subsystem '" << subsystem_name << "' does not exist!\n" << std::flush; Unregister_Object(sys_file); delete sys_file; Unregister_Object(sublist); delete sublist; Unregister_Pointer(stream); delete[] stream; goto Unwind_1; } Unregister_Object(sys_file); delete sys_file; Unregister_Object(sublist); delete sublist; } } // //-------------------------- // Find the Mode to use //-------------------------- // const char *entry_string; if ( !mapping_file->GetEntry( page_name, "Mode", &entry_string ) ) { DEBUG_STREAM << model_name << ':' << page_name << " is missing Mode!\n" << std::flush; Unwind_3: Unregister_Pointer(stream); delete[] stream; goto Unwind_1; } CString match_filter_string = entry_string, mode_string, null_string = NULL; ModeMask mode_mask; mapping->modeMask = (ModeMask) 0; { int i; for( i=0; (mode_string=match_filter_string.GetNthToken(i)) != null_string; ++i ) { Logical result = current_tool->ConvertStringToModeMask( mode_string, &mode_mask ); if (result == False) { DEBUG_STREAM << model_name << ':' << page_name << " has an unsupported Mode type '" << mode_string << "'\n" << std::flush; goto Unwind_3; } mapping->modeMask |= mode_mask; } if (i == 0) { DEBUG_STREAM << model_name << ':' << page_name << " has no Mode specified!\n" << std::flush; goto Unwind_3; } } // //------------------------------------ // Find the type of connection to make //------------------------------------ // const char *type; if ( !mapping_file->GetEntry( page_name, "Type", &type ) ) { DEBUG_STREAM << model_name << ':' << page_name << " is missing type!\n" << std::flush; goto Unwind_3; } Simulation::SharedData *shared_data = GetSubsystemSharedData(subsystem_type); if (!shared_data) { DEBUG_STREAM << model_name << ':' << subsystem_name << " is an unsupported subsystem!\n" << std::flush; goto Unwind_3; } // //---------------------------------------------------------------------- // If this is an event mapping, we have to have a message ID. //---------------------------------------------------------------------- // if (!strcmp(type, "EventMapping")) { mapping->mappingType = ControlsMapping::EventMapping; const char* message; if ( !mapping_file->GetEntry( page_name, "MessageID", &message ) ) { DEBUG_STREAM << model_name << ':' << page_name << " is missing MessageID!\n" << std::flush; goto Unwind_3; } const Receiver::HandlerEntry *entry = shared_data->activeMessageHandlers->Find(message); if (!entry) { DEBUG_STREAM << model_name << ':' << subsystem_name << ':' << message << " is an unsupported messageID!\n" << std::flush; goto Unwind_3; } mapping->messageID = entry->entryID; } // //---------------------------------------------------------------------- // If this is a direct mapping, we have to have an attribute ID. How do // we find an unknown attribute of an unknown subsystem? //---------------------------------------------------------------------- // else if (!strcmp(type, "DirectMapping")) { mapping->mappingType = ControlsMapping::DirectMapping; const char *attribute; if ( !mapping_file->GetEntry( page_name, "AttributeID", &attribute ) ) { DEBUG_STREAM << model_name << ':' << page_name << " is missing AttributeID!\n" << std::flush; goto Unwind_3; } const Simulation::IndexEntry *entry = shared_data->activeAttributeIndex->FindEntry(attribute); if (!entry) { DEBUG_STREAM << model_name << ':' << subsystem_name << ':' << attribute << " is an unsupported AttributeID!\n" << std::flush; goto Unwind_3; } mapping->attributeID = entry->entryID; } else { DEBUG_STREAM << model_name << ':' << page_name << " has unknown mapping type!\n" << std::flush; goto Unwind_3; } mappings = (int*)((char*)mappings + sizeof(ControlsMapping)); ++real_count; // //------------------------- // Move to the next mapping //------------------------- // entry = entry->GetNextEntry(); } // //-------------------------------------------------------------------- // Write the stream out to disk. Size is equal to the byte difference // between stream and subsystems //-------------------------------------------------------------------- // *stream = real_count; ResourceDescription *new_res = resource_file->AddResource( mapping_name, ResourceDescription::ControlMappingStreamResourceType, 1, ResourceDescription::Preload, stream, (char*)mappings - (char*)stream ); Check(new_res); // //------------------------- // Clean up after ourselves //------------------------- // Unregister_Pointer(stream); delete[] stream; Unregister_Object(namelist); delete namelist; Check_Fpu(); return new_res->resourceID; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // VTV* VTV::Make(VTV::MakeMessage *creation_message) { Check_Fpu(); return new VTV(creation_message); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // ResourceDescription::ResourceID VTV::CreateDamageZoneStream( ResourceFile *resource_file, const char *model_name, NotationFile *model_file, const ResourceDirectories *directories ) { Check(resource_file); Check_Pointer(model_name); Check(model_file); Check_Pointer(directories); // //------------------- // Find the .skl file //------------------- // const char *skl_entry; if ( !model_file->GetEntry( "video", "skeleton", &skl_entry ) ) { DEBUG_STREAM << model_name << " is missing .skl file specification!\n" << std::flush; Check_Fpu(); return -1; } char *skl_filename = MakePathedFilename(directories->videoDirectory, skl_entry); Register_Pointer(skl_filename); NotationFile *skl_file = new NotationFile(skl_filename); Register_Object(skl_file); if (!skl_file->PageCount()) { DEBUG_STREAM << skl_filename << " is empty or missing!\n" << std::flush; Dump_And_Die_1: Unregister_Pointer(skl_filename); delete[] skl_filename; Unregister_Object(skl_file); delete skl_file; Check_Fpu(); return ResourceDescription::NullResourceID; } // //------------------- // Find the .dmg file //------------------- // const char *dmg_entry; if ( !model_file->GetEntry( "gamedata", "DamageZones", &dmg_entry ) ) { DEBUG_STREAM << model_name << " is missing .dmg file specification!\n" << std::flush; goto Dump_And_Die_1; } char *dmg_filename = MakePathedFilename(directories->modelDirectory, dmg_entry); Register_Pointer(dmg_filename); NotationFile *dmg_file = new NotationFile(dmg_filename); Register_Object(dmg_file); if (!dmg_file->PageCount()) { DEBUG_STREAM << dmg_filename << " is empty or missing!\n" << std::flush; Dump_And_Die_2: Unregister_Pointer(dmg_filename); delete[] dmg_filename; Unregister_Object(dmg_file); delete dmg_file; goto Dump_And_Die_1; } // // Get the number of Damage zones // int dzone_count; if ( !skl_file->GetEntry( "ROOT", "DZoneCount", &dzone_count ) ) { DEBUG_STREAM << model_name << " is missing DZoneCount \n" << std::flush; goto Dump_And_Die_2; } // // Make a stream big enough for all the damage zones // DynamicMemoryStream damage_zone_stream; // // write the Damage Zone Count Info from the // .dmg file to the stream // damage_zone_stream << dzone_count; // // Make an entry list of all the entries in the damage zone page // NameList *dzone_namelist = skl_file->MakeEntryList("DamageZones","dz_"); Register_Object(dzone_namelist); if (dzone_namelist->EntryCount() == 0) { DEBUG_STREAM << "No dZones listed in DamageZones Page"<GetFirstEntry(); while (dzone_entry) { ++num_dzones_found; // // Get dzone name in .skl file // Str_Copy( current_dzone_name, dzone_entry->GetName(), sizeof(current_dzone_name) ); // // Create the stream for this damage zone // This Should Default to VTV::DamageZone::Create // VTV__DamageZone::CreateStreamedDamageZone( model_file, model_name, skl_file, current_dzone_name, &damage_zone_stream, dmg_file, directories ); // // Get next dzone entry in this segment page // dzone_entry = dzone_entry->GetNextEntry(); } // End while more dzone_entries if(dzone_count != num_dzones_found) { DEBUG_STREAM << "DZoneCount != damage zones found in Page DamageZones"<AddResourceMemoryStream( model_name, ResourceDescription::DamageZoneStreamResourceType, 1, ResourceDescription::Preload, &damage_zone_stream ); Check(new_res); // // Free mem // Unregister_Pointer(skl_filename); delete skl_filename; Unregister_Object(skl_file); delete skl_file; Unregister_Pointer(dmg_filename); delete dmg_filename; Unregister_Object(dmg_file); delete dmg_file; Unregister_Object(dzone_namelist); delete dzone_namelist; Check_Fpu(); return new_res->resourceID; } //############################################################################# //VTV::DamageZone // void VTV__DamageZone::TakeDamage(Damage& damage) { Check(this); Check_Pointer(&damage); DamageZone::TakeDamage(damage); // //--------------------------------- // If the VTV is dead, make it burn //--------------------------------- // VTV *vtv = (VTV*)GetOwningSimulation(); Check(vtv); if (damageLevel >= 1.0f) { vtv->SetSimulationState(VTV::BurningState); } // //---------------------------------------------------------------------- // If this VTV is linked to a player, send the score loss to that player //---------------------------------------------------------------------- // RPPlayer *rp_player = (RPPlayer*) vtv->GetPlayerLink(); if (rp_player) { Check(rp_player); // //-------------------------------------------------------- // If we died, send the I'm dead now message to the player //-------------------------------------------------------- // if ( vtv->GetSimulationState() == VTV::BurningState && vtv->simulationState.GetOldState() != VTV::BurningState ) { RPPlayer::VehicleDeadMessage dead( RPPlayer::VehicleDeadMessageID, sizeof(RPPlayer::VehicleDeadMessage) ); rp_player->Dispatch(&dead); } // //------------------------------------------- // Notify this player that damaged was caused //------------------------------------------- // RPPlayer::TakeDamageMessage damage_message( RPPlayer::TakeDamageMessageID, sizeof(RPPlayer::TakeDamageMessage), rp_player->GetEntityID(), 0, damage ); rp_player->Dispatch(&damage_message); } Check_Fpu(); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // VTV__DamageZone::VTV__DamageZone(VTV *vtv, int damage_zone_index, MemoryStream *damage_zone_stream) : DamageZone(vtv, damage_zone_index, damage_zone_stream) { Scalar dmg_data(vtv->maxImpactSpeed); dmg_data /= 3.6f; damageScale[Damage::CollisionDamageType] = 2000.0f / vtv->moverMass / (dmg_data*dmg_data) / ( 1.0f - vtv->elasticityCoefficient * vtv->elasticityCoefficient ); } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Logical VTV__DamageZone::CreateStreamedDamageZone( NotationFile *model_file, const char *model_name, NotationFile *skl_file, const char *damage_zone_name, MemoryStream *damage_zone_stream, NotationFile *dmg_file, const ResourceDirectories *directories ) { if(!DamageZone::CreateStreamedDamageZone( model_file, model_name, skl_file, damage_zone_name, damage_zone_stream, dmg_file, directories ) ) { Check_Fpu(); return -1; } Check_Fpu(); return True; } //~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ // Logical VTV::TestInstance() const { return IsDerivedFrom(*GetClassDerivations()); }