changeset 252:a54da5a8b5bb

Re #268: Restructure stats for re-use * Add "Member Type" class * Add member type setting and getting to Race * Load member types from XML files * Make unit type pull stat line from stats or first member type, or fall back to a blank stat line * Change Stats object to initialise blank values * Change schema * Make stats optional * Add member type list to race * Add optional member type references to units
author IBBoard <dev@ibboard.co.uk>
date Sun, 25 Apr 2010 15:07:08 +0000
parents 22509bd03ca2
children 79943fcf4de2
files IBBoard.WarFoundry.API.csproj api/Factories/Xml/WarFoundryXmlRaceFactory.cs api/Objects/Race.cs api/Objects/Stats.cs api/Objects/UnitMemberType.cs api/Objects/UnitType.cs schemas/race.xsd
diffstat 7 files changed, 197 insertions(+), 13 deletions(-) [+]
line wrap: on
line diff
--- a/IBBoard.WarFoundry.API.csproj	Fri Apr 23 20:00:06 2010 +0000
+++ b/IBBoard.WarFoundry.API.csproj	Sun Apr 25 15:07:08 2010 +0000
@@ -156,6 +156,7 @@
     </None>
     <Compile Include="api\AbstractWarFoundryLoader.cs" />
     <Compile Include="api\DefaultWarFoundryLoader.cs" />
+    <Compile Include="api\Objects\UnitMemberType.cs" />
   </ItemGroup>
   <ItemGroup>
     <Content Include="libs\ICSharpCode.SharpZipLib.dll" />
--- a/api/Factories/Xml/WarFoundryXmlRaceFactory.cs	Fri Apr 23 20:00:06 2010 +0000
+++ b/api/Factories/Xml/WarFoundryXmlRaceFactory.cs	Sun Apr 25 15:07:08 2010 +0000
@@ -81,6 +81,11 @@
 			{
 				CreateAbilityFromElement(node, race);
 			}
+							
+			foreach (XmlElement node  in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:memberTypes/race:memberType"))
+			{
+				CreateMemberTypeFromElement(node, race);
+			}
 			
 			foreach (XmlElement node in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:units/race:unit"))
 			{
@@ -165,8 +170,21 @@
 			
 			type.MainCategory = cat;
 			XmlElement statsElement = WarFoundryXmlFactoryUtils.SelectSingleElement(elem, "race:stats");
-			Stats unitStats = ParseUnitStats(statsElement, type.GameSystem);
-			type.SetUnitStats(unitStats);
+			
+			if (statsElement!=null)
+			{
+				Stats unitStats = ParseUnitStats(statsElement, type.GameSystem);
+				type.SetUnitStats(unitStats);
+			}
+			
+			XmlNodeList unitMemberReferences = WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitMembers/race:unitMember");
+			
+			foreach (XmlElement unitMemberRef in unitMemberReferences)
+			{
+				string typeID = unitMemberRef.GetAttribute("typeID");
+				UnitMemberType unitMemberType = type.Race.GetUnitMemberType(typeID);
+				type.AddUnitMemberType(unitMemberType);
+			}
 		}
 
 		private void LoadEquipmentSlotsForUnitType(XmlElement elem, UnitType type)
@@ -388,6 +406,11 @@
 		
 		private Stats ParseUnitStats(XmlElement elem, GameSystem system)
 		{
+			if (elem == null)
+			{
+				return null;
+			}
+			
 			String statsID = elem.GetAttribute("statSet");
 			SystemStats statsSet;
 			
@@ -467,6 +490,13 @@
 			ability.Description = (node == null) ? "" : node.InnerText;
 			race.AddAbility(ability);
 			return ability;
+		}		
+
+		private void CreateMemberTypeFromElement(XmlElement elem, Race race)
+		{
+			Stats stats = ParseUnitStats(WarFoundryXmlFactoryUtils.SelectSingleElement(elem, "race:stats"), race.GameSystem);
+			UnitMemberType unitMemberType = new UnitMemberType(elem.GetAttribute("id"), elem.GetAttribute("name"), stats);
+			race.AddUnitMemberType(unitMemberType);
 		}
 	}
 }
--- a/api/Objects/Race.cs	Fri Apr 23 20:00:06 2010 +0000
+++ b/api/Objects/Race.cs	Sun Apr 25 15:07:08 2010 +0000
@@ -22,6 +22,7 @@
 		private Dictionary<string, EquipmentItem> equipment = new Dictionary<string,EquipmentItem>();
 		private Dictionary<string, Ability> abilities = new Dictionary<string,Ability>();
 		private Dictionary<string, Category> categories = new Dictionary<string,Category>();
