#region File Description
//-----------------------------------------------------------------------------
// ArtificialIntelligence.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using RolePlayingGameData;
#endregion
namespace RolePlaying
{
///
/// Determines actions for a given monster in combat.
///
///
/// This was separated from the Monster type so the two kinds of code
/// (combat operation and data encapsulation) remain clear for easy re-use.
///
class ArtificialIntelligence
{
///
/// The monster that this object is choosing actions for.
///
private CombatantMonster monster;
#region Action Lists
///
/// The offensive actions available to the monster.
///
private List offensiveActions = new List();
///
/// The defensive actions available to the monster.
///
private List defensiveActions = new List();
#endregion
#region Initialization
///
/// Construct a new ArtificialIntelligence object to control a given combatant.
///
public ArtificialIntelligence(CombatantMonster monster)
{
// check the parameter
if (monster == null)
{
throw new ArgumentNullException("monster");
}
// assign the parameter
this.monster = monster;
// generate all actions available
GenerateAllActions();
}
#endregion
#region Action Generation
///
/// Generate the actions available to this monster.
///
private void GenerateAllActions()
{
// clear out any pre-existing actions
offensiveActions.Clear();
defensiveActions.Clear();
// generate the melee attack option
GenerateMeleeAction();
// generate the spell attack options
GenerateSpellAttackActions();
// generate the defend action
GenerateDefendAction();
// sort the lists by potential, descending
offensiveActions.Sort(CombatAction.CompareCombatActionsByHeuristic);
defensiveActions.Sort(CombatAction.CompareCombatActionsByHeuristic);
}
///
/// Generate the melee attack option for this monster.
///
private void GenerateMeleeAction()
{
// add a new melee action to the list
offensiveActions.Add(new MeleeCombatAction(monster));
}
///
/// Generate the melee attack option for this monster.
///
private void GenerateDefendAction()
{
// add a new melee action to the list
defensiveActions.Add(new DefendCombatAction(monster));
}
///
/// Generate the spell attack options for this monster.
///
private void GenerateSpellAttackActions()
{
// retrieve the spells for this monster
List spells = monster.Monster.Spells;
// if there are no spells, then there's nothing to do
if ((spells == null) || (spells.Count <= 0))
{
return;
}
// check each spell for attack actions
foreach (Spell spell in spells)
{
// skip non-offensive spells
if (!spell.IsOffensive)
{
continue;
}
// add the new action to the list
offensiveActions.Add(new SpellCombatAction(monster, spell));
}
}
#endregion
#region Action Selection
///
/// Choose the next action for the monster.
///
/// The chosen action, or null if no action is desired.
public CombatAction ChooseAction()
{
CombatAction combatAction = null;
// determine if the monster will use a defensive action
if ((monster.Monster.DefendPercentage > 0) &&
(defensiveActions.Count > 0) &&
(Session.Random.Next(0, 100) < monster.Monster.DefendPercentage))
{
combatAction = ChooseDefensiveAction();
}
// if we do not have an action yet, choose an offensive action
combatAction = (combatAction ?? ChooseOffensiveAction());
// reset the action to the initial state
combatAction.Reset();
return combatAction;
}
///
/// Choose which offensive action to perform.
///
/// The chosen action, or null if no action is desired.
private CombatAction ChooseOffensiveAction()
{
List players = CombatEngine.Players;
// be sure that there is a valid combat in progress
if ((players == null) || (players.Count <= 0))
{
return null;
}
// randomly choose a living target from the party
int targetIndex;
do
{
targetIndex = Session.Random.Next(players.Count);
}
while (players[targetIndex].IsDeadOrDying);
CombatantPlayer target = players[targetIndex];
// the action lists are sorted by descending potential,
// so find the first eligible action
foreach (CombatAction action in offensiveActions)
{
// check the restrictions on the action
if (action.IsCharacterValidUser)
{
action.Target = target;
return action;
}
}
// no eligible actions found
return null;
}
///
/// Choose which defensive action to perform.
///
/// The chosen action, or null if no action is desired.
private CombatAction ChooseDefensiveAction()
{
List monsters = CombatEngine.Monsters;
// be sure that there is a valid combat in progress
if ((monsters == null) || (monsters.Count <= 0))
{
return null;
}
// find the monster with the least health
CombatantMonster target = null;
int leastHealthAmount = Int32.MaxValue;
foreach (CombatantMonster targetMonster in monsters)
{
// skip dead or dying targets
if (targetMonster.IsDeadOrDying)
{
continue;
}
// if the monster is damaged and it has the least health points,
// then it becomes the new target
StatisticsValue maxStatistics =
targetMonster.Monster.CharacterClass.GetStatisticsForLevel(
targetMonster.Monster.CharacterLevel);
int targetMonsterHealthPoints = targetMonster.Statistics.HealthPoints;
if ((targetMonsterHealthPoints < maxStatistics.HealthPoints) &&
(targetMonsterHealthPoints < leastHealthAmount))
{
target = targetMonster;
leastHealthAmount = targetMonsterHealthPoints;
}
}
// if there is no target, then don't do anything
if (target == null)
{
return null;
}
// the action lists are sorted by descending potential,
// so find the first eligible action
foreach (CombatAction action in defensiveActions)
{
// check the restrictions on the action
if (action.IsCharacterValidUser)
{
action.Target = target;
return action;
}
}
// no eligible actions found
return null;
}
#endregion
}
}