#region File Description
//-----------------------------------------------------------------------------
// FightingCharacter.cs
//
// Microsoft XNA Community Game Platform
// Copyright (C) Microsoft Corporation. All rights reserved.
//-----------------------------------------------------------------------------
#endregion
#region Using Statements
using System;
using System.Collections.Generic;
using Microsoft.Xna.Framework.Content;
#endregion
namespace RolePlayingGameData
{
///
/// A character that engages in combat.
///
public abstract class FightingCharacter : Character
{
#region Class Data
///
/// The name of the character class.
///
private string characterClassContentName;
///
/// The name of the character class.
///
public string CharacterClassContentName
{
get { return characterClassContentName; }
set { characterClassContentName = value; }
}
///
/// The character class itself.
///
private CharacterClass characterClass;
///
/// The character class itself.
///
[ContentSerializerIgnore]
public CharacterClass CharacterClass
{
get { return characterClass; }
set
{
characterClass = value;
ResetBaseStatistics();
}
}
///
/// The level of the character.
///
private int characterLevel = 1;
///
/// The level of the character.
///
public int CharacterLevel
{
get { return characterLevel; }
set
{
characterLevel = value;
ResetBaseStatistics();
spells = null;
}
}
///
/// Returns true if the character is at the maximum level allowed by their class.
///
public bool IsMaximumCharacterLevel
{
get
{
return characterLevel >= characterClass.LevelEntries.Count;
}
}
///
/// The cached list of spells for this level.
///
private List spells = null;
///
/// The cached list of spells for this level.
///
[ContentSerializerIgnore]
public List Spells
{
get
{
if ((spells == null) && (characterClass != null))
{
spells = characterClass.GetAllSpellsForLevel(characterLevel);
}
return spells;
}
}
#endregion
#region Experience
///
/// The amount of experience points that this character has.
///
private int experience;
///
/// The amount of experience points that this character has.
///
[ContentSerializerIgnore]
public int Experience
{
get { return experience; }
set
{
experience = value;
while (experience >= ExperienceForNextLevel)
{
if (IsMaximumCharacterLevel)
{
break;
}
experience -= ExperienceForNextLevel;
CharacterLevel++;
}
}
}
///
/// Returns the amount of experience necessary to reach the next character level.
///
public int ExperienceForNextLevel
{
get
{
int checkIndex = Math.Min(characterLevel,
characterClass.LevelEntries.Count) - 1;
return characterClass.LevelEntries[checkIndex].ExperiencePoints;
}
}
#endregion
#region Statistics
///
/// The base statistics of this character, from the character class and level.
///
private StatisticsValue baseStatistics = new StatisticsValue();
///
/// The base statistics of this character, from the character class and level.
///
[ContentSerializerIgnore]
public StatisticsValue BaseStatistics
{
get { return baseStatistics; }
set { baseStatistics = value; }
}
///
/// Reset the character's base statistics.
///
public void ResetBaseStatistics()
{
if (characterClass == null)
{
baseStatistics = new StatisticsValue();
}
else
{
baseStatistics = characterClass.GetStatisticsForLevel(characterLevel);
}
}
///
/// The total statistics for this character.
///
[ContentSerializerIgnore]
public StatisticsValue CharacterStatistics
{
get { return baseStatistics + equipmentBuffStatistics; }
}
#endregion
#region Equipment
///
/// The equipment currently equipped on this character.
///
private List equippedEquipment = new List();
///
/// The equipment currently equipped on this character.
///
[ContentSerializerIgnore]
public List EquippedEquipment
{
get { return equippedEquipment; }
}
///
/// The content names of the equipment initially equipped on the character.
///
private List initialEquipmentContentNames = new List();
///
/// The content names of the equipment initially equipped on the character.
///
public List InitialEquipmentContentNames
{
get { return initialEquipmentContentNames; }
set { initialEquipmentContentNames = value; }
}
///
/// Retrieve the currently equipped weapon.
///
/// There can only be one weapon equipped at the same time.
public Weapon GetEquippedWeapon()
{
return equippedEquipment.Find(delegate(Equipment equipment)
{ return equipment is Weapon; }) as Weapon;
}
///
/// Equip a new weapon.
///
/// True if the weapon was equipped.
public bool EquipWeapon(Weapon weapon, out Equipment oldEquipment)
{
// check the parameter
if (weapon == null)
{
throw new ArgumentNullException("weapon");
}
// check equipment restrictions
if (!weapon.CheckRestrictions(this))
{
oldEquipment = null;
return false;
}
// unequip any existing weapon
Weapon existingWeapon = GetEquippedWeapon();
if (existingWeapon != null)
{
oldEquipment = existingWeapon;
equippedEquipment.Remove(existingWeapon);
}
else
{
oldEquipment = null;
}
// add the weapon
equippedEquipment.Add(weapon);
// recalculate the statistic changes from equipment
RecalculateEquipmentStatistics();
RecalculateTotalTargetDamageRange();
return true;
}
///
/// Remove any equipped weapons.
///
public void UnequipWeapon()
{
equippedEquipment.RemoveAll(delegate(Equipment equipment)
{ return equipment is Weapon; });
RecalculateEquipmentStatistics();
}
///
/// Retrieve the armor equipped in the given slot.
///
public Armor GetEquippedArmor(Armor.ArmorSlot slot)
{
return equippedEquipment.Find(delegate(Equipment equipment)
{
Armor armor = equipment as Armor;
return ((armor != null) && (armor.Slot == slot));
}) as Armor;
}
///
/// Equip a new piece of armor.
///
/// True if the armor could be equipped.
public bool EquipArmor(Armor armor, out Equipment oldEquipment)
{
// check the parameter
if (armor == null)
{
throw new ArgumentNullException("armor");
}
// check equipment requirements
if (!armor.CheckRestrictions(this))
{
oldEquipment = null;
return false;
}
// remove any armor equipped in this slot
Armor equippedArmor = GetEquippedArmor(armor.Slot);
if (equippedArmor != null)
{
oldEquipment = equippedArmor;
equippedEquipment.Remove(equippedArmor);
}
else
{
oldEquipment = null;
}
// add the armor
equippedEquipment.Add(armor);
// recalcuate the total armor defense values
RecalculateTotalDefenseRanges();
// recalculate the statistics buffs from equipment
RecalculateEquipmentStatistics();
return true;
}
///
/// Unequip any armor in the given slot.
///
public void UnequipArmor(Armor.ArmorSlot slot)
{
equippedEquipment.RemoveAll(delegate(Equipment equipment)
{
Armor armor = equipment as Armor;
return ((armor != null) && (armor.Slot == slot));
});
RecalculateEquipmentStatistics();
RecalculateTotalDefenseRanges();
}
///
/// Equip a new piece of equipment.
///
/// True if the equipment could be equipped.
public virtual bool Equip(Equipment equipment)
{
Equipment oldEquipment;
return Equip(equipment, out oldEquipment);
}
///
/// Equip a new piece of equipment, specifying any equipment auto-unequipped.
///
/// True if the equipment could be equipped.
public virtual bool Equip(Equipment equipment, out Equipment oldEquipment)
{
if (equipment == null)
{
throw new ArgumentNullException("equipment");
}
if (equipment is Weapon)
{
return EquipWeapon(equipment as Weapon, out oldEquipment);
}
else if (equipment is Armor)
{
return EquipArmor(equipment as Armor, out oldEquipment);
}
else
{
oldEquipment = null;
}
return false;
}
///
/// Unequip a piece of equipment.
///
/// True if the equipment could be unequipped.
public virtual bool Unequip(Equipment equipment)
{
if (equipment == null)
{
throw new ArgumentNullException("equipment");
}
if (equippedEquipment.Remove(equipment))
{
RecalculateEquipmentStatistics();
RecalculateTotalTargetDamageRange();
RecalculateTotalDefenseRanges();
return true;
}
return false;
}
#endregion
#region Combined Equipment Values
///
/// The total statistics changes (buffs) from all equipped equipment.
///
private StatisticsValue equipmentBuffStatistics = new StatisticsValue();
///
/// The total statistics changes (buffs) from all equipped equipment.
///
[ContentSerializerIgnore]
public StatisticsValue EquipmentBuffStatistics
{
get { return equipmentBuffStatistics; }
set { equipmentBuffStatistics = value; }
}
///
/// Recalculate the character's equipment-buff statistics.
///
public void RecalculateEquipmentStatistics()
{
// start from scratch
equipmentBuffStatistics = new StatisticsValue();
// add the statistics for each piece of equipped equipment
foreach (Equipment equipment in equippedEquipment)
{
equipmentBuffStatistics += equipment.OwnerBuffStatistics;
}
}
///
/// The target damage range for this character, aggregated from all weapons.
///
private Int32Range targetDamageRange;
///
/// The health damage range for this character, aggregated from all weapons.
///
public Int32Range TargetDamageRange
{
get { return targetDamageRange; }
}
///
/// Recalculate the character's defense ranges from all of their armor.
///
public void RecalculateTotalTargetDamageRange()
{
// set the initial damage range to the physical offense statistic
targetDamageRange = new Int32Range();
// add each weapon's target damage range
foreach (Equipment equipment in equippedEquipment)
{
Weapon weapon = equipment as Weapon;
if (weapon != null)
{
targetDamageRange += weapon.TargetDamageRange;
}
}
}
///
/// The health defense range for this character, aggregated from all armor.
///
private Int32Range healthDefenseRange;
///
/// The health defense range for this character, aggregated from all armor.
///
public Int32Range HealthDefenseRange
{
get { return healthDefenseRange; }
}
///
/// The magic defense range for this character, aggregated from all armor.
///
private Int32Range magicDefenseRange;
///
/// The magic defense range for this character, aggregated from all armor.
///
public Int32Range MagicDefenseRange
{
get { return magicDefenseRange; }
}
///
/// Recalculate the character's defense ranges from all of their armor.
///
public void RecalculateTotalDefenseRanges()
{
// set the initial damage ranges based on character statistics
healthDefenseRange = new Int32Range();
magicDefenseRange = new Int32Range();
// add the defense ranges for each piece of equipped armor
foreach (Equipment equipment in equippedEquipment)
{
Armor armor = equipment as Armor;
if (armor != null)
{
healthDefenseRange += armor.OwnerHealthDefenseRange;
magicDefenseRange += armor.OwnerMagicDefenseRange;
}
}
}
#endregion
#region Inventory
///
/// The gear in this character's inventory (and not equipped).
///
private List> inventory = new List>();
///
/// The gear in this character's inventory (and not equipped).
///
public List> Inventory
{
get { return inventory; }
}
#endregion
#region Graphics Data
///
/// The animating sprite for the combat view of this character.
///
private AnimatingSprite combatSprite;
///
/// The animating sprite for the combat view of this character.
///
public AnimatingSprite CombatSprite
{
get { return combatSprite; }
set { combatSprite = value; }
}
///
/// Reset the animations for this character.
///
public override void ResetAnimation(bool isWalking)
{
base.ResetAnimation(isWalking);
if (combatSprite != null)
{
combatSprite.PlayAnimation("Idle");
}
}
#endregion
#region Static Animation Data
///
/// The default animation interval for the combat map sprite.
///
private int combatAnimationInterval = 100;
///
/// The default animation interval for the combat map sprite.
///
[ContentSerializer(Optional=true)]
public int CombatAnimationInterval
{
get { return combatAnimationInterval; }
set { combatAnimationInterval = value; }
}
///
/// Add the standard character walk animations to this character.
///
private void AddStandardCharacterCombatAnimations()
{
if (combatSprite != null)
{
combatSprite.AddAnimation(new Animation("Idle", 37, 42,
CombatAnimationInterval, true));
combatSprite.AddAnimation(new Animation("Walk", 25, 30,
CombatAnimationInterval, true));
combatSprite.AddAnimation(new Animation("Attack", 1, 6,
CombatAnimationInterval, false));
combatSprite.AddAnimation(new Animation("SpellCast", 31, 36,
CombatAnimationInterval, false));
combatSprite.AddAnimation(new Animation("Defend", 13, 18,
CombatAnimationInterval, false));
combatSprite.AddAnimation(new Animation("Dodge", 13, 18,
CombatAnimationInterval, false));
combatSprite.AddAnimation(new Animation("Hit", 19, 24,
CombatAnimationInterval, false));
combatSprite.AddAnimation(new Animation("Die", 7, 12,
CombatAnimationInterval, false));
}
}
#endregion
#region Content Type Reader
///
/// Reads a FightingCharacter object from the content pipeline.
///
public class FightingCharacterReader : ContentTypeReader
{
///
/// Reads a FightingCharacter object from the content pipeline.
///
protected override FightingCharacter Read(ContentReader input,
FightingCharacter existingInstance)
{
FightingCharacter fightingCharacter = existingInstance;
if (fightingCharacter == null)
{
throw new ArgumentNullException("existingInstance");
}
input.ReadRawObject(fightingCharacter as Character);
fightingCharacter.CharacterClassContentName = input.ReadString();
fightingCharacter.CharacterLevel = input.ReadInt32();
fightingCharacter.InitialEquipmentContentNames.AddRange(
input.ReadObject>());
fightingCharacter.Inventory.AddRange(
input.ReadObject>>());
fightingCharacter.CombatAnimationInterval = input.ReadInt32();
fightingCharacter.CombatSprite = input.ReadObject();
fightingCharacter.AddStandardCharacterCombatAnimations();
fightingCharacter.ResetAnimation(false);
// load the character class
fightingCharacter.CharacterClass =
input.ContentManager.Load(
System.IO.Path.Combine("CharacterClasses",
fightingCharacter.CharacterClassContentName));
// populate the equipment list
foreach (string gearName in
fightingCharacter.InitialEquipmentContentNames)
{
fightingCharacter.Equip(input.ContentManager.Load(
System.IO.Path.Combine("Gear", gearName)));
}
fightingCharacter.RecalculateEquipmentStatistics();
fightingCharacter.RecalculateTotalTargetDamageRange();
fightingCharacter.RecalculateTotalDefenseRanges();
// populate the inventory based on the content names
foreach (ContentEntry inventoryEntry in
fightingCharacter.Inventory)
{
inventoryEntry.Content = input.ContentManager.Load(
System.IO.Path.Combine("Gear", inventoryEntry.ContentName));
}
return fightingCharacter;
}
}
#endregion
}
}