+		private Dictionary<string, UnitMemberType> memberTypes = new Dictionary<string, UnitMemberType>();
 		
 		public Race(string raceID, string raceName, GameSystem gameSystem, IWarFoundryFactory creatingFactory) : this(raceID, "", raceName, gameSystem, creatingFactory)
 		{
@@ -265,5 +266,34 @@
 			get { return abilities; }
 			set { abilities = value; }
 		}
+		
+		public void AddUnitMemberType(UnitMemberType memberType)
+		{
+			memberTypes[memberType.ID] = memberType;
+		}
+
+		/// <summary>
+		/// Gets a unit member type by its ID.
+		/// </summary>
+		/// <param name="id">
+		/// The ID of the unit member type to get
+		/// </param>
+		/// <returns>
+		/// The <code>UnitMemberType</code> with the specified ID, or null if one doesn't exist. 
+		/// </returns>
+		public UnitMemberType GetUnitMemberType(string id)
+		{
+			EnsureFullyLoaded();
+			return DictionaryUtils.GetValue(memberTypes, id);
+		}
+
+		public UnitMemberType[] UnitMemberTypes
+		{
+			get 
+			{ 
+				EnsureFullyLoaded();
+				return DictionaryUtils.ToArray(memberTypes);
+			}
+		}
 	}
 }
--- a/api/Objects/Stats.cs	Fri Apr 23 20:00:06 2010 +0000
+++ b/api/Objects/Stats.cs	Sun Apr 25 15:07:08 2010 +0000
@@ -20,10 +20,11 @@
 			sysStats = systemStats;
 			int statCount = sysStats.SlotCount;
 			stats = new List<Stat>(statCount);
+			int i = 0;
 
-			for (int i = 0; i < statCount; i++)
+			foreach (StatSlot slot in sysStats.StatSlots)
 			{
-				stats.Add(null);
+				stats.Add(new Stat(slot, ""));
 			}
 		}
 		
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/api/Objects/UnitMemberType.cs	Sun Apr 25 15:07:08 2010 +0000
@@ -0,0 +1,45 @@
+//  This file (UnitMember.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2010 IBBoard
+// 
+//  // The file and the library/program it is in are licensed and distributed, without warranty, under the GNU Affero GPL license, either version 3 of the License or (at your option) any later version. Please see COPYING for more information and the full license.
+
+using System;
+
+namespace IBBoard.WarFoundry.API.Objects
+{
+	/// <summary>
+	/// A container object for representations of different unit member types, such as "Infantry", "Elite Infantry" and "Commoner".
+	/// The idea of the UnitMemberType is to define not just the <see>UnitType</see>s, but also the types of type, as Archer and Swordsmen
+	/// are often just differently equipped versions of the same member type (Infantry).
+	/// </summary>
+	public class UnitMemberType : WarFoundryObject
+	{
+		private Stats stats;
+		
+		public UnitMemberType(string typeID, string typeName, Stats typeStats) : base(typeID, typeName)
+		{
+			stats = typeStats;
+		}
+
+		/// <value>
+		/// The set of <see cref="Stat"/>s for the unit member type in a format that is valid for the game system.
+		/// </value>
+		public Stat[] StatsArray
+		{
+			get 
+			{				
+				return stats.StatsArray;
+			}
+		}
+		
+		public Stat[] StatsArrayWithName
+		{
+			get
+			{
+				Stat[] extendedStats = new Stat[stats.StatCount+1];
+				extendedStats[0] = new Stat(new StatSlot("name"), Name);
+				stats.StatsArray.CopyTo(extendedStats, 1);
+				return extendedStats;
+			}
+		}
+	}
+}
--- a/api/Objects/UnitType.cs	Fri Apr 23 20:00:06 2010 +0000
+++ b/api/Objects/UnitType.cs	Sun Apr 25 15:07:08 2010 +0000
@@ -33,7 +33,7 @@
 		private List<UnitType> containedTypes = new List<UnitType>();
 		private Dictionary<string, string> extraData = new Dictionary<string, string>();
 		private Dictionary<string, AbstractLimit> slotLimits = new Dictionary<string, AbstractLimit>();
