ArtificialIntelligence.cs 8.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287
  1. #region File Description
  2. //-----------------------------------------------------------------------------
  3. // ArtificialIntelligence.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 System.Collections.Generic;
  12. using RolePlayingGameData;
  13. #endregion
  14. namespace RolePlaying
  15. {
  16. /// <summary>
  17. /// Determines actions for a given monster in combat.
  18. /// </summary>
  19. /// <remarks>
  20. /// This was separated from the Monster type so the two kinds of code
  21. /// (combat operation and data encapsulation) remain clear for easy re-use.
  22. /// </remarks>
  23. class ArtificialIntelligence
  24. {
  25. /// <summary>
  26. /// The monster that this object is choosing actions for.
  27. /// </summary>
  28. private CombatantMonster monster;
  29. #region Action Lists
  30. /// <summary>
  31. /// The offensive actions available to the monster.
  32. /// </summary>
  33. private List<CombatAction> offensiveActions = new List<CombatAction>();
  34. /// <summary>
  35. /// The defensive actions available to the monster.
  36. /// </summary>
  37. private List<CombatAction> defensiveActions = new List<CombatAction>();
  38. #endregion
  39. #region Initialization
  40. /// <summary>
  41. /// Construct a new ArtificialIntelligence object to control a given combatant.
  42. /// </summary>
  43. public ArtificialIntelligence(CombatantMonster monster)
  44. {
  45. // check the parameter
  46. if (monster == null)
  47. {
  48. throw new ArgumentNullException("monster");
  49. }
  50. // assign the parameter
  51. this.monster = monster;
  52. // generate all actions available
  53. GenerateAllActions();
  54. }
  55. #endregion
  56. #region Action Generation
  57. /// <summary>
  58. /// Generate the actions available to this monster.
  59. /// </summary>
  60. private void GenerateAllActions()
  61. {
  62. // clear out any pre-existing actions
  63. offensiveActions.Clear();
  64. defensiveActions.Clear();
  65. // generate the melee attack option
  66. GenerateMeleeAction();
  67. // generate the spell attack options
  68. GenerateSpellAttackActions();
  69. // generate the defend action
  70. GenerateDefendAction();
  71. // sort the lists by potential, descending
  72. offensiveActions.Sort(CombatAction.CompareCombatActionsByHeuristic);
  73. defensiveActions.Sort(CombatAction.CompareCombatActionsByHeuristic);
  74. }
  75. /// <summary>
  76. /// Generate the melee attack option for this monster.
  77. /// </summary>
  78. private void GenerateMeleeAction()
  79. {
  80. // add a new melee action to the list
  81. offensiveActions.Add(new MeleeCombatAction(monster));
  82. }
  83. /// <summary>
  84. /// Generate the melee attack option for this monster.
  85. /// </summary>
  86. private void GenerateDefendAction()
  87. {
  88. // add a new melee action to the list
  89. defensiveActions.Add(new DefendCombatAction(monster));
  90. }
  91. /// <summary>
  92. /// Generate the spell attack options for this monster.
  93. /// </summary>
  94. private void GenerateSpellAttackActions()
  95. {
  96. // retrieve the spells for this monster
  97. List<Spell> spells = monster.Monster.Spells;
  98. // if there are no spells, then there's nothing to do
  99. if ((spells == null) || (spells.Count <= 0))
  100. {
  101. return;
  102. }
  103. // check each spell for attack actions
  104. foreach (Spell spell in spells)
  105. {
  106. // skip non-offensive spells
  107. if (!spell.IsOffensive)
  108. {
  109. continue;
  110. }
  111. // add the new action to the list
  112. offensiveActions.Add(new SpellCombatAction(monster, spell));
  113. }
  114. }
  115. #endregion
  116. #region Action Selection
  117. /// <summary>
  118. /// Choose the next action for the monster.
  119. /// </summary>
  120. /// <returns>The chosen action, or null if no action is desired.</returns>
  121. public CombatAction ChooseAction()
  122. {
  123. CombatAction combatAction = null;
  124. // determine if the monster will use a defensive action
  125. if ((monster.Monster.DefendPercentage > 0) &&
  126. (defensiveActions.Count > 0) &&
  127. (Session.Random.Next(0, 100) < monster.Monster.DefendPercentage))
  128. {
  129. combatAction = ChooseDefensiveAction();
  130. }
  131. // if we do not have an action yet, choose an offensive action
  132. combatAction = (combatAction ?? ChooseOffensiveAction());
  133. // reset the action to the initial state
  134. combatAction.Reset();
  135. return combatAction;
  136. }
  137. /// <summary>
  138. /// Choose which offensive action to perform.
  139. /// </summary>
  140. /// <returns>The chosen action, or null if no action is desired.</returns>
  141. private CombatAction ChooseOffensiveAction()
  142. {
  143. List<CombatantPlayer> players = CombatEngine.Players;
  144. // be sure that there is a valid combat in progress
  145. if ((players == null) || (players.Count <= 0))
  146. {
  147. return null;
  148. }
  149. // randomly choose a living target from the party
  150. int targetIndex;
  151. do
  152. {
  153. targetIndex = Session.Random.Next(players.Count);
  154. }
  155. while (players[targetIndex].IsDeadOrDying);
  156. CombatantPlayer target = players[targetIndex];
  157. // the action lists are sorted by descending potential,
  158. // so find the first eligible action
  159. foreach (CombatAction action in offensiveActions)
  160. {
  161. // check the restrictions on the action
  162. if (action.IsCharacterValidUser)
  163. {
  164. action.Target = target;
  165. return action;
  166. }
  167. }
  168. // no eligible actions found
  169. return null;
  170. }
  171. /// <summary>
  172. /// Choose which defensive action to perform.
  173. /// </summary>
  174. /// <returns>The chosen action, or null if no action is desired.</returns>
  175. private CombatAction ChooseDefensiveAction()
  176. {
  177. List<CombatantMonster> monsters = CombatEngine.Monsters;
  178. // be sure that there is a valid combat in progress
  179. if ((monsters == null) || (monsters.Count <= 0))
  180. {
  181. return null;
  182. }
  183. // find the monster with the least health
  184. CombatantMonster target = null;
  185. int leastHealthAmount = Int32.MaxValue;
  186. foreach (CombatantMonster targetMonster in monsters)
  187. {
  188. // skip dead or dying targets
  189. if (targetMonster.IsDeadOrDying)
  190. {
  191. continue;
  192. }
  193. // if the monster is damaged and it has the least health points,
  194. // then it becomes the new target
  195. StatisticsValue maxStatistics =
  196. targetMonster.Monster.CharacterClass.GetStatisticsForLevel(
  197. targetMonster.Monster.CharacterLevel);
  198. int targetMonsterHealthPoints = targetMonster.Statistics.HealthPoints;
  199. if ((targetMonsterHealthPoints < maxStatistics.HealthPoints) &&
  200. (targetMonsterHealthPoints < leastHealthAmount))
  201. {
  202. target = targetMonster;
  203. leastHealthAmount = targetMonsterHealthPoints;
  204. }
  205. }
  206. // if there is no target, then don't do anything
  207. if (target == null)
  208. {
  209. return null;
  210. }
  211. // the action lists are sorted by descending potential,
  212. // so find the first eligible action
  213. foreach (CombatAction action in defensiveActions)
  214. {
  215. // check the restrictions on the action
  216. if (action.IsCharacterValidUser)
  217. {
  218. action.Target = target;
  219. return action;
  220. }
  221. }
  222. // no eligible actions found
  223. return null;
  224. }
  225. #endregion
  226. }
  227. }