Projectile.cs 7.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // Projectile.cs
  4. //
  5. // Microsoft XNA Community Game Platform
  6. // Copyright (C) Microsoft Corporation. All rights reserved.
  7. //-----------------------------------------------------------------------------
  8. #endregion
  9. #region Using Statements
  10. using System;
  11. using Microsoft.Xna.Framework;
  12. using Microsoft.Xna.Framework.Graphics;
  13. #endregion
  14. namespace VectorRumble
  15. {
  16. /// <summary>
  17. /// Base class for all projectiles that exist in the game.
  18. /// </summary>
  19. abstract class Projectile : Actor
  20. {
  21. #region Fields
  22. /// <summary>
  23. /// The player who fired this projectile.
  24. /// </summary>
  25. protected Ship owner;
  26. /// <summary>
  27. /// The speed that the projectile will move at.
  28. /// </summary>
  29. protected float speed = 0f;
  30. /// <summary>
  31. /// The amount that this projectile hurts it's target and those around it.
  32. /// </summary>
  33. protected float damageAmount = 0f;
  34. /// <summary>
  35. /// The radius at which this projectile hurts other actors when it explodes.
  36. /// </summary>
  37. protected float damageRadius = 0f;
  38. /// <summary>
  39. /// The amount of time before this projectile dies on it's own.
  40. /// </summary>
  41. protected float duration = 0f;
  42. /// <summary>
  43. /// If true, this object will damage it's owner if it hits it
  44. /// </summary>
  45. protected bool damageOwner = true;
  46. /// <summary>
  47. /// If true, this object explodes - calling Explode() - when it dies.
  48. /// </summary>
  49. protected bool explodes = false;
  50. /// <summary>
  51. /// The colors used in the particle system shown when this projectile hits.
  52. /// </summary>
  53. protected Color[] explosionColors;
  54. #endregion
  55. #region Properties
  56. public Ship Owner
  57. {
  58. get { return owner; }
  59. }
  60. #endregion
  61. #region Initialization
  62. /// <summary>
  63. /// Constructs a new projectile.
  64. /// </summary>
  65. /// <param name="world">The world that this projectile belongs to.</param>
  66. /// <param name="owner">The ship that fired this projectile, if any.</param>
  67. /// <param name="direction">The initial direction for this projectile.</param>
  68. public Projectile(World world, Ship owner, Vector2 direction)
  69. : base(world)
  70. {
  71. this.owner = owner;
  72. this.position = owner.Position;
  73. this.velocity = direction;
  74. }
  75. #endregion
  76. #region Update
  77. /// <summary>
  78. /// Update the projectile.
  79. /// </summary>
  80. /// <param name="elapsedTime">The amount of elapsed time, in seconds.</param>
  81. public override void Update(float elapsedTime)
  82. {
  83. // projectiles can "time out"
  84. if (duration > 0f)
  85. {
  86. duration -= elapsedTime;
  87. if (duration < 0f)
  88. {
  89. Die(null);
  90. }
  91. }
  92. base.Update(elapsedTime);
  93. }
  94. #endregion
  95. #region Interaction
  96. /// <summary>
  97. /// Damages all actors in a radius around the projectile.
  98. /// </summary>
  99. /// <param name="touchedActor">The actor that was originally hit.</param>
  100. public virtual void Explode(Actor touchedActor)
  101. {
  102. // if there is no radius, then don't bother
  103. if (damageRadius <= 0f)
  104. {
  105. return;
  106. }
  107. // check each actor for damage
  108. foreach (Actor actor in world.Actors)
  109. {
  110. // don't bother if it's already dead
  111. if (actor.Dead == true)
  112. {
  113. continue;
  114. }
  115. // don't hurt the actor that the projectile hit, it's already hurt
  116. if (actor == touchedActor)
  117. {
  118. continue;
  119. }
  120. // don't hit the owner if the damageOwner flag is off
  121. if ((actor == owner) && (damageOwner == false))
  122. {
  123. continue;
  124. }
  125. // measure the distance to the actor and see if it's in range
  126. float distance = (actor.Position - this.Position).Length();
  127. if (distance <= damageRadius)
  128. {
  129. // adjust the amount of damage based on the distance
  130. // -- note that damageRadius <= 0 is accounted for earlier
  131. float adjustedDamage = damageAmount *
  132. (damageRadius - distance) / damageRadius;
  133. // if we're still damaging the actor, then go ahead and apply it
  134. if (adjustedDamage > 0f)
  135. {
  136. actor.Damage(this, adjustedDamage);
  137. }
  138. }
  139. }
  140. }
  141. /// <summary>
  142. /// Defines the interaction between this projectile and a target actor
  143. /// when they touch.
  144. /// </summary>
  145. /// <param name="target">The actor that is touching this object.</param>
  146. /// <returns>True if the objects meaningfully interacted.</returns>
  147. public override bool Touch(Actor target)
  148. {
  149. // check the target, if we have one
  150. if (target != null)
  151. {
  152. // don't bother hitting any power-ups
  153. if (target is PowerUp)
  154. {
  155. return false;
  156. }
  157. // don't hit the owner if the damageOwner flag isn't set
  158. if ((target == owner) && (this.damageOwner == false))
  159. {
  160. return false;
  161. }
  162. // don't hit other projectiles from the same ship
  163. Projectile projectile = target as Projectile;
  164. if ((projectile != null) && (projectile.Owner == this.Owner))
  165. {
  166. return false;
  167. }
  168. // damage the target
  169. target.Damage(this, this.damageAmount);
  170. }
  171. // either we hit something or the target is null - in either case, die
  172. Die(target);
  173. return base.Touch(target);
  174. }
  175. /// <summary>
  176. /// Kills this projectile, in response to the given actor.
  177. /// </summary>
  178. /// <param name="source">The actor responsible for the kill.</param>
  179. public override void Die(Actor source)
  180. {
  181. if (dead == false)
  182. {
  183. base.Die(source);
  184. if (dead && explodes)
  185. {
  186. Explode(source);
  187. }
  188. }
  189. }
  190. /// <summary>
  191. /// Place this projectile in the world.
  192. /// </summary>
  193. /// <param name="findSpawnPoint">
  194. /// If true, the actor's position is changed to a valid, non-colliding point.
  195. /// </param>
  196. public override void Spawn(bool findSpawnPoint)
  197. {
  198. Vector2 newVelocity = speed * Vector2.Normalize(velocity);
  199. base.Spawn(findSpawnPoint);
  200. // reset the velocity to the speed times the current direction;
  201. velocity = newVelocity;
  202. }
  203. #endregion
  204. }
  205. }