PHP Code:
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
namespace EBForBIHelper
{
internal sealed class Unit
{
const int Indent = 17;
enum UnitCategory : byte
{
infantry, cavalry, siege, handler, ship, non_combatant
}
enum UnitClass : byte
{
light, heavy, missile, spearmen
}
enum Voice : byte
{
Light_1, Medium_1, Heavy_1, General_1, Female_1
}
enum LeashedAnimal : byte
{
wardogs,
pigs
}
enum MountEffect : byte
{
elephant,
camel,
horse,
chariot
}
[FlagsAttribute]
enum Attribute
{
/// <summary>
/// can board ships
/// </summary>
sea_faring = 0x00000001,
hide_forest = 0x00000002, //defines where the unit can hide
hide_improved_forest = 0x00000004,
hide_long_grass = 0x00000008,
hide_anywhere = 0x00000010,
/// <summary>
/// Can dig tunnels under walls
/// </summary>
can_sap = 0x00000020,
frighten_foot = 0x00000040, //Cause fear to certain nearby unit types
frighten_mounted = 0x00000080,
/// <summary>
/// Unit may go out of control when riders lose control of animals
/// </summary>
can_run_amok = 0x00000100,
/// <summary>
/// The unit can be used for a named character's bodyguard
/// </summary>
general_unit = 0x00000200,
cantabrian_circle = 0x00000400,
/// <summary>
/// The unit may not be selected in custom battles
/// </summary>
no_custom = 0x00000800,
/// <summary>
/// The unit carries a legionary eagle, and gives bonuses to nearby units
/// </summary>
command = 0x00001000,
/// <summary>
/// The unit is s mercenary unit available to all factions
/// </summary>
mercenary_unit = 0x00002000,
hardy = 0x00004000, //The unit takes longer to suffer the effects of fatigue, and recovers from them quicker
very_hardy = 0x00008000,
/// <summary>
/// Grants attack bonus of +10. It takes 10 seconds to activate, and has a 30 second duration
/// </summary>
warcry = 0x00010000,
/// <summary>
/// The chanting ability of Druids
/// </summary>
druid = 0x00020000,
/// <summary>
/// The screeching ability of Screeching Women
/// </summary>
screeching_women = 0x00040000,
/// <summary>
/// (BI only) The unit can swim
/// </summary>
can_swim = 0x00080000,
/// <summary>
/// (BI only) This is a horde unit
/// </summary>
can_horde = 0x00100000,
/// <summary>
/// (BI only) Gives a legionary name and number to the unit based on the region it is recruited from
/// </summary>
legionary_name = 0x00200000,
power_charge = 0x00400000,
general_unit_upgrade = 0x00800000
}
enum PrimaryFormation : byte
{
/// <summary>
/// The standard infantry formation, really a rectangle
/// </summary>
square,
/// <summary>
/// The round formation that units like Peasants use
/// </summary>
horde
}
enum SecondaryFormation : byte
{
phalanx,
testudo,
/// <summary>
/// cavalry only
/// </summary>
wedge,
/// <summary>
/// BI only
/// </summary>
schiltrom,
/// <summary>
/// BI only
/// </summary>
shield_wall
}
enum Projectile : byte
{
/// <summary>
/// Used when unit doesn't have a missile weapon
/// </summary>
no,
arrow,
arrow_fiery,
head,
boulder,
fiery_boulder,
big_boulder,
big_fiery_boulder,
tarred_rock,
stone,
bolt,
bullet,
javelin,
pilum,
solifera,
dart,
axe,
ballista,
flaming_ballista,
tower_ballista,
tower_flaming_ballista,
scorpion,
flaming_scorpion,
repeating_ballista
}
enum WeaponType : byte
{
no, melee, thrown, missile, siege_missile
}
enum WeaponTech : byte
{
no, simple, other, blade, archery, siege
}
enum DamageType : byte
{
no, piercing, blunt, slashing, fire
}
enum WeaponSound : byte
{
none, knife, mace, axe, sword, spear
}
[FlagsAttribute]
enum WeaponAttribute : short
{
/// <summary>
/// Not applicable or none
/// </summary>
no = 0x0000,
/// <summary>
/// Missile weapon is thrown just before charging into combat. It can still be used in "Fire At Will" mode.
/// </summary>
prec = 0x0001,
/// <summary>
/// The missile type if thrown rather than fired.
/// </summary>
thrown = 0x0002,
/// <summary>
/// armour piercing. Only counts half of target's armour
/// </summary>
ap = 0x0004,
/// <summary>
/// body piercing. Missile can pass through men and hit those behind
/// </summary>
bp = 0x0008,
/// <summary>
/// Used for long spears. +8 vs. cavalry and -4 defense vs infantry
/// </summary>
spear = 0x0010,
/// <summary>
/// Same as spear, but unit has less pushing power than Spear.
/// </summary>
light_spear = 0x0020,
/// <summary>
/// Use very long pikes. Phalanx capable units only.
/// </summary>
long_pike = 0x0040,
/// <summary>
/// Use shorter than normal spears. Phalanx capable units only.
/// </summary>
short_pike = 0x0080,
/// <summary>
/// attack may throw target men into the air.
/// </summary>
launching = 0x0100,
/// <summary>
/// attack affects an area, not just one soldier
/// </summary>
area = 0x0200
}
enum ArmorSound : byte
{
flesh, leather, metal
}
enum Discipline : byte
{
low, normal, disciplined,
/// <summary>
/// Units may charge without orders
/// </summary>
impetuous,
/// <summary>
/// Berserker units can go berserk (BI Only)
/// </summary>
berserker
}
enum Training : byte
{
untrained,
trained,
highly_trained
}
bool m_updated;
/// <summary>
/// The internal name of the unit. Note this not necessarily the same as the on screen name
/// </summary>
string Type;
/// <summary>
/// The tag used to look up the on screen name
/// </summary>
string Dictionary;
/// <summary>
/// Category and class define the rough type of the unit. They're used for setting some
/// default attributes and for determining where units go in formation amongst other things
/// </summary>
UnitCategory Category;
UnitClass Class;
/// <summary>
/// Used to determine the type of voice used by the unit
/// </summary>
Voice VoiceType;
/// <summary>
/// Name of the soldier model to use (from descr_models_battle.txt)
/// </summary>
string SoldierModel;
/// <summary>
/// the number of ordinary soldiers in the unit
/// </summary>
byte SoldierUnitSize;
/// <summary>
/// the number of extras (pigs dogs, elephants, chariots artillery pieces etc attached to the unit)
/// </summary>
byte SoldierExtras;
/// <summary>
/// the collision mass of the men. 1.0 is normal. Only applies to infantry
/// </summary>
decimal SoldierMass;
/// <summary>
/// Name of officer model. There may be up to 0-3 officer lines per unit
/// </summary>
List<string> Officers = new List<string>();
/// <summary>
/// Type of ship used if applicable
/// </summary>
string Ship;
/// <summary>
/// Type of siege engine used by unit
/// </summary>
string Engine;
/// <summary>
/// The type of non ridden on animals used by the unit
/// </summary>
LeashedAnimal? Animal;
/// <summary>
/// Type of animal or vehicle ridden on
/// </summary>
string Mount;
/// <summary>
/// Factors to add when in combat against enemy units that have the specified mounts
/// Up to three factors may be specified, which may be classes of mount, or specific types
/// </summary>
Dictionary<MountEffect, short> MountEffects = new Dictionary<MountEffect,short>();
/// <summary>
/// A miscellanious list of attributes and abilities the unit may have.
/// </summary>
Attribute Attributes;
/// <summary>
/// soldier spacing (in metres) side to side for close formation
/// </summary>
decimal FormationNormalSide;
/// <summary>
/// soldier spacing (in metres) front to back for close formation
/// </summary>
decimal FormationNormalFront;
/// <summary>
/// soldier spacing (in metres) side to side for loose formation
/// </summary>
decimal FormationLooseSide;
/// <summary>
/// soldier spacing (in metres) front to back for loose formation
/// </summary>
decimal FormationLooseFront;
/// <summary>
/// default number of ranks for the unit
/// </summary>
byte FormationRanks;
/// <summary>
/// formations possible for the unit
/// </summary>
PrimaryFormation FormationPrimary;
SecondaryFormation? FormationSecondary;
/// <summary>
/// Hit points of man, followed by hit points of mount or attached animal (if applicable)
/// Ridden horses and camels do not have separate hit points
/// </summary>
byte[] StatHealth = new byte[2];
/// <summary>
/// Details of unit's primary weapon. If the unit has a missile weapon it must be the primary.
/// </summary>
byte StatPriAttack;
/// <summary>
/// attack bonus factor if charging
/// </summary>
byte StatPriAttackBonus;
/// <summary>
/// missile type fired (no if not a missile weapon type)
/// </summary>
Projectile StatPriProjectile;
/// <summary>
/// range of missile
/// </summary>
decimal StatPriRange;
/// <summary>
/// amount of missile ammunition per man
/// </summary>
ushort StatPriAmmunition;
WeaponType StatPriWeaponType;
WeaponTech StatPriWeaponTech;
DamageType StatPriDamageType;
/// <summary>
/// Sound type when weapon hits
/// </summary>
WeaponSound StatPriWeaponSound;
/// <summary>
/// Min delay between attacks (in 1/10th of a second)
/// </summary>
ushort StatPriAttackDelay;
decimal StatPriLethality;
WeaponAttribute StatPriAttribute;
/// <summary>
/// Details of secondary weapons. If the unit rides on, or has attached animals or vehicles
/// then the secondary weapon details refer to their attacks. If the unit has missile weapons
/// the secondary weapon will be the one used for melee
/// If the unit has a primary melee weapon, it may have a secondary side arm
/// </summary>
byte StatSecAttack;
byte StatSecAttackBonus;
Projectile StatSecProjectile;
decimal StatSecRange;
ushort StatSecAmmunition;
WeaponType StatSecWeaponType;
WeaponTech StatSecWeaponTech;
DamageType StatSecDamageType;
WeaponSound StatSecWeaponSound;
ushort StatSecAttackDelay;
decimal StatSecLethality;
WeaponAttribute StatSecAttribute;
/// <summary>
/// Details of the man's defences
/// </summary>
byte StatPriArmourFactor;
/// <summary>
/// defensive skill factor (not used when shot at)
/// </summary>
byte StatPriArmourSkill;
/// <summary>
/// shield factor (only used for attacks from the front of left)
/// </summary>
byte StatPriArmourShield;
/// <summary>
/// sound type when hit
/// </summary>
ArmorSound StatPriArmourSound;
/// <summary>
/// Details of animal's or vehicle's defenses (note riden horses do not have a separate defence)
/// As per stat_pri_armour, except that the shield entry is ommited
/// </summary>
byte StatSecArmourFactor;
byte StatSecArmourSkill;
ArmorSound StatSecArmourSound;
/// <summary>
/// Extra fatigue suffered by the unit in hot climates
/// </summary>
byte StatHeat;
/// <summary>
/// Combat modifiers in scrub
/// </summary>
short StatGroundScrub;
/// <summary>
/// Combat modifiers in sand
/// </summary>
short StatGroundSand;
/// <summary>
/// Combat modifiers in forest
/// </summary>
short StatGroundForest;
/// <summary>
/// Combat modifiers in snow
/// </summary>
short StatGroundSnow;
/// <summary>
/// The base morale level., followed by discipline and training
/// discipline may be normal, low, disciplined or imperuous. Impetuous units may charge without orders
/// </summary>
byte StatMentalMorale;
/// <summary>
/// determines how quickly a unit loses and regains morale
/// </summary>
Discipline StatMentalDiscipline;
/// <summary>
/// determines how tidy the unit's formation is
/// </summary>
Training StatMentalTraining;
/// <summary>
/// Distance from the enemy that the unit will begin charging
/// </summary>
byte StatChargeDist;
/// <summary>
/// An extra delay between missile attacks over that imposed by the animation.
/// </summary>
byte StatFireDelay;
/// <summary>
/// No longer used
/// </summary>
string StatFood;
/// <summary>
/// Number of turns to build
/// </summary>
byte StatCostTurns;
/// <summary>
/// Cost of unit to construct
/// </summary>
ushort StatCostConstruct;
/// <summary>
/// Cost of upkeep
/// </summary>
ushort StatCostUpkeep;
/// <summary>
/// Cost of upgrading weapons
/// </summary>
ushort StatCostUpgradeWeapons;
/// <summary>
/// Cost of upgrading armour
/// </summary>
ushort StatCostUpgradeArmor;
/// <summary>
/// Cost for custom battles
/// </summary>
ushort StatCostCustomBattle;
/// <summary>
/// List of factions and cultures that may have this unit
/// </summary>
string[] Ownership;
static T EnumParse<T>(string s)
{
return (T)Enum.Parse(typeof(T), s);
}
/// <summary>
/// Parses a bunch of text to assign values to a unit. If needed, the unit will be updated with new unit features.
/// </summary>
public void Parse(string text)
{
foreach (string szLine in text.Split(Environment.NewLine.ToCharArray(), StringSplitOptions.RemoveEmptyEntries))
{
//Make sure to replace tabs with spaces in case some foolio threw them in there
string szTrimmed = szLine.Replace("\t", " ").Substring(Indent);
string[] aszSubEntries;
switch (szLine.Replace("\t", " ").Substring(0, Indent))
{
case "type ":
Type = szTrimmed;
break;
case "dictionary ":
Dictionary = szTrimmed;
break;
case "category ":
Category = EnumParse<UnitCategory>(szTrimmed);
break;
case "class ":
Class = EnumParse<UnitClass>(szTrimmed);
break;
case "voice_type ":
VoiceType = EnumParse<Voice>(szTrimmed);
break;
case "soldier ":
aszSubEntries = szTrimmed.Split(',');
SoldierModel = aszSubEntries[0];
SoldierUnitSize = byte.Parse(aszSubEntries[1]);
SoldierExtras = byte.Parse(aszSubEntries[2]);
SoldierMass = decimal.Parse(aszSubEntries[3], NumberStyles.Float);
break;
case "officer ":
Officers.Add(szTrimmed);
break;
case "ship ":
Ship = szTrimmed;
break;
case "engine ":
Engine = szTrimmed;
break;
case "animal ":
Animal = EnumParse<LeashedAnimal>(szTrimmed);
break;
case "mount ":
Mount = szTrimmed;
break;
case "mount_effect ":
aszSubEntries = szTrimmed.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
for (int i = 0; i < aszSubEntries.Length; i += 2)
{
MountEffects.Add(EnumParse<MountEffect>(aszSubEntries[i]), short.Parse(aszSubEntries[i + 1]));
}
break;
case "attributes ":
int nComment = szTrimmed.IndexOf(';');
if (nComment >= 0)
{
szTrimmed = szTrimmed.Remove(nComment).TrimEnd(',', ' ');
}
Attributes = EnumParse<Attribute>(szTrimmed);
break;
case "formation ":
aszSubEntries = szTrimmed.Split(',');
FormationNormalSide = decimal.Parse(aszSubEntries[0], NumberStyles.Float);
FormationNormalFront = decimal.Parse(aszSubEntries[1], NumberStyles.Float);
FormationLooseSide = decimal.Parse(aszSubEntries[2], NumberStyles.Float);
FormationLooseFront = decimal.Parse(aszSubEntries[3], NumberStyles.Float);
FormationRanks = byte.Parse(aszSubEntries[4]);
FormationPrimary = EnumParse<PrimaryFormation>(aszSubEntries[5]);
if (aszSubEntries.Length == 7)
FormationSecondary = EnumParse<SecondaryFormation>(aszSubEntries[6]);
break;
case "stat_health ":
aszSubEntries = szTrimmed.Split(',');
StatHealth[0] = byte.Parse(aszSubEntries[0]);
StatHealth[1] = byte.Parse(aszSubEntries[1]);
break;
case "stat_pri ":
aszSubEntries = szTrimmed.Split(',');
StatPriAttack = byte.Parse(aszSubEntries[0]);
StatPriAttackBonus = byte.Parse(aszSubEntries[1]);
StatPriProjectile = EnumParse<Projectile>(aszSubEntries[2]);
StatPriRange = decimal.Parse(aszSubEntries[3], NumberStyles.Float);
StatPriAmmunition = ushort.Parse(aszSubEntries[4]);
StatPriWeaponType = EnumParse<WeaponType>(aszSubEntries[5]);
StatPriWeaponTech = EnumParse<WeaponTech>(aszSubEntries[6]);
StatPriDamageType = EnumParse<DamageType>(aszSubEntries[7]);
StatPriWeaponSound = EnumParse<WeaponSound>(aszSubEntries[8]);
StatPriAttackDelay = ushort.Parse(aszSubEntries[9]);
StatPriLethality = decimal.Parse(aszSubEntries[10], NumberStyles.Float);
break;
case "stat_pri_attr ":
StatPriAttribute = EnumParse<WeaponAttribute>(szTrimmed);
break;
case "stat_sec ":
aszSubEntries = szTrimmed.Split(',');
StatSecAttack = byte.Parse(aszSubEntries[0]);
StatSecAttackBonus = byte.Parse(aszSubEntries[1]);
StatSecProjectile = EnumParse<Projectile>(aszSubEntries[2]);
StatSecRange = decimal.Parse(aszSubEntries[3], NumberStyles.Float);
StatSecAmmunition = ushort.Parse(aszSubEntries[4]);
StatSecWeaponType = EnumParse<WeaponType>(aszSubEntries[5]);
StatSecWeaponTech = EnumParse<WeaponTech>(aszSubEntries[6]);
StatSecDamageType = EnumParse<DamageType>(aszSubEntries[7]);
StatSecWeaponSound = EnumParse<WeaponSound>(aszSubEntries[8]);
StatSecAttackDelay = ushort.Parse(aszSubEntries[9]);
StatSecLethality = decimal.Parse(aszSubEntries[10], NumberStyles.Float);
break;
case "stat_sec_attr ":
szTrimmed = szTrimmed.TrimEnd(',', ' ');
StatSecAttribute = EnumParse<WeaponAttribute>(szTrimmed);
break;
case "stat_pri_armour ":
aszSubEntries = szTrimmed.Split(',');
StatPriArmourFactor = byte.Parse(aszSubEntries[0]);
StatPriArmourSkill = byte.Parse(aszSubEntries[1]);
StatPriArmourShield = byte.Parse(aszSubEntries[2]);
StatPriArmourSound = EnumParse<ArmorSound>(aszSubEntries[3]);
break;
case "stat_sec_armour ":
aszSubEntries = szTrimmed.Split(',');
StatSecArmourFactor = byte.Parse(aszSubEntries[0]);
StatSecArmourSkill = byte.Parse(aszSubEntries[1]);
StatSecArmourSound = EnumParse<ArmorSound>(aszSubEntries[2]);
break;
case "stat_heat ":
StatHeat = byte.Parse(szTrimmed);
break;
case "stat_ground ":
aszSubEntries = szTrimmed.Split(',');
StatGroundScrub = short.Parse(aszSubEntries[0]);
StatGroundSand = short.Parse(aszSubEntries[1]);
StatGroundForest = short.Parse(aszSubEntries[2]);
StatGroundSnow = short.Parse(aszSubEntries[3]);
break;
case "stat_mental ":
aszSubEntries = szTrimmed.Split(',');
StatMentalMorale = byte.Parse(aszSubEntries[0]);
StatMentalDiscipline = EnumParse<Discipline>(aszSubEntries[1]);
StatMentalTraining = EnumParse<Training>(aszSubEntries[2]);
break;
case "stat_charge_dist ":
StatChargeDist = byte.Parse(szTrimmed);
break;
case "stat_fire_delay ":
StatFireDelay = byte.Parse(szTrimmed);
break;
case "stat_food ":
StatFood = szTrimmed;
break;
case "stat_cost ":
aszSubEntries = szTrimmed.Split(',');
StatCostTurns = byte.Parse(aszSubEntries[0]);
StatCostConstruct = ushort.Parse(aszSubEntries[1]);
StatCostUpkeep = ushort.Parse(aszSubEntries[2]);
StatCostUpgradeWeapons = ushort.Parse(aszSubEntries[3]);
StatCostUpgradeArmor = ushort.Parse(aszSubEntries[4]);
StatCostCustomBattle = ushort.Parse(aszSubEntries[5]);
break;
case "ownership ":
Ownership = szTrimmed.Split(", ".ToCharArray(), StringSplitOptions.RemoveEmptyEntries);
break;
}
}
}
/// <summary>
/// Adds BI-only features to this unit
/// </summary>
public void UpdateToBI()
{
//Append can_swim attribute to:
//+ Cavalry units with armor less than or equal to 10
//+ Infantry units with armor less than or equal to 5
// - Except for desert peoples: Garamantines, Maures, Numidian, African Slingers, arab (with space), Bnei Shevet Aravim,
// Sabean,
// - Except for chariots
if(( (Category == UnitCategory.cavalry) && (StatPriArmourFactor <= 10) &&
!Mount.Contains("chariot")) ||
( (Category == UnitCategory.infantry) && (StatPriArmourFactor <= 5) &&
!Dictionary.Contains("Numidian") &&
!Dictionary.Contains("Garamantines") &&
!SoldierModel.Contains("redsea_axemen") &&
!Dictionary.Contains("Sabean") &&
!Dictionary.Contains("Maures") &&
!Dictionary.Contains("African Slingers") &&
!Dictionary.Contains("arab ") &&
!Dictionary.Contains("Bnei Shevet Aravim")))
{
Attributes |= Attribute.can_swim;
m_updated = true;
}
//Append warcry attribute to:
//+ various angry savages
if ((Category == UnitCategory.infantry) &&
( SoldierModel.Contains("gaesatae") ||
SoldierModel.Contains("wodanawulfoz") ||
Dictionary.Contains("Caturiges Spearmen") ||
Dictionary.Contains("Balroae") ||
Dictionary.Contains("Taxeis Triballoi") ||
Dictionary.Contains("Druhtiz Skandzisku") ||
Dictionary.Contains("Chatti Clubmen") ||
Dictionary.Contains("Wargoz") ||
Dictionary.Contains("Goidilic Ordmhornaght") ||
Dictionary.Contains("Roscaithrera") ||
Dictionary.Contains("Voinu Spearmen")))
{
Attributes |= Attribute.warcry;
m_updated = true;
}
//assign shield_wall as secondary formation to:
//+ hoplite
//+ pseudoshieldwall
//+ disciplined / trained with large shields
if ((Category == UnitCategory.infantry) &&
(FormationSecondary != SecondaryFormation.phalanx) &&
(FormationSecondary != SecondaryFormation.testudo) &&
((Attributes & Attribute.warcry) == 0))
{
if( Dictionary.Contains("Hoplit") ||
SoldierModel.Contains("hoplit") ||
(FormationNormalSide < 0.8m))
{
FormationNormalSide = 1.2m;
FormationNormalFront = 1.5m;
FormationLooseSide = 2.4m;
FormationLooseFront = 3;
FormationSecondary = SecondaryFormation.shield_wall;
m_updated = true;
}
else if ((StatPriArmourShield >= 3) &&
!Type.Contains("skirmisher") &&
((StatMentalDiscipline == Discipline.disciplined) ||
(StatMentalTraining == Training.highly_trained)))
{
FormationNormalSide = 1.3m;
FormationNormalFront = 1.7m;
FormationLooseSide = 2.6m;
FormationLooseFront = 3.4m;
FormationSecondary = SecondaryFormation.shield_wall;
m_updated = true;
}
}
}
/// <summary>
/// Indicates that this unit has been modified with Barbarian Invasion features.
/// </summary>
public bool Updated
{
get
{
return m_updated;
}
}
/// <summary>
/// Get the text representation of this unit, from 'type' to the end of the 'ownership' line.
/// </summary>
/// <returns></returns>
public override string ToString()
{
StringBuilder sb = new StringBuilder();
sb.Append("type ");
sb.AppendLine(Type);
sb.Append("dictionary ");
sb.AppendLine(Dictionary);
sb.Append("category ");
sb.Append(Category);
sb.AppendLine();
sb.Append("class ");
sb.Append(Class);
sb.AppendLine();
sb.Append("voice_type ");
sb.Append(VoiceType);
sb.AppendLine();
sb.Append("soldier ");
sb.Append(SoldierModel);
sb.Append(", ");
sb.Append(SoldierUnitSize);
sb.Append(", ");
sb.Append(SoldierExtras);
sb.Append(", ");
sb.Append(SoldierMass);
sb.AppendLine();
Officers.ForEach(delegate(string szOfficer)
{
sb.Append("officer ");
sb.AppendLine(szOfficer);
});
if (!string.IsNullOrEmpty(Ship))
{
sb.Append("ship ");
sb.AppendLine(Ship);
}
if (!string.IsNullOrEmpty(Engine))
{
sb.Append("engine ");
sb.AppendLine(Engine);
}
if (Animal.HasValue)
{
sb.Append("animal ");
sb.Append(Animal);
sb.AppendLine();
}
if (!string.IsNullOrEmpty(Mount))
{
sb.Append("mount ");
sb.AppendLine(Mount);
}
if (MountEffects.Count > 0)
{
sb.Append("mount_effect ");
bool bFirst = true;
foreach (KeyValuePair<MountEffect, short> pair in MountEffects)
{
if (bFirst)
bFirst = false;
else
sb.Append(", ");
sb.Append(pair.Key);
sb.Append(' ');
if (pair.Value > 0)
sb.Append('+');
sb.Append(pair.Value);
}
sb.AppendLine();
}
sb.Append("attributes ");
sb.Append(Attributes);
sb.AppendLine();
sb.Append("formation ");
sb.Append(FormationNormalSide);
sb.Append(", ");
sb.Append(FormationNormalFront);
sb.Append(", ");
sb.Append(FormationLooseSide);
sb.Append(", ");
sb.Append(FormationLooseFront);
sb.Append(", ");
sb.Append(FormationRanks);
sb.Append(", ");
sb.Append(FormationPrimary);
if (FormationSecondary.HasValue)
{
sb.Append(", ");
sb.Append(FormationSecondary);
}
sb.AppendLine();
sb.Append("stat_health ");
sb.AppendLine(string.Join(", ", Array.ConvertAll<byte, string>(StatHealth, Convert.ToString)));
sb.Append("stat_pri ");
sb.Append(StatPriAttack);
sb.Append(", ");
sb.Append(StatPriAttackBonus);
sb.Append(", ");
sb.Append(StatPriProjectile);
sb.Append(", ");
sb.Append(StatPriRange);
sb.Append(", ");
sb.Append(StatPriAmmunition);
sb.Append(", ");
sb.Append(StatPriWeaponType);
sb.Append(", ");
sb.Append(StatPriWeaponTech);
sb.Append(", ");
sb.Append(StatPriDamageType);
sb.Append(", ");
sb.Append(StatPriWeaponSound);
sb.Append(", ");
sb.Append(StatPriAttackDelay);
//Lethality is messed up. It needs a space and *then* a comma
sb.Append(" ,");
sb.Append(StatPriLethality);
sb.AppendLine();
sb.Append("stat_pri_attr ");
sb.Append(StatPriAttribute);
sb.AppendLine();
sb.Append("stat_sec ");
sb.Append(StatSecAttack);
sb.Append(", ");
sb.Append(StatSecAttackBonus);
sb.Append(", ");
sb.Append(StatSecProjectile);
sb.Append(", ");
sb.Append(StatSecRange);
sb.Append(", ");
sb.Append(StatSecAmmunition);
sb.Append(", ");
sb.Append(StatSecWeaponType);
sb.Append(", ");
sb.Append(StatSecWeaponTech);
sb.Append(", ");
sb.Append(StatSecDamageType);
sb.Append(", ");
sb.Append(StatSecWeaponSound);
sb.Append(", ");
sb.Append(StatSecAttackDelay);
//Lethality is messed up. It needs a space and *then* a comma
sb.Append(" ,");
sb.Append(StatSecLethality);
sb.AppendLine();
sb.Append("stat_sec_attr ");
sb.Append(StatSecAttribute);
sb.AppendLine();
sb.Append("stat_pri_armour ");
sb.Append(StatPriArmourFactor);
sb.Append(", ");
sb.Append(StatPriArmourSkill);
sb.Append(", ");
sb.Append(StatPriArmourShield);
sb.Append(", ");
sb.Append(StatPriArmourSound);
sb.AppendLine();
sb.Append("stat_sec_armour ");
sb.Append(StatSecArmourFactor);
sb.Append(", ");
sb.Append(StatSecArmourSkill);
sb.Append(", ");
sb.Append(StatSecArmourSound);
sb.AppendLine();
sb.Append("stat_heat ");
sb.Append(StatHeat);
sb.AppendLine();
sb.Append("stat_ground ");
sb.Append(StatGroundScrub);
sb.Append(", ");
sb.Append(StatGroundSand);
sb.Append(", ");
sb.Append(StatGroundForest);
sb.Append(", ");
sb.Append(StatGroundSnow);
sb.AppendLine();
sb.Append("stat_mental ");
sb.Append(StatMentalMorale);
sb.Append(", ");
sb.Append(StatMentalDiscipline);
sb.Append(", ");
sb.Append(StatMentalTraining);
sb.AppendLine();
sb.Append("stat_charge_dist ");
sb.Append(StatChargeDist);
sb.AppendLine();
sb.Append("stat_fire_delay ");
sb.Append(StatFireDelay);
sb.AppendLine();
sb.Append("stat_food ");
sb.AppendLine(StatFood);
sb.Append("stat_cost ");
sb.Append(StatCostTurns);
sb.Append(", ");
sb.Append(StatCostConstruct);
sb.Append(", ");
sb.Append(StatCostUpkeep);
sb.Append(", ");
sb.Append(StatCostUpgradeWeapons);
sb.Append(", ");
sb.Append(StatCostUpgradeArmor);
sb.Append(", ");
sb.Append(StatCostCustomBattle);
sb.AppendLine();
//note: after ownership there is no endline
sb.Append("ownership ");
sb.Append(string.Join(", ", Ownership));
return sb.ToString();
}
}
}
Bookmarks