//---------------------------------------------------------------------------------------------------------------------------------------------------
//
// Copyright (C)2007 DarkWynter Studios. All rights reserved.
//
//---------------------------------------------------------------------------------------------------------------------------------------------------
// {Contact : darkwynter.com for licensing information
//---------------------------------------------------------------------------------------------------------------------------------------------------
namespace DarkWynterEngine.GameObjects
{
#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;
using System.Diagnostics;
using System.Xml;
#endregion
using Globals;
using Physics;
using ObjectLib;
using GameObjects;
///
/// Particle game object
///
public class Particle : GameObject
{
double angle = 0.0;
///
/// Particle-type Enumeration
///
public Enums.ParticleType particleType;
///
/// Number of collisions
///
public int hasCollided = -1;
///
/// Time after last collision occured
///
public Stopwatch hasCollidedTimer = new Stopwatch();
///
/// ID of player who fired this particle
///
public int ownerID = -1;
///
/// Is the particle allowed to hurt the player who fired it
///
public bool hurtOwner = false;
private Stopwatch hurtOwnerTimer = new Stopwatch();
private float waveRadius = 0.0f;
List billboards = new List();
int smokeCounter = 0;
///
/// Constructor
///
public Particle()
{
mass.objectType = Enums.ObjectType.PARTICLE;
mass.gameObjectPointer = this;
mass.boundingVolume = new BoundingVolume(new BoundingSphere(mass.currentPosition, 2.0f));
mass.objectHeight = mass.boundingVolume.radius;
mass.mass = Statics.ParticleSettings.Water.WATER_MASS;
mass.COR = Statics.ParticleSettings.Water.WATER_COLD_COR;
mass.dynamicFrictionCoefficient = Statics.ParticleSettings.Water.WATER_COLD_FRICTION;
mass.energy = 1.0f;
mass.isMoving = true;
waveRadius = 1.0f;
hasCollided = -1;
hurtOwner = false;
hurtOwnerTimer = Stopwatch.StartNew();
collisionWithPlayerResponse = Enums.CollisionResponses.HEALTHLOSS;
}
///
/// Load particle info from XML
///
/// Particle node
/// ObjectLibrary
/// True if load was successful
public override bool Load(XmlNode node, ObjectLibrary objectLibrary)
{
//base.Load(node, objectLibrary);
// Set up Model Manager
bool success = objectLibrary.modelManager.AddNewModel(node.Attributes["type"].Value,
node.Attributes["model"].Value,
node.Attributes["texture"].Value,
node.Attributes["bumpTexture"].Value);
// Return unsuccessfully if can't load user supplied XML data
if (!success)
{ return true; }
// Start Type properties
objectModelName = node.Attributes["type"].Value;
// Add Sprite Textures
textureList.Add(Statics.SystemSettings.content.Load("Content/_textures/Fire"));
textureList.Add(Statics.SystemSettings.content.Load("Content/_textures/Wind"));
mass.scale = new Vector3(float.Parse(node.Attributes["maxScale"].Value));
mass.mass = float.Parse(node.Attributes["mass"].Value);
mass.isMoving = false;
isCollectable = bool.Parse(node.Attributes["isCollectable"].Value);
isKey = bool.Parse(node.Attributes["isKey"].Value);
if (isKey)
{
collisionWithPlayerResponse = Enums.CollisionResponses.HEALTHBONUS;
}
//objectValues = new VertexFogBinormalTangent();
//objectValues.Fog = new Vector4(mass.scale, 0.0f); // Store the scale
//objectValues.Binormal = new Vector4(mass.currentRotation.X,
// mass.currentRotation.Y,
// mass.currentRotation.Z,
// mass.currentRotation.W); // Store the quaternion rotation
//objectValues.Tangent = new Vector4(mass.currentPosition, 0.0f);
return true;
}
///
/// Prepare to fire the particle
///
/// ID of player who fired this particle
/// Type of particle
/// Starting position
/// Normal direction of particle
/// Mass of particle
/// Themal value of particle
public void PrepAttack(int playerID, Enums.ParticleType type, Vector3 initialPosition, Vector3 direction, int particleMassValue, int particleThermalValue)
{
particleType = type;
mass.AddForce(direction * Statics.PlayerSettings.DIRECT_ATTACK_FORCE);
mass.SetPosition(initialPosition + (Statics.PlayerSettings.PARTICLE_START_DISTANCE * direction),
initialPosition + ((Statics.PlayerSettings.PARTICLE_START_DISTANCE + 10) * direction));
mass.lastPosition = Vector3.Zero + initialPosition;
ownerID = playerID;
direction.Normalize();
mass.normalVector = Vector3.Zero + direction;
// Scale size of particle; Apply DPad Values between 1 and 75
mass.scale = new Vector3(particleMassValue / 2250.0f + 3);
mass.boundingVolume.radius = mass.scale.Y;
mass.boundingVolume.UpdateSphere(mass.currentPosition);
// Change Engergy of particle; Shift from 0 to 100 to -1 to 1
mass.energy = ((float)particleThermalValue * 2 - 75) / 75;
if (mass.energy != 0.0f) { mass.isChanging = true; }
// Instancing
//objectValues = new VertexFogBinormalTangent();
//objectValues.Fog = new Vector4(mass.scale, 0.0f); // Store the scale
//objectValues.Binormal = new Vector4(mass.currentRotation.X,
// mass.currentRotation.Y,
// mass.currentRotation.Z,
// mass.currentRotation.W); // Store the quaternion rotation
//objectValues.Tangent = new Vector4(mass.currentPosition, 0.0f); // Store the translation
}
///
/// Update our particle object
///
/// ObjectLibrary
public override void Update(ref ObjectLibrary objectLibrary)
{
mass.UpdatePosition();
mass.boundingVolume.UpdateSphere(mass.currentPosition);
//Update COR based on energy level
mass.COR = Statics.ParticleSettings.Wind.WIND_COLD_COR - ((mass.energy / 2.0f) + 0.5f) * (Statics.ParticleSettings.Wind.WIND_COLD_COR - Statics.ParticleSettings.Wind.WIND_HOT_COR);
mass.dynamicFrictionCoefficient = Statics.ParticleSettings.Wind.WIND_COLD_FRICTION - ((mass.energy / 2.0f) + 0.5f) * (Statics.ParticleSettings.Wind.WIND_COLD_FRICTION - Statics.ParticleSettings.Wind.WIND_HOT_FRICTION);
mass.Rotate(0.0f, 0.2f);
// Send properties and control signal to Shaders
Vector4 gpu_particleType = new Vector4(0);
if (this.particleType == Enums.ParticleType.Earth) { gpu_particleType.Y = 0.0f; } // Earth = x0xx
if (this.particleType == Enums.ParticleType.Water) { gpu_particleType.Y = 1.0f; } // Water = x1xx
if (this.particleType == Enums.ParticleType.Air) { gpu_particleType.Y = 2.0f; } // Gas = x2xx
gpu_particleType.Z = (mass.energy / 2.0f) + 0.5f; // Fire = xxNx
gpu_particleType.W = waveRadius;
// Instancing buffers
//objectValues = new VertexFogBinormalTangent();
//objectValues.Fog = new Vector4(mass.scale.Y, gpu_particleType.Y, gpu_particleType.Z, gpu_particleType.W); // Store the scale
//objectValues.Binormal = new Vector4(mass.currentRotation.X,
// mass.currentRotation.Y,
// mass.currentRotation.Z,
// mass.currentRotation.W); // Store the quaternion rotation
//objectValues.Tangent = new Vector4(mass.currentPosition, 0.0f); // Store the translation
}
///
/// Draw the particle object
///
/// ModelManager
/// Technique to use when drawing the particle
public override void Draw(ModelManager modelManager, string technique)
{
// Calculate ObjectSpace(Rotation) and WorldSpace(Translation) Transformation Matrix
matrix = Matrix.CreateScale(mass.scale) *
Matrix.CreateFromQuaternion(mass.currentRotation) *
Matrix.CreateTranslation(mass.currentPosition);
// Matricies
ShaderParameters.World.SetValue(matrix);
// Lighting
ShaderParameters.shininess.SetValue(7000.0f);
//ShaderParameters.materialDiffuse.SetValue(materialDiffuse);
//ShaderParameters.materialSpecular.SetValue(materialSpecular);
ModelInfo modelInfo = modelManager.GetModelInfo(objectModelName);
ShaderParameters.modelTexture1.SetValue(modelInfo.currentTexture);
ShaderParameters.bumpTexture1.SetValue(modelInfo.bumpTexture);
base.DrawTriStrips(modelInfo.model, technique);
}
///
/// Draw the particle's bounding sphere around it
///
/// ModelManager
public void DrawBoundingSphere(ModelManager modelManager)
{
matrix = Matrix.CreateScale(mass.boundingVolume.radius) *
Matrix.CreateFromQuaternion(mass.currentRotation) *
Matrix.CreateTranslation(mass.currentPosition);
ShaderParameters.World.SetValue(matrix);
ModelInfo modelInfo = modelManager.GetModelInfo(objectModelName);
float[] color = { 1, 1, 1, 1 };
ShaderParameters.materialDiffuse.SetValue(color);
ShaderParameters.modelTexture1.SetValue(textureList[0]);
// Draw the model but with the shield shader
foreach (ModelMesh mesh in modelInfo.model.Meshes)
{
foreach (Effect currentEffect in mesh.Effects)
{
currentEffect.CurrentTechnique = currentEffect.Techniques["ShieldShaderMain"];
}
mesh.Draw();
}
}
///
/// Draw any billboard associated with this particle
///
/// ObjectLibrary
/// List of Billboards
public override void Draw_Billboards(ObjectLibrary objectLibrary, BillboardList gameObjectBillboards)
{
float radius = 10.0f;
// Trig Position Cycle
angle += 0.1;
if (angle > Math.PI * 2.0f)
{ angle = 0.0f; }
Random random = new Random();
Vector3 offset = new Vector3(
(float) (radius * Math.Cos(angle)),
0.0f,
(float) (radius * Math.Cos(angle))
);
if( billboards.Count < 10)
{
billboards.Add(new Billboard(mass.currentPosition + offset, Vector3.Zero, 15.0f, 15.0f, textureList[0]));
}
else
{
// Remove next billboard
Billboard billboard = billboards[smokeCounter];
billboards.RemoveAt(smokeCounter);
smokeCounter++;
// Recycle counter
if (smokeCounter == 5)
smokeCounter = 0;
billboard.SetPosition(mass.currentPosition + offset);
billboards.Add(billboard);
}
// Add a billboard texture above the particle
gameObjectBillboards.Add(billboards);
}
///
/// Add Particle specific responses to an object to object collision involving this and collidedObject
///
/// Object colliding with this object
/// Resultant force of collision
/// ObjectLibrary
/// True = this object needs to be recycled
public override bool ObjectCollisionResponse(GameObject collidedObject, Vector3 resultantForce, ObjectLibrary objectLibrary)
{
mass.AddForce(resultantForce);
if (collidedObject.mass.objectType == Enums.ObjectType.PLAYER)
{
// Add Player specific responses to the collided player
// Remove this object
return true;
}
return false;
}
///
/// Add Particle specific responses to an object to terrain collision involving this
///
/// Terrain
public override void TerrainCollisionResponse(Terrain terrain)
{
base.GetObjectHeight(terrain);
// Add Gravity if terrain and player are touching
if (heightDifference < 0.0f)
{
mass.AddForce(mass.mass * Statics.GameSettings.accelDueToGravity);
}
else if (heightDifference > 0.0f)
{
// Call base first
base.TerrainCollisionResponse(terrain);
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,
Vector3.Distance((mass.mass * gravityNormalComponent), totalForceNormalComponent),
combinedTangentialForce);
}
else
{
mass.AddForce(mass.mass * -velocityTangentialComponent / Statics.SystemSettings.dt);
mass.AddForce(-totalForceTangentialComponent);
}
}
}
}
}