-
+		private Dictionary<string, UnitMemberType> unitMemberTypes = new Dictionary<string, UnitMemberType>();
 
 		public UnitType(string id, string typeName, Race parentRace) : base(id, typeName)
 		{
@@ -199,9 +199,27 @@
 		/// </value>
 		public Stat[] UnitStatsArray
 		{
-			get 
+			get
 			{
-				return stats.StatsArray;
+				Stat[] statsArray = null;
+				
+				if (stats != null)
+				{
+					statsArray = stats.StatsArray;
+				}
+				else if (unitMemberTypes.Count > 0)
+				{
+					UnitMemberType[] memberTypeArray = DictionaryUtils.ToArray(unitMemberTypes);
+					statsArray = memberTypeArray[0].StatsArray;
+				}
+				else
+				{
+					SystemStats systemStats = GameSystem.StandardSystemStats;
+					Stats tempStats = new Stats(systemStats);				
+					statsArray = tempStats.StatsArray;
+				}
+				
+				return statsArray;
 			}
 		}
 
@@ -211,13 +229,36 @@
 		public Stat[] UnitStatsArrayWithName
 		{
 			get
-			{
-				Stat[] extendedStats = new Stat[stats.StatCount+1];
-				extendedStats[0] = new Stat(new StatSlot("name"), Name);
-				stats.StatsArray.CopyTo(extendedStats, 1);
-				return extendedStats;
+			{				
+				Stat[] statsArray = null;
+				
+				if (stats != null)
+				{
+					statsArray = ExtendStatsArrayWithName(stats.StatsArray);
+				}
+				else if (unitMemberTypes.Count > 0)
+				{
+					UnitMemberType[] memberTypeArray = DictionaryUtils.ToArray(unitMemberTypes);
+					statsArray = memberTypeArray[0].StatsArrayWithName;
+				}
+				else
+				{
+					SystemStats systemStats = GameSystem.StandardSystemStats;
+					Stats tempStats = new Stats(systemStats);				
+					statsArray = ExtendStatsArrayWithName(tempStats.StatsArray);
+				}
+				
+				return statsArray;
 			}
 		}
+		
+		public Stat[] ExtendStatsArrayWithName(Stat[] statsArray)
+		{
+			Stat[] extendedStats = new Stat[statsArray.Length+1];
+			extendedStats[0] = new Stat(new StatSlot("name"), Name);
+			statsArray.CopyTo(extendedStats, 1);
+			return extendedStats;
+		}
 
 		public void SetUnitStats(Stats newStats)
 		{
@@ -482,5 +523,10 @@
 
 			return slotLimit;
 		}
+
+		public void AddUnitMemberType(UnitMemberType unitMemberType)
+		{
+			unitMemberTypes.Add(unitMemberType.ID, unitMemberType);
+		}
 	}
 }
\ No newline at end of file
--- a/schemas/race.xsd	Fri Apr 23 20:00:06 2010 +0000
+++ b/schemas/race.xsd	Sun Apr 25 15:07:08 2010 +0000
@@ -32,6 +32,22 @@
 					<xs:anyAttribute processContents="lax"/>
 				</xs:complexType>
 			</xs:element>
+			<xs:element name="memberTypes" minOccurs="0">
+				<xs:complexType>
+					<xs:sequence>
+						<xs:element name="memberType" maxOccurs="unbounded">
+							<xs:complexType>
+								<xs:sequence>
+									<xs:element name="stats" type="stats" minOccurs="0"/>
+								</xs:sequence>
+								<xs:attribute name="id" type="xs:ID" use="required"/>
+								<xs:attribute name="name" type="xs:string" use="required"/>
+								<xs:anyAttribute processContents="lax"/>
+							</xs:complexType>
+						</xs:element>						
+					</xs:sequence>
+				</xs:complexType>
+			</xs:element>
 			<xs:any minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
 		</xs:sequence>
 		<xs:attribute name="id" type="xs:ID" />
@@ -45,7 +61,7 @@
 <!--Reusable complex types -->
 <xs:complexType name="unit">
 	<xs:sequence>
-		<xs:element name="stats" type="stats"/>
+		<xs:element name="stats" type="stats" minOccurs="0"/>
 		<xs:element name="equipmentSlots" minOccurs="0">
 			<xs:complexType>
 				<xs:sequence>
@@ -163,6 +179,21 @@
 				<xs:anyAttribute processContents="lax"/>
 			</xs:complexType>
 		</xs:element>
+		<xs:element name="unitMembers" minOccurs="0">
+			<xs:complexType>
+				<xs:sequence>
+					<xs:element name="unitMember" maxOccurs="unbounded">
+						<xs:complexType>
+							<xs:sequence>
+								<xs:any minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
+							</xs:sequence>
+							<xs:attribute name="typeID" type="xs:IDREF" use="required"/>
+							<xs:anyAttribute processContents="lax"/>						
+						</xs:complexType>
+					</xs:element>
+				</xs:sequence>
+			</xs:complexType>
+		</xs:element>
 		<xs:element name="notes" type="xs:string" minOccurs="0" />
 		<xs:any minOccurs="0" maxOccurs="unbounded" processContents="lax"/>
 	</xs:sequence>