//--------------------------------------------------------------------------------------------------------------------------------------------------- // // 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)); } } }