//---------------------------------------------------------------------------------------------------------------------------------------------------
//
// Copyright (C)2007 DarkWynter Studios. All rights reserved.
//
//---------------------------------------------------------------------------------------------------------------------------------------------------
// {License Information: Creative Commons}
//---------------------------------------------------------------------------------------------------------------------------------------------------
#region Comments
/*
* We'll need to interface Collision when we do gpgpu collision
* This way we can call either cpu or gpu configurations dependent on machine caps
*
* This will also provide us a clean break for a dll
*
*
*/
#endregion
namespace ElementalGame
{
#region Using Statements
using System;
using System.Collections.Generic;
using System.Threading;
using System.Diagnostics;
using Microsoft.Xna.Framework;
using Microsoft.Xna.Framework.Audio;
using Microsoft.Xna.Framework.Content;
using Microsoft.Xna.Framework.Graphics;
using Microsoft.Xna.Framework.Input;
using Microsoft.Xna.Framework.Storage;
#endregion
// Main class where all of our object collisions are handled place
public class Collision
{
// Attributes:
Vector3 tangentialComponent = new Vector3();
Vector3 normalComponent = new Vector3();
float timeInterval;
Vector3 normalForce1, normalForce2; // Stores each object's normal force component
//constant max mass
private const float MAXIMUM_MASS = 35;
// Constructor:
public Collision() { }
// Calls all the Collision functions in order
public void UpdateCollision(ObjectLibrary objectLibrary)
{
timeInterval = ElementalGame.dt;
List newDynamicObject = new List();
List dynamicMasses = ObjectLibrary.lookupMap.GetDynamicList();
bool deletedDynamic = false;
for (int i = 0; i < objectLibrary.humans.Count; i++)
{
objectLibrary.humans[i].hitWhere.Clear();
}
for (int i = 0; i < objectLibrary.bots.Count; i++)
{
objectLibrary.bots[i].hitWhere.Clear();
}
// For each moving GameObject
for (int i = 0; i < dynamicMasses.Count; i++)
{
// Need to clear out some values for collision
dynamicMasses[i].objectHitBy.Clear();
// For each object in the vacinity of the gameObject
LinkedList collidingObjects = ObjectLibrary.lookupMap.GetCollisionObjects(dynamicMasses[i]);
LinkedListNode node = collidingObjects.First;
if (collidingObjects.Count != 0 && dynamicMasses[i].gameObjectPointer != null)
{
Mass cObject;
do
{
cObject = node.Value;
// If not already checked
if (!cObject.collisionChecked && cObject.gameObjectPointer != null)
{
// If collided
if (dynamicMasses[i].gameObjectPointer.boundingSphere.Intersects(cObject.gameObjectPointer.boundingSphere))
{
// Check if the colliding object is already in our dynamic list, if not then add it to our list of objects which need
// to be added to the dynamic list
if (!dynamicMasses.Contains(cObject) && !newDynamicObject.Contains(cObject))
{
newDynamicObject.Add(cObject);
}
// Cohesion
if (dynamicMasses[i].objectType == Mass.ObjectType.PARTICLE && cObject.objectType == Mass.ObjectType.PARTICLE)
{
// Particle Interaction
if (ParticleInteraction(dynamicMasses[i], cObject))
{
if (i < dynamicMasses.Count)
{
deletedDynamic = true;
ObjectTerrainCollision(dynamicMasses[i], (int)dynamicMasses[i].objectHeight, objectLibrary.terrain);
i--;
}
else
{
i--;
ObjectTerrainCollision(dynamicMasses[i], (int)dynamicMasses[i].objectHeight, objectLibrary.terrain);
}
break;
}
}
// Collectable Objects
else if (dynamicMasses[i].objectType == Mass.ObjectType.PLAYER && cObject.gameObjectPointer.isCollectable)
{
// Increment Number of Keys found
Player player = (Player)dynamicMasses[i].gameObjectPointer;
if (player.IsHuman())
{
Audio.KeyFound();
if (GameFlow.gameType == GameFlow.GameType.STORY_MODE)
{
player.numberOfKeysFound++;
}
player.ChangeHealth(XML.GameSettings.HEALTH_BOOST_FOR_KEY, objectLibrary);
ObjectLibrary.lookupMap.RemoveLast(cObject);
ObjectLibrary.lookupMap.RemoveCurrent(cObject);
if (ObjectLibrary.lookupMap.DynamicContains(cObject))
{
ObjectLibrary.lookupMap.RemoveDynamic(cObject);
}
cObject.ClearValues();
cObject.deadObject = true;
((Human)player).ShowKeyIndicator();
}
}
else if (dynamicMasses[i].gameObjectPointer.isCollectable && cObject.objectType == Mass.ObjectType.PLAYER)
{
// Increment Number of Keys found
Player player = (Player)cObject.gameObjectPointer;
if (player.IsHuman())
{
player.numberOfKeysFound++;
ObjectLibrary.lookupMap.RemoveLast(dynamicMasses[i]);
ObjectLibrary.lookupMap.RemoveCurrent(dynamicMasses[i]);
if (ObjectLibrary.lookupMap.DynamicContains(dynamicMasses[i]))
{
ObjectLibrary.lookupMap.RemoveDynamic(dynamicMasses[i]);
}
dynamicMasses[i].ClearValues();
dynamicMasses[i].deadObject = true;
((Human)player).ShowKeyIndicator();
// Remove Collectable from list and give to player
if (i < dynamicMasses.Count)
{
deletedDynamic = true;
i--;
}
else
{
i--;
}
break;
}
}
else
{
// Adhesion
//else if ((dynamicMasses[i].objectType == Mass.ObjectType.PARTICLE && cObject != Mass.ObjectType.PARTICLE)||
// (dynamicMasses[i].objectType != Mass.ObjectType.PARTICLE && cObject == Mass.ObjectType.PARTICLE) )
//{
//}
// Handle collision physics
ObjectObjectCollision(dynamicMasses[i], cObject);
if ((dynamicMasses[i].objectType == Mass.ObjectType.PLAYER && cObject.objectType == Mass.ObjectType.PARTICLE) ||
(dynamicMasses[i].objectType == Mass.ObjectType.PARTICLE && cObject.objectType == Mass.ObjectType.PLAYER))
{
if (dynamicMasses[i].objectType == Mass.ObjectType.PLAYER)
{
if (((Player)dynamicMasses[i].gameObjectPointer).IsAlive())
{
PlayerHitByParticle((Player)dynamicMasses[i].gameObjectPointer, normalForce1,
(Particle)cObject.gameObjectPointer, normalForce2, objectLibrary);
Player player = (Player)dynamicMasses[i].gameObjectPointer;
if (player.IsAlive() == false)
{
AssignKillCredit(player, objectLibrary);
}
}
}
else
{
if (((Player)cObject.gameObjectPointer).IsAlive())
{
if (PlayerHitByParticle((Player)cObject.gameObjectPointer, normalForce2,
(Particle)dynamicMasses[i].gameObjectPointer, normalForce1, objectLibrary))
{
Player player = (Player)cObject.gameObjectPointer;
if (player.IsAlive() == false)
{
AssignKillCredit(player, objectLibrary);
}
if (i < dynamicMasses.Count)
{
deletedDynamic = true;
ObjectTerrainCollision(dynamicMasses[i],
(int)dynamicMasses[i].objectHeight,
objectLibrary.terrain);
i--;
}
else
{
i--;
ObjectTerrainCollision(dynamicMasses[i],
(int)dynamicMasses[i].objectHeight,
objectLibrary.terrain);
}
break;
}
}
}
}
}
}
}
node = node.Next;
} while (node != null);
if (deletedDynamic)
{
deletedDynamic = false;
continue;
}
// Mark that object has been checked for collisions
dynamicMasses[i].collisionChecked = true;
}
////////////////////////////////////////
//only do for player for now
if (dynamicMasses[i].objectType == Mass.ObjectType.PLAYER)// || dynamicMasses[i].objectType == Mass.ObjectType.PARTICLE)
{
Sensor collisionSensor;
float kickBackRatio = 2.0f;
collisionSensor = ((Player)dynamicMasses[i].gameObjectPointer).collisionSensor;
float heightDifference = ObjectTerrainCollision(collisionSensor.mass, (int)dynamicMasses[i].objectHeight, objectLibrary.terrain);
Vector3 sensorDiff = collisionSensor.mass.currentPosition - dynamicMasses[i].currentPosition;
//if primarily pointing down.. ignore
if (sensorDiff.Y < 0 && (Math.Abs(sensorDiff.Y) > Math.Abs(sensorDiff.X) && Math.Abs(sensorDiff.Y) > Math.Abs(sensorDiff.Z)))
{
}
else if (heightDifference >= XML.PlayerSettings.DEFAULT_PLAYER_HEIGHT)
{
// Apply a negative velocity force to the player to slow them down
float sensorDistance = sensorDiff.Length();
dynamicMasses[i].KickBack(kickBackRatio);
dynamicMasses[i].objectHitBy.Add(Mass.TerrainType.TERRAIN_GROUND);
}
}
// Check if particle and not moving much
//else if (dynamicMasses[i].objectType == Mass.ObjectType.PARTICLE)
//{
// if (dynamicMasses[i].velocity.Length() < 50.0f)
// {
// if (dynamicMasses[i].mass > 0.1f)
// {
// //shrink the particle
// dynamicMasses[i].mass -= 0.001f;
// float massRatio = (dynamicMasses[i].mass / 75.0f);
// dynamicMasses[i].scale = massRatio * 30.0f + 3;//old way: particleMassValue * 0.50f;
// }
// else
// {
// //Get rid of it altogether
// ObjectLibrary.lookupMap.RemoveLast(dynamicMasses[i]);
// ObjectLibrary.lookupMap.RemoveCurrent(dynamicMasses[i]);
// if (ObjectLibrary.lookupMap.DynamicContains(dynamicMasses[i]))
// {
// ObjectLibrary.lookupMap.RemoveDynamic(dynamicMasses[i]);
// dynamicMasses[i].ClearValues();
// }
// dynamicMasses[i].ClearValues();
// }
// }
//}
ObjectTerrainCollision(dynamicMasses[i], (int)dynamicMasses[i].objectHeight, objectLibrary.terrain);
}
ObjectLibrary.lookupMap.AddRange(newDynamicObject);
}
public static void AssignKillCredit(Player killedPlayer, ObjectLibrary objectLibrary)
{
// Insert killed by stuff here
// They were alive going into this method if they come out dead then
// We know someone killed them and can assign credit
// If the player did not suicide find out who killed them
if (killedPlayer.killedBy != killedPlayer.playerIndex)
{
if (killedPlayer.killedBy < 4)
{
// Find the killer who is human
for (int i = 0; i < objectLibrary.humans.Count; i++)
{
// Assign credit to the killer
if (killedPlayer.killedBy == objectLibrary.humans[i].playerIndex)
{
objectLibrary.humans[i].kills++;
}
}
}
else
{
// Find the bot that killed
for (int i = 0; i < objectLibrary.bots.Count; i++)
{
// Assign credit to the killer
if (killedPlayer.killedBy == objectLibrary.bots[i].playerIndex)
{
objectLibrary.bots[i].kills++;
}
}
}
}
else
{
killedPlayer.kills--;
}
}
private bool ParticleInteraction(Mass mass1, Mass mass2)
{
Vector3 object1NormalVelocity = new Vector3(); // Stores the first object's normal velocity
Vector3 object2NormalVelocity = new Vector3(); // Stores the second object's normal velocity
Vector3 totalNormalVelocity = new Vector3(); // Stores the sum of both object's normal velocities
float stickiness;
float totalMass = mass1.mass + mass2.mass; // Sum of both masses
Vector3 normalVectorOfImpact = new Vector3(); // Stores the normal vector of impact/contact
Particle particle1 = ((Particle)mass1.gameObjectPointer);
Particle particle2 = ((Particle)mass1.gameObjectPointer);
if (particle1.particleType == particle2.particleType)
{
stickiness = (mass1.cohesion + mass2.cohesion) / 2.0f;
}
else
{
stickiness = (mass1.adhesion + mass2.adhesion) / 2.0f;
}
// Check which mass has the greater velocity and calculate the normal based on that
if (mass1.velocity.Length() >= mass2.velocity.Length())
{
// Mass1 has higher velocity so impact vector is mass2 - mass1, ie, from mass1 to mass2
normalVectorOfImpact = mass2.currentPosition - mass1.currentPosition;
if (normalVectorOfImpact != Vector3.Zero)
{
normalVectorOfImpact.Normalize();
}
// Ensure that mass1's velocity is not equal to 0
if (mass1.velocity.Length() != 0.0f)
{
// Check that the velocity is not moving away from the impact, ie, the angle between velocity
// and impact vector is between 90->0->270. If so the two objects are moving away from each
// other, so return.
if (Vector3.Dot(mass1.velocity, normalVectorOfImpact) / mass1.velocity.Length() < 0.0f)
{
return false;
}
}
// Calculate Mass1's normal velocity component
CalculateVectorComponents(normalVectorOfImpact, mass1.velocity);
object1NormalVelocity = normalComponent;
// Calculate Mass2's normal velocity component
CalculateVectorComponents(-normalVectorOfImpact, mass2.velocity);
object2NormalVelocity = normalComponent;
// Find the difference between these two velocity to give our final resultant velocity
totalNormalVelocity = object1NormalVelocity - object2NormalVelocity;
}
else
{
// Mass2 has higher velocity so impact vector is mass1 - mass2, ie, from mass2 to mass1
normalVectorOfImpact = mass1.currentPosition - mass2.currentPosition;
if (normalVectorOfImpact != Vector3.Zero)
{
normalVectorOfImpact.Normalize();
}
if (mass2.velocity.Length() != 0.0f)
{
// Check that the velocity is not moving away from the impact, ie, the angle between velocity
// and impact vector is between 90->0->270. If so the two objects are moving away from each
// other, so return.
if (Vector3.Dot(mass2.velocity, normalVectorOfImpact) / mass2.velocity.Length() < 0.0f)
{
return false;
}
}
// Calculate Mass1's normal velocity component
CalculateVectorComponents(normalVectorOfImpact, mass1.velocity);
object1NormalVelocity = normalComponent;
// Calculate Mass2's normal velocity component
CalculateVectorComponents(-normalVectorOfImpact, mass2.velocity);
object2NormalVelocity = normalComponent;
// Find the difference between these two velocity to give our final resultant velocity
totalNormalVelocity = object2NormalVelocity - object1NormalVelocity;
}
if (totalNormalVelocity.Length() >= 15.0f)
{
normalForce1 = 2.0f * mass2.mass * totalNormalVelocity * stickiness / (timeInterval * totalMass);
normalForce2 = 2.0f * mass1.mass * -totalNormalVelocity * stickiness / (timeInterval * totalMass);
// Add the forces to the respective masses
mass1.AddForce(normalForce1);
mass2.AddForce(normalForce2);
mass1.isMoving = true;
mass2.isMoving = true;
}
else
{
if (((Particle)mass1.gameObjectPointer).particleType == ((Particle)mass2.gameObjectPointer).particleType)
{
//Some particles can't combine..
//Earth boulders don't combine
if ((((Particle)mass1.gameObjectPointer).particleType == Particle.ParticleType.Earth && (mass1.energy < 0.2f && mass2.energy < 0.2f)) == false &&
//Ice balls can't combine
(((Particle)mass1.gameObjectPointer).particleType == Particle.ParticleType.Water && (mass1.energy < -0.2f && mass2.energy < -0.2f)) == false &&
//Make sure things don't get TOO big
(mass1.mass + mass2.mass) < MAXIMUM_MASS)
{
mass1.energy = ((mass1.energy * mass1.mass) + (mass2.energy * mass2.mass)) / (mass1.mass + mass2.mass);
mass1.currentPosition = ((mass1.currentPosition * mass1.mass) + (mass2.currentPosition * mass2.mass)) / (mass1.mass + mass2.mass);
mass1.lastPosition = ((mass1.lastPosition * mass1.mass) + (mass2.lastPosition * mass2.mass)) / (mass1.mass + mass2.mass);
mass1.totalForce = ((mass1.totalForce * mass1.mass) + (mass2.totalForce * mass2.mass)) / (mass1.mass + mass2.mass);
mass1.velocity = ((mass1.velocity * mass1.mass) + (mass2.velocity * mass2.mass)) / (mass1.mass + mass2.mass);
mass1.objectHeight = mass1.objectHeight + mass2.objectHeight;
mass1.mass += mass2.mass;
mass1.scale = mass1.scale + mass2.scale;
//a hack for fixing the bounding sphere issue
particle1._boundingSphere.Radius = particle1._boundingSphere.Radius + particle2._boundingSphere.Radius;
particle1._boundingSphere.Radius *= 0.8f;
ObjectLibrary.lookupMap.RemoveLast(mass2);
ObjectLibrary.lookupMap.RemoveCurrent(mass2);
if (ObjectLibrary.lookupMap.DynamicContains(mass2))
{
ObjectLibrary.lookupMap.RemoveDynamic(mass2);
mass2.ClearValues();
mass2.deadObject = true;
return true;
}
mass2.ClearValues();
mass2.deadObject = true;
return false;
}
normalForce1 = 2.0f * mass2.mass * -totalNormalVelocity * stickiness / (timeInterval * totalMass);
normalForce2 = 2.0f * mass1.mass * totalNormalVelocity * stickiness / (timeInterval * totalMass);
// Add the forces to the respective masses
mass1.AddForce(normalForce1);
mass2.AddForce(normalForce2);
mass1.isMoving = true;
mass2.isMoving = true;
}
}
return false;
}
private bool PlayerHitByParticle(Player player, Vector3 playerCollisionForce, Particle particle, Vector3 particleCollisionForce, ObjectLibrary objectLibrary)
{
//Vector3 normalizedVelocity = particle.mass.velocity + Vector3.Zero;
//normalizedVelocity.Normalize();
//Ray particleRay = new Ray(particle.mass.lastPosition, normalizedVelocity);
//float? distanceToImpact = player.boundingSphere.Intersects(particleRay);
//float distanceMoved = Vector3.Distance(particle.mass.currentPosition, particle.mass.lastPosition);
//bool intersects = false;
//if (distanceToImpact != null)
//{
// if ((float)distanceToImpact <= distanceMoved)
// {
// intersects = true;
// }
//}
//if (player.boundingSphere.Intersects(particle.boundingSphere))
//{
// intersects = true;
//}
if ((particle.hasCollided != player.playerIndex && particle.ownerID != player.playerIndex) ||
(particle.hurtOwner == true && particle.ownerID == player.playerIndex))
{
// We know player is hit... determine which general direction
Vector3 toParticle = new Vector3(particle.mass.currentPosition.X - player.mass.currentPosition.X,
particle.mass.currentPosition.Y - player.mass.currentPosition.Y,
particle.mass.currentPosition.Z - player.mass.currentPosition.Z);
player.SetHitShader(toParticle, particle);
Vector3 playerLook = new Vector3(player.mass.normalVector.X, 0, player.mass.normalVector.Z);
playerLook.Normalize();
Vector3 cross = Vector3.Cross(toParticle, playerLook);
if (cross.Y < 0)
{
player.hitWhere.Add(Player.HitWhere.LEFT);
}
else
{
player.hitWhere.Add(Player.HitWhere.RIGHT);
}
// Player shielded properly.. no damage and absorbs the particle
if ((player.activeShield == Player.ShieldType.EARTH_SHIELD && particle.particleType == Particle.ParticleType.Earth) ||
(player.activeShield == Player.ShieldType.WATER_SHIELD && particle.particleType == Particle.ParticleType.Water) ||
(player.activeShield == Player.ShieldType.WIND_SHIELD && particle.particleType == Particle.ParticleType.Air))
{
// Correct shield -> player absorbs manna
player.ChangeManna(XML.PlayerSettings.SHIELD_MANNA_GAIN * particle.mass.mass);
if (player.IsHuman())
{
((Human)player).ShowMannaIndicator();
}
//Get rid of it altogether
ObjectLibrary.lookupMap.RemoveLast(particle.mass);
ObjectLibrary.lookupMap.RemoveCurrent(particle.mass);
if (ObjectLibrary.lookupMap.DynamicContains(particle.mass))
{
ObjectLibrary.lookupMap.RemoveDynamic(particle.mass);
}
particle.mass.ClearValues();
particle.mass.deadObject = true;
return true;
}
else
{
// Wrong shield -> player loses health
//player.ChangeHealth(-XML.PlayerSettings.DIRECT_HEALTH_LOSS * particle.mass.scale);
player.ChangeHealth(DamageCalculations(particle.mass, player.mass), objectLibrary);
// If tornado.. apply random force!
if (particle.particleType == Particle.ParticleType.Air)
{
Random rand = new Random();
float multiplier = particle.mass.mass * 100000;
player.mass.AddForce(new Vector3(0, multiplier, 0));
}
// If the player is dead and they haven't been assigned who killed them then do so
// Also need to set their killed by info
if (!player.IsAlive() && player.killedBy == -1)
{
player.Kill(particle.ownerID);
if (player.playerIndex < 4)
{
//Its a human.. set its killedby HUD info
((Human)player).SetKilledByInfo(particle);
}
}
}
particle.hasCollided = player.playerIndex;
particle.hasCollidedTimer = Stopwatch.StartNew();
}
return false;
}
public float DamageCalculations(Mass particleMass, Mass playerMass)
{
float damage = 0.0f;
Particle particle = ((Particle)particleMass.gameObjectPointer);
// ~ about between 0 and 1 for slow to fast
float particleVelocityRatio = particleMass.velocity.Length() / 2000.0f;
// Velocity.Length() range~
// Earth: 200-1600
// Water: 400-1700
// Air: 500-1700
// Particle type based damage modifier
if (particle.particleType == Particle.ParticleType.Earth)
{
if (particleMass.energy > 0.3f)
{
//lava hurts more
if (particleMass.velocity.Length() < 50)
{
//its slower.. it hurts more?
damage = 25.0f;
}
else
{
damage = 10.0f;
}
}
else
{
//frozen rock and rock are the same
damage = 5.0f + particleVelocityRatio;
}
}
else if (particle.particleType == Particle.ParticleType.Water)
{
if (particleMass.energy > 0.5f)
{
//boiling water hurts alot
if (particleMass.velocity.Length() < 50)
{
damage = 25.0f;
}
else
{
damage = 20.0f;
}
}
else if (particleMass.energy < 0.0f)
{
//frozen ice hurts a bit more
damage = 3.0f + particleVelocityRatio;
}
else
{
//regular water doesn't hurt that much
if (particleMass.velocity.Length() < 50)
{
//slow water doesn't hurt
damage = 0.0f;
}
else
{
damage = 10.0f;
}
}
}
else //air particle is all the same
{
damage = 0.1f + particleVelocityRatio;
}
float totalDamage = -damage * (1 + (particleMass.mass / 5.0f)) * XML.PlayerSettings.DIRECT_HEALTH_LOSS;
Debug.WriteLine("Particle type=" + particle.particleType.ToString() + ",mass=" + particleMass.mass + ", vRatio=" + particleVelocityRatio
+ ", v=" + particleMass.velocity.Length() + " damage=" + damage + "...total= " + totalDamage + ".... energy=" + particleMass.energy);
return totalDamage;
}
// Handles collisions between two objects. Note: All references to normal in this function refers to the normal of impact
private void ObjectObjectCollision(Mass mass1, Mass mass2)
{
//check to see if its a player and a liquid particle
//check if mass1 is the player
if (mass1.objectType == Mass.ObjectType.PLAYER && mass2.objectType == Mass.ObjectType.PARTICLE)
{
Particle particle = (Particle)mass2.gameObjectPointer;
if (particle.particleType == Particle.ParticleType.Water && particle.mass.energy > 0 && particle.mass.energy < 0.5)
{
//its regular water
return;
}
}
//check if mass2 is the player
if (mass2.objectType == Mass.ObjectType.PLAYER && mass1.objectType == Mass.ObjectType.PARTICLE)
{
Particle particle = (Particle)mass1.gameObjectPointer;
if (particle.particleType == Particle.ParticleType.Water && particle.mass.energy > 0 && particle.mass.energy < 0.5)
{
//its regular water
return;
}
}
Vector3 object1NormalVelocity = new Vector3(); // Stores the first object's normal velocity
Vector3 object2NormalVelocity = new Vector3(); // Stores the second object's normal velocity
Vector3 totalNormalVelocity = new Vector3(); // Stores the sum of both object's normal velocities
float totalMass = mass1.mass + mass2.mass; // Sum of both masses
Vector3 normalVectorOfImpact = new Vector3(); // Stores the normal vector of impact/contact
// Check which mass has the greater velocity and calculate the normal based on that
if (mass1.velocity.Length() >= mass2.velocity.Length())
{
// Mass1 has higher velocity so impact vector is mass2 - mass1, ie, from mass1 to mass2
normalVectorOfImpact = mass2.currentPosition - mass1.currentPosition;
if (normalVectorOfImpact != Vector3.Zero)
{
normalVectorOfImpact.Normalize();
}
// Ensure that mass1's velocity is not equal to 0
if (mass1.velocity.Length() != 0.0f)
{
// Check that the velocity is not moving away from the impact, ie, the angle between velocity
// and impact vector is between 90->0->270. If so the two objects are moving away from each
// other, so return.
if (Vector3.Dot(mass1.velocity, normalVectorOfImpact) / mass1.velocity.Length() < 0.0f)
{
return;
}
}
// Calculate Mass1's normal velocity component
CalculateVectorComponents(normalVectorOfImpact, mass1.velocity);
object1NormalVelocity = normalComponent;
// Calculate Mass2's normal velocity component
CalculateVectorComponents(-normalVectorOfImpact, mass2.velocity);
object2NormalVelocity = normalComponent;
// Find the difference between these two velocity to give our final resultant velocity
totalNormalVelocity = object1NormalVelocity - object2NormalVelocity;
// Based on In-elastic collision v1 = (m2 * totalforce)/(m1 + m2). The rest of the stuff is
// to convert a velocity into a force.
normalForce1 = 2.0f * mass2.mass * -totalNormalVelocity / (timeInterval * totalMass);
normalForce2 = 2.0f * mass1.mass * totalNormalVelocity / (timeInterval * totalMass);
}
else
{
// Mass2 has higher velocity so impact vector is mass1 - mass2, ie, from mass2 to mass1
normalVectorOfImpact = mass1.currentPosition - mass2.currentPosition;
if (normalVectorOfImpact != Vector3.Zero)
{
normalVectorOfImpact.Normalize();
}
if (mass1.velocity.Length() != 0.0f)
{
// Check that the velocity is not moving away from the impact, ie, the angle between velocity
// and impact vector is between 90->0->270. If so the two objects are moving away from each
// other, so return.
if (Vector3.Dot(mass2.velocity, normalVectorOfImpact) / mass2.velocity.Length() < 0.0f)
{
return;
}
}
// Calculate Mass1's normal velocity component
CalculateVectorComponents(normalVectorOfImpact, mass1.velocity);
object1NormalVelocity = normalComponent;
// Calculate Mass2's normal velocity component
CalculateVectorComponents(-normalVectorOfImpact, mass2.velocity);
object2NormalVelocity = normalComponent;
// Find the difference between these two velocity to give our final resultant velocity
totalNormalVelocity = object2NormalVelocity - object1NormalVelocity;
// Based on In-elastic collision v1 = (m2 * totalforce)/(m1 + m2). The rest of the stuff is
// to convert a velocity into a force.
normalForce1 = 2.0f * mass2.mass * totalNormalVelocity / (timeInterval * totalMass);
normalForce2 = 2.0f * mass1.mass * -totalNormalVelocity / (timeInterval * totalMass);
}
// Add the forces to the respective masses
mass1.AddForce(normalForce1);
mass2.AddForce(normalForce2);
mass1.numberOfCollision++;
mass2.numberOfCollision++;
// Set each mass's boolean check so that it will get updated next cycle
mass1.isMoving = true;
mass2.isMoving = true;
}
// Collisions between an object and the terrain. The float return value is the height difference of the object and the terrain
public float ObjectTerrainCollision(Mass mass, int objectHeight, Terrain terrain)
{
// Used to calculate the average terrain height around the player
float totalHeight = 0;
// Get X and Z positions
int posX = (int)mass.currentPosition.X;
int posZ = (int)mass.currentPosition.Z;
// Make sure we don't go out of bounds
if (posX <= 0)
{
posX = 1;
}
if (posZ <= 0)
{
posZ = 1;
}
if (posX >= Terrain.collisionMapSize)
{
posX = Terrain.collisionMapSize - 1;
}
if (posZ >= Terrain.collisionMapSize)
{
posZ = Terrain.collisionMapSize - 1;
}
// Add up all neighboring terrain points
totalHeight = terrain.GetTerrainHeight(posX / Terrain.terrainScaleFactor, posZ / Terrain.terrainScaleFactor);
// Average all the neighboring terrain points
float newHeight = totalHeight + (objectHeight / 2);
// Get difference between average and player
float heightDifference = newHeight - mass.currentPosition.Y;
Vector3 totalForce = mass.totalForce + Vector3.Zero;
Vector3 velocity = mass.velocity + Vector3.Zero;
if (mass.objectType == Mass.ObjectType.SENSOR)
{
return heightDifference;
}
// If terrain and player are touching
if (heightDifference < 0.0f)
{
mass.AddForce(mass.mass * Mass.accelDueToGravity);
}
else if (heightDifference > 0.0f)
{
Vector3 surfaceNormal = terrain.GetTerrainNormal(posX / Terrain.terrainScaleFactor, posZ / Terrain.terrainScaleFactor, mass.velocity);
//Vector3.Zero + terrain.vertices[(int)((posZ / Terrain.terrainScaleFactor) +
// (posX / Terrain.terrainScaleFactor) * Terrain.vertexMapSize)
// ].Normal;
// Calculate the components of gravity with reference to surface normal
CalculateVectorComponents(surfaceNormal, Mass.accelDueToGravity);
Vector3 gravityNormalComponent = normalComponent;
Vector3 gravityTangentialComponent = tangentialComponent;
// Resolve all forces (gravity n totalForce) and velocity into their normal and tangential components based on the surface normal
// Calculate the components of our velocity with reference to the surface normal
CalculateVectorComponents(surfaceNormal, velocity);
Vector3 velocityNormalComponent = normalComponent;
Vector3 velocityTangentialComponent = tangentialComponent;
CalculateVectorComponents(surfaceNormal, totalForce);
Vector3 totalForceNormalComponent = normalComponent;
Vector3 totalForceTangentialComponent = tangentialComponent;
Vector3 combinedNormalForce = (mass.mass * velocityNormalComponent / timeInterval) + totalForceNormalComponent;
Vector3 combinedTangentialForce = (mass.mass * velocityTangentialComponent / timeInterval) +
totalForceTangentialComponent +
(mass.mass * gravityTangentialComponent);
mass.numberOfCollision++;
if (mass.objectType == Mass.ObjectType.PARTICLE)
{
if (combinedNormalForce.Y < 0.0f)
{
mass.AddForce(-combinedNormalForce);
}
else
{
mass.AddForce(-velocityNormalComponent);
}
mass.AddForce(heightDifference * surfaceNormal * terrain.particleSurfaceTension * mass.COR);
if (combinedTangentialForce.Length() >= 0.5f)
{
mass.AddForce(mass.mass * -gravityTangentialComponent);
mass.AddFriction(mass.dynamicFrictionCoefficient + terrain.mass.dynamicFrictionCoefficient,
(mass.mass * gravityNormalComponent) + totalForceNormalComponent,
combinedTangentialForce);
}
else
{
mass.AddForce(mass.mass * -velocityTangentialComponent / timeInterval);
mass.AddForce(-totalForceTangentialComponent);
}
}
else
{
if (totalForce.Y > 0.0f)
{
totalForce.Y = 0.0f;
}
if (velocity.Y > 0.0f)
{
velocity.Y = 0.0f;
}
if (mass.objectType == Mass.ObjectType.PLAYER)
{
if (mass.lastMaxHeight - totalHeight > 1000 && velocity.Length() > 500)
{
// Player hit the terrain hard while falling
mass.objectHitBy.Add(Mass.TerrainType.TERRAIN_GROUND);
mass.fallingDamageMultiplier = (mass.lastMaxHeight - totalHeight) / 100;
}
if (heightDifference < XML.PlayerSettings.DEFAULT_PLAYER_HEIGHT / 4.0f)
{
// Player hit a terrain thats low
mass.objectHitBy.Add(Mass.TerrainType.TERRAIN_LOW);
}
else if (heightDifference < XML.PlayerSettings.DEFAULT_PLAYER_HEIGHT / 3.0f)
{
// Player hit a terrain thats relatively high
mass.objectHitBy.Add(Mass.TerrainType.TERRAIN_MEDIUM);
}
else if (heightDifference >= XML.PlayerSettings.DEFAULT_PLAYER_HEIGHT)
{
// Player hit a terrain wall
mass.objectHitBy.Add(Mass.TerrainType.TERRAIN_HIGH);
}
// Ground pushing up to negate gravity
if (mass.movementType == Mass.MovementType.WALK)
{
//only if its NOT a wall
if (heightDifference < XML.PlayerSettings.DEFAULT_PLAYER_HEIGHT)
{
ObjectLibrary.lookupMap.RemoveLast(mass);
ObjectLibrary.lookupMap.RemoveCurrent(mass);
//just set their height!
mass.currentPosition.Y = totalHeight + (objectHeight / 2);
ObjectLibrary.lookupMap.SetPosition(mass);
//negate forces on player
mass.velocity = Vector3.Zero;
mass.totalForce = Vector3.Zero;
}
else
{
//mass.KickBack(5.0f);
//mass.currentPosition = mass.lastPosition + Vector3.Zero;
}
}
else if(mass.movementType == Mass.MovementType.HOVER)
{
mass.AddForce((heightDifference * terrain.playerSurfaceTension * surfaceNormal) -
(new Vector3(0.0f, totalForce.Y, 0.0f) +
new Vector3(0.0f, velocity.Y * mass.mass / timeInterval, 0.0f)));
if (velocityTangentialComponent.Length() > 10.0f)
{
//if (mass.velocity.Length() < XML.PlayerSettings.PLAYER_SPEED_ON_THE_GROUND / 10)
//{
// // Pass everything to friction (friction coefficients, the normal force on the body and the direction of friction)
// mass.AddFriction(mass.staticFrictionCoefficient + terrain.mass.staticFrictionCoefficient,
// (gravityNormalComponent * mass.mass),
// velocityTangentialComponent);
//}
//else
{
// Pass everything to friction (friction coefficients, the normal force on the body and the direction of friction)
mass.AddFriction(mass.dynamicFrictionCoefficient + terrain.mass.dynamicFrictionCoefficient,
(gravityNormalComponent * mass.mass),
velocityTangentialComponent);
}
}
else
{
mass.AddForce(mass.mass * velocityTangentialComponent / timeInterval);
}
}
mass.lastMaxHeight = 0;
}
else
{
//mass.AddForce((heightDifference * terrain.playerSurfaceTension * surfaceNormal) -
// (new Vector3(0.0f, totalForce.Y, 0.0f) +
// new Vector3(0.0f, velocity.Y * mass.mass / timeInterval, 0.0f)));
// Ground pushing up to negate gravity
mass.AddForce(new Vector3(0.0f, heightDifference * terrain.propSurfaceTension, 0.0f) -
(new Vector3(0.0f, totalForce.Y, 0.0f) +
new Vector3(0.0f, velocity.Y * mass.mass / timeInterval, 0.0f)));
}
// Calculate the components of our velocity with reference to the surface normal
//CalculateVectorComponents(surfaceNormal, mass.velocity);
}
}
return heightDifference;
}
public void CalculateVectorComponents(Vector3 surfaceNormal, Vector3 objectVelocity)
{
float vectorToNormalAngle, vectorToSurfaceAngle;
float normalComponentOfVector;
if (objectVelocity.Length() >= 0.01f || !float.IsInfinity(objectVelocity.Length()))
{
// Based on vector dot product to get the angle between the two vectors
vectorToNormalAngle = (float)Math.Acos(Vector3.Dot(surfaceNormal, objectVelocity) / objectVelocity.Length());
// Stupid check required due to XNA's inaccuracy
if (float.IsNaN(vectorToNormalAngle))
{
vectorToNormalAngle = 0.0f;
}
// The angle between the ground and the vector is pi/2 - previous angle
vectorToSurfaceAngle = (float)Math.PI / 2.0f - vectorToNormalAngle;
// Scalar component of the vector in the the normal direction
normalComponentOfVector = objectVelocity.Length() * (float)Math.Sin(vectorToSurfaceAngle);
if (float.IsNaN(normalComponentOfVector))
{
//if objectVelocity.Length gives an invalid value
normalComponentOfVector = 0.0f;
}
// Above scalar component multiplied by the surface normal gives us the normal component of the vector
normalComponent = normalComponentOfVector * surfaceNormal;
// The vector plus the negative normalComponent will give us the tangential component in the reverse direction
tangentialComponent = -(objectVelocity + (-normalComponent));
}
else
{
// Object velocity is equal to zero so set both components to zero
normalComponent = Vector3.Zero;
tangentialComponent = Vector3.Zero;
}
}
}
}