//--------------------------------------------------------------------------------------------------------------------------------------------------- // // 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.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; using DarkWynter.Engine.Init; using DarkWynter.Stream; /// /// Particle game object /// public class Particle : GameObject { #region Properties double angle = 0.0; /// /// Particle-type Enumeration /// public Enums_Engine.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; #endregion #region Construction Methods /// /// Constructor /// public Particle(Load gameObjectLoader):base(gameObjectLoader) { mass.objectType = Enums_Engine.ObjectType.PARTICLE; mass.gameObjectPointer = this; mass.boundingVolume = new BoundingVolume(new BoundingSphere(mass.currentPosition, 2.0f)); mass.objectHeight = mass.boundingVolume.radius; mass.mass = Statics_Engine.ParticleSettings.Water.WATER_MASS; mass.COR = Statics_Engine.ParticleSettings.Water.WATER_COLD_COR; mass.dynamicFrictionCoefficient = Statics_Engine.ParticleSettings.Water.WATER_COLD_FRICTION; mass.energy = 1.0f; mass.isMoving = true; waveRadius = 1.0f; hasCollided = -1; hurtOwner = false; hurtOwnerTimer = Stopwatch.StartNew(); collisionWithPlayerResponse = Enums_Engine.CollisionResponses.HEALTHLOSS; } /// /// Load particle info from XML /// /// Particle node /// ObjectLibrary /// True if load was successful public override bool Load(ObjectLibrary objectLibrary) { // Set up Model bool success = LoadModel(load.node); // Return unsuccessfully if can't load user supplied XML data if (!success) { return false; } draw.drawMethod = Enums_Stream.DrawMethod.BasicGameObject_Draw; drawBoundingVolume = draw; draw.technique = "BasicLightingShaderMain"; drawBoundingVolume.technique = "ShieldShaderMain"; // Start Type properties objectModelName = load.node.Attributes["name"].Value; // Add Sprite Textures draw.textureList.Add(Statics_Engine.SystemSettings.content.Load("Content/_textures/Fire")); draw.textureList.Add(Statics_Engine.SystemSettings.content.Load("Content/_textures/Wind")); drawBoundingVolume.textureList.Add(Statics_Engine.SystemSettings.content.Load("Content/_textures/whitesquare")); drawBoundingVolume.textureList.Add(Statics_Engine.SystemSettings.content.Load("Content/_textures/Wind")); mass.scale = new Vector3(float.Parse(load.node.Attributes["maxScale"].Value)); mass.mass = float.Parse(load.node.Attributes["mass"].Value); mass.isMoving = false; isCollectable = bool.Parse(load.node.Attributes["isCollectable"].Value); isKey = bool.Parse(load.node.Attributes["isKey"].Value); if (isKey) { collisionWithPlayerResponse = Enums_Engine.CollisionResponses.HEALTHBONUS; } 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_Engine.ParticleType type, Vector3 initialPosition, Vector3 direction, int particleMassValue, int particleThermalValue) { particleType = type; mass.AddForce(direction * Statics_Engine.PlayerSettings.DIRECT_ATTACK_FORCE); mass.SetPosition(initialPosition + (Statics_Engine.PlayerSettings.PARTICLE_START_DISTANCE * direction), initialPosition + ((Statics_Engine.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; } } #endregion #region Update Methods /// /// Update our particle object /// /// ObjectLibrary public override void Update(ref ObjectLibrary objectLibrary) { mass.Update(); mass.boundingVolume.UpdateSphere(mass.currentPosition); //Update COR based on energy level mass.COR = Statics_Engine.ParticleSettings.Wind.WIND_COLD_COR - ((mass.energy / 2.0f) + 0.5f) * (Statics_Engine.ParticleSettings.Wind.WIND_COLD_COR - Statics_Engine.ParticleSettings.Wind.WIND_HOT_COR); mass.dynamicFrictionCoefficient = Statics_Engine.ParticleSettings.Wind.WIND_COLD_FRICTION - ((mass.energy / 2.0f) + 0.5f) * (Statics_Engine.ParticleSettings.Wind.WIND_COLD_FRICTION - Statics_Engine.ParticleSettings.Wind.WIND_HOT_FRICTION); mass.Rotate(0.0f, 0.2f); // Send properties and control signal to gpu Vector4 gpu_particleType = new Vector4(0); if (this.particleType == Enums_Engine.ParticleType.Earth) { gpu_particleType.Y = 0.0f; } // Earth = x0xx if (this.particleType == Enums_Engine.ParticleType.Water) { gpu_particleType.Y = 1.0f; } // Water = x1xx if (this.particleType == Enums_Engine.ParticleType.Air) { gpu_particleType.Y = 2.0f; } // Gas = x2xx gpu_particleType.Z = (mass.energy / 2.0f) + 0.5f; // Fire = xxNx gpu_particleType.W = waveRadius; } #endregion #region Collision Responses /// /// 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_Engine.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_Engine.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_Engine.SystemSettings.dt); mass.AddForce(-totalForceTangentialComponent); } } } #endregion #region Draw Methods /// /// Draw the particle object /// public override Draw Draw() { // Calculate ObjectSpace(Rotation) and WorldSpace(Translation) Transformation Matrix draw.matrix = draw.initialTransform * Matrix.CreateScale(mass.scale) * Matrix.CreateFromQuaternion(mass.currentRotation) * Matrix.CreateTranslation(mass.currentPosition); return draw; } /// /// Draw the particle's bounding sphere around it /// public void DrawBoundingSphere() { drawBoundingVolume.matrix = drawBoundingVolume.initialTransform * Matrix.CreateScale(mass.boundingVolume.radius) * Matrix.CreateFromQuaternion(mass.currentRotation) * Matrix.CreateTranslation(mass.currentPosition); float[] color = { 1, 1, 1, 1 }; ShaderParameters.DrawFX.materialDiffuse.SetValue(color); drawBoundingVolume.DoDraw(); } /// /// 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) { Billboard billboard = new Billboard(null); billboard.Load(mass.currentPosition + offset, Vector3.Zero, 15.0f, 15.0f, draw.textureList[0]); //billboard.Load(mass.currentPosition + offset, Vector3.Zero, 15.0f, 15.0f, Engine.DarkWynterEngine.video.CurrentTexture); billboards.Add(billboard); } else { // Remove next billboard Billboard billboard = billboards[smokeCounter]; billboards.RemoveAt(smokeCounter); smokeCounter++; // Recycle counter if (smokeCounter == 5) smokeCounter = 0; billboard.Position = mass.currentPosition + offset; billboard.Load(mass.currentPosition + offset, Vector3.Zero, 15.0f, 15.0f, draw.textureList[0]); //billboard.SetTexture(Engine.DarkWynterEngine.video.CurrentTexture); billboards.Add(billboard); } // Add a billboard texture above the particle gameObjectBillboards.AddRange(billboards); } #endregion } }