//---------------------------------------------------------------------------------------------------------------------------------------------------
//
// Copyright (C)2007 DarkWynter Studios. All rights reserved.
//
//---------------------------------------------------------------------------------------------------------------------------------------------------
// {License Information: Creative Commons}
//---------------------------------------------------------------------------------------------------------------------------------------------------
#define HACK_MOVEMENT
namespace ElementalGame
{
#region Using Statements
using System;
using System.Collections.Generic;
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
public class Mass
{
public GameObject gameObjectPointer;
public static int MASS_LENGTH = 8;
public static float MASS_NORMALIZER = 1 / 1000;
public enum ObjectType
{
PARTICLE,
PROP,
PLAYER,
HUMAN,
AI,
TERRAIN,
SKYSPHERE,
SENSOR,
QUAD,
TRIANGLE,
SPHERE,
CUBE,
CYLINDER,
CIRCLE,
PYRAMID,
KEY,
NONE
};
public enum TerrainType
{
TERRAIN_GROUND,
TERRAIN_HIGH,
TERRAIN_MEDIUM,
TERRAIN_LOW,
NONE
};
public ObjectType objectType = new ObjectType();
// Orientation
public Quaternion currentRotation = new Quaternion(); // Stores current rotation information
public Vector3 normalVector = new Vector3(); // Straight ahead - normalized
public Vector3 upVector = new Vector3(); // Straight up - normalized
public Vector3 perpVector = new Vector3(); // Straight to the right?(left?)
// Position
public Vector3 currentPosition = new Vector3(); // The objects center location
public Vector3 lastPosition = new Vector3(); // Needed for updating the lookup map
// Motion Variables
public Vector3 acceleration = new Vector3();
public Vector3 velocity = new Vector3(); // Un-Normalized Vector in the direction of object movement
public Vector3 totalForce = new Vector3(); // Used to sum the forces on a GameObject during Update
// Gravity Variables
public static Vector3 accelDueToGravity = Vector3.Zero;
public static float gravityForce = -400f;
// Reaction Properties
public float mass = 0; // Kilos
public float scale = 1; // Model Scale Factor
public float adhesion = 0.1f; // Used to calculate bonding factor to other game objects
public float cohesion = 0.5f; // Used to calculate bonding factor to like objects
// Energy State
public float energy = 0.0f; // Initial Fire energy given to player
public float newEnergy = 0.0f;
public float stateChangeThreshold = 0; // Temperature (Celcius) at which a particle changes state
public float COR; // Coefficient Of Restitution for this object
public float staticFrictionCoefficient; // Static Friction Coefficient of this object
public float dynamicFrictionCoefficient; // Dynamic Friction Coefficient of this object
public bool isChanging = false;
public bool isMoving = false;
public bool collisionChecked = false;
public List objectHitBy = new List();
public float fallingDamageMultiplier = 0.0f;
public enum MovementType { WALK, HOVER };
public MovementType movementType = MovementType.WALK;
//only used in walk mode since velocity is useless
public Vector3 sensorDirection = new Vector3();
public float lastMaxHeight = 0;
public int numberOfCollision = 0;
public int numberOfTimesUnderTerrain = 0;
public float objectHeight;
public bool deadObject = false;
public Mass()
{
totalForce = Vector3.Zero;
velocity = Vector3.Zero;
}
// Reset mass to initial state
public void ClearValues()
{
// Reset everything here
// Called by constructor...
objectType = ObjectType.NONE;
objectHeight = 0;
gameObjectPointer = null;
// Orientation
currentRotation = Quaternion.Identity; // Stores current rotation information
normalVector = Vector3.Zero; // Straight ahead - normalized
upVector = Vector3.Zero; // Straight up - normalized
perpVector = Vector3.Zero; // Straight to the right?(left?)
numberOfCollision = 0;
// Position
currentPosition = Vector3.Zero; // The objects center location
lastPosition = Vector3.Zero; // Needed for updating the lookup map
// Motion Variables
acceleration = Vector3.Zero;
velocity = Vector3.Zero; // Un-Normalized Vector in the direction of object movement
totalForce = Vector3.Zero; // Used to sum the forces on a GameObject during Update
// Reaction Properties
mass = 1; // Kilos
scale = 1; // Model Scale Factor
adhesion = 0; // Used to calculate bonding factor to other game objects
cohesion = 0; // Used to calculate bonding factor to like objects
// Energy State
energy = 0.0f; // Initial Fire energy given to player
newEnergy = 0.0f;
stateChangeThreshold = 0; // Temperature (Celcius) at which a particle changes state
COR = 0.0f; // Coefficient Of Restitution for this object
staticFrictionCoefficient = 0.0f; // Static Friction Coefficient of this object
dynamicFrictionCoefficient = 0.0f; // Dynamic Friction Coefficient of this object
isChanging = false;
isMoving = false;
collisionChecked = false;
//hitWhere
objectHitBy.Clear();
fallingDamageMultiplier = 0.0f;
deadObject = false;
}
public void SetPosition(Vector3 position, Vector3 reference)
{
normalVector = new Vector3(0, 0, 1); //reference - position;
normalVector.Normalize();
upVector = new Vector3(0.0f, 1.0f, 0.0f);
perpVector = new Vector3(-1.0f, 0.0f, 0.0f);//Vector3.Cross(normalVector, upVector); //new Vector3(-1.0f, 0.0f, 0.0f);
//upVector = Vector3.Cross(perpVector, normalVector);
// Look in Z+ direction
currentRotation = -Quaternion.Identity;
Vector2 unitHoriz = new Vector2(0, 1);
Vector2 unitVert = new Vector2(1, 0);
Vector3 N = reference - position;
N.Normalize();
Vector2 Nhoriz = new Vector2(N.X, N.Z);
Vector2 Nvert = new Vector2(N.Z, N.Y);
float angleHoriz = (float)Math.Acos((double)(Vector2.Dot(unitHoriz, Nhoriz)));
float anglVert = 0.0f;// (float)Math.Acos((double)(Vector2.Dot(unitVert, Nvert)));
//Determine the sign of the angle needed
Vector3 cross = Vector3.Cross(Vector3.UnitZ, N);
if (cross.Y <= 0)
{
angleHoriz = -Math.Abs(angleHoriz);
}
else
{
angleHoriz = Math.Abs(angleHoriz);
}
//passing negative because I am negating it again inside the function
Rotate(anglVert, -angleHoriz);
// perpVector = Vector3.Cross(normalVector, upVector);
//upVector = Vector3.Cross(perpVector, normalVector);
currentPosition = new Vector3() + position;
lastPosition = position - normalVector;
}
public void UpdatePosition()
{
collisionChecked = false;
if (totalForce != Vector3.Zero)
{
isMoving = true;
// Particle should update its physics here
lastPosition = currentPosition;
acceleration = totalForce / mass;
totalForce = Vector3.Zero;
// Movement according to time S = u*t + 1/2*g*t^2
currentPosition = lastPosition + velocity * ElementalGame.dt + (0.5f * acceleration * ElementalGame.dt * ElementalGame.dt);
if (float.IsNaN(currentPosition.X))
{
System.Diagnostics.Debug.WriteLine("CurrentPosition = NaN");
Logging.addNan();
//throw new Exception("NaN Error.");
currentPosition = lastPosition;
}
velocity += acceleration * ElementalGame.dt;
EnforceBoundary();
//set the height
if (currentPosition.Y > lastMaxHeight)
{
lastMaxHeight = currentPosition.Y;
}
}
else
{
isMoving = false;
}
// Check if new energy has been added to the object
if (newEnergy != 0.0f)
{
// Call the energy update function
UpdateEnergy();
}
}
public void EnforceBoundary()
{
// X = 0 plane
if (currentPosition.X < 0.0f)
{
ObjectLibrary.lookupMap.RemoveLast(this);
// Clamp the position
currentPosition.X = 1.0f;
lastPosition.X = 0.0f;
AddForce(new Vector3(-2.0f * mass * velocity.X * COR / ElementalGame.dt, 0.0f, 0.0f));
ObjectLibrary.lookupMap.SetPosition(this);
}
// X = mapSize plane
if (currentPosition.X > (Terrain.vertexMapSize - 1) * Terrain.terrainScaleFactor)
{
ObjectLibrary.lookupMap.RemoveLast(this);
// Clamp the position
currentPosition.X = (Terrain.vertexMapSize - 1) * Terrain.terrainScaleFactor - 1;
lastPosition.X = (Terrain.vertexMapSize - 1) * Terrain.terrainScaleFactor - 2;
AddForce(new Vector3(-2.0f * mass * velocity.X * COR / ElementalGame.dt, 0.0f, 0.0f));
ObjectLibrary.lookupMap.SetPosition(this);
}
//// Y = 0 plane
//if (currentPosition.Y < 0.0f)
//{
// ObjectLibrary.lookupMap.RemoveLast(this);
// // Clamp the position
// currentPosition.Y = 1.0f;
// lastPosition.Y = 0.0f;
// AddForce(new Vector3(0.0f, -2.0f * mass * velocity.Y * COR / ElementalGame.dt, 0.0f));
// ObjectLibrary.lookupMap.SetPosition(this);
//}
// Y = mapSize plane
if (currentPosition.Y > (Terrain.vertexMapSize - 1) * Terrain.terrainScaleFactor)
{
ObjectLibrary.lookupMap.RemoveLast(this);
// Clamp the position
currentPosition.Y = (Terrain.vertexMapSize - 1) * Terrain.terrainScaleFactor - 1;
lastPosition.Y = (Terrain.vertexMapSize - 1) * Terrain.terrainScaleFactor - 2;
AddForce(new Vector3(0.0f, -2.0f * mass * velocity.Y * COR / ElementalGame.dt, 0.0f));
ObjectLibrary.lookupMap.SetPosition(this);
}
// Z = 0 plane
if (currentPosition.Z < 0.0f)
{
ObjectLibrary.lookupMap.RemoveLast(this);
// Clamp the position
currentPosition.Z = 1.0f;
lastPosition.Z = 0.0f;
AddForce(new Vector3(0.0f, 0.0f, -2.0f * mass * velocity.Z * COR / ElementalGame.dt));
ObjectLibrary.lookupMap.SetPosition(this);
}
// Z = mapSize plane
if (currentPosition.Z > (Terrain.vertexMapSize - 1) * Terrain.terrainScaleFactor)
{
ObjectLibrary.lookupMap.RemoveLast(this);
// Clamp the position
currentPosition.Z = (Terrain.vertexMapSize - 1) * Terrain.terrainScaleFactor - 1;
lastPosition.Z = (Terrain.vertexMapSize - 1) * Terrain.terrainScaleFactor - 2;
AddForce(new Vector3(0.0f, 0.0f, -2.0f * mass * velocity.Z * COR / ElementalGame.dt));
ObjectLibrary.lookupMap.SetPosition(this);
}
if (float.IsNaN(currentPosition.X))
{
ObjectLibrary.lookupMap.RemoveLast(this);
Logging.addNan();
currentPosition.X = 1.0f;
lastPosition.X = 1.0f;
ObjectLibrary.lookupMap.SetPosition(this);
}
if (float.IsNaN(currentPosition.Y))
{
ObjectLibrary.lookupMap.RemoveLast(this);
Logging.addNan();
currentPosition.Y = 1.0f;
lastPosition.Y = 0.0f;
ObjectLibrary.lookupMap.SetPosition(this);
}
if (float.IsNaN(currentPosition.Z))
{
ObjectLibrary.lookupMap.RemoveLast(this);
Logging.addNan();
currentPosition.Z = 1.0f;
lastPosition.Z = 0.0f;
ObjectLibrary.lookupMap.SetPosition(this);
}
/////////////////////////////////////////
// this is a hack to fix the crashes due to lastPosition being invalid.. we really shouldn't have to do this
//
// X = 0 plane
if (lastPosition.X < 0.0f)
{
ObjectLibrary.lookupMap.RemoveLast(this);
// Clamp the position
lastPosition.X = 0.0f;
ObjectLibrary.lookupMap.SetPosition(this);
}
// X = mapSize plane
if (lastPosition.X > (Terrain.vertexMapSize - 1) * Terrain.terrainScaleFactor)
{
ObjectLibrary.lookupMap.RemoveLast(this);
// Clamp the position
lastPosition.X = (Terrain.vertexMapSize - 1) * Terrain.terrainScaleFactor - 2;
ObjectLibrary.lookupMap.SetPosition(this);
}
// Y = 0 plane
if (lastPosition.Y < 0.0f)
{
ObjectLibrary.lookupMap.RemoveLast(this);
// Clamp the position
lastPosition.Y = 0.0f;
ObjectLibrary.lookupMap.SetPosition(this);
}
// Y = mapSize plane
if (lastPosition.Y > (Terrain.vertexMapSize - 1) * Terrain.terrainScaleFactor)
{
ObjectLibrary.lookupMap.RemoveLast(this);
// Clamp the position
lastPosition.Y = (Terrain.vertexMapSize - 1) * Terrain.terrainScaleFactor - 2;
ObjectLibrary.lookupMap.SetPosition(this);
}
// Z = 0 plane
if (lastPosition.Z < 0.0f)
{
ObjectLibrary.lookupMap.RemoveLast(this);
// Clamp the position
lastPosition.Z = 0.0f;
ObjectLibrary.lookupMap.SetPosition(this);
}
// Z = mapSize plane
if (lastPosition.Z > (Terrain.vertexMapSize - 1) * Terrain.terrainScaleFactor)
{
ObjectLibrary.lookupMap.RemoveLast(this);
// Clamp the position
lastPosition.Z = (Terrain.vertexMapSize - 1) * Terrain.terrainScaleFactor - 2;
ObjectLibrary.lookupMap.SetPosition(this);
}
}
// Add an external force to be applied to the mass
public void AddForce(Vector3 force)
{
totalForce += force;
if (float.IsNaN(totalForce.X))
{
Logging.addNan();
System.Diagnostics.Debug.WriteLine("AddForce.. totalForce = NaN");
totalForce = Vector3.Zero;
}
}
public void AddFriction(float totalFriction, Vector3 perpendicularForce, Vector3 tangentialDirection)
{
if (tangentialDirection.Length() != 0.0f)
{
tangentialDirection.Normalize();
}
totalForce += (totalFriction * Math.Abs(perpendicularForce.Length())) * tangentialDirection;
if (float.IsNaN(totalForce.X))
{
Logging.addNan();
//throw new Exception("NaN Error.");
System.Diagnostics.Debug.WriteLine("AddFriction.. totalForce = NaN");
totalForce = Vector3.Zero;
}
}
// Function to add/remove energy from the object
public void UpdateEnergy()
{
// Add newEnergy to energy and reset newEnergy
energy += newEnergy;
newEnergy = 0.0f;
// Bounds checking
if (energy > 1.0f)
{
energy = 1.0f;
}
else if (energy < -1.0f)
{
energy = -1.0f;
}
// Check if the energy is below a certain threshold (<0.0x) and then set it to 0.0f
//Why was this here? -Priyesh
//else if ((int)(energy * 10) == 0)
//{
// energy = 0.0f;
//}
}
// Function to set the energy to an exact value
public void SetEnergy(float newEnergyValue)
{
energy = newEnergyValue;
// Check if the energy is below a certain threshold (<0.0x) and then set it to 0.0f
if ((int)(energy * 10) == 0)
{
energy = 0.0f;
}
}
// Add/Remove energy to the mass
public void ChangeEnergy(float amount)
{
newEnergy += amount;
}
public void MovePlayer(float dn, float dx)
{
if (movementType == MovementType.WALK)
{
ObjectLibrary.lookupMap.RemoveCurrent(this);
float moveRatioN = dn / XML.PlayerSettings.PLAYER_SPEED_ON_THE_GROUND;
float moveRatioX = dx / XML.PlayerSettings.PLAYER_SPEED_ON_THE_GROUND;
sensorDirection = Vector3.Zero;
if (dn != 0)
{
sensorDirection += new Vector3(normalVector.X, 0, normalVector.Z) * moveRatioN * XML.PlayerSettings.PLAYER_WALK_SPEED;
}
if (dx != 0)
{
sensorDirection += new Vector3(perpVector.X, 0, perpVector.Z) * moveRatioX * XML.PlayerSettings.PLAYER_WALK_SPEED;
}
currentPosition += sensorDirection;
EnforceBoundary();
}
else
{
AddForce(normalVector * dn);
AddForce(perpVector * dx);
}
}
//Strafe object on the V (sideways) plane
public void Strafe(float dx)
{
if (movementType == MovementType.WALK && dx != 0.0f)
{
ObjectLibrary.lookupMap.RemoveCurrent(this);
float moveRatio = dx / XML.PlayerSettings.PLAYER_SPEED_ON_THE_GROUND;
lastPosition = currentPosition;
sensorDirection = new Vector3(perpVector.X, 0, perpVector.Z) * moveRatio * XML.PlayerSettings.PLAYER_WALK_SPEED;
currentPosition += sensorDirection;
EnforceBoundary();
}
else
{
AddForce(perpVector * dx);
}
}
//Move forwards or backwards along normal (does not restrict Y value)
public void Move(float dn)
{
if (movementType == MovementType.WALK && dn != 0.0f)
{
ObjectLibrary.lookupMap.RemoveCurrent(this);
float moveRatio = dn / XML.PlayerSettings.PLAYER_SPEED_ON_THE_GROUND;
lastPosition = currentPosition;
sensorDirection = new Vector3(normalVector.X, 0, normalVector.Z) * moveRatio * XML.PlayerSettings.PLAYER_WALK_SPEED;
currentPosition += sensorDirection;
EnforceBoundary();
}
else
{
AddForce(normalVector * dn);
}
}
public void KickBack(float amount)
{
if (movementType == MovementType.WALK && amount != 0.0f)
{
ObjectLibrary.lookupMap.RemoveCurrent(this);
lastPosition = currentPosition;
currentPosition += new Vector3(-sensorDirection.X, 0, -sensorDirection.Z) * amount * XML.PlayerSettings.PLAYER_WALK_SPEED;
EnforceBoundary();
}
else
{
velocity += -velocity * amount;
}
}
//Rotate the coordinate system
public void Rotate(float dVert, float dHoriz)
{
Quaternion vertRotation = Quaternion.CreateFromAxisAngle(perpVector, dVert);
Quaternion horizRotation = Quaternion.CreateFromAxisAngle(upVector, -dHoriz);
Quaternion totalRotation;
Vector3 newNormal = Vector3.Transform(normalVector, Matrix.CreateFromQuaternion(horizRotation * vertRotation));
// Limit the rotation
double angle1 = Math.Acos((double)(Vector3.Dot(normalVector, new Vector3(0, 1, 0))));
double angle2 = Math.Acos((double)(Vector3.Dot(normalVector, newNormal)));
double angle;
if (dVert > 0)
{
angle = angle1 - angle2;
}
else
{
angle = angle1 + angle2;
}
if (angle < 0.5f || angle > 3.0f)
{
//ignore the vertical rotation
totalRotation = horizRotation;// *(-vertRotation);
}
else
{
//do both rotations
totalRotation = horizRotation * vertRotation;
}
currentRotation = Quaternion.Normalize(totalRotation * currentRotation);
normalVector = Vector3.Transform(normalVector, Matrix.CreateFromQuaternion(totalRotation));
perpVector = Vector3.Transform(perpVector, Matrix.CreateFromQuaternion(totalRotation));
}
}
}