//---------------------------------------------------------------------------------------------------------------------------------------------------
//
// 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