PageRenderTime 65ms CodeModel.GetById 25ms RepoModel.GetById 0ms app.codeStats 0ms

/Classes/Object/Unit.cs

http://github.com/Concliff/Maze
C# | 775 lines | 437 code | 110 blank | 228 comment | 67 complexity | 99b4f41db2d2f81bbdbdf55d35661745 MD5 | raw file
  1. using System;
  2. using System.Collections.Generic;
  3. using System.Collections;
  4. using System.Linq;
  5. using System.Text;
  6. using Maze.Forms;
  7. namespace Maze.Classes
  8. {
  9. /// <summary>
  10. /// Specifies the type of object (or derived class) that an instance of the <see cref="Unit"/> class represents.
  11. /// </summary>
  12. public enum UnitTypes
  13. {
  14. /// <summary>
  15. /// Default type. An <see cref="Unit"/> instance is not initialized and cannot belong to the specific type.
  16. /// </summary>
  17. Unit,
  18. /// <summary>
  19. /// An <see cref="Unit"/> instance is derived from <see cref="Deimos"/> class.
  20. /// </summary>
  21. Deimos,
  22. /// <summary>
  23. /// An <see cref="Unit"/> instance is derived from <see cref="Phobos"/> class.
  24. /// </summary>
  25. Phobos,
  26. /// <summary>
  27. /// An <see cref="Unit"/> instance is derived from <see cref="Slug"/> class.
  28. /// </summary>
  29. Slug,
  30. /// <summary>
  31. /// An <see cref="Unit"/> instance is derived from <see cref="SlugClone"/> class.
  32. /// </summary>
  33. SlugClone,
  34. };
  35. /// <summary>
  36. /// Defines the Unit state showing possibility to interact with other objects or environment.
  37. /// </summary>
  38. public enum DeathStates
  39. {
  40. /// <summary>
  41. /// Unit is existing on map and can move, cast spells etc.
  42. /// </summary>
  43. Alive,
  44. /// <summary>
  45. /// Unit is waiting for respawn timer expired to become <see cref="DeathStates.Alive"/>.
  46. /// </summary>
  47. Dead,
  48. };
  49. /// <summary>
  50. /// Defines two sides of the Unit forces that are the enemies to each other.
  51. /// </summary>
  52. public enum UnitSides
  53. {
  54. /// <summary>
  55. /// Any friendly unit and the Slug itself.
  56. /// </summary>
  57. Good,
  58. /// <summary>
  59. /// Any Hostile units.
  60. /// </summary>
  61. Evil,
  62. };
  63. /// <summary>
  64. /// Specifies unit special features.
  65. /// </summary>
  66. [Flags]
  67. public enum UnitFlags
  68. {
  69. /// <summary>
  70. /// Default value.
  71. /// </summary>
  72. None = 0x000,
  73. /// <summary>
  74. /// The <see cref="Unit"/> cannot be in the <see cref="DeathStatus.Dead"/> state.
  75. /// </summary>
  76. CanNotBeKilled = 0x001,
  77. };
  78. /// <summary>
  79. /// Provides data for the <see cref="EffectHandler.EffectApplied"/> and <see cref="EffectHandler.EffectRemoved"/> events.
  80. /// </summary>
  81. public class EffectEventArgs : EventArgs
  82. {
  83. private EffectHolder pr_Holder;
  84. /// <summary>
  85. /// Gets an <see cref="EffectHolder"/> instance that raises the event.
  86. /// </summary>
  87. public EffectHolder Holder { get { return this.pr_Holder; } }
  88. /// <summary>
  89. /// Initializes a new instance of the EffectEventArgs class.
  90. /// </summary>
  91. /// <param name="holder">EffectHolder that raises the event.</param>
  92. public EffectEventArgs(EffectHolder holder)
  93. {
  94. this.pr_Holder = holder;
  95. }
  96. }
  97. /// <summary>
  98. /// Represents custom collection class that stores all the <see cref="EffectHolder"/> instances applied to the specific <see cref="Unit"/> instance.
  99. /// </summary>
  100. public class EffectCollection
  101. {
  102. /// <summary>
  103. /// Represents the method that will handle the <see cref="EffectApplied"/> or <see cref="EffectRemoved"/> event.
  104. /// </summary>
  105. /// <param name="sender">The sourse of the event.</param>
  106. /// <param name="e">A <see cref="EffectEventArgs"/> that contains the event data.</param>
  107. public delegate void EffectHandler(object sender, EffectEventArgs e);
  108. /// <summary>
  109. /// Occurs after a holder has been added from <see cref="EffectCollection"/>.
  110. /// </summary>
  111. public event EffectHandler EffectApplied;
  112. /// <summary>
  113. /// Occurs after a holder has been deleted from <see cref="EffectCollection"/>.
  114. /// </summary>
  115. public event EffectHandler EffectRemoved;
  116. private List<EffectHolder> effectList;
  117. private Unit owner;
  118. /// <summary>
  119. /// Initializes a new instance of the EffectCollection class.
  120. /// </summary>
  121. /// <param name="owner">The <see cref="Unit"/> instance whom all these effect will belong to.</param>
  122. public EffectCollection(Unit owner)
  123. {
  124. this.owner = owner;
  125. effectList = new List<EffectHolder>();
  126. }
  127. /// <summary>
  128. /// Gets a number of effects contained in <see cref="EffectCollection"/>.
  129. /// </summary>
  130. public int Count
  131. {
  132. get
  133. {
  134. return effectList.Count;
  135. }
  136. }
  137. /// <summary>
  138. /// Gets an effect holder at the specific index.
  139. /// </summary>
  140. /// <param name="index">The zero-based index of the element to get or set.</param>
  141. /// <returns>The <see cref="EffectHolder"/> instance at the specific index.</returns>
  142. public EffectHolder this[int index]
  143. {
  144. get
  145. {
  146. return effectList[index];
  147. }
  148. }
  149. public bool Add(EffectHolder holder)
  150. {
  151. // prevent applying double effect
  152. if (effectList.Count > 0)
  153. foreach (EffectHolder effect in effectList)
  154. {
  155. if (effect.EffectInfo.ID == holder.EffectInfo.ID)
  156. {
  157. effect.Refresh();
  158. return true;
  159. }
  160. }
  161. effectList.Add(holder);
  162. if (EffectApplied != null)
  163. {
  164. EffectEventArgs e = new EffectEventArgs(holder);
  165. EffectApplied(owner, e);
  166. }
  167. return true;
  168. }
  169. public EffectHolder GetHolder(int effectId)
  170. {
  171. return this.effectList.Find(p => p.EffectInfo.ID == effectId);
  172. }
  173. public bool Remove(EffectHolder holder)
  174. {
  175. if (!effectList.Contains(holder))
  176. return false;
  177. // Save Type of removable effect
  178. EffectTypes effectType = holder.EffectInfo.EffectType;
  179. if (effectList.Remove(holder))
  180. {
  181. if (EffectRemoved != null)
  182. {
  183. EffectEventArgs e = new EffectEventArgs(holder);
  184. EffectRemoved(owner, e);
  185. }
  186. }
  187. return true;
  188. }
  189. public void RemoveAll()
  190. {
  191. while(effectList.Count > 0)
  192. {
  193. Remove(effectList[0]);
  194. }
  195. }
  196. public void Update(int timeP)
  197. {
  198. for (int i = 0; i < effectList.Count; )
  199. {
  200. if (effectList[i].EffectState == EffectState.Expired)
  201. {
  202. Remove(effectList[i]);
  203. continue;
  204. }
  205. effectList[i].UpdateTime(timeP);
  206. ++i;
  207. }
  208. }
  209. }
  210. /// <summary>
  211. /// Represents a base class for dynamic objects that can move through the map.
  212. /// </summary>
  213. public abstract class Unit : Object
  214. {
  215. /// <summary>
  216. /// Occurs when object changed its Position without any movement processes.
  217. /// </summary>
  218. public event PositionHandler Relocated;
  219. /// <summary>
  220. /// Gets or sets respawn Location of the Unit.
  221. /// </summary>
  222. protected GridLocation respawnLocation;
  223. /// <summary>
  224. /// The current unit state.
  225. /// </summary>
  226. protected DeathStates deathState;
  227. protected int respawnTime;
  228. /// <summary>
  229. /// Shows after which time the unit will respawn (will change its deathState from <see cref="DeathState.Dead"/> to <see cref="DeathState.Alive"/>).
  230. /// </summary>
  231. protected int respawnTimer;
  232. /// <summary>
  233. /// Contains the flags set.
  234. /// </summary>
  235. protected UnitFlags unitFlags;
  236. /// <summary>
  237. /// Collection of effect holders that the unit has at this moment.
  238. /// </summary>
  239. protected EffectCollection effectList;
  240. /// <summary>
  241. /// The current movement engine of the unit.
  242. /// </summary>
  243. protected MovementGenerator motionMaster;
  244. /// <summary>
  245. /// Type of the unit. Specifies the unit's derived class.
  246. /// </summary>
  247. protected UnitTypes unitType;
  248. /// <summary>
  249. /// The forces side where the unit belongs to.
  250. /// </summary>
  251. protected UnitSides unitSide;
  252. /// <summary>
  253. /// A collection of the currently colliding units.
  254. /// </summary>
  255. private List<Unit> collidingUnits;
  256. /// <summary>
  257. /// Initializes a new instance of the Unit class.
  258. /// </summary>
  259. public Unit()
  260. {
  261. SetDeathState(DeathStates.Alive);
  262. this.unitFlags = UnitFlags.None;
  263. this.respawnLocation = new GridLocation();
  264. ObjectType = ObjectTypes.Unit;
  265. this.unitType = UnitTypes.Unit;
  266. this.unitSide = UnitSides.Evil;
  267. this.effectList = new EffectCollection(this);
  268. this.collidingUnits = new List<Unit>();
  269. BaseSpeed = 1.0d;
  270. SpeedRate = BaseSpeed;
  271. this.respawnTimer = 3000;
  272. this.effectList.EffectApplied += new EffectCollection.EffectHandler(OnEffectApplied);
  273. this.effectList.EffectRemoved += new EffectCollection.EffectHandler(OnEffectRemoved);
  274. }
  275. protected double pr_BaseSpeed;
  276. /// <summary>
  277. /// Gets or sets the Unit base speed value. Base Unit Speed is the speed of specific Unit type, without any effects or smth else.
  278. /// </summary>
  279. public double BaseSpeed
  280. {
  281. get
  282. {
  283. return this.pr_BaseSpeed;
  284. }
  285. protected set
  286. {
  287. this.pr_BaseSpeed = value;
  288. CalculateSpeedRate();
  289. }
  290. }
  291. protected double pt_SpeedRate; // Current speed(+effects)
  292. /// <summary>
  293. /// Gets or sets Unit current speed considering all movement effect at it.
  294. /// </summary>
  295. public double SpeedRate
  296. {
  297. get
  298. {
  299. return this.pt_SpeedRate;
  300. }
  301. protected set
  302. {
  303. this.pt_SpeedRate = value;
  304. }
  305. }
  306. /// <summary>
  307. /// Gets respawn location of the unit.
  308. /// </summary>
  309. public GridLocation Home
  310. {
  311. get
  312. {
  313. return this.respawnLocation;
  314. }
  315. }
  316. /// <summary>
  317. /// Gets a value indicating that the unit is in the <see cref="DeathStates.Alive"/> state.
  318. /// </summary>
  319. public bool IsAlive
  320. {
  321. get
  322. {
  323. return deathState == DeathStates.Alive;
  324. }
  325. }
  326. /// <summary>
  327. /// Gets a value indicating whether a Unit is located at its respawn <see cref="Cell"/>.
  328. /// </summary>
  329. public bool IsAtHome
  330. {
  331. get
  332. {
  333. return currentCell.Location.Equals(Home);
  334. }
  335. }
  336. /// <summary>
  337. /// Gets a value indicating that the unit is displayed on map and other units can interact with it.
  338. /// </summary>
  339. public bool IsVisible
  340. {
  341. get
  342. {
  343. return !HasEffectType(EffectTypes.Invisibility);
  344. }
  345. }
  346. /// <summary>
  347. /// Gets the unit type.
  348. /// </summary>
  349. public UnitTypes UnitType
  350. {
  351. get { return this.unitType; }
  352. }
  353. /// <summary>
  354. /// Gets the forces side of the unit.
  355. /// </summary>
  356. public UnitSides UnitSide
  357. {
  358. get { return this.unitSide; }
  359. }
  360. /// <summary>
  361. /// Registers this unit in <see cref="ObjectContainer"/> and sets its Home location.
  362. /// </summary>
  363. /// <param name="respawnLocation">Home location of the unit.</param>
  364. public void Create(GridLocation respawnLocation)
  365. {
  366. this.respawnLocation = respawnLocation;
  367. base.Create(new GPS(respawnLocation));
  368. }
  369. public void OnEffectApplied(object sender, EffectEventArgs e)
  370. {
  371. EffectHolder holder = e.Holder;
  372. // Update unit stats
  373. if (holder.EffectInfo.EffectType == EffectTypes.Snare ||
  374. holder.EffectInfo.EffectType == EffectTypes.IncreaseSpeed)
  375. CalculateSpeedRate();
  376. }
  377. public void OnEffectRemoved(object sender, EffectEventArgs e)
  378. {
  379. EffectHolder holder = e.Holder;
  380. // Update unit stats
  381. if (holder.EffectInfo.EffectType == EffectTypes.Snare ||
  382. holder.EffectInfo.EffectType == EffectTypes.IncreaseSpeed)
  383. CalculateSpeedRate();
  384. }
  385. /// <summary>
  386. /// Inidicating whether the unit has the specified flags.
  387. /// </summary>
  388. /// <param name="flags">Set of the flags to check. Multiple flags should be write as 'Flag1 | Flag2 | Flag3 ...'.</param>
  389. /// <returns><c>true</c> if the unit has all the checked flags; otherwise, <c>false</c>.</returns>
  390. public bool HasUnitFlags(UnitFlags flags)
  391. {
  392. return (flags & this.unitFlags) == flags;
  393. }
  394. /// <summary>
  395. /// Moves(Teleport) Unit on current direction
  396. /// </summary>
  397. /// <param name="distance">Distance in pixels</param>
  398. public void JumpThroughDistance(int distance)
  399. {
  400. if (this.motionMaster == null)
  401. return;
  402. // TODO:
  403. // 1. Add diagonal checking
  404. // 2. Improve current method
  405. bool isDiagonal = false;
  406. if (this.motionMaster.CurrentDirection.Second != Directions.None)
  407. {
  408. isDiagonal = true;
  409. distance = (int)Math.Sqrt(distance);
  410. }
  411. GPS newPosition = Position;
  412. GPS intermidiatePosition = Position;
  413. switch (this.motionMaster.CurrentDirection.First)
  414. {
  415. case Directions.Down:
  416. while (distance >= GlobalConstants.CELL_HEIGHT)
  417. {
  418. intermidiatePosition.Location.Y++;
  419. Cell interimPoint = Map.Instance.GetCell(intermidiatePosition.Location);
  420. if (interimPoint.ID != -1)
  421. newPosition = intermidiatePosition;
  422. distance -= GlobalConstants.CELL_HEIGHT;
  423. }
  424. break;
  425. case Directions.Up:
  426. while (distance >= GlobalConstants.CELL_HEIGHT)
  427. {
  428. intermidiatePosition.Location.Y--;
  429. Cell interimPoint = Map.Instance.GetCell(intermidiatePosition.Location);
  430. if (interimPoint.ID != -1)
  431. newPosition = intermidiatePosition;
  432. distance -= GlobalConstants.CELL_HEIGHT;
  433. }
  434. break;
  435. case Directions.Left:
  436. while (distance >= GlobalConstants.CELL_HEIGHT)
  437. {
  438. intermidiatePosition.Location.X--;
  439. Cell interimPoint = Map.Instance.GetCell(intermidiatePosition.Location);
  440. if (interimPoint.ID != -1)
  441. newPosition = intermidiatePosition;
  442. distance -= GlobalConstants.CELL_HEIGHT;
  443. }
  444. break;
  445. case Directions.Right:
  446. while (distance >= GlobalConstants.CELL_HEIGHT)
  447. {
  448. intermidiatePosition.Location.X++;
  449. Cell interimPoint = Map.Instance.GetCell(intermidiatePosition.Location);
  450. if (interimPoint.ID != -1)
  451. newPosition = intermidiatePosition;
  452. distance -= GlobalConstants.CELL_HEIGHT;
  453. }
  454. break;
  455. }
  456. // save previous position
  457. GPS prevPosition = Position;
  458. Position = newPosition;
  459. if (Relocated != null)
  460. Relocated(this, new PositionEventArgs(prevPosition, Position));
  461. }
  462. /// <summary>
  463. /// Relocates the unit to the specifed destination <see cref="Cell"/>.
  464. /// </summary>
  465. /// <param name="destinationCell">A cell where the unit is relocating</param>
  466. public void TeleportTo(Cell destinationCell)
  467. {
  468. // save old position
  469. GPS prevPosition = Position;
  470. Position = new GPS(destinationCell.Location, 25, 25);
  471. if (Relocated != null)
  472. Relocated(this, new PositionEventArgs(prevPosition, Position));
  473. }
  474. /// <summary>
  475. /// Relocates the unit to the secified destination position.
  476. /// </summary>
  477. /// <param name="destinationGPS">A new position where the unit is relocating.</param>
  478. public void TeleportTo(GridLocation destinationGPS)
  479. {
  480. Cell destinationCell = Map.Instance.GetCell(destinationGPS);
  481. TeleportTo(destinationCell);
  482. }
  483. public void CastEffect(ushort effectID, Unit target)
  484. {
  485. EffectEntry effectEntry = DBStores.EffectStore[effectID];
  486. Effect effect = new Effect(effectEntry, target, this);
  487. effect.Cast();
  488. }
  489. public void ApplyEffect(EffectHolder newHolder)
  490. {
  491. effectList.Add(newHolder);
  492. }
  493. protected void RemoveEffect(EffectHolder effectHolder)
  494. {
  495. effectList.Remove(effectHolder);
  496. }
  497. /// <summary>
  498. /// Determines whether the unit has applied aura with the specified effect type.
  499. /// </summary>
  500. /// <param name="effectType">One of the <see cref="EffectTypes"/> value.</param>
  501. /// <returns><c>true</c> if the unit has at least one aura with the specified type; otherwise, <c>false</c>.</returns>
  502. public bool HasEffectType(EffectTypes effectType)
  503. {
  504. return GetEffectsByType(effectType).Count > 0;
  505. }
  506. /// <summary>
  507. /// Returns a collection of effects with the specifed type.
  508. /// </summary>
  509. /// <param name="effectType"></param>
  510. /// <returns></returns>
  511. public List<EffectEntry> GetEffectsByType(EffectTypes effectType)
  512. {
  513. List<EffectEntry> result = new List<EffectEntry>();
  514. /*foreach (EffectHolder effect in effectList)
  515. {
  516. if (effect.EffectInfo.EffectType == effectType)
  517. result.Add(effect.EffectInfo);
  518. }*/
  519. for (int i = 0; i < effectList.Count; ++i)
  520. {
  521. if (effectList[i].EffectInfo.EffectType == effectType)
  522. result.Add(effectList[i].EffectInfo);
  523. }
  524. return result;
  525. }
  526. /// <summary>
  527. /// Computes the actual speed rate considering all the influencing effects.
  528. /// </summary>
  529. protected void CalculateSpeedRate()
  530. {
  531. SpeedRate = BaseSpeed;
  532. double speedModifier = 100;
  533. List<EffectEntry> speedEffects;
  534. speedEffects = GetEffectsByType(EffectTypes.Snare);
  535. foreach (EffectEntry effect in speedEffects)
  536. speedModifier *= effect.Value / 100d;
  537. speedEffects = GetEffectsByType(EffectTypes.IncreaseSpeed);
  538. foreach (EffectEntry effect in speedEffects)
  539. speedModifier *= effect.Value/100d + 1;
  540. SpeedRate *= speedModifier / 100d;
  541. }
  542. public virtual void SetDeathState(DeathStates deathState)
  543. {
  544. if (deathState == DeathStates.Dead)
  545. {
  546. // Remove All Effects
  547. effectList.RemoveAll();
  548. }
  549. this.deathState = deathState;
  550. }
  551. /// <summary>
  552. /// The unit changes the death state of another unit by force, getting a reward for it.
  553. /// </summary>
  554. /// <param name="victim">The target unit</param>
  555. /// <returns><c>true</c>, if a victim bacame dead; otherwise, <c>false</c>.</returns>
  556. public bool KillUnit(Unit victim)
  557. {
  558. if (victim.HasUnitFlags(UnitFlags.CanNotBeKilled))
  559. return false;
  560. victim.SetDeathState(DeathStates.Dead);
  561. // TODO:
  562. // Kind of reward for the killer
  563. return true;
  564. }
  565. /// <summary>
  566. /// Returns the unit to its Home location and changes the state to <see cref="DeathState.Alive"/>.
  567. /// </summary>
  568. protected void Respawn()
  569. {
  570. // Return to start location
  571. Position = new GPS(Home, 25, 25);
  572. SetDeathState(DeathStates.Alive);
  573. }
  574. public override void UpdateState(int timeP)
  575. {
  576. // Only GOOD units are checked for collisions
  577. if (UnitSide == UnitSides.Good)
  578. CheckUnitsCollision();
  579. if (deathState == DeathStates.Dead)
  580. {
  581. if (respawnTimer < 0)
  582. {
  583. Respawn();
  584. respawnTimer = 3000;
  585. }
  586. else
  587. respawnTimer -= timeP;
  588. }
  589. // Update unit effects
  590. effectList.Update(timeP);
  591. // Check for the nearest GridObjects
  592. List<GridObject> objects = GetGridObjectsWithinRange(30);
  593. foreach (GridObject obj in objects)
  594. {
  595. if (obj.IsActive && obj.HasFlags(GridObjectFlags.Usable))
  596. obj.Use(this);
  597. }
  598. base.UpdateState(timeP);
  599. }
  600. /// <summary>
  601. /// Starts the motion of a unit's movement generator.
  602. /// </summary>
  603. public void StartMotion()
  604. {
  605. if (this.motionMaster != null)
  606. this.motionMaster.StartMotion();
  607. }
  608. /// <summary>
  609. /// Stops the motion of a unit's movement generator.
  610. /// </summary>
  611. public void StopMotion()
  612. {
  613. if (this.motionMaster != null)
  614. this.motionMaster.StopMotion();
  615. }
  616. /// <summary>
  617. /// Checks whether the unit contacts with hostiles units and then calls the appropriate methods.
  618. /// </summary>
  619. protected void CheckUnitsCollision()
  620. {
  621. if (!IsAlive || !IsVisible)
  622. return;
  623. // Kill a unit in a collision with others Units with the opposite side.
  624. // Collision = any "hostile" Unit is passing by closer then 30
  625. List<Unit> Units = GetUnitsWithinRange(30);
  626. if (Units.Count > 0)
  627. {
  628. foreach (Unit unit in Units)
  629. {
  630. // Skip the same side units
  631. if (UnitSide == unit.UnitSide)
  632. continue;
  633. if (!this.collidingUnits.Exists(p => p.GUID == unit.GUID))
  634. {
  635. UnitCollisionBegins(unit);
  636. this.collidingUnits.Add(unit);
  637. }
  638. UnitCollision(unit);
  639. }
  640. }
  641. else
  642. if (this.collidingUnits.Count > 0)
  643. {
  644. foreach (Unit unit in this.collidingUnits)
  645. UnitCollisionEnds(unit);
  646. this.collidingUnits.Clear();
  647. }
  648. }
  649. /// <summary>
  650. /// Called every update when the unit is in contact with another unit.
  651. /// </summary>
  652. /// <param name="unit">The source of the collision.</param>
  653. protected virtual void UnitCollision(Unit unit) { }
  654. /// <summary>
  655. /// Called when the unit first time came in contact with another unit.
  656. /// </summary>
  657. /// <param name="unit">The source of the collision.</param>
  658. protected virtual void UnitCollisionBegins(Unit unit) { }
  659. /// <summary>
  660. /// Called when the unit goes away from the collision with another unit.
  661. /// </summary>
  662. /// <param name="unit">The source of the collision.</param>
  663. protected virtual void UnitCollisionEnds(Unit unit) { }
  664. }
  665. }