ArtificialIntelligence.cs 8.3 KB

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