using System; using System.Collections.Generic; using AI; namespace CevoAILib { enum ModelDomain { Ground = 0, Sea = 1, Air = 2 } enum ModelKind { OwnDesign = 0x00, ForeignDesign = 0x01, LongBoats = 0x08, TownGuard = 0x10, Glider = 0x11, Slaves = 0x21, Settlers = 0x22, SpecialCommando = 0x23, Freight = 0x24, Engineers = 0x122 } enum ModelProperty { Weapons = 0, Armor = 1, Mobility = 2, SeaTransport = 3, Carrier = 4, Turbines = 5, Bombs = 6, Fuel = 7, AirTransport = 8, Navigation = 9, RadarSonar = 10, Submarine = 11, Artillery = 12, Alpine = 13, SupplyShip = 14, Overweight = 15, AirDefence = 16, SpyPlane = 17, SteamPower = 18, NuclearPower = 19, JetEngines = 20, Stealth = 21, Fanatic = 22, FirstStrike = 23, PowerOfWill = 24, AcademyTraining = 25, LineProduction = 26 } struct Stage { public readonly int MaximumWeight; public readonly int StrengthMultiplier; public readonly int TransportMultiplier; public readonly int CostMultiplier; readonly int upgradeArray; bool ContainsUpgrade(int upgrade) { return (upgradeArray & (1 << upgrade)) != 0; } public Stage(int maximumWeight, int strengthMultiplier, int transportMultiplier, int costMultiplier, int upgradeArray) { this.MaximumWeight = maximumWeight; this.StrengthMultiplier = strengthMultiplier; this.TransportMultiplier = transportMultiplier; this.CostMultiplier = costMultiplier; this.upgradeArray = upgradeArray; } public override string ToString() { return string.Format("x{0}", StrengthMultiplier); } } /// /// basic model information as available for both own and foreign models /// abstract class ModelBase { #region abstract public abstract int ID { get; } public abstract ModelKind Kind { get; } public abstract Nation Nation { get; } public abstract ModelDomain Domain { get; } public abstract int Attack { get; } public abstract int AttackPlusWithBombs { get; } public abstract int Defense { get; } public abstract int Speed { get; } public abstract int Cost { get; } public abstract int TransportCapacity { get; } public abstract int CarrierCapacity { get; } public abstract int Fuel { get; } public abstract int Weight { get; } public abstract bool HasFeature(ModelProperty feature); #endregion /// /// whether model has 2 tiles observation range (distance 5) instead of adjacent locations only /// public bool HasExtendedObservationRange { get { return (Kind == ModelKind.SpecialCommando || Domain == ModelDomain.Air || HasFeature(ModelProperty.RadarSonar) || HasFeature(ModelProperty.AcademyTraining) || CarrierCapacity > 0); } } public bool HasZoC { get { return Domain == ModelDomain.Ground && Kind != ModelKind.SpecialCommando; } } public bool IsCivil { get { return Attack + AttackPlusWithBombs == 0 || Kind == ModelKind.SpecialCommando; } } /// /// whether units of this model are capable of doing settler jobs /// public bool CanDoJobs { get { return Kind == ModelKind.Settlers || Kind == ModelKind.Engineers || Kind == ModelKind.Slaves; } } public bool CanCaptureCity { get { return Domain == ModelDomain.Ground && !IsCivil; } } public bool CanBombardCity { get { return Attack + AttackPlusWithBombs > 0 && ((Domain == ModelDomain.Sea && HasFeature(ModelProperty.Artillery)) || Domain == ModelDomain.Air); } } /// /// whether units of this model pass hostile terrain without damage /// public bool IsTerrainResistant { get { return Domain != ModelDomain.Ground || Kind == ModelKind.Engineers; } } /// /// By which value the size of a city grows when a unit of this model is added to it. 0 if adding to a city is not possible. /// public int AddsToCitySize { get { switch (Kind) { case ModelKind.Settlers: return 2; case ModelKind.Slaves: return 1; default: return 0; } } } } /// /// own model, abstract base class /// unsafe abstract class AModel : ModelBase { protected readonly Empire theEmpire; public AModel(Empire empire, int indexInSharedMemory) { this.theEmpire = empire; this.IndexInSharedMemory = indexInSharedMemory; address = (int*)theEmpire.address[6] + ROReadPoint.SizeOfModel * indexInSharedMemory; } protected AModel(Empire empire) // for Blueprint only { this.theEmpire = empire; this.IndexInSharedMemory = -1; address = theEmpire.address + ROReadPoint.TestFlags + 19; } public override string ToString() { if (Kind == ModelKind.OwnDesign || Kind == ModelKind.ForeignDesign) return string.Format("Model{0}.{1}({2}/{3}/{4})", (address[2] >> 12) & 0xF, address[2] & 0xFFF, Attack, Defense, Speed); else return string.Format("{0}", Kind); } byte* capacity { get { return (byte*)(address + 10); } } #region IModel Members /// /// unique model ID /// public override int ID { get { return (address[2] & 0xFFFF); } } public override ModelKind Kind { get { ModelKind kind = (ModelKind)(address[4] & 0xFF); if (kind == ModelKind.Settlers && ((address[5] >> 16) & 0xFFFF) > 150) // assume fast settlers are engineers return ModelKind.Engineers; else return kind; } } public override Nation Nation { get { return theEmpire.Us; } } public override ModelDomain Domain { get { return (ModelDomain)((address[4] >> 8) & 0xFF); } } public override int Attack { get { return (address[4] >> 16) & 0xFFFF; } } public override int AttackPlusWithBombs { get { return capacity[(int)ModelProperty.Bombs] * Stage.StrengthMultiplier * 2; } } public override int Defense { get { return address[5] & 0xFFFF; } } public override int Speed { get { return (address[5] >> 16) & 0xFFFF; } } public override int Cost { get { return address[6] & 0xFFFF; } } public override int CarrierCapacity { get { return capacity[(int)ModelProperty.Carrier] * Stage.TransportMultiplier; } } public override int Fuel { get { return capacity[(int)ModelProperty.Fuel]; } } public override int Weight { get { return (address[7] >> 16) & 0xFF; } } public override int TransportCapacity { get { if (Domain == ModelDomain.Air) return capacity[(int)ModelProperty.AirTransport] * Stage.TransportMultiplier; else return capacity[(int)ModelProperty.SeaTransport] * Stage.TransportMultiplier; } } /// /// Whether model has a certain feature. /// Does not work for capacities (Weapons, Armor, Mobility, SeaTransport, Carrier, Turbines, Bombs, Fuel), always returns false for these. /// /// the feature /// true if model has feature, false if not public override bool HasFeature(ModelProperty feature) { if ((int)feature >= Protocol.mcFirstNonCap) return capacity[(int)feature] > 0; else return false; // to maintain consistency with AForeignModel (capacities have special properties) } #endregion public bool RequiresDoubleSupport { get { return (address[9] & Protocol.mdDoubleSupport) != 0; } } public int TurnOfIntroduction { get { return (address[2] >> 16) & 0xFFFF; } } public Stage Stage { get { return new Stage((address[7] >> 24) & 0xFF, (address[6] >> 16) & 0xFFFF, address[7] & 0xFF, (address[7] >> 8) & 0xFF, address[8]); } } public int NumberBuilt { get { return address[3] & 0xFFFF; } } public int NumberLost { get { return (address[3] >> 16) & 0xFFFF; } } /// /// persistent custom value /// public int Status { get { return address[0]; } set { address[0] = value; } } #region template internal stuff /// /// INTERNAL - only access from CevoAILib classes! /// public int IndexInSharedMemory; int* address; #endregion } /// /// foreign model, abstract base class /// unsafe abstract class AForeignModel : ModelBase { protected readonly Empire theEmpire; public AForeignModel(Empire empire, int indexInSharedMemory) { this.theEmpire = empire; this.indexInSharedMemory = indexInSharedMemory; address = (int*)theEmpire.address[9] + ROReadPoint.SizeOfModelInfo * indexInSharedMemory; } public override string ToString() { if (Kind == ModelKind.OwnDesign || Kind == ModelKind.ForeignDesign) return string.Format("Model{0}.{1}({2}/{3}/{4})", (address[1] >> 12) & 0xF, address[1] & 0xFFF, Attack, Defense, Speed); else return string.Format("{0}", Kind); } #region IModel Members /// /// unique model ID /// public override int ID { get { return address[1] & 0xFFFF; } } public override ModelKind Kind { get { ModelKind kind = (ModelKind)((address[1] >> 16) & 0xFF); if (kind == ModelKind.Settlers && (address[3] & 0xFFFF) > 150) // assume fast settlers are engineers return ModelKind.Engineers; else return kind; } } public override Nation Nation { get { return new Nation(theEmpire, address[0] & 0xFFFF); } } public override ModelDomain Domain { get { return (ModelDomain)((address[1] >> 24) & 0xFF); } } public override int Attack { get { return address[2] & 0xFFFF; } } public override int AttackPlusWithBombs { get { return (address[4] >> 16) & 0xFFFF; } } public override int Defense { get { return (address[2] >> 16) & 0xFFFF; } } public override int Speed { get { return address[3] & 0xFFFF; } } public override int Cost { get { return (address[3] >> 16) & 0xFFFF; } } public override int TransportCapacity { get { return address[4] & 0xFF; } } public override int Weight { get { return (address[6] >> 8) & 0xFF; } } public override int CarrierCapacity { get { if (Domain == ModelDomain.Sea) return (address[4] >> 8) & 0xFF; else return 0; } } public override int Fuel { get { if (Domain == ModelDomain.Air) return (address[4] >> 8) & 0xFF; else return 0; } } /// /// Whether model has a certain feature. /// Does not work for capacities (Weapons, Armor, Mobility, SeaTransport, Carrier, Turbines, Bombs, Fuel), always returns false for these. /// /// the feature /// true if model has feature, false if not public override bool HasFeature(ModelProperty feature) { if ((int)feature >= Protocol.mcFirstNonCap) return (address[5] & (1 << ((int)feature - Protocol.mcFirstNonCap))) != 0; else return false; } #endregion public int NumberDefeatet { get { return (address[6] >> 16) & 0xFFFF; } } #region template internal stuff int indexInSharedMemory = -1; int* address; /// /// INTERNAL - only access from CevoAILib classes! /// public int IndexInNationsSharedMemory { get { return (address[0] >> 16) & 0xFFFF; } } #endregion } /// /// Model blueprint for military research. Class of AEmpire.Blueprint. /// class Blueprint : AModel { public Blueprint(Empire empire) : base(empire) { } /// /// Set domain of model. Do this before setting properties. /// /// the domain /// result of operation public PlayResult SetDomain__Turn(ModelDomain domain) { if (theEmpire.Researching == Advance.MilitaryResearch) return new PlayResult(PlayError.ResearchInProgress); else return theEmpire.Play(Protocol.sCreateDevModel, (int)domain); } /// /// Set property of model. Do this after setting the domain. Earlier calls for the same property are voided. /// /// the property /// for capacities: count of usage, for features: 1 = use, 0 = don't use /// result of operation public PlayResult SetProperty__Turn(ModelProperty property, int value) { if (theEmpire.Researching == Advance.MilitaryResearch) return new PlayResult(PlayError.ResearchInProgress); else return theEmpire.Play(Protocol.sSetDevModelCap + (value << 4), (int)property); } } }