#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 } }