//--------------------------------------------------------------------------------------------------------------------------------------------------- // // Copyright (C)2007 DarkWynter Studios. All rights reserved. // //--------------------------------------------------------------------------------------------------------------------------------------------------- // {Contact : darkwynter.com for licensing information //--------------------------------------------------------------------------------------------------------------------------------------------------- namespace DarkWynter.Engine.GameObjects { #region Using Statements using System; using System.Collections.Generic; using Microsoft.Xna.Framework; using Microsoft.Xna.Framework.Graphics; using System.Xml; using Xclna.Xna.Animation.Content; using Xclna.Xna.Animation; using Physics; using Globals; using ObjectLib; using DarkWynter.Engine.Init; using DarkWynter.Stream; using DarkWynter.Engine.Audio; #endregion /// /// Methods of GameObject follow the GameFlow pattern to ensure quick and efficient loading and unloading procedures. /// Additional properties are included in GameObject and are used by Renderer and Update to manipulate the GameObject. /// Each object specifies its draw and update procedures internally and is passed to Physics for in game motion and rotation. /// public class GameObject { #region GameObject Interface Properties private Mass _mass; //private XmlNode _xml; private Load _load; private string _objectModelName; private bool _isCollectable; private bool _isKey; private Draw _draw; private Draw _drawBoundingVolume; private Draw _drawAIVision; /// /// Draw object /// public Draw draw { get { return _draw; } set { _draw = value; } }/// /// Draw object's bounding volume /// public Draw drawBoundingVolume { get { return _drawBoundingVolume; } set { _drawBoundingVolume = value; } } /// /// AI Vision Draw object /// public Draw drawAIVision { get { return _drawAIVision; } set { _drawAIVision = value; } } /// /// Mass object, handles physics of GameObject. /// public Mass mass { get { return _mass; } set { _mass = value; } } ///// ///// Xml data associated with this object. ///// //public XmlNode xml { get { return _xml; } set { _xml = value; } } /// /// Draw object /// public Load load { get { return _load; } set { _load = value; } } /// /// Text identifier for model name. /// public string objectModelName { get { return _objectModelName; } set { _objectModelName = value; } } /// /// Defines if this object is collectable by players. /// public bool isCollectable { get { return _isCollectable; } set { _isCollectable = value; } } /// /// Defines if this object is a Key object collected to pass the level. /// public bool isKey { get { return _isKey; } set { _isKey = value; } } /// /// Difference between object height and terrain at it's x/z location. /// HeightDifference is positive when object is below the terrain. /// public float heightDifference; /// /// Terrain height at object's x/z location. /// public float localTerrainHeight; /// /// Defines what kind of collision response this GameObject should have. /// public Enums_Engine.CollisionResponses collisionWithPlayerResponse = Enums_Engine.CollisionResponses.NONE; /// /// Defines the scale of damage or health an object bestows. /// public float collisionMultiplier = 0.001f; /// /// Game Object animated or not /// public bool modelAnimated; // Animation /// /// Model animator /// public ModelAnimator animator; /// /// Idle animation /// public AnimationController idle; /// /// Walk animation /// public AnimationController walk; /// /// Die animation /// public AnimationController die; /// /// Animation currently running /// public AnimationController currentAnimation; /// /// 3D Cue used with jump sound. /// public Cue3D jump; /// /// Index to the game controller associated with this object /// public int controllerIndex; #endregion #region Physics Properties /// /// Used to pass static instance data to shader (for static objects) /// public VertexFogBinormalTangentDepth staticObjectValues; /// /// Tangential component of force calculations. /// public Vector3 tangentialComponent = new Vector3(); /// /// Normal component of force calculations. /// public Vector3 normalComponent = new Vector3(); /// /// Used in collision; surface normal calculation. /// public Vector3 surfaceNormal; /// /// Used in collision; gravity normal calculation. /// public Vector3 gravityNormalComponent; /// /// Used in collision; tangential gravity-force calculation. /// public Vector3 gravityTangentialComponent; /// /// Used in collision; normal velocity-force calculation. /// public Vector3 velocityNormalComponent; /// /// Used in collision; tangential velocity-force calculation. /// public Vector3 velocityTangentialComponent; /// /// Used in collision; normal totalForce calculation. /// public Vector3 totalForceNormalComponent; /// /// Used in collision; tangential totalForce calculation. /// public Vector3 totalForceTangentialComponent; /// /// Used in collision; combined velocity and totalForce normal calculation. /// public Vector3 combinedNormalForce; /// /// Used in collision; combined velocity and totalForce tangential calculation. /// public Vector3 combinedTangentialForce; #endregion #region Xml Interface private string _type; public string type { get { return _type; } set { _type = value; } } private string _id; public string id { get { return _id; } set { _id = value; } } private string _typeID; public string typeID { get { return _typeID; } set { _typeID = value; } } private string _name; public string name { get { return _name; } set { _name = value; } } /// /// Defines the coordinate system the object is working in. /// public enum GridType { unit, coord, tile }; private GridType _grid; public GridType grid { get { return _grid; } set { _grid = value; } } private string _x; private string _z; public string x { get { return mass.currentPosition.X.ToString(); } set { _x = value; float Y_height = DarkWynterEngine.objectLibrary.terrain.GetTerrainHeight( float.Parse(value) / Statics_Engine.TerrainSettings.terrainScaleFactor, mass.currentPosition.Z / Statics_Engine.TerrainSettings.terrainScaleFactor); mass.SetPosition( new Vector3(float.Parse(value), Y_height, mass.currentPosition.Z), new Vector3(float.Parse(value), Y_height, mass.currentPosition.Z - 1.0f)); } } public string z { get { return mass.currentPosition.Z.ToString(); } set { _z = value; float Y_height = DarkWynterEngine.objectLibrary.terrain.GetTerrainHeight( mass.currentPosition.X / Statics_Engine.TerrainSettings.terrainScaleFactor, float.Parse(value) / Statics_Engine.TerrainSettings.terrainScaleFactor); mass.SetPosition( new Vector3(mass.currentPosition.X, Y_height, float.Parse(value)), new Vector3(mass.currentPosition.X, Y_height, float.Parse(value) - 1.0f)); } } private string _maxScale; public string maxScale { get { return mass.scale.X.ToString(); } set { mass.scale = new Vector3(float.Parse(value)); } } private string _animated; public string animated { get { if (_animated == "Yes") {return "Yes";} else {return "No";} } set { if (_animated == "Yes"){modelAnimated = true;} else{modelAnimated = false;} } } private string _model; public string model { get { return _model; } set { _model = value; draw.model = Statics_Engine.SystemSettings.content.Load(this.model); foreach (ModelMesh mesh in draw.model.Meshes) { for (int i = 0; i < mesh.MeshParts.Count; i++) { ModelMeshPart part = mesh.MeshParts[i]; part.Effect = ShaderParameters.DrawFX.effect;//ShaderParameters.DrawFX.effect.Clone(Statics.SystemSettings.graphics.GraphicsDevice); } } } } private string _texture; public string texture { get { if(_texture != null) return _texture; else return "Content\\_textures\\"; } set { _texture = value; if (draw.textureList.Count > 0) draw.textureList[0] = Statics_Engine.SystemSettings.content.Load(value); else draw.textureList.Add(Statics_Engine.SystemSettings.content.Load(value)); } } private string _bumpTexture; public string bumpTexture { get { if (_bumpTexture != null) return _bumpTexture; else return "Content\\_textures\\"; } set { _bumpTexture = value; if (draw.textureList.Count > 1) draw.textureList[1] = Statics_Engine.SystemSettings.content.Load(value); else draw.textureList.Add(Statics_Engine.SystemSettings.content.Load(value)); } } #endregion #region Methods public GameObject() { mass = new Mass(); mass.gameObjectPointer = this; } /// /// Game object constructor. /// Gets a pre-initialized mass object from the Statics.LevelSettings.darkMatter list. /// public GameObject(Load gameObjectLoader) { load = gameObjectLoader; //mass = Statics_Engine.LevelSettings.darkMatter[Statics_Engine.LevelSettings.darkMatterIterator++]; mass = new Mass(); mass.gameObjectPointer = this; //drawAIVision = new Draw(); } /// /// Load object data from Xml. /// /// ObjectLibrary that this object belongs to. public virtual bool Load(ObjectLibrary objectLibrary) { return load.Load_Default(objectLibrary, this, false); #region Old Code //// Set up Model Manager //if (!LoadModel(load.node)) //{ // return false; //} //// Get the Location Map //Texture2D locationMap = Statics_Engine.SystemSettings.content.Load(node.Attributes["locationMap"].Value); //int length = locationMap.Width * locationMap.Width; //Color[] locationColorValues = new Color[length]; //locationMap.GetData(locationColorValues); //Random rand = new Random(); //// Precalculate the vertexmap/locationmap ratio for use in the for loop below //float vertexmapToObjectmapRelationship = Statics_Engine.TerrainSettings.vertexMapSize / locationMap.Width; //// Create a ruler 1 terrain mod long (distance between verticies //float terrainModWidth = Statics_Engine.TerrainSettings.terrainScaleFactor * vertexmapToObjectmapRelationship; //// Get the color value of the node //int mapColorR = int.Parse(node.Attributes["red"].Value); //int mapColorG = int.Parse(node.Attributes["green"].Value); //int mapColorB = int.Parse(node.Attributes["blue"].Value); //Vector3 scale = new Vector3((int)(float.Parse(node.Attributes["maxScale"].Value))); //float massValue = float.Parse(node.Attributes["mass"].Value); //isCollectable = bool.Parse(node.Attributes["isCollectable"].Value); //isKey = bool.Parse(node.Attributes["isKey"].Value); //// For each pixel of the treeMap //for (int l = 0; l < length; l++) //{ // // If the color matches create a prop // if (locationColorValues[l].R == mapColorR && locationColorValues[l].G == mapColorG && locationColorValues[l].B == mapColorB) // { // // Get the 2D location from 1D array // int h = l % locationMap.Width; // int w = (int)l / locationMap.Width; // // Start Type properties // //gameObject.mass.objectType = objectType; // mass.gameObjectPointer = this; // objectModelName = node.Attributes["name"].Value; // // Get terrain height at position // float yPosition = objectLibrary.terrain.GetTerrainHeight(w * vertexmapToObjectmapRelationship, // h * vertexmapToObjectmapRelationship); // // Set the object world properties // Vector3 locpos = new Vector3(w * terrainModWidth, yPosition, h * terrainModWidth); // Vector3 refpos = new Vector3(w * terrainModWidth, yPosition, h * terrainModWidth - 1); // mass.SetPosition(locpos, refpos); // mass.Rotate(0.0f, 0.0f); // mass.scale = scale; // mass.mass = massValue; // mass.isMoving = false; // if (isKey) // { // collisionWithPlayerResponse = Enums_Engine.CollisionResponses.HEALTHBONUS; // } // } //} //return true; #endregion } /// /// Load object data from Xml. /// /// ObjectLibrary that this object belongs to. public bool Load_Instanced(ObjectLibrary objectLibrary) { return load.Load_Default(objectLibrary, this, false); } /// /// Load model data from XML /// /// The model xml node corresponding to this object /// public bool LoadModel(XmlNode node) { try { this.name = node.Attributes["name"].Value; this.maxScale = node.Attributes["maxScale"].Value; this.animated = node.Attributes["animated"].Value; if (node.Attributes["grid"] != null) { switch (node.Attributes["grid"].Value) { case "unit": grid = GridType.unit; break; case "tile": grid = GridType.tile; break; case "coord": grid = GridType.coord; break; default: grid = GridType.unit; break; } } // Load Textures and Model draw = new Draw(); draw.textureList.Clear(); this.texture = node.Attributes["texture"].Value; this.bumpTexture = node.Attributes["bumpTexture"].Value; this.model = node.Attributes["model"].Value; // Create Animation Transforms if (this.name == "ss3" || this.name == "dojogirl") { draw.initialTransform = Matrix.CreateRotationY((float)(-Math.PI / 2.0f)) * Matrix.CreateTranslation(new Vector3(0, -Statics_Engine.PlayerSettings.DEFAULT_PLAYER_HEIGHT / 2.0f, 0)); } else if (this.name == "bunny") { draw.initialTransform = Matrix.CreateTranslation(new Vector3(0, Statics_Engine.PlayerSettings.DEFAULT_PLAYER_HEIGHT, 0)); } else if (this.name == "fish") { draw.initialTransform = Matrix.CreateRotationY((float)(-Math.PI)) * Matrix.CreateTranslation(new Vector3(0, -Statics_Engine.PlayerSettings.DEFAULT_PLAYER_HEIGHT / 2.0f + 5, 0)); } else { draw.initialTransform = Matrix.Identity; } return true; } catch { return false; } } public virtual string ToXml() { string xml = "\n" + "<" + this.type+ " " + " typeID=\"" + this.typeID + "\"" + " id=\"" + this.id + "\"" + " name=\"" + this.name + "\"" + " x=\"" + this.x + "\"" + " z=\"" + this.z + "\"" + " grid=\"" + this.grid.ToString() + "\"" + " model=\"" + this.model + "\"" + " texture=\"" + this.texture + "\"" + " bumpTexture=\"" + this.bumpTexture + "\"" + " maxScale=\"" + this.maxScale + "\"" + " animated=\"" + this.animated + "\"" + "/>" + "\n"; return xml; //switch (_grid) //{ // case GridType.unit: // return "unit"; // break; // case GridType.tile: // return "tile"; // break; // case GridType.coord: // return "coord"; // break; // default: // return "unit"; // break; //} } /// /// Runs the appropriate animation on the model /// /// The animation to run public void RunController(AnimationController controller) { foreach (BonePose p in animator.BonePoses) { p.CurrentController = (IAnimationController)controller; p.CurrentBlendController = null; } currentAnimation = controller; } /// /// Update this object /// /// ObjectLibrary public virtual void Update(ref ObjectLibrary objectLibrary) { mass.Update(); mass.boundingVolume.UpdateSphere(mass.currentPosition); } /// /// Method called when the motion buttons have been pressed /// /// Vector 2 amount of translation to induce public virtual void Translate(Vector2 value) { mass.MoveObject(value.Y, value.X); } /// /// Method called when the rotation buttons have been pressed /// /// Vector 2 amount of rotation public virtual void Rotate(Vector2 value) { mass.Rotate(value.Y, value.X); } /// /// Default behavior applies force based physics for rebound. /// Override to supplement behavior. /// Return true if collidedObject is absorbed or destroyed, else false. /// /// GameObject that collided with this GameObject. /// The force applied by the colliding GameObject to this GameObject /// ObjectLibrary that this object belongs to. /// True = this object has to be recycled public virtual bool ObjectCollisionResponse(GameObject collidedObject, Vector3 resultantForce, ObjectLibrary objectLibrary) { mass.numberOfCollision++; mass.isMoving = true; return false; } /// /// Default behavior applies force based physics for rebound. /// Override to supplement or replace behavior. /// /// A copy of the terrain, provided for overrides public virtual void TerrainCollisionResponse(Terrain terrain) { CalculateVectorComponents(surfaceNormal, Statics_Engine.GameSettings.accelDueToGravity); gravityNormalComponent = normalComponent + Vector3.Zero; gravityTangentialComponent = tangentialComponent + Vector3.Zero; // Calculate the components of velocity with reference to the surface normal CalculateVectorComponents(surfaceNormal, mass.velocity); velocityNormalComponent = normalComponent + Vector3.Zero; velocityTangentialComponent = tangentialComponent + Vector3.Zero; // Calculate the components of totalForce with reference to the surface normal CalculateVectorComponents(surfaceNormal, mass.totalForce); totalForceNormalComponent = normalComponent + Vector3.Zero; totalForceTangentialComponent = tangentialComponent + Vector3.Zero; // Calculate normal and tangential forces with reference to the their components combinedNormalForce = (mass.mass * velocityNormalComponent / Statics_Engine.SystemSettings.dt) + totalForceNormalComponent; combinedTangentialForce = (mass.mass * velocityTangentialComponent / Statics_Engine.SystemSettings.dt) + totalForceTangentialComponent + (mass.mass * gravityTangentialComponent); mass.numberOfCollision++; } /// /// Calculate components of collision. /// /// Normal of surface that is being collided against. /// Velocity of this GameObject. private void CalculateVectorComponents(Vector3 surfaceNormal, Vector3 objectVelocity) { if (objectVelocity.Length() >= 0.01f || !float.IsInfinity(objectVelocity.Length())) { normalComponent = Vector3.Dot(objectVelocity, surfaceNormal) * surfaceNormal; tangentialComponent = objectVelocity - normalComponent; } else { // Object velocity is equal to zero so set both components to zero normalComponent = Vector3.Zero; tangentialComponent = Vector3.Zero; } } /// /// Sets the heightDifference parameter for this GameObject. /// HeightDifference is positive when object is below the terrain. /// /// Terrain object to determine height difference with. public void GetObjectHeight(Terrain terrain) { // Used to calculate the average terrain height around the player localTerrainHeight = 0; // Error check mass.EnforceLevelBoundary(); // Get X and Z positions int posX = (int)mass.currentPosition.X; int posZ = (int)mass.currentPosition.Z; // Add up all neighboring terrain points localTerrainHeight = terrain.GetTerrainHeight(posX / Statics_Engine.TerrainSettings.terrainScaleFactor, posZ / Statics_Engine.TerrainSettings.terrainScaleFactor); surfaceNormal = terrain.GetTerrainNormal(posX / Statics_Engine.TerrainSettings.terrainScaleFactor, posZ / Statics_Engine.TerrainSettings.terrainScaleFactor, mass.velocity); // Average all the neighboring terrain points float newHeight = localTerrainHeight + (mass.objectHeight / 2); // Get difference between average and player heightDifference = newHeight - mass.currentPosition.Y; } /// /// Draws a stand alone model with no instancing or triangle stripping. /// public virtual Draw Draw() { return null; } /// /// Draws the model for ai vision /// public virtual void DrawAIVision() { } /// /// Called after Draw method. /// Allows GameObjects to add Billboards to be drawn. /// List is cleared each pass, or frame. /// /// ObjectLibrary that this object belongs to. /// Dynamic Billboard list. public virtual void Draw_Billboards(ObjectLibrary objectLibrary, BillboardList gameObjectBillboards) { } #endregion } } #region Legacy Code - Heat dissipation and State Change /* Legacy Code // Test Function to update energy distribution public void UpdateHeatDissipation(ObjectLibrary objectLibrary, float dt) { List newParticlesToAddToList = new List(); foreach (Particle particle in particles) { particle.mass.UpdatePosition(dt); } // Iterate through dynamic particles for (int i = 0; i < particles.Count; i++) { Particle particle = particles[i]; // List to store the pointers to neighboring particles List neighboringParticleList = new List(); // Used to calculate the average value of all neighboring particles float averageEnergy = particle.mass.energy; // Get the list of neighboring particle pointers neighboringParticleList = GetSurroundingParticles(particle.mass.currentPosition, objectLibrary); // Calculate their total energy foreach (Particle neigborParticle in neighboringParticleList) { averageEnergy += neigborParticle.mass.energy; } // Find the average averageEnergy /= neighboringParticleList.Count + 1; // If the average value is not equal to zero then we have some heat dissipation to do if (averageEnergy != particle.mass.energy && neighboringParticleList.Count != 0) { // Iterate through neighboring particles foreach (Particle neigborParticle in neighboringParticleList) { // If their value is not equal to the average set it and set the flag if (neigborParticle.mass.energy != averageEnergy) { neigborParticle.mass.SetEnergy(averageEnergy); if (!neigborParticle.mass.isChanging) { neigborParticle.mass.isChanging = true; newParticlesToAddToList.Add(neigborParticle); } } } // Set the starting particle's energy to average value as well particle.mass.SetEnergy(averageEnergy); } else if (particle.mass.energy != 0.0f) { DissipateHeat(particle); } else { // The particle and it's neighbors are in equilibrium so remove it from the list particle.mass.isChanging = false; if (!particle.mass.isMoving) { // Particle needs to be put back into the static list MoveParticle(particles, particles, particle); } else { particles.Remove(particle); } i--; } } foreach (Particle newParticle in newParticlesToAddToList) { if (particles.Contains(newParticle)) { continue; } if (newParticle.mass.isMoving) { CopyParticle(particles, particles, newParticle); } else { MoveParticle(particles, particles, newParticle); } } } public void StateChange(ObjectLibrary objectLibrary, float elementUse, GameObject gameObject) { Particle particle = (Particle)gameObject; gameObject.mass.ChangeEnergy(elementUse / 2.0f); if (gameObject.mass.isChanging) { return; } else if (gameObject.mass.isMoving) { // Particle is in the dynamic list so we need to copy it to the Thermal list switch (particle.particleType) { case Particle.ParticleType.Air: { objectLibrary.particleManager.air.CopyParticle(objectLibrary.particleManager.air.dynamicParticles, objectLibrary.particleManager.air.thermalParticles, particle); break; } case Particle.ParticleType.Water: { objectLibrary.particleManager.water.CopyParticle(objectLibrary.particleManager.water.dynamicParticles, objectLibrary.particleManager.water.thermalParticles, particle); break; } case Particle.ParticleType.Earth: { objectLibrary.particleManager.earth.CopyParticle(objectLibrary.particleManager.earth.dynamicParticles, objectLibrary.particleManager.earth.thermalParticles, particle); break; } } gameObject.mass.isChanging = true; } else { // It's in the static list, so move the particle to the dynamic list switch (particle.particleType) { case Particle.ParticleType.Air: { objectLibrary.particleManager.air.MoveParticle(objectLibrary.particleManager.air.staticParticles, objectLibrary.particleManager.air.thermalParticles, particle); break; } case Particle.ParticleType.Water: { objectLibrary.particleManager.water.MoveParticle(objectLibrary.particleManager.water.staticParticles, objectLibrary.particleManager.water.thermalParticles, particle); break; } case Particle.ParticleType.Earth: { objectLibrary.particleManager.earth.MoveParticle(objectLibrary.particleManager.earth.staticParticles, objectLibrary.particleManager.earth.thermalParticles, particle); break; } } gameObject.mass.isChanging = true; } } */ #endregion