changeset 337:3c4a6403a88c

* Fix capitalisation so that new files are in the namespace no-open-ticket
author IBBoard <dev@ibboard.co.uk>
date Sun, 03 Apr 2011 18:50:32 +0000
parents 3631c1493c7f
children 4497ebce9a57
files API/AbstractWarFoundryLoader.cs API/Commands/AbstractReplaceUnitEquipmentCommand.cs API/Commands/AbstractSetUnitEquipmentAmountCommand.cs API/Commands/CreateAndAddUnitCommand.cs API/Commands/RemoveUnitCommand.cs API/Commands/ReplaceUnitEquipmentWithNumericAmountItemCommand.cs API/Commands/ReplaceUnitEquipmentWithRatioAmountItemCommand.cs API/Commands/SetNameCommand.cs API/Commands/SetUnitEquipmentNumericAmountCommand.cs API/Commands/SetUnitEquipmentRatioAmountCommand.cs API/Commands/SetUnitSizeCommand.cs API/DefaultWarFoundryLoader.cs API/Delegates.cs API/Exporters/IWarFoundryExporter.cs API/Exporters/WarFoundryHtmlExporter.cs API/Factories/AbstractNativeWarFoundryFactory.cs API/Factories/AbstractNonNativeFileExtensionWarFoundryFactory.cs API/Factories/AbstractNonNativeWarFoundryFactory.cs API/Factories/AbstractWarFoundryFactory.cs API/Factories/DummyWarFoundryFactory.cs API/Factories/INativeWarFoundryFactory.cs API/Factories/INonNativeWarFoundryFactory.cs API/Factories/IWarFoundryFactory.cs API/Factories/RequiredDataMissingException.cs API/Factories/Xml/AbstractStagedLoadedSubFactory.cs API/Factories/Xml/WarFoundryXmlArmyFactory.cs API/Factories/Xml/WarFoundryXmlArmyParser.cs API/Factories/Xml/WarFoundryXmlElementName.cs API/Factories/Xml/WarFoundryXmlFactory.cs API/Factories/Xml/WarFoundryXmlFactoryUtils.cs API/Factories/Xml/WarFoundryXmlGameSystemFactory.cs API/Factories/Xml/WarFoundryXmlLimitParser.cs API/Factories/Xml/WarFoundryXmlRaceFactory.cs API/Factories/Xml/Zip/StringZipEntrySource.cs API/FileLoadFailure.cs API/Objects/Ability.cs API/Objects/AbstractUnitEquipmentItemSelection.cs API/Objects/Army.cs API/Objects/ArmyCategory.cs API/Objects/Category.cs API/Objects/CompositeEquipmentItem.cs API/Objects/DuplicateItemException.cs API/Objects/EquipmentItem.cs API/Objects/GameSystem.cs API/Objects/ICostedWarFoundryObject.cs API/Objects/IWarFoundryNativeSourceObject.cs API/Objects/IWarFoundryObject.cs API/Objects/IWarFoundryStagedLoadObject.cs API/Objects/InvalidContainershipException.cs API/Objects/Race.cs API/Objects/Requirement/RequiresAtLeastNUnitsRequirement.cs API/Objects/Requirement/RequiresNoMoreThanNOfUnitTypeRequirement.cs API/Objects/Requirement/UnitCountRequirementData.cs API/Objects/Requirement/UnitRequiresAtLeastNUnitsRequirement.cs API/Objects/Stat.cs API/Objects/StatSlot.cs API/Objects/Stats.cs API/Objects/SystemStats.cs API/Objects/Unit.cs API/Objects/UnitEquipmentItem.cs API/Objects/UnitEquipmentNumericSelection.cs API/Objects/UnitEquipmentRatioSelection.cs API/Objects/UnitMemberType.cs API/Objects/UnitType.cs API/Objects/WarFoundryLoadedObject.cs API/Objects/WarFoundryObject.cs API/Objects/WarFoundryStagedLoadingObject.cs API/Requirements/AbstractArmyRequirement.cs API/Requirements/AbstractFailedRequirement.cs API/Requirements/AbstractRequirement.cs API/Requirements/AbstractUnitRequirement.cs API/Requirements/Delegates.cs API/Requirements/FailedRequirement.cs API/Requirements/FailedUnitRequirement.cs API/Requirements/RequirementAND.cs API/Requirements/RequirementOR.cs API/Requirements/UnitExcludesRequirement.cs API/Requirements/UnitRequirement.cs API/Requirements/UnitRequirementItem.cs API/Requirements/UnitRequirementMaxNumber.cs API/Requirements/UnitRequirementMinNumber.cs API/Requirements/UnitRequiresAtLeastRequirement.cs API/Savers/IWarFoundryFileSaver.cs API/Savers/WarFoundrySaver.cs API/Savers/Xml/WarFoundryXmlArmySaver.cs API/Savers/Xml/WarFoundryXmlFileSaver.cs API/Savers/Xml/WarFoundryXmlGameSystemSaver.cs API/Util/UnitEquipmentUtil.cs API/WarFoundryCore.cs API/WarFoundryLoader.cs IBBoard.WarFoundry.API.csproj api/AbstractWarFoundryLoader.cs api/Commands/AbstractReplaceUnitEquipmentCommand.cs api/Commands/AbstractSetUnitEquipmentAmountCommand.cs api/Commands/CreateAndAddUnitCommand.cs api/Commands/RemoveUnitCommand.cs api/Commands/ReplaceUnitEquipmentWithNumericAmountItemCommand.cs api/Commands/ReplaceUnitEquipmentWithRatioAmountItemCommand.cs api/Commands/SetNameCommand.cs api/Commands/SetUnitEquipmentNumericAmountCommand.cs api/Commands/SetUnitEquipmentRatioAmountCommand.cs api/Commands/SetUnitSizeCommand.cs api/DefaultWarFoundryLoader.cs api/Delegates.cs api/Exporters/IWarFoundryExporter.cs api/Exporters/WarFoundryHtmlExporter.cs api/Factories/AbstractNativeWarFoundryFactory.cs api/Factories/AbstractNonNativeFileExtensionWarFoundryFactory.cs api/Factories/AbstractNonNativeWarFoundryFactory.cs api/Factories/AbstractWarFoundryFactory.cs api/Factories/DummyWarFoundryFactory.cs api/Factories/INativeWarFoundryFactory.cs api/Factories/INonNativeWarFoundryFactory.cs api/Factories/IWarFoundryFactory.cs api/Factories/RequiredDataMissingException.cs api/Factories/Xml/AbstractStagedLoadedSubFactory.cs api/Factories/Xml/WarFoundryXmlArmyFactory.cs api/Factories/Xml/WarFoundryXmlArmyParser.cs api/Factories/Xml/WarFoundryXmlElementName.cs api/Factories/Xml/WarFoundryXmlFactory.cs api/Factories/Xml/WarFoundryXmlFactoryUtils.cs api/Factories/Xml/WarFoundryXmlGameSystemFactory.cs api/Factories/Xml/WarFoundryXmlLimitParser.cs api/Factories/Xml/WarFoundryXmlRaceFactory.cs api/Factories/Xml/Zip/StringZipEntrySource.cs api/FileLoadFailure.cs api/Objects/Ability.cs api/Objects/AbstractUnitEquipmentItemSelection.cs api/Objects/Army.cs api/Objects/ArmyCategory.cs api/Objects/Category.cs api/Objects/CompositeEquipmentItem.cs api/Objects/DuplicateItemException.cs api/Objects/EquipmentItem.cs api/Objects/GameSystem.cs api/Objects/ICostedWarFoundryObject.cs api/Objects/IWarFoundryNativeSourceObject.cs api/Objects/IWarFoundryObject.cs api/Objects/IWarFoundryStagedLoadObject.cs api/Objects/InvalidContainershipException.cs api/Objects/Race.cs api/Objects/Requirement/RequiresAtLeastNUnitsRequirement.cs api/Objects/Requirement/RequiresNoMoreThanNOfUnitTypeRequirement.cs api/Objects/Requirement/UnitCountRequirementData.cs api/Objects/Requirement/UnitRequiresAtLeastNUnitsRequirement.cs api/Objects/Stat.cs api/Objects/StatSlot.cs api/Objects/Stats.cs api/Objects/SystemStats.cs api/Objects/Unit.cs api/Objects/UnitEquipmentItem.cs api/Objects/UnitEquipmentNumericSelection.cs api/Objects/UnitEquipmentRatioSelection.cs api/Objects/UnitMemberType.cs api/Objects/UnitType.cs api/Objects/WarFoundryLoadedObject.cs api/Objects/WarFoundryObject.cs api/Objects/WarFoundryStagedLoadingObject.cs api/Requirements/AbstractArmyRequirement.cs api/Requirements/AbstractFailedRequirement.cs api/Requirements/AbstractRequirement.cs api/Requirements/AbstractUnitRequirement.cs api/Requirements/Delegates.cs api/Requirements/FailedRequirement.cs api/Requirements/FailedUnitRequirement.cs api/Requirements/RequirementAND.cs api/Requirements/RequirementOR.cs api/Requirements/UnitExcludesRequirement.cs api/Requirements/UnitRequirement.cs api/Requirements/UnitRequirementItem.cs api/Requirements/UnitRequirementMaxNumber.cs api/Requirements/UnitRequirementMinNumber.cs api/Requirements/UnitRequiresAtLeastRequirement.cs api/Savers/IWarFoundryFileSaver.cs api/Savers/WarFoundrySaver.cs api/Savers/Xml/WarFoundryXmlArmySaver.cs api/Savers/Xml/WarFoundryXmlFileSaver.cs api/Savers/Xml/WarFoundryXmlGameSystemSaver.cs api/Util/UnitEquipmentUtil.cs api/WarFoundryCore.cs api/WarFoundryLoader.cs
diffstat 181 files changed, 9436 insertions(+), 9436 deletions(-) [+]
line diff
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/API/AbstractWarFoundryLoader.cs	Sun Apr 03 18:50:32 2011 +0000
     1.3 @@ -0,0 +1,695 @@
     1.4 +// This file (AbstractWarFoundryLoader.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
     1.5 +// 
     1.6 +// 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.
     1.7 +
     1.8 +using System;
     1.9 +using System.Collections.Generic;
    1.10 +using System.IO;
    1.11 +using IBBoard.Collections;
    1.12 +using IBBoard.IO;
    1.13 +using IBBoard.Logging;
    1.14 +using IBBoard.WarFoundry.API.Factories;
    1.15 +using IBBoard.WarFoundry.API.Objects;
    1.16 +
    1.17 +namespace IBBoard.WarFoundry.API
    1.18 +{
    1.19 +	/// <summary>
    1.20 +	/// The base abstract implementation of a WarFoundry file loader
    1.21 +	/// </summary>
    1.22 +	public abstract class AbstractWarFoundryLoader
    1.23 +	{
    1.24 +		private ICollection<DirectoryInfo> directories;
    1.25 +		private ICollection<INativeWarFoundryFactory> factories;
    1.26 +		private ICollection<INonNativeWarFoundryFactory> nonNativeFactories;
    1.27 +		private Dictionary<IWarFoundryFactory, SimpleSet<IWarFoundryObject>> loadedObjects;
    1.28 +
    1.29 +		public delegate void FileLoadingCompleteDelegate(List<FileLoadFailure> failures);
    1.30 +
    1.31 +		public event MethodInvoker FileLoadingStarted;
    1.32 +
    1.33 +		public event FileLoadingCompleteDelegate FileLoadingFinished;
    1.34 +
    1.35 +		protected AbstractWarFoundryLoader()
    1.36 +		{
    1.37 +			directories = new List<DirectoryInfo>();
    1.38 +			factories = new List<INativeWarFoundryFactory>();
    1.39 +			nonNativeFactories = new List<INonNativeWarFoundryFactory>();
    1.40 +			loadedObjects = new Dictionary<IWarFoundryFactory,SimpleSet<IWarFoundryObject>>();
    1.41 +		}
    1.42 +		
    1.43 +		/// <summary>
    1.44 +		/// Adds a directory to the collection of directories that will be searched for WarFoundry data files.
    1.45 +		/// </summary>
    1.46 +		/// <param name="directory">
    1.47 +		/// The <see cref="DirectoryInfo"/> to add to the list for searching for data files
    1.48 +		/// </param>
    1.49 +		public void AddLoadDirectory(DirectoryInfo directory)
    1.50 +		{
    1.51 +			if (!directories.Contains(directory))
    1.52 +			{
    1.53 +				directories.Add(directory);
    1.54 +			}
    1.55 +		}
    1.56 +		
    1.57 +		/// <summary>
    1.58 +		/// Removes a directory from the collection of directories that will be searched for WarFoundry data files.
    1.59 +		/// </summary>
    1.60 +		/// <param name="directory">
    1.61 +		/// A <see cref="DirectoryInfo"/>
    1.62 +		/// </param>
    1.63 +		public void RemoveLoadDirectory(DirectoryInfo directory)
    1.64 +		{
    1.65 +			if (directories.Contains(directory))
    1.66 +			{
    1.67 +				directories.Remove(directory);
    1.68 +			}
    1.69 +		}
    1.70 +		
    1.71 +		/// <summary>
    1.72 +		/// Registers a <see cref="INativeWarFoundryFactory"/> as a factory that can parse native data files.
    1.73 +		/// </summary>
    1.74 +		/// <param name="factory">
    1.75 +		/// The <see cref="INativeWarFoundryFactory"/> to register to parse native data files.
    1.76 +		/// </param>
    1.77 +		public virtual void RegisterFactory(INativeWarFoundryFactory factory)
    1.78 +		{
    1.79 +			if (!factories.Contains(factory))
    1.80 +			{
    1.81 +				factories.Add(factory);
    1.82 +			}
    1.83 +		}
    1.84 +		
    1.85 +		/// <summary>
    1.86 +		/// Unregisters a <see cref="INativeWarFoundryFactory"/> so that it will no longer be used to try to parse native data files.
    1.87 +		/// </summary>
    1.88 +		/// <param name="factory">
    1.89 +		/// The <see cref="INativeWarFoundryFactory"/> to remove from the collection of factories that are used to try to parse native data files.
    1.90 +		/// </param>
    1.91 +		public virtual void UnregisterFactory(INativeWarFoundryFactory factory)
    1.92 +		{
    1.93 +			if (factories.Contains(factory))
    1.94 +			{
    1.95 +				factories.Remove(factory);
    1.96 +			}
    1.97 +		}
    1.98 +		
    1.99 +		/// <summary>
   1.100 +		/// Registers a <see cref="INonNativeWarFoundryFactory"/> so that it will be used to try to parse non-native data files from other applications.
   1.101 +		/// </summary>
   1.102 +		/// <param name="factory">
   1.103 +		/// The <see cref="INonNativeWarFoundryFactory"/> to register to parse non-native data files.
   1.104 +		/// </param>
   1.105 +		public virtual void RegisterNonNativeFactory(INonNativeWarFoundryFactory factory)
   1.106 +		{
   1.107 +			if (!nonNativeFactories.Contains(factory))
   1.108 +			{
   1.109 +				nonNativeFactories.Add(factory);
   1.110 +			}
   1.111 +		}
   1.112 +		
   1.113 +		/// <summary>
   1.114 +		/// Unregisters a <see cref="INonNativeWarFoundryFactory"/> so that it will no longer be used to try to parse non-native data files from other applications.
   1.115 +		/// </summary>
   1.116 +		/// <param name="factory">
   1.117 +		/// The <see cref="INonNativeWarFoundryFactory"/> to remove from the collection of factories that are used to try to parse non-native data files.
   1.118 +		/// </param>
   1.119 +		public virtual void UnregisterNonNativeFactory(INonNativeWarFoundryFactory factory)
   1.120 +		{
   1.121 +			if (nonNativeFactories.Contains(factory))
   1.122 +			{
   1.123 +				nonNativeFactories.Remove(factory);
   1.124 +			}
   1.125 +		}
   1.126 +		
   1.127 +		/// <summary>
   1.128 +		/// Loads all of the data files in the registered directories.
   1.129 +		/// </summary>
   1.130 +		/// <returns>
   1.131 +		/// A <see cref="List"/> of <see cref="FileLoadFailure"/> for files that failed to load
   1.132 +		/// </returns>
   1.133 +		public List<FileLoadFailure> LoadFiles()
   1.134 +		{
   1.135 +			PrepareForFileLoad();
   1.136 +			Dictionary<FileInfo, IWarFoundryFactory> loadableRaces = new Dictionary<FileInfo, IWarFoundryFactory>();
   1.137 +			Dictionary<FileInfo, IWarFoundryFactory> loadableGameSystems = new Dictionary<FileInfo, IWarFoundryFactory>();
   1.138 +			List<FileLoadFailure> failedLoads = FillLoadableFiles(loadableRaces, loadableGameSystems);
   1.139 +			failedLoads.AddRange(LoadGameSystems(loadableGameSystems));
   1.140 +			failedLoads.AddRange(LoadRaces(loadableRaces));
   1.141 +			OnFileLoadingFinished(failedLoads);
   1.142 +			FinishFileLoad();
   1.143 +			return failedLoads;
   1.144 +		}
   1.145 +
   1.146 +		private void OnFileLoadingFinished(List<FileLoadFailure> failures)
   1.147 +		{
   1.148 +			if (FileLoadingFinished != null)
   1.149 +			{
   1.150 +				FileLoadingFinished(failures);
   1.151 +			}
   1.152 +		}
   1.153 +
   1.154 +		protected virtual void PrepareForFileLoad()
   1.155 +		{
   1.156 +			//Do nothing special
   1.157 +		}
   1.158 +
   1.159 +		protected virtual void FinishFileLoad()
   1.160 +		{
   1.161 +			//Do nothing special
   1.162 +		}
   1.163 +
   1.164 +		private List<FileLoadFailure> FillLoadableFiles(Dictionary<FileInfo, IWarFoundryFactory> loadableRaces, Dictionary<FileInfo, IWarFoundryFactory> loadableGameSystems)
   1.165 +		{			
   1.166 +			List<FileLoadFailure> fails = new List<FileLoadFailure>();
   1.167 +			
   1.168 +			foreach (DirectoryInfo directory in directories)
   1.169 +			{
   1.170 +				directory.Refresh();
   1.171 +
   1.172 +				if (directory.Exists)
   1.173 +				{
   1.174 +					List<FileLoadFailure> directoryFails = FillLoadableFilesForDirectory(loadableRaces, loadableGameSystems, directory);
   1.175 +					fails.AddRange(directoryFails);
   1.176 +				}
   1.177 +				else
   1.178 +				{
   1.179 +					LogNotifier.WarnFormat(GetType(), "Load for {0} failed because directory didn't exist", directory.FullName);
   1.180 +				}
   1.181 +			}
   1.182 +			
   1.183 +			return fails;
   1.184 +		}
   1.185 +
   1.186 +		private List<FileLoadFailure> FillLoadableFilesForDirectory(Dictionary<FileInfo, IWarFoundryFactory> loadableRaces, Dictionary<FileInfo, IWarFoundryFactory> loadableGameSystems, DirectoryInfo directory)
   1.187 +		{
   1.188 +			List<FileLoadFailure> fails = new List<FileLoadFailure>();
   1.189 +			LogNotifier.Debug(GetType(), "Load from " + directory.FullName);
   1.190 +		
   1.191 +			foreach (FileInfo file in directory.GetFiles())
   1.192 +			{
   1.193 +				IWarFoundryFactory factory = GetGameSystemLoadingFactoryForFile(file);
   1.194 +				
   1.195 +				if (factory != null)
   1.196 +				{
   1.197 +					loadableGameSystems.Add(file, factory);
   1.198 +				}
   1.199 +				else
   1.200 +				{
   1.201 +					factory = GetRaceLoadingFactoryForFile(file);
   1.202 +	
   1.203 +					if (factory != null)
   1.204 +					{
   1.205 +						loadableRaces.Add(file, factory);
   1.206 +					}
   1.207 +					else
   1.208 +					{
   1.209 +						FileLoadFailure failure = new FileLoadFailure(file, "File not handled as a Race or Game System definition: {0}", "FileNotHandled");
   1.210 +						fails.Add(failure);
   1.211 +						LogNotifier.Info(GetType(), failure.Message);
   1.212 +					}
   1.213 +				}
   1.214 +			}
   1.215 +			
   1.216 +			foreach (DirectoryInfo subdir in directory.GetDirectories())
   1.217 +			{
   1.218 +				fails.AddRange(FillLoadableFilesForDirectory(loadableRaces, loadableGameSystems, subdir));
   1.219 +			}
   1.220 +
   1.221 +			return fails;
   1.222 +		}
   1.223 +
   1.224 +		private IWarFoundryFactory GetGameSystemLoadingFactoryForFile(FileInfo file)
   1.225 +		{
   1.226 +			IWarFoundryFactory loadingFactory = null;
   1.227 +			
   1.228 +			foreach (INonNativeWarFoundryFactory factory in nonNativeFactories)
   1.229 +			{
   1.230 +				if (factory.CanHandleFileAsGameSystem(file))
   1.231 +				{
   1.232 +					loadingFactory = factory;
   1.233 +					break;
   1.234 +				}
   1.235 +			}
   1.236 +
   1.237 +			if (loadingFactory == null)
   1.238 +			{
   1.239 +				foreach (INativeWarFoundryFactory factory in factories)
   1.240 +				{
   1.241 +					if (factory.CanHandleFileAsGameSystem(file))
   1.242 +					{
   1.243 +						loadingFactory = factory;
   1.244 +						break;
   1.245 +					}
   1.246 +				}
   1.247 +			}
   1.248 +
   1.249 +			return loadingFactory;
   1.250 +		}
   1.251 +
   1.252 +		private IWarFoundryFactory GetRaceLoadingFactoryForFile(FileInfo file)
   1.253 +		{
   1.254 +			IWarFoundryFactory loadingFactory = null;
   1.255 +			
   1.256 +			foreach (INonNativeWarFoundryFactory factory in nonNativeFactories)
   1.257 +			{
   1.258 +				if (factory.CanHandleFileAsRace(file))
   1.259 +				{
   1.260 +					loadingFactory = factory;
   1.261 +					break;
   1.262 +				}
   1.263 +			}
   1.264 +
   1.265 +			if (loadingFactory == null)
   1.266 +			{
   1.267 +				foreach (INativeWarFoundryFactory factory in factories)
   1.268 +				{
   1.269 +					if (factory.CanHandleFileAsRace(file))
   1.270 +					{
   1.271 +						loadingFactory = factory;
   1.272 +						break;
   1.273 +					}
   1.274 +				}
   1.275 +			}
   1.276 +
   1.277 +			return loadingFactory;
   1.278 +		}
   1.279 +
   1.280 +		private List<FileLoadFailure> LoadGameSystems(Dictionary<FileInfo, IWarFoundryFactory> gameSystemFiles)
   1.281 +		{
   1.282 +			List<FileLoadFailure> fails = new List<FileLoadFailure>();
   1.283 +
   1.284 +			
   1.285 +			foreach (FileInfo file in gameSystemFiles.Keys)
   1.286 +			{
   1.287 +				FileLoadFailure failure = null;
   1.288 +				
   1.289 +				try
   1.290 +				{
   1.291 +					bool loaded = LoadObject(file, gameSystemFiles[file]);
   1.292 +	
   1.293 +					if (!loaded)
   1.294 +					{
   1.295 +						failure = new FileLoadFailure(file, "FileLoadFailed", "Failed to load {0} as GameSystem using {1}");
   1.296 +					}
   1.297 +				}
   1.298 +				catch (Exception ex)
   1.299 +				{
   1.300 +					failure = new FileLoadFailure(file, null, ex.Message, null, ex);
   1.301 +				}
   1.302 +						
   1.303 +				if (failure != null)
   1.304 +				{
   1.305 +					fails.Add(failure);
   1.306 +					LogNotifier.Warn(GetType(), failure.Message, failure.Exception);
   1.307 +				}
   1.308 +			}
   1.309 +			
   1.310 +			return fails;
   1.311 +		}
   1.312 +
   1.313 +		private List<FileLoadFailure> LoadRaces(Dictionary<FileInfo, IWarFoundryFactory> raceFiles)
   1.314 +		{
   1.315 +			List<FileLoadFailure> fails = new List<FileLoadFailure>();
   1.316 +			
   1.317 +			foreach (FileInfo file in raceFiles.Keys)
   1.318 +			{
   1.319 +				FileLoadFailure failure = null;
   1.320 +				
   1.321 +				try
   1.322 +				{
   1.323 +					bool loaded = LoadObject(file, raceFiles[file]);
   1.324 +	
   1.325 +					if (!loaded)
   1.326 +					{
   1.327 +						failure = new FileLoadFailure(file, "FileLoadFailed", "Failed to load {0} as Race using {1}");
   1.328 +					}
   1.329 +				}
   1.330 +				catch (Exception ex)
   1.331 +				{
   1.332 +					failure = new FileLoadFailure(file, null, ex.Message, null, ex);
   1.333 +				}
   1.334 +						
   1.335 +				if (failure != null)
   1.336 +				{
   1.337 +					fails.Add(failure);
   1.338 +					LogNotifier.Warn(GetType(), failure.Message, failure.Exception);
   1.339 +				}
   1.340 +			}
   1.341 +			
   1.342 +			return fails;
   1.343 +		}
   1.344 +
   1.345 +		private bool LoadObject(FileInfo file, IWarFoundryFactory factory)
   1.346 +		{
   1.347 +			LogNotifier.DebugFormat(GetType(), "Loading {0} using {1}", file.FullName, factory.GetType().Name);
   1.348 +			factory.RaceLoaded+= StoreRace;
   1.349 +			factory.GameSystemLoaded+= StoreGameSystem;
   1.350 +			ICollection<IWarFoundryObject> objects = factory.CreateObjectsFromFile(file);
   1.351 +			return objects.Count > 0;
   1.352 +		}
   1.353 +		
   1.354 +		
   1.355 +		/// <summary>
   1.356 +		/// Loads a single file through the registered WarFoundryFactories, if a factory exists that supports the file format.
   1.357 +		/// </summary>
   1.358 +		/// <param name="file">
   1.359 +		/// A <see cref="FileInfo"/> for the file to attempt to load
   1.360 +		/// </param>
   1.361 +		/// <returns>
   1.362 +		/// An ICollection of IWarFoundryObjects loaded from <code>file</code>
   1.363 +		/// </returns>
   1.364 +		public ICollection<IWarFoundryObject> LoadFile(FileInfo file)
   1.365 +		{
   1.366 +			ICollection<IWarFoundryObject> objs = null;
   1.367 +			IWarFoundryFactory loadFactory = null;
   1.368 +			
   1.369 +			try
   1.370 +			{
   1.371 +				objs = LoadFileWithNonNativeFactories(file, out loadFactory);
   1.372 +				
   1.373 +				if (objs == null)
   1.374 +				{
   1.375 +					objs = LoadFileWithNativeFactories(file, out loadFactory);
   1.376 +				}
   1.377 +			}
   1.378 +			catch (InvalidFileException ex)
   1.379 +			{
   1.380 +				LogNotifier.Error(GetType(), file.FullName + " failed to load", ex);
   1.381 +			}
   1.382 +				
   1.383 +			if (objs != null)
   1.384 +			{
   1.385 +				AddLoadedObjects(objs, loadFactory);
   1.386 +			}
   1.387 +			else
   1.388 +			{
   1.389 +				objs = new List<IWarFoundryObject>();
   1.390 +			}
   1.391 +
   1.392 +			return objs;
   1.393 +		}
   1.394 +
   1.395 +		private ICollection<IWarFoundryObject> LoadFileWithNonNativeFactories(FileInfo file, out IWarFoundryFactory loadFactory)
   1.396 +		{
   1.397 +			ICollection<IWarFoundryObject> objs = null;
   1.398 +			loadFactory = null;
   1.399 +			
   1.400 +			if (nonNativeFactories.Count > 0)
   1.401 +			{
   1.402 +				LogNotifier.Debug(GetType(), "Attempting to load " + file.FullName + " as a non-native file");
   1.403 +				
   1.404 +				foreach (INonNativeWarFoundryFactory factory in nonNativeFactories)
   1.405 +				{
   1.406 +					bool canLoad = factory.CanHandleFileFormat(file);
   1.407 +					LogNotifier.Debug(GetType(), "Load using " + factory.GetType().FullName + "? " + (canLoad ? "yes" : "no"));
   1.408 +					
   1.409 +					if (canLoad)
   1.410 +					{
   1.411 +						objs = factory.CreateObjectsFromFile(file);
   1.412 +						
   1.413 +						if (objs != null)
   1.414 +						{
   1.415 +							loadFactory = factory;
   1.416 +							break;
   1.417 +						}
   1.418 +					}			         
   1.419 +				}
   1.420 +			}
   1.421 +			
   1.422 +			return objs;
   1.423 +		}
   1.424 +
   1.425 +		private ICollection<IWarFoundryObject> LoadFileWithNativeFactories(FileInfo file, out IWarFoundryFactory loadFactory)
   1.426 +		{
   1.427 +			ICollection<IWarFoundryObject> objs = null;
   1.428 +			loadFactory = null;
   1.429 +			
   1.430 +			if (factories.Count > 0)
   1.431 +			{
   1.432 +				LogNotifier.Debug(GetType(), "Attempting to load " + file.FullName + " as native file");
   1.433 +						
   1.434 +				foreach (INativeWarFoundryFactory factory in factories)
   1.435 +				{
   1.436 +					if (factory.CanHandleFileFormat(file))
   1.437 +					{
   1.438 +						objs = factory.CreateObjectsFromFile(file);
   1.439 +						
   1.440 +						if (objs != null)
   1.441 +						{
   1.442 +							loadFactory = factory;
   1.443 +							break;
   1.444 +						}
   1.445 +					}
   1.446 +				}
   1.447 +			}
   1.448 +			
   1.449 +			return objs;
   1.450 +		}
   1.451 +
   1.452 +		private void AddLoadedObjects(ICollection<IWarFoundryObject> loadedObjs, IWarFoundryFactory factory)
   1.453 +		{
   1.454 +			SimpleSet<IWarFoundryObject> objs;
   1.455 +			loadedObjects.TryGetValue(factory, out objs);
   1.456 +			
   1.457 +			if (objs == null)
   1.458 +			{
   1.459 +				objs = new SimpleSet<IWarFoundryObject>();
   1.460 +				loadedObjects.Add(factory, objs);
   1.461 +			}
   1.462 +				
   1.463 +			objs.AddRange(loadedObjs);
   1.464 +			StoreObjects(loadedObjs);
   1.465 +		}
   1.466 +
   1.467 +		private void StoreObjects(ICollection<IWarFoundryObject> loadedObjects)
   1.468 +		{
   1.469 +			foreach (IWarFoundryObject loadedObject in loadedObjects)
   1.470 +			{
   1.471 +				if (loadedObject is GameSystem)
   1.472 +				{
   1.473 +					StoreGameSystem((GameSystem)loadedObject);
   1.474 +				}
   1.475 +				else if (loadedObject is Race)
   1.476 +				{
   1.477 +					StoreRace((Race)loadedObject);
   1.478 +				}
   1.479 +			}
   1.480 +		}
   1.481 +
   1.482 +		protected void StoreGameSystem(GameSystem system)
   1.483 +		{
   1.484 +			GameSystem existingSystem = GetExistingSystemForSystem(system);
   1.485 +			
   1.486 +			if (existingSystem != null)
   1.487 +			{				
   1.488 +				if (!system.Equals(existingSystem))
   1.489 +				{
   1.490 +					//TODO: Raise an event to say we got a different duplicate
   1.491 +					//We can't just fail, because failing is for completely unhandled files, not for objects in a file
   1.492 +				}
   1.493 +			}
   1.494 +			else
   1.495 +			{
   1.496 +				DoStoreGameSystem(system);
   1.497 +			}
   1.498 +		}
   1.499 +		
   1.500 +		/// <summary>
   1.501 +		/// Gets a game system that has already been loaded that duplicates the supplied game system's ID, if one exists.
   1.502 +		/// </summary>
   1.503 +		/// <param name="system">
   1.504 +		/// The <see cref="GameSystem"/> to find pre-existing duplicates of
   1.505 +		/// </param>
   1.506 +		/// <returns>
   1.507 +		/// <code>null</code> if no existing duplicate exists, else the duplicate <see cref="GameSystem"/>
   1.508 +		/// </returns>
   1.509 +		protected abstract GameSystem GetExistingSystemForSystem(GameSystem system);
   1.510 +		
   1.511 +		/// <summary>
   1.512 +		/// Stores a GameSystem in the loader's relevant storage structure
   1.513 +		/// </summary>
   1.514 +		/// <param name="system">
   1.515 +		/// The loaded <see cref="GameSystem"/> to store
   1.516 +		/// </param>
   1.517 +		protected abstract void DoStoreGameSystem(GameSystem system);
   1.518 +
   1.519 +		protected void StoreRace(Race race)
   1.520 +		{
   1.521 +			if (race.GameSystem == null)
   1.522 +			{
   1.523 +				throw new InvalidOperationException("Race cannot have null game system. Game system should be loaded before race.");
   1.524 +			}
   1.525 +			
   1.526 +			DoStoreRace(race);
   1.527 +		}
   1.528 +		
   1.529 +		/// <summary>
   1.530 +		/// Performs the implementation specific storage of a race
   1.531 +		/// </summary>
   1.532 +		/// <param name="race">
   1.533 +		/// The <see cref="Race"/> to store
   1.534 +		/// </param>
   1.535 +		protected abstract void DoStoreRace(Race race);
   1.536 +		
   1.537 +		/// <summary>
   1.538 +		/// Gets all <see cref="GameSystem"/>s that are currently available, determined by those that can be loaded with the current <see cref="IWarFoundryFactory"/>s. 
   1.539 +		/// </summary>
   1.540 +		/// <returns>
   1.541 +		/// An array of <see cref="GameSystem"/>s that are currently available.
   1.542 +		/// </returns>
   1.543 +		public abstract GameSystem[] GetGameSystems();
   1.544 +
   1.545 +		/// <summary>
   1.546 +		/// Gets a single <see cref="GameSystem"/> with a given ID. 
   1.547 +		/// </summary>
   1.548 +		/// <param name="systemID">
   1.549 +		/// The ID of the <see cref="GameSystem"/> to get, as a <see cref="System.String"/>.
   1.550 +		/// </param>
   1.551 +		/// <returns>
   1.552 +		/// The <see cref="GameSystem"/> with the given ID, or <code>null</code> if one doesn't exist.
   1.553 +		/// </returns>
   1.554 +		public abstract GameSystem GetGameSystem(string systemID);
   1.555 +
   1.556 +		/// <summary>
   1.557 +		/// Removes a loaded <see cref="GameSystem"/>. Used when a GameSystem fails to complete loading
   1.558 +		/// </summary>
   1.559 +		/// <param name="system">The GameSystem to remove</param>
   1.560 +		protected internal abstract void RemoveGameSystem(GameSystem system);
   1.561 +
   1.562 +		/// <summary>
   1.563 +		/// Gets an array of the races for the specified <see cref="GameSystem"/>.
   1.564 +		/// </summary>
   1.565 +		/// <param name="system">
   1.566 +		/// The <see cref="GameSystem"/> to get the available races for.
   1.567 +		/// </param>
   1.568 +		/// <returns>
   1.569 +		/// An array of <see cref="Race"/>s for the <see cref="GameSystem"/>
   1.570 +		/// </returns>
   1.571 +		public abstract Race[] GetRaces(GameSystem system);
   1.572 +
   1.573 +		/// <summary>
   1.574 +		/// Gets a single race for a given <see cref="GameSystem"/> by ID of the race.
   1.575 +		/// </summary>
   1.576 +		/// <param name="system">
   1.577 +		/// The <see cref="GameSystem"/> that the race is part of.
   1.578 +		/// </param>
   1.579 +		/// <param name="raceID">
   1.580 +		/// A <see cref="System.String"/> ID for the race to load.
   1.581 +		/// </param>
   1.582 +		/// <returns>
   1.583 +		/// A <see cref="Race"/> with the specified ID from the <see cref="GameSystem"/>, or <code>null</code> if one doesn't exist.
   1.584 +		/// </returns>
   1.585 +		public abstract Race GetRace(GameSystem system, string raceID);
   1.586 +
   1.587 +		/// <summary>
   1.588 +		/// Gets a single race for a given <see cref="GameSystem"/> by the race's ID and sub-race ID.
   1.589 +		/// </summary>
   1.590 +		/// <param name="system">
   1.591 +		/// The <see cref="GameSystem"/> that the race is part of.
   1.592 +		/// </param>
   1.593 +		/// <param name="raceID">
   1.594 +		/// The <see cref="System.String"/> ID for the race to load.
   1.595 +		/// </param>
   1.596 +		/// <param name="raceSubID">
   1.597 +		/// A <see cref="System.String"/>
   1.598 +		/// </param>
   1.599 +		/// <returns>
   1.600 +		/// A <see cref="Race"/>
   1.601 +		/// </returns>
   1.602 +		public abstract Race GetRace(GameSystem system, string raceID, string raceSubID);
   1.603 +
   1.604 +		protected internal abstract void RemoveRace(Race race);
   1.605 +
   1.606 +		/// <summary>
   1.607 +		/// Gets the IDs of all of the game systems currently available.
   1.608 +		/// </summary>
   1.609 +		/// <returns>
   1.610 +		/// An array of <see cref="System.String"/>s representing the IDs of the game systems.
   1.611 +		/// </returns>
   1.612 +		public virtual string[] GetGameSystemIDs()
   1.613 +		{
   1.614 +			GameSystem[] systems = GetGameSystems();
   1.615 +			return GetWarFoundryObjectIDs(systems);
   1.616 +		}
   1.617 +
   1.618 +		protected string[] GetWarFoundryObjectIDs(WarFoundryObject[] objs)
   1.619 +		{
   1.620 +			int objCount = objs.Length;
   1.621 +			string[] keys = new string[objCount];
   1.622 +
   1.623 +			for (int i = 0; i < objCount; i++)
   1.624 +			{
   1.625 +				keys[i] = objs[i].ID;
   1.626 +			}
   1.627 +
   1.628 +			return keys;
   1.629 +		}
   1.630 +		
   1.631 +		/// <summary>
   1.632 +		/// Gets the IDs of all of the races of a specified game system.
   1.633 +		/// </summary>
   1.634 +		/// <param name="system">
   1.635 +		/// The <see cref="GameSystem"/> to get the available races for.
   1.636 +		/// </param>
   1.637 +		/// <returns>
   1.638 +		/// An array of <see cref="System.String"/>s representing the IDs of the races of the specified game system.
   1.639 +		/// </returns>
   1.640 +		public virtual string[] GetSystemRaceIDs(GameSystem system)
   1.641 +		{
   1.642 +			Race[] races = GetRaces(system);
   1.643 +			return GetWarFoundryObjectIDs(races);
   1.644 +		}
   1.645 +
   1.646 +		public Army LoadArmy(FileInfo file)
   1.647 +		{
   1.648 +			IWarFoundryFactory factory = GetArmyLoadingFactoryForFile(file);			
   1.649 +			Army loadedArmy = null;
   1.650 +			
   1.651 +			if (factory != null)
   1.652 +			{
   1.653 +				ICollection<IWarFoundryObject> objs = factory.CreateObjectsFromFile(file);
   1.654 +								
   1.655 +				if (objs.Count == 1)
   1.656 +				{
   1.657 +					foreach (IWarFoundryObject systemCount in objs)
   1.658 +					{
   1.659 +						if (systemCount is Army)
   1.660 +						{
   1.661 +							loadedArmy = (Army)systemCount;
   1.662 +						}
   1.663 +					}
   1.664 +				}
   1.665 +			}
   1.666 +			
   1.667 +			return loadedArmy;
   1.668 +		}
   1.669 +
   1.670 +		private IWarFoundryFactory GetArmyLoadingFactoryForFile(FileInfo file)
   1.671 +		{
   1.672 +			IWarFoundryFactory loadingFactory = null;
   1.673 +			
   1.674 +			foreach (INonNativeWarFoundryFactory factory in nonNativeFactories)
   1.675 +			{
   1.676 +				if (factory.CanHandleFileAsArmy(file))
   1.677 +				{
   1.678 +					loadingFactory = factory;
   1.679 +					break;
   1.680 +				}
   1.681 +			}
   1.682 +
   1.683 +			if (loadingFactory == null)
   1.684 +			{
   1.685 +				foreach (INativeWarFoundryFactory factory in factories)
   1.686 +				{
   1.687 +					if (factory.CanHandleFileAsArmy(file))
   1.688 +					{
   1.689 +						loadingFactory = factory;
   1.690 +						break;
   1.691 +					}
   1.692 +				}
   1.693 +			}
   1.694 +
   1.695 +			return loadingFactory;
   1.696 +		}
   1.697 +	}
   1.698 +}
     2.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     2.2 +++ b/API/Commands/AbstractReplaceUnitEquipmentCommand.cs	Sun Apr 03 18:50:32 2011 +0000
     2.3 @@ -0,0 +1,74 @@
     2.4 +// This file (AbstractReplaceUnitEquipmentCommand.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard.
     2.5 +//
     2.6 +// 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.
     2.7 +
     2.8 +using System;
     2.9 +using IBBoard.Commands;
    2.10 +using IBBoard.Lang;
    2.11 +using IBBoard.WarFoundry.API.Objects;
    2.12 +
    2.13 +namespace IBBoard.WarFoundry.API.Commands
    2.14 +{
    2.15 +	/// <summary>
    2.16 +	/// An abstract implementation of the core method for replacing one equipment item with another
    2.17 +	/// </summary>
    2.18 +	public abstract class AbstractReplaceUnitEquipmentCommand : Command
    2.19 +	{
    2.20 +		private SetUnitEquipmentNumericAmountCommand removeOldCommand;
    2.21 +		private AbstractSetUnitEquipmentAmountCommand addNewCommand;
    2.22 +
    2.23 +		public AbstractReplaceUnitEquipmentCommand(Unit unit, UnitEquipmentItem oldItem, AbstractSetUnitEquipmentAmountCommand addNewEquipmentCommand)
    2.24 +		{
    2.25 +			//We can get away with a numeric amount here even if it is a ratio item because we're setting it to 0
    2.26 +			removeOldCommand = new SetUnitEquipmentNumericAmountCommand(unit, oldItem, 0);
    2.27 +			addNewCommand = addNewEquipmentCommand;
    2.28 +		}
    2.29 +
    2.30 +		public override bool CanExecute()
    2.31 +		{
    2.32 +			return removeOldCommand.CanExecute() && addNewCommand.CanExecute();
    2.33 +		}
    2.34 +
    2.35 +		public override string Description
    2.36 +		{
    2.37 +			get
    2.38 +			{
    2.39 +				return Translation.GetTranslation("replaceUnitEquipmentCommandDescription", "replace {0} with {1} for {2}", removeOldCommand.EquipItem.Name, addNewCommand.EquipItem.Name, removeOldCommand.Unit.Name);
    2.40 +			}
    2.41 +		}
    2.42 +
    2.43 +		public override string UndoDescription
    2.44 +		{
    2.45 +			get
    2.46 +			{
    2.47 +				return Translation.GetTranslation("replaceUnitEquipmentCommandUndoDescription", "replace {0} with {1} for {2}", addNewCommand.EquipItem.Name, removeOldCommand.EquipItem.Name, removeOldCommand.Unit.Name);
    2.48 +			}
    2.49 +		}
    2.50 +
    2.51 +		public override bool Execute()
    2.52 +		{
    2.53 +			this.Redo();
    2.54 +			return true;
    2.55 +		}
    2.56 +
    2.57 +		public override void Redo()
    2.58 +		{
    2.59 +			removeOldCommand.Redo();
    2.60 +			addNewCommand.Redo();
    2.61 +		}
    2.62 +
    2.63 +		public override void Undo()
    2.64 +		{
    2.65 +			addNewCommand.Undo();
    2.66 +			removeOldCommand.Undo();
    2.67 +		}
    2.68 +
    2.69 +		public override string Name
    2.70 +		{
    2.71 +			get
    2.72 +			{
    2.73 +				return "Replace required equipment";
    2.74 +			}
    2.75 +		}
    2.76 +	}
    2.77 +}
     3.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     3.2 +++ b/API/Commands/AbstractSetUnitEquipmentAmountCommand.cs	Sun Apr 03 18:50:32 2011 +0000
     3.3 @@ -0,0 +1,120 @@
     3.4 +//  This file (AbstractSetUnitEquipmentAmountCommand.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
     3.5 +//
     3.6 +// 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.
     3.7 +
     3.8 +using System;
     3.9 +using IBBoard.Commands;
    3.10 +using IBBoard.Lang;
    3.11 +using IBBoard.WarFoundry.API.Objects;
    3.12 +using IBBoard.WarFoundry.API.Util;
    3.13 +
    3.14 +namespace IBBoard.WarFoundry.API.Commands
    3.15 +{
    3.16 +	/// <summary>
    3.17 +	/// Abstract parent class for commands that set the amount of an equipment item a unit has to a fixed numeric or ratio value
    3.18 +	/// </summary>
    3.19 +	public abstract class AbstractSetUnitEquipmentAmountCommand : Command
    3.20 +	{
    3.21 +		private Unit unit;
    3.22 +		private UnitEquipmentItem equip;
    3.23 +		private double oldAmount;
    3.24 +		private bool oldAmountWasRatio;
    3.25 +
    3.26 +		public AbstractSetUnitEquipmentAmountCommand(Unit unit, UnitEquipmentItem item)
    3.27 +		{
    3.28 +			this.unit = unit;
    3.29 +			equip = item;
    3.30 +			oldAmount = UnitEquipmentUtil.GetEquipmentAmount(unit, equip);
    3.31 +			oldAmountWasRatio = UnitEquipmentUtil.GetEquipmentAmountIsRatio(unit, equip);
    3.32 +		}
    3.33 +
    3.34 +		public override bool CanExecute()
    3.35 +		{
    3.36 +			return (unit != null && equip != null);
    3.37 +		}
    3.38 +
    3.39 +		public override string Description
    3.40 +		{
    3.41 +			get
    3.42 +			{
    3.43 +				return Translation.GetTranslation("setEquipmentAmountCommandDescription", "set {0} amount for {1} to {2}", equip.Name, unit.Name, GetNewAmountString());
    3.44 +			}
    3.45 +		}
    3.46 +
    3.47 +		/// <summary>
    3.48 +		/// Gets the string representation for the new amount of the equipment item to take
    3.49 +		/// </summary>
    3.50 +		/// <returns>
    3.51 +		/// the string representation for the new amount of the equipment item to take
    3.52 +		/// </returns>
    3.53 +		protected abstract string GetNewAmountString();
    3.54 +
    3.55 +		public override string UndoDescription
    3.56 +		{
    3.57 +			get
    3.58 +			{
    3.59 +				return Translation.GetTranslation("setEquipmentAmountCommandUndoDescription", "set {0} amount for {1} to {2}", equip.Name, unit.Name, GetOldAmountString());
    3.60 +			}
    3.61 +		}
    3.62 +
    3.63 +		/// <summary>
    3.64 +		/// Gets the string representation for the old amount of the equipment item to take
    3.65 +		/// </summary>
    3.66 +		/// <returns>
    3.67 +		/// the string representation for the old amount of the equipment item to take
    3.68 +		/// </returns>
    3.69 +		protected string GetOldAmountString()
    3.70 +		{
    3.71 +			return oldAmountWasRatio ? GetRatioAmountString(oldAmount, UnitEquipmentRatioSelection.CalculateNumberTaken(Unit, EquipItem, oldAmount)) : GetNumberAmountString((int)oldAmount);
    3.72 +		}
    3.73 +
    3.74 +		protected string GetNumberAmountString(int number)
    3.75 +		{
    3.76 +			return Translation.GetTranslation("equipmentAmountNumber", "{0}", number);
    3.77 +		}
    3.78 +
    3.79 +		protected string GetRatioAmountString(double amount, int number)
    3.80 +		{
    3.81 +			string amountString;
    3.82 +			
    3.83 +			if (amount == 100)
    3.84 +			{
    3.85 +				amountString = Translation.GetTranslation("equipmentAmountAll", "all ({1})", amount, number);
    3.86 +			}
    3.87 +			else
    3.88 +			{
    3.89 +				amountString = Translation.GetTranslation("equipmentAmountPercentage", "{0}% ({1})", amount, number);
    3.90 +			}
    3.91 +			
    3.92 +			return amountString;
    3.93 +		}
    3.94 +
    3.95 +		public override bool Execute()
    3.96 +		{
    3.97 +			this.Redo();
    3.98 +			return true;
    3.99 +		}
   3.100 +
   3.101 +		public override void Undo()
   3.102 +		{
   3.103 +			if (oldAmountWasRatio)
   3.104 +			{
   3.105 +				unit.SetEquipmentRatio(equip, oldAmount);
   3.106 +			}
   3.107 +			else
   3.108 +			{
   3.109 +				unit.SetEquipmentAmount(equip, (int)oldAmount);
   3.110 +			}
   3.111 +		}
   3.112 +
   3.113 +		public UnitEquipmentItem EquipItem
   3.114 +		{
   3.115 +			get { return equip; }
   3.116 +		}
   3.117 +
   3.118 +		public Unit Unit
   3.119 +		{
   3.120 +			get { return unit; }
   3.121 +		}
   3.122 +	}
   3.123 +}
     4.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     4.2 +++ b/API/Commands/CreateAndAddUnitCommand.cs	Sun Apr 03 18:50:32 2011 +0000
     4.3 @@ -0,0 +1,72 @@
     4.4 +// This file (CreateAndAddUnitCommand.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
     4.5 +//
     4.6 +// 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.
     4.7 +
     4.8 +using System;
     4.9 +using IBBoard.Commands;
    4.10 +using IBBoard.Lang;
    4.11 +using IBBoard.WarFoundry.API.Objects;
    4.12 +
    4.13 +namespace IBBoard.WarFoundry.API.Commands
    4.14 +{
    4.15 +	public class CreateAndAddUnitCommand : Command
    4.16 +	{
    4.17 +		private UnitType addedUnitType;
    4.18 +		private ArmyCategory armyCat;
    4.19 +		private Unit addedUnit;
    4.20 +
    4.21 +		public CreateAndAddUnitCommand(UnitType toAdd, ArmyCategory armyCatTo)
    4.22 +		{
    4.23 +			addedUnitType = toAdd;
    4.24 +			armyCat = armyCatTo;
    4.25 +		}
    4.26 +
    4.27 +		public override bool CanExecute()
    4.28 +		{
    4.29 +			return (addedUnitType != null && armyCat != null);
    4.30 +		}
    4.31 +
    4.32 +		public override string Description
    4.33 +		{
    4.34 +			get
    4.35 +			{
    4.36 +				return Translation.GetTranslation("createAndAddUnitCommandDescription", "add unit of {0} to the army", addedUnitType.Name);
    4.37 +			}
    4.38 +		}
    4.39 +
    4.40 +		public override string UndoDescription
    4.41 +		{
    4.42 +			get
    4.43 +			{
    4.44 +				return Translation.GetTranslation("createAndAddUnitCommandUndoDescription", "remove unit of {0} from army", addedUnitType.Name);
    4.45 +			}
    4.46 +		}
    4.47 +
    4.48 +		public override bool Execute()
    4.49 +		{
    4.50 +			addedUnit = new Unit(addedUnitType, armyCat);
    4.51 +			this.Redo();
    4.52 +			return true;
    4.53 +		}
    4.54 +
    4.55 +		public override void Redo()
    4.56 +		{
    4.57 +			armyCat.AddUnit(addedUnit);
    4.58 +		}
    4.59 +
    4.60 +		public override void Undo()
    4.61 +		{
    4.62 +			armyCat.RemoveUnit(addedUnit);
    4.63 +		}
    4.64 +
    4.65 +		public override string Name
    4.66 +		{
    4.67 +			get { return "Add new unit"; }
    4.68 +		}
    4.69 +
    4.70 +		public Unit Unit
    4.71 +		{
    4.72 +			get { return addedUnit; }
    4.73 +		}
    4.74 +	}
    4.75 +}
     5.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     5.2 +++ b/API/Commands/RemoveUnitCommand.cs	Sun Apr 03 18:50:32 2011 +0000
     5.3 @@ -0,0 +1,68 @@
     5.4 +// This file (RemoveUnitCommand.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
     5.5 +//
     5.6 +// 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.
     5.7 +
     5.8 +using System;
     5.9 +using IBBoard.Commands;
    5.10 +using IBBoard.Lang;
    5.11 +using IBBoard.WarFoundry.API.Objects;
    5.12 +
    5.13 +namespace IBBoard.WarFoundry.API.Commands
    5.14 +{
    5.15 +	/// <summary>
    5.16 +	/// Summary description for RemoveUnitCommand.
    5.17 +	/// </summary>
    5.18 +	public class RemoveUnitCommand : Command
    5.19 +	{
    5.20 +		private Unit unit;
    5.21 +		private ArmyCategory cat;
    5.22 +
    5.23 +		public RemoveUnitCommand(Unit toRemove)
    5.24 +		{
    5.25 +			unit = toRemove;
    5.26 +			cat = unit.Category;
    5.27 +		}
    5.28 +
    5.29 +		public override bool CanExecute()
    5.30 +		{
    5.31 +			return (unit != null);
    5.32 +		}
    5.33 +
    5.34 +		public override string Description
    5.35 +		{
    5.36 +			get
    5.37 +			{
    5.38 +				return Translation.GetTranslation("removeUnitCommandDescription", "remove {0} from the army", unit.Name);
    5.39 +			}
    5.40 +		}
    5.41 +
    5.42 +		public override string UndoDescription
    5.43 +		{
    5.44 +			get
    5.45 +			{
    5.46 +				return Translation.GetTranslation("removeUnitCommandUndoDescription", "re-add {0} to the army", unit.Name);
    5.47 +			}
    5.48 +		}
    5.49 +
    5.50 +		public override bool Execute()
    5.51 +		{
    5.52 +			this.Redo();
    5.53 +			return true;
    5.54 +		}
    5.55 +
    5.56 +		public override void Redo()
    5.57 +		{
    5.58 +			cat.RemoveUnit(unit);
    5.59 +		}
    5.60 +
    5.61 +		public override void Undo()
    5.62 +		{
    5.63 +			cat.AddUnit(unit);
    5.64 +		}
    5.65 +
    5.66 +		public override string Name
    5.67 +		{
    5.68 +			get { return "Remove unit"; }
    5.69 +		}
    5.70 +	}
    5.71 +}
     6.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     6.2 +++ b/API/Commands/ReplaceUnitEquipmentWithNumericAmountItemCommand.cs	Sun Apr 03 18:50:32 2011 +0000
     6.3 @@ -0,0 +1,21 @@
     6.4 +//  This file (ReplaceUnitEquipmentWithNumericAmountItemCommand.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
     6.5 +// 
     6.6 +// 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.
     6.7 +// 
     6.8 +
     6.9 +using System;
    6.10 +using IBBoard.WarFoundry.API.Objects;
    6.11 +
    6.12 +namespace IBBoard.WarFoundry.API.Commands
    6.13 +{
    6.14 +	/// <summary>
    6.15 +	/// A concrete implementation of an equipment replacing command that replaces a given equipment item with a different item that has an absolute numeric amount.
    6.16 +	/// </summary>
    6.17 +	public class ReplaceUnitEquipmentWithNumericAmountItemCommand : AbstractReplaceUnitEquipmentCommand
    6.18 +	{	
    6.19 +		public ReplaceUnitEquipmentWithNumericAmountItemCommand(Unit unit, UnitEquipmentItem oldItem, UnitEquipmentItem newItem, int newItemAmount) : base(unit, oldItem, new SetUnitEquipmentNumericAmountCommand(unit, newItem, newItemAmount))
    6.20 +		{
    6.21 +			//Do nothing special
    6.22 +		}
    6.23 +	}
    6.24 +}
     7.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     7.2 +++ b/API/Commands/ReplaceUnitEquipmentWithRatioAmountItemCommand.cs	Sun Apr 03 18:50:32 2011 +0000
     7.3 @@ -0,0 +1,21 @@
     7.4 +//  This file (ReplaceUnitEquipmentWithRatioAmountItemCommand.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
     7.5 +// 
     7.6 +// 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.
     7.7 +// 
     7.8 +
     7.9 +using System;
    7.10 +using IBBoard.WarFoundry.API.Objects;
    7.11 +
    7.12 +namespace IBBoard.WarFoundry.API.Commands
    7.13 +{
    7.14 +	/// <summary>
    7.15 +	/// A concrete implementation of an equipment replacing command that replaces a given equipment item with a different item that has a ratio/percentage amount.
    7.16 +	/// </summary>
    7.17 +	public class ReplaceUnitEquipmentWithRatioAmountItemCommand : AbstractReplaceUnitEquipmentCommand
    7.18 +	{	
    7.19 +		public ReplaceUnitEquipmentWithRatioAmountItemCommand(Unit unit, UnitEquipmentItem oldItem, UnitEquipmentItem newItem, double newItemRatio) : base(unit, oldItem, new SetUnitEquipmentRatioAmountCommand(unit, newItem, newItemRatio))
    7.20 +		{
    7.21 +			//Do nothing special
    7.22 +		}
    7.23 +	}
    7.24 +}
     8.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     8.2 +++ b/API/Commands/SetNameCommand.cs	Sun Apr 03 18:50:32 2011 +0000
     8.3 @@ -0,0 +1,69 @@
     8.4 +// This file (SetNameCommand.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
     8.5 +//
     8.6 +// 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.
     8.7 +
     8.8 +using System;
     8.9 +using IBBoard.Commands;
    8.10 +using IBBoard.Lang;
    8.11 +using IBBoard.WarFoundry.API.Objects;
    8.12 +
    8.13 +namespace IBBoard.WarFoundry.API.Commands
    8.14 +{
    8.15 +	/// <summary>
    8.16 +	/// Summary description for SetNameCommand.
    8.17 +	/// </summary>
    8.18 +	public class SetNameCommand : Command
    8.19 +	{
    8.20 +		private WarFoundryObject obj;
    8.21 +		private string newName, oldName;
    8.22 +
    8.23 +		public SetNameCommand(WarFoundryObject toRename, string name)
    8.24 +		{
    8.25 +			obj = toRename;
    8.26 +			newName = name;
    8.27 +			oldName = obj.Name;
    8.28 +		}
    8.29 +
    8.30 +		public override bool CanExecute()
    8.31 +		{
    8.32 +			return (obj != null && newName != null && newName != "");
    8.33 +		}
    8.34 +
    8.35 +		public override string Description
    8.36 +		{
    8.37 +			get
    8.38 +			{
    8.39 +				return Translation.GetTranslation("setUnitNameCommandDescription", "rename \"{0}\" to \"{1}\"", oldName, newName);
    8.40 +			}
    8.41 +		}
    8.42 +
    8.43 +		public override string UndoDescription
    8.44 +		{
    8.45 +			get
    8.46 +			{
    8.47 +				return Translation.GetTranslation("setUnitNameCommandUndoDescription", "rename \"{0}\" to \"{1}\"", newName, oldName);
    8.48 +			}
    8.49 +		}
    8.50 +
    8.51 +		public override bool Execute()
    8.52 +		{
    8.53 +			this.Redo();
    8.54 +			return true;
    8.55 +		}
    8.56 +
    8.57 +		public override void Redo()
    8.58 +		{
    8.59 +			obj.Name = newName;
    8.60 +		}
    8.61 +
    8.62 +		public override void Undo()
    8.63 +		{
    8.64 +			obj.Name = oldName;
    8.65 +		}
    8.66 +
    8.67 +		public override string Name
    8.68 +		{
    8.69 +			get { return "Rename item"; }
    8.70 +		}
    8.71 +	}
    8.72 +}
     9.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     9.2 +++ b/API/Commands/SetUnitEquipmentNumericAmountCommand.cs	Sun Apr 03 18:50:32 2011 +0000
     9.3 @@ -0,0 +1,39 @@
     9.4 +// This file (SetUnitEquipmentNumericAmountCommand.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard.
     9.5 +//
     9.6 +// 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.
     9.7 +
     9.8 +using System;
     9.9 +using IBBoard.Commands;
    9.10 +using IBBoard.Lang;
    9.11 +using IBBoard.WarFoundry.API.Objects;
    9.12 +
    9.13 +namespace IBBoard.WarFoundry.API.Commands
    9.14 +{
    9.15 +	/// <summary>
    9.16 +	/// Summary description for SetUnitEquipmentNumericAmountCommand.
    9.17 +	/// </summary>
    9.18 +	public class SetUnitEquipmentNumericAmountCommand : AbstractSetUnitEquipmentAmountCommand
    9.19 +	{
    9.20 +		private int newAmount;
    9.21 +		
    9.22 +		public SetUnitEquipmentNumericAmountCommand(Unit unit, UnitEquipmentItem item, int amount) : base(unit, item)
    9.23 +		{
    9.24 +			newAmount = amount;
    9.25 +		}
    9.26 +
    9.27 +		protected override string GetNewAmountString ()
    9.28 +		{
    9.29 +			return GetNumberAmountString(newAmount);
    9.30 +		}
    9.31 +		
    9.32 +		public override void Redo()
    9.33 +		{
    9.34 +			Unit.SetEquipmentAmount(EquipItem, newAmount);
    9.35 +		}
    9.36 +
    9.37 +		public override string Name
    9.38 +		{
    9.39 +			get { return "Set equipment amount"; }
    9.40 +		}
    9.41 +	}
    9.42 +}
    10.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    10.2 +++ b/API/Commands/SetUnitEquipmentRatioAmountCommand.cs	Sun Apr 03 18:50:32 2011 +0000
    10.3 @@ -0,0 +1,39 @@
    10.4 +// This file (SetUnitEquipmentRatioAmountCommand.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard.
    10.5 +//
    10.6 +// 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.
    10.7 +
    10.8 +using System;
    10.9 +using IBBoard.Commands;
   10.10 +using IBBoard.Lang;
   10.11 +using IBBoard.WarFoundry.API.Objects;
   10.12 +
   10.13 +namespace IBBoard.WarFoundry.API.Commands
   10.14 +{
   10.15 +	/// <summary>
   10.16 +	/// Summary description for SetUnitEquipmentRatioAmountCommand.
   10.17 +	/// </summary>
   10.18 +	public class SetUnitEquipmentRatioAmountCommand : AbstractSetUnitEquipmentAmountCommand
   10.19 +	{
   10.20 +		private double newAmount;
   10.21 +		
   10.22 +		public SetUnitEquipmentRatioAmountCommand(Unit unit, UnitEquipmentItem item, double amount) : base(unit, item)
   10.23 +		{
   10.24 +			newAmount = amount;
   10.25 +		}
   10.26 +
   10.27 +		protected override string GetNewAmountString ()
   10.28 +		{
   10.29 +			return GetRatioAmountString(newAmount, UnitEquipmentRatioSelection.CalculateNumberTaken(Unit, EquipItem, newAmount));
   10.30 +		}
   10.31 +
   10.32 +		public override void Redo()
   10.33 +		{
   10.34 +			Unit.SetEquipmentRatio(EquipItem, newAmount);
   10.35 +		}
   10.36 +
   10.37 +		public override string Name
   10.38 +		{
   10.39 +			get { return "Set equipment ratio"; }
   10.40 +		}
   10.41 +	}
   10.42 +}
    11.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    11.2 +++ b/API/Commands/SetUnitSizeCommand.cs	Sun Apr 03 18:50:32 2011 +0000
    11.3 @@ -0,0 +1,69 @@
    11.4 +// This file (SetUnitSizeCommand.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    11.5 +//
    11.6 +// 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.
    11.7 +
    11.8 +using System;
    11.9 +using IBBoard.Commands;
   11.10 +using IBBoard.Lang;
   11.11 +using IBBoard.WarFoundry.API.Objects;
   11.12 +
   11.13 +namespace IBBoard.WarFoundry.API.Commands
   11.14 +{
   11.15 +	/// <summary>
   11.16 +	/// Summary description for SetNameCommand.
   11.17 +	/// </summary>
   11.18 +	public class SetUnitSizeCommand : Command
   11.19 +	{
   11.20 +		private Unit unit;
   11.21 +		private int newSize, oldSize;
   11.22 +
   11.23 +		public SetUnitSizeCommand(Unit toResize, int size)
   11.24 +		{
   11.25 +			unit = toResize;
   11.26 +			newSize = size;
   11.27 +			oldSize = unit.Size;
   11.28 +		}
   11.29 +
   11.30 +		public override bool CanExecute()
   11.31 +		{
   11.32 +			return (unit != null && newSize > 0 && oldSize > 0);
   11.33 +		}
   11.34 +
   11.35 +		public override string Description
   11.36 +		{
   11.37 +			get
   11.38 +			{
   11.39 +				return Translation.GetTranslation("setUnitSizeCommandDescription", "set size of {0} to {1}", unit.Name, newSize);
   11.40 +			}
   11.41 +		}
   11.42 +
   11.43 +		public override string UndoDescription
   11.44 +		{
   11.45 +			get
   11.46 +			{
   11.47 +				return Translation.GetTranslation("setUnitSizeCommandUndoDescription", "set size of {0} to {1}", unit.Name, oldSize);
   11.48 +			}
   11.49 +		}
   11.50 +
   11.51 +		public override bool Execute()
   11.52 +		{
   11.53 +			this.Redo();
   11.54 +			return true;
   11.55 +		}
   11.56 +
   11.57 +		public override void Redo()
   11.58 +		{
   11.59 +			unit.Size = newSize;
   11.60 +		}
   11.61 +
   11.62 +		public override void Undo()
   11.63 +		{
   11.64 +			unit.Size = oldSize;
   11.65 +		}
   11.66 +
   11.67 +		public override string Name
   11.68 +		{
   11.69 +			get { return "Change unit size"; }
   11.70 +		}
   11.71 +	}
   11.72 +}
    12.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    12.2 +++ b/API/DefaultWarFoundryLoader.cs	Sun Apr 03 18:50:32 2011 +0000
    12.3 @@ -0,0 +1,304 @@
    12.4 +// This file (DefaultWarFoundryLoader.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    12.5 +// 
    12.6 +// 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.
    12.7 +
    12.8 +using System;
    12.9 +using System.Collections.Generic;
   12.10 +using IBBoard.WarFoundry.API.Objects;
   12.11 +
   12.12 +namespace IBBoard.WarFoundry.API
   12.13 +{
   12.14 +	/// <summary>
   12.15 +	/// The default implementation of a <see cref="AbstractWarFoundryLoader"/>
   12.16 +	/// </summary>
   12.17 +	public class DefaultWarFoundryLoader : AbstractWarFoundryLoader
   12.18 +	{
   12.19 +		private Dictionary<string, GameSystem> systemsTable;
   12.20 +		private Dictionary<string, Dictionary<string, Dictionary<string, Race>>> racesTable; //Keys are: System, Race, SubRace
   12.21 +		private bool loaded = false;
   12.22 +		private bool loading = false;
   12.23 +
   12.24 +		public DefaultWarFoundryLoader()
   12.25 +		{
   12.26 +			systemsTable = new Dictionary<string,GameSystem>();
   12.27 +			racesTable = new Dictionary<string,Dictionary<string,Dictionary<string,Race>>>();
   12.28 +		}
   12.29 +
   12.30 +		protected override void PrepareForFileLoad()
   12.31 +		{
   12.32 +			loading = true;
   12.33 +			systemsTable.Clear();
   12.34 +			racesTable.Clear();
   12.35 +		}
   12.36 +
   12.37 +		protected override void FinishFileLoad()
   12.38 +		{
   12.39 +			loaded = true;
   12.40 +			loading = false;
   12.41 +		}
   12.42 +
   12.43 +		protected override GameSystem GetExistingSystemForSystem(GameSystem system)
   12.44 +		{
   12.45 +			return DictionaryUtils.GetValue(systemsTable, system.ID.ToLower());
   12.46 +		}
   12.47 +
   12.48 +		protected override void DoStoreGameSystem(GameSystem system)
   12.49 +		{
   12.50 +			systemsTable[system.ID.ToLower()] = system;
   12.51 +		}
   12.52 +
   12.53 +		protected override void DoStoreRace(Race race)
   12.54 +		{
   12.55 +			Dictionary<string, Dictionary<string, Race>> systemRaces;
   12.56 +			
   12.57 +			string systemID = race.GameSystem.ID.ToLower();
   12.58 +			racesTable.TryGetValue(systemID, out systemRaces);
   12.59 +			
   12.60 +			if (systemRaces == null)
   12.61 +			{
   12.62 +				systemRaces = new Dictionary<string,Dictionary<string,Race>>();
   12.63 +				racesTable.Add(systemID, systemRaces);
   12.64 +			}
   12.65 +			
   12.66 +			Dictionary<string, Race> subRaces;
   12.67 +			systemRaces.TryGetValue(race.ID.ToLower(), out subRaces);
   12.68 +			
   12.69 +			if (subRaces == null)
   12.70 +			{
   12.71 +				subRaces = new Dictionary<string,Race>();
   12.72 +				systemRaces.Add(race.ID.ToLower(), subRaces);
   12.73 +			}
   12.74 +			
   12.75 +			string subID = race.SubID.ToLower();
   12.76 +
   12.77 +			if (subRaces.ContainsKey(subID))
   12.78 +			{
   12.79 +				Race existingRace = subRaces[subID];
   12.80 +				
   12.81 +				if (!race.Equals(existingRace))
   12.82 +				{
   12.83 +					//TODO: Raise an event to say we got a different duplicate
   12.84 +					//We can't just fail, because failing is for completely unhandled files, not for objects in a file
   12.85 +				}
   12.86 +			}
   12.87 +			else
   12.88 +			{
   12.89 +				subRaces.Add(race.SubID.ToLower(), race);
   12.90 +			}
   12.91 +		}
   12.92 +
   12.93 +		public override GameSystem[] GetGameSystems()
   12.94 +		{
   12.95 +			if (!loaded && !loading)
   12.96 +			{
   12.97 +				LoadFiles();
   12.98 +			}
   12.99 +			
  12.100 +			return DictionaryUtils.ToArray<string, GameSystem>(systemsTable);
  12.101 +		}
  12.102 +
  12.103 +		public override GameSystem GetGameSystem(string systemID)
  12.104 +		{
  12.105 +			if (!loaded && !loading)
  12.106 +			{
  12.107 +				LoadFiles();
  12.108 +			}
  12.109 +			
  12.110 +			GameSystem system;
  12.111 +			systemsTable.TryGetValue(systemID.ToLower(), out system);
  12.112 +			return system;
  12.113 +		}
  12.114 +
  12.115 +		protected internal override void RemoveGameSystem(GameSystem system)
  12.116 +		{
  12.117 +			systemsTable.Remove(system.ID.ToLower());
  12.118 +		}
  12.119 +
  12.120 +		public override Race[] GetRaces(GameSystem system)
  12.121 +		{
  12.122 +			return GetRaces(system.ID);
  12.123 +		}
  12.124 +
  12.125 +		/// <summary>
  12.126 +		/// Gets an array of the races for a game system by ID.
  12.127 +		/// </summary>
  12.128 +		/// <param name="systemID">
  12.129 +		/// The <see cref="System.String"/> ID of the game system to get races for
  12.130 +		/// </param>
  12.131 +		/// <returns>
  12.132 +		/// An array of <see cref="Race"/>s for the specified game system
  12.133 +		/// </returns>
  12.134 +		public Race[] GetRaces(string systemID)
  12.135 +		{
  12.136 +			if (!loaded && !loading)
  12.137 +			{
  12.138 +				LoadFiles();
  12.139 +			}
  12.140 +			
  12.141 +			systemID = systemID.ToLower();
  12.142 +			Dictionary<string, Dictionary<string, Race>> system;
  12.143 +			racesTable.TryGetValue(systemID, out system);
  12.144 +			
  12.145 +			if (system == null)
  12.146 +			{
  12.147 +				return new Race[0];
  12.148 +			}
  12.149 +
  12.150 +			int count = 0;
  12.151 +
  12.152 +			foreach (Dictionary<string, Race> racesDict in system.Values)
  12.153 +			{
  12.154 +				count += racesDict.Count;
  12.155 +			}
  12.156 +
  12.157 +			Race[] races = new Race[count];
  12.158 +			int i = 0;
  12.159 +
  12.160 +			foreach (string raceID in system.Keys)
  12.161 +			{
  12.162 +				foreach (string raceSubId in system[raceID].Keys)
  12.163 +				{
  12.164 +					races[i++] = GetRace(systemID, raceID, raceSubId);
  12.165 +				}
  12.166 +			}
  12.167 +
  12.168 +			return races;
  12.169 +		}
  12.170 +
  12.171 +		public override Race GetRace(GameSystem system, string raceID)
  12.172 +		{
  12.173 +			return GetRace(system.ID, raceID);
  12.174 +		}
  12.175 +
  12.176 +		/// <summary>
  12.177 +		/// Gets a single race for a given game system by ID of the game system and race.
  12.178 +		/// </summary>
  12.179 +		/// <param name="systemID">
  12.180 +		/// The <see cref="System.String"/> ID of the game system that the race is part of.
  12.181 +		/// </param>
  12.182 +		/// <param name="raceID">
  12.183 +		/// The <see cref="System.String"/> ID for the race to load.
  12.184 +		/// </param>
  12.185 +		/// <returns>
  12.186 +		/// A <see cref="Race"/> with the specified ID from the game system with the specified ID, or <code>null</code> if there is no race or game system with those IDs.
  12.187 +		/// </returns>
  12.188 +		public Race GetRace(string systemID, string raceID)
  12.189 +		{
  12.190 +			return GetRace(systemID, raceID, "");
  12.191 +		}
  12.192 +
  12.193 +		public override Race GetRace(GameSystem system, string raceID, string raceSubID)
  12.194 +		{
  12.195 +			return GetRace(system.ID, raceID, raceSubID);
  12.196 +		}
  12.197 +
  12.198 +		/// <summary>
  12.199 +		/// Gets a single race for a given game system by the game system's ID and the race's ID and sub-race ID.
  12.200 +		/// </summary>
  12.201 +		/// <param name="systemID">
  12.202 +		/// The <see cref="System.String"/> ID of the game system that the race is part of.
  12.203 +		/// </param>
  12.204 +		/// <param name="raceID">
  12.205 +		/// The <see cref="System.String"/> ID for the race to load.
  12.206 +		/// </param>
  12.207 +		/// <param name="raceSubID">
  12.208 +		/// A <see cref="System.String"/>
  12.209 +		/// </param>
  12.210 +		/// <returns>
  12.211 +		/// A <see cref="Race"/>
  12.212 +		/// </returns>
  12.213 +		public Race GetRace(string systemID, string raceID, string raceSubID)
  12.214 +		{
  12.215 +			if (!loaded && !loading)
  12.216 +			{
  12.217 +				LoadFiles();
  12.218 +			}
  12.219 +			
  12.220 +			Race race = null;
  12.221 +			
  12.222 +			Dictionary<string, Race> subraces = GetRaceTable(systemID, raceID);
  12.223 +
  12.224 +			if (subraces != null)
  12.225 +			{
  12.226 +				subraces.TryGetValue(raceSubID.ToLower(), out race);
  12.227 +			}
  12.228 +			
  12.229 +			return race;
  12.230 +		}
  12.231 +
  12.232 +		private Dictionary<string, Race> GetRaceTable(string systemID, string raceID)
  12.233 +		{
  12.234 +			Dictionary<string, Dictionary<string, Race>> races;
  12.235 +			racesTable.TryGetValue(systemID.ToLower(), out races);
  12.236 +			Dictionary<string, Race> subraces = null;
  12.237 +
  12.238 +			if (races != null)
  12.239 +			{
  12.240 +				races.TryGetValue(raceID.ToLower(), out subraces);
  12.241 +			}
  12.242 +
  12.243 +			return subraces;
  12.244 +		}
  12.245 +
  12.246 +		protected internal override void RemoveRace(Race race)
  12.247 +		{
  12.248 +			Dictionary<string, Race> subraces = GetRaceTable(race.GameSystem.ID, race.ID);
  12.249 +
  12.250 +			if (subraces != null)
  12.251 +			{
  12.252 +				subraces.Remove(race.SubID.ToLower());
  12.253 +			}
  12.254 +		}
  12.255 +
  12.256 +		public override string[] GetGameSystemIDs()
  12.257 +		{
  12.258 +			if (!loaded && !loading)
  12.259 +			{
  12.260 +				LoadFiles();
  12.261 +			}
  12.262 +
  12.263 +			return DictionaryUtils.ToKeyArray(systemsTable);
  12.264 +		}
  12.265 +
  12.266 +		public override string[] GetSystemRaceIDs(GameSystem system)
  12.267 +		{
  12.268 +			return GetSystemRaceIDs(system.ID);
  12.269 +		}
  12.270 +
  12.271 +		/// <summary>
  12.272 +		/// Gets the IDs of all of the races of a specified game system.
  12.273 +		/// </summary>
  12.274 +		/// <param name="systemID">
  12.275 +		/// The <see cref="System.String"/> ID of the game system to get the available races for.
  12.276 +		/// </param>
  12.277 +		/// <returns>
  12.278 +		/// An array of <see cref="System.String"/>s representing the IDs of the races of the specified game system.
  12.279 +		/// </returns>
  12.280 +		public string[] GetSystemRaceIDs(string systemID)
  12.281 +		{
  12.282 +			if (!loaded && !loading)
  12.283 +			{
  12.284 +				LoadFiles();
  12.285 +			}
  12.286 +
  12.287 +			Dictionary<string, Dictionary<string, Race>> races = racesTable[systemID.ToLower()];
  12.288 +
  12.289 +			if (races == null)
  12.290 +			{
  12.291 +				return new string[0];
  12.292 +			}
  12.293 +			else
  12.294 +			{
  12.295 +				string[] keys = new string[races.Keys.Count];
  12.296 +				int i = 0;
  12.297 +
  12.298 +				foreach (string key in races.Keys)
  12.299 +				{
  12.300 +					keys[i++] = key;
  12.301 +				}
  12.302 +
  12.303 +				return keys;
  12.304 +			}
  12.305 +		}
  12.306 +	}
  12.307 +}
    13.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    13.2 +++ b/API/Delegates.cs	Sun Apr 03 18:50:32 2011 +0000
    13.3 @@ -0,0 +1,23 @@
    13.4 +// This file (Delegates.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    13.5 +//
    13.6 +// 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.
    13.7 +
    13.8 +using System;
    13.9 +using System.Collections.Generic;
   13.10 +using IBBoard.WarFoundry.API.Objects;
   13.11 +
   13.12 +namespace IBBoard.WarFoundry.API
   13.13 +{
   13.14 +	public delegate void ObjectChangedDelegate(WarFoundryObject oldValue, WarFoundryObject newValue);
   13.15 +	public delegate void ArmyChangedDelegate(Army oldValue, Army newValue);
   13.16 +	public delegate void GameSystemChangedDelegate(GameSystem oldValue, GameSystem newValue);
   13.17 +	public delegate void ObjectAddDelegate(WarFoundryObject val);
   13.18 +	public delegate void ObjectRemoveDelegate(WarFoundryObject val);	
   13.19 +	public delegate void UnitAddDelegate(Unit val);
   13.20 +	public delegate void UnitRemoveDelegate(Unit val);
   13.21 +	public delegate void ObjectUpdatedDelegate(WarFoundryObject val, string updatedValName);
   13.22 +	public delegate void DoubleValChangedDelegate(WarFoundryObject obj, double oldValue, double newValue);
   13.23 +	public delegate void FloatValChangedDelegate(WarFoundryObject obj, float oldValue, float newValue);
   13.24 +	public delegate void StringValChangedDelegate(WarFoundryObject obj, string oldValue, string newValue);
   13.25 +	public delegate void IntValChangedDelegate(WarFoundryObject obj, int oldValue, int newValue);
   13.26 +}
    14.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    14.2 +++ b/API/Exporters/IWarFoundryExporter.cs	Sun Apr 03 18:50:32 2011 +0000
    14.3 @@ -0,0 +1,26 @@
    14.4 +// This file (IWarFoundryExporter.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    14.5 +// 
    14.6 +// 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.
    14.7 +
    14.8 +using System;
    14.9 +using IBBoard.WarFoundry.API.Objects;
   14.10 +
   14.11 +namespace IBBoard.WarFoundry.API.Exporters
   14.12 +{
   14.13 +	/// <summary>
   14.14 +	/// An interface to be implemented by classes that export WarFoundry armies to other formats than "saved armies" (e.g. HTML)
   14.15 +	/// </summary>
   14.16 +	public interface IWarFoundryExporter
   14.17 +	{
   14.18 +		/// <summary>
   14.19 +		/// Exports the army to the specified path
   14.20 +		/// </summary>
   14.21 +		/// <param name="army">
   14.22 +		/// The <see cref="Army"/> to export
   14.23 +		/// </param>
   14.24 +		/// <param name="path">
   14.25 +		/// The file path to export to
   14.26 +		/// </param>
   14.27 +		void ExportArmy(Army army, string path);
   14.28 +	}
   14.29 +}
    15.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    15.2 +++ b/API/Exporters/WarFoundryHtmlExporter.cs	Sun Apr 03 18:50:32 2011 +0000
    15.3 @@ -0,0 +1,396 @@
    15.4 +// This file (WarFoundryHtmlExporter.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    15.5 +// 
    15.6 +// 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.
    15.7 +
    15.8 +using System;
    15.9 +using System.Collections.Generic;
   15.10 +using System.IO;
   15.11 +using System.Text;
   15.12 +using System.Xml;
   15.13 +using System.Xml.Schema;
   15.14 +using IBBoard.Lang;
   15.15 +using IBBoard.Xml;
   15.16 +using IBBoard.WarFoundry.API.Objects;
   15.17 +using IBBoard.WarFoundry.API.Util;
   15.18 +
   15.19 +namespace IBBoard.WarFoundry.API.Exporters
   15.20 +{
   15.21 +	/// <summary>
   15.22 +	/// Custom exporter that exports an army as a basic HTML file
   15.23 +	/// </summary>
   15.24 +	public class WarFoundryHtmlExporter : IWarFoundryExporter
   15.25 +	{
   15.26 +		private static WarFoundryHtmlExporter exporter;
   15.27 +		private delegate string GetStatCellTextDelegate(Stat stat);
   15.28 +		
   15.29 +		public static WarFoundryHtmlExporter GetDefault()
   15.30 +		{
   15.31 +			if (exporter == null)
   15.32 +			{
   15.33 +				exporter = new WarFoundryHtmlExporter();
   15.34 +			}
   15.35 +			
   15.36 +			return exporter;
   15.37 +		}
   15.38 +		
   15.39 +		private WarFoundryHtmlExporter()
   15.40 +		{
   15.41 +			//Hide constructor
   15.42 +		}
   15.43 +		
   15.44 +		public void ExportArmy(Army army, string path)
   15.45 +		{
   15.46 +			XmlDocument doc = new XmlDocument();
   15.47 +			CustomXmlResolver resolver = new CustomXmlResolver();
   15.48 +			resolver.AddMapping("-//W3C//DTD XHTML 1.0 Strict//EN", new Uri("file://" + IBBoard.Constants.ExecutablePath + "/schemas/xhtml1-strict.dtd"));
   15.49 +			doc.XmlResolver = resolver;
   15.50 +			doc.AppendChild(doc.CreateDocumentType("html", "-//W3C//DTD XHTML 1.0 Strict//EN", "http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd", null));
   15.51 +			XmlElement html = doc.CreateElement("html");
   15.52 +			doc.AppendChild(html);
   15.53 +			XmlElement head = doc.CreateElement("head");
   15.54 +			html.AppendChild(head);
   15.55 +			XmlElement title = doc.CreateElement("title");
   15.56 +			title.InnerXml = army.Name;
   15.57 +			head.AppendChild(title);
   15.58 +			XmlElement metaCharset = doc.CreateElement("meta");
   15.59 +			metaCharset.SetAttribute("http-equiv", "Content-Type");
   15.60 +			metaCharset.SetAttribute("content", "text/html;charset=UTF-8");
   15.61 +			head.AppendChild(metaCharset);
   15.62 +			XmlElement style = doc.CreateElement("style");
   15.63 +			style.InnerText = "table, th, td { border: 1px solid #000; border-spacing: 0; border-collapse: collapse; margin: 0 }\n"
   15.64 +				+"table table { width: 100%; border-width: 0; margin: -2px }\n"
   15.65 +				+"table table td { border-width:0 1px }";
   15.66 +			head.AppendChild(style);
   15.67 +			XmlElement body = doc.CreateElement("body");
   15.68 +			html.AppendChild(body);
   15.69 +			XmlElement header = doc.CreateElement("h1");
   15.70 +			header.InnerText = Translation.GetTranslation("armyHtmlOutputBodyHeader", "{0} - {1}pts", army.Name, army.Points);
   15.71 +			body.AppendChild(header);
   15.72 +			
   15.73 +			foreach (XmlElement table in CreateTables(army, doc))
   15.74 +			{
   15.75 +				if (!IsTableOnlyHeader(table))
   15.76 +				{
   15.77 +					body.AppendChild(table);
   15.78 +				}
   15.79 +			}
   15.80 +			
   15.81 +			StreamWriter writer = new StreamWriter(path, false);
   15.82 +			
   15.83 +			try
   15.84 +			{
   15.85 +				writer.Write(doc.OuterXml);
   15.86 +			}
   15.87 +			finally
   15.88 +			{
   15.89 +				writer.Close();
   15.90 +			}
   15.91 +		}
   15.92 +
   15.93 +		private bool IsTableOnlyHeader(XmlElement table)
   15.94 +		{
   15.95 +			return table.ChildNodes.Count == 1;
   15.96 +		}
   15.97 +		
   15.98 +		private XmlElement[] CreateTables(Army army, XmlDocument doc)
   15.99 +		{
  15.100 +			Dictionary<string, XmlElement> tables = new Dictionary<string, XmlElement>();
  15.101 +			
  15.102 +			foreach (SystemStats statSets in army.GameSystem.SystemStats)
  15.103 +			{
  15.104 +				tables[statSets.ID] = CreateTable(statSets, doc);
  15.105 +			}
  15.106 +			
  15.107 +			foreach (Unit unit in army.GetUnits())
  15.108 +			{
  15.109 +				CreateUnitRow(unit, tables[GetFirstStatType(unit)]);
  15.110 +			}
  15.111 +			
  15.112 +			return DictionaryUtils.ToArray(tables);
  15.113 +		}
  15.114 +
  15.115 +		private static string GetFirstStatType(Unit unit)
  15.116 +		{
  15.117 +			string[] unitStatIDs = unit.UnitStatsArrayIDs;
  15.118 +			return GetFirstStatType(unitStatIDs);
  15.119 +		}
  15.120 +		
  15.121 +		public static string GetFirstStatType(string[] unitStatIDs)
  15.122 +		{
  15.123 +			return unitStatIDs[0];
  15.124 +		}
  15.125 +		
  15.126 +		private XmlElement CreateTable(SystemStats stats, XmlDocument doc)
  15.127 +		{
  15.128 +			XmlElement table = doc.CreateElement("table");
  15.129 +			XmlElement headerRow = doc.CreateElement("tr");
  15.130 +			table.AppendChild(headerRow);
  15.131 +			XmlElement name = doc.CreateElement("th");
  15.132 +			name.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitName", "name");
  15.133 +			headerRow.AppendChild(name);
  15.134 +			
  15.135 +			XmlElement unitTypeName = doc.CreateElement("th");
  15.136 +			unitTypeName.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitTypeName", "type name");
  15.137 +			headerRow.AppendChild(unitTypeName);
  15.138 +			
  15.139 +			foreach (StatSlot stat in stats.StatSlots)
  15.140 +			{
  15.141 +				XmlElement statHeader = doc.CreateElement("th");
  15.142 +				statHeader.InnerText = stat.Name;
  15.143 +				headerRow.AppendChild(statHeader);
  15.144 +			}
  15.145 +			
  15.146 +			XmlElement notes = doc.CreateElement("th");
  15.147 +			notes.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitNotes", "name");;
  15.148 +			headerRow.AppendChild(notes);
  15.149 +			
  15.150 +			XmlElement points = doc.CreateElement("th");
  15.151 +			points.InnerText = Translation.GetTranslation("armyHtmlOutputTableHeaderUnitPoints", "name");;
  15.152 +			headerRow.AppendChild(points);
  15.153 +			
  15.154 +			return table;
  15.155 +		}
  15.156 +		
  15.157 +		private XmlElement CreateUnitRow(Unit unit, XmlElement tableElem)
  15.158 +		{
  15.159 +			XmlDocument doc = tableElem.OwnerDocument;
  15.160 +			XmlElement row = doc.CreateElement("tr");
  15.161 +			tableElem.AppendChild(row);
  15.162 +			Stat[][] memberStats = unit.UnitStatsArraysWithName;
  15.163 +			string[] statTypeIDs = unit.UnitStatsArrayIDs;
  15.164 +			string defaultStatType = GetFirstStatType(statTypeIDs);
  15.165 +			int statRowCount = 0;
  15.166 +			bool hasOther = false;
  15.167 +			
  15.168 +			foreach (string statTypeID in statTypeIDs)
  15.169 +			{
  15.170 +				if (statTypeID.Equals(defaultStatType))
  15.171 +				{
  15.172 +					statRowCount++;
  15.173 +				}
  15.174 +				else if (!hasOther)
  15.175 +				{
  15.176 +					statRowCount++;
  15.177 +					hasOther = true;
  15.178 +				}
  15.179 +			}
  15.180 +			
  15.181 +			XmlElement name = doc.CreateElement("td");
  15.182 +			name.InnerText = unit.Name;
  15.183 +			SetRowSpan(name, statRowCount);
  15.184 +			row.AppendChild(name);
  15.185 +			CreateStatsBlock(row, memberStats, statTypeIDs);
  15.186 +
  15.187 +			StringBuilder sb = new StringBuilder();
  15.188 +			UnitEquipmentItem[] unitEquipment = unit.GetEquipment();
  15.189 +			
  15.190 +			if (unitEquipment.Length > 0)
  15.191 +			{
  15.192 +				bool addSeparator = false;
  15.193 +				
  15.194 +				foreach (UnitEquipmentItem equip in unitEquipment)
  15.195 +				{
  15.196 +					if (!addSeparator)
  15.197 +					{
  15.198 +						addSeparator = true;
  15.199 +					}
  15.200 +					else
  15.201 +					{
  15.202 +						sb.Append(", ");
  15.203 +					}
  15.204 +
  15.205 +					string amountString;
  15.206 +					double amount = UnitEquipmentUtil.GetEquipmentAmount(unit, equip);
  15.207 +
  15.208 +					if (UnitEquipmentUtil.GetEquipmentAmountIsRatio(unit, equip))
  15.209 +					{
  15.210 +
  15.211 +						if (amount == 100)
  15.212 +						{
  15.213 +							amountString = GetEquipmentAmountAllTranslation(unit);
  15.214 +						}
  15.215 +						else
  15.216 +						{
  15.217 +							int number = UnitEquipmentUtil.GetEquipmentAmountTaken(unit, equip);
  15.218 +							amountString = GetEquipmentAmountRatioTranslation(amount, number);
  15.219 +						}
  15.220 +					}
  15.221 +					else
  15.222 +					{
  15.223 +						if (amount == -1)
  15.224 +						{
  15.225 +							amountString = GetEquipmentAmountAllTranslation(unit);
  15.226 +						}
  15.227 +						else
  15.228 +						{
  15.229 +							amountString = GetEquipmentAmountNumberTranslation((int)amount);
  15.230 +						}
  15.231 +					}
  15.232 +
  15.233 +					sb.Append(Translation.GetTranslation("armyHtmlExportEquipAmountRatio", "{0} for {1}", equip.Name, amountString));
  15.234 +				}
  15.235 +				
  15.236 +				sb.Append(". ");
  15.237 +			}
  15.238 +			
  15.239 +			ICollection<Ability> abilities = unit.Abilities;
  15.240 +			
  15.241 +			if (abilities.Count > 0)
  15.242 +			{
  15.243 +				bool addSeparator = false;
  15.244 +				
  15.245 +				foreach (Ability ability in abilities)
  15.246 +				{
  15.247 +					if (!addSeparator)
  15.248 +					{
  15.249 +						addSeparator = true;
  15.250 +					}
  15.251 +					else
  15.252 +					{
  15.253 +						sb.Append(", ");
  15.254 +					}
  15.255 +					
  15.256 +					sb.Append(ability.Name);
  15.257 +				}
  15.258 +				
  15.259 +				sb.Append(". ");
  15.260 +			}
  15.261 +			
  15.262 +			XmlElement notes = doc.CreateElement("td");
  15.263 +			notes.InnerText = sb.ToString();
  15.264 +			SetRowSpan(notes, statRowCount);
  15.265 +			row.AppendChild(notes);
  15.266 +			
  15.267 +			XmlElement points = doc.CreateElement("td");
  15.268 +			points.InnerText = unit.Points.ToString();
  15.269 +			SetRowSpan(points, statRowCount);
  15.270 +			row.AppendChild(points);
  15.271 +			
  15.272 +			return row;
  15.273 +		}
  15.274 +		
  15.275 +		private static void SetRowSpan(XmlElement xmlElement, int statRowCount)
  15.276 +		{
  15.277 +			if (statRowCount > 1)
  15.278 +			{
  15.279 +				xmlElement.SetAttribute("rowspan", statRowCount.ToString());
  15.280 +			}
  15.281 +		}
  15.282 +		
  15.283 +		private void CreateStatsBlock(XmlElement unitRow, Stat[][] memberStats, string[] statTypeIDs)
  15.284 +		{
  15.285 +			XmlDocument doc = unitRow.OwnerDocument;
  15.286 +			string defaultStatType = GetFirstStatType(statTypeIDs);
  15.287 +			
  15.288 +			Stat[] defaultStatLine = memberStats[0];
  15.289 +			int defaultStatLineCount = defaultStatLine.Length;
  15.290 +			AddStatCell(defaultStatLine[0].SlotValueString, unitRow);
  15.291 +			
  15.292 +			for (int i = 1; i < defaultStatLineCount; i++)
  15.293 +			{
  15.294 +				string statText = GetDefaultStatCellText(defaultStatLine[i]);
  15.295 +				AddStatCell(statText, unitRow);
  15.296 +			}
  15.297 +			
  15.298 +			int statCount = statTypeIDs.Length;
  15.299 +			
  15.300 +			if (statCount > 1)
  15.301 +			{
  15.302 +				XmlElement unitTable = (XmlElement)unitRow.ParentNode;
  15.303 +				Dictionary<string, XmlElement> statParents = CreateStatsParentElements(statTypeIDs, unitTable);
  15.304 +				
  15.305 +				for (int i = 1; i < statCount; i++)
  15.306 +				{
  15.307 +					Stat[] statLine = memberStats[i];
  15.308 +					string statTypeID = statTypeIDs[i];
  15.309 +					XmlElement tableElement = DictionaryUtils.GetValue(statParents, statTypeID);
  15.310 +					int statLineCount = statLine.Length;
  15.311 +					XmlElement statRow = doc.CreateElement("tr");
  15.312 +					tableElement.AppendChild(statRow);
  15.313 +					GetStatCellTextDelegate statCellTextDelegate = (statTypeID.Equals(defaultStatType) ? new GetStatCellTextDelegate(GetDefaultStatCellText) : new GetStatCellTextDelegate(GetOtherStatCellText));
  15.314 +					AddStatCell(statLine[0].SlotValueString, statRow);
  15.315 +				
  15.316 +					for (int j = 1; j < statLineCount; j++)
  15.317 +					{
  15.318 +						string statText = statCellTextDelegate(statLine[j]);
  15.319 +						AddStatCell(statText, statRow);
  15.320 +					}
  15.321 +				}
  15.322 +				
  15.323 +				if (statParents.Count > 1)
  15.324 +				{
  15.325 +					AddOtherUnitStatTables(statParents, unitTable, defaultStatLineCount);
  15.326 +				}
  15.327 +			}
  15.328 +		}
  15.329 +		
  15.330 +		private static void AddOtherUnitStatTables(Dictionary<string, XmlElement> statParents, XmlElement unitTable, int defaultStatLineCount)
  15.331 +		{
  15.332 +			XmlDocument doc = unitTable.OwnerDocument;
  15.333 +			XmlElement otherStatsRow = doc.CreateElement("tr");
  15.334 +			unitTable.AppendChild(otherStatsRow);
  15.335 +			XmlElement otherStatsCell = doc.CreateElement("td");
  15.336 +			otherStatsCell.SetAttribute("colspan", defaultStatLineCount.ToString());
  15.337 +			otherStatsRow.AppendChild(otherStatsCell);
  15.338 +			
  15.339 +			foreach (XmlElement tableElem in statParents.Values)
  15.340 +			{
  15.341 +				if (tableElem != unitTable)
  15.342 +				{
  15.343 +					otherStatsCell.AppendChild(tableElem);
  15.344 +				}
  15.345 +			}
  15.346 +		}
  15.347 +
  15.348 +		private Dictionary<string, XmlElement> CreateStatsParentElements(string[] statTypeIDs, XmlElement parentTable)
  15.349 +		{
  15.350 +			Dictionary<string, XmlElement> statParents = new Dictionary<string, XmlElement>();
  15.351 +			XmlDocument doc = parentTable.OwnerDocument;
  15.352 +			string defaultStatTypeID = GetFirstStatType(statTypeIDs);
  15.353 +			statParents[defaultStatTypeID] = parentTable;
  15.354 +			
  15.355 +			foreach (string statTypeID in statTypeIDs)
  15.356 +			{
  15.357 +				if (!statParents.ContainsKey(statTypeID))
  15.358 +				{
  15.359 +					XmlElement tableElement = doc.CreateElement("table");
  15.360 +					statParents[statTypeID] = tableElement;
  15.361 +				}
  15.362 +			}
  15.363 +			
  15.364 +			return statParents;
  15.365 +		}
  15.366 +
  15.367 +		private string GetDefaultStatCellText(Stat stat)
  15.368 +		{
  15.369 +			return Translation.GetTranslation("armyHtmlExportDefaultStatCellText", "{0}", stat.SlotValueString, stat.ParentSlotName);
  15.370 +		}
  15.371 +
  15.372 +		private string GetOtherStatCellText(Stat stat)
  15.373 +		{
  15.374 +			return Translation.GetTranslation("armyHtmlExportOtherStatCellText", "{1}: {0}", stat.SlotValueString, stat.ParentSlotName);
  15.375 +		}
  15.376 +		
  15.377 +		private static void AddStatCell(string statValue, XmlElement row)
  15.378 +		{
  15.379 +			XmlElement statCell = row.OwnerDocument.CreateElement("td");
  15.380 +			statCell.InnerText = statValue;
  15.381 +			row.AppendChild(statCell);
  15.382 +		}
  15.383 +		
  15.384 +		private string GetEquipmentAmountRatioTranslation (double amount, int number)
  15.385 +		{
  15.386 +			return Translation.GetTranslation ("armyHtmlExportEquipAmountPercentage", "{0}% ({1})", amount, number);
  15.387 +		}
  15.388 +		
  15.389 +		private string GetEquipmentAmountNumberTranslation(int amount)
  15.390 +		{
  15.391 +			return Translation.GetTranslation("armyHtmlExportEquipAmountNumber", "{0}", amount);
  15.392 +		}
  15.393 +		
  15.394 +		private string GetEquipmentAmountAllTranslation(Unit unit)
  15.395 +		{
  15.396 +			return Translation.GetTranslation("armyHtmlExportEquipAmountAll", "all ({1})", 100, unit.Size);
  15.397 +		}
  15.398 +	}
  15.399 +}
    16.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    16.2 +++ b/API/Factories/AbstractNativeWarFoundryFactory.cs	Sun Apr 03 18:50:32 2011 +0000
    16.3 @@ -0,0 +1,194 @@
    16.4 +// This file (AbstractNativeWarFoundryFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    16.5 +//
    16.6 +// 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.
    16.7 +
    16.8 +using System;
    16.9 +using System.IO;
   16.10 +using System.Xml;
   16.11 +using System.Xml.Schema;
   16.12 +using System.Collections.Generic;
   16.13 +using System.Text;
   16.14 +using IBBoard;
   16.15 +using IBBoard.IO;
   16.16 +using IBBoard.Lang;
   16.17 +using IBBoard.Logging;
   16.18 +using IBBoard.Xml;
   16.19 +using IBBoard.WarFoundry.API.Objects;
   16.20 +using ICSharpCode.SharpZipLib.Zip;
   16.21 +
   16.22 +namespace IBBoard.WarFoundry.API.Factories
   16.23 +{
   16.24 +	/// <summary>
   16.25 +	/// Base abstract class for all factories that load native WarFoundry data.
   16.26 +	/// </summary>
   16.27 +	public abstract class AbstractNativeWarFoundryFactory : AbstractWarFoundryFactory<ZipFile>, INativeWarFoundryFactory
   16.28 +	{				
   16.29 +		protected AbstractNativeWarFoundryFactory()
   16.30 +		{
   16.31 +			//Do nothing - just make the constructor non-public
   16.32 +		}
   16.33 +				
   16.34 +		protected override ZipFile GetFileAsSupportedType (FileInfo file)
   16.35 +		{
   16.36 +			ZipFile zip = null;
   16.37 +			
   16.38 +			try
   16.39 +			{
   16.40 +				zip = new ZipFile(file.FullName);
   16.41 +			}
   16.42 +			catch(ZipException)
   16.43 +			{
   16.44 +				//Silently dispose as per spec for the method
   16.45 +			}
   16.46 +			catch (IOException)
   16.47 +			{
   16.48 +				//Silently dispose as per spec for the method
   16.49 +			}
   16.50 +			
   16.51 +			return zip;
   16.52 +		}
   16.53 +		
   16.54 +		protected override bool CheckCanHandleFileFormat (ZipFile file)
   16.55 +		{	
   16.56 +			return CheckCanHandleFileAsGameSystem(file) || CheckCanHandleFileAsRace(file) || CheckCanHandleFileAsArmy(file); 
   16.57 +		}
   16.58 +		
   16.59 +		protected override bool CheckCanHandleFileAsGameSystem(ZipFile file)
   16.60 +		{
   16.61 +			return CheckCanFindSystemFileContent(file);
   16.62 +		}
   16.63 +		
   16.64 +		protected abstract bool CheckCanFindSystemFileContent(ZipFile file);
   16.65 +		
   16.66 +		protected override bool CheckCanHandleFileAsRace(ZipFile file)
   16.67 +		{
   16.68 +			return CheckCanFindRaceFileContent(file);
   16.69 +		}
   16.70 +		
   16.71 +		protected abstract bool CheckCanFindRaceFileContent(ZipFile file);
   16.72 +		
   16.73 +		protected override bool CheckCanHandleFileAsArmy(ZipFile file)
   16.74 +		{
   16.75 +			return CheckCanFindArmyFileContent(file);
   16.76 +		}
   16.77 +		
   16.78 +		protected abstract bool CheckCanFindArmyFileContent(ZipFile file);
   16.79 +		
   16.80 +		protected override ICollection<IWarFoundryObject> DoCreateObjectsFromFile (ZipFile file)
   16.81 +		{
   16.82 +			ICollection<IWarFoundryObject> objects = new List<IWarFoundryObject>();
   16.83 +
   16.84 +			try
   16.85 +			{
   16.86 +				if (CheckCanFindSystemFileContent(file))
   16.87 +				{
   16.88 +					foreach (GameSystem system in CreateGameSystemsFromFile(file))
   16.89 +					{
   16.90 +						OnGameSystemLoaded(system);
   16.91 +						objects.Add(system);
   16.92 +					}
   16.93 +				}
   16.94 +				
   16.95 +				if (CheckCanFindRaceFileContent(file))
   16.96 +				{
   16.97 +					foreach(Race race in CreateRacesFromFile(file))
   16.98 +					{
   16.99 +						OnRaceLoaded(race);
  16.100 +						objects.Add(race);
  16.101 +					}
  16.102 +				}
  16.103 +				
  16.104 +				if (CheckCanFindArmyFileContent(file))
  16.105 +				{
  16.106 +					foreach (Army army in CreateArmiesFromFile(file))
  16.107 +					{
  16.108 +						OnArmyLoaded(army);
  16.109 +						objects.Add(army);
  16.110 +					}
  16.111 +				}
  16.112 +			}
  16.113 +			finally
  16.114 +			{
  16.115 +				file.Close();
  16.116 +			}
  16.117 +			
  16.118 +			return objects;
  16.119 +		}
  16.120 +		
  16.121 +		protected ICollection<Army> CreateArmiesFromFile(ZipFile file)
  16.122 +		{
  16.123 +			ICollection<ZipEntry> dataStreams = GetArmyZipEntries(file);
  16.124 +			ICollection<Army> armies = new List<Army>();
  16.125 +
  16.126 +			foreach (ZipEntry entry in dataStreams)
  16.127 +			{
  16.128 +				using (Stream dataStream = file.GetInputStream(entry))
  16.129 +				{
  16.130 +				armies.Add(CreateArmyFromStream(file, dataStream));
  16.131 +				}
  16.132 +			}
  16.133 +			
  16.134 +			return armies;
  16.135 +		}
  16.136 +		
  16.137 +		protected abstract ICollection<ZipEntry> GetArmyZipEntries(ZipFile file);
  16.138 +		protected abstract Army CreateArmyFromStream(ZipFile file, Stream dataStream);
  16.139 +		
  16.140 +		protected ICollection<Race> CreateRacesFromFile(ZipFile file)
  16.141 +		{
  16.142 +			ICollection<ZipEntry> dataStreams = GetRaceZipEntries(file);
  16.143 +			ICollection<Race> races = new List<Race>();
  16.144 +
  16.145 +			foreach (ZipEntry entry in dataStreams)
  16.146 +			{					
  16.147 +				using (Stream dataStream = file.GetInputStream(entry))
  16.148 +				{
  16.149 +					races.Add(CreateRaceFromStream(file, dataStream));
  16.150 +				}
  16.151 +			}
  16.152 +			
  16.153 +			return races;
  16.154 +		}
  16.155 +		
  16.156 +		protected abstract ICollection<ZipEntry> GetRaceZipEntries(ZipFile file);
  16.157 +		protected abstract Race CreateRaceFromStream(ZipFile file, Stream dataStream);
  16.158 +		
  16.159 +		protected ICollection<GameSystem> CreateGameSystemsFromFile(ZipFile file)
  16.160 +		{
  16.161 +			ICollection<ZipEntry> dataStreams = GetGameSystemZipEntries(file);
  16.162 +			ICollection<GameSystem> systems = new List<GameSystem>();
  16.163 +
  16.164 +			foreach (ZipEntry entry in dataStreams)
  16.165 +			{
  16.166 +				using (Stream dataStream = file.GetInputStream(entry))
  16.167 +				{
  16.168 +					systems.Add(CreateGameSystemFromStream(file, dataStream));
  16.169 +				}
  16.170 +			}
  16.171 +			
  16.172 +			return systems;
  16.173 +		}
  16.174 +		
  16.175 +		protected abstract ICollection<ZipEntry> GetGameSystemZipEntries(ZipFile file);
  16.176 +		protected abstract GameSystem CreateGameSystemFromStream(ZipFile file, Stream dataStream);
  16.177 +		
  16.178 +		public override bool Equals (object o)
  16.179 +		{
  16.180 +			if (o == this)
  16.181 +			{
  16.182 +				return true;
  16.183 +			}
  16.184 +			else if (o == null || !(this.GetType().Equals(o.GetType())))
  16.185 +			{
  16.186 +				return false;
  16.187 +			}
  16.188 +			
  16.189 +			return true;
  16.190 +		}
  16.191 +		
  16.192 +		public override int GetHashCode ()
  16.193 +		{
  16.194 +			return GetType().FullName.GetHashCode();
  16.195 +		}
  16.196 +	}
  16.197 +}
    17.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    17.2 +++ b/API/Factories/AbstractNonNativeFileExtensionWarFoundryFactory.cs	Sun Apr 03 18:50:32 2011 +0000
    17.3 @@ -0,0 +1,85 @@
    17.4 +// This file (AbstractNonNativeFileExtensionWarFoundryFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    17.5 +//
    17.6 +// 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.
    17.7 +
    17.8 +using System;
    17.9 +using System.Collections.Generic;
   17.10 +using System.IO;
   17.11 +using IBBoard.Logging;
   17.12 +using IBBoard.WarFoundry.API.Objects;
   17.13 +
   17.14 +namespace IBBoard.WarFoundry.API.Factories
   17.15 +{	
   17.16 +	public abstract class AbstractNonNativeFileExtensionWarFoundryFactory : AbstractNonNativeWarFoundryFactory<FileInfo>
   17.17 +	{
   17.18 +		protected abstract string ArmyFileExtension { get; }
   17.19 +		protected abstract string RaceFileExtension { get; }
   17.20 +		protected abstract string GameSystemFileExtension { get; }
   17.21 +		
   17.22 +		protected override bool CheckCanHandleFileFormat (FileInfo file)
   17.23 +		{
   17.24 +			return CheckCanHandleFileAsArmy(file) || CheckCanHandleFileAsRace(file) || CheckCanHandleFileAsGameSystem(file);
   17.25 +		}
   17.26 +		
   17.27 +		protected override bool CheckCanHandleFileAsArmy(FileInfo file)
   17.28 +		{
   17.29 +			return ArmyFileExtension!=null && file.Name.ToLower().EndsWith(ArmyFileExtension);
   17.30 +		}
   17.31 +		
   17.32 +		protected override bool CheckCanHandleFileAsRace(FileInfo file)
   17.33 +		{
   17.34 +			return RaceFileExtension!=null && file.Name.ToLower().EndsWith(RaceFileExtension);
   17.35 +		}
   17.36 +		
   17.37 +		protected override bool CheckCanHandleFileAsGameSystem(FileInfo file)
   17.38 +		{
   17.39 +			return GameSystemFileExtension!=null && file.Name.ToLower().EndsWith(GameSystemFileExtension);
   17.40 +		}
   17.41 +		
   17.42 +		protected override FileInfo GetFileAsSupportedType (FileInfo file)
   17.43 +		{
   17.44 +			return file;
   17.45 +		}		
   17.46 +		
   17.47 +		protected abstract Army CreateArmyFromFile(FileInfo file);
   17.48 +		protected abstract Race CreateRaceFromFile(FileInfo file);
   17.49 +		protected abstract GameSystem CreateGameSystemFromFile(FileInfo file);
   17.50 +		
   17.51 +		protected override ICollection<IWarFoundryObject> DoCreateObjectsFromFile (FileInfo file)
   17.52 +		{
   17.53 +			IWarFoundryObject obj = null;
   17.54 +			
   17.55 +			if (CheckCanHandleFileAsGameSystem(file))
   17.56 +			{
   17.57 +				GameSystem gameSystem = CreateGameSystemFromFile (file);
   17.58 +				OnGameSystemLoaded(gameSystem);
   17.59 +				obj = gameSystem;
   17.60 +			}
   17.61 +			else if (CheckCanHandleFileAsRace(file))
   17.62 +			{
   17.63 +				Race race = CreateRaceFromFile (file);
   17.64 +				OnRaceLoaded(race);
   17.65 +				obj = race;
   17.66 +			}
   17.67 +			else if (CheckCanHandleFileAsArmy(file))
   17.68 +			{
   17.69 +				Army army = CreateArmyFromFile (file);
   17.70 +				OnArmyLoaded(army);
   17.71 +				obj = army;
   17.72 +			}
   17.73 +			else
   17.74 +			{
   17.75 +				LogNotifier.Warn(GetType(), "Failed trying to create from "+file.FullName+" - not a Race, Army or GameSystem");
   17.76 +			}
   17.77 +			
   17.78 +			ICollection<IWarFoundryObject> objects = new List<IWarFoundryObject>();
   17.79 +			
   17.80 +			if (obj != null)
   17.81 +			{
   17.82 +				objects.Add(obj);
   17.83 +			}
   17.84 +			
   17.85 +			return objects;
   17.86 +		}
   17.87 +	}
   17.88 +}
    18.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    18.2 +++ b/API/Factories/AbstractNonNativeWarFoundryFactory.cs	Sun Apr 03 18:50:32 2011 +0000
    18.3 @@ -0,0 +1,15 @@
    18.4 +// This file (AbstractNonNativeWarFoundryFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    18.5 +//
    18.6 +// 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.
    18.7 +
    18.8 +using System;
    18.9 +using System.IO;
   18.10 +using IBBoard.WarFoundry.API.Objects;
   18.11 +
   18.12 +namespace IBBoard.WarFoundry.API.Factories
   18.13 +{
   18.14 +	public abstract class AbstractNonNativeWarFoundryFactory<FILE_TYPE> : AbstractWarFoundryFactory<FILE_TYPE>, INonNativeWarFoundryFactory
   18.15 +	{
   18.16 +		public abstract string NonNativeDataType { get;	}
   18.17 +	}
   18.18 +}
    19.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    19.2 +++ b/API/Factories/AbstractWarFoundryFactory.cs	Sun Apr 03 18:50:32 2011 +0000
    19.3 @@ -0,0 +1,181 @@
    19.4 +// This file (AbstractWarFoundryFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    19.5 +//
    19.6 +// 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.
    19.7 +
    19.8 +using System;
    19.9 +using System.IO;
   19.10 +using System.Collections.Generic;
   19.11 +using IBBoard.WarFoundry.API.Objects;
   19.12 +
   19.13 +namespace IBBoard.WarFoundry.API.Factories
   19.14 +{
   19.15 +	public abstract class AbstractWarFoundryFactory<FILE_TYPE> : IWarFoundryFactory
   19.16 +	{		
   19.17 +		public event SingleArgMethodInvoker<GameSystem> GameSystemLoaded;
   19.18 +		
   19.19 +		public event SingleArgMethodInvoker<Race> RaceLoaded;
   19.20 +		
   19.21 +		public event SingleArgMethodInvoker<Army> ArmyLoaded;
   19.22 +		
   19.23 +		public virtual void CompleteLoading(IWarFoundryStagedLoadObject obj)
   19.24 +		{
   19.25 +			//Pretend we've fully loaded, as this will probably be standard for non-native factories and some native factories
   19.26 +			obj.SetAsFullyLoaded();
   19.27 +		}
   19.28 +
   19.29 +		public bool CanHandleFileFormat (FileInfo file)
   19.30 +		{
   19.31 +			FILE_TYPE typedFile = GetFileAsSupportedType(file);
   19.32 +			bool canHandle = typedFile != null && CheckCanHandleFileFormat(typedFile);
   19.33 +
   19.34 +			if (typedFile != null)
   19.35 +			{
   19.36 +				CleanUpFileAsSupportedType(typedFile);
   19.37 +			}
   19.38 +
   19.39 +			return canHandle;
   19.40 +		}
   19.41 +
   19.42 +		public bool CanHandleFileAsRace(FileInfo file)
   19.43 +		{
   19.44 +			FILE_TYPE typedFile = GetFileAsSupportedType(file);
   19.45 +			bool canHandle = typedFile != null && CheckCanHandleFileAsRace(typedFile);
   19.46 +
   19.47 +			if (typedFile != null)
   19.48 +			{
   19.49 +				CleanUpFileAsSupportedType(typedFile);
   19.50 +			}
   19.51 +
   19.52 +			return canHandle;
   19.53 +		}
   19.54 +
   19.55 +		public bool CanHandleFileAsGameSystem(FileInfo file)
   19.56 +		{
   19.57 +			FILE_TYPE typedFile = GetFileAsSupportedType(file);
   19.58 +			bool canHandle = typedFile != null && CheckCanHandleFileAsGameSystem(typedFile);
   19.59 +
   19.60 +			if (typedFile != null)
   19.61 +			{
   19.62 +				CleanUpFileAsSupportedType(typedFile);
   19.63 +			}
   19.64 +
   19.65 +			return canHandle;
   19.66 +		}
   19.67 +
   19.68 +		public bool CanHandleFileAsArmy(FileInfo file)
   19.69 +		{
   19.70 +			FILE_TYPE typedFile = GetFileAsSupportedType(file);
   19.71 +			bool canHandle = typedFile != null && CheckCanHandleFileAsArmy(typedFile);
   19.72 +
   19.73 +			if (typedFile != null)
   19.74 +			{
   19.75 +				CleanUpFileAsSupportedType(typedFile);
   19.76 +			}
   19.77 +
   19.78 +			return canHandle;
   19.79 +		}
   19.80 +
   19.81 +		protected virtual void CleanUpFileAsSupportedType(FILE_TYPE typedFile)
   19.82 +		{
   19.83 +			//Do nothing by default
   19.84 +		}
   19.85 +		
   19.86 +		/// <summary>
   19.87 +		/// Converts the <see cref="FileInfo"/> object in to the appropriate type for this class so that it can perform its checks. If no conversion is required (the test can be performed on a <see cref="FileInfo"/> object) the object should be returned with no modification. 
   19.88 +		/// If the file is not of supported type the <code>null</code> should be returned.
   19.89 +		/// </summary>
   19.90 +		/// <param name="file">
   19.91 +		/// A <see cref="FileInfo"/> to get the supported source object from.
   19.92 +		/// </param>
   19.93 +		/// <returns>
   19.94 +		/// An object of type <see cref="FILE_TYPE"/> that has been converted from the input <see cref="FileInfo"/> object, or <code>null</code> if the conversion cannot be made.
   19.95 +		/// </returns>
   19.96 +		protected abstract FILE_TYPE GetFileAsSupportedType(FileInfo file);
   19.97 +		
   19.98 +		/// <summary>
   19.99 +		/// Checks whether the factory thinks it can load data from the file in its paramaterised type.
  19.100 +		/// </summary>
  19.101 +		/// <param name="file">
  19.102 +		/// An object of the converted <see cref="FILE_TYPE"/> to check support for
  19.103 +		/// </param>
  19.104 +		/// <returns>
  19.105 +		/// <code>true</code> if the factory thinks it can support the file, else <code>false</code>
  19.106 +		/// </returns>
  19.107 +		protected abstract bool CheckCanHandleFileFormat(FILE_TYPE file);
  19.108 +
  19.109 +		/// <summary>
  19.110 +		/// Checks whether the factory thinks it can load data from the file in its paramaterised type as a Race object.
  19.111 +		/// </summary>
  19.112 +		/// <param name="file">
  19.113 +		/// An object of the converted <see cref="FILE_TYPE"/> to check support for
  19.114 +		/// </param>
  19.115 +		/// <returns>
  19.116 +		/// <code>true</code> if the factory thinks it can support the file as a Race, else <code>false</code>
  19.117 +		/// </returns>
  19.118 +		protected abstract bool CheckCanHandleFileAsRace(FILE_TYPE file);
  19.119 +
  19.120 +		/// <summary>
  19.121 +		/// Checks whether the factory thinks it can load data from the file in its paramaterised type as a GameSystem object.
  19.122 +		/// </summary>
  19.123 +		/// <param name="file">
  19.124 +		/// An object of the converted <see cref="FILE_TYPE"/> to check support for
  19.125 +		/// </param>
  19.126 +		/// <returns>
  19.127 +		/// <code>true</code> if the factory thinks it can support the file as a GameSystem, else <code>false</code>
  19.128 +		/// </returns>
  19.129 +		protected abstract bool CheckCanHandleFileAsGameSystem(FILE_TYPE file);
  19.130 +
  19.131 +		/// <summary>
  19.132 +		/// Checks whether the factory thinks it can load data from the file in its paramaterised type as an Army object.
  19.133 +		/// </summary>
  19.134 +		/// <param name="file">
  19.135 +		/// An object of the converted <see cref="FILE_TYPE"/> to check support for
  19.136 +		/// </param>
  19.137 +		/// <returns>
  19.138 +		/// <code>true</code> if the factory thinks it can support the file as a Army, else <code>false</code>
  19.139 +		/// </returns>
  19.140 +		protected abstract bool CheckCanHandleFileAsArmy(FILE_TYPE file);
  19.141 +		
  19.142 +		
  19.143 +		public ICollection<IWarFoundryObject> CreateObjectsFromFile(FileInfo file)
  19.144 +		{
  19.145 +			return DoCreateObjectsFromFile(GetFileAsSupportedType(file));
  19.146 +		}
  19.147 +		
  19.148 +		/// <summary>
  19.149 +		/// Reads the data from the supplied converted <see cref="FILE_TYPE"/> object and returns it as a collection of loadable objects.
  19.150 +		/// In addition, the method fires the appropriate XxxLoaded event for each object created for asynchronous use.
  19.151 +		/// </summary>
  19.152 +		/// <param name="file">
  19.153 +		/// An object of the converted <see cref="FILE_TYPE"/> for the file to load data from
  19.154 +		/// </param>
  19.155 +		/// <returns>
  19.156 +		/// A <see cref="ICollection`1"/> of <see cref="IWarFoundryObject"/>s that were loaded from the file object
  19.157 +		/// </returns>
  19.158 +		protected abstract ICollection<IWarFoundryObject> DoCreateObjectsFromFile(FILE_TYPE file);
  19.159 +		
  19.160 +		protected void OnGameSystemLoaded(GameSystem system)
  19.161 +		{
  19.162 +			if (GameSystemLoaded != null)
  19.163 +			{
  19.164 +				GameSystemLoaded(system);
  19.165 +			}
  19.166 +		}
  19.167 +		
  19.168 +		protected void OnRaceLoaded(Race race)
  19.169 +		{
  19.170 +			if (RaceLoaded != null)
  19.171 +			{
  19.172 +				RaceLoaded(race);
  19.173 +			}
  19.174 +		}
  19.175 +		
  19.176 +		protected void OnArmyLoaded(Army army)
  19.177 +		{
  19.178 +			if (ArmyLoaded != null)
  19.179 +			{
  19.180 +				ArmyLoaded(army);
  19.181 +			}
  19.182 +		}
  19.183 +	}
  19.184 +}
    20.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    20.2 +++ b/API/Factories/DummyWarFoundryFactory.cs	Sun Apr 03 18:50:32 2011 +0000
    20.3 @@ -0,0 +1,59 @@
    20.4 +//  This file (DummyWarFoundryFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2010 IBBoard
    20.5 +// 
    20.6 +//  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.
    20.7 +
    20.8 +using IBBoard.WarFoundry.API.Factories;
    20.9 +using IBBoard.WarFoundry.API.Objects;
   20.10 +using System.IO;
   20.11 +using System.Collections.Generic;
   20.12 +
   20.13 +namespace IBBoard.WarFoundry.API.Factories
   20.14 +{
   20.15 +	///<summary>
   20.16 +	///A dummy factory for use with <see cref="WarFoundryStagedLoadingObject"/>s that implements the bare minimum of the methods but won't load anything
   20.17 +	///</summary>
   20.18 +	public class DummyWarFoundryFactory : IWarFoundryFactory
   20.19 +	{
   20.20 +		public event SingleArgMethodInvoker<GameSystem> GameSystemLoaded;
   20.21 +
   20.22 +		public event SingleArgMethodInvoker<Race> RaceLoaded;
   20.23 +
   20.24 +		public event SingleArgMethodInvoker<Army> ArmyLoaded;
   20.25 +		
   20.26 +		public DummyWarFoundryFactory()
   20.27 +		{
   20.28 +			//Public constructor
   20.29 +		}
   20.30 +
   20.31 +		public void CompleteLoading(IWarFoundryStagedLoadObject obj)
   20.32 +		{
   20.33 +			obj.SetAsFullyLoaded();
   20.34 +		}
   20.35 +
   20.36 +		public bool CanHandleFileFormat(FileInfo file)
   20.37 +		{
   20.38 +			return false;
   20.39 +		}
   20.40 +
   20.41 +		public bool CanHandleFileAsRace(FileInfo file)
   20.42 +		{
   20.43 +			return false;
   20.44 +		}
   20.45 +
   20.46 +		public bool CanHandleFileAsGameSystem(FileInfo file)
   20.47 +		{
   20.48 +			return false;
   20.49 +		}
   20.50 +
   20.51 +		public bool CanHandleFileAsArmy(FileInfo file)
   20.52 +		{
   20.53 +			return false;
   20.54 +		}
   20.55 +
   20.56 +		public ICollection<IWarFoundryObject> CreateObjectsFromFile(FileInfo file)
   20.57 +		{
   20.58 +			return new List<IWarFoundryObject>();
   20.59 +		}
   20.60 +	}
   20.61 +}
   20.62 +
    21.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    21.2 +++ b/API/Factories/INativeWarFoundryFactory.cs	Sun Apr 03 18:50:32 2011 +0000
    21.3 @@ -0,0 +1,13 @@
    21.4 +// This file (INativeWarFoundryFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    21.5 +//
    21.6 +// 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.
    21.7 +
    21.8 +using System;
    21.9 +
   21.10 +namespace IBBoard.WarFoundry.API.Factories
   21.11 +{
   21.12 +	public interface INativeWarFoundryFactory : IWarFoundryFactory
   21.13 +	{
   21.14 +		//Marker interface
   21.15 +	}
   21.16 +}
    22.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    22.2 +++ b/API/Factories/INonNativeWarFoundryFactory.cs	Sun Apr 03 18:50:32 2011 +0000
    22.3 @@ -0,0 +1,13 @@
    22.4 +// This file (INonNativeWarFoundryFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    22.5 +//
    22.6 +// 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.
    22.7 +
    22.8 +using System;
    22.9 +
   22.10 +namespace IBBoard.WarFoundry.API.Factories
   22.11 +{
   22.12 +	public interface INonNativeWarFoundryFactory : IWarFoundryFactory
   22.13 +	{
   22.14 +		string NonNativeDataType { get; } 
   22.15 +	}
   22.16 +}
    23.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    23.2 +++ b/API/Factories/IWarFoundryFactory.cs	Sun Apr 03 18:50:32 2011 +0000
    23.3 @@ -0,0 +1,86 @@
    23.4 +// This file (IWarFoundryFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    23.5 +//
    23.6 +// 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.
    23.7 +
    23.8 +using System;
    23.9 +using System.IO;
   23.10 +using System.Collections.Generic;
   23.11 +using IBBoard.WarFoundry.API.Objects;
   23.12 +
   23.13 +namespace IBBoard.WarFoundry.API.Factories
   23.14 +{
   23.15 +	public interface IWarFoundryFactory
   23.16 +	{		
   23.17 +		event SingleArgMethodInvoker<GameSystem> GameSystemLoaded;
   23.18 +		
   23.19 +		event SingleArgMethodInvoker<Race> RaceLoaded;
   23.20 +		
   23.21 +		event SingleArgMethodInvoker<Army> ArmyLoaded;
   23.22 +		
   23.23 +		/// <summary>
   23.24 +		/// Completes the loading of an object if it is loaded in stages.
   23.25 +		/// </summary>
   23.26 +		/// <param name="obj">
   23.27 +		/// The <see cref="IWarFoundryStagedLoadObject"/> that should be fully loaded.
   23.28 +		/// </param>
   23.29 +		void CompleteLoading(IWarFoundryStagedLoadObject obj);
   23.30 +		
   23.31 +		/// <summary>
   23.32 +		/// Checks if the factory thinks it can handle the supplied file. Checks can be performed on file extension or some basic check of file content, or some other method.
   23.33 +		/// </summary>
   23.34 +		/// <param name="file">
   23.35 +		/// A <see cref="FileInfo"/> for the file to check support for.
   23.36 +		/// </param>
   23.37 +		/// <returns>
   23.38 +		/// <code>true</code> if the file appears to be supported for loading by this factory, else returns <code>false</code>
   23.39 +		/// </returns>
   23.40 +		bool CanHandleFileFormat(FileInfo file);
   23.41 +
   23.42 +		/// <summary>
   23.43 +		/// Checks if the factory thinks it can handle the supplied file as a Race. Checks can be performed on file extension or some basic check of file content, or some other method.
   23.44 +		/// </summary>
   23.45 +		/// <param name="file">
   23.46 +		/// A <see cref="FileInfo"/> for the file to check support for as a file containing Race information.
   23.47 +		/// </param>
   23.48 +		/// <returns>
   23.49 +		/// <code>true</code> if the file appears to be supported for loading by this factory as a Race, else returns <code>false</code>
   23.50 +		/// </returns>
   23.51 +		bool CanHandleFileAsRace(FileInfo file);
   23.52 +
   23.53 +		/// <summary>
   23.54 +		/// Checks if the factory thinks it can handle the supplied file as a GameSystem. Checks can be performed on file extension or some basic check of file content, or some other method.
   23.55 +		/// </summary>
   23.56 +		/// <param name="file">
   23.57 +		/// A <see cref="FileInfo"/> for the file to check support for as a file containing GameSystem information.
   23.58 +		/// </param>
   23.59 +		/// <returns>
   23.60 +		/// <code>true</code> if the file appears to be supported for loading by this factory as a GameSystem, else returns <code>false</code>
   23.61 +		/// </returns>
   23.62 +		bool CanHandleFileAsGameSystem(FileInfo file);
   23.63 +
   23.64 +		/// <summary>
   23.65 +		/// Checks if the factory thinks it can handle the supplied file as a Army. Checks can be performed on file extension or some basic check of file content, or some other method.
   23.66 +		/// </summary>
   23.67 +		/// <param name="file">
   23.68 +		/// A <see cref="FileInfo"/> for the file to check support for as a file containing Army information.
   23.69 +		/// </param>
   23.70 +		/// <returns>
   23.71 +		/// <code>true</code> if the file appears to be supported for loading by this factory as a Army, else returns <code>false</code>
   23.72 +		/// </returns>
   23.73 +		bool CanHandleFileAsArmy(FileInfo file);
   23.74 +		
   23.75 +		/// <summary>
   23.76 +		/// Reads the data from the supplied file and returns it as a collection of loadable objects. In addition, it fires the appropriate XxxLoaded event
   23.77 +		/// for each object loaded for asynchronous use.
   23.78 +		/// 
   23.79 +		/// May throw a <see cref=" IBBoard.IO.InvalidFileException"/> if the file is supported by the Factory but the content is invalid.
   23.80 +		/// </summary>
   23.81 +		/// <param name="file">
   23.82 +		/// A <see cref="FileInfo"/> for the file to load data from
   23.83 +		/// </param>
   23.84 +		/// <returns>
   23.85 +		/// A <see cref="ICollection`1"/> of <see cref="IWarFoundryObject"/>s that were loaded from the file
   23.86 +		/// </returns>
   23.87 +		ICollection<IWarFoundryObject> CreateObjectsFromFile(FileInfo file);
   23.88 +	}
   23.89 +}
    24.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    24.2 +++ b/API/Factories/RequiredDataMissingException.cs	Sun Apr 03 18:50:32 2011 +0000
    24.3 @@ -0,0 +1,19 @@
    24.4 +// This file (RequiredDataMissingException.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    24.5 +// 
    24.6 +// 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.
    24.7 +
    24.8 +using System;
    24.9 +
   24.10 +namespace IBBoard.WarFoundry.API.Factories
   24.11 +{
   24.12 +	/// <summary>
   24.13 +	/// An exception that is thrown when a file cannot be loaded because one of the data files that it depends on has
   24.14 +	/// not been loaded. Normally occurs when loading an army file without having the correct game system or race.
   24.15 +	/// </summary>	
   24.16 +	public class RequiredDataMissingException : Exception
   24.17 +	{
   24.18 +		public RequiredDataMissingException(String file, String missingObjectType, String requiredValue) : base(String.Format("Could not find data for {1} object with ID {2} required by {0}", file, missingObjectType, requiredValue))
   24.19 +		{
   24.20 +		}
   24.21 +	}
   24.22 +}
    25.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    25.2 +++ b/API/Factories/Xml/AbstractStagedLoadedSubFactory.cs	Sun Apr 03 18:50:32 2011 +0000
    25.3 @@ -0,0 +1,33 @@
    25.4 +//  This file (AbstractStagedLoadedSubFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    25.5 +// 
    25.6 +// 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.
    25.7 +
    25.8 +using System;
    25.9 +using System.Xml;
   25.10 +using IBBoard.Xml;
   25.11 +using IBBoard.WarFoundry.API.Objects;
   25.12 +
   25.13 +namespace IBBoard.WarFoundry.API.Factories.Xml
   25.14 +{
   25.15 +	public class AbstractStagedLoadedSubFactory
   25.16 +	{
   25.17 +		protected WarFoundryXmlFactory mainFactory;
   25.18 +		
   25.19 +		protected AbstractStagedLoadedSubFactory(WarFoundryXmlFactory factory)
   25.20 +		{
   25.21 +			mainFactory = factory;
   25.22 +		}
   25.23 +		
   25.24 +		protected Category CreateCategoryFromElement(XmlElement elem)
   25.25 +		{
   25.26 +			string id = elem.GetAttribute("id");
   25.27 +			string name = elem.GetAttribute("name");
   25.28 +			Category cat = new Category(id, name);
   25.29 +			cat.MaximumPercentage = XmlTools.GetIntValueFromAttribute(elem, "maxPercentage");
   25.30 +			cat.MinimumPercentage = XmlTools.GetIntValueFromAttribute(elem, "minPercentage");
   25.31 +			cat.MaximumPoints = XmlTools.GetIntValueFromAttribute(elem, "maxPoints");
   25.32 +			cat.MinimumPoints = XmlTools.GetIntValueFromAttribute(elem, "minPoints");
   25.33 +			return cat;
   25.34 +		}	
   25.35 +	}
   25.36 +}
    26.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    26.2 +++ b/API/Factories/Xml/WarFoundryXmlArmyFactory.cs	Sun Apr 03 18:50:32 2011 +0000
    26.3 @@ -0,0 +1,23 @@
    26.4 +//  This file (WarFoundryXmlArmyFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    26.5 +// 
    26.6 +// 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.
    26.7 +
    26.8 +using System;
    26.9 +using System.Xml;
   26.10 +using IBBoard.Xml;
   26.11 +using ICSharpCode.SharpZipLib.Zip;
   26.12 +using IBBoard.WarFoundry.API.Objects;
   26.13 +
   26.14 +namespace IBBoard.WarFoundry.API.Factories.Xml
   26.15 +{
   26.16 +	/// <summary>
   26.17 +	/// A sub-factory for loading WarFoundry Army XML files
   26.18 +	/// </summary>
   26.19 +	public class WarFoundryXmlArmyFactory
   26.20 +	{			
   26.21 +		public Army CreateArmyFromElement(ZipFile file, XmlElement elem)
   26.22 +		{
   26.23 +			return new WarFoundryXmlArmyParser(file, elem).GetArmy();
   26.24 +		}
   26.25 +	}
   26.26 +}
    27.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    27.2 +++ b/API/Factories/Xml/WarFoundryXmlArmyParser.cs	Sun Apr 03 18:50:32 2011 +0000
    27.3 @@ -0,0 +1,131 @@
    27.4 +//  This file (WarFoundryXmlArmyParser.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    27.5 +// 
    27.6 +// 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.
    27.7 +
    27.8 +using System;
    27.9 +using System.Collections.Generic;
   27.10 +using System.Xml;
   27.11 +using IBBoard.IO;
   27.12 +using IBBoard.Xml;
   27.13 +using ICSharpCode.SharpZipLib.Zip;
   27.14 +using IBBoard.WarFoundry.API.Objects;
   27.15 +
   27.16 +namespace IBBoard.WarFoundry.API.Factories.Xml
   27.17 +{
   27.18 +	public class WarFoundryXmlArmyParser
   27.19 +	{
   27.20 +		private ZipFile file;
   27.21 +		private XmlElement elem;
   27.22 +		private Army army;
   27.23 +		private Dictionary<String, Unit> units;
   27.24 +		
   27.25 +		public WarFoundryXmlArmyParser(ZipFile file, XmlElement elem)
   27.26 +		{
   27.27 +			this.file = file;
   27.28 +			this.elem = elem;
   27.29 +		}
   27.30 +
   27.31 +		public Army GetArmy()
   27.32 +		{
   27.33 +			if (army == null)
   27.34 +			{
   27.35 +				ParseArmy();
   27.36 +			}
   27.37 +
   27.38 +			return army;
   27.39 +		}
   27.40 +
   27.41 +		private void ParseArmy()
   27.42 +		{
   27.43 +			string name = elem.GetAttribute("name");
   27.44 +			string systemID = elem.GetAttribute("system");
   27.45 +			GameSystem system = WarFoundryLoader.GetDefault().GetGameSystem(systemID);
   27.46 +			
   27.47 +			if (system == null)
   27.48 +			{
   27.49 +				throw new RequiredDataMissingException(file.Name, "Game System", systemID);
   27.50 +			}
   27.51 +			
   27.52 +			string raceID = elem.GetAttribute("race");
   27.53 +			Race race = WarFoundryLoader.GetDefault().GetRace(system, raceID);
   27.54 +			
   27.55 +			if (race == null)
   27.56 +			{
   27.57 +				throw new RequiredDataMissingException(file.Name, "Race", raceID);
   27.58 +			}
   27.59 +			
   27.60 +			int points = XmlTools.GetIntValueFromAttribute(elem, "maxPoints");			
   27.61 +			army = new Army(race, name, points, file);
   27.62 +			LoadUnits();
   27.63 +		}
   27.64 +
   27.65 +		private void LoadUnits()
   27.66 +		{
   27.67 +			units = new Dictionary<string, Unit>();
   27.68 +
   27.69 +			foreach (XmlElement unitElem in WarFoundryXmlFactoryUtils.SelectNodes(elem, "/army:army/army:units/army:unit"))
   27.70 +			{
   27.71 +				string id = unitElem.GetAttribute("id");
   27.72 +
   27.73 +				if (!units.ContainsKey(id))
   27.74 +				{
   27.75 +					string unitTypeId = unitElem.GetAttribute("unitType");
   27.76 +					UnitType unitType = army.Race.GetUnitType(unitTypeId);
   27.77 +
   27.78 +					if (unitType == null)
   27.79 +					{
   27.80 +						throw new RequiredDataMissingException(file.Name, "Unit Type", unitTypeId);
   27.81 +					}
   27.82 +					
   27.83 +					string name = unitElem.GetAttribute("unitName");
   27.84 +					int size = XmlTools.GetIntValueFromAttribute(unitElem, "size");
   27.85 +					
   27.86 +					string catID = unitElem.GetAttribute("category");
   27.87 +					Category cat = army.Race.GetCategory(catID);
   27.88 +					
   27.89 +					if (cat == null)
   27.90 +					{
   27.91 +						cat = unitType.MainCategory;
   27.92 +					}
   27.93 +
   27.94 +					Unit unit = new Unit(id, name, size, unitType, army.GetCategory(cat));
   27.95 +					army.AddUnit(unit, cat);
   27.96 +					units.Add(id, unit);
   27.97 +
   27.98 +					LoadUnitEquipment(unitElem, unit);
   27.99 +				}
  27.100 +				else
  27.101 +				{
  27.102 +					throw new InvalidFileException("Duplicate unit ID found in army file: "+id);
  27.103 +				}
  27.104 +			}
  27.105 +		}
  27.106 +
  27.107 +		private void LoadUnitEquipment(XmlElement unitElem, Unit unit)
  27.108 +		{
  27.109 +			foreach (XmlElement elem in WarFoundryXmlFactoryUtils.SelectNodes(unitElem, "army:equipment/army:equipItem"))
  27.110 +			{
  27.111 +				string equipID = elem.GetAttribute("id");
  27.112 +				UnitEquipmentItem item = unit.UnitType.GetEquipmentItem(equipID);
  27.113 +	
  27.114 +				if (item == null)
  27.115 +				{
  27.116 +					throw new RequiredDataMissingException(file.Name, "Equipment Item", equipID);
  27.117 +				}
  27.118 +
  27.119 +				double amount = XmlTools.GetDoubleValueFromAttribute(elem, "amount");
  27.120 +				string equipTypeString = elem.GetAttribute("amountType");
  27.121 +
  27.122 +				if (equipTypeString == "ratio")
  27.123 +				{
  27.124 +					unit.SetEquipmentRatio(item, amount);
  27.125 +				}
  27.126 +				else
  27.127 +				{
  27.128 +					//amount should be a whole number, so do type-cast rounding
  27.129 +					unit.SetEquipmentAmount(item, (int) amount);
  27.130 +				}
  27.131 +			}
  27.132 +		}
  27.133 +	}
  27.134 +}
    28.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    28.2 +++ b/API/Factories/Xml/WarFoundryXmlElementName.cs	Sun Apr 03 18:50:32 2011 +0000
    28.3 @@ -0,0 +1,67 @@
    28.4 +// This file (WarFoundryXmlElementName.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard.
    28.5 +//
    28.6 +// 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.
    28.7 +
    28.8 +using System;
    28.9 +using System.Collections.Generic;
   28.10 +using IBBoard.Xml;
   28.11 +
   28.12 +namespace IBBoard.WarFoundry.API.Factories.Xml
   28.13 +{
   28.14 +	/// <summary>
   28.15 +	/// An enumeration class for valid WarFoundry XML elements, designed to imitate Java's extensible complex object enumerations. 
   28.16 +	/// </summary>
   28.17 +	public class WarFoundryXmlElementName : IXmlElementName, IExtendedEnum<string>
   28.18 +	{
   28.19 +		public static WarFoundryXmlElementName SYSTEM_ELEMENT = new WarFoundryXmlElementName("SYSTEM_ELEMENT", "system");
   28.20 +		public static WarFoundryXmlElementName ARMY_ELEMENT = new WarFoundryXmlElementName("ARMY_ELEMENT", "army");
   28.21 +		public static WarFoundryXmlElementName RACE_ELEMENT = new WarFoundryXmlElementName("RACE_ELEMENT", "race");
   28.22 +        public static WarFoundryXmlElementName ARMY_DEFAULTNAME_ELEMENT = new WarFoundryXmlElementName("ARMY_DEFAULTNAME_ELEMENT", "defaultName");
   28.23 +		public static WarFoundryXmlElementName CATEGORIES_ELEMENT = new WarFoundryXmlElementName("CATEGORIES_ELEMENT", "categories");
   28.24 +		public static WarFoundryXmlElementName CATEGORY_ELEMENT = new WarFoundryXmlElementName("CATEGORY_ELEMENT", "cat");
   28.25 +		public static WarFoundryXmlElementName UNITTYPES_ELEMENT = new WarFoundryXmlElementName("UNITTYPES_ELEMENT", "units");
   28.26 +		public static WarFoundryXmlElementName UNITTYPE_ELEMENT = new WarFoundryXmlElementName("UNITTYPE_ELEMENT", "unit");
   28.27 +		public static WarFoundryXmlElementName RACE_EQUIPMENT_ITEMS_ELEMENT = new WarFoundryXmlElementName("RACE_EQUIPMENT_ITEMS_ELEMENT", "equipment");
   28.28 +		public static WarFoundryXmlElementName RACE_EQUIPMENT_ITEM_ELEMENT = new WarFoundryXmlElementName("RACE_EQUIPMENT_ITEMS_ELEMENT", "equipmentItem");
   28.29 +		
   28.30 +		private static ICollection<WarFoundryXmlElementName> enumValues;
   28.31 +		private string name;
   28.32 +		private string val;
   28.33 +		
   28.34 +		private WarFoundryXmlElementName(string elemName, string elemVal)
   28.35 +		{
   28.36 +			name = elemName;
   28.37 +			val = elemVal;
   28.38 +		}
   28.39 +		
   28.40 +		public string Name
   28.41 +		{
   28.42 +			get {
   28.43 +				return name;
   28.44 +			}
   28.45 +		}
   28.46 +		
   28.47 +		public string Value
   28.48 +		{
   28.49 +			get {
   28.50 +				return val;
   28.51 +			}
   28.52 +		}
   28.53 +		
   28.54 +		/// <summary>
   28.55 +		/// Gets an ICollection of the values so that they can be looped over like a standard enumeration.
   28.56 +		/// </summary>
   28.57 +		/// <returns>
   28.58 +		/// A <see cref="ICollection`1"/> of all of the static 'enumeration' values of the class.
   28.59 +		/// </returns>
   28.60 +		public static ICollection<WarFoundryXmlElementName> GetEnumValues()
   28.61 +		{
   28.62 +			if (enumValues == null)
   28.63 +			{
   28.64 +				enumValues = new WarFoundryXmlElementName[]{SYSTEM_ELEMENT, ARMY_ELEMENT, RACE_ELEMENT, CATEGORIES_ELEMENT, CATEGORY_ELEMENT, UNITTYPES_ELEMENT, UNITTYPE_ELEMENT};
   28.65 +			}
   28.66 +			
   28.67 +			return enumValues;
   28.68 +		}
   28.69 +	}
   28.70 +}
    29.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    29.2 +++ b/API/Factories/Xml/WarFoundryXmlFactory.cs	Sun Apr 03 18:50:32 2011 +0000
    29.3 @@ -0,0 +1,191 @@
    29.4 +// This file (WarFoundryXmlFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    29.5 +//
    29.6 +// 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.
    29.7 +
    29.8 +using System;
    29.9 +using System.IO;
   29.10 +using System.Xml;
   29.11 +using System.Xml.Schema;
   29.12 +using System.Xml.XPath;
   29.13 +using System.Collections.Generic;
   29.14 +using System.Text;
   29.15 +using IBBoard;
   29.16 +using IBBoard.IO;
   29.17 +using IBBoard.Lang;
   29.18 +using IBBoard.Logging;
   29.19 +using IBBoard.Xml;
   29.20 +using IBBoard.WarFoundry.API.Requirements;
   29.21 +using IBBoard.WarFoundry.API.Objects;
   29.22 +using ICSharpCode.SharpZipLib.Zip;
   29.23 +using System.Text.RegularExpressions;
   29.24 +
   29.25 +namespace IBBoard.WarFoundry.API.Factories.Xml
   29.26 +{
   29.27 +	/// <summary>
   29.28 +	/// The WarFoundryXmlFactory loads WarFoundry classes from the native "XML in a zip" file format. Files are validated using the schema for the file type, so structurally invalid files should be identified at initial load.
   29.29 +	/// </summary>
   29.30 +	public class WarFoundryXmlFactory : AbstractNativeWarFoundryFactory
   29.31 +	{
   29.32 +		private static WarFoundryXmlFactory factory;
   29.33 +		private WarFoundryXmlGameSystemFactory gameSystemFactory;
   29.34 +		private WarFoundryXmlRaceFactory raceFactory;
   29.35 +		private WarFoundryXmlArmyFactory armyFactory;
   29.36 +
   29.37 +		public static WarFoundryXmlFactory GetFactory()
   29.38 +		{
   29.39 +			if (factory == null)
   29.40 +			{
   29.41 +				factory = new WarFoundryXmlFactory();
   29.42 +			}
   29.43 +			
   29.44 +			return factory;
   29.45 +		}
   29.46 +		
   29.47 +		private WarFoundryXmlFactory() : base()
   29.48 +		{
   29.49 +			gameSystemFactory = new WarFoundryXmlGameSystemFactory(this);
   29.50 +			raceFactory = new WarFoundryXmlRaceFactory(this);
   29.51 +			armyFactory = new WarFoundryXmlArmyFactory();
   29.52 +		}
   29.53 +		
   29.54 +		public WarFoundryXmlGameSystemFactory GetSystemFactory()
   29.55 +		{
   29.56 +			return gameSystemFactory;
   29.57 +		}
   29.58 +		
   29.59 +		public WarFoundryXmlRaceFactory GetRaceFactory()
   29.60 +		{
   29.61 +			return raceFactory;
   29.62 +		}
   29.63 +		
   29.64 +		public WarFoundryXmlArmyFactory GetArmyFactory()
   29.65 +		{
   29.66 +			return armyFactory;
   29.67 +		}
   29.68 +		
   29.69 +		protected override bool CheckCanFindArmyFileContent(ZipFile file)
   29.70 +		{
   29.71 +			return FindEntries(file, "*.armyx").Count > 0;
   29.72 +		}
   29.73 +		
   29.74 +		protected override bool CheckCanFindSystemFileContent(ZipFile file)
   29.75 +		{
   29.76 +			return FindEntries(file, "*.systemx").Count > 0;
   29.77 +		}
   29.78 +		
   29.79 +		protected override bool CheckCanFindRaceFileContent(ZipFile file)
   29.80 +		{
   29.81 +			return FindEntries(file, "*.racex").Count > 0;
   29.82 +		}
   29.83 +		
   29.84 +		protected override ICollection<ZipEntry> GetArmyZipEntries(ZipFile file)
   29.85 +		{
   29.86 +			return FindEntries(file, "*.armyx");
   29.87 +		}
   29.88 +		
   29.89 +		private ICollection<ZipEntry> FindEntries(ZipFile file, string wildcardPattern)
   29.90 +		{
   29.91 +			Regex re = new Regex("^" + Regex.Escape(wildcardPattern).Replace(@"\*", ".*").Replace(@"\?", ".") + "$", RegexOptions.IgnoreCase | RegexOptions.Singleline);
   29.92 +			ICollection<ZipEntry> entries = new List<ZipEntry>();
   29.93 +				
   29.94 +			foreach (ZipEntry entry in file)
   29.95 +			{
   29.96 +				if (re.IsMatch(entry.Name))
   29.97 +				{
   29.98 +					entries.Add(entry);
   29.99 +				}
  29.100 +			}
  29.101 +			
  29.102 +			return entries;
  29.103 +		}
  29.104 +		
  29.105 +		protected override Army CreateArmyFromStream (ZipFile file, Stream dataStream)
  29.106 +		{
  29.107 +			XmlElement elem = GetRootElementFromStream(dataStream, WarFoundryXmlElementName.ARMY_ELEMENT);
  29.108 +			return armyFactory.CreateArmyFromElement(file, elem);
  29.109 +		}
  29.110 +		
  29.111 +		private XmlElement GetRootElementFromStream(Stream stream, WarFoundryXmlElementName elementName)
  29.112 +		{
  29.113 +			XmlDocument doc = WarFoundryXmlFactoryUtils.CreateXmlDocumentFromStream(stream);
  29.114 +
  29.115 +			XmlElement elem = (XmlElement)doc.LastChild;
  29.116 +			
  29.117 +			if (!elem.LocalName.Equals(elementName.Value))
  29.118 +			{
  29.119 +				throw new InvalidFileException(String.Format("Root element of XML was not valid. Expected {0} but got {1}", elementName.Value, elem.Name));
  29.120 +			}
  29.121 +			
  29.122 +			return elem;
  29.123 +		}
  29.124 +
  29.125 +		protected override ICollection<ZipEntry> GetGameSystemZipEntries(ZipFile file)
  29.126 +		{
  29.127 +			return FindEntries(file, "*.systemx");
  29.128 +		}
  29.129 +		
  29.130 +		protected override GameSystem CreateGameSystemFromStream (ZipFile file, Stream dataStream)
  29.131 +		{
  29.132 +			XmlElement elem = GetRootElementFromStream(dataStream, WarFoundryXmlElementName.SYSTEM_ELEMENT);
  29.133 +			LogNotifier.Debug(GetType(), "Create GameSystem");
  29.134 +			return gameSystemFactory.CreateSystemFromElement(file, elem);
  29.135 +		}
  29.136 +		
  29.137 +		protected override ICollection<ZipEntry> GetRaceZipEntries(ZipFile file)
  29.138 +		{
  29.139 +			return FindEntries(file, "*.racex");
  29.140 +		}
  29.141 +		
  29.142 +		protected override Race CreateRaceFromStream (ZipFile file, Stream dataStream)
  29.143 +		{
  29.144 +			XmlElement elem = GetRootElementFromStream(dataStream, WarFoundryXmlElementName.RACE_ELEMENT);
  29.145 +			LogNotifier.Debug(GetType(), "Create Race");
  29.146 +			return raceFactory.CreateRaceFromElement(file, elem);
  29.147 +		}
  29.148 +
  29.149 +		protected override void CleanUpFileAsSupportedType(ZipFile typedFile)
  29.150 +		{
  29.151 +			typedFile.Close();
  29.152 +		}
  29.153 +
  29.154 +		public override void CompleteLoading(IWarFoundryStagedLoadObject obj)
  29.155 +		{			
  29.156 +			LogNotifier.DebugFormat(GetType(), "Complete loading of {0} with ID {1}", obj.GetType().Name, obj.ID);
  29.157 +
  29.158 +			if (obj is GameSystem)
  29.159 +			{
  29.160 +				CompleteLoadingGameSystem((GameSystem) obj);
  29.161 +			}
  29.162 +			else if (obj is Race)
  29.163 +			{
  29.164 +				CompleteLoadingRace((Race) obj);
  29.165 +			}
  29.166 +		}
  29.167 +
  29.168 +		private void CompleteLoadingRace(Race race)
  29.169 +		{
  29.170 +			try
  29.171 +			{
  29.172 +				raceFactory.CompleteLoading(race);
  29.173 +			}
  29.174 +			catch (InvalidFileException ex)
  29.175 +			{
  29.176 +				WarFoundryLoader.GetDefault().RemoveRace(race);
  29.177 +				throw;
  29.178 +			}
  29.179 +		}
  29.180 +
  29.181 +		private void CompleteLoadingGameSystem(GameSystem system)
  29.182 +		{
  29.183 +			try
  29.184 +			{
  29.185 +				gameSystemFactory.CompleteLoading(system);
  29.186 +			}
  29.187 +			catch (InvalidFileException ex)
  29.188 +			{
  29.189 +				WarFoundryLoader.GetDefault().RemoveGameSystem(system);
  29.190 +				throw;
  29.191 +			}
  29.192 +		}
  29.193 +	}
  29.194 +}
  29.195 \ No newline at end of file
    30.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    30.2 +++ b/API/Factories/Xml/WarFoundryXmlFactoryUtils.cs	Sun Apr 03 18:50:32 2011 +0000
    30.3 @@ -0,0 +1,149 @@
    30.4 +//  This file (WarFoundryXmlFactoryUtils.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    30.5 +// 
    30.6 +// 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.
    30.7 +
    30.8 +using System;
    30.9 +using System.IO;
   30.10 +using System.Xml;
   30.11 +using System.Xml.Schema;
   30.12 +using IBBoard.WarFoundry.API.Objects;
   30.13 +using IBBoard.IO;
   30.14 +
   30.15 +namespace IBBoard.WarFoundry.API.Factories.Xml
   30.16 +{
   30.17 +	/// <summary>
   30.18 +	/// A collection of useful utility methods for loading WarFoundry data from XML files
   30.19 +	/// </summary>
   30.20 +	public class WarFoundryXmlFactoryUtils
   30.21 +	{
   30.22 +		public static readonly string NS_BASE = "http://ibboard.co.uk/warfoundry/";
   30.23 +		private static XmlReaderSettings settings;
   30.24 +		private static XmlNamespaceManager nsManager;
   30.25 +		
   30.26 +		public static XmlNodeList SelectNodes(XmlNode element, string xpathQuery)
   30.27 +		{
   30.28 +			return element.SelectNodes(xpathQuery, GetNamespaceManager());
   30.29 +		}
   30.30 +		
   30.31 +		public static XmlNode SelectSingleNode(XmlNode element, string xpathQuery)
   30.32 +		{
   30.33 +			return element.SelectSingleNode(xpathQuery, GetNamespaceManager());
   30.34 +		}
   30.35 +		
   30.36 +		public static XmlElement SelectSingleElement(XmlNode element, string xpathQuery)
   30.37 +		{
   30.38 +			XmlNode node = SelectSingleNode(element, xpathQuery);
   30.39 +			return (node is XmlElement) ? (XmlElement) node : null;
   30.40 +		}
   30.41 +				
   30.42 +		public static XmlNamespaceManager GetNamespaceManager()
   30.43 +		{
   30.44 +			if (nsManager == null)
   30.45 +			{
   30.46 +				nsManager = new XmlNamespaceManager(new NameTable());
   30.47 +				nsManager.AddNamespace("core", NS_BASE + "core");
   30.48 +				nsManager.AddNamespace("cat", NS_BASE + "cats");
   30.49 +				nsManager.AddNamespace("race", NS_BASE + "race");
   30.50 +				nsManager.AddNamespace("system", NS_BASE + "system");
   30.51 +				nsManager.AddNamespace("army", NS_BASE + "army");
   30.52 +			}
   30.53 +			
   30.54 +			return nsManager;
   30.55 +		}
   30.56 +		
   30.57 +		/// <summary>
   30.58 +		/// Lazy-getter for XML reader settings. May throw a <see cref="InvalidDataException"/> if there is a problem with the translation schema.
   30.59 +		/// </summary>
   30.60 +		/// <returns>
   30.61 +		/// A <see cref="XmlReaderSettings"/> with the default values for validating the translation document against the translation schema
   30.62 +		/// </returns>
   30.63 +		public static XmlReaderSettings GetReaderSettings()
   30.64 +		{
   30.65 +			if (settings == null)
   30.66 +			{
   30.67 +				settings = new XmlReaderSettings();
   30.68 +				settings.ValidationType = ValidationType.Schema;
   30.69 +				//settings.ValidationFlags = XmlSchemaValidationFlags.ReportValidationWarnings;
   30.70 +				settings.ProhibitDtd = true;
   30.71 +				settings.ValidationEventHandler+= new ValidationEventHandler(ValidationEventMethod);
   30.72 +				XmlSchemaSet cache = new XmlSchemaSet();
   30.73 +				string path =  IBBoard.Constants.ExecutablePath + "/schemas/";
   30.74 +				AddSchemaToCache(cache, NS_BASE + "core", path + "warfoundry-core.xsd");
   30.75 +				AddSchemaToCache(cache, NS_BASE + "cats", path + "warfoundry-cats.xsd");
   30.76 +				AddSchemaToCache(cache, NS_BASE + "race", path + "race.xsd");
   30.77 +				AddSchemaToCache(cache, NS_BASE + "system", path + "system.xsd");
   30.78 +				AddSchemaToCache(cache, NS_BASE + "army", path + "army.xsd");
   30.79 +				settings.Schemas.Add(cache);
   30.80 +				settings.Schemas.CompilationSettings.EnableUpaCheck = false;
   30.81 +			}
   30.82 +			
   30.83 +			return settings;
   30.84 +		}
   30.85 +		
   30.86 +		private static void ValidationEventMethod(object sender, ValidationEventArgs e)
   30.87 +		{
   30.88 +			if (e.Severity == XmlSeverityType.Error)
   30.89 +			{
   30.90 +				throw new InvalidFileException("Problem validating against schema for WarFoundry data: " + e.Message, e.Exception);
   30.91 +			}
   30.92 +			else
   30.93 +			{
   30.94 +				//TODO: Fire some kind of warning event
   30.95 +			}
   30.96 +		}
   30.97 +		
   30.98 +		private static void AddSchemaToCache(XmlSchemaSet cache, string xmlNamespace, string schemaLocation)
   30.99 +		{
  30.100 +			try
  30.101 +			{
  30.102 +				cache.Add(xmlNamespace, schemaLocation);
  30.103 +			}
  30.104 +			catch (IOException ex)
  30.105 +			{
  30.106 +				//TODO: Warn on schema failure
  30.107 +			}
  30.108 +			catch (XmlSchemaException ex)
  30.109 +			{
  30.110 +				//TODO: Warn on schema failure
  30.111 +			}
  30.112 +			catch (XmlException ex)
  30.113 +			{
  30.114 +				//TODO: Warn on schema failure
  30.115 +			}
  30.116 +		}
  30.117 +		
  30.118 +		public static XmlDocument CreateXmlDocumentFromStream(Stream stream)
  30.119 +		{
  30.120 +			XmlDocument doc = new XmlDocument();
  30.121 +			XmlReader reader = XmlReader.Create(stream, GetReaderSettings());
  30.122 +			
  30.123 +			try
  30.124 +			{
  30.125 +				doc.Load(reader);
  30.126 +			}
  30.127 +			//Don't catch XMLSchemaExceptions - let them get thrown out
  30.128 +			finally
  30.129 +			{
  30.130 +				reader.Close();
  30.131 +			}
  30.132 +
  30.133 +			return doc;
  30.134 +		}
  30.135 +		
  30.136 +		public static bool CanCompleteLoading(IWarFoundryStagedLoadObject obj)
  30.137 +		{
  30.138 +			bool canLoad = true;			
  30.139 +			
  30.140 +			if (obj.IsFullyLoaded)
  30.141 +			{
  30.142 +				canLoad = false;
  30.143 +			}
  30.144 +			else if (obj.IsLoading)
  30.145 +			{
  30.146 +				canLoad = false;
  30.147 +			}
  30.148 +			
  30.149 +			return canLoad;
  30.150 +		}
  30.151 +	}
  30.152 +}
    31.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    31.2 +++ b/API/Factories/Xml/WarFoundryXmlGameSystemFactory.cs	Sun Apr 03 18:50:32 2011 +0000
    31.3 @@ -0,0 +1,102 @@
    31.4 +//  This file (WarFoundryXmlGameSystemFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    31.5 +// 
    31.6 +// 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.
    31.7 +
    31.8 +using System;
    31.9 +using System.Collections.Generic;
   31.10 +using System.IO;
   31.11 +using System.Xml;
   31.12 +using ICSharpCode.SharpZipLib.Zip;
   31.13 +using IBBoard.Xml;
   31.14 +using IBBoard.WarFoundry.API.Objects;
   31.15 +
   31.16 +namespace IBBoard.WarFoundry.API.Factories.Xml
   31.17 +{
   31.18 +	/// <summary>
   31.19 +	/// A sub-factory specifically for loading GameSystems from WarFoundry XML files
   31.20 +	/// </summary>
   31.21 +	public class WarFoundryXmlGameSystemFactory : AbstractStagedLoadedSubFactory
   31.22 +	{	
   31.23 +		private Dictionary<GameSystem, XmlDocument> extraData = new Dictionary<GameSystem, XmlDocument>();
   31.24 +		
   31.25 +		public WarFoundryXmlGameSystemFactory(WarFoundryXmlFactory factory) : base(factory)
   31.26 +		{
   31.27 +		}
   31.28 +		
   31.29 +		private void StoreExtraData(GameSystem wfObject, XmlElement elem)
   31.30 +		{
   31.31 +			extraData[wfObject] = elem.OwnerDocument;
   31.32 +		}
   31.33 +	
   31.34 +		private XmlDocument GetExtraData(GameSystem obj)
   31.35 +		{
   31.36 +			XmlDocument extra = null;
   31.37 +			extraData.TryGetValue(obj, out extra);			
   31.38 +			return extra;
   31.39 +		}
   31.40 +		
   31.41 +		public GameSystem CreateSystemFromElement(ZipFile file, XmlElement elem)
   31.42 +		{
   31.43 +			string id = elem.GetAttribute("id");
   31.44 +			string name = elem.GetAttribute("name");
   31.45 +			GameSystem system = new GameSystem(id, name, mainFactory);
   31.46 +            system.SystemArmyDefaultSize =  XmlTools.GetIntValueFromAttribute (elem, "defaultArmySize");
   31.47 +            system.SystemPtsAbbrevSingle = elem.GetAttribute ("defaultPtsAbbreviationSingular");
   31.48 +            system.SystemPtsAbbrevPlural = elem.GetAttribute ("defaultPtsAbbreviationPlural");
   31.49 +            system.SystemPtsNameSingle = elem.GetAttribute ("defaultPtsNameSingular");
   31.50 +            system.SystemPtsNamePlural = elem.GetAttribute ("defaultPtsNamePlural");
   31.51 +			StoreExtraData(system, elem);
   31.52 +			return system;
   31.53 +		}		
   31.54 +		
   31.55 +		public void CompleteLoading(GameSystem system)
   31.56 +		{
   31.57 +			if (!WarFoundryXmlFactoryUtils.CanCompleteLoading(system))
   31.58 +			{
   31.59 +				return;
   31.60 +			}
   31.61 +			
   31.62 +			system.SetAsLoading();			
   31.63 +			XmlDocument extraData = GetExtraData(system);
   31.64 +			LoadCategoriesForSystem(system, extraData);
   31.65 +			XmlElement statsElem = WarFoundryXmlFactoryUtils.SelectSingleElement(extraData, "/system:system/system:sysStatsList");
   31.66 +			string defaultStatsID = statsElem.GetAttribute("defaultStats");
   31.67 +			LoadSystemStatsForSystem(system, extraData);
   31.68 +			system.StandardSystemStatsID = defaultStatsID;
   31.69 +			XmlElement systemElement = WarFoundryXmlFactoryUtils.SelectSingleElement(extraData, "/system:system");
   31.70 +			system.WarnOnError = XmlTools.GetBoolValueFromAttribute(systemElement, "warn");
   31.71 +			system.AllowAllies = XmlTools.GetBoolValueFromAttribute(systemElement, "allowAllies");
   31.72 +			system.SetAsFullyLoaded();
   31.73 +		}
   31.74 +
   31.75 +		
   31.76 +		private void LoadCategoriesForSystem(GameSystem system, XmlNode elem)
   31.77 +		{
   31.78 +			foreach (XmlElement cat in WarFoundryXmlFactoryUtils.SelectNodes(elem, "/system:system/system:categories/cat:cat"))
   31.79 +			{
   31.80 +				system.AddCategory(CreateCategoryFromElement(cat));
   31.81 +			}
   31.82 +		}	
   31.83 +		
   31.84 +		private void LoadSystemStatsForSystem(GameSystem system, XmlNode elem)
   31.85 +		{
   31.86 +			foreach (XmlElement stats in WarFoundryXmlFactoryUtils.SelectNodes(elem, "/system:system/system:sysStatsList/system:sysStats"))
   31.87 +			{
   31.88 +				SystemStats sysStats = CreateSystemStatsFromElement(stats);
   31.89 +				system.AddSystemStats(sysStats);
   31.90 +			}
   31.91 +		}
   31.92 +		
   31.93 +		private SystemStats CreateSystemStatsFromElement(XmlElement elem)
   31.94 +		{
   31.95 +			SystemStats sysStats = new SystemStats(elem.GetAttribute("id"));
   31.96 +			
   31.97 +			foreach (XmlElement slot in WarFoundryXmlFactoryUtils.SelectNodes(elem, "system:sysStat"))
   31.98 +			{
   31.99 +				sysStats.AddStatSlot(slot.GetAttribute("name"));
  31.100 +			}
  31.101 +
  31.102 +			return sysStats;
  31.103 +		}	
  31.104 +	}
  31.105 +}
  31.106 \ No newline at end of file
    32.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    32.2 +++ b/API/Factories/Xml/WarFoundryXmlLimitParser.cs	Sun Apr 03 18:50:32 2011 +0000
    32.3 @@ -0,0 +1,91 @@
    32.4 +//  This file (WarFoundryXmlLimitParser.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2010 IBBoard
    32.5 +// 
    32.6 +//  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.
    32.7 +using System.Collections.Generic;
    32.8 +using System.Xml;
    32.9 +using IBBoard.Limits;
   32.10 +using IBBoard.Xml;
   32.11 +
   32.12 +namespace IBBoard.WarFoundry.API.Factories.Xml
   32.13 +{
   32.14 +	public class WarFoundryXmlLimitParser
   32.15 +	{
   32.16 +		public ILimit GetMinLimit(XmlElement elem)
   32.17 +		{
   32.18 +			XmlElement limitElem = WarFoundryXmlFactoryUtils.SelectSingleElement(elem, "race:minLimit/*[1]");
   32.19 +			return GetLimitFromElement(limitElem);
   32.20 +		}
   32.21 +
   32.22 +		public ILimit GetMaxLimit(XmlElement equipSlot)
   32.23 +		{
   32.24 +			XmlElement limitElem = WarFoundryXmlFactoryUtils.SelectSingleElement(equipSlot, "race:maxLimit/*[1]");
   32.25 +			return GetLimitFromElement(limitElem);
   32.26 +		}
   32.27 +
   32.28 +		public ILimit GetLimitFromElement(XmlElement limitElem)
   32.29 +		{
   32.30 +			ILimit limit = null;
   32.31 +			
   32.32 +			if (limitElem != null)
   32.33 +			{
   32.34 +				switch (limitElem.LocalName)
   32.35 +				{
   32.36 +					case "percentageLimit":
   32.37 +						double limitPercent = XmlTools.GetDoubleValueFromAttribute(limitElem, "limit");
   32.38 +						bool roundUp = limitElem.GetAttribute("round").Equals("up");
   32.39 +						limit = new SimpleRoundedPercentageLimit(limitPercent, roundUp);
   32.40 +						break;
   32.41 +					case "sizeConstrainedLimit":
   32.42 +						limit = new NumericSizeConstrainedLimit(XmlTools.GetIntValueFromAttribute(limitElem, "limit"));
   32.43 +						break;
   32.44 +					case "absoluteLimit":
   32.45 +						limit = new AbsoluteNumericLimit(XmlTools.GetIntValueFromAttribute(limitElem, "limit"));
   32.46 +						break;
   32.47 +					case "unitSizeLimit":
   32.48 +						limit = new SimpleRoundedPercentageLimit(100);
   32.49 +						break;
   32.50 +					case "compositeMaxLimit":
   32.51 +						ICollection<ILimit> maxSubLimits = GetSubLimits(limitElem);
   32.52 +						limit = new CompositeMaximumLimit(maxSubLimits);
   32.53 +						break;
   32.54 +					case "compositeMinLimit":
   32.55 +						ICollection<ILimit> minSubLimits = GetSubLimits(limitElem);
   32.56 +						limit = new CompositeMinimumLimit(minSubLimits);
   32.57 +						break;
   32.58 +					default:
   32.59 +						//TODO: Warn of missing handler for when we've extended the limit list
   32.60 +						break;
   32.61 +				}
   32.62 +			}
   32.63 +			
   32.64 +			return limit;
   32.65 +		}
   32.66 +
   32.67 +		private ICollection<ILimit> GetSubLimits(XmlElement limitElem)
   32.68 +		{
   32.69 +			XmlNodeList subLimitNodes = GetSubLimitElements(limitElem);
   32.70 +			ICollection<ILimit> limits = new List<ILimit>();
   32.71 +			
   32.72 +			foreach (XmlNode node in subLimitNodes)
   32.73 +			{
   32.74 +				if (node is XmlElement)
   32.75 +				{
   32.76 +					ILimit limit = GetLimitFromElement((XmlElement)node);
   32.77 +					
   32.78 +					if (limit != null)
   32.79 +					{
   32.80 +						limits.Add(limit);
   32.81 +					}
   32.82 +				}
   32.83 +			}
   32.84 +			
   32.85 +			return limits;
   32.86 +		}
   32.87 +
   32.88 +		private XmlNodeList GetSubLimitElements(XmlElement limitElem)
   32.89 +		{
   32.90 +			return limitElem.ChildNodes;
   32.91 +		}
   32.92 +	}
   32.93 +}
   32.94 +
    33.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    33.2 +++ b/API/Factories/Xml/WarFoundryXmlRaceFactory.cs	Sun Apr 03 18:50:32 2011 +0000
    33.3 @@ -0,0 +1,481 @@
    33.4 +//  This file (WarFoundryXmlRaceFactory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    33.5 +// 
    33.6 +// 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.
    33.7 +
    33.8 +using System;
    33.9 +using System.Collections.Generic;
   33.10 +using System.IO;
   33.11 +using System.Xml;
   33.12 +using IBBoard.Xml;
   33.13 +using IBBoard.IO;
   33.14 +using IBBoard.Limits;
   33.15 +using IBBoard.CustomMath;
   33.16 +using ICSharpCode.SharpZipLib.Zip;
   33.17 +using IBBoard.WarFoundry.API.Objects;
   33.18 +
   33.19 +namespace IBBoard.WarFoundry.API.Factories.Xml
   33.20 +{
   33.21 +	/// <summary>
   33.22 +	/// A sub-factory for loading WarFoundry Race XML files
   33.23 +	/// </summary>
   33.24 +	public class WarFoundryXmlRaceFactory : AbstractStagedLoadedSubFactory
   33.25 +	{
   33.26 +		private Dictionary<Race, XmlDocument> extraData = new Dictionary<Race, XmlDocument>();
   33.27 +		private WarFoundryXmlLimitParser limitParser = new WarFoundryXmlLimitParser();
   33.28 +		
   33.29 +		public WarFoundryXmlRaceFactory(WarFoundryXmlFactory factory) : base (factory)
   33.30 +		{
   33.31 +			//Do nothing special
   33.32 +		}
   33.33 +		
   33.34 +		private void StoreExtraData(Race wfObject, XmlElement elem)
   33.35 +		{
   33.36 +			extraData[wfObject] = elem.OwnerDocument;
   33.37 +		}
   33.38 +	
   33.39 +		private XmlDocument GetExtraData(Race obj)
   33.40 +		{
   33.41 +			XmlDocument extra = null;
   33.42 +			extraData.TryGetValue(obj, out extra);
   33.43 +			return extra;
   33.44 +		}
   33.45 +		
   33.46 +		public Race CreateRaceFromElement(ZipFile file, XmlElement elem)
   33.47 +		{
   33.48 +			string id = elem.GetAttribute("id");
   33.49 +			string subid = elem.GetAttribute("subid");
   33.50 +			string systemID = elem.GetAttribute("system");
   33.51 +			string name = elem.GetAttribute("name");
   33.52 +            string armyDefaultName = elem.GetAttribute("defaultArmyName");
   33.53 +			GameSystem gameSystem = WarFoundryLoader.GetDefault ().GetGameSystem (systemID);
   33.54 +
   33.55 +			if (gameSystem == null)
   33.56 +			{
   33.57 +				throw new InvalidFileException("Referenced game system, '"+systemID+"', did not exist");
   33.58 +			}
   33.59 +
   33.60 +            Race race = new Race(id, subid, name, gameSystem, mainFactory);
   33.61 +			race.ArmyDefaultName = armyDefaultName;
   33.62 +			StoreExtraData(race, elem);
   33.63 +			return race;
   33.64 +		}
   33.65 +		
   33.66 +		public void CompleteLoading(Race race)
   33.67 +		{
   33.68 +			if (!WarFoundryXmlFactoryUtils.CanCompleteLoading(race))
   33.69 +			{
   33.70 +				return;
   33.71 +			}
   33.72 +			
   33.73 +			race.SetAsLoading();			
   33.74 +			XmlDocument extraData = GetExtraData(race);
   33.75 +			
   33.76 +			foreach (XmlElement node in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:categories/cat:cat"))
   33.77 +			{
   33.78 +				CreateCategoryFromElement(node, race);
   33.79 +			}
   33.80 +							
   33.81 +			foreach (XmlElement node  in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:equipment/race:equipmentItem"))
   33.82 +			{
   33.83 +				CreateEquipmentItemFromElement(node, race);
   33.84 +			}
   33.85 +							
   33.86 +			foreach (XmlElement node  in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:abilities/race:ability"))
   33.87 +			{
   33.88 +				CreateAbilityFromElement(node, race);
   33.89 +			}
   33.90 +							
   33.91 +			foreach (XmlElement node  in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:memberTypes/race:memberType"))
   33.92 +			{
   33.93 +				CreateMemberTypeFromElement(node, race);
   33.94 +			}
   33.95 +			
   33.96 +			foreach (XmlElement node in WarFoundryXmlFactoryUtils.SelectNodes(extraData, "/race:race/race:units/race:unit"))
   33.97 +			{
   33.98 +				GetUnitTypeForElement(node, race);
   33.99 +			}
  33.100 +			
  33.101 +			race.SetAsFullyLoaded();
  33.102 +		}
  33.103 +
  33.104 +		private Category CreateCategoryFromElement(XmlElement elem, Race parentRace)
  33.105 +		{
  33.106 +			Category cat = CreateCategoryFromElement(elem);
  33.107 +			parentRace.AddCategory(cat);
  33.108 +			return cat;
  33.109 +		}
  33.110 +
  33.111 +		private UnitType GetUnitTypeFromDocument(XmlDocument doc, string id, Race parentRace)
  33.112 +		{
  33.113 +			XmlElement unitWithId = WarFoundryXmlFactoryUtils.SelectSingleElement (doc, "/race:race/race:units/race:unit[@id='" + id + "']");
  33.114 +			
  33.115 +			if (unitWithId == null)
  33.116 +			{
  33.117 +				throw new InvalidFileException("Could not find unit with ID "+id);
  33.118 +			}
  33.119 +			
  33.120 +			return GetUnitTypeForElement(unitWithId, parentRace);
  33.121 +		}
  33.122 +						
  33.123 +		private UnitType GetUnitTypeForElement(XmlElement elem, Race parentRace)
  33.124 +		{
  33.125 +			string id = elem.GetAttribute("id");
  33.126 +			UnitType type = parentRace.GetUnitType(id);
  33.127 +
  33.128 +			if (type==null)
  33.129 +			{
  33.130 +				type = CreateUnitTypeFromElement(elem, id, parentRace);
  33.131 +			}
  33.132 +			
  33.133 +			return type;
  33.134 +		}
  33.135 +
  33.136 +		private UnitType CreateUnitTypeFromElement(XmlElement elem, string id, Race parentRace)
  33.137 +		{
  33.138 +			string name = elem.GetAttribute("typeName");
  33.139 +			UnitType type = new UnitType(id, name, parentRace);
  33.140 +			LoadCoreValuesForUnitType(elem, type);
  33.141 +			LoadEquipmentSlotsForUnitType(elem, type);
  33.142 +			LoadEquipmentForUnitType(elem, type);
  33.143 +			LoadAbilitiesForUnitType(elem, type);
  33.144 +			LoadContainedUnitsForUnitType(elem, type);
  33.145 +			LoadRequirementsForUnitType(elem, type);
  33.146 +			LoadExtraDataForUnitType(elem, type);
  33.147 +			LoadNotesForUnitType(elem, type);
  33.148 +			parentRace.AddUnitType(type);
  33.149 +			return type;
  33.150 +		}
  33.151 +
  33.152 +		private void LoadCoreValuesForUnitType(XmlElement elem, UnitType type)
  33.153 +		{
  33.154 +			try
  33.155 +			{
  33.156 +				type.MaxNumber = XmlTools.GetIntValueFromAttribute(elem, "maxNum");
  33.157 +				type.MinNumber = XmlTools.GetIntValueFromAttribute(elem, "minNum");
  33.158 +				type.MaxSize = XmlTools.GetIntValueFromAttribute(elem, "maxSize");
  33.159 +				type.MinSize = XmlTools.GetIntValueFromAttribute(elem, "minSize");
  33.160 +				type.BaseSize = XmlTools.GetIntValueFromAttribute(elem, "baseSize");
  33.161 +				type.CostPerTrooper = XmlTools.GetDoubleValueFromAttribute(elem, "points");
  33.162 +				type.BaseUnitCost = XmlTools.GetDoubleValueFromAttribute(elem, "basePoints");
  33.163 +			}
  33.164 +			catch (FormatException ex)
  33.165 +			{
  33.166 +				throw new InvalidFileException(ex.Message, ex);
  33.167 +			}
  33.168 +
  33.169 +			Race race = type.Race;
  33.170 +			string mainCatID = elem.GetAttribute("cat");
  33.171 +			Category cat = race.GetCategory(mainCatID);
  33.172 +			
  33.173 +			if (cat == null)
  33.174 +			{
  33.175 +				throw new InvalidFileException(String.Format("Category with ID '{1}' did not exist for UnitType '{0}'", type.Name, mainCatID));
  33.176 +			}
  33.177 +			
  33.178 +			type.MainCategory = cat;
  33.179 +			
  33.180 +			XmlNodeList unitCategories = WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitCategories/race:unitCategory");
  33.181 +			
  33.182 +			foreach (XmlElement unitCategory in unitCategories)
  33.183 +			{
  33.184 +				string catID = unitCategory.GetAttribute("catID");
  33.185 +				Category unitCat = race.GetCategory(catID);
  33.186 +				
  33.187 +				if (unitCat == null)
  33.188 +				{
  33.189 +					throw new InvalidFileException(String.Format("Category with ID '{1}' did not exist for UnitType '{0}'", type.Name, catID));
  33.190 +				}
  33.191 +				
  33.192 +				type.AddCategory(unitCat);
  33.193 +			}
  33.194 +			
  33.195 +			XmlElement statsElement = WarFoundryXmlFactoryUtils.SelectSingleElement(elem, "race:stats");
  33.196 +			
  33.197 +			if (statsElement!=null)
  33.198 +			{
  33.199 +				Stats unitStats = ParseUnitStats(statsElement, type.GameSystem);
  33.200 +				type.SetUnitStats(unitStats);
  33.201 +			}
  33.202 +			
  33.203 +			XmlNodeList unitMemberReferences = WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitMembers/race:unitMember");
  33.204 +			
  33.205 +			foreach (XmlElement unitMemberRef in unitMemberReferences)
  33.206 +			{
  33.207 +				string typeID = unitMemberRef.GetAttribute("typeID");
  33.208 +				UnitMemberType unitMemberType = race.GetUnitMemberType(typeID);
  33.209 +				type.AddUnitMemberType(unitMemberType);
  33.210 +			}
  33.211 +		}
  33.212 +
  33.213 +		private void LoadEquipmentSlotsForUnitType(XmlElement elem, UnitType type)
  33.214 +		{
  33.215 +			foreach (XmlElement equipSlot in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:equipmentSlots/race:equipmentSlot"))
  33.216 +			{
  33.217 +				LoadEquipmentSlotForUnitType (type, equipSlot);
  33.218 +			}
  33.219 +		}
  33.220 +
  33.221 +		private void LoadEquipmentSlotForUnitType(UnitType type, XmlElement equipSlot)
  33.222 +		{
  33.223 +			string slotName = equipSlot.GetAttribute("name");
  33.224 +			ILimit limit = GetMaxLimit(equipSlot);
  33.225 +			
  33.226 +			if (limit != null)
  33.227 +			{
  33.228 +				type.AddEquipmentSlot(slotName, limit);
  33.229 +			}
  33.230 +		}
  33.231 +		
  33.232 +		private ILimit GetMinLimit(XmlElement elem)
  33.233 +		{
  33.234 +			return limitParser.GetMinLimit(elem);
  33.235 +		}
  33.236 +
  33.237 +		private ILimit GetMaxLimit(XmlElement elem)
  33.238 +		{
  33.239 +			return limitParser.GetMaxLimit(elem);
  33.240 +		}
  33.241 +
  33.242 +		private void LoadEquipmentForUnitType(XmlElement elem, UnitType type)
  33.243 +		{
  33.244 +			foreach (XmlElement equip in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitEquipment/race:unitEquipmentItem"))
  33.245 +			{
  33.246 +				string id = equip.GetAttribute("id");
  33.247 +				EquipmentItem equipItem = type.Race.GetEquipmentItem(id);
  33.248 +				
  33.249 +				if (equipItem!=null)
  33.250 +				{
  33.251 +					string mutexGroupString = equip.GetAttribute("exclusivityGroups");
  33.252 +					string[] mutexGroups;
  33.253 +
  33.254 +					if (mutexGroupString == "")
  33.255 +					{
  33.256 +						mutexGroupString = equip.GetAttribute("exclusivityGroup");
  33.257 +					}
  33.258 +
  33.259 +					if (mutexGroupString != "")
  33.260 +					{
  33.261 +						string[] groups = mutexGroupString.Split(',');
  33.262 +						int groupCount = groups.Length;
  33.263 +
  33.264 +						for (int i = 0; i < groupCount; i++)
  33.265 +						{
  33.266 +							groups[i] = groups[i].Trim();
  33.267 +						}
  33.268 +
  33.269 +						mutexGroups = groups;
  33.270 +					}
  33.271 +					else
  33.272 +					{
  33.273 +						mutexGroups = new string[0];
  33.274 +					}
  33.275 +
  33.276 +					UnitEquipmentItem unitEquipItem = new UnitEquipmentItem(equipItem, type, mutexGroups);
  33.277 +
  33.278 +					string equipSlot = equip.GetAttribute("equipmentSlot");
  33.279 +
  33.280 +					if (equipSlot != "")
  33.281 +					{
  33.282 +						if (type.HasEquipmentSlot(equipSlot))
  33.283 +						{
  33.284 +							unitEquipItem.SlotName = equipSlot;
  33.285 +						}
  33.286 +						else
  33.287 +						{
  33.288 +							throw new InvalidFileException("Attribute 'equipmentSlot' of unit equipment item " + id + " for " + type.Name + " was not a valid slot name");
  33.289 +						}
  33.290 +					}
  33.291 +
  33.292 +					ILimit limit = GetMaxLimit(equip);
  33.293 +
  33.294 +					if (limit != null)
  33.295 +					{
  33.296 +						unitEquipItem.MaxLimit = limit;
  33.297 +					}
  33.298 +
  33.299 +					limit = GetMinLimit(equip);
  33.300 +
  33.301 +					if (limit != null)
  33.302 +					{
  33.303 +						unitEquipItem.MinLimit = limit;
  33.304 +					}
  33.305 +					
  33.306 +					unitEquipItem.RoundNumberUp = equip.GetAttribute("roundDirection").Equals("up");
  33.307 +					
  33.308 +					try
  33.309 +					{
  33.310 +						unitEquipItem.IsRequired = XmlTools.GetBoolValueFromAttribute(equip, "required");
  33.311 +					}
  33.312 +					catch(FormatException e)
  33.313 +					{
  33.314 +						throw new InvalidFileException("Attribute 'required' of unit equipment item " + id + " for " + type.Name + " was not a valid boolean", e);
  33.315 +					}
  33.316 +					
  33.317 +					try
  33.318 +					{
  33.319 +						unitEquipItem.CostMultiplier = XmlTools.GetDoubleValueFromAttribute(equip, "costMultiplier");
  33.320 +					}
  33.321 +					catch (FormatException e)
  33.322 +					{
  33.323 +						throw new InvalidFileException("Attribute 'costMultiplier' of unit equipment item " + id + " for " + type.Name + " was not a valid decimal number", e);
  33.324 +					}
  33.325 +					
  33.326 +					try
  33.327 +					{
  33.328 +						unitEquipItem.CostRoundType = (RoundType) Enum.Parse(typeof(RoundType), equip.GetAttribute("costRounding"));
  33.329 +					}
  33.330 +					catch (ArgumentException e)
  33.331 +					{
  33.332 +						throw new InvalidFileException("Attribute 'costRounding' of unit equipment item " + id + " for " + type.Name + " was not a valid rounding type", e);
  33.333 +					}
  33.334 +				}
  33.335 +				else
  33.336 +				{
  33.337 +					throw new InvalidFileException("Equipment item with ID '" + id + "' was required by " + type.Name + " but was not found");
  33.338 +				}
  33.339 +			}		
  33.340 +		}
  33.341 +		
  33.342 +		private void LoadAbilitiesForUnitType(XmlElement elem, UnitType type)
  33.343 +		{
  33.344 +			foreach (XmlElement abilityElem in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:unitAbilities/race:unitAbility"))
  33.345 +			{
  33.346 +				string id = abilityElem.GetAttribute("abilityID");
  33.347 +				Ability ability = type.Race.GetAbility(id);
  33.348 +				
  33.349 +				if (ability == null)
  33.350 +				{
  33.351 +					throw new InvalidFileException("Ability for "+type.Name+ " with ID "+id+ " did not exist in race definition");
  33.352 +				}
  33.353 +
  33.354 +				bool required = XmlTools.GetBoolValueFromAttribute(abilityElem, "required");
  33.355 +				type.AddAbility(ability, required);
  33.356 +			}
  33.357 +		}
  33.358 +		
  33.359 +		private void LoadContainedUnitsForUnitType(XmlElement elem, UnitType type)
  33.360 +		{
  33.361 +			foreach (XmlElement containedUnitType in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:contains/race:containedUnit"))
  33.362 +			{
  33.363 +				string id = containedUnitType.GetAttribute("containedID");
  33.364 +				UnitType containedType = GetUnitTypeFromDocument(elem.OwnerDocument, id, type.Race);
  33.365 +
  33.366 +				if (containedType!=null)
  33.367 +				{
  33.368 +					type.AddContainedUnitType(containedType);
  33.369 +				}
  33.370 +				else
  33.371 +				{
  33.372 +					throw new InvalidFileException("Unit type " + type.Name + " tried to contain undefined unit with ID "+id);
  33.373 +				}
  33.374 +			}
  33.375 +		}
  33.376 +
  33.377 +		private void LoadRequirementsForUnitType(XmlElement elem, UnitType type)
  33.378 +		{
  33.379 +			//TODO: Load requirements
  33.380 +		}
  33.381 +		
  33.382 +		private void LoadExtraDataForUnitType(XmlElement elem, UnitType type)
  33.383 +		{
  33.384 +			foreach (XmlElement extraData in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:extraData/race:data"))
  33.385 +			{
  33.386 +				string id = extraData.GetAttribute("id");
  33.387 +				string data = extraData.InnerXml;
  33.388 +				type.AddExtraData(id, data);
  33.389 +			}
  33.390 +		}
  33.391 +		
  33.392 +		private void LoadNotesForUnitType(XmlElement elem, UnitType type)
  33.393 +		{
  33.394 +			XmlNode node = WarFoundryXmlFactoryUtils.SelectSingleNode(elem, "race:notes");
  33.395 +
  33.396 +			if (node!=null)
  33.397 +			{
  33.398 +				type.Notes = node.InnerText;
  33.399 +			}
  33.400 +		}
  33.401 +		
  33.402 +		private Stats ParseUnitStats(XmlElement elem, GameSystem system)
  33.403 +		{
  33.404 +			if (elem == null)
  33.405 +			{
  33.406 +				return null;
  33.407 +			}
  33.408 +			
  33.409 +			String statsID = elem.GetAttribute("statSet");
  33.410 +			SystemStats statsSet;
  33.411 +			
  33.412 +			if (statsID == "")
  33.413 +			{
  33.414 +				statsSet = system.StandardSystemStats;
  33.415 +			}
  33.416 +			else
  33.417 +			{
  33.418 +				statsSet = system.GetSystemStatsForID(statsID);
  33.419 +			}
  33.420 +			
  33.421 +			Stats stats = new Stats(statsSet);
  33.422 +			
  33.423 +			foreach (XmlElement stat in WarFoundryXmlFactoryUtils.SelectNodes(elem, "race:stat"))
  33.424 +			{
  33.425 +				String statName = stat.GetAttribute("name");
  33.426 +				stats.SetStatValue(statName, stat.InnerText);
  33.427 +			}
  33.428 +			
  33.429 +			return stats;
  33.430 +		}
  33.431 +		
  33.432 +		private EquipmentItem CreateEquipmentItemFromElement(XmlElement elem, Race race)
  33.433 +		{
  33.434 +			string id = elem.GetAttribute("id");
  33.435 +			EquipmentItem item = race.GetEquipmentItem(id);
  33.436 +
  33.437 +			if (item == null)
  33.438 +			{
  33.439 +				item = CreateEquipmentItemFromElement(elem, id, race);
  33.440 +			}
  33.441 +			
  33.442 +			return item;
  33.443 +		}
  33.444 +
  33.445 +		private EquipmentItem CreateEquipmentItemFromElement(XmlElement elem, string id, Race race)
  33.446 +		{
  33.447 +			string name = elem.GetAttribute("name");
  33.448 +			EquipmentItem item = new EquipmentItem(id, name, race);
  33.449 +			double cost = 0;
  33.450 +			
  33.451 +			try
  33.452 +			{
  33.453 +				cost = XmlTools.GetDoubleValueFromAttribute(elem, "cost");
  33.454 +			}
  33.455 +			catch(FormatException ex)
  33.456 +			{
  33.457 +				throw new InvalidFileException("Attribute 'cost' of equipment item "+id+" was not a valid number", ex);
  33.458 +			}			
  33.459 +						
  33.460 +			//TODO: Parse equipment stats if there are any
  33.461 +			item.Cost = cost;
  33.462 +			race.AddEquipmentItem(item);			
  33.463 +			return item;
  33.464 +		}
  33.465 +		
  33.466 +		private Ability CreateAbilityFromElement(XmlElement elem, Race race)
  33.467 +		{
  33.468 +			string id = elem.GetAttribute("id");
  33.469 +			string name = elem.GetAttribute("name");
  33.470 +			Ability ability = new Ability(id, name);
  33.471 +			XmlNode node = WarFoundryXmlFactoryUtils.SelectSingleNode(elem, "race:description");
  33.472 +			ability.Description = (node == null) ? "" : node.InnerText;
  33.473 +			race.AddAbility(ability);
  33.474 +			return ability;
  33.475 +		}		
  33.476 +
  33.477 +		private void CreateMemberTypeFromElement(XmlElement elem, Race race)
  33.478 +		{
  33.479 +			Stats stats = ParseUnitStats(WarFoundryXmlFactoryUtils.SelectSingleElement(elem, "race:stats"), race.GameSystem);
  33.480 +			UnitMemberType unitMemberType = new UnitMemberType(elem.GetAttribute("id"), elem.GetAttribute("name"), stats);
  33.481 +			race.AddUnitMemberType(unitMemberType);
  33.482 +		}
  33.483 +	}
  33.484 +}
    34.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    34.2 +++ b/API/Factories/Xml/Zip/StringZipEntrySource.cs	Sun Apr 03 18:50:32 2011 +0000
    34.3 @@ -0,0 +1,30 @@
    34.4 +// This file (StringZipEntrySource.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    34.5 +// 
    34.6 +// 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.
    34.7 +
    34.8 +
    34.9 +using System;
   34.10 +using System.IO;
   34.11 +using IBBoard.Lang;
   34.12 +using ICSharpCode.SharpZipLib.Zip;
   34.13 +
   34.14 +namespace IBBoard.WarFoundry.API.Factories.Xml.Zip
   34.15 +{
   34.16 +	/// <summary>
   34.17 +	/// A simple implementation of IStaticDataSource that lets us add a string directly to a Zip file
   34.18 +	/// </summary>
   34.19 +	public class StringZipEntrySource : IStaticDataSource
   34.20 +	{
   34.21 +		private byte[] entryContent;
   34.22 +		
   34.23 +		public StringZipEntrySource(String content)
   34.24 +		{
   34.25 +			entryContent = StringManipulation.StringToBytes(content);
   34.26 +		}
   34.27 +
   34.28 +		public Stream GetSource()
   34.29 +		{
   34.30 +			return new MemoryStream(entryContent);
   34.31 +		}
   34.32 +	}
   34.33 +}
    35.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    35.2 +++ b/API/FileLoadFailure.cs	Sun Apr 03 18:50:32 2011 +0000
    35.3 @@ -0,0 +1,121 @@
    35.4 +// This file (FileLoadFailure.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard.
    35.5 +//
    35.6 +// 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.
    35.7 +
    35.8 +using System;
    35.9 +using System.IO;
   35.10 +using IBBoard.Lang;
   35.11 +using IBBoard.WarFoundry.API.Factories;
   35.12 +
   35.13 +namespace IBBoard.WarFoundry.API
   35.14 +{
   35.15 +	/// <summary>
   35.16 +	/// A container class that holds information about file load failures. Core information covers the file that failed and a message. Additional information includes the factory loading the file and the excetion that was thrown. Messages are passed through <code>String.Format</code> and supplied with the failed file path and the failing factory
   35.17 +	/// </summary>
   35.18 +	public class FileLoadFailure
   35.19 +	{
   35.20 +		private FileInfo failedFile;
   35.21 +		private IWarFoundryFactory loadingFactory;
   35.22 +		private string defaultMessage;
   35.23 +		private string messageTranslationID;
   35.24 +		private string message;
   35.25 +		private Exception cause;
   35.26 +		
   35.27 +		/// <summary>
   35.28 +		/// Constructor for a failed file load where no factory was found. Translatable messages can be providied through a <code>translationID</code> or skipped by passing <code>null</code>.
   35.29 +		/// </summary>
   35.30 +		/// <param name="file">
   35.31 +		/// The <see cref="FileInfo"/> that failed to load
   35.32 +		/// </param>
   35.33 +		/// <param name="message">
   35.34 +		/// A message about the failure in English - used as a default fall-back message.
   35.35 +		/// </param>
   35.36 +		/// <param name="translationID">
   35.37 +		/// The ID of a translation for the message.
   35.38 +		/// </param>
   35.39 +		public FileLoadFailure(FileInfo file, string message, string translationID) : this (file, null, message, "")
   35.40 +		{
   35.41 +		}
   35.42 +		
   35.43 +		/// <summary>
   35.44 +		/// Constructor for a failed file load where a factory was identified as supporting the file but failed to load it. Translatable messages can be providied through a <code>translationID</code> or skipped by passing <code>null</code>.
   35.45 +		/// </summary>
   35.46 +		/// <param name="file">
   35.47 +		/// The <see cref="FileInfo"/> that failed to load
   35.48 +		/// </param>
   35.49 +		/// <param name="factory">
   35.50 +		/// The <see cref="IWarFoundryFactory"/> that failed to load the file
   35.51 +		/// </param>
   35.52 +		/// <param name="message">
   35.53 +		/// A message about the failure in English - used as a default fall-back message.
   35.54 +		/// </param>
   35.55 +		/// <param name="translationID">
   35.56 +		/// The ID of a translation for the message.
   35.57 +		/// </param>
   35.58 +		public FileLoadFailure(FileInfo file, IWarFoundryFactory factory, string message, string translationID) : this(file, factory, message, translationID, null)
   35.59 +		{
   35.60 +		}
   35.61 +		
   35.62 +		/// <summary>
   35.63 +		/// Constructor for a failed file load where a factory was identified as supporting the file but an exception occurred while loading it. Translatable messages can be providied through a <code>translationID</code> or skipped by passing <code>null</code>.
   35.64 +		/// </summary>
   35.65 +		/// <param name="file">
   35.66 +		/// The <see cref="FileInfo"/> that failed to load
   35.67 +		/// </param>
   35.68 +		/// <param name="factory">
   35.69 +		/// The <see cref="IWarFoundryFactory"/> that failed to load the file
   35.70 +		/// </param>
   35.71 +		/// <param name="message">
   35.72 +		/// A message about the failure in English - used as a default fall-back message.
   35.73 +		/// </param>
   35.74 +		/// <param name="translationID">
   35.75 +		/// The ID of a translation for the message.
   35.76 +		/// </param>
   35.77 +		/// <param name="exception">
   35.78 +		/// The <see cref="Exception"/> that occurred to cause the load to fail
   35.79 +		/// </param>
   35.80 +		public FileLoadFailure(FileInfo file, IWarFoundryFactory factory, string message, string translationID, Exception exception)
   35.81 +		{
   35.82 +			failedFile = file;
   35.83 +			loadingFactory = factory;
   35.84 +			defaultMessage = message;
   35.85 +			messageTranslationID = translationID;
   35.86 +			cause = exception;
   35.87 +		}
   35.88 +
   35.89 +		public FileInfo FailedFile
   35.90 +		{
   35.91 +			get
   35.92 +			{
   35.93 +				return failedFile;
   35.94 +			}
   35.95 +		}
   35.96 +
   35.97 +		public string Message
   35.98 +		{
   35.99 +			get
  35.100 +			{
  35.101 +				if (message == null)
  35.102 +				{
  35.103 +					string fileName = FailedFile.FullName;
  35.104 +					string factoryType = (loadingFactory == null ? "" : loadingFactory.GetType().Name);
  35.105 +					if (messageTranslationID == "" || messageTranslationID == null)
  35.106 +					{
  35.107 +						message = String.Format(defaultMessage, fileName, factoryType);
  35.108 +				 	}
  35.109 +					else
  35.110 +				 	{
  35.111 +						message = Translation.GetTranslation(messageTranslationID, defaultMessage, fileName, factoryType);
  35.112 +					}
  35.113 +				}
  35.114 +				
  35.115 +				return message;
  35.116 +			}
  35.117 +		}
  35.118 +		
  35.119 +		public Exception Exception
  35.120 +		{
  35.121 +			get { return cause; }
  35.122 +		}
  35.123 +	}
  35.124 +}
    36.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    36.2 +++ b/API/Objects/Ability.cs	Sun Apr 03 18:50:32 2011 +0000
    36.3 @@ -0,0 +1,32 @@
    36.4 +// This file (Ability.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    36.5 +//
    36.6 +// 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.
    36.7 +
    36.8 +using System;
    36.9 +
   36.10 +namespace IBBoard.WarFoundry.API.Objects
   36.11 +{
   36.12 +	/// <summary>
   36.13 +	/// An Ability is a special rule that a UnitType has, made up of an ability name and a description.
   36.14 +	/// </summary>
   36.15 +	public class Ability : WarFoundryObject
   36.16 +	{
   36.17 +		private string description;
   36.18 +		
   36.19 +		public Ability(String id, String name) : base(id, name)
   36.20 +		{
   36.21 +		}
   36.22 +		
   36.23 +		public string Description
   36.24 +		{
   36.25 +			get { return description; }
   36.26 +			set
   36.27 +			{
   36.28 +				if (value!=null)
   36.29 +				{
   36.30 +					description = value.Trim();
   36.31 +				}
   36.32 +			}
   36.33 +		}
   36.34 +	}
   36.35 +}
    37.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    37.2 +++ b/API/Objects/AbstractUnitEquipmentItemSelection.cs	Sun Apr 03 18:50:32 2011 +0000
    37.3 @@ -0,0 +1,92 @@
    37.4 +// This file (AbstractUnitEquipmentItemSelection.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    37.5 +// 
    37.6 +// 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.
    37.7 +
    37.8 +using System;
    37.9 +
   37.10 +namespace IBBoard.WarFoundry.API.Objects
   37.11 +{
   37.12 +	/// <summary>
   37.13 +	/// An abstract class that defines a selection of equipment for a unit
   37.14 +	/// </summary>	
   37.15 +	public abstract class AbstractUnitEquipmentItemSelection
   37.16 +	{
   37.17 +		private Unit selectionForUnit;
   37.18 +		private UnitEquipmentItem selectedItem;
   37.19 +		private double amountTaken;
   37.20 +		
   37.21 +		public AbstractUnitEquipmentItemSelection(Unit unit, UnitEquipmentItem item, double amount)
   37.22 +		{
   37.23 +			selectionForUnit = unit;
   37.24 +			selectedItem = item;
   37.25 +			AmountTaken = amount;
   37.26 +		}
   37.27 +		
   37.28 +		public Unit EquipmentForUnit
   37.29 +		{
   37.30 +			get
   37.31 +			{
   37.32 +				return selectionForUnit;
   37.33 +			}
   37.34 +		}
   37.35 +		
   37.36 +		public UnitEquipmentItem EquipmentItem
   37.37 +		{
   37.38 +			get
   37.39 +			{
   37.40 +				return selectedItem;
   37.41 +			}
   37.42 +		}
   37.43 +		
   37.44 +		public double AmountTaken
   37.45 +		{
   37.46 +			get 
   37.47 +			{
   37.48 +				return amountTaken;
   37.49 +			}
   37.50 +			set
   37.51 +			{
   37.52 +				amountTaken = value;
   37.53 +				
   37.54 +				if (!IsValidValue(value))
   37.55 +				{
   37.56 +					//Fire validation failed event (once we have one)
   37.57 +				}
   37.58 +			}
   37.59 +		}
   37.60 +		
   37.61 +		public bool IsValid
   37.62 +		{
   37.63 +			get
   37.64 +			{
   37.65 +				return IsValidValue(AmountTaken) && IsInRange(AmountTaken);
   37.66 +			}	
   37.67 +		}
   37.68 +		
   37.69 +		protected virtual bool IsValidValue(double newValue)
   37.70 +		{
   37.71 +			return true;
   37.72 +		}
   37.73 +		
   37.74 +		protected bool IsInRange(double newValue)
   37.75 +		{
   37.76 +			int unitSize = EquipmentForUnit.Size;
   37.77 +			int minLimit = EquipmentItem.MinLimit.GetLimit(unitSize);
   37.78 +			int maxLimit = EquipmentItem.MaxLimit.GetLimit(unitSize);
   37.79 +			return (minLimit <= newValue) && (newValue <= maxLimit);
   37.80 +		}
   37.81 +		
   37.82 +		public double TotalCost
   37.83 +		{
   37.84 +			get
   37.85 +			{
   37.86 +				return NumberTaken * EquipmentItem.Cost;
   37.87 +			}
   37.88 +		}
   37.89 +		
   37.90 +		public abstract int NumberTaken
   37.91 +		{
   37.92 +			 get;
   37.93 +		}
   37.94 +	}
   37.95 +}
    38.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    38.2 +++ b/API/Objects/Army.cs	Sun Apr 03 18:50:32 2011 +0000
    38.3 @@ -0,0 +1,277 @@
    38.4 +// This file (Army.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    38.5 +//
    38.6 +// 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.
    38.7 +
    38.8 +using System;
    38.9 +using System.IO;
   38.10 +using System.Collections.Generic;
   38.11 +using System.Text;
   38.12 +using System.Xml;
   38.13 +using IBBoard.WarFoundry.API;
   38.14 +using IBBoard.WarFoundry.API.Factories;
   38.15 +using IBBoard.WarFoundry.API.Requirements;
   38.16 +using ICSharpCode.SharpZipLib.Zip;
   38.17 +
   38.18 +namespace IBBoard.WarFoundry.API.Objects
   38.19 +{
   38.20 +	/// <summary>
   38.21 +	/// Summary description for Army.
   38.22 +	/// </summary>
   38.23 +	public class Army : WarFoundryLoadedObject, ICostedWarFoundryObject
   38.24 +	{
   38.25 +		//private GameSystem system;
   38.26 +		private Race armyRace;
   38.27 +		private int maxPoints;
   38.28 +		private double pointsTotal;
   38.29 +		private Dictionary<Category, ArmyCategory> categories;
   38.30 +
   38.31 +		public event ObjectAddDelegate UnitAdded;
   38.32 +		public event ObjectRemoveDelegate UnitRemoved;
   38.33 +		public event FailedUnitRequirementDelegate FailedRequirement;
   38.34 +		public event DoubleValChangedDelegate PointsValueChanged;
   38.35 +		private DoubleValChangedDelegate PointsValueChangedMethod;
   38.36 +		
   38.37 +		public Army(Race race, string armyName, int maxArmyPoints) : this(race, armyName, maxArmyPoints, null)
   38.38 +		{
   38.39 +		}
   38.40 +
   38.41 +		public Army(Race race, string armyName, int maxArmyPoints, ZipFile file) : base(armyName)
   38.42 +		{
   38.43 +			armyRace = race;
   38.44 +			Name = armyName;
   38.45 +			maxPoints = maxArmyPoints;
   38.46 +			PointsValueChangedMethod = new DoubleValChangedDelegate(PointsValueChangedHandler);
   38.47 +		}
   38.48 +		
   38.49 +		public ArmyCategory GetCategory(Category cat)
   38.50 +		{
   38.51 +			ArmyCategory armyCat = null;
   38.52 +			ArmyCategories.TryGetValue(cat, out armyCat);
   38.53 +			return armyCat;
   38.54 +		}
   38.55 +		
   38.56 +		private Dictionary<Category, ArmyCategory> ArmyCategories
   38.57 +		{
   38.58 +			get
   38.59 +			{
   38.60 +				if (categories==null)
   38.61 +				{
   38.62 +					categories = new Dictionary<Category, ArmyCategory>();
   38.63 +					Category[] raceCats = Race.Categories;
   38.64 +					ArmyCategory cat;
   38.65 +					int raceCatCount = raceCats.Length;
   38.66 +
   38.67 +					for (int i = 0; i < raceCatCount; i++)
   38.68 +					{
   38.69 +						Category raceCat = raceCats[i];
   38.70 +						cat = new ArmyCategory(this, raceCat);
   38.71 +						categories[raceCat] = cat;
   38.72 +						cat.PointsValueChanged+= PointsValueChangedMethod;
   38.73 +						cat.UnitAdded+=new ObjectAddDelegate(Army_UnitAdded);
   38.74 +						cat.UnitRemoved+=new ObjectRemoveDelegate(Army_UnitRemoved);
   38.75 +						cat.FailedRequirement+=new FailedUnitRequirementDelegate(Army_FailedRequirement);
   38.76 +					}
   38.77 +				}
   38.78 +				
   38.79 +				return categories;
   38.80 +			}
   38.81 +		}
   38.82 +
   38.83 +		public ArmyCategory[] Categories
   38.84 +		{
   38.85 +			get 
   38.86 +			{
   38.87 +				return DictionaryUtils.ToArray<Category, ArmyCategory>(ArmyCategories);
   38.88 +			}
   38.89 +		}
   38.90 +
   38.91 +		public Race Race
   38.92 +		{
   38.93 +			get { return armyRace; }
   38.94 +		}
   38.95 +
   38.96 +		public GameSystem GameSystem
   38.97 +		{
   38.98 +			get { return (armyRace!=null ? armyRace.GameSystem : null); }
   38.99 +		}
  38.100 +
  38.101 +		protected void OnUnitAdded(Unit unit)
  38.102 +		{
  38.103 +			OnUnitAdded(unit, null);
  38.104 +		}
  38.105 +
  38.106 +		protected void OnUnitAdded(Unit unit, List<FailedUnitRequirement> failedReqs)
  38.107 +		{
  38.108 +			if (UnitAdded != null)
  38.109 +			{
  38.110 +				UnitAdded(unit);
  38.111 +			}
  38.112 +
  38.113 +			OnFailedRequirement(failedReqs);
  38.114 +		}
  38.115 +
  38.116 +		protected void OnUnitRemoved(Unit unit)
  38.117 +		{
  38.118 +			OnUnitRemoved(unit, null);
  38.119 +		}
  38.120 +
  38.121 +		protected void OnUnitRemoved(Unit unit, List<FailedUnitRequirement> failedReqs)
  38.122 +		{
  38.123 +			if (UnitRemoved!=null)
  38.124 +			{
  38.125 +				UnitRemoved(unit);
  38.126 +			}
  38.127 +
  38.128 +			OnFailedRequirement(failedReqs);
  38.129 +		}
  38.130 +
  38.131 +		protected void OnFailedRequirement(List<FailedUnitRequirement> failedReqs)
  38.132 +		{
  38.133 +			if (FailedRequirement != null && failedReqs != null && failedReqs.Count > 0)
  38.134 +			{
  38.135 +				FailedRequirement(failedReqs);
  38.136 +			}
  38.137 +		}
  38.138 +
  38.139 +		private void OnPointsValueChanged(double oldValue, double newValue)
  38.140 +		{
  38.141 +			if (PointsValueChanged!=null)
  38.142 +			{
  38.143 +				PointsValueChanged(this, oldValue, newValue);
  38.144 +			}
  38.145 +		}
  38.146 +		
  38.147 +		private double TotalPoints
  38.148 +		{
  38.149 +			get { return pointsTotal; }
  38.150 +			set
  38.151 +			{
  38.152 +				double oldPoints = pointsTotal;
  38.153 +				pointsTotal = value;
  38.154 +
  38.155 +				if (oldPoints!=pointsTotal)
  38.156 +				{
  38.157 +					OnPointsValueChanged(oldPoints, pointsTotal);
  38.158 +				}
  38.159 +			}
  38.160 +		}
  38.161 +		
  38.162 +		public double Points
  38.163 +		{
  38.164 +			get { return TotalPoints; }
  38.165 +		}
  38.166 +		
  38.167 +		public void AddUnit(Unit unit)
  38.168 +		{
  38.169 +			Category category = unit.UnitType.MainCategory;
  38.170 +			AddUnit(unit, category);
  38.171 +		}
  38.172 +		
  38.173 +		public void AddUnit(Unit unit, Category category)
  38.174 +		{			
  38.175 +			ArmyCategory armyCat = GetCategory(category);
  38.176 +			armyCat.AddUnit(unit);
  38.177 +		}
  38.178 +		
  38.179 +		public void RemoveUnit(Unit unit)
  38.180 +		{
  38.181 +			unit.Category.RemoveUnit(unit);
  38.182 +		}
  38.183 +
  38.184 +		public Unit[] GetUnits(Category cat)
  38.185 +		{
  38.186 +			return GetUnits(this.GetCategory(cat));
  38.187 +		}
  38.188 +
  38.189 +		public Unit[] GetUnits(ArmyCategory cat)
  38.190 +		{
  38.191 +			return cat.GetUnits();
  38.192 +		}
  38.193 +
  38.194 +		public Unit[] GetUnits()
  38.195 +		{
  38.196 +			List<Unit> fullList = new List<Unit>();
  38.197 +
  38.198 +			foreach(ArmyCategory cat in Categories)
  38.199 +			{
  38.200 +				fullList.AddRange(cat.GetUnits());
  38.201 +			}
  38.202 +
  38.203 +			return fullList.ToArray();
  38.204 +		}
  38.205 +
  38.206 +		public int MaxPoints
  38.207 +		{
  38.208 +			get { return maxPoints; }
  38.209 +			set 
  38.210 +			{
  38.211 +				if (value > 0)
  38.212 +				{
  38.213 +					maxPoints = value;
  38.214 +				}
  38.215 +			}
  38.216 +		}
  38.217 +
  38.218 +		private void PointsValueChangedHandler(WarFoundryObject obj, double oldVal, double newVal)
  38.219 +		{
  38.220 +			if (obj is ArmyCategory)
  38.221 +			{
  38.222 +				double points = 0;
  38.223 +
  38.224 +				foreach (ArmyCategory cat in Categories)
  38.225 +				{
  38.226 +					points+= cat.Points;
  38.227 +				}
  38.228 +
  38.229 +				TotalPoints = points;
  38.230 +			}
  38.231 +		}
  38.232 +
  38.233 +		public List<FailedUnitRequirement> CanAddUnit(Unit unit)
  38.234 +		{
  38.235 +			return CanAddUnitType(unit.UnitType);
  38.236 +		}
  38.237 +
  38.238 +		public List<FailedUnitRequirement> CanAddUnitType(UnitType unitType)
  38.239 +		{
  38.240 +			return unitType.CanAddToArmy(this);
  38.241 +		}
  38.242 +
  38.243 +		public List<FailedUnitRequirement> CanRemoveUnit(Unit unit)
  38.244 +		{
  38.245 +			return CanRemoveUnitType(unit.UnitType);
  38.246 +		}
  38.247 +
  38.248 +		public List<FailedUnitRequirement> CanRemoveUnitType(UnitType unitType)
  38.249 +		{
  38.250 +			return unitType.CanRemoveFromArmy(this);
  38.251 +		}
  38.252 +
  38.253 +		public int GetUnitTypeCount(UnitType unitType)
  38.254 +		{
  38.255 +			int count = 0;
  38.256 +
  38.257 +			foreach (ArmyCategory cat in Categories)
  38.258 +			{
  38.259 +				count+= cat.GetUnitTypeCount(unitType);
  38.260 +			}
  38.261 +
  38.262 +			return count;
  38.263 +		}
  38.264 +
  38.265 +		private void Army_UnitAdded(WarFoundryObject val)
  38.266 +		{
  38.267 +			OnUnitAdded((Unit)val);
  38.268 +		}
  38.269 +
  38.270 +		private void Army_UnitRemoved(WarFoundryObject val)
  38.271 +		{
  38.272 +			OnUnitRemoved((Unit)val);
  38.273 +		}
  38.274 +
  38.275 +		private void Army_FailedRequirement(List<FailedUnitRequirement> val)
  38.276 +		{
  38.277 +			OnFailedRequirement(val);
  38.278 +		}
  38.279 +	}
  38.280 +}
    39.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    39.2 +++ b/API/Objects/ArmyCategory.cs	Sun Apr 03 18:50:32 2011 +0000
    39.3 @@ -0,0 +1,184 @@
    39.4 +// This file (ArmyCategory.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    39.5 +//
    39.6 +// 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.
    39.7 +
    39.8 +using System;
    39.9 +using System.Collections.Generic;
   39.10 +using IBBoard.WarFoundry.API.Requirements;
   39.11 +
   39.12 +namespace IBBoard.WarFoundry.API.Objects
   39.13 +{
   39.14 +	/// <summary>
   39.15 +	/// Summary description for ArmyCategory.
   39.16 +	/// </summary>
   39.17 +	public class ArmyCategory : WarFoundryObject, ICostedWarFoundryObject
   39.18 +	{
   39.19 +		private Category category;
   39.20 +		private Army parentArmy;
   39.21 +		private double pointsTotal;
   39.22 +		private List<Unit> units;
   39.23 +		private Dictionary<string, int> unitTypes;
   39.24 +		private DoubleValChangedDelegate PointsValueChangedMethod;
   39.25 +		public event ObjectAddDelegate UnitAdded;
   39.26 +		public event ObjectRemoveDelegate UnitRemoved;
   39.27 +		public event FailedUnitRequirementDelegate FailedRequirement;
   39.28 +		public event DoubleValChangedDelegate PointsValueChanged;
   39.29 +
   39.30 +		public ArmyCategory(Army army, Category cat) : base()
   39.31 +		{
   39.32 +			parentArmy = army;
   39.33 +			category = cat;
   39.34 +			cat.NameChanged+=new StringValChangedDelegate(cat_NameChanged);
   39.35 +			PointsValueChangedMethod = new DoubleValChangedDelegate(PointsValueChangedHandler);
   39.36 +			units = new List<Unit>();
   39.37 +			unitTypes = new Dictionary<string,int>();
   39.38 +		}
   39.39 +
   39.40 +		public Category Category
   39.41 +		{
   39.42 +			get { return category; }
   39.43 +		}
   39.44 +
   39.45 +		public Army ParentArmy
   39.46 +		{
   39.47 +			get { return parentArmy; }
   39.48 +		}
   39.49 +
   39.50 +		public override string ID
   39.51 +		{
   39.52 +			get
   39.53 +			{
   39.54 +				return Category.ID;
   39.55 +			}
   39.56 +			set
   39.57 +			{
   39.58 +				Category.ID = value;
   39.59 +			}
   39.60 +		}
   39.61 +
   39.62 +		public override string Name
   39.63 +		{
   39.64 +			get { return category.Name; }
   39.65 +			set 
   39.66 +			{
   39.67 +				category.Name = value;
   39.68 +			}
   39.69 +		}
   39.70 +
   39.71 +		internal void AddUnit(Unit unit)
   39.72 +		{
   39.73 +			List<FailedUnitRequirement> failedReqs = ParentArmy.CanAddUnit(unit);
   39.74 +			units.Add(unit);
   39.75 +			unit.Category = this;
   39.76 +			unit.PointsValueChanged+= PointsValueChangedMethod;
   39.77 +			int unitTypeCount;
   39.78 +			unitTypes.TryGetValue(unit.UnitType.ID, out unitTypeCount);
   39.79 +			unitTypes[unit.UnitType.ID] = (int)unitTypeCount + 1;
   39.80 +			TotalPoints+= unit.Points;
   39.81 +			OnUnitAdded(unit, failedReqs);
   39.82 +		}
   39.83 +
   39.84 +		internal void RemoveUnit(Unit unit)
   39.85 +		{
   39.86 +			List<FailedUnitRequirement> failedReqs = ParentArmy.CanRemoveUnit(unit);
   39.87 +			units.Remove(unit);
   39.88 +			unitTypes[unit.UnitType.ID] = ((int)unitTypes[unit.UnitType.ID])-1;
   39.89 +			TotalPoints-= unit.Points;
   39.90 +			unit.PointsValueChanged-= PointsValueChangedMethod;
   39.91 +			OnUnitRemoved(unit, failedReqs);
   39.92 +		}
   39.93 +
   39.94 +		public int GetUnitTypeCount(UnitType unitType)
   39.95 +		{
   39.96 +			return unitTypes.ContainsKey(unitType.ID) ? (int)unitTypes[unitType.ID] : 0;
   39.97 +		}
   39.98 +
   39.99 +		public Unit[] GetUnits()
  39.100 +		{
  39.101 +			return units.ToArray();
  39.102 +		}
  39.103 +
  39.104 +		private double TotalPoints
  39.105 +		{
  39.106 +			get { return pointsTotal; }
  39.107 +			set 
  39.108 +			{
  39.109 +				double oldVal = pointsTotal;
  39.110 +				pointsTotal = value;
  39.111 +
  39.112 +				if (oldVal!=pointsTotal)
  39.113 +				{
  39.114 +					OnPointsValueChanged(oldVal, pointsTotal);
  39.115 +				}
  39.116 +			}
  39.117 +		}
  39.118 +
  39.119 +		public double Points
  39.120 +		{
  39.121 +			get { return TotalPoints; }
  39.122 +		}
  39.123 +
  39.124 +		private void PointsValueChangedHandler(WarFoundryObject obj, double oldVal, double newVal)
  39.125 +		{
  39.126 +			if (obj is Unit)
  39.127 +			{
  39.128 +				double diff = newVal - oldVal;
  39.129 +				TotalPoints+= diff;
  39.130 +			}
  39.131 +		}
  39.132 +
  39.133 +		protected void OnUnitAdded(Unit unit)
  39.134 +		{
  39.135 +			OnUnitAdded(unit, null);
  39.136 +		}
  39.137 +
  39.138 +		protected void OnUnitAdded(Unit unit, List<FailedUnitRequirement> failedReqs)
  39.139 +		{
  39.140 +			if (UnitAdded != null)
  39.141 +			{
  39.142 +				UnitAdded(unit);
  39.143 +			}
  39.144 +
  39.145 +			if (FailedRequirement != null && failedReqs != null && failedReqs.Count > 0)
  39.146 +			{
  39.147 +				FailedRequirement(failedReqs);
  39.148 +			}
  39.149 +		}
  39.150 +
  39.151 +		protected void OnUnitRemoved(Unit unit)
  39.152 +		{
  39.153 +			OnUnitRemoved(unit, null);
  39.154 +		}
  39.155 +
  39.156 +		protected void OnUnitRemoved(Unit unit, List<FailedUnitRequirement> failedReqs)
  39.157 +		{
  39.158 +			if (UnitRemoved != null)
  39.159 +			{
  39.160 +				UnitRemoved(unit);
  39.161 +			}
  39.162 +
  39.163 +			if (FailedRequirement != null && failedReqs != null && failedReqs.Count > 0)
  39.164 +			{
  39.165 +				FailedRequirement(failedReqs);
  39.166 +			}
  39.167 +		}
  39.168 +
  39.169 +		protected virtual void OnPointsValueChanged(double oldValue, double newValue)
  39.170 +		{
  39.171 +			if (PointsValueChanged!=null)
  39.172 +			{
  39.173 +				PointsValueChanged(this, oldValue, newValue);
  39.174 +			}
  39.175 +		}
  39.176 +
  39.177 +		protected void cat_NameChanged(WarFoundryObject obj, string oldValue, string newValue)
  39.178 +		{
  39.179 +			OnNameChanged(oldValue, newValue);
  39.180 +		}
  39.181 +				
  39.182 +		public int GetPointsPercentage()
  39.183 +		{
  39.184 +			return (int)Math.Round((Points / ParentArmy.MaxPoints) * 100, 0);
  39.185 +		}
  39.186 +	}
  39.187 +}
    40.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    40.2 +++ b/API/Objects/Category.cs	Sun Apr 03 18:50:32 2011 +0000
    40.3 @@ -0,0 +1,119 @@
    40.4 +// This file (Category.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    40.5 +//
    40.6 +// 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.
    40.7 +
    40.8 +using System;
    40.9 +using System.Xml;
   40.10 +using IBBoard.Logging;
   40.11 +
   40.12 +namespace IBBoard.WarFoundry.API.Objects
   40.13 +{
   40.14 +	/// <summary>
   40.15 +	/// A Category is a definition at the <see cref=" GameSystem"/> or <see cref=" Race"/> level of a group that <see cref=" UnitType"/>s belong to. Each category has a name and a min/max limit on points or percentage of a total army cost that units in the category can total.
   40.16 +	/// </summary>
   40.17 +	public class Category : WarFoundryObject
   40.18 +	{
   40.19 +		private int minPts = 0;
   40.20 +		private int maxPts = WarFoundryCore.INFINITY;
   40.21 +		private int minPc = 0;
   40.22 +		private int maxPc = 100;
   40.23 +
   40.24 +		
   40.25 +		public Category(string id, string name) : base(id, name)
   40.26 +		{
   40.27 +		}
   40.28 +
   40.29 +		protected override string DefaultName()
   40.30 +		{
   40.31 +			return "";
   40.32 +		}
   40.33 +		
   40.34 +		/// <value>
   40.35 +		/// Gets or sets the minimum number of points that the units of this category can cost. Note: This should be set AFTER MaximumPoints, otherwise an unintended default value may be set for the minimum
   40.36 +		/// </value>
   40.37 +		public int MinimumPoints
   40.38 +		{
   40.39 +			get { return minPts; }
   40.40 +			set
   40.41 +			{
   40.42 +				minPts = (value >= 0 ? value : 0);
   40.43 +				CheckMinimumPoints();
   40.44 +			}
   40.45 +		}
   40.46 +		
   40.47 +		/// <value>
   40.48 +		/// Gets or sets the maximum number of points that the units of this category can cost. Note: This should be set BEFORE MinimumPoints, otherwise an unintended default value may be set for the minimum
   40.49 +		/// </value>
   40.50 +		public int MaximumPoints
   40.51 +		{
   40.52 +			get { return maxPts; }
   40.53 +			set
   40.54 +			{
   40.55 +				maxPts = (value >= 0 ? value : WarFoundryCore.INFINITY);
   40.56 +				CheckMinimumPoints();
   40.57 +			}
   40.58 +		}
   40.59 +		
   40.60 +		/// <summary>
   40.61 +		/// Makes sure that the minimum points value isn't more than the maximum points value, hence the warning on the properties
   40.62 +		/// </summary>
   40.63 +		private void CheckMinimumPoints()
   40.64 +		{
   40.65 +			if (MinimumPoints > MaximumPoints && MaximumPoints!=WarFoundryCore.INFINITY)
   40.66 +			{
   40.67 +				MinimumPoints = MaximumPoints;
   40.68 +				LogNotifier.WarnFormat(GetType(), "Category {0} ({1}) had a minimum points limit greater than its maximum points limit.", Name, ID);
   40.69 +			}
   40.70 +		}
   40.71 +		
   40.72 +		/// <value>
   40.73 +		/// Gets or sets the minimum percentage of the total army points value that the units of this category can cost. Note: This should be set AFTER MaximumPercentage, otherwise an unintended default value may be set for the minimum
   40.74 +		/// </value>
   40.75 +		public int MinimumPercentage
   40.76 +		{
   40.77 +			get { return minPc; }
   40.78 +			set
   40.79 +			{
   40.80 +				minPc = (value >= 0 ? value : 0);
   40.81 +				CheckMinimumPercentage();
   40.82 +			}
   40.83 +		}
   40.84 +		
   40.85 +		/// <value>
   40.86 +		/// Gets or sets the maximum percentage of the total army points value that the units of this category can cost. Note: This should be set BEFORE MinimumPercentage, otherwise an unintended default value may be set for the minimum
   40.87 +		/// </value>
   40.88 +		public int MaximumPercentage
   40.89 +		{
   40.90 +			get { return maxPc; }
   40.91 +			set
   40.92 +			{
   40.93 +				if (value < 0)
   40.94 +				{
   40.95 +					maxPc = 0;
   40.96 +				}
   40.97 +				else if (value > 100)
   40.98 +				{
   40.99 +					maxPc = 100;
  40.100 +				}
  40.101 +				else
  40.102 +				{
  40.103 +					maxPc = value;
  40.104 +				}
  40.105 +				
  40.106 +				CheckMinimumPercentage();
  40.107 +			}
  40.108 +		}
  40.109 +		
  40.110 +		/// <summary>
  40.111 +		/// Makes sure that the minimum percentage value isn't more than the maximum points value, hence the warning on the properties
  40.112 +		/// </summary>
  40.113 +		private void CheckMinimumPercentage()
  40.114 +		{
  40.115 +			if (MinimumPercentage > MaximumPercentage)
  40.116 +			{
  40.117 +				MinimumPercentage = MaximumPercentage;
  40.118 +				LogNotifier.WarnFormat(GetType(), "Category {0} ({1}) had a minimum percentage limit greater than its maximum percentage limit.", Name, ID);
  40.119 +			}
  40.120 +		}
  40.121 +	}
  40.122 +}
    41.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    41.2 +++ b/API/Objects/CompositeEquipmentItem.cs	Sun Apr 03 18:50:32 2011 +0000
    41.3 @@ -0,0 +1,39 @@
    41.4 +//  This file (CompositeEquipmentItem.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    41.5 +// 
    41.6 +// 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.
    41.7 +
    41.8 +using System;
    41.9 +using System.Collections.Generic;
   41.10 +
   41.11 +namespace IBBoard.WarFoundry.API.Objects
   41.12 +{
   41.13 +	/// <summary>
   41.14 +	/// A special <see cref=" EquipmentItem"/> that is made up of a number of other <code>EquipmentItem</code>s
   41.15 +	/// </summary>
   41.16 +	public class CompositeEquipmentItem : EquipmentItem
   41.17 +	{
   41.18 +		private List<EquipmentItem> compositeItems;
   41.19 +			
   41.20 +		public CompositeEquipmentItem(string id, string name, Race race) : base(id, name, race)
   41.21 +		{
   41.22 +			compositeItems = new List<EquipmentItem>();
   41.23 +		}
   41.24 +		
   41.25 +		public void AddItem(EquipmentItem item)
   41.26 +		{
   41.27 +			compositeItems.Add(item);
   41.28 +			Cost+= item.Cost;
   41.29 +		}
   41.30 +		
   41.31 +		public void RemoveItem(EquipmentItem item)
   41.32 +		{
   41.33 +			compositeItems.Remove(item);
   41.34 +			Cost-= item.Cost;
   41.35 +		}
   41.36 +		
   41.37 +		public EquipmentItem[] Items
   41.38 +		{
   41.39 +			get { return compositeItems.ToArray(); }
   41.40 +		}
   41.41 +	}
   41.42 +}
    42.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    42.2 +++ b/API/Objects/DuplicateItemException.cs	Sun Apr 03 18:50:32 2011 +0000
    42.3 @@ -0,0 +1,15 @@
    42.4 +// This file (DuplicateItemException.cs) is a part of IBBoard.WarFoundry.API and is copyright 2009 IBBoard.
    42.5 +//
    42.6 +// 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.
    42.7 +
    42.8 +using System;
    42.9 +
   42.10 +namespace IBBoard.WarFoundry
   42.11 +{
   42.12 +	public class DuplicateItemException
   42.13 +	{
   42.14 +		public DuplicateItemException()
   42.15 +		{
   42.16 +		}
   42.17 +	}
   42.18 +}
    43.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    43.2 +++ b/API/Objects/EquipmentItem.cs	Sun Apr 03 18:50:32 2011 +0000
    43.3 @@ -0,0 +1,58 @@
    43.4 +// This file (EquipmentItem.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    43.5 +//
    43.6 +// 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.
    43.7 +
    43.8 +using System;
    43.9 +using System.Xml;
   43.10 +
   43.11 +namespace IBBoard.WarFoundry.API.Objects
   43.12 +{
   43.13 +	/// <summary>
   43.14 +	/// Summary description for EquipmentItem.
   43.15 +	/// </summary>
   43.16 +	public class EquipmentItem : WarFoundryObject
   43.17 +	{
   43.18 +		private double cost;
   43.19 +		private string description;
   43.20 +		private Race equipForRace;
   43.21 +		
   43.22 +		public EquipmentItem(string id, string name, Race race) : base(id, name)
   43.23 +		{
   43.24 +			equipForRace = race;
   43.25 +			description = "";
   43.26 +		}
   43.27 +
   43.28 +		public double Cost
   43.29 +		{
   43.30 +			get { return cost; }
   43.31 +			set
   43.32 +			{
   43.33 +				if (value >= 0)
   43.34 +				{
   43.35 +					cost = value;
   43.36 +				}
   43.37 +			}
   43.38 +		}
   43.39 +		
   43.40 +		public string Description
   43.41 +		{
   43.42 +			get { return description; }
   43.43 +			set { description = (value == null ? "" : value); }
   43.44 +		}
   43.45 +		
   43.46 +		public Race EquipmentForRace
   43.47 +		{
   43.48 +			get { return equipForRace; }
   43.49 +		}
   43.50 +		
   43.51 +		public GameSystem GameSystem
   43.52 +		{
   43.53 +			get { return equipForRace.GameSystem; }
   43.54 +		}
   43.55 +
   43.56 +		public bool CanBeUsedWithItem(EquipmentItem item)
   43.57 +		{
   43.58 +			return true;
   43.59 +		}
   43.60 +	}
   43.61 +}
    44.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    44.2 +++ b/API/Objects/GameSystem.cs	Sun Apr 03 18:50:32 2011 +0000
    44.3 @@ -0,0 +1,354 @@
    44.4 +// This file (GameSystem.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009, 2010, 2011 IBBoard.
    44.5 +//
    44.6 +// 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.
    44.7 +
    44.8 +using System.Collections.Generic;
    44.9 +using IBBoard.WarFoundry.API.Factories;
   44.10 +
   44.11 +namespace IBBoard.WarFoundry.API.Objects
   44.12 +{
   44.13 +	/// <summary>
   44.14 +	/// Summary description for GameSystem.
   44.15 +	/// </summary>
   44.16 +	public class GameSystem : WarFoundryStagedLoadingObject
   44.17 +	{
   44.18 +		private static int SYSTEM_DEFAULT_ARMY_SIZE = 1000;
   44.19 +		private bool warnOnError;
   44.20 +		private bool allowAllies;
   44.21 +		private Dictionary<string, Category> categories = new Dictionary<string, Category>();
   44.22 +		private Dictionary<string, SystemStats> stats = new Dictionary<string, SystemStats>();
   44.23 +		private string defaultStats;
   44.24 +		private int defaultArmySize;
   44.25 +
   44.26 +		public GameSystem(string systemID, string systemName, IWarFoundryFactory creatingFactory) : base(systemID, systemName, creatingFactory)
   44.27 +		{
   44.28 +			stats = new Dictionary<string, SystemStats>();
   44.29 +			defaultStats = "";
   44.30 +		}
   44.31 +
   44.32 +		public int SystemArmyDefaultSize
   44.33 +		{
   44.34 +			get { return defaultArmySize; }
   44.35 +			set
   44.36 +			{
   44.37 +				if (value == 0)
   44.38 +				{
   44.39 +					defaultArmySize = SYSTEM_DEFAULT_ARMY_SIZE;
   44.40 +				}
   44.41 +				else
   44.42 +				{
   44.43 +					defaultArmySize = value;
   44.44 +				}
   44.45 +			}
   44.46 +			}
   44.47 +
   44.48 +		public string SystemPtsAbbrevSingle
   44.49 +		{
   44.50 +			get; set;
   44.51 +		}
   44.52 +		public string SystemPtsAbbrevPlural
   44.53 +		{
   44.54 +			get; set;
   44.55 +		}
   44.56 +		public string SystemPtsNameSingle
   44.57 +		{
   44.58 +			get; set;
   44.59 +		}
   44.60 +		public string SystemPtsNamePlural
   44.61 +		{
   44.62 +			get; set;
   44.63 +		}
   44.64 +		public bool AllowAllies
   44.65 +		{
   44.66 +			get { return allowAllies; }
   44.67 +			set { allowAllies = value; }
   44.68 +		}
   44.69 +
   44.70 +		public void AddCategory(Category cat)
   44.71 +		{
   44.72 +			RawCategories[cat.ID] = cat;
   44.73 +		}
   44.74 +
   44.75 +		public Category GetCategory(string id)
   44.76 +		{
   44.77 +			EnsureFullyLoaded();
   44.78 +			Category cat = null;
   44.79 +			RawCategories.TryGetValue(id, out cat);
   44.80 +			return cat;
   44.81 +		}
   44.82 +
   44.83 +		public void SetCategory(Category cat)
   44.84 +		{
   44.85 +			Category old;
   44.86 +			RawCategories.TryGetValue(cat.ID, out old);
   44.87 +
   44.88 +			if (old == null)
   44.89 +			{
   44.90 +				AddCategory(cat);
   44.91 +			}
   44.92 +			else
   44.93 +			{
   44.94 +				if (!old.Equals(cat))
   44.95 +				{
   44.96 +					RawCategories[old.ID] = cat;
   44.97 +				}
   44.98 +			}
   44.99 +		}
  44.100 +
  44.101 +		public void RemoveCategory(string id)
  44.102 +		{
  44.103 +			RawCategories.Remove(id);
  44.104 +		}
  44.105 +
  44.106 +		public Category[] Categories
  44.107 +		{
  44.108 +			get
  44.109 +			{
  44.110 +				EnsureFullyLoaded();
  44.111 +				return DictionaryUtils.ToArray<string, Category>(RawCategories);
  44.112 +			}
  44.113 +		}
  44.114 +
  44.115 +		protected Dictionary<string, Category> RawCategories
  44.116 +		{
  44.117 +			get { return categories; }
  44.118 +		}
  44.119 +
  44.120 +		public bool WarnOnError
  44.121 +		{
  44.122 +			get
  44.123 +			{
  44.124 +				return warnOnError;
  44.125 +			}
  44.126 +			set { warnOnError = value; }
  44.127 +		}
  44.128 +
  44.129 +		public void AddSystemStats(SystemStats sysStats)
  44.130 +		{
  44.131 +			stats[sysStats.ID] = sysStats;
  44.132 +		}
  44.133 +
  44.134 +		public SystemStats StandardSystemStats
  44.135 +		{
  44.136 +			get
  44.137 +			{
  44.138 +				EnsureFullyLoaded();
  44.139 +				return stats[defaultStats];
  44.140 +			}
  44.141 +		}
  44.142 +
  44.143 +		public string StandardSystemStatsID
  44.144 +		{
  44.145 +			get
  44.146 +			{
  44.147 +				EnsureFullyLoaded();
  44.148 +				return defaultStats;
  44.149 +			}
  44.150 +
  44.151 +			set
  44.152 +			{
  44.153 +				if (value != null && value.Trim().Length > 0)
  44.154 +				{
  44.155 +					defaultStats = value;
  44.156 +				}
  44.157 +			}
  44.158 +		}
  44.159 +
  44.160 +		public SystemStats[] SystemStats
  44.161 +		{
  44.162 +			get
  44.163 +			{
  44.164 +				EnsureFullyLoaded();
  44.165 +				SystemStats[] statsArray = new SystemStats[stats.Count];
  44.166 +				stats.Values.CopyTo(statsArray, 0);
  44.167 +				return statsArray;
  44.168 +			}
  44.169 +		}
  44.170 +
  44.171 +		public SystemStats GetSystemStatsForID(string id)
  44.172 +		{
  44.173 +			EnsureFullyLoaded();
  44.174 +			SystemStats statsForID;
  44.175 +			stats.TryGetValue(id, out statsForID);
  44.176 +			return statsForID;
  44.177 +		}
  44.178 +
  44.179 +		public void SetSystemStats(SystemStats newStats)
  44.180 +		{
  44.181 +			SystemStats old;
  44.182 +			stats.TryGetValue(newStats.ID, out old);
  44.183 +
  44.184 +			if (old == null)
  44.185 +			{
  44.186 +				AddSystemStats(newStats);
  44.187 +			}
  44.188 +			else
  44.189 +			{
  44.190 +				if (!old.Equals(newStats))
  44.191 +				{
  44.192 +					stats[old.ID] = newStats;
  44.193 +				}
  44.194 +			}
  44.195 +		}
  44.196 +
  44.197 +		public void RemoveSystemStats(string id)
  44.198 +		{
  44.199 +			stats.Remove(id);
  44.200 +		}
  44.201 +
  44.202 +		public Race SystemDefaultRace
  44.203 +		{
  44.204 +			get { return WarFoundryLoader.GetDefault().GetRace(this, Race.SYSTEM_DEFAULT_RACE_ID); }
  44.205 +		}
  44.206 +
  44.207 +		public bool Matches(GameSystem otherSystem)
  44.208 +		{
  44.209 +			if (otherSystem == null)
  44.210 +			{
  44.211 +				return false;
  44.212 +			}
  44.213 +
  44.214 +			return this.ID == otherSystem.ID;
  44.215 +		}
  44.216 +
  44.217 +		public override bool Equals(object obj)
  44.218 +		{
  44.219 +			if (obj == null)
  44.220 +			{
  44.221 +				return false;
  44.222 +			}
  44.223 +
  44.224 +			if (obj.GetType().Equals(this.GetType()))
  44.225 +			{
  44.226 +				GameSystem otherSystem = (GameSystem)obj;
  44.227 +
  44.228 +				return this.ID == otherSystem.ID && this.Name == otherSystem.Name && ((this.RawCategories == null && otherSystem.RawCategories == null) || this.RawCategories.Equals(otherSystem.RawCategories));
  44.229 +			}
  44.230 +			else
  44.231 +			{
  44.232 +				return false;
  44.233 +			}
  44.234 +		}
  44.235 +
  44.236 +		public override int GetHashCode()
  44.237 +		{
  44.238 +			return ID.GetHashCode() + Name.GetHashCode() + (RawCategories != null ? RawCategories.GetHashCode() : 0) + warnOnError.GetHashCode();
  44.239 +		}
  44.240 +
  44.241 +		public bool UnitTypeMaxed(UnitType unitType, Army army)
  44.242 +		{
  44.243 +			return unitType.MaxNumber != WarFoundryCore.INFINITY && army.GetUnitTypeCount(unitType) >= unitType.MaxNumber;
  44.244 +		}
  44.245 +
  44.246 +		public bool UnitTypeMinned(UnitType unitType, Army army)
  44.247 +		{
  44.248 +			return army.GetUnitTypeCount(unitType) <= unitType.MinNumber;
  44.249 +		}
  44.250 +
  44.251 +		public List<EquipmentItem> GetSystemEquipmentList()
  44.252 +		{
  44.253 +			List<EquipmentItem> items = new List<EquipmentItem>();
  44.254 +			Race defaultRace = SystemDefaultRace;
  44.255 +
  44.256 +			if (defaultRace != null)
  44.257 +			{
  44.258 +				items = defaultRace.GetEquipmentList();
  44.259 +			}
  44.260 +
  44.261 +			return items;
  44.262 +		}
  44.263 +
  44.264 +		public EquipmentItem GetSystemEquipmentItem(string id)
  44.265 +		{
  44.266 +			EquipmentItem item = null;
  44.267 +			Race defaultRace = SystemDefaultRace;
  44.268 +
  44.269 +			if (defaultRace != null)
  44.270 +			{
  44.271 +				item = defaultRace.GetEquipmentItem(id);
  44.272 +			}
  44.273 +
  44.274 +			return item;
  44.275 +		}
  44.276 +
  44.277 +		public UnitType[] GetSystemUnitTypes(Category cat)
  44.278 +		{
  44.279 +			UnitType[] items = new UnitType[0];
  44.280 +			Race defaultRace = SystemDefaultRace;
  44.281 +
  44.282 +			if (defaultRace != null)
  44.283 +			{
  44.284 +				items = defaultRace.GetUnitTypes(cat);
  44.285 +			}
  44.286 +
  44.287 +			return items;
  44.288 +		}
  44.289 +
  44.290 +		public UnitType GetSystemUnitType(string id)
  44.291 +		{
  44.292 +			UnitType unit = null;
  44.293 +			Race defaultRace = SystemDefaultRace;
  44.294 +
  44.295 +			if (defaultRace != null)
  44.296 +			{
  44.297 +				unit = defaultRace.GetUnitType(id);
  44.298 +			}
  44.299 +
  44.300 +			return unit;
  44.301 +		}
  44.302 +
  44.303 +		public List<Ability> GetSystemAbilityList()
  44.304 +		{
  44.305 +			List<Ability> items = new List<Ability>();
  44.306 +			Race defaultRace = SystemDefaultRace;
  44.307 +
  44.308 +			if (defaultRace != null)
  44.309 +			{
  44.310 +				items = defaultRace.GetAbilityList();
  44.311 +			}
  44.312 +
  44.313 +			return items;
  44.314 +		}
  44.315 +
  44.316 +		public Ability GetSystemAbility(string id)
  44.317 +		{
  44.318 +			Ability ability = null;
  44.319 +			Race defaultRace = SystemDefaultRace;
  44.320 +
  44.321 +			if (defaultRace != null)
  44.322 +			{
  44.323 +				ability = defaultRace.GetAbility(id);
  44.324 +			}
  44.325 +
  44.326 +			return ability;
  44.327 +		}
  44.328 +		
  44.329 +		public string GetPointsAbbrev(double pointTemp)
  44.330 +		{
  44.331 +			string abbrev = (pointTemp == 1 ? GetPreferredString(SystemPtsAbbrevSingle, SystemPtsAbbrevPlural) : GetPreferredString(SystemPtsAbbrevPlural, SystemPtsAbbrevSingle));
  44.332 +			return abbrev;
  44.333 +		}
  44.334 +
  44.335 +		public string GetPointsName(double pointTemp)
  44.336 +		{
  44.337 +			string ptsName = (pointTemp == 1 ? GetPreferredString(SystemPtsNameSingle, SystemPtsNamePlural) : GetPreferredString(SystemPtsNamePlural, SystemPtsNameSingle));
  44.338 +			return ptsName;
  44.339 +		}
  44.340 +		
  44.341 +		private string GetPreferredString(string str1, string str2)
  44.342 +		{
  44.343 +			string preferred = "";
  44.344 +			
  44.345 +			if (str1 != null)
  44.346 +			{
  44.347 +				preferred = str1;
  44.348 +			}
  44.349 +			else if (str2 != null)
  44.350 +			{
  44.351 +				preferred = str2;
  44.352 +			}
  44.353 +			
  44.354 +			return preferred;
  44.355 +		}
  44.356 +	}
  44.357 +}
    45.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    45.2 +++ b/API/Objects/ICostedWarFoundryObject.cs	Sun Apr 03 18:50:32 2011 +0000
    45.3 @@ -0,0 +1,24 @@
    45.4 +// This file (ICostedNamedObject.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard.
    45.5 +//
    45.6 +// 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.
    45.7 +
    45.8 +using System;
    45.9 +
   45.10 +namespace IBBoard.WarFoundry.API.Objects
   45.11 +{
   45.12 +	/// <summary>
   45.13 +	/// An interface for WarFoundry objects that have a points value (cost)
   45.14 +	/// </summary>
   45.15 +	public interface ICostedWarFoundryObject : IWarFoundryObject
   45.16 +	{
   45.17 +		/// <summary>
   45.18 +		/// A getter for the points value of the costed object
   45.19 +		/// </summary>
   45.20 +		double Points { get; }
   45.21 +		
   45.22 +		/// <summary>
   45.23 +		/// An event that is fired when the points value of the object changes
   45.24 +		/// </summary>
   45.25 +		event DoubleValChangedDelegate PointsValueChanged;
   45.26 +	}
   45.27 +}
    46.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    46.2 +++ b/API/Objects/IWarFoundryNativeSourceObject.cs	Sun Apr 03 18:50:32 2011 +0000
    46.3 @@ -0,0 +1,17 @@
    46.4 +// This file (IWarFoundryNativeSourceObject.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    46.5 +//
    46.6 +// 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.
    46.7 +
    46.8 +using System;
    46.9 +using ICSharpCode.SharpZipLib.Zip;
   46.10 +
   46.11 +namespace IBBoard.WarFoundry.API.Objects
   46.12 +{
   46.13 +	/// <summary>
   46.14 +	/// Interface for native WarFoundry objects that are the main object in a file and need to reference the file at a later date.
   46.15 +	/// </summary>
   46.16 +	public interface IWarFoundryNativeSourceObject : IWarFoundryObject
   46.17 +	{
   46.18 +		ZipFile SourceFile { get; set; }
   46.19 +	}
   46.20 +}
    47.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    47.2 +++ b/API/Objects/IWarFoundryObject.cs	Sun Apr 03 18:50:32 2011 +0000
    47.3 @@ -0,0 +1,22 @@
    47.4 +// This file (IWarFoundryObject.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    47.5 +//
    47.6 +// 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.
    47.7 +
    47.8 +using System;
    47.9 +using IBBoard.WarFoundry.API.Factories;
   47.10 +
   47.11 +namespace IBBoard.WarFoundry.API.Objects
   47.12 +{
   47.13 +	public interface IWarFoundryObject
   47.14 +	{
   47.15 +		/// <value>
   47.16 +		/// The unique identifier for the object
   47.17 +		/// </value>
   47.18 +		string ID { get; set; }
   47.19 +
   47.20 +		/// <value>
   47.21 +		/// The display name of the WarFoundry object
   47.22 +		/// </value>
   47.23 +		string Name { get; set;	}
   47.24 +	}
   47.25 +}
    48.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    48.2 +++ b/API/Objects/IWarFoundryStagedLoadObject.cs	Sun Apr 03 18:50:32 2011 +0000
    48.3 @@ -0,0 +1,44 @@
    48.4 +// This file (IWarFoundryStagedLoadObject.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    48.5 +//
    48.6 +// 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.
    48.7 +
    48.8 +using System;
    48.9 +using ICSharpCode.SharpZipLib.Zip;
   48.10 +using IBBoard.WarFoundry.API.Factories;
   48.11 +
   48.12 +namespace IBBoard.WarFoundry.API.Objects
   48.13 +{
   48.14 +	public interface IWarFoundryStagedLoadObject : IWarFoundryObject
   48.15 +	{	
   48.16 +		/// <summary>
   48.17 +		/// Checks whether the object has been fully loaded or whether only the first stage of loading has been performed.
   48.18 +		/// If the object is not fully loaded then the method must finish loading the object.
   48.19 +		/// </summary>
   48.20 +		void EnsureFullyLoaded();
   48.21 +		
   48.22 +		/// <value>
   48.23 +		/// Gets the <code>AbstractNativeWarFoundryFactory</code> that created the object.
   48.24 +		/// </value>
   48.25 +		IWarFoundryFactory Factory	{ get; }
   48.26 +		
   48.27 +		/// <value>
   48.28 +		/// Returns <code>true</code> if the object has been fully loaded with all data, else returns <code>false</code>
   48.29 +		/// </value>
   48.30 +		bool IsFullyLoaded { get; }
   48.31 +		
   48.32 +		/// <value>
   48.33 +		/// Returns <code>true</code> if the object is in the process of being fully loaded with all data, else returns <code>false</code>
   48.34 +		/// </value>
   48.35 +		bool IsLoading { get; }
   48.36 +		
   48.37 +		/// <summary>
   48.38 +		/// Marks the object as fully loaded so that no more load checking is required.
   48.39 +		/// </summary>
   48.40 +		void SetAsFullyLoaded();
   48.41 +		
   48.42 +		/// <summary>
   48.43 +		/// Markes the object as being in the process of being fully loaded.
   48.44 +		/// </summary>
   48.45 +		void SetAsLoading();
   48.46 +	}
   48.47 +}
    49.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    49.2 +++ b/API/Objects/InvalidContainershipException.cs	Sun Apr 03 18:50:32 2011 +0000
    49.3 @@ -0,0 +1,46 @@
    49.4 +//  This file (InvalidContainershipException.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    49.5 +// 
    49.6 +// 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.
    49.7 +
    49.8 +using System;
    49.9 +using IBBoard.Lang;
   49.10 +
   49.11 +namespace IBBoard.WarFoundry.API.Objects
   49.12 +{
   49.13 +	/// <summary>
   49.14 +	/// A custom exception for when a unit was added as a sub-unit of another unit, but was not of a <see cref=" UnitType"/> that can be contained
   49.15 +	/// by that unit.
   49.16 +	/// </summary>
   49.17 +	public class InvalidContainershipException : Exception
   49.18 +	{
   49.19 +		private Unit containing;
   49.20 +		private Unit contained;
   49.21 +		
   49.22 +		public InvalidContainershipException(Unit containingUnit, Unit containedUnit) : base(CreateMessageString(containingUnit, containedUnit))
   49.23 +		{
   49.24 +			containing = containingUnit;
   49.25 +			contained = containedUnit;
   49.26 +		}
   49.27 +		
   49.28 +		private static string CreateMessageString(Unit containingUnit, Unit containedUnit)
   49.29 +		{
   49.30 +			return String.Format("{0} cannot contain {1} because units of type {2} cannot contain units of type {3}", containingUnit.Name, containedUnit.Name, containingUnit.UnitType.Name, containedUnit.UnitType.Name);
   49.31 +		}
   49.32 +		
   49.33 +		/// <value>
   49.34 +		/// The <see cref=" Unit"/> that the contained unit was added to
   49.35 +		/// </value>
   49.36 +		public Unit ContainingUnit
   49.37 +		{
   49.38 +			get { return containing; }
   49.39 +		}
   49.40 +		
   49.41 +		/// <value>
   49.42 +		/// The <see cref=" Unit"/> that was added as a contained unit, but which was not of an allowed type
   49.43 +		/// </value>
   49.44 +		public Unit ContainedUnit
   49.45 +		{
   49.46 +			get { return contained; }
   49.47 +		}
   49.48 +	}
   49.49 +}
    50.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    50.2 +++ b/API/Objects/Race.cs	Sun Apr 03 18:50:32 2011 +0000
    50.3 @@ -0,0 +1,308 @@
    50.4 +// This file (Race.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    50.5 +//
    50.6 +// 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.
    50.7 +
    50.8 +using System;
    50.9 +using System.Collections.Generic;
   50.10 +using System.IO;
   50.11 +using System.Xml;
   50.12 +using IBBoard.IO;
   50.13 +using IBBoard.WarFoundry.API.Factories;
   50.14 +
   50.15 +namespace IBBoard.WarFoundry.API.Objects
   50.16 +{
   50.17 +	public class Race : WarFoundryStagedLoadingObject
   50.18 +	{
   50.19 +		public static string SYSTEM_DEFAULT_RACE_ID = "GameDefault";
   50.20 +
   50.21 +		private string subID;
   50.22 +		private GameSystem system;
   50.23 +		private string defaultArmyName = "";
   50.24 +		private Dictionary<Category, Dictionary<string, UnitType>> unitTypesByCat;
   50.25 +		private Dictionary<string, UnitType> unitTypes = new Dictionary<string,UnitType>();
   50.26 +		private Dictionary<string, EquipmentItem> equipment = new Dictionary<string,EquipmentItem>();
   50.27 +		private Dictionary<string, Ability> abilities = new Dictionary<string,Ability>();
   50.28 +		private Dictionary<string, Category> categories = new Dictionary<string,Category>();
   50.29 +		private Dictionary<string, UnitMemberType> memberTypes = new Dictionary<string, UnitMemberType>();
   50.30 +
   50.31 +		public Race(string raceID, string raceName, GameSystem gameSystem, IWarFoundryFactory creatingFactory) : this(raceID, "", raceName, gameSystem, creatingFactory)
   50.32 +		{
   50.33 +		}
   50.34 +
   50.35 +		public Race(string raceID, string raceSubID, string raceName, GameSystem gameSystem, IWarFoundryFactory creatingFactory) : base(raceID + (raceSubID != "" ? "_" + raceSubID : ""), raceName, creatingFactory)
   50.36 +		{
   50.37 +			subID = (raceSubID == null ? "" : raceSubID);
   50.38 +			system = gameSystem;
   50.39 +		}
   50.40 +
   50.41 +		public string SubID
   50.42 +		{
   50.43 +			get { return subID; }
   50.44 +			set { subID = (value == null ? "" : value.Trim()); }
   50.45 +		}
   50.46 +
   50.47 +		public GameSystem GameSystem
   50.48 +		{
   50.49 +			get { return system; }
   50.50 +			set
   50.51 +			{
   50.52 +				if (value == null)
   50.53 +				{
   50.54 +					throw new ArgumentException("Game system for a race cannot be null");
   50.55 +				}
   50.56 +
   50.57 +				system = value;
   50.58 +			}
   50.59 +		}
   50.60 +
   50.61 +        public string ArmyDefaultName
   50.62 +        {
   50.63 +            get { return defaultArmyName; }
   50.64 +            set
   50.65 +            {
   50.66 +                if (value == null)
   50.67 +                {
   50.68 +                    throw new ArgumentException("No default army name");
   50.69 +                }
   50.70 +
   50.71 +                defaultArmyName = value;
   50.72 +            }
   50.73 +        }
   50.74 +
   50.75 +		public void AddCategory(Category cat)
   50.76 +		{
   50.77 +			categories[cat.ID] = cat;
   50.78 +		}
   50.79 +
   50.80 +		/// <summary>
   50.81 +		/// Gets a category from its ID. Attempts to get the category from the race's overrides, or else it falls back to getting the Game System's category with that ID.
   50.82 +		/// </summary>
   50.83 +		/// <param name="id">
   50.84 +		/// The ID of the category to get
   50.85 +		/// </param>
   50.86 +		/// <returns>
   50.87 +		/// The <code>Category</code> with the specified ID, or null if one doesn't exist.
   50.88 +		/// </returns>
   50.89 +		public Category GetCategory(string id)
   50.90 +		{
   50.91 +			EnsureFullyLoaded();
   50.92 +			Category cat = null;
   50.93 +			categories.TryGetValue(id, out cat);
   50.94 +
   50.95 +			if (cat == null)
   50.96 +			{
   50.97 +				cat = GameSystem.GetCategory(id);
   50.98 +			}
   50.99 +
  50.100 +			return cat;
  50.101 +		}
  50.102 +
  50.103 +		public Category[] Categories
  50.104 +		{
  50.105 +			get
  50.106 +			{
  50.107 +				EnsureFullyLoaded();
  50.108 +				Category[] cats;
  50.109 +
  50.110 +				if (!HasCategoryOverrides())
  50.111 +				{
  50.112 +					cats = GameSystem.Categories;
  50.113 +				}
  50.114 +				else
  50.115 +				{
  50.116 +					cats = DictionaryUtils.ToArray<string, Category>(categories);
  50.117 +				}
  50.118 +
  50.119 +				return cats;
  50.120 +			}
  50.121 +		}
  50.122 +
  50.123 +		public bool HasCategoryOverrides()
  50.124 +		{
  50.125 +			EnsureFullyLoaded();
  50.126 +			return categories.Count > 0;
  50.127 +		}
  50.128 +
  50.129 +		public void AddEquipmentItem(EquipmentItem item)
  50.130 +		{
  50.131 +			//TODO: Throw DuplicateItemException
  50.132 +			equipment.Add(item.ID, item);
  50.133 +		}
  50.134 +
  50.135 +		public EquipmentItem GetEquipmentItem(string id)
  50.136 +		{
  50.137 +			EnsureFullyLoaded();
  50.138 +			return DictionaryUtils.GetValue(equipment, id);
  50.139 +		}
  50.140 +
  50.141 +		public List<EquipmentItem> GetEquipmentList()
  50.142 +		{
  50.143 +			EnsureFullyLoaded();
  50.144 +			List<EquipmentItem> items = new List<EquipmentItem>();
  50.145 +
  50.146 +			foreach (EquipmentItem item in equipment.Values)
  50.147 +			{
  50.148 +				items.Add(item);
  50.149 +			}
  50.150 +
  50.151 +			return items;
  50.152 +		}
  50.153 +
  50.154 +		public void AddUnitType(UnitType type)
  50.155 +		{
  50.156 +			CacheUnitType(type);
  50.157 +			unitTypes.Add(type.ID, type);
  50.158 +		}
  50.159 +
  50.160 +		public UnitType[] GetUnitTypes(Category cat)
  50.161 +		{
  50.162 +			EnsureFullyLoaded();
  50.163 +			BuildUnitTypesByCategoryCache();
  50.164 +			Dictionary<string, UnitType> unitTypesDictionary;
  50.165 +			unitTypesByCat.TryGetValue(cat, out unitTypesDictionary);
  50.166 +			UnitType[] unitTypesArray;
  50.167 +
  50.168 +			if (unitTypesDictionary == null)
  50.169 +			{
  50.170 +				unitTypesArray = new UnitType[0];
  50.171 +			}
  50.172 +			else
  50.173 +			{
  50.174 +				unitTypesArray = DictionaryUtils.ToArray<string, UnitType>(unitTypesDictionary);
  50.175 +			}
  50.176 +
  50.177 +			return unitTypesArray;
  50.178 +		}
  50.179 +
  50.180 +		private void CacheUnitType(UnitType unit)
  50.181 +		{
  50.182 +			BuildUnitTypesByCategoryCache();
  50.183 +
  50.184 +			foreach (Category cat in unit.Categories)
  50.185 +			{
  50.186 +				Dictionary<string, UnitType> catUnitTypes = DictionaryUtils.GetValue(unitTypesByCat, cat);
  50.187 +
  50.188 +				if (catUnitTypes == null)
  50.189 +				{
  50.190 +					throw new InvalidFileException(String.Format("Unit type {0} with name {1} is a unit of an undefined category ({2})", unit.ID, unit.Name, cat.ID));
  50.191 +				}
  50.192 +
  50.193 +				catUnitTypes.Add(unit.ID, unit);
  50.194 +			}
  50.195 +		}
  50.196 +
  50.197 +		private void BuildUnitTypesByCategoryCache()
  50.198 +		{
  50.199 +			if (unitTypesByCat == null)
  50.200 +			{
  50.201 +				DoBuildUnitTypesByCategoryCache();
  50.202 +			}
  50.203 +		}
  50.204 +
  50.205 +		private void DoBuildUnitTypesByCategoryCache()
  50.206 +		{
  50.207 +			unitTypesByCat = new Dictionary<Category,Dictionary<string,UnitType>>();
  50.208 +
  50.209 +			foreach (Category category in Categories)
  50.210 +			{
  50.211 +				unitTypesByCat.Add(category, new Dictionary<string, UnitType>());
  50.212 +			}
  50.213 +
  50.214 +			foreach (UnitType unit in unitTypes.Values)
  50.215 +			{
  50.216 +				CacheUnitType(unit);
  50.217 +			}
  50.218 +		}
  50.219 +
  50.220 +		public UnitType GetUnitType(string id)
  50.221 +		{
  50.222 +			EnsureFullyLoaded();
  50.223 +			return DictionaryUtils.GetValue(unitTypes, id);
  50.224 +		}
  50.225 +
  50.226 +		public List<Ability> GetAbilityList()
  50.227 +		{
  50.228 +			EnsureFullyLoaded();
  50.229 +			List<Ability> items = new List<Ability>();
  50.230 +			items.AddRange(abilities.Values);
  50.231 +			return items;
  50.232 +		}
  50.233 +
  50.234 +		public void AddAbility(Ability newAbility)
  50.235 +		{
  50.236 +			//TODO: Throw DuplicateItemException
  50.237 +			abilities.Add(newAbility.ID, newAbility);
  50.238 +		}
  50.239 +
  50.240 +		public Ability GetAbility(string id)
  50.241 +		{
  50.242 +			EnsureFullyLoaded();
  50.243 +			return DictionaryUtils.GetValue(abilities, id);
  50.244 +		}
  50.245 +
  50.246 +		protected virtual Dictionary<string, UnitType> RaceUnitTypes
  50.247 +		{
  50.248 +			get { return RaceRawUnitTypes; }
  50.249 +			set	{ RaceRawUnitTypes = value; }
  50.250 +		}
  50.251 +
  50.252 +		protected virtual Dictionary<string, EquipmentItem> RaceEquipment
  50.253 +		{
  50.254 +			get { return RaceRawEquipment; }
  50.255 +			set { RaceRawEquipment = value; }
  50.256 +		}
  50.257 +
  50.258 +		protected virtual Dictionary<string, Ability> RaceAbilities
  50.259 +		{
  50.260 +			get { return RaceRawAbilities; }
  50.261 +			set { RaceRawAbilities = value; }
  50.262 +		}
  50.263 +
  50.264 +		protected Dictionary<string, UnitType> RaceRawUnitTypes
  50.265 +		{
  50.266 +			get { return unitTypes; }
  50.267 +			set	{ unitTypes = value; }
  50.268 +		}
  50.269 +
  50.270 +		protected Dictionary<string, EquipmentItem> RaceRawEquipment
  50.271 +		{
  50.272 +			get { return equipment; }
  50.273 +			set { equipment = value; }
  50.274 +		}
  50.275 +
  50.276 +		protected Dictionary<string, Ability> RaceRawAbilities
  50.277 +		{
  50.278 +			get { return abilities; }
  50.279 +			set { abilities = value; }
  50.280 +		}
  50.281 +
  50.282 +		public void AddUnitMemberType(UnitMemberType memberType)
  50.283 +		{
  50.284 +			memberTypes[memberType.ID] = memberType;
  50.285 +		}
  50.286 +
  50.287 +		/// <summary>
  50.288 +		/// Gets a unit member type by its ID.
  50.289 +		/// </summary>
  50.290 +		/// <param name="id">
  50.291 +		/// The ID of the unit member type to get
  50.292 +		/// </param>
  50.293 +		/// <returns>
  50.294 +		/// The <code>UnitMemberType</code> with the specified ID, or null if one doesn't exist.
  50.295 +		/// </returns>
  50.296 +		public UnitMemberType GetUnitMemberType(string id)
  50.297 +		{
  50.298 +			EnsureFullyLoaded();
  50.299 +			return DictionaryUtils.GetValue(memberTypes, id);
  50.300 +		}
  50.301 +
  50.302 +		public UnitMemberType[] UnitMemberTypes
  50.303 +		{
  50.304 +			get
  50.305 +			{
  50.306 +				EnsureFullyLoaded();
  50.307 +				return DictionaryUtils.ToArray(memberTypes);
  50.308 +			}
  50.309 +		}
  50.310 +	}
  50.311 +}
    51.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    51.2 +++ b/API/Objects/Requirement/RequiresAtLeastNUnitsRequirement.cs	Sun Apr 03 18:50:32 2011 +0000
    51.3 @@ -0,0 +1,95 @@
    51.4 +// This file (UnitRequiresAtLeastNUnitsRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2011 IBBoard
    51.5 +// 
    51.6 +// 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.
    51.7 +using System;
    51.8 +using System.Collections.Generic;
    51.9 +using IBBoard.WarFoundry.API.Objects;
   51.10 +
   51.11 +namespace IBBoard.WarFoundry.API.Objects.Requirement
   51.12 +{
   51.13 +	/// <summary>
   51.14 +	/// A requirement where a WarFoundryObject requires at least N units of one or more unit types before any number of that object can be taken in an army.
   51.15 +	/// </summary>
   51.16 +	public class RequiresAtLeastNUnitsRequirement
   51.17 +	{
   51.18 +		private List<UnitCountRequirementData> requiredTypes;
   51.19 +
   51.20 +		public RequiresAtLeastNUnitsRequirement(params UnitType[] requiredUnitTypes)
   51.21 +		{
   51.22 +			requiredTypes = new List<UnitCountRequirementData>();
   51.23 +
   51.24 +			foreach (UnitType unitType in requiredUnitTypes)
   51.25 +			{
   51.26 +				AddUnitTypeRequirement(unitType);
   51.27 +			}
   51.28 +		}
   51.29 +
   51.30 +		/// <summary>
   51.31 +		/// Checks whether the supplied WarFoundryObject can be added to the supplied army.
   51.32 +		/// </summary>
   51.33 +		/// <returns>
   51.34 +		/// <c>true</c> if the object can be added, else <c>false</c>
   51.35 +		/// </returns>
   51.36 +		/// <param name='wfObject'>
   51.37 +		/// The object that we want to add. This may be involved in the check, or it may not affect the evaluation of the requirement
   51.38 +		/// </param>
   51.39 +		/// <param name='toArmy'>
   51.40 +		/// The army to add the object to.
   51.41 +		/// </param>
   51.42 +		public virtual bool AllowsAdding(WarFoundryObject wfObject, Army toArmy)
   51.43 +		{
   51.44 +			return this.ValidatesArmy(toArmy);
   51.45 +		}
   51.46 +
   51.47 +		/// <summary>
   51.48 +		/// Adds a requirement for there to be at least minCount of a given UnitType
   51.49 +		/// </summary>
   51.50 +		/// <param name='unitType'>
   51.51 +		/// The unit type to require.
   51.52 +		/// </param>
   51.53 +		/// <param name='minCount'>
   51.54 +		/// The minimum number of that type that must exist.
   51.55 +		/// </param>
   51.56 +		public void AddUnitTypeRequirement(UnitType unitType, int minCount)
   51.57 +		{
   51.58 +			requiredTypes.Add(new UnitCountRequirementData(unitType, minCount));
   51.59 +		}
   51.60 +
   51.61 +		/// <summary>
   51.62 +		/// Adds a requirement for there to be one or more of a given UnitType
   51.63 +		/// </summary>
   51.64 +		/// <param name='unitType'>
   51.65 +		/// The unit type to require.
   51.66 +		/// </param>
   51.67 +		public void AddUnitTypeRequirement (UnitType unitType)
   51.68 +		{
   51.69 +			AddUnitTypeRequirement(unitType, 1);
   51.70 +		}
   51.71 +
   51.72 +		/// <summary>
   51.73 +		/// Checks whether the supplied army is currently valid according to this requirement.
   51.74 +		/// </summary>
   51.75 +		/// <returns>
   51.76 +		/// <c>true</c> if the army is valid, else <c>false</c>
   51.77 +		/// </returns>
   51.78 +		/// <param name='toValidate'>
   51.79 +		/// The army to validate
   51.80 +		/// </param>
   51.81 +		public bool ValidatesArmy(Army toValidate)
   51.82 +		{
   51.83 +			bool canAdd = true;
   51.84 +
   51.85 +			foreach (UnitCountRequirementData requirement in requiredTypes)
   51.86 +			{
   51.87 +				if (toValidate.GetUnitTypeCount(requirement.UnitType) < requirement.Count)
   51.88 +				{
   51.89 +					canAdd = false;
   51.90 +					break;
   51.91 +				}
   51.92 +			}
   51.93 +
   51.94 +			return canAdd;
   51.95 +		}
   51.96 +	}
   51.97 +}
   51.98 +
    52.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    52.2 +++ b/API/Objects/Requirement/RequiresNoMoreThanNOfUnitTypeRequirement.cs	Sun Apr 03 18:50:32 2011 +0000
    52.3 @@ -0,0 +1,70 @@
    52.4 +// This file (RequiresNoMoreThanNOfUnitTypeRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2011 IBBoard
    52.5 +// 
    52.6 +// 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.
    52.7 +using System;
    52.8 +using IBBoard.WarFoundry.API.Objects;
    52.9 +using System.Collections.Generic;
   52.10 +
   52.11 +namespace IBBoard.WarFoundry.API.Objects.Requirement
   52.12 +{
   52.13 +	/// <summary>
   52.14 +	/// A requirement where a WarFoundryObject cannot be taken in an army if more than N of a UnitType will be in the army.
   52.15 +	/// </summary>
   52.16 +	public class RequiresNoMoreThanNOfUnitTypeRequirement
   52.17 +	{
   52.18 +		private List<UnitCountRequirementData> limitedTypes;
   52.19 +
   52.20 +		public RequiresNoMoreThanNOfUnitTypeRequirement(params UnitType[] limitedUnitTypes)
   52.21 +		{
   52.22 +			limitedTypes = new List<UnitCountRequirementData>();
   52.23 +
   52.24 +			foreach (UnitType unitType in limitedUnitTypes)
   52.25 +			{
   52.26 +				AddUnitTypeRequirement(unitType, 0);
   52.27 +			}
   52.28 +		}
   52.29 +
   52.30 +		/// <summary>
   52.31 +		/// Checks whether the supplied WarFoundryObject can be added to the supplied army.
   52.32 +		/// </summary>
   52.33 +		/// <returns>
   52.34 +		/// <c>true</c> if the object can be added, else <c>false</c>
   52.35 +		/// </returns>
   52.36 +		/// <param name='wfObject'>
   52.37 +		/// The object that we want to add. This may be involved in the check, or it may not affect the evaluation of the requirement
   52.38 +		/// </param>
   52.39 +		/// <param name='toArmy'>
   52.40 +		/// The army to add the object to.
   52.41 +		/// </param>
   52.42 +		public bool AllowsAdding(WarFoundryObject wfObject, Army toArmy)
   52.43 +		{
   52.44 +			bool canAdd = true;
   52.45 +
   52.46 +			foreach (UnitCountRequirementData limit in limitedTypes)
   52.47 +			{
   52.48 +				if (toArmy.GetUnitTypeCount(limit.UnitType) > limit.Count)
   52.49 +				{
   52.50 +					canAdd = false;
   52.51 +					break;
   52.52 +				}
   52.53 +			}
   52.54 +
   52.55 +			return canAdd;
   52.56 +		}
   52.57 +
   52.58 +		/// <summary>
   52.59 +		/// Adds a requirement for there to be at least minCount of a given UnitType
   52.60 +		/// </summary>
   52.61 +		/// <param name='unitType'>
   52.62 +		/// The unit type to require.
   52.63 +		/// </param>
   52.64 +		/// <param name='minCount'>
   52.65 +		/// The minimum number of that type that must exist.
   52.66 +		/// </param>
   52.67 +		public void AddUnitTypeRequirement(UnitType unitType, int minCount)
   52.68 +		{
   52.69 +			limitedTypes.Add(new UnitCountRequirementData(unitType, minCount));
   52.70 +		}
   52.71 +	}
   52.72 +}
   52.73 +
    53.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    53.2 +++ b/API/Objects/Requirement/UnitCountRequirementData.cs	Sun Apr 03 18:50:32 2011 +0000
    53.3 @@ -0,0 +1,31 @@
    53.4 +// This file (UnitCountRequirementData.cs) is a part of the IBBoard.WarFoundry.API.Tests project and is copyright 2011 IBBoard
    53.5 +// 
    53.6 +// 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.
    53.7 +using System;
    53.8 +using IBBoard.WarFoundry.API.Objects;
    53.9 +
   53.10 +namespace IBBoard.WarFoundry.API.Objects.Requirement
   53.11 +{
   53.12 +	public class UnitCountRequirementData
   53.13 +	{
   53.14 +		private UnitType unitType;
   53.15 +		private int count;
   53.16 +
   53.17 +		public UnitCountRequirementData(UnitType unitType, int count)
   53.18 +		{
   53.19 +			this.unitType = unitType;
   53.20 +			this.count = count;
   53.21 +		}
   53.22 +
   53.23 +		public UnitType UnitType
   53.24 +		{
   53.25 +			get { return unitType; }
   53.26 +		}
   53.27 +
   53.28 +		public int Count
   53.29 +		{
   53.30 +			get { return count; }
   53.31 +		}
   53.32 +	}
   53.33 +}
   53.34 +
    54.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    54.2 +++ b/API/Objects/Requirement/UnitRequiresAtLeastNUnitsRequirement.cs	Sun Apr 03 18:50:32 2011 +0000
    54.3 @@ -0,0 +1,45 @@
    54.4 +// This file (UnitRequiresAtLeastNUnitsRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2011 IBBoard
    54.5 +// 
    54.6 +// 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.
    54.7 +using System;
    54.8 +using System.Collections.Generic;
    54.9 +using IBBoard.WarFoundry.API.Objects;
   54.10 +
   54.11 +namespace IBBoard.WarFoundry.API.Objects.Requirement
   54.12 +{
   54.13 +	/// <summary>
   54.14 +	/// A requirement where a UnitType requires at least N units of one or more unit types before any number of that object can be taken in an army.
   54.15 +	/// </summary>
   54.16 +	public class UnitRequiresAtLeastNUnitsRequirement : RequiresAtLeastNUnitsRequirement
   54.17 +	{
   54.18 +		private UnitType requirementOnType;
   54.19 +
   54.20 +		public UnitRequiresAtLeastNUnitsRequirement(UnitType requirementOn) : base()
   54.21 +		{
   54.22 +			requirementOnType = requirementOn;
   54.23 +		}
   54.24 +
   54.25 +		/// <summary>
   54.26 +		/// Checks whether the supplied WarFoundryObject can be added to the supplied army.
   54.27 +		/// </summary>
   54.28 +		/// <returns>
   54.29 +		/// <c>true</c> if the object can be added, else <c>false</c>
   54.30 +		/// </returns>
   54.31 +		/// <param name='wfObject'>
   54.32 +		/// The object that we want to add. This may be involved in the check, or it may not affect the evaluation of the requirement
   54.33 +		/// </param>
   54.34 +		/// <param name='toArmy'>
   54.35 +		/// The army to add the object to.
   54.36 +		/// </param>
   54.37 +		public override bool AllowsAdding(WarFoundryObject wfObject, Army toArmy)
   54.38 +		{
   54.39 +			return IsApplicable(wfObject, toArmy) ? base.ValidatesArmy(toArmy) : true;
   54.40 +		}
   54.41 +
   54.42 +		public bool IsApplicable (WarFoundryObject wfObject, Army toArmy)
   54.43 +		{
   54.44 +			return toArmy.GetUnitTypeCount(requirementOnType) > 0 || requirementOnType.Equals(wfObject) || (wfObject is Unit && requirementOnType.Equals(((Unit)wfObject).UnitType));
   54.45 +		}
   54.46 +	}
   54.47 +}
   54.48 +
    55.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    55.2 +++ b/API/Objects/Stat.cs	Sun Apr 03 18:50:32 2011 +0000
    55.3 @@ -0,0 +1,40 @@
    55.4 +// This file (Stat.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    55.5 +//
    55.6 +// 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.
    55.7 +
    55.8 +using System;
    55.9 +
   55.10 +namespace IBBoard.WarFoundry.API.Objects
   55.11 +{
   55.12 +	/// <summary>
   55.13 +	/// Summary description for Stat.
   55.14 +	/// </summary>
   55.15 +	public class Stat
   55.16 +	{
   55.17 +		private StatSlot parentStatSlot;
   55.18 +		private string statString;
   55.19 +
   55.20 +		public Stat(StatSlot parentSlot, string statValue)
   55.21 +		{
   55.22 +				parentStatSlot = parentSlot;
   55.23 +				statString = statValue;
   55.24 +		}
   55.25 +
   55.26 +		public StatSlot ParentSlot
   55.27 +		{
   55.28 +			get { return parentStatSlot; }
   55.29 +			set { parentStatSlot = value; }
   55.30 +		}
   55.31 +
   55.32 +		public string ParentSlotName
   55.33 +		{
   55.34 +			get { return ParentSlot.Name; }
   55.35 +		}
   55.36 +
   55.37 +		public string SlotValueString
   55.38 +		{
   55.39 +			get { return statString; }
   55.40 +			set { statString = (value == null ? "" : value); }
   55.41 +		}
   55.42 +	}
   55.43 +}
    56.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    56.2 +++ b/API/Objects/StatSlot.cs	Sun Apr 03 18:50:32 2011 +0000
    56.3 @@ -0,0 +1,37 @@
    56.4 +// This file (StatSlot.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    56.5 +//
    56.6 +// 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.
    56.7 +
    56.8 +using System;
    56.9 +using System.Text.RegularExpressions;
   56.10 +using System.Xml;
   56.11 +using IBBoard;
   56.12 +
   56.13 +namespace IBBoard.WarFoundry.API.Objects
   56.14 +{
   56.15 +	/// <summary>
   56.16 +	/// Summary description for StatSlot.
   56.17 +	/// </summary>
   56.18 +	public class StatSlot
   56.19 +	{
   56.20 +		private string name;
   56.21 +		private SystemStats sysStats;
   56.22 +
   56.23 +		public StatSlot(String statName)
   56.24 +		{
   56.25 +			name = statName;
   56.26 +		}
   56.27 +
   56.28 +		public string Name
   56.29 +		{
   56.30 +			get { return name; }
   56.31 +			set { value = name; }
   56.32 +		}
   56.33 +		
   56.34 +		public SystemStats SystemStats
   56.35 +		{
   56.36 +			get { return sysStats; }
   56.37 +			set { sysStats = value; }
   56.38 +		}
   56.39 +	}
   56.40 +}
    57.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    57.2 +++ b/API/Objects/Stats.cs	Sun Apr 03 18:50:32 2011 +0000
    57.3 @@ -0,0 +1,104 @@
    57.4 +// This file (Stats.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    57.5 +//
    57.6 +// 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.
    57.7 +
    57.8 +using System;
    57.9 +using System.Collections.Generic;
   57.10 +
   57.11 +namespace IBBoard.WarFoundry.API.Objects
   57.12 +{
   57.13 +	/// <summary>
   57.14 +	/// Stats defines the statistic/attribute values for an entity (for example a unit or any of their equipment that has a stat line) paired against a <see cref=" SystemStats"/> stat line definition.
   57.15 +	/// </summary>
   57.16 +	public class Stats
   57.17 +	{
   57.18 +		private List<Stat> stats;
   57.19 +		private SystemStats sysStats;
   57.20 +		
   57.21 +		public Stats(SystemStats systemStats)
   57.22 +		{
   57.23 +			sysStats = systemStats;
   57.24 +			int statCount = sysStats.SlotCount;
   57.25 +			stats = new List<Stat>(statCount);
   57.26 +
   57.27 +			foreach (StatSlot slot in sysStats.StatSlots)
   57.28 +			{
   57.29 +				stats.Add(new Stat(slot, ""));
   57.30 +			}
   57.31 +		}
   57.32 +		
   57.33 +		public Stat[] StatsArray
   57.34 +		{
   57.35 +			get { return stats.ToArray(); }
   57.36 +		}
   57.37 +
   57.38 +		public void SetStatValue(string statName, string statValue)
   57.39 +		{
   57.40 +			StatSlot slot = sysStats[statName.ToLower()];
   57.41 +
   57.42 +			if (slot!=null)
   57.43 +			{
   57.44 +				int pos = sysStats.GetStatSlotPosition(slot);
   57.45 +
   57.46 +				if (pos > -1)
   57.47 +				{
   57.48 +					stats[pos] = new Stat(slot, statValue);
   57.49 +				}
   57.50 +			}				
   57.51 +		}
   57.52 +		
   57.53 +		public Stat this[string id]
   57.54 +		{
   57.55 +			get
   57.56 +			{
   57.57 +				StatSlot slot = sysStats[id.ToLower()];
   57.58 +				int pos = sysStats.GetStatSlotPosition(slot);
   57.59 +				Stat stat = null;
   57.60 +				
   57.61 +				try
   57.62 +				{
   57.63 +					stat = this[pos];
   57.64 +				}
   57.65 +				catch (ArgumentException ex)
   57.66 +				{
   57.67 +					throw new ArgumentException(String.Format("Invalid statistic ID {0} for stats based on system stats set {1}", new object[]{id, sysStats.ID}), ex);
   57.68 +				}
   57.69 +				
   57.70 +				return stat;
   57.71 +			}
   57.72 +		}
   57.73 +		
   57.74 +		public Stat this[int pos]
   57.75 +		{
   57.76 +			get
   57.77 +			{
   57.78 +				if (pos < stats.Count && pos >= 0)
   57.79 +				{
   57.80 +					return stats[pos];
   57.81 +				}
   57.82 +				else
   57.83 +				{
   57.84 +					throw new ArgumentException(String.Format("Invalid statistic position {0} for stats based on system stats set {1}", new object[]{pos, sysStats.ID})); 
   57.85 +				}
   57.86 +			}
   57.87 +		}
   57.88 +
   57.89 +		public string GetStatValue(string id)
   57.90 +		{
   57.91 +			return this[id.ToLower()].SlotValueString;
   57.92 +		}
   57.93 +		
   57.94 +		public int StatCount
   57.95 +		{
   57.96 +			get { return stats.Count; }
   57.97 +		}
   57.98 +		
   57.99 +		public string StatsID
  57.100 +		{
  57.101 +			get
  57.102 +			{
  57.103 +				return sysStats.ID;
  57.104 +			}
  57.105 +		}
  57.106 +	}
  57.107 +}
    58.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    58.2 +++ b/API/Objects/SystemStats.cs	Sun Apr 03 18:50:32 2011 +0000
    58.3 @@ -0,0 +1,71 @@
    58.4 +// This file (SystemStats.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    58.5 +//
    58.6 +// 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.
    58.7 +
    58.8 +using System;
    58.9 +using System.Collections.Generic;
   58.10 +
   58.11 +namespace IBBoard.WarFoundry.API.Objects
   58.12 +{
   58.13 +	/// <summary>
   58.14 +	/// SystemStats defines the available statistics/attributes that entity types can use (either a unit or an equipment item that has a stats line). Statistic/attribute values will be defined by a <see cref="Stats"/> object.
   58.15 +	/// </summary>
   58.16 +	public class SystemStats
   58.17 +	{
   58.18 +		private Dictionary<string, StatSlot> statsByName;
   58.19 +		private List<StatSlot> stats;
   58.20 +		private string id;
   58.21 +
   58.22 +		public SystemStats(string statsID)
   58.23 +		{
   58.24 +			id = statsID;
   58.25 +			statsByName = new Dictionary<string, StatSlot>();
   58.26 +			stats = new List<StatSlot>();
   58.27 +		}
   58.28 +
   58.29 +		public void AddStatSlot(string slotName)
   58.30 +		{
   58.31 +			StatSlot slot = new StatSlot(slotName);
   58.32 +			slot.SystemStats = this;
   58.33 +			statsByName[slot.Name.ToLower()] = slot;
   58.34 +			stats.Add(slot);
   58.35 +		}		
   58.36 +
   58.37 +		public StatSlot[] StatSlots
   58.38 +		{
   58.39 +			get
   58.40 +			{
   58.41 +				return stats.ToArray();
   58.42 +			}
   58.43 +		}
   58.44 +		
   58.45 +		public StatSlot this[string statName]
   58.46 +		{
   58.47 +			get 
   58.48 +			{
   58.49 +				return DictionaryUtils.GetValue(statsByName, statName.ToLower());
   58.50 +			}
   58.51 +		}
   58.52 +
   58.53 +		public int GetStatSlotPosition(StatSlot slot)
   58.54 +		{
   58.55 +			return stats.IndexOf(slot);
   58.56 +		}
   58.57 +		
   58.58 +		public void RemoveStatSlot(string name)
   58.59 +		{
   58.60 +			statsByName.Remove(name);
   58.61 +			stats.Remove(this[name]);
   58.62 +		}
   58.63 +		
   58.64 +        public int SlotCount
   58.65 +        {
   58.66 +            get { return stats.Count; }
   58.67 +        }
   58.68 +		
   58.69 +		public string ID
   58.70 +		{
   58.71 +			get { return id; }
   58.72 +		}
   58.73 +	}
   58.74 +}
    59.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    59.2 +++ b/API/Objects/Unit.cs	Sun Apr 03 18:50:32 2011 +0000
    59.3 @@ -0,0 +1,544 @@
    59.4 +// This file (Unit.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 2007, 2008, IBBoard.
    59.5 +//
    59.6 +// 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.
    59.7 +
    59.8 +using System;
    59.9 +using System.Collections.Generic;
   59.10 +using System.Text;
   59.11 +using System.Xml;
   59.12 +using IBBoard.Lang;
   59.13 +using IBBoard.Limits;
   59.14 +using IBBoard.WarFoundry.API.Util;
   59.15 +
   59.16 +namespace IBBoard.WarFoundry.API.Objects
   59.17 +{
   59.18 +	/// <summary>
   59.19 +	/// Summary description for UnitInstance.
   59.20 +	/// </summary>
   59.21 +	public class Unit : WarFoundryObject, ICostedWarFoundryObject
   59.22 +	{
   59.23 +		private UnitType type;
   59.24 +		private int size;
   59.25 +		private Unit parentUnit;
   59.26 +		private double points;
   59.27 +		private ArmyCategory cat;
   59.28 +		private Dictionary<UnitEquipmentItem, AbstractUnitEquipmentItemSelection> equipment = new Dictionary<UnitEquipmentItem, AbstractUnitEquipmentItemSelection>();
   59.29 +		private Dictionary<string, List<AbstractUnitEquipmentItemSelection>> equipmentSlots = new Dictionary<string, List<AbstractUnitEquipmentItemSelection>>();
   59.30 +		private List<Unit> containedUnits = new List<Unit>();
   59.31 +
   59.32 +		public event DoubleValChangedDelegate PointsValueChanged;
   59.33 +		public event IntValChangedDelegate UnitSizeChanged;
   59.34 +		public event DoubleValChangedDelegate UnitEquipmentAmountChanged;
   59.35 +
   59.36 +		public Unit(UnitType unitType, ArmyCategory parentArmyCat) : this(unitType, unitType.MinSize, parentArmyCat)
   59.37 +		{
   59.38 +			//Do nothing extra
   59.39 +		}
   59.40 +
   59.41 +		public Unit(UnitType unitType, int startSize, ArmyCategory parentArmyCat) : this("", "", startSize, unitType, parentArmyCat)
   59.42 +		{
   59.43 +			SetInitialEquipment();
   59.44 +			UnitSizeChanged += new IntValChangedDelegate(RefreshUnitEquipmentAmounts);
   59.45 +		}	
   59.46 +
   59.47 +		public Unit(string id, string name, int startSize, UnitType unitType, ArmyCategory parentArmyCat) : base(id, name)
   59.48 +		{
   59.49 +			Category = parentArmyCat;
   59.50 +			type = unitType;
   59.51 +			Size = startSize;
   59.52 +			CalcCost();
   59.53 +			UnitEquipmentAmountChanged += new DoubleValChangedDelegate(UnitEquipmentAmountChangedHandler);
   59.54 +			UnitSizeChanged += new IntValChangedDelegate(UnitSizeChangedHandler);
   59.55 +			Translation.TranslationChanged += HandleTranslationChanged;
   59.56 +		}
   59.57 +
   59.58 +		private void UnitEquipmentAmountChangedHandler(WarFoundryObject obj, double oldVal, double newVal)
   59.59 +		{
   59.60 +			CalcCost();
   59.61 +		}
   59.62 +
   59.63 +		private void UnitSizeChangedHandler(WarFoundryObject obj, int oldVal, int newVal)
   59.64 +		{
   59.65 +			CalcCost();
   59.66 +			
   59.67 +			if (HasDefaultName())
   59.68 +			{
   59.69 +				OnNameChanged("", Name);
   59.70 +			}
   59.71 +		}
   59.72 +
   59.73 +		protected override string DefaultName()
   59.74 +		{
   59.75 +			if (type != null)
   59.76 +			{
   59.77 +				if (size == 1)
   59.78 +				{
   59.79 +					return type.Name;
   59.80 +				}
   59.81 +				else
   59.82 +				{
   59.83 +					return String.Format(Translation.GetTranslation("defaultUnitName"), size, type.Name);
   59.84 +				}
   59.85 +			}
   59.86 +			else
   59.87 +			{
   59.88 +				return "Unknown Unit";
   59.89 +			}
   59.90 +		}
   59.91 +
   59.92 +		private void HandleTranslationChanged()
   59.93 +		{
   59.94 +			if (type != null && HasDefaultName() && size != 1)
   59.95 +			{
   59.96 +				OnNameChanged(null, DefaultName());
   59.97 +			}
   59.98 +		}
   59.99 +
  59.100 +		private void SetInitialEquipment()
  59.101 +		{
  59.102 +			foreach (UnitEquipmentItem unitEquip in UnitType.GetEquipmentItems())
  59.103 +			{
  59.104 +				if (unitEquip.IsRequired)
  59.105 +				{
  59.106 +					if (CanEquipWithItem(unitEquip))
  59.107 +					{
  59.108 +						ILimit minLimit = unitEquip.MinLimit;
  59.109 +						
  59.110 +						if (minLimit is IPercentageLimit)
  59.111 +						{
  59.112 +							SetEquipmentRatio(unitEquip, UnitEquipmentUtil.GetMinEquipmentPercentage(this, unitEquip));
  59.113 +						}
  59.114 +						else
  59.115 +						{
  59.116 +							SetEquipmentAmount(unitEquip, UnitEquipmentUtil.GetMinEquipmentCount(this, unitEquip));
  59.117 +						}
  59.118 +					}
  59.119 +				}
  59.120 +			}
  59.121 +		}
  59.122 +
  59.123 +		private void CalcCost()
  59.124 +		{
  59.125 +			double oldpoints = points;
  59.126 +			points = type.CostPerTrooper * AdditionalTroopers + type.BaseUnitCost;
  59.127 +
  59.128 +			foreach (AbstractUnitEquipmentItemSelection equipSelection in equipment.Values)
  59.129 +			{
  59.130 +				points += equipSelection.TotalCost;
  59.131 +			}
  59.132 +
  59.133 +			if (oldpoints != points)
  59.134 +			{
  59.135 +				OnPointsValueChanged(oldpoints, points);
  59.136 +			}
  59.137 +		}
  59.138 +
  59.139 +		public int AdditionalTroopers
  59.140 +		{
  59.141 +			get { return Math.Max(Size - type.BaseSize, 0); }
  59.142 +		}
  59.143 +
  59.144 +		public int Size
  59.145 +		{
  59.146 +			get { return size; }
  59.147 +			set
  59.148 +			{
  59.149 +				if (value != size)
  59.150 +				{
  59.151 +					int oldValue = size;
  59.152 +					size = (value > 0 ? value : 1);
  59.153 +					OnUnitSizeChanged(oldValue, size);
  59.154 +				}
  59.155 +			}
  59.156 +		}
  59.157 +
  59.158 +		public UnitType UnitType
  59.159 +		{
  59.160 +			get { return type; }
  59.161 +		}
  59.162 +
  59.163 +		public Army Army
  59.164 +		{
  59.165 +			get { return (Category == null ? null : Category.ParentArmy); }
  59.166 +		}
  59.167 +
  59.168 +		public Race Race
  59.169 +		{
  59.170 +			get { return UnitType.Race; }
  59.171 +		}
  59.172 +
  59.173 +		public ArmyCategory Category
  59.174 +		{
  59.175 +			get
  59.176 +			{
  59.177 +				return cat;
  59.178 +			}
  59.179 +			set { cat = value; }
  59.180 +		}
  59.181 +
  59.182 +		public double Points
  59.183 +		{
  59.184 +			get
  59.185 +			{
  59.186 +				if (points == 0)
  59.187 +				{
  59.188 +					CalcCost();
  59.189 +				}
  59.190 +
  59.191 +				return points;
  59.192 +			}
  59.193 +		}
  59.194 +
  59.195 +		public Unit[] ContainedUnits
  59.196 +		{
  59.197 +			get { return containedUnits.ToArray(); }
  59.198 +		}
  59.199 +
  59.200 +		public void AddContainedUnit(Unit unit)
  59.201 +		{
  59.202 +			if (UnitType.CanContainUnit(unit))
  59.203 +			{
  59.204 +				if (!containedUnits.Contains(unit))
  59.205 +				{
  59.206 +					containedUnits.Add(unit);
  59.207 +				}
  59.208 +				
  59.209 +				unit.ParentUnit = this;
  59.210 +			}
  59.211 +			else
  59.212 +			{
  59.213 +				throw new InvalidContainershipException(this, unit);
  59.214 +			}
  59.215 +		}
  59.216 +
  59.217 +		public void RemoveContainedUnit(Unit unit)
  59.218 +		{
  59.219 +			containedUnits.Remove(unit);
  59.220 +		}
  59.221 +
  59.222 +		public Unit ParentUnit
  59.223 +		{
  59.224 +			get { return parentUnit; }
  59.225 +			set
  59.226 +			{
  59.227 +				if (!(parentUnit == value || (parentUnit != null && parentUnit.Equals(value))))
  59.228 +				{
  59.229 +					parentUnit = value;
  59.230 +					
  59.231 +					if (value != null)
  59.232 +					{
  59.233 +						value.AddContainedUnit(this);
  59.234 +					}
  59.235 +				}
  59.236 +			}
  59.237 +		}
  59.238 +
  59.239 +		public UnitEquipmentItem[] GetEquipment()
  59.240 +		{
  59.241 +			return DictionaryUtils.ToKeyArray(equipment);
  59.242 +		}
  59.243 +
  59.244 +		public EquipmentItem[] GetRequiredEquipment()
  59.245 +		{
  59.246 +			List<EquipmentItem> list = new List<EquipmentItem>();
  59.247 +
  59.248 +			foreach (UnitEquipmentItem item in GetEquipment())
  59.249 +			{
  59.250 +				if (item.IsRequired)
  59.251 +				{
  59.252 +					list.Add(item.EquipmentItem);
  59.253 +				}
  59.254 +			}
  59.255 +
  59.256 +			return list.ToArray();
  59.257 +		}
  59.258 +
  59.259 +		internal AbstractUnitEquipmentItemSelection GetEquipmentSelection(UnitEquipmentItem item)
  59.260 +		{
  59.261 +			return DictionaryUtils.GetValue(equipment, item);
  59.262 +		}
  59.263 +
  59.264 +		public void SetEquipmentAmount(UnitEquipmentItem equip, int amount)
  59.265 +		{
  59.266 +			if (amount < 1 && amount != WarFoundryCore.INFINITY)
  59.267 +			{
  59.268 +				amount = 0;
  59.269 +			}
  59.270 +			
  59.271 +			if (amount == 0)
  59.272 +			{
  59.273 +				RemoveEquipmentItem(equip);
  59.274 +			}
  59.275 +			else
  59.276 +			{
  59.277 +				AbstractUnitEquipmentItemSelection currSelection = DictionaryUtils.GetValue(equipment, equip);
  59.278 +				double oldAmount = (currSelection == null ? 0 : currSelection.AmountTaken);
  59.279 +	
  59.280 +				if (amount != oldAmount)
  59.281 +				{
  59.282 +					if (oldAmount == 0)
  59.283 +					{
  59.284 +						AddEquipmentAmount(equip, amount);
  59.285 +					}
  59.286 +					else if (currSelection is UnitEquipmentNumericSelection)
  59.287 +					{
  59.288 +						//A UnitEquipmentItem shouldn't change its IsRatio value, so assume we already have the right sub-type
  59.289 +						currSelection.AmountTaken = amount;
  59.290 +					}
  59.291 +					else
  59.292 +					{
  59.293 +						RemoveEquipmentItem(equip);
  59.294 +						AddEquipmentAmount(equip, amount);
  59.295 +					}
  59.296 +	
  59.297 +					OnUnitEquipmentAmountChanged(equip, oldAmount, amount);
  59.298 +				}
  59.299 +			}
  59.300 +		}
  59.301 +
  59.302 +		private void AddEquipmentAmount(UnitEquipmentItem equip, int amount)
  59.303 +		{
  59.304 +			AbstractUnitEquipmentItemSelection newItem = new UnitEquipmentNumericSelection(this, equip, amount);			
  59.305 +			equipment[equip] = newItem;
  59.306 +			List<AbstractUnitEquipmentItemSelection> selections = DictionaryUtils.GetValue(equipmentSlots, equip.SlotName);
  59.307 +			
  59.308 +			if (selections == null)
  59.309 +			{
  59.310 +				selections = new List<AbstractUnitEquipmentItemSelection>();
  59.311 +				equipmentSlots[equip.SlotName] = selections;
  59.312 +			}
  59.313 +			
  59.314 +			selections.Add(newItem);
  59.315 +		}
  59.316 +
  59.317 +		public void SetEquipmentRatio(UnitEquipmentItem equip, double ratio)
  59.318 +		{
  59.319 +			if (!equip.IsRatioLimit)
  59.320 +			{
  59.321 +				throw new InvalidOperationException("Equipment with ID " + equip.ID + " for unit of type " + UnitType.ID + " has an absolute limit, not a ratio limit");
  59.322 +			}
  59.323 +			
  59.324 +			if (ratio > 100)
  59.325 +			{
  59.326 +				ratio = 100;
  59.327 +			}
  59.328 +			else if (ratio < 0)
  59.329 +			{
  59.330 +				ratio = 0;
  59.331 +			}
  59.332 +			
  59.333 +			if (ratio == 0)
  59.334 +			{
  59.335 +				RemoveEquipmentItem(equip);
  59.336 +			}
  59.337 +			else
  59.338 +			{
  59.339 +				AbstractUnitEquipmentItemSelection currSelection = DictionaryUtils.GetValue(equipment, equip);
  59.340 +				double oldRatio = (currSelection == null ? 0 : currSelection.AmountTaken);
  59.341 +	
  59.342 +				if (ratio != oldRatio)
  59.343 +				{
  59.344 +					if (oldRatio == 0)
  59.345 +					{
  59.346 +						AddEquipmentRatio(equip, ratio);
  59.347 +					}
  59.348 +					else if (currSelection is UnitEquipmentRatioSelection)
  59.349 +					{
  59.350 +						currSelection.AmountTaken = ratio;
  59.351 +					}
  59.352 +					else
  59.353 +					{
  59.354 +						RemoveEquipmentItem(equip);
  59.355 +						AddEquipmentRatio(equip, ratio);
  59.356 +					}
  59.357 +	
  59.358 +					OnUnitEquipmentAmountChanged(equip, oldRatio, ratio);
  59.359 +				}
  59.360 +			}
  59.361 +		}
  59.362 +
  59.363 +		private void AddEquipmentRatio(UnitEquipmentItem equip, double ratio)
  59.364 +		{
  59.365 +			UnitEquipmentRatioSelection newItem = new UnitEquipmentRatioSelection(this, equip, ratio);
  59.366 +			equipment[equip] = newItem;
  59.367 +			List<AbstractUnitEquipmentItemSelection> selections = DictionaryUtils.GetValue(equipmentSlots, equip.SlotName);
  59.368 +			
  59.369 +			if (selections == null)
  59.370 +			{
  59.371 +				selections = new List<AbstractUnitEquipmentItemSelection>();
  59.372 +				equipmentSlots[equip.SlotName] = selections;
  59.373 +			}
  59.374 +			
  59.375 +			selections.Add(newItem);
  59.376 +		}
  59.377 +
  59.378 +		private void RemoveEquipmentItem(UnitEquipmentItem equip)
  59.379 +		{
  59.380 +			double oldAmount = UnitEquipmentUtil.GetEquipmentAmount(this, equip);
  59.381 +		
  59.382 +			if (oldAmount != 0)
  59.383 +			{
  59.384 +				AbstractUnitEquipmentItemSelection selection = DictionaryUtils.GetValue(equipment, equip);
  59.385 +				equipment.Remove(equip);
  59.386 +				List<AbstractUnitEquipmentItemSelection> slotSelections = DictionaryUtils.GetValue(equipmentSlots, equip.SlotName);
  59.387 +				slotSelections.Remove(selection);
  59.388 +				OnUnitEquipmentAmountChanged(equip, oldAmount, 0);
  59.389 +			}
  59.390 +		}
  59.391 +
  59.392 +		public bool CanEquipWithItem(UnitEquipmentItem item)
  59.393 +		{
  59.394 +			string[] mutexes = item.MutexGroups;
  59.395 +			bool canEquip = false;
  59.396 +
  59.397 +			if (mutexes.Length == 0)
  59.398 +			{
  59.399 +				canEquip = true;
  59.400 +			}
  59.401 +			else
  59.402 +			{
  59.403 +				canEquip = UnitEquipmentUtil.GetBlockingEquipmentItems(this, item).Count == 0;
  59.404 +			}
  59.405 +
  59.406 +			return canEquip;
  59.407 +		}
  59.408 +
  59.409 +		public bool CanEquipWithItem(string equipID)
  59.410 +		{
  59.411 +			return CanEquipWithItem(UnitType.GetEquipmentItem(equipID));
  59.412 +		}
  59.413 +
  59.414 +		private void OnPointsValueChanged(double oldValue, double newValue)
  59.415 +		{
  59.416 +			if (PointsValueChanged != null)
  59.417 +			{
  59.418 +				PointsValueChanged(this, oldValue, newValue);
  59.419 +			}
  59.420 +		}
  59.421 +
  59.422 +		private void OnUnitSizeChanged(int oldValue, int newValue)
  59.423 +		{
  59.424 +			if (UnitSizeChanged != null)
  59.425 +			{
  59.426 +				UnitSizeChanged(this, oldValue, newValue);
  59.427 +			}
  59.428 +		}
  59.429 +
  59.430 +		private void OnUnitEquipmentAmountChanged(UnitEquipmentItem equip, double oldValue, double newValue)
  59.431 +		{
  59.432 +			if (UnitEquipmentAmountChanged != null)
  59.433 +			{
  59.434 +				UnitEquipmentAmountChanged(equip, oldValue, newValue);
  59.435 +			}
  59.436 +		}
  59.437 +
  59.438 +		public Stat[][] UnitStatsArrays
  59.439 +		{
  59.440 +			get { return UnitType.UnitStatsArrays; }
  59.441 +		}
  59.442 +
  59.443 +		public Stat[][] UnitStatsArraysWithName
  59.444 +		{
  59.445 +			get { return UnitType.UnitStatsArraysWithName; }
  59.446 +		}
  59.447 +
  59.448 +		public string[] UnitStatsArrayIDs
  59.449 +		{
  59.450 +			get
  59.451 +			{
  59.452 +				return UnitType.UnitStatsArrayIDs;
  59.453 +			}
  59.454 +		}
  59.455 +
  59.456 +		public string GetStatValue(string statName)
  59.457 +		{
  59.458 +			return UnitType.GetStatValue(statName);
  59.459 +		}
  59.460 +
  59.461 +		public int GetEquipmentAmountInSlot(string slotName)
  59.462 +		{
  59.463 +			int amount = 0;
  59.464 +
  59.465 +			List<AbstractUnitEquipmentItemSelection> selections = GetEquipmentSlotSelections(slotName);
  59.466 +			
  59.467 +			if (selections != null)
  59.468 +			{
  59.469 +				amount = GetSelectionTotal(selections);
  59.470 +			}			
  59.471 +			
  59.472 +			return amount;
  59.473 +		}
  59.474 +
  59.475 +		internal List<AbstractUnitEquipmentItemSelection> GetEquipmentSlotSelections(string slotName)
  59.476 +		{
  59.477 +			return DictionaryUtils.GetValue(equipmentSlots, slotName);
  59.478 +		}
  59.479 +
  59.480 +		/// <summary>
  59.481 +		/// Gets the total amount of items taken for the item's slot, excluding the provided item
  59.482 +		/// </summary>
  59.483 +		/// <param name="item">the item to exclude from the count</param>
  59.484 +		/// <returns>the total number of items</returns>
  59.485 +		public int GetEquipmentAmountInSlotExcludingItem(UnitEquipmentItem item)
  59.486 +		{
  59.487 +			int amount = 0;
  59.488 +
  59.489 +			List<AbstractUnitEquipmentItemSelection> selections = DictionaryUtils.GetValue(equipmentSlots, item.SlotName);
  59.490 +
  59.491 +			if (selections != null)
  59.492 +			{
  59.493 +				selections = new List<AbstractUnitEquipmentItemSelection>(selections);
  59.494 +				RemoveSelectionFromList(item, selections);
  59.495 +				amount = GetSelectionTotal(selections);
  59.496 +			}
  59.497 +
  59.498 +			return amount;
  59.499 +		}
  59.500 +
  59.501 +		private void RemoveSelectionFromList(UnitEquipmentItem item, List<AbstractUnitEquipmentItemSelection> selections)
  59.502 +		{
  59.503 +			AbstractUnitEquipmentItemSelection selection = GetEquipmentSelection(item);
  59.504 +
  59.505 +			if (selection != null)
  59.506 +			{
  59.507 +				selections.Remove(selection);
  59.508 +			}
  59.509 +		}
  59.510 +
  59.511 +		private int GetSelectionTotal(List<AbstractUnitEquipmentItemSelection> selections)
  59.512 +		{
  59.513 +			int amount = 0;
  59.514 +			
  59.515 +			foreach (AbstractUnitEquipmentItemSelection selection in selections)
  59.516 +			{
  59.517 +				amount += selection.NumberTaken;
  59.518 +			}
  59.519 +			
  59.520 +			return amount;
  59.521 +		}
  59.522 +		
  59.523 +		/// <summary>
  59.524 +		/// Default stub implementation of getting the unit's abilities - defaults to just the unit type's required abilities until we get a way to modify them
  59.525 +		/// </summary>
  59.526 +		public ICollection<Ability> Abilities
  59.527 +		{
  59.528 +			get
  59.529 +			{
  59.530 +				return UnitType.GetRequiredAbilities();
  59.531 +			}
  59.532 +		}
  59.533 +
  59.534 +		private void RefreshUnitEquipmentAmounts(WarFoundryObject obj, int oldValue, int newValue)
  59.535 +		{
  59.536 +			foreach (UnitEquipmentItem item in equipment.Keys)
  59.537 +			{
  59.538 +				AbstractUnitEquipmentItemSelection selection = equipment[item];
  59.539 +				
  59.540 +				if (selection is UnitEquipmentRatioSelection)
  59.541 +				{
  59.542 +					OnUnitEquipmentAmountChanged(item, selection.AmountTaken, selection.AmountTaken);
  59.543 +				}
  59.544 +			}
  59.545 +		}	
  59.546 +	}
  59.547 +}
    60.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    60.2 +++ b/API/Objects/UnitEquipmentItem.cs	Sun Apr 03 18:50:32 2011 +0000
    60.3 @@ -0,0 +1,248 @@
    60.4 +// This file (UnitEquipmentItem.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    60.5 +//
    60.6 +// 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.
    60.7 +
    60.8 +using System;
    60.9 +using IBBoard.CustomMath;
   60.10 +using IBBoard.Limits;
   60.11 +using IBBoard.WarFoundry.API.Util;
   60.12 +using IBBoard.Lang;
   60.13 +//using IBBoard.WarFoundry.API.Objects;
   60.14 +
   60.15 +namespace IBBoard.WarFoundry.API.Objects
   60.16 +{
   60.17 +	/// <summary>
   60.18 +	/// Summary description for UnitEquipmentItem.
   60.19 +	/// </summary>
   60.20 +	public class UnitEquipmentItem : WarFoundryObject
   60.21 +	{
   60.22 +		private EquipmentItem item;
   60.23 +		private bool required;
   60.24 +		private bool roundUp;
   60.25 +		private double costMultiplier;
   60.26 +		private RoundType roundType;
   60.27 +		private string[] mutexGroups;
   60.28 +		private UnitType unitType;
   60.29 +		private string slotName = "";
   60.30 +		private ILimit minLimit;
   60.31 +		private ILimit maxLimit;
   60.32 +		public UnitEquipmentItem(EquipmentItem equipmentItem, UnitType equipmentFor)
   60.33 +			: this(equipmentItem, equipmentFor, new string[0])
   60.34 +		{
   60.35 +			//Do nothing extra
   60.36 +		}
   60.37 +
   60.38 +		public UnitEquipmentItem(EquipmentItem equipmentItem, UnitType equipmentFor, params string[] mutexGroups)
   60.39 +		{
   60.40 +			item = equipmentItem;
   60.41 +			unitType = equipmentFor;
   60.42 +			this.mutexGroups = mutexGroups;
   60.43 +			unitType.AddEquipmentItem(this);
   60.44 +		}
   60.45 +
   60.46 +		public override string Name
   60.47 +		{
   60.48 +			get
   60.49 +			{
   60.50 +				return item.Name;
   60.51 +			}
   60.52 +			set
   60.53 +			{
   60.54 +				base.Name = value;
   60.55 +			}
   60.56 +		}
   60.57 +
   60.58 +		public override string ID
   60.59 +		{
   60.60 +			get
   60.61 +			{
   60.62 +				return (EquipmentForUnit == null ? base.ID : EquipmentForUnit.ID) + EquipmentItemID;
   60.63 +			}
   60.64 +			set
   60.65 +			{
   60.66 +				base.ID = value;
   60.67 +			}
   60.68 +		}
   60.69 +
   60.70 +		public string EquipmentItemID
   60.71 +		{
   60.72 +			get { return item.ID; }
   60.73 +		}
   60.74 +
   60.75 +		public double Cost
   60.76 +		{
   60.77 +			get
   60.78 +			{
   60.79 +				return IBBMath.Round(EquipmentItem.Cost * CostMultiplier, CostRoundType);
   60.80 +			}
   60.81 +		}
   60.82 +
   60.83 +		public double CostMultiplier
   60.84 +		{
   60.85 +			get { return costMultiplier; }
   60.86 +			set
   60.87 +			{
   60.88 +				costMultiplier = value;
   60.89 +			}
   60.90 +		}
   60.91 +
   60.92 +		public RoundType CostRoundType
   60.93 +		{
   60.94 +			get { return roundType; }
   60.95 +			set
   60.96 +			{
   60.97 +				roundType = value;
   60.98 +			}
   60.99 +		}
  60.100 +
  60.101 +		public bool IsRequired
  60.102 +		{
  60.103 +			get { return required; }
  60.104 +			set { required = value; }
  60.105 +		}
  60.106 +
  60.107 +		public bool RoundNumberUp
  60.108 +		{
  60.109 +			get { return roundUp; }
  60.110 +			set { roundUp = value; }
  60.111 +		}
  60.112 +		
  60.113 +		public GameSystem GameSystem
  60.114 +		{
  60.115 +			get { return EquipmentItem.GameSystem; }
  60.116 +		}
  60.117 +
  60.118 +		public String[] MutexGroups
  60.119 +		{
  60.120 +			get { return (string[]) mutexGroups.Clone(); }
  60.121 +		}
  60.122 +
  60.123 +		public UnitType EquipmentForUnit
  60.124 +		{
  60.125 +			get { return unitType; }
  60.126 +		}
  60.127 +
  60.128 +		public bool IsRatioLimit
  60.129 +		{
  60.130 +			get { return MinLimit is IPercentageLimit && MaxLimit is IPercentageLimit; }
  60.131 +		}
  60.132 +		
  60.133 +		/// <summary>
  60.134 +		/// Gets the Limit object for the minimum number of items that can be taken
  60.135 +		/// </summary>
  60.136 +		public ILimit MinLimit
  60.137 +		{
  60.138 +			get
  60.139 +			{
  60.140 +				ILimit limit = minLimit;
  60.141 +				
  60.142 +				if (limit == null)
  60.143 +				{
  60.144 +					if (maxLimit != null)
  60.145 +					{
  60.146 +						limit = maxLimit;
  60.147 +					}
  60.148 +					else
  60.149 +					{
  60.150 +						limit = new SimpleRoundedPercentageLimit(100, false);
  60.151 +					}
  60.152 +				}
  60.153 +				
  60.154 +				return limit;
  60.155 +			}
  60.156 +			set
  60.157 +			{
  60.158 +				if (value != null)
  60.159 +				{
  60.160 +					minLimit = value;
  60.161 +				}
  60.162 +			}
  60.163 +		}
  60.164 +		
  60.165 +		/// <summary>
  60.166 +		/// Gets the Limit object for the maximum number of items that can be taken
  60.167 +		/// </summary>
  60.168 +		public ILimit MaxLimit
  60.169 +		{
  60.170 +			get
  60.171 +			{
  60.172 +				ILimit limit = maxLimit;
  60.173 +				
  60.174 +				if (limit == null)
  60.175 +				{
  60.176 +					if (minLimit != null)
  60.177 +					{
  60.178 +						limit = minLimit;
  60.179 +					}
  60.180 +					else
  60.181 +					{
  60.182 +						limit = new SimpleRoundedPercentageLimit(100, false);
  60.183 +					}
  60.184 +				}
  60.185 +				
  60.186 +				return limit;
  60.187 +			}
  60.188 +			set
  60.189 +			{
  60.190 +				if (value != null)
  60.191 +				{
  60.192 +					maxLimit = value;
  60.193 +				}
  60.194 +			}
  60.195 +		}
  60.196 +
  60.197 +		public EquipmentItem EquipmentItem
  60.198 +		{
  60.199 +			get { return item; }
  60.200 +		}
  60.201 +
  60.202 +		public override string ToString()
  60.203 +		{
  60.204 +            return Translation.GetTranslation("UnitEquipmentItemName", "{0} ({1}{2} each)", Name, Cost, GameSystem.GetPointsAbbrev(Cost));
  60.205 +		}
  60.206 +
  60.207 +		public bool HasAlternatives()
  60.208 +		{
  60.209 +			if (MutexGroups.Length == 0)
  60.210 +			{
  60.211 +				return false;
  60.212 +			}
  60.213 +			else if (EquipmentForUnit == null)
  60.214 +			{
  60.215 +				return false;
  60.216 +			}
  60.217 +			else
  60.218 +			{
  60.219 +				//If the number of items in the MutEx group is greater than one then it must be this item plus another
  60.220 +				return EquipmentForUnit.GetEquipmentItemsByExclusionGroups(MutexGroups).Length > 1;
  60.221 +			}
  60.222 +		}
  60.223 +
  60.224 +		public string Description
  60.225 +		{
  60.226 +			get { return EquipmentItem.Description; }
  60.227 +		}
  60.228 +
  60.229 +		public Race EquipmentForRace
  60.230 +		{
  60.231 +			get { return EquipmentItem.EquipmentForRace; }
  60.232 +		}
  60.233 +
  60.234 +		public bool CanBeUsedWithItem(EquipmentItem item)
  60.235 +		{
  60.236 +			return EquipmentItem.CanBeUsedWithItem(item);
  60.237 +		}
  60.238 +
  60.239 +		public string SlotName
  60.240 +		{
  60.241 +			get { return slotName; }
  60.242 +			set
  60.243 +			{
  60.244 +				if (value != null && value != "")
  60.245 +				{
  60.246 +					slotName = value;
  60.247 +				}
  60.248 +			}
  60.249 +		}
  60.250 +	}
  60.251 +}
    61.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    61.2 +++ b/API/Objects/UnitEquipmentNumericSelection.cs	Sun Apr 03 18:50:32 2011 +0000
    61.3 @@ -0,0 +1,40 @@
    61.4 +// This file (UnitEquipmentNumericSelection.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    61.5 +// 
    61.6 +// 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.
    61.7 +
    61.8 +using System;
    61.9 +
   61.10 +namespace IBBoard.WarFoundry.API.Objects
   61.11 +{	
   61.12 +	/// <summary>
   61.13 +	/// An object to hold the selection of a unit's equipment where the selection was made as an absolute number
   61.14 +	/// </summary>
   61.15 +	public class UnitEquipmentNumericSelection : AbstractUnitEquipmentItemSelection
   61.16 +	{	
   61.17 +		public UnitEquipmentNumericSelection(Unit unit, UnitEquipmentItem item, double amount) : base(unit, item, amount)
   61.18 +		{
   61.19 +		}
   61.20 +		
   61.21 +		public UnitEquipmentNumericSelection(Unit unit, UnitEquipmentItem item) : base(unit, item, item.MinLimit.GetLimit(unit.Size))
   61.22 +		{
   61.23 +		}
   61.24 +					
   61.25 +		public override int NumberTaken
   61.26 +		{
   61.27 +			get
   61.28 +			{
   61.29 +				return (int) AmountTaken;
   61.30 +			}
   61.31 +		}
   61.32 +		
   61.33 +		protected bool IsWholeNumber(double newValue)
   61.34 +		{
   61.35 +			return newValue == Math.Round(newValue);
   61.36 +		}
   61.37 +		
   61.38 +		protected override bool IsValidValue (double newValue)
   61.39 +		{
   61.40 +			return base.IsValidValue(newValue) && IsWholeNumber(newValue);
   61.41 +		}
   61.42 +	}
   61.43 +}
    62.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    62.2 +++ b/API/Objects/UnitEquipmentRatioSelection.cs	Sun Apr 03 18:50:32 2011 +0000
    62.3 @@ -0,0 +1,43 @@
    62.4 +// This file (UnitEquipmentRatioSelection.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2009 IBBoard
    62.5 +// 
    62.6 +// 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.
    62.7 +
    62.8 +using System;
    62.9 +using IBBoard.CustomMath;
   62.10 +using IBBoard.Limits;
   62.11 +using IBBoard.WarFoundry.API.Util;
   62.12 +
   62.13 +namespace IBBoard.WarFoundry.API.Objects
   62.14 +{	
   62.15 +	/// <summary>
   62.16 +	/// An object to hold the selection of a unit's equipment where the selection was made as a percentage or ratio
   62.17 +	/// of the total size of the unit
   62.18 +	/// </summary>
   62.19 +	public class UnitEquipmentRatioSelection : AbstractUnitEquipmentItemSelection
   62.20 +	{	
   62.21 +		public UnitEquipmentRatioSelection(Unit unit, UnitEquipmentItem item, double amount) : base(unit, item, amount)
   62.22 +		{
   62.23 +		}
   62.24 +		
   62.25 +		public UnitEquipmentRatioSelection(Unit unit, UnitEquipmentItem item) : base(unit, item, ((IPercentageLimit)item.MinLimit).Percentage)
   62.26 +		{
   62.27 +		}
   62.28 +					
   62.29 +		public override int NumberTaken
   62.30 +		{
   62.31 +			get
   62.32 +			{
   62.33 +				return CalculateNumberTaken (EquipmentForUnit, EquipmentItem, AmountTaken);
   62.34 +			}
   62.35 +		}
   62.36 +
   62.37 +		internal static int CalculateNumberTaken (Unit unit, UnitEquipmentItem item, double ratioTaken)
   62.38 +		{
   62.39 +			double exactNumberTaken = (ratioTaken / 100) * unit.Size;
   62.40 +			int wholeNumberTaken = (int)IBBMath.Round (exactNumberTaken, item.RoundNumberUp);
   62.41 +			int maxTaken = UnitEquipmentUtil.GetMaxEquipmentCount (unit, item);
   62.42 +			int minTaken = UnitEquipmentUtil.GetMinEquipmentCount (unit, item);
   62.43 +			return Math.Min (Math.Max (wholeNumberTaken, minTaken), maxTaken);
   62.44 +		}
   62.45 +	}
   62.46 +}
    63.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    63.2 +++ b/API/Objects/UnitMemberType.cs	Sun Apr 03 18:50:32 2011 +0000
    63.3 @@ -0,0 +1,53 @@
    63.4 +//  This file (UnitMember.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2010 IBBoard
    63.5 +// 
    63.6 +//  // 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.
    63.7 +
    63.8 +using System;
    63.9 +
   63.10 +namespace IBBoard.WarFoundry.API.Objects
   63.11 +{
   63.12 +	/// <summary>
   63.13 +	/// A container object for representations of different unit member types, such as "Infantry", "Elite Infantry" and "Commoner".
   63.14 +	/// 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
   63.15 +	/// are often just differently equipped versions of the same member type (Infantry).
   63.16 +	/// </summary>
   63.17 +	public class UnitMemberType : WarFoundryObject
   63.18 +	{
   63.19 +		private Stats stats;
   63.20 +		
   63.21 +		public UnitMemberType(string typeID, string typeName, Stats typeStats) : base(typeID, typeName)
   63.22 +		{
   63.23 +			stats = typeStats;
   63.24 +		}
   63.25 +		
   63.26 +		public string StatsID
   63.27 +		{
   63.28 +			get 
   63.29 +			{
   63.30 +				return stats.StatsID;
   63.31 +			}
   63.32 +		}
   63.33 +
   63.34 +		/// <value>
   63.35 +		/// The set of <see cref="Stat"/>s for the unit member type in a format that is valid for the game system.
   63.36 +		/// </value>
   63.37 +		public Stat[] StatsArray
   63.38 +		{
   63.39 +			get 
   63.40 +			{				
   63.41 +				return stats.StatsArray;
   63.42 +			}
   63.43 +		}
   63.44 +		
   63.45 +		public Stat[] StatsArrayWithName
   63.46 +		{
   63.47 +			get
   63.48 +			{
   63.49 +				Stat[] extendedStats = new Stat[stats.StatCount+1];
   63.50 +				extendedStats[0] = new Stat(new StatSlot("name"), Name);
   63.51 +				stats.StatsArray.CopyTo(extendedStats, 1);
   63.52 +				return extendedStats;
   63.53 +			}
   63.54 +		}
   63.55 +	}
   63.56 +}
    64.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    64.2 +++ b/API/Objects/UnitType.cs	Sun Apr 03 18:50:32 2011 +0000
    64.3 @@ -0,0 +1,591 @@
    64.4 +// This file (UnitType.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    64.5 +//
    64.6 +// 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.
    64.7 +
    64.8 +using System;
    64.9 +using System.Collections.Generic;
   64.10 +using System.Xml;
   64.11 +using IBBoard.Limits;
   64.12 +using IBBoard.Logging;
   64.13 +using IBBoard.WarFoundry.API.Requirements;
   64.14 +
   64.15 +namespace IBBoard.WarFoundry.API.Objects
   64.16 +{
   64.17 +	/// <summary>
   64.18 +	/// A UnitType is a type for a <see cref=" Unit"/>, normally relating to an entry in an army list. The UnitType defines the name, cost, minimum and maximum limits of a unit, and the equipment units of the type can take.
   64.19 +	/// </summary>
   64.20 +	public class UnitType : WarFoundryObject
   64.21 +	{
   64.22 +		private Category mainCat;
   64.23 +		private Race race;
   64.24 +		private int min, max, baseSize = 0;
   64.25 +		private int minSize, maxSize;
   64.26 +		private double baseUnitCost;
   64.27 +		private double costPerTrooper;
   64.28 +		private Stats stats;
   64.29 +		private List<UnitRequirement> requirements = new List<UnitRequirement>();
   64.30 +		private Dictionary<string, UnitEquipmentItem> equipment = new Dictionary<string, UnitEquipmentItem>();
   64.31 +		private Dictionary<string, List<UnitEquipmentItem>> equipmentExclusionGroups = new Dictionary<string, List<UnitEquipmentItem>>();
   64.32 +		private List<string> equipmentKeyOrder = new List<string>();
   64.33 +		private Dictionary<string, Ability> requiredAbilities = new Dictionary<string, Ability>();
   64.34 +		private Dictionary<string, Ability> optionalAbilities = new Dictionary<string, Ability>();
   64.35 +		private String notes = "";
   64.36 +		private List<UnitType> containedTypes = new List<UnitType>();
   64.37 +		private Dictionary<string, string> extraData = new Dictionary<string, string>();
   64.38 +		private Dictionary<string, ILimit> slotLimits = new Dictionary<string, ILimit>();
   64.39 +		private Dictionary<string, UnitMemberType> unitMemberTypes = new Dictionary<string, UnitMemberType>();
   64.40 +		private List<Category> cats = new List<Category>();
   64.41 +			
   64.42 +
   64.43 +		public UnitType(string id, string typeName, Race parentRace) : base(id, typeName)
   64.44 +		{
   64.45 +			race = parentRace;
   64.46 +		}
   64.47 +
   64.48 +		public GameSystem GameSystem
   64.49 +		{
   64.50 +			get { return Race.GameSystem; }
   64.51 +		}
   64.52 +		
   64.53 +		/// <value>
   64.54 +		/// Gets the <see cref=" Race"/> that this unit belongs to.
   64.55 +		/// </value>
   64.56 +		public Race Race
   64.57 +		{
   64.58 +			get { return race; }
   64.59 +		}
   64.60 +
   64.61 +		/// <value>
   64.62 +		/// Gets or sets the default <see cref=" Category"/> that this unit type is a member of.
   64.63 +		/// If it is not already in the collection of categories then it will be added.
   64.64 +		/// </value>
   64.65 +		public virtual Category MainCategory
   64.66 +		{
   64.67 +			get
   64.68 +			{ 
   64.69 +				return mainCat;
   64.70 +			}
   64.71 +			set
   64.72 +			{
   64.73 +				mainCat = value;
   64.74 +				AddCategory(value);
   64.75 +			}
   64.76 +		}
   64.77 +		/// <summary>
   64.78 +		/// Gets the collection of <see cref="Category"/> objects that this UnitType can be a member of
   64.79 +		/// </summary>
   64.80 +		public Category[] Categories
   64.81 +		{
   64.82 +			get
   64.83 +			{
   64.84 +				return cats.ToArray();
   64.85 +			}
   64.86 +		}
   64.87 +		
   64.88 +		/// <summary>
   64.89 +		/// Adds a category to the set of categories that this unit can be taken from. The first category added will automatically become the MainCategory.
   64.90 +		/// </summary>
   64.91 +		/// <param name="cat">
   64.92 +		/// A <see cref="Category"/> that this unit can be taken from
   64.93 +		/// </param>
   64.94 +		public void AddCategory(Category cat)
   64.95 +		{
   64.96 +			if (!cats.Contains(cat))
   64.97 +			{
   64.98 +				cats.Add(cat);
   64.99 +				
  64.100 +				if (MainCategory == null)
  64.101 +				{
  64.102 +					MainCategory = cat;
  64.103 +				}
  64.104 +			}
  64.105 +		}
  64.106 +
  64.107 +		/// <value>
  64.108 +		/// Gets or sets the minimum size of each unit of this type. Note: This should be set AFTER MaxSize, otherwise an unintended default value may be set for the minimum
  64.109 +		/// </value>
  64.110 +		public int MinSize
  64.111 +		{
  64.112 +			get { return minSize; }
  64.113 +			set
  64.114 +			{
  64.115 +				minSize = (value >= 0 ? value : 0);
  64.116 +				CheckMinimumSize();
  64.117 +			}
  64.118 +		}
  64.119 +
  64.120 +		/// <value>
  64.121 +		/// Gets or sets the maximum size of each unit of this type. Note: This should be set BEFORE MinSize, otherwise an unintended default value may be set for the minimum
  64.122 +		/// </value>
  64.123 +		public int MaxSize
  64.124 +		{
  64.125 +			get { return maxSize; }
  64.126 +			set
  64.127 +			{
  64.128 +				maxSize = (value >= 0 ? value : WarFoundryCore.INFINITY);
  64.129 +				CheckMinimumSize();
  64.130 +			}
  64.131 +		}
  64.132 +		
  64.133 +		/// <value>
  64.134 +		/// Gets or sets the minimum number of units of this type that must be taken in an army. Note: This should be set AFTER MaxNumber, otherwise an unintended default value may be set for the minimum
  64.135 +		/// </value>
  64.136 +		public int MinNumber
  64.137 +		{
  64.138 +			get { return min; }
  64.139 +			set
  64.140 +			{
  64.141 +				min = (value >= 0 ? value : 0);
  64.142 +				CheckMinimumNumber();
  64.143 +			}
  64.144 +		}
  64.145 +
  64.146 +		/// <value>
  64.147 +		/// Gets or sets the maximum number of units of this type that can be taken in an army. Note: This should be set BEFORE MinNumber, otherwise an unintended default value may be set for the minimum
  64.148 +		/// </value>
  64.149 +		public int MaxNumber
  64.150 +		{
  64.151 +			get { return max; }
  64.152 +			set
  64.153 +			{
  64.154 +				max = (value >= 0 ? value : WarFoundryCore.INFINITY);
  64.155 +				CheckMinimumNumber();
  64.156 +			}
  64.157 +		}
  64.158 +
  64.159 +		/// <summary>
  64.160 +		/// Makes sure that the minimum number isn't more than the maximum number, hence the warning on the properties
  64.161 +		/// </summary>
  64.162 +		private void CheckMinimumNumber()
  64.163 +		{
  64.164 +			if (MinNumber > MaxNumber && MaxNumber!=WarFoundryCore.INFINITY)
  64.165 +			{
  64.166 +				MinNumber = MaxNumber;
  64.167 +				LogNotifier.WarnFormat(GetType(), "Unit type {0} ({1}) had a minimum number greater than their maximum number.", Name, ID);
  64.168 +			}
  64.169 +		}
  64.170 +
  64.171 +		/// <summary>
  64.172 +		/// Makes sure that the minimum unit size isn't more than the maximum unit size, hence the warning on the properties
  64.173 +		/// </summary>
  64.174 +		private void CheckMinimumSize()
  64.175 +		{
  64.176 +			if (MinSize > MaxSize && MaxSize!=WarFoundryCore.INFINITY)
  64.177 +			{
  64.178 +				MinSize = MaxSize;
  64.179 +				LogNotifier.WarnFormat(GetType(), "Unit type {0} ({1}) had a minimum size greater than their maximum size.", Name, ID);
  64.180 +			}
  64.181 +		}
  64.182 +		
  64.183 +		//// <value>
  64.184 +		/// Gets or sets the "base size" of a unit, which is the number of troopers the unit has in it for its "base cost". For a lot of units this value will be 0 as the cost is worked out based on the total number of members.
  64.185 +		/// </value>
  64.186 +		public int BaseSize
  64.187 +		{
  64.188 +			get { return baseSize; }
  64.189 +			set { baseSize = (value >= 0 ? value : 0); }
  64.190 +		}
  64.191 +		
  64.192 +		/// <value>
  64.193 +		/// The number of points that a "base unit" of <code>BaseSize</code> models costs. Additional models are charged at <code>CostPerTrooper</code> each.
  64.194 +		/// </value>
  64.195 +		public double BaseUnitCost
  64.196 +		{
  64.197 +			get { return baseUnitCost; }
  64.198 +			set { baseUnitCost = (value >= 0 ? value : 0); }
  64.199 +		}
  64.200 +
  64.201 +		//// <value>
  64.202 +		/// The cost of an individual trooper. This value is the cost for a basic trooper without weapons, which are added on top of the cost before calculating a unit cost.
  64.203 +		/// </value>
  64.204 +		public double CostPerTrooper
  64.205 +		{
  64.206 +			get { return costPerTrooper; }
  64.207 +			set { costPerTrooper = (value >= 0 ? value : 0); }
  64.208 +		}
  64.209 +
  64.210 +		protected override string DefaultName()
  64.211 +		{
  64.212 +			throw new InvalidOperationException("Unit type with id "+id+" did not have a name specified");
  64.213 +		}
  64.214 +		
  64.215 +		/// <value>
  64.216 +		/// The array of <see cref="Stat"/>s for each of the unit's stat lines
  64.217 +		/// </value>
  64.218 +		public Stat[][] UnitStatsArrays
  64.219 +		{
  64.220 +			get
  64.221 +			{
  64.222 +				Stat[][] statsArray;
  64.223 +				
  64.224 +				if (stats != null)
  64.225 +				{
  64.226 +					statsArray = new Stat[][]{ stats.StatsArray };
  64.227 +				}
  64.228 +				else if (unitMemberTypes.Count > 0)
  64.229 +				{
  64.230 +					int memTypeCount = unitMemberTypes.Count;
  64.231 +					statsArray = new Stat[memTypeCount][];
  64.232 +					int i = 0;
  64.233 +					
  64.234 +					foreach (UnitMemberType memType in unitMemberTypes.Values)
  64.235 +					{
  64.236 +						statsArray[i] = memType.StatsArray;
  64.237 +						i++;
  64.238 +					}
  64.239 +				}
  64.240 +				else
  64.241 +				{
  64.242 +					SystemStats systemStats = GameSystem.StandardSystemStats;
  64.243 +					Stats tempStats = new Stats(systemStats);				
  64.244 +					statsArray = new Stat[][]{ tempStats.StatsArray };
  64.245 +				}
  64.246 +				
  64.247 +				return statsArray;
  64.248 +			}
  64.249 +		}
  64.250 +		
  64.251 +		public string[] UnitStatsArrayIDs
  64.252 +		{
  64.253 +			get 
  64.254 +			{
  64.255 +				string[] ids;
  64.256 +				
  64.257 +				if (stats != null)
  64.258 +				{
  64.259 +					ids = new string[]{ stats.StatsID };
  64.260 +				}
  64.261 +				else if (unitMemberTypes.Count > 0)
  64.262 +				{
  64.263 +					ids = new string[unitMemberTypes.Count];
  64.264 +					int i = 0;
  64.265 +					
  64.266 +					foreach (UnitMemberType memType in unitMemberTypes.Values)
  64.267 +					{
  64.268 +						ids[i] = memType.StatsID;
  64.269 +						i++;
  64.270 +					}
  64.271 +				}
  64.272 +				else
  64.273 +				{
  64.274 +					ids = new string[]{ GameSystem.StandardSystemStatsID };
  64.275 +				}
  64.276 +				
  64.277 +				return ids;
  64.278 +			}
  64.279 +		}
  64.280 +
  64.281 +		//// <value>
  64.282 +		/// The array of <see cref="Stat"/>s for each of the unit's stat lines including an additional column that contains the unit type name
  64.283 +		/// </value>
  64.284 +		public Stat[][] UnitStatsArraysWithName
  64.285 +		{
  64.286 +			get
  64.287 +			{				
  64.288 +				Stat[][] statsArray;
  64.289 +				
  64.290 +				if (stats != null)
  64.291 +				{
  64.292 +					statsArray = new Stat[][]{ ExtendStatsArrayWithName(stats.StatsArray) };
  64.293 +				}
  64.294 +				else if (unitMemberTypes.Count > 0)
  64.295 +				{
  64.296 +					int memTypeCount = unitMemberTypes.Count;
  64.297 +					statsArray = new Stat[memTypeCount][];
  64.298 +					int i = 0;
  64.299 +					
  64.300 +					foreach (UnitMemberType memType in unitMemberTypes.Values)
  64.301 +					{
  64.302 +						statsArray[i] = memType.StatsArrayWithName;
  64.303 +						i++;
  64.304 +					}
  64.305 +				}
  64.306 +				else
  64.307 +				{
  64.308 +					SystemStats systemStats = GameSystem.StandardSystemStats;
  64.309 +					Stats tempStats = new Stats(systemStats);				
  64.310 +					statsArray = new Stat[][]{ ExtendStatsArrayWithName(tempStats.StatsArray) };
  64.311 +				}
  64.312 +				
  64.313 +				return statsArray;
  64.314 +			}
  64.315 +		}
  64.316 +		
  64.317 +		public Stat[] ExtendStatsArrayWithName(Stat[] statsArray)
  64.318 +		{
  64.319 +			Stat[] extendedStats = new Stat[statsArray.Length+1];
  64.320 +			extendedStats[0] = new Stat(new StatSlot("name"), Name);
  64.321 +			statsArray.CopyTo(extendedStats, 1);
  64.322 +			return extendedStats;
  64.323 +		}
  64.324 +
  64.325 +		public void SetUnitStats(Stats newStats)
  64.326 +		{
  64.327 +			stats = newStats;
  64.328 +		}
  64.329 +
  64.330 +		public string GetStatValue(string statName)
  64.331 +		{
  64.332 +			return stats.GetStatValue(statName.ToLower());
  64.333 +		}
  64.334 +		
  64.335 +		internal void AddEquipmentItem(UnitEquipmentItem item)
  64.336 +		{
  64.337 +			if (!equipment.ContainsKey(item.ID))
  64.338 +			{
  64.339 +				equipment.Add(item.ID, item);
  64.340 +				equipmentKeyOrder.Add(item.ID);
  64.341 +				AddToMutexGroups(item);
  64.342 +			}
  64.343 +		}
  64.344 +		
  64.345 +		private void AddToMutexGroups(UnitEquipmentItem item)
  64.346 +		{
  64.347 +			string[] mutexGroups = item.MutexGroups;
  64.348 +			
  64.349 +			foreach (string mutexGroup in mutexGroups)
  64.350 +			{
  64.351 +				List<UnitEquipmentItem> items = DictionaryUtils.GetValue(equipmentExclusionGroups, mutexGroup);
  64.352 +				
  64.353 +				if (items == null)
  64.354 +				{
  64.355 +					items = new List<UnitEquipmentItem>();
  64.356 +					equipmentExclusionGroups.Add(mutexGroup, items);
  64.357 +				}
  64.358 +				
  64.359 +				items.Add(item);
  64.360 +			}
  64.361 +		}
  64.362 +
  64.363 +		/// <summary>
  64.364 +		/// Gets a <see cref="UnitEquipmentItem"/> for the given ID string, or <code>null</code> if nothing exists for that ID
  64.365 +		/// </summary>
  64.366 +		/// <param name="id">
  64.367 +		/// The ID of the UnitEquipmentItem to get
  64.368 +		/// </param>
  64.369 +		/// <returns>
  64.370 +		/// The <see cref="UnitEquipmentItem"/> for the given ID string, or <code>null</code> if nothing exists for that ID
  64.371 +		/// </returns>
  64.372 +		public UnitEquipmentItem GetEquipmentItem(string id)
  64.373 +		{
  64.374 +			return DictionaryUtils.GetValue(equipment, id);
  64.375 +		}
  64.376 +		
  64.377 +		/// <summary>
  64.378 +		/// Gets a <see cref=" UnitEquipmentItem"/> for the given <see cref=" EquipmentItem"/>, or <code>null</code> if the unit can't take that <code>EquipmentItem</code>
  64.379 +		/// </summary>
  64.380 +		/// <param name="item">
  64.381 +		/// The <see cref="EquipmentItem"/> to get the <see cref=" UnitEquipmentItem"/>
  64.382 +		/// </param>
  64.383 +		/// <returns>
  64.384 +		/// The <see cref="UnitEquipmentItem"/> that definies the UnitType's restrictions for taking the <see cref=" EquipmentItem"/>
  64.385 +		/// </returns>
  64.386 +		public UnitEquipmentItem GetEquipmentItem(EquipmentItem item)
  64.387 +		{
  64.388 +			return GetEquipmentItem(item.ID);
  64.389 +		}
  64.390 +
  64.391 +		/// <summary>
  64.392 +		/// Gets an array of all available <see cref="UnitEquipmentItem"/>s for this UnitType
  64.393 +		/// </summary>
  64.394 +		/// <returns>
  64.395 +		/// An array of all available <see cref="UnitEquipmentItem"/>s for this UnitType
  64.396 +		/// </returns>
  64.397 +		public UnitEquipmentItem[] GetEquipmentItems()
  64.398 +		{
  64.399 +			return DictionaryUtils.ToArray<string, UnitEquipmentItem>(equipment);
  64.400 +		}
  64.401 +
  64.402 +		public UnitEquipmentItem[] GetEquipmentItemsByExclusionGroup(string group)
  64.403 +		{
  64.404 +			return GetEquipmentItemsByExclusionGroups(new string[] { group });
  64.405 +		}
  64.406 +
  64.407 +		public UnitEquipmentItem[] GetEquipmentItemsByExclusionGroups(string[] groups)
  64.408 +		{
  64.409 +			List<UnitEquipmentItem> list = new List<UnitEquipmentItem>();
  64.410 +
  64.411 +			foreach (string group in groups)
  64.412 +			{
  64.413 +				List<UnitEquipmentItem> groupList = DictionaryUtils.GetValue(equipmentExclusionGroups, group);
  64.414 +
  64.415 +				if (groupList != null)
  64.416 +				{
  64.417 +					list.AddRange(groupList);
  64.418 +				}
  64.419 +			}
  64.420 +
  64.421 +			return list.ToArray();
  64.422 +		}
  64.423 +		
  64.424 +		public bool IsRatioLimitedEquipmentItem(EquipmentItem item)
  64.425 +		{
  64.426 +			UnitEquipmentItem equip = GetEquipmentItem(item);
  64.427 +			return equip != null && equip.IsRatioLimit;
  64.428 +		}
  64.429 +		
  64.430 +		public bool IsAbsoluteLimitedEquipmentItem(EquipmentItem item)
  64.431 +		{
  64.432 +			UnitEquipmentItem equip = GetEquipmentItem(item);
  64.433 +			return equip != null && !equip.IsRatioLimit;
  64.434 +		}
  64.435 +		
  64.436 +		public ICollection<Ability> GetRequiredAbilities()
  64.437 +		{
  64.438 +			return requiredAbilities.Values;
  64.439 +		}
  64.440 +		
  64.441 +		public ICollection<Ability> GetOptionalAbilities()
  64.442 +		{
  64.443 +			return optionalAbilities.Values;
  64.444 +		}
  64.445 +		
  64.446 +		public void AddAbility(Ability ability, bool isRequired)
  64.447 +		{
  64.448 +			string id = ability.ID;
  64.449 +			
  64.450 +			if (!requiredAbilities.ContainsKey(id) && !optionalAbilities.ContainsKey(id))
  64.451 +			{				
  64.452 +				if (isRequired)
  64.453 +				{
  64.454 +					requiredAbilities[id] = ability;
  64.455 +				}
  64.456 +				else
  64.457 +				{
  64.458 +					optionalAbilities[id] = ability;
  64.459 +				}
  64.460 +			}
  64.461 +		}
  64.462 +
  64.463 +		public void AddRequirement(UnitRequirement requirement)
  64.464 +		{
  64.465 +			requirements.Add(requirement);
  64.466 +		}
  64.467 +		
  64.468 +		public UnitRequirement[] Requirements
  64.469 +		{
  64.470 +			get { return requirements.ToArray(); }
  64.471 +		}
  64.472 +		
  64.473 +		public List<FailedUnitRequirement> CanAddToArmy(Army army)
  64.474 +		{
  64.475 +			List<FailedUnitRequirement> failures = new List<FailedUnitRequirement>();
  64.476 +			
  64.477 +			if (requirements!=null && requirements.Count > 0)
  64.478 +			{
  64.479 +				foreach (UnitRequirement requirement in requirements)
  64.480 +				{
  64.481 +					FailedUnitRequirement failure = (FailedUnitRequirement)requirement.CanAddToWarFoundryObject(army);
  64.482 +					
  64.483 +					if (failure!=null)
  64.484 +					{
  64.485 +						failures.Add(failure);
  64.486 +					}
  64.487 +				}
  64.488 +			}
  64.489 +			
  64.490 +			return failures;
  64.491 +		}
  64.492 +		
  64.493 +		public List<FailedUnitRequirement> CanRemoveFromArmy(Army army)
  64.494 +		{
  64.495 +			List<FailedUnitRequirement> failures = new List<FailedUnitRequirement>();
  64.496 +			
  64.497 +			if (requirements!=null && requirements.Count > 0)
  64.498 +			{
  64.499 +				foreach (UnitRequirement requirement in requirements)
  64.500 +				{
  64.501 +					FailedUnitRequirement failure = (FailedUnitRequirement)requirement.CanRemoveFromWarFoundryObject(army);
  64.502 +					
  64.503 +					if (failure!=null)
  64.504 +					{
  64.505 +						failures.Add(failure);
  64.506 +					}
  64.507 +				}
  64.508 +			}
  64.509 +			
  64.510 +			return failures;
  64.511 +		}
  64.512 +		
  64.513 +		public string Notes
  64.514 +		{
  64.515 +			get { return notes; }
  64.516 +			set { notes = value; }
  64.517 +		}
  64.518 +				
  64.519 +		public bool CanContainUnit(Unit unit)
  64.520 +		{
  64.521 +			return CanContainUnitType(unit.UnitType);
  64.522 +		}
  64.523 +		
  64.524 +		public bool CanContainUnitType(UnitType unitType)
  64.525 +		{
  64.526 +			return containedTypes.Contains(unitType);
  64.527 +		}
  64.528 +		
  64.529 +		public UnitType[] ContainedUnitTypes
  64.530 +		{
  64.531 +			get { return containedTypes.ToArray(); }
  64.532 +		}
  64.533 +		
  64.534 +		public void AddContainedUnitType(UnitType containedType)
  64.535 +		{
  64.536 +			containedTypes.Add(containedType);
  64.537 +		}
  64.538 +		
  64.539 +		public void AddExtraData(string id, string data)
  64.540 +		{
  64.541 +			extraData[id] = data;
  64.542 +		}
  64.543 +		
  64.544 +		public string GetExtraData(string id)
  64.545 +		{
  64.546 +			return DictionaryUtils.GetValue(extraData, id);
  64.547 +		}
  64.548 +		
  64.549 +		public string StatsID
  64.550 +		{
  64.551 +			get
  64.552 +			{
  64.553 +				return stats.StatsID;
  64.554 +			}
  64.555 +		}
  64.556 +
  64.557 +		public void AddEquipmentSlot(string slotName, ILimit slotLimit)
  64.558 +		{
  64.559 +			slotLimits.Add(slotName, slotLimit);
  64.560 +		}
  64.561 +
  64.562 +		public bool HasEquipmentSlot(string slotName)
  64.563 +		{
  64.564 +			return slotLimits.ContainsKey(slotName);
  64.565 +		}
  64.566 +
  64.567 +		/// <summary>
  64.568 +		/// Gets the maximum limit on the number of items allowed in a single slot
  64.569 +		/// </summary>
  64.570 +		/// <param name="slotName">The name of the equipment slot to get the limit for</param>
  64.571 +		/// <returns>The limit of the number of items allowed in a slot, or an infinite limit if the slot is the default one or has not been specified</returns>
  64.572 +		public ILimit GetEquipmentSlotLimit(string slotName)
  64.573 +		{
  64.574 +			ILimit slotLimit = null;
  64.575 +
  64.576 +			if (HasEquipmentSlot(slotName))
  64.577 +			{
  64.578 +				slotLimit = DictionaryUtils.GetValue(slotLimits, slotName);
  64.579 +			}
  64.580 +			
  64.581 +			if (slotLimit == null)
  64.582 +			{
  64.583 +				slotLimit = new UnlimitedLimit();
  64.584 +			}
  64.585 +
  64.586 +			return slotLimit;
  64.587 +		}
  64.588 +
  64.589 +		public void AddUnitMemberType(UnitMemberType unitMemberType)
  64.590 +		{
  64.591 +			unitMemberTypes.Add(unitMemberType.ID, unitMemberType);
  64.592 +		}
  64.593 +	}
  64.594 +}
  64.595 \ No newline at end of file
    65.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    65.2 +++ b/API/Objects/WarFoundryLoadedObject.cs	Sun Apr 03 18:50:32 2011 +0000
    65.3 @@ -0,0 +1,26 @@
    65.4 +using System;
    65.5 +using IBBoard.WarFoundry.API.Objects;
    65.6 +
    65.7 +namespace IBBoard.WarFoundry.API.Objects
    65.8 +{
    65.9 +	/// <summary>
   65.10 +	/// A marker class for objects that are loaded from a file
   65.11 +	/// </summary>
   65.12 +	public class WarFoundryLoadedObject : WarFoundryObject
   65.13 +	{
   65.14 +		protected WarFoundryLoadedObject() : base()
   65.15 +		{
   65.16 +			//Do nothing
   65.17 +		}
   65.18 +				
   65.19 +		protected WarFoundryLoadedObject(string objName) : base(objName)
   65.20 +		{
   65.21 +			//Do nothing
   65.22 +		}
   65.23 +		
   65.24 +		protected WarFoundryLoadedObject(string objId, string objName) : base(objId, objName)
   65.25 +		{
   65.26 +			//Do nothing
   65.27 +		}	
   65.28 +	}
   65.29 +}
    66.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    66.2 +++ b/API/Objects/WarFoundryObject.cs	Sun Apr 03 18:50:32 2011 +0000
    66.3 @@ -0,0 +1,100 @@
    66.4 +// This file (WarFoundryObject.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    66.5 +//
    66.6 +// 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.
    66.7 +
    66.8 +using System;
    66.9 +using IBBoard.WarFoundry.API.Factories;
   66.10 +
   66.11 +namespace IBBoard.WarFoundry.API.Objects
   66.12 +{
   66.13 +	/// <summary>
   66.14 +	/// Summary description for WarFoundryObject.
   66.15 +	/// </summary>
   66.16 +	public abstract class WarFoundryObject : IWarFoundryObject
   66.17 +	{
   66.18 +		protected string id;
   66.19 +		protected string name;
   66.20 +		public event StringValChangedDelegate NameChanged;
   66.21 +		
   66.22 +		protected WarFoundryObject()
   66.23 +		{
   66.24 +		}
   66.25 +				
   66.26 +		protected WarFoundryObject(string objName) : this()
   66.27 +		{
   66.28 +			Name = objName;
   66.29 +		}
   66.30 +		
   66.31 +		protected WarFoundryObject(string objId, string objName) : this(objName)
   66.32 +		{
   66.33 +			ID = objId;
   66.34 +		}	
   66.35 +
   66.36 +		public virtual string ID
   66.37 +		{
   66.38 +			get
   66.39 +			{
   66.40 +				if (id == null || id == "")
   66.41 +				{
   66.42 +					id = GenerateID();
   66.43 +				}
   66.44 +				
   66.45 +				return id;
   66.46 +			}
   66.47 +			
   66.48 +			set
   66.49 +			{
   66.50 +				string newId = (value == null ? "" : value.Trim());
   66.51 +				id = (newId == "" ? GenerateID() : newId);
   66.52 +			}
   66.53 +		}
   66.54 +
   66.55 +		public virtual string Name
   66.56 +		{
   66.57 +			get 
   66.58 +			{
   66.59 +				if (HasDefaultName())
   66.60 +				{
   66.61 +					return DefaultName();
   66.62 +				}
   66.63 +				else
   66.64 +				{
   66.65 +					return name;
   66.66 +				} 
   66.67 +			}
   66.68 +			set 
   66.69 +			{ 
   66.70 +				string oldValue = name;
   66.71 +				name = value;
   66.72 +
   66.73 +				if (name!=oldValue)
   66.74 +				{
   66.75 +					OnNameChanged(oldValue, name);
   66.76 +				}
   66.77 +			}
   66.78 +		}
   66.79 +
   66.80 +		public bool HasDefaultName()
   66.81 +		{
   66.82 +			return (name == null || name == "");
   66.83 +		}
   66.84 +
   66.85 +		protected void OnNameChanged(string oldValue, string newValue)
   66.86 +		{
   66.87 +			if (NameChanged!=null)
   66.88 +			{
   66.89 +				NameChanged(this, oldValue, newValue);
   66.90 +			}
   66.91 +		}
   66.92 +
   66.93 +		protected virtual string DefaultName()
   66.94 +		{
   66.95 +			return "-";
   66.96 +		}
   66.97 +
   66.98 +		protected string GenerateID()
   66.99 +		{
  66.100 +			return Name + UnixTimestamp.GetTimestamp(DateTime.Now) + "." + DateTime.Now.Millisecond;
  66.101 +		}
  66.102 +	}
  66.103 +}
    67.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    67.2 +++ b/API/Objects/WarFoundryStagedLoadingObject.cs	Sun Apr 03 18:50:32 2011 +0000
    67.3 @@ -0,0 +1,80 @@
    67.4 +// This file (WarFoundryStagedLoadingObject.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2007, 2008, 2009 IBBoard.
    67.5 +//
    67.6 +// 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.
    67.7 +
    67.8 +using System;
    67.9 +using System.IO;
   67.10 +using IBBoard.WarFoundry.API.Factories;
   67.11 +
   67.12 +namespace IBBoard.WarFoundry.API.Objects
   67.13 +{	
   67.14 +	public class WarFoundryStagedLoadingObject : WarFoundryLoadedObject, IWarFoundryStagedLoadObject
   67.15 +	{
   67.16 +		private bool isFullyLoaded;
   67.17 +		private bool isLoading;
   67.18 +		private IWarFoundryFactory creatingFactory;
   67.19 +		private FileInfo sourceFile;
   67.20 +		
   67.21 +		protected WarFoundryStagedLoadingObject(IWarFoundryFactory creatingFactory) : this (null, creatingFactory)
   67.22 +		{
   67.23 +		}
   67.24 +				
   67.25 +		protected WarFoundryStagedLoadingObject(string objName, IWarFoundryFactory creatingFactory) : this(null, objName, creatingFactory)
   67.26 +		{
   67.27 +		}
   67.28 +		
   67.29 +		protected WarFoundryStagedLoadingObject(string objId, string objName, IWarFoundryFactory creatingFactory) : base(objId, objName)
   67.30 +		{
   67.31 +			this.creatingFactory = creatingFactory;
   67.32 +			isFullyLoaded = false;
   67.33 +		}	
   67.34 +		
   67.35 +		public FileInfo SourceFile
   67.36 +		{
   67.37 +			get { return sourceFile; }
   67.38 +			set { sourceFile = value; }
   67.39 +		}
   67.40 +		
   67.41 +		public void EnsureFullyLoaded ()
   67.42 +		{
   67.43 +			if (!IsFullyLoaded && !IsLoading)
   67.44 +			{
   67.45 +				if (Factory == null)
   67.46 +				{
   67.47 +					throw new InvalidOperationException("No factory set for partially loaded object with ID "+ID);
   67.48 +				}
   67.49 +				
   67.50 +				Factory.CompleteLoading(this);
   67.51 +			}
   67.52 +		}
   67.53 +		
   67.54 +		public IWarFoundryFactory Factory
   67.55 +		{
   67.56 +			get { return creatingFactory; }
   67.57 +		}
   67.58 +		
   67.59 +		public bool IsFullyLoaded
   67.60 +		{
   67.61 +			get { return isFullyLoaded; }
   67.62 +		}
   67.63 +		
   67.64 +		public bool IsLoading
   67.65 +		{
   67.66 +			get { return isLoading; }
   67.67 +		}
   67.68 +		
   67.69 +		public void SetAsFullyLoaded()
   67.70 +		{
   67.71 +			isLoading = false;
   67.72 +			isFullyLoaded = true;
   67.73 +		}
   67.74 +		
   67.75 +		public void SetAsLoading()
   67.76 +		{
   67.77 +			if (!isFullyLoaded)
   67.78 +			{
   67.79 +				isLoading = true;
   67.80 +			}
   67.81 +		}
   67.82 +	}
   67.83 +}
    68.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    68.2 +++ b/API/Requirements/AbstractArmyRequirement.cs	Sun Apr 03 18:50:32 2011 +0000
    68.3 @@ -0,0 +1,52 @@
    68.4 +// This file (AbstractArmyRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    68.5 +//
    68.6 +// 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.
    68.7 +
    68.8 +using System;
    68.9 +using System.Collections.Generic;
   68.10 +using IBBoard.WarFoundry.API.Objects;
   68.11 +
   68.12 +
   68.13 +namespace IBBoard.WarFoundry.API.Requirements
   68.14 +{	
   68.15 +	/// <summary>
   68.16 +	/// Abstract class for any requirement for adding an object to an army, e.g. adding units.
   68.17 +	/// </summary>
   68.18 +	public abstract class AbstractArmyRequirement : AbstractRequirement
   68.19 +	{		
   68.20 +		protected abstract AbstractFailedRequirement CanAddToArmy(Army army);
   68.21 +		protected abstract AbstractFailedRequirement CanRemoveFromArmy(Army army);
   68.22 +		
   68.23 +		public override AbstractFailedRequirement CanAddToWarFoundryObject (WarFoundryObject obj)
   68.24 +		{
   68.25 +			AbstractFailedRequirement fail = null;
   68.26 +			
   68.27 +			if (obj is Army)
   68.28 +			{
   68.29 +				fail = CanAddToArmy((Army)obj);
   68.30 +			}
   68.31 +			else
   68.32 +			{
   68.33 +				fail = new FailedRequirement(this);
   68.34 +			}
   68.35 +			
   68.36 +			return fail;
   68.37 +		}
   68.38 +		
   68.39 +		public override AbstractFailedRequirement CanRemoveFromWarFoundryObject (WarFoundryObject obj)
   68.40 +		{
   68.41 +			AbstractFailedRequirement fail = null;
   68.42 +			
   68.43 +			if (obj is Army)
   68.44 +			{
   68.45 +				fail = CanRemoveFromArmy((Army)obj);
   68.46 +			}
   68.47 +			else
   68.48 +			{
   68.49 +				fail = new FailedRequirement(this);
   68.50 +			}
   68.51 +			
   68.52 +			return fail;
   68.53 +		}
   68.54 +	}
   68.55 +}
    69.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    69.2 +++ b/API/Requirements/AbstractFailedRequirement.cs	Sun Apr 03 18:50:32 2011 +0000
    69.3 @@ -0,0 +1,20 @@
    69.4 +// This file (AbstractFailedRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    69.5 +//
    69.6 +// 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.
    69.7 +
    69.8 +using System;
    69.9 +
   69.10 +namespace IBBoard.WarFoundry.API.Requirements
   69.11 +{
   69.12 +	public abstract class AbstractFailedRequirement
   69.13 +	{	
   69.14 +		protected AbstractRequirement failedReq;
   69.15 +		
   69.16 +		public AbstractFailedRequirement(AbstractRequirement req)
   69.17 +		{
   69.18 +			failedReq = req;
   69.19 +		}
   69.20 +		
   69.21 +		public abstract string Description { get; }
   69.22 +	}
   69.23 +}
    70.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    70.2 +++ b/API/Requirements/AbstractRequirement.cs	Sun Apr 03 18:50:32 2011 +0000
    70.3 @@ -0,0 +1,19 @@
    70.4 +// This file (AbstractRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    70.5 +//
    70.6 +// 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.
    70.7 +
    70.8 +using System;
    70.9 +using IBBoard.WarFoundry.API.Objects;
   70.10 +
   70.11 +namespace IBBoard.WarFoundry.API.Requirements
   70.12 +{	
   70.13 +	/// <summary>
   70.14 +	/// The base class for Requirements. Specific types of abstract requirement should extend this class.
   70.15 +	/// </summary>
   70.16 +	public abstract class AbstractRequirement
   70.17 +	{				
   70.18 +		public abstract string Description { get; }
   70.19 +		public abstract AbstractFailedRequirement CanAddToWarFoundryObject(WarFoundryObject obj);
   70.20 +		public abstract AbstractFailedRequirement CanRemoveFromWarFoundryObject(WarFoundryObject obj);
   70.21 +	}
   70.22 +}
    71.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    71.2 +++ b/API/Requirements/AbstractUnitRequirement.cs	Sun Apr 03 18:50:32 2011 +0000
    71.3 @@ -0,0 +1,50 @@
    71.4 +// This file (AbstractUnitRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    71.5 +//
    71.6 +// 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.
    71.7 +
    71.8 +using System;
    71.9 +using IBBoard.WarFoundry.API.Objects;
   71.10 +
   71.11 +namespace IBBoard.WarFoundry.API.Requirements
   71.12 +{
   71.13 +	/// <summary>
   71.14 +	/// Base abstract class for all requirements related to adding/removing something from a Unit (e.g. adding equipment or abilities)
   71.15 +	/// </summary>
   71.16 +	public abstract class AbstractUnitRequirement : AbstractRequirement
   71.17 +	{		
   71.18 +		protected abstract AbstractFailedRequirement CanAddToUnit(Unit unit);
   71.19 +		protected abstract AbstractFailedRequirement CanRemoveFromUnit(Unit unit);
   71.20 +		
   71.21 +		public override AbstractFailedRequirement CanAddToWarFoundryObject (WarFoundryObject obj)
   71.22 +		{
   71.23 +			AbstractFailedRequirement fail = null;
   71.24 +			
   71.25 +			if (obj is Unit)
   71.26 +			{
   71.27 +				fail = CanAddToUnit((Unit)obj);
   71.28 +			}
   71.29 +			else
   71.30 +			{
   71.31 +				fail = new FailedRequirement(this);
   71.32 +			}
   71.33 +			
   71.34 +			return fail;
   71.35 +		}
   71.36 +		
   71.37 +		public override AbstractFailedRequirement CanRemoveFromWarFoundryObject (WarFoundryObject obj)
   71.38 +		{
   71.39 +			AbstractFailedRequirement fail = null;
   71.40 +			
   71.41 +			if (obj is Unit)
   71.42 +			{
   71.43 +				fail = CanRemoveFromUnit((Unit)obj);
   71.44 +			}
   71.45 +			else
   71.46 +			{
   71.47 +				fail = new FailedRequirement(this);
   71.48 +			}
   71.49 +			
   71.50 +			return fail;
   71.51 +		}
   71.52 +	}
   71.53 +}
    72.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    72.2 +++ b/API/Requirements/Delegates.cs	Sun Apr 03 18:50:32 2011 +0000
    72.3 @@ -0,0 +1,11 @@
    72.4 +// This file (Delegates.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    72.5 +//
    72.6 +// 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.
    72.7 +
    72.8 +using System;
    72.9 +using System.Collections.Generic;
   72.10 +
   72.11 +namespace IBBoard.WarFoundry.API.Requirements
   72.12 +{
   72.13 +	public delegate void FailedUnitRequirementDelegate(List<FailedUnitRequirement> failedRequirements);
   72.14 +}
    73.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    73.2 +++ b/API/Requirements/FailedRequirement.cs	Sun Apr 03 18:50:32 2011 +0000
    73.3 @@ -0,0 +1,20 @@
    73.4 +// This file (FailedRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    73.5 +//
    73.6 +// 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.
    73.7 +
    73.8 +using System;
    73.9 +
   73.10 +namespace IBBoard.WarFoundry.API.Requirements
   73.11 +{
   73.12 +	public class FailedRequirement : AbstractFailedRequirement
   73.13 +	{	
   73.14 +		public FailedRequirement(AbstractRequirement req) : base(req)
   73.15 +		{
   73.16 +		}
   73.17 +		
   73.18 +		public override string Description
   73.19 +		{
   73.20 +			get { return failedReq.Description; }
   73.21 +		}
   73.22 +	}
   73.23 +}
    74.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    74.2 +++ b/API/Requirements/FailedUnitRequirement.cs	Sun Apr 03 18:50:32 2011 +0000
    74.3 @@ -0,0 +1,24 @@
    74.4 +// This file (FailedUnitRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    74.5 +//
    74.6 +// 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.
    74.7 +
    74.8 +using System;
    74.9 +
   74.10 +namespace IBBoard.WarFoundry.API.Requirements
   74.11 +{
   74.12 +	/// <summary>
   74.13 +	/// Summary description for UnitRequirement.
   74.14 +	/// </summary>
   74.15 +	public class FailedUnitRequirement : AbstractFailedRequirement
   74.16 +	{
   74.17 +		public FailedUnitRequirement(UnitRequirement requirement) : base(requirement)
   74.18 +		{
   74.19 +		}
   74.20 +		
   74.21 +		public override string Description
   74.22 +		{
   74.23 +			get { return failedReq.Description; }
   74.24 +		}
   74.25 +
   74.26 +	}
   74.27 +}
    75.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    75.2 +++ b/API/Requirements/RequirementAND.cs	Sun Apr 03 18:50:32 2011 +0000
    75.3 @@ -0,0 +1,56 @@
    75.4 +// This file (RequirementAND.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    75.5 +//
    75.6 +// 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.
    75.7 +
    75.8 +using System;
    75.9 +using System.Collections.Generic;
   75.10 +using IBBoard.Lang;
   75.11 +using IBBoard.WarFoundry.API.Objects;
   75.12 +
   75.13 +namespace IBBoard.WarFoundry.API.Requirements
   75.14 +{
   75.15 +	/// <summary>
   75.16 +	/// Summary description for RequirementAND.
   75.17 +	/// </summary>
   75.18 +	public class RequirementAND : AbstractRequirement
   75.19 +	{
   75.20 +		private static string and = Translation.GetTranslation("requirementAND", "{0} and {1}");
   75.21 +
   75.22 +		private AbstractRequirement reqA, reqB;
   75.23 +
   75.24 +		public RequirementAND(AbstractRequirement requirementA, AbstractRequirement requirementB)
   75.25 +		{
   75.26 +			reqA = requirementA;
   75.27 +			reqB = requirementB;
   75.28 +		}
   75.29 +
   75.30 +		public override AbstractFailedRequirement CanAddToWarFoundryObject(WarFoundryObject obj)
   75.31 +		{	
   75.32 +			FailedRequirement failed = null;
   75.33 +			
   75.34 +			if (reqA.CanAddToWarFoundryObject(obj) !=null || reqB.CanAddToWarFoundryObject(obj)!=null)
   75.35 +			{
   75.36 +				failed = new FailedRequirement(this);
   75.37 +			}
   75.38 +			
   75.39 +			return failed;
   75.40 +		}
   75.41 +
   75.42 +		public override AbstractFailedRequirement CanRemoveFromWarFoundryObject(WarFoundryObject obj)
   75.43 +		{
   75.44 +			FailedRequirement failed = null;
   75.45 +			
   75.46 +			if (reqA.CanRemoveFromWarFoundryObject(obj) !=null || reqB.CanRemoveFromWarFoundryObject(obj)!=null)
   75.47 +			{
   75.48 +				failed = new FailedRequirement(this);
   75.49 +			}
   75.50 +			
   75.51 +			return failed;
   75.52 +		}
   75.53 +
   75.54 +		public override string Description 
   75.55 +		{
   75.56 +			get { return String.Format(and, reqA.Description, reqB.Description); }
   75.57 +		}
   75.58 +	}
   75.59 +}
    76.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    76.2 +++ b/API/Requirements/RequirementOR.cs	Sun Apr 03 18:50:32 2011 +0000
    76.3 @@ -0,0 +1,56 @@
    76.4 +// This file (RequirementOR.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    76.5 +//
    76.6 +// 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.
    76.7 +
    76.8 +using System;
    76.9 +using System.Collections.Generic;
   76.10 +using IBBoard.Lang;
   76.11 +using IBBoard.WarFoundry.API.Objects;
   76.12 +
   76.13 +namespace IBBoard.WarFoundry.API.Requirements
   76.14 +{
   76.15 +	/// <summary>
   76.16 +	/// Summary description for UnitRequirementOR.
   76.17 +	/// </summary>
   76.18 +	public class RequirementOR : AbstractRequirement
   76.19 +	{
   76.20 +		private static string or = Translation.GetTranslation("requirementOR", "{0} or {1}");
   76.21 +
   76.22 +		private AbstractRequirement reqA, reqB;
   76.23 +
   76.24 +		public RequirementOR(AbstractRequirement requirementA, AbstractRequirement requirementB)
   76.25 +		{
   76.26 +			reqA = requirementA;
   76.27 +			reqB = requirementB;
   76.28 +		}
   76.29 +
   76.30 +		public override AbstractFailedRequirement CanAddToWarFoundryObject(WarFoundryObject obj)
   76.31 +		{		
   76.32 +			FailedRequirement failed = null;
   76.33 +			
   76.34 +			if (reqA.CanAddToWarFoundryObject(obj) !=null && reqB.CanAddToWarFoundryObject(obj)!=null)
   76.35 +			{
   76.36 +				failed = new FailedRequirement(this);
   76.37 +			}
   76.38 +			
   76.39 +			return failed;
   76.40 +		}
   76.41 +
   76.42 +		public override AbstractFailedRequirement CanRemoveFromWarFoundryObject(WarFoundryObject obj)
   76.43 +		{		
   76.44 +			FailedRequirement failed = null;
   76.45 +			
   76.46 +			if (reqA.CanRemoveFromWarFoundryObject(obj)!=null && reqB.CanRemoveFromWarFoundryObject(obj)!=null)
   76.47 +			{
   76.48 +				failed = new FailedRequirement(this);
   76.49 +			}
   76.50 +			
   76.51 +			return failed;
   76.52 +		}
   76.53 +
   76.54 +		public override string Description 
   76.55 +		{
   76.56 +			get { return String.Format(or, reqA.Description, reqB.Description); }
   76.57 +		}
   76.58 +	}
   76.59 +}
    77.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    77.2 +++ b/API/Requirements/UnitExcludesRequirement.cs	Sun Apr 03 18:50:32 2011 +0000
    77.3 @@ -0,0 +1,68 @@
    77.4 +// This file (UnitExcludesRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    77.5 +//
    77.6 +// 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.
    77.7 +
    77.8 +using System;
    77.9 +using System.Collections.Generic;
   77.10 +using System.Text;
   77.11 +using IBBoard.WarFoundry.API.Objects;
   77.12 +using IBBoard.Lang;
   77.13 +
   77.14 +namespace IBBoard.WarFoundry.API.Requirements
   77.15 +{
   77.16 +	/// <summary>
   77.17 +	/// Summary description for UnitExcludesRequirement.
   77.18 +	/// </summary>
   77.19 +	public class UnitExcludesRequirement : UnitRequirement
   77.20 +	{
   77.21 +		private UnitRequirementItem[] excludingTypes;
   77.22 +
   77.23 +		public UnitExcludesRequirement(UnitType type, UnitRequirementItem[] excludingUnitTypes) : base(type)
   77.24 +		{
   77.25 +			excludingTypes = excludingUnitTypes;
   77.26 +		}
   77.27 +
   77.28 +		public override string Description
   77.29 +		{
   77.30 +			get 
   77.31 +			{
   77.32 +				string otherUnits = GetOtherUnitTypeNames();
   77.33 +				return Translation.GetTranslation("requirementUnitExcludesDescription", "{0} can only be taken if none of the following are taken: {1}", unitType.Name, otherUnits);
   77.34 +			}
   77.35 +		}
   77.36 +		
   77.37 +		private string GetOtherUnitTypeNames()
   77.38 +		{
   77.39 +			StringBuilder sb = new StringBuilder();
   77.40 +			
   77.41 +			foreach (UnitRequirementItem req in excludingTypes)
   77.42 +			{
   77.43 +				sb.Append(req.UnitType.Name);
   77.44 +			}
   77.45 +			
   77.46 +			return sb.ToString();
   77.47 +		}
   77.48 +
   77.49 +		protected override AbstractFailedRequirement CanAddToArmy(Army army, UnitType type)
   77.50 +		{		
   77.51 +			FailedUnitRequirement failed = null;
   77.52 +
   77.53 +			for (int i = 0; i<excludingTypes.Length; i++)
   77.54 +			{
   77.55 +				if (army.GetUnitTypeCount(excludingTypes[i].UnitType) > 0)
   77.56 +				{
   77.57 +					failed = new FailedUnitRequirement(this);
   77.58 +					break;
   77.59 +				}
   77.60 +			}
   77.61 +			
   77.62 +			return failed;
   77.63 +		}
   77.64 +
   77.65 +		protected override AbstractFailedRequirement CanRemoveFromArmy(Army army, UnitType type)
   77.66 +		{
   77.67 +			return null;
   77.68 +		}
   77.69 +
   77.70 +	}
   77.71 +}
    78.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    78.2 +++ b/API/Requirements/UnitRequirement.cs	Sun Apr 03 18:50:32 2011 +0000
    78.3 @@ -0,0 +1,96 @@
    78.4 +// This file (UnitRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    78.5 +//
    78.6 +// 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.
    78.7 +
    78.8 +using System;
    78.9 +using System.Collections.Generic;
   78.10 +using IBBoard.WarFoundry.API;
   78.11 +using IBBoard.WarFoundry.API.Objects;
   78.12 +
   78.13 +namespace IBBoard.WarFoundry.API.Requirements
   78.14 +{
   78.15 +	/// <summary>
   78.16 +	/// Summary description for UnitRequirement.
   78.17 +	/// </summary>
   78.18 +	public abstract class UnitRequirement : AbstractArmyRequirement
   78.19 +	{
   78.20 +		protected UnitType unitType;
   78.21 +		
   78.22 +		protected UnitRequirement(UnitType requirementFor)
   78.23 +		{
   78.24 +			unitType = requirementFor;	
   78.25 +		}		
   78.26 +		
   78.27 +		/// <summary>
   78.28 +		/// Checks whether the specified unit type can be added to the specified army. Returns a <see cref="FailedRequirement"> obejct if a unit of that type cannot legally be added, or no failure (null) if it can be added. 
   78.29 +		/// </summary>
   78.30 +		/// <param name="army">
   78.31 +		/// The <see cref="Army"/> that the unit should be checked for adding to
   78.32 +		/// </param>
   78.33 +		/// <param name="type">
   78.34 +		/// The <see cref="UnitType"/> that is being checked.
   78.35 +		/// </param>
   78.36 +		/// <returns>
   78.37 +		/// A <see cref="AbstractFailedRequirement"/> if the requirement means the <see cref="UnitType"/> cannot be legally added, else <code>null</code>.
   78.38 +		/// </returns>
   78.39 +		protected abstract AbstractFailedRequirement CanAddToArmy(Army army, UnitType type);
   78.40 +		
   78.41 +		/// <summary>
   78.42 +		/// Checks whether the specified unit can be added to the specified army. Returns a <see cref="FailedRequirement"> obejct if the unit cannot legally be added, or no failure (null) if it can be added. 
   78.43 +		/// </summary>
   78.44 +		/// <param name="army">
   78.45 +		/// The <see cref="Army"/> that the unit should be checked for adding to
   78.46 +		/// </param>
   78.47 +		/// <param name="type">
   78.48 +		/// The <see cref="Unit"/> that is being checked.
   78.49 +		/// </param>
   78.50 +		/// <returns>
   78.51 +		/// A <see cref="AbstractFailedRequirement"/> if the requirement means the <see cref="Unit"/> cannot be legally added, else <code>null</code>.
   78.52 +		/// </returns>
   78.53 +		protected AbstractFailedRequirement CanAddToArmy(Army army, Unit unit)
   78.54 +		{
   78.55 +			return CanAddToArmy(army, unit.UnitType);
   78.56 +		}
   78.57 +		
   78.58 +		/// <summary>
   78.59 +		/// Checks whether the specified unit type can be removed from the specified army. Returns a <see cref="FailedRequirement"> obejct if a unit of that type cannot legally be removed, or no failure (null) if it can be removed. 
   78.60 +		/// </summary>
   78.61 +		/// <param name="army">
   78.62 +		/// The <see cref="Army"/> that the unit should be checked for adding to
   78.63 +		/// </param>
   78.64 +		/// <param name="type">
   78.65 +		/// The <see cref="UnitType"/> that is being checked.
   78.66 +		/// </param>
   78.67 +		/// <returns>
   78.68 +		/// A <see cref="AbstractFailedRequirement"/> if the requirement means the <see cref="UnitType"/> cannot be legally added, else <code>null</code>.
   78.69 +		/// </returns>
   78.70 +		protected abstract AbstractFailedRequirement CanRemoveFromArmy(Army army, UnitType type);
   78.71 +		
   78.72 +		/// <summary>
   78.73 +		/// Checks whether the specified unit can be removed from the specified army. Returns a <see cref="FailedRequirement"> obejct if the unit cannot legally be removed, or no failure (null) if it can be removed. 
   78.74 +		/// </summary>
   78.75 +		/// <param name="army">
   78.76 +		/// The <see cref="Army"/> that the unit should be checked for adding to
   78.77 +		/// </param>
   78.78 +		/// <param name="type">
   78.79 +		/// The <see cref="Unit"/> that is being checked.
   78.80 +		/// </param>
   78.81 +		/// <returns>
   78.82 +		/// A <see cref="AbstractFailedRequirement"/> if the requirement means the <see cref="Unit"/> cannot be legally removed, else <code>null</code>.
   78.83 +		/// </returns>
   78.84 +		protected AbstractFailedRequirement CanRemoveFromArmy(Army army, Unit unit)
   78.85 +		{
   78.86 +			return CanRemoveFromArmy(army, unit.UnitType);
   78.87 +		}
   78.88 +				
   78.89 +		protected override AbstractFailedRequirement CanAddToArmy(Army army)
   78.90 +		{
   78.91 +			return CanAddToArmy(army, unitType);
   78.92 +		}
   78.93 +		
   78.94 +		protected override AbstractFailedRequirement CanRemoveFromArmy(Army army)
   78.95 +		{
   78.96 +			return CanRemoveFromArmy(army, unitType);
   78.97 +		}
   78.98 +	}
   78.99 +}
    79.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    79.2 +++ b/API/Requirements/UnitRequirementItem.cs	Sun Apr 03 18:50:32 2011 +0000
    79.3 @@ -0,0 +1,36 @@
    79.4 +// This file (UnitRequirementItem.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    79.5 +//
    79.6 +// 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.
    79.7 +
    79.8 +using System;
    79.9 +using IBBoard.WarFoundry.API.Objects;
   79.10 +
   79.11 +namespace IBBoard.WarFoundry.API.Requirements
   79.12 +{
   79.13 +	/// <summary>
   79.14 +	/// Summary description for UnitRequirementItem.
   79.15 +	/// </summary>
   79.16 +	public class UnitRequirementItem
   79.17 +	{
   79.18 +		private UnitType type;
   79.19 +		private int requiredNum;
   79.20 +
   79.21 +		public UnitRequirementItem(UnitType unitType, int reqNumber)
   79.22 +		{
   79.23 +			type = unitType;
   79.24 +			requiredNum = reqNumber;
   79.25 +		}
   79.26 +
   79.27 +		public UnitRequirementItem(UnitType type) : this(type, 1) { }
   79.28 +
   79.29 +		public UnitType UnitType
   79.30 +		{
   79.31 +			get { return type; }
   79.32 +		}
   79.33 +
   79.34 +		public int Amount
   79.35 +		{
   79.36 +			get { return requiredNum; }
   79.37 +		}
   79.38 +	}
   79.39 +}
    80.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    80.2 +++ b/API/Requirements/UnitRequirementMaxNumber.cs	Sun Apr 03 18:50:32 2011 +0000
    80.3 @@ -0,0 +1,42 @@
    80.4 +// This file (UnitRequirementMaxNumber.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    80.5 +//
    80.6 +// 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.
    80.7 +
    80.8 +using System;
    80.9 +using IBBoard.Lang;
   80.10 +using IBBoard.WarFoundry.API.Objects;
   80.11 +
   80.12 +namespace IBBoard.WarFoundry.API.Requirements
   80.13 +{
   80.14 +	public class UnitRequirementMaxNumber : UnitRequirement
   80.15 +	{		
   80.16 +		private int maxUnitCount;
   80.17 +		
   80.18 +		public UnitRequirementMaxNumber(UnitType type, int maxNumber) : base(type)
   80.19 +		{
   80.20 +			maxUnitCount = maxNumber;
   80.21 +		}
   80.22 +		
   80.23 +		public override string Description
   80.24 +		{
   80.25 +			get { return Translation.GetTranslation("requirementUnitMaxNumber", "an army can contain up to {0} units of type {1}", maxUnitCount, unitType.Name); }
   80.26 +		}
   80.27 +		
   80.28 +		protected override AbstractFailedRequirement CanAddToArmy (Army army, UnitType type)
   80.29 +		{
   80.30 +			FailedUnitRequirement failed = null;
   80.31 +			
   80.32 +			if (army.GetUnitTypeCount(type) >= maxUnitCount)
   80.33 +			{
   80.34 +				failed = new FailedUnitRequirement(this);
   80.35 +			}
   80.36 +			
   80.37 +			return failed;
   80.38 +		}
   80.39 +
   80.40 +		protected override AbstractFailedRequirement CanRemoveFromArmy (Army army, UnitType type)
   80.41 +		{
   80.42 +			return null;
   80.43 +		}
   80.44 +	}
   80.45 +}
    81.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    81.2 +++ b/API/Requirements/UnitRequirementMinNumber.cs	Sun Apr 03 18:50:32 2011 +0000
    81.3 @@ -0,0 +1,42 @@
    81.4 +// This file (UnitRequirementMinNumber.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    81.5 +//
    81.6 +// 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.
    81.7 +
    81.8 +using System;
    81.9 +using IBBoard.Lang;
   81.10 +using IBBoard.WarFoundry.API.Objects;
   81.11 +
   81.12 +namespace IBBoard.WarFoundry.API.Requirements
   81.13 +{
   81.14 +	public class UnitRequirementMinNumber : UnitRequirement
   81.15 +	{
   81.16 +		private int minUnitCount;
   81.17 +		
   81.18 +		public UnitRequirementMinNumber(UnitType type, int minNumber) : base(type)
   81.19 +		{
   81.20 +			minUnitCount = minNumber;
   81.21 +		}
   81.22 +		
   81.23 +		public override string Description
   81.24 +		{
   81.25 +			get { return Translation.GetTranslation("requirementUnitMinNumber", "you must include at least {0} of {1} in an army", minUnitCount, unitType.Name); }
   81.26 +		}
   81.27 +		
   81.28 +		protected override AbstractFailedRequirement CanAddToArmy(Army army, UnitType type)
   81.29 +		{
   81.30 +			return null;
   81.31 +		}
   81.32 +
   81.33 +		protected override AbstractFailedRequirement CanRemoveFromArmy (Army army, UnitType type)
   81.34 +		{
   81.35 +			FailedUnitRequirement failed = null;
   81.36 +			
   81.37 +			if (army.GetUnitTypeCount(type) <= minUnitCount)
   81.38 +			{
   81.39 +				failed = new FailedUnitRequirement(this);
   81.40 +			}
   81.41 +			
   81.42 +			return failed;
   81.43 +		}
   81.44 +	}
   81.45 +}
    82.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    82.2 +++ b/API/Requirements/UnitRequiresAtLeastRequirement.cs	Sun Apr 03 18:50:32 2011 +0000
    82.3 @@ -0,0 +1,73 @@
    82.4 +// This file (UnitRequiresAtLeastRequirement.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    82.5 +//
    82.6 +// 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.
    82.7 +
    82.8 +using System;
    82.9 +using System.Collections.Generic;
   82.10 +using System.Text;
   82.11 +using IBBoard.Lang;
   82.12 +using IBBoard.WarFoundry.API;
   82.13 +using IBBoard.WarFoundry.API.Objects;
   82.14 +
   82.15 +namespace IBBoard.WarFoundry.API.Requirements
   82.16 +{
   82.17 +	/// <summary>
   82.18 +	/// Summary description for UnitRequiresRequirement.
   82.19 +	/// </summary>
   82.20 +	public class UnitRequiresAtLeastRequirement : UnitRequirement
   82.21 +	{
   82.22 +		private UnitRequirementItem[] requiredTypes;
   82.23 +		private String unitList;
   82.24 +
   82.25 +		public UnitRequiresAtLeastRequirement(UnitType type, UnitType requiredUnitType) : this(type, new UnitRequirementItem[]{new UnitRequirementItem(requiredUnitType)})
   82.26 +		{
   82.27 +		}
   82.28 +		
   82.29 +		public UnitRequiresAtLeastRequirement(UnitType type, UnitRequirementItem[] requiredUnitTypes) : base(type)
   82.30 +		{			
   82.31 +			requiredTypes = requiredUnitTypes;
   82.32 +			bool first = true;
   82.33 +			
   82.34 +			foreach (UnitRequirementItem req in requiredTypes)
   82.35 +			{
   82.36 +				string reqString = Translation.GetTranslation("requirementUnitTypeAtLeastSingle", "{1} {0}", req.UnitType.Name, req.Amount);
   82.37 +				
   82.38 +				if (first)
   82.39 +				{
   82.40 +					first = false;
   82.41 +					unitList = reqString;
   82.42 +				}
   82.43 +				else
   82.44 +				{
   82.45 +					unitList = Translation.GetTranslation("requirementUnitTypeAtLeastJoiner", "{0}, {1}", unitList, reqString);
   82.46 +				}
   82.47 +			}
   82.48 +		}
   82.49 +
   82.50 +		public override string Description
   82.51 +		{
   82.52 +			get { return Translation.GetTranslation("requirementUnitTypeAtLeast", "the army must include at least the following units to include a unit of type {0}: {1}", unitType.Name, unitList); }
   82.53 +		}
   82.54 +
   82.55 +		protected override AbstractFailedRequirement CanRemoveFromArmy(Army army, UnitType type)
   82.56 +		{
   82.57 +			return null;
   82.58 +		}
   82.59 +
   82.60 +		protected override AbstractFailedRequirement CanAddToArmy(Army army, UnitType type)
   82.61 +		{
   82.62 +			FailedRequirement failure = null;
   82.63 +			
   82.64 +			foreach (UnitRequirementItem req in requiredTypes)
   82.65 +			{
   82.66 +				if (army.GetUnitTypeCount(req.UnitType) < req.Amount)
   82.67 +				{				
   82.68 +					failure = new FailedRequirement(this);
   82.69 +					break;
   82.70 +				}
   82.71 +			}
   82.72 +						
   82.73 +			return failure;
   82.74 +		}
   82.75 +	}
   82.76 +}
    83.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    83.2 +++ b/API/Savers/IWarFoundryFileSaver.cs	Sun Apr 03 18:50:32 2011 +0000
    83.3 @@ -0,0 +1,19 @@
    83.4 +// This file (IWarFoundryFileSaver.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    83.5 +//
    83.6 +// 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.
    83.7 +
    83.8 +using System;
    83.9 +using IBBoard.WarFoundry.API.Objects;
   83.10 +
   83.11 +namespace IBBoard.WarFoundry.API.Savers
   83.12 +{
   83.13 +	/// <summary>
   83.14 +	/// Saves one or more objects into a native-format zip file.
   83.15 +	/// </summary>
   83.16 +	public interface IWarFoundryFileSaver
   83.17 +	{
   83.18 +		bool Save(string path, params WarFoundryLoadedObject[] objects);
   83.19 +		
   83.20 +		string GetFileExtension (WarFoundryLoadedObject obj);
   83.21 +	}
   83.22 +}
    84.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    84.2 +++ b/API/Savers/WarFoundrySaver.cs	Sun Apr 03 18:50:32 2011 +0000
    84.3 @@ -0,0 +1,23 @@
    84.4 +// This file (WarFoundrySaver.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    84.5 +//
    84.6 +// 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.
    84.7 +
    84.8 +using System;
    84.9 +
   84.10 +namespace IBBoard.WarFoundry.API.Savers
   84.11 +{
   84.12 +	public class WarFoundrySaver
   84.13 +	{
   84.14 +		private static IWarFoundryFileSaver fileSaver;
   84.15 +		
   84.16 +		public static IWarFoundryFileSaver GetSaver()
   84.17 +		{
   84.18 +			return fileSaver;
   84.19 +		}
   84.20 +		
   84.21 +		public static void SetFileSaver(IWarFoundryFileSaver newFileSaver)
   84.22 +		{
   84.23 +			fileSaver = newFileSaver;
   84.24 +		}
   84.25 +	}
   84.26 +}
    85.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    85.2 +++ b/API/Savers/Xml/WarFoundryXmlArmySaver.cs	Sun Apr 03 18:50:32 2011 +0000
    85.3 @@ -0,0 +1,143 @@
    85.4 +// This file (WarFoundryXmlSaver.cs) is a part of the IBBoard.WarFoundry.API project and is copyright 2008, 2009 IBBoard.
    85.5 +//
    85.6 +// 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.
    85.7 +
    85.8 +using System;
    85.9 +using System.Collections.Generic;
   85.10 +using System.IO;
   85.11 +using System.Xml;
   85.12 +using System.Xml.Schema;
   85.13 +using IBBoard.Lang;
   85.14 +using IBBoard.Xml;
   85.15 +using IBBoard.WarFoundry.API.Factories.Xml.Zip;
   85.16 +using IBBoard.WarFoundry.API.Objects;
   85.17 +using IBBoard.WarFoundry.API.Savers;
   85.18 +using IBBoard.WarFoundry.API.Util;
   85.19 +using ICSharpCode.SharpZipLib.Zip;
   85.20 +
   85.21 +namespace IBBoard.WarFoundry.API.Savers.Xml
   85.22 +{
   85.23 +	public class WarFoundryXmlArmySaver
   85.24 +	{
   85.25 +		public string CreateXmlString(Army toSave)
   85.26 +		{
   85.27 +			XmlDocument doc = new XmlDocument();
   85.28 +			XmlDeclaration declaration = doc.CreateXmlDeclaration("1.0", "UTF-8", null);
   85.29 +			doc.AppendChild(declaration);
   85.30 +			XmlSchema schema = new XmlSchema();
   85.31 +			schema.Namespaces.Add("", "http://ibboard.co.uk/warfoundry/army");
   85.32 +			schema.Namespaces.Add("core", "http://ibboard.co.uk/warfoundry/core");
   85.33 +			doc.Schemas.Add(schema);
   85.34 +			XmlElement root = doc.CreateElement("army");
   85.35 +			root.SetAttribute("xmlns", "http://ibboard.co.uk/warfoundry/army");
   85.36 +			root.SetAttribute("xmlns:core", "http://ibboard.co.uk/warfoundry/core");
   85.37 +			doc.AppendChild(root);
   85.38 +			root.SetAttribute("id", XmlTools.GetAsciiXmlIdForString(toSave.ID));
   85.39 +			root.SetAttribute("name", toSave.Name);
   85.40 +			//Don't convert system and race to ID format as they could be stored in non-XML file formats
   85.41 +			//If they are in XML files then they'll already be valid
   85.42 +			root.SetAttribute("system", toSave.GameSystem.ID);
   85.43 +			root.SetAttribute("race", toSave.Race.ID);
   85.44 +			root.SetAttribute("maxPoints", toSave.MaxPoints.ToString());
   85.45 +			XmlElement units = doc.CreateElement("units");
   85.46 +			root.AppendChild(units);
   85.47 +			
   85.48 +			foreach (Unit unit in toSave.GetUnits())
   85.49 +			{
   85.50 +				units.AppendChild(CreateUnitElement(unit, doc));
   85.51 +			}
   85.52 +			
   85.53 +			return doc.OuterXml;
   85.54 +		}
   85.55 +
   85.56 +		private XmlElement CreateUnitElement(Unit unit, XmlDocument doc)
   85.57 +		{
   85.58 +			XmlElement unitElem = doc.CreateElement("unit");
   85.59 +			unitElem.SetAttribute("id", XmlTools.GetAsciiXmlIdForString(unit.ID));
   85.60 +			unitElem.SetAttribute("unitName", (unit.HasDefaultName() ? "" : unit.Name));
   85.61 +			unitElem.SetAttribute("unitType", unit.UnitType.ID);
   85.62 +			unitElem.SetAttribute("size", unit.Size.ToString());
   85.63 +			
   85.64 +			if (!unit.Race.Equals(unit.Army.Race))
   85.65 +			{
   85.66 +				unitElem.SetAttribute("race", unit.Race.ID);
   85.67 +			}
   85.68 +			
   85.69 +			Category unitCategory = unit.Category.Category;
   85.70 +			if (!unit.UnitType.MainCategory.Equals(unitCategory))
   85.71 +			{
   85.72 +				unitElem.SetAttribute("category", unitCategory.ID);
   85.73 +			}
   85.74 +
   85.75 +			XmlElement equipmentElem = CreateEquipmentItemsElement(unit, doc);
   85.76 +
   85.77 +			if (equipmentElem != null)
   85.78 +			{
   85.79 +				unitElem.AppendChild(equipmentElem);
   85.80 +			}
   85.81 +			
   85.82 +			XmlElement containedElem = CreateContainedUnitsElement(unit, doc);
   85.83 +
   85.84 +			if (containedElem != null)
   85.85 +			{
   85.86 +				unitElem.AppendChild(containedElem);
   85.87 +			}
   85.88 +			
   85.89 +			return unitElem;
   85.90 +		}
   85.91 +
   85.92 +		private XmlElement CreateEquipmentItemsElement(Unit unit, XmlDocument doc)
   85.93 +		{
   85.94 +			UnitEquipmentItem[] equipItems = unit.GetEquipment();
   85.95 +			int equipItemCount = equipItems.Length;
   85.96 +			XmlElement equipmentElem = null;
   85.97 +
   85.98 +			if (equipItemCount > 0)
   85.99 +			{
  85.100 +				equipmentElem = doc.CreateElement("equipment");
  85.101 +				
  85.102 +				for (int i = 0; i < equipItemCount; i++)
  85.103 +				{
  85.104 +					equipmentElem.AppendChild(CreateEquipmentElement(equipItems[i], unit, doc));
  85.105 +				}
  85.106 +			}
  85.107 +
  85.108 +			return equipmentElem;
  85.109 +		}
  85.110 +
  85.111 +		private XmlElement CreateEquipmentElement(UnitEquipmentItem item, Unit unit, XmlDocument doc)
  85.112 +		{
  85.113 +			XmlElement equipmentItemElem = doc.CreateElement("equipItem");
  85.114 +			equipmentItemElem.SetAttribute("id", item.ID);
  85.115 +			equipmentItemElem.SetAttribute("amount", UnitEquipmentUtil.GetEquipmentAmount(unit, item).ToString());
  85.116 +			equipmentItemElem.SetAttribute("amountType", UnitEquipmentUtil.GetEquipmentAmountIsRatio(unit, item) ? "ratio" : "fixed");
  85.117 +			return equipmentItemElem;
  85.118 +		}
  85.119 +		
  85.120 +		private XmlElement CreateContainedUnitsElement(Unit unit, XmlDocument doc)
  85.121 +		{
  85.122 +			Unit[] containedUnits = unit.ContainedUnits;
  85.123 +			int containedCount = containedUnits.Length;
  85.124 +			XmlElement containedElem = null;
  85.125 +
  85.126 +			if (containedCount > 0)
  85.127 +			{
  85.128 +				containedElem = doc.CreateElement("contained");
  85.129 +				
  85.130 +				for (int i = 0; i < containedCount; i++)
  85.131 +				{
  85.132 +					containedElem.AppendChild(CreateContainedUnitElement(containedUnits[i], doc));
  85.133 +				}
  85.134 +			}
  85.135 +
  85.136 +			return containedElem;
  85.137 +		}
  85.138 +
  85.139 +		private XmlElement CreateContainedUnitElement(Unit unit,  XmlDocument doc)
  85.140 +		{
  85.141 +			XmlElement containedUnitElem = doc.CreateElement("containedUnit");
  85.142 +			containedUnitElem.SetAttribute("containedID", XmlTools.GetAsciiXmlIdForString(unit.ID));
  85.143 +			return containedUnitElem;
  85.144 +		}
  85.145 +	}
  85.146 +}
    86.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
    86.2 +++ b/API/Savers/Xml/WarFoundryXmlFileSaver.cs	Sun Apr 03 18:50:32 2011 +0000
    86.3 @@ -0,0 +1,92 @@
    86.4 +using System;
    86.5 +using IBBoard.WarFoundry.API.Savers;
    86.6 +using IBBoard.WarFoundry.API.Objects;
    86.7 +using ICSharpCode.SharpZipLib.Zip;
    86.8 +using System.IO;
    86.9 +using IBBoard.IO;
   86.10 +using IBBoard.Lang;
   86.11 +
   86.12 +namespace IBBoard.WarFoundry.API.Savers.Xml
   86.13 +{
   86.14 +	public class WarFoundryXmlFileSaver : IWarFoundryFileSaver
   86.15 +	{
   86.16 +		public WarFoundryXmlFileSaver()
   86.17 +		{
   86.18 +		}
   86.19 +
   86.20 +		public bool Save(string path, params WarFoundryLoadedObject[] objects)
   86.21 +		{
   86.22 +			ZipOutputStream zipStream = new ZipOutputStream(new FileStream(path, FileMode.Create));
   86.23 +			AddFiles(zipStream, objects);
   86.24 +			zipStream.Close();
   86.25 +			return true;
   86.26 +		}
   86.27 +
   86.28 +		public void AddFiles(ZipOutputStream zipStream, WarFoundryLoadedObject[] objects)
   86.29 +		{
   86.30 +			foreach (WarFoundryLoadedObject obj in objects)
   86.31 +			{
   86.32 +				AddFile(zipStream, obj);
   86.33 +			}
   86.34