using System; using System.Collections.Generic; using Common; using CevoAILib.Diplomacy; using AI; namespace CevoAILib { enum Phase { BeginOfTurn, Turn, EndOfTurn, ForeignTurn }; enum Relation { NoContact = 0, NoTreaty = 1, CeaseFire = 2, Peace = 3, FriendlyContact = 4, Alliance = 5, Identity = 6 } enum Attitude { Hostile = 0, Icy = 1, Uncooperative = 2, Neutral = 3, Receptive = 4, Cordial = 5, Enthusiastic = 6 } enum Government { Anarchy = 0, Despotism = 1, Monarchy = 2, Republic = 3, Fundamentalism = 4, Communism = 5, Democracy = 6, FutureSociety = 7 } enum EmpireEvent { ResearchComplete = 0, AnarchyOver = 3, GliderLost = 8, AircraftLost = 9, PeaceViolation = 10, PeaceEvacuation = 11 } enum Advance { None = -1, MilitaryResearch = 0x800, AdvancedFlight = 0, AmphibiousWarfare = 1, Astronomy = 2, AtomicTheory = 3, Automobile = 4, Ballistics = 5, Banking = 6, BridgeBuilding = 7, BronzeWorking = 8, CeremonialBurial = 9, Chemistry = 10, Chivalry = 11, Composites = 12, CodeOfLaws = 13, CombinedArms = 14, CombustionEngine = 15, Communism = 16, Computers = 17, Conscription = 18, Construction = 19, TheCorporation = 20, SpaceFlight = 21, Currency = 22, Democracy = 23, Economics = 24, Electricity = 25, Electronics = 26, Engineering = 27, Environmentalism = 28, TheWheel = 29, Explosives = 30, Flight = 31, Espionage = 32, Gunpowder = 33, HorsebackRiding = 34, ImpulseDrive = 35, Industrialization = 36, SmartWeapons = 37, Invention = 38, IronWorking = 39, TheLaser = 40, NuclearPower = 41, Literature = 42, TheInternet = 43, Magnetism = 44, MapMaking = 45, Masonry = 46, MassProduction = 47, Mathematics = 48, Medicine = 49, Metallurgy = 50, Miniaturization = 51, MobileWarfare = 52, Monarchy = 53, Mysticism = 54, Navigation = 55, NuclearFission = 56, Philosophy = 57, Physics = 58, Plastics = 59, Poetry = 60, Pottery = 61, Radio = 62, Recycling = 63, Refrigeration = 64, Monotheism = 65, TheRepublic = 66, Robotics = 67, Rocketry = 68, Railroad = 69, Sanitation = 70, Science = 71, Writing = 72, Seafaring = 73, SelfContainedEnvironment = 74, Stealth = 75, SteamEngine = 76, Steel = 77, SyntheticFood = 78, Tactics = 79, Theology = 80, TheoryOfGravity = 81, Trade = 82, TransstellarColonization = 83, University = 84, AdvancedRocketry = 85, WarriorCode = 86, Alphabet = 87, Polytheism = 88, Refining = 89, ComputingTechnology = 90, NanoTechnology = 91, MaterialTechnology = 92, ArtificialIntelligence = 93, FirstCommon = 0, LastCommon = 89, FirstFuture = 90, LastFuture = 93 } struct Economy { public readonly int TaxRate; public readonly int Research; public readonly int Wealth; public Economy(int taxRate, int wealth) { this.TaxRate = taxRate; this.Wealth = wealth; this.Research = 100 - taxRate - wealth; } public override string ToString() { return string.Format("T{0} R{1} W{2}", TaxRate, Research, Wealth); } } struct ColonyShipParts { public readonly int ComponentCount; public readonly int PowerCount; public readonly int HabitationCount; public int this[Building part] { get { switch (part) { case Building.ColonyShipComponent: return ComponentCount; case Building.PowerModule: return PowerCount; case Building.HabitationModule: return HabitationCount; default: return 0; } } } public ColonyShipParts(int componentCount, int powerCount, int habitationCount) { this.ComponentCount = componentCount; this.PowerCount = powerCount; this.HabitationCount = habitationCount; } public override string ToString() { return string.Format("C{0} P{1} H{2}", ComponentCount, PowerCount, HabitationCount); } } unsafe struct Nation { public static Nation None { get { return new Nation(null, -1); } } readonly AEmpire theEmpire; public readonly int ID; public Nation(AEmpire empire, int id) // empire refers to the own empire, not the one of the nation { this.theEmpire = empire; this.ID = id; } public override string ToString() { return string.Format("{0}", ID); } public static bool operator ==(Nation nation1, Nation nation2) { return nation1.ID == nation2.ID; } public static bool operator !=(Nation nation1, Nation nation2) { return nation1.ID != nation2.ID; } public override bool Equals(object obj) { return ID == ((Nation)obj).ID; } public override int GetHashCode() { return ID; } int* report { get { return (int*)theEmpire.address[10 + ID]; } } /// /// whether this nation is still in the game /// public bool Subsists { get { if (ID < 0) return false; else return (theEmpire.address[ROReadPoint.TestFlags + 2] & (1 << ID)) != 0; } } /// /// whether this nation has a specific wonder in one of its cities AND this wonder's effect has not yet expired /// /// the wonder /// true if nation has wonder and wonder is effective, false if it has not or wonder is expired public bool HasWonder(Building wonder) { if (ID < 0) return false; else return theEmpire.address[ROReadPoint.Wonder + 2 * (int)wonder + 1] == ID; } /// /// government form of this nation /// public Government Government { get { if (this == theEmpire.Us) return (Government)theEmpire.address[ROReadPoint.TestFlags + 13]; else return (Government)report[5 + Protocol.nPl]; } } /// /// credibility of this nation /// public int Credibility { get { if (this == theEmpire.Us) return theEmpire.address[ROReadPoint.TestFlags + 5]; else return report[4]; } } /// /// colony ship of this nation /// public ColonyShipParts ColonyShip { get { return new ColonyShipParts(theEmpire.address[ROReadPoint.Ship + 3 * ID], theEmpire.address[ROReadPoint.Ship + 3 * ID + 1], theEmpire.address[ROReadPoint.Ship + 3 * ID + 2]); } } } interface IDossier { int TurnOfReport { get; } int Treasury { get; } bool Has(Advance advance); bool HasAlmost(Advance advance); int FutureTechnology(Advance advance); Advance Researching { get; } int ResearchPile { get; } Relation RelationTo(Nation nation); } /// /// own empire, abstract base class /// unsafe abstract class AEmpire : IDossier { #region abstract protected abstract void NewGame(); protected abstract void Resume(); protected abstract void OnTurn(); protected abstract void OnStealAdvance(Advance[] selection); protected abstract void OnForeignMove(IUnitInfo unit, Location destination); protected abstract void OnBeforeForeignCapture(Nation nation, ICity city); protected abstract void OnAfterForeignCapture(); protected abstract void OnBeforeForeignAttack(IUnitInfo attacker, Location target, BattleOutcome outcome); protected abstract void OnAfterForeignAttack(); protected abstract void OnChanceToNegotiate(Phase situation, Nation Opponent, ref bool wantNegotiation, ref bool cancelTreatyIfRejected); protected abstract void OnNegotiate(Negotiation negotiation); #endregion public readonly Nation Us; public readonly Map Map; public readonly ToughSet Units = new ToughSet(); public readonly ForeignUnitList ForeignUnits; public readonly List Models = new List(); public readonly List ForeignModels = new List(); public readonly ToughSet Cities = new ToughSet(); public readonly ToughSet ForeignCities = new ToughSet(); /// /// Model blueprint for military research. /// public readonly Blueprint Blueprint; /// /// persistent memory /// public readonly Persistent Persistent; public AEmpire(int nationID, IntPtr serverPtr, IntPtr dataPtr, bool isNewGame) { int* data = (int*)dataPtr; serverCall = (ServerCall)System.Runtime.InteropServices.Marshal.GetDelegateForFunctionPointer(serverPtr, typeof(ServerCall)); address = (int*)data[4 + Protocol.nPl + nationID]; DifficultyLevel = data[4 + nationID]; TurnWhenGameEnds = data[3]; this.isNewGame = isNewGame; foreignTurnUpdateAreas = UpdateArea.All; Us = new Nation(this, nationID); Map = new Map(this, data[0], data[1], data[2]); ForeignUnits = new ForeignUnitList(this); Blueprint = new Blueprint((Empire)this); Persistent = new Persistent((Empire)this, (IntPtr)address[0]); debugMapAddress = (int*)address[ROReadPoint.OracleIncome + 1]; UpdateLists(UpdateArea.All); } // for convenience, map all members of Us to empire public bool Subsists { get { return Us.Subsists; } } public Government Government { get { return Us.Government; } } public int Credibility { get { return Us.Credibility; } } public bool HasWonder(Building wonder) { return Us.HasWonder(wonder); } public ColonyShipParts ColonyShip { get { return Us.ColonyShip; } } #region IDossier members public int TurnOfReport { get { return Turn; } } public int Treasury { get { return address[ROReadPoint.TestFlags + 14]; } } /// /// whether an advance has been completely researched /// /// the advance /// true if researched, false if not public bool Has(Advance advance) { return ((sbyte*)(address + ROReadPoint.Tech))[(int)advance] >= 0; } /// /// whether an advance was gained from a trade with another nation or from the temple of zeus wonder /// /// the advance /// true if gained, false if not public bool HasAlmost(Advance advance) { return ((sbyte*)(address + ROReadPoint.Tech))[(int)advance] == -1; } /// /// science points collected for current research /// public int ResearchPile { get { return address[ROReadPoint.TestFlags + 17]; } } /// /// advance currently researched /// public Advance Researching { get { int ad = address[ROReadPoint.TestFlags + 18]; if (ad < 0) return Advance.None; else return (Advance)ad; } } /// /// relation to specific other nation /// /// the other nation /// the relation public Relation RelationTo(Nation nation) { if (nation == Us) return Relation.Identity; else return (Relation)(address[ROReadPoint.Attitude + Protocol.nPl + nation.ID] + 1); } /// /// number of future technologies developed /// /// the future technology /// number public int FutureTechnology(Advance advance) { sbyte raw = ((sbyte*)(address + ROReadPoint.Tech))[(int)advance]; if (raw <= 0) return 0; else return raw; } #endregion public bool IsMyTurn { get { return phase != Phase.ForeignTurn; } } public int Turn { get { return address[ROReadPoint.TestFlags + 1]; } } /// /// whether a specific nation level event occurred in this turn /// /// the event /// true if the event occurred, false if not public bool HadEvent__Turn(EmpireEvent empireEvent) { return (address[ROReadPoint.TestFlags + 3] & (1 << (int)empireEvent)) != 0; } public readonly int DifficultyLevel; //bool IsManipulationActivated(Manipulation manipulation); todo !!! public readonly int TurnWhenGameEnds; public int TurnOfAnarchyBegin { get { return address[ROReadPoint.TestFlags + 4]; } } public int MaximumCredibilityLeft { get { return address[ROReadPoint.TestFlags + 6]; } } /// /// current economy settings, call ChangeEconomy__Turn to change /// public Economy Economy { get { return new Economy(address[ROReadPoint.TestFlags + 15], address[ROReadPoint.TestFlags + 16]); } } public int IncomeFromOracle { get { return address[ROReadPoint.OracleIncome]; } } public bool CanSetResearch__Turn(Advance advance) { return TestPlay(Protocol.sSetResearch, (int)advance).OK; } public RelationDetails RelationDetailsTo(Nation nation) { return new RelationDetails(this, nation); } public BattleHistory BattleHistory { get { return new BattleHistory(this); } } /// /// number of nations that are still in the game /// public int NumberOfSubsistingNations { get { int aliveArray = address[ROReadPoint.TestFlags + 2]; int count = 0; for (int nationID = 0; nationID < Protocol.nPl; nationID++) { if ((aliveArray & (1 << nationID)) != 0) count++; } return count; } } /// /// set of nations that are still in the game /// public Nation[] SubsistingNations { get { Nation[] subsistingNations = new Nation[NumberOfSubsistingNations]; int aliveArray = address[ROReadPoint.TestFlags + 2]; int count = 0; for (int nationID = 0; nationID < Protocol.nPl; nationID++) { if ((aliveArray & (1 << nationID)) != 0) { subsistingNations[count] = new Nation(this, nationID); count++; } } return subsistingNations; } } /// /// science points to collect before the current research is complete /// public int CurrentResearchCost { get { fixed (int* researchCost = new int[1]) { Play(Protocol.sGetTechCost, 0, researchCost); return researchCost[0]; } } } /// /// Whether a specific building is built in one of the own cities. /// Applicable to wonders and state improvements. /// In case of wonders true is only returned if this wonder's effect has not yet expired. /// /// the wonder /// true if built and effective, false if not public bool Has(Building building) { if (building < Building.WonderRange) return Us.HasWonder(building); else return ((sbyte*)(address + ROReadPoint.NatBuilt))[(int)building - (int)Building.WonderRange] > 0; } public bool Wonder_WasBuilt(Building wonder) { return address[ROReadPoint.Wonder + 2 * (int)wonder] != -1; } public bool Wonder_WasDestroyed(Building wonder) { return address[ROReadPoint.Wonder + 2 * (int)wonder] == -2; } public bool Wonder_IsInCity(Building wonder, ICity city) { return address[ROReadPoint.Wonder + 2 * (int)wonder] == city.ID; } public Dossier LastDossier(Nation nation) { return new Dossier(this, nation); } public MilitaryReport LastMilitaryReport(Nation nation) { return new MilitaryReport(this, nation); } #region effective methods /// /// Start revolution. /// /// result of operation public PlayResult Revolution__Turn() { PlayResult result = Play(Protocol.sRevolution); if (result.OK) InvalidateAllCityReports(); return result; } /// /// Set government form. Requires AnarchyOver event to have occurred this turn. /// /// new government form /// result of operation public PlayResult SetGovernment__Turn(Government newGovernment) { PlayResult result = Play(Protocol.sSetGovernment, (int)newGovernment); if (result.Effective) InvalidateAllCityReports(); return result; } /// /// Change economy settings. /// /// new economy settings /// result of operation public PlayResult ChangeEconomy__Turn(Economy economy) { PlayResult result = Play(Protocol.sSetRates, (economy.TaxRate / 10 & 0xf) + ((economy.Wealth / 10 & 0xf) << 4)); if (result.Effective) InvalidateAllCityReports(); return result; } /// /// Set new advance to research. Requires ResearchComplete event to have occurred this turn. /// If advance is MilitaryResearch Blueprint must have been designed as desired already. /// /// advance to research /// result of operation public PlayResult SetResearch__Turn(Advance advance) { return Play(Protocol.sSetResearch, (int)advance); } /// /// Steal advance as offered by the temple of zeus wonder. /// Call from OnStealAdvance handler only. /// /// the advance to steal /// result of operation public PlayResult StealAdvance__Turn(Advance advance) { return Play(Protocol.sStealTech, (int)advance); } /// /// change attitude to other nation /// /// the nation /// the attitude /// result of operation public PlayResult ChangeAttitudeTo(Nation nation, Attitude attitude) { return Play(Protocol.sSetAttitude + (nation.ID << 4), (int)attitude); } #endregion #region template internal stuff [System.Runtime.InteropServices.UnmanagedFunctionPointer(System.Runtime.InteropServices.CallingConvention.StdCall)] delegate int ServerCall(int command, int nation, int subject, void* data); readonly ServerCall serverCall; readonly bool isNewGame; /// /// INTERNAL - only access from CevoAILib classes! /// public readonly int* address; /// /// INTERNAL - only access from CevoAILib classes! /// public readonly int* debugMapAddress; /// /// INTERNAL - only access from CevoAILib classes! /// public readonly Unit[] UnitLookup = new Unit[Cevo.MaxUnitsPerNation]; /// /// INTERNAL - only access from CevoAILib classes! /// public readonly City[] CityLookup = new City[Cevo.MaxCitiesPerNation]; readonly byte[] foreignCityLookup = new byte[Cevo.MaxCitiesPerNation * Cevo.MaxNumberOfNations]; bool called = false; Phase phase = Phase.ForeignTurn; bool foreignMoveSkipped; bool foreignMoveIsCapture; UpdateArea foreignTurnUpdateAreas; // diplomacy Nation possibleNegotiationWith = Nation.None; bool cancelTreatyIfRejected = false; List nationsContacted = new List(); List nationsNotToContact = new List(); Negotiation currentNegotiation = null; /// /// INTERNAL - only call from CevoAILib classes! /// public void StealAdvance() { List stealable = new List(); for (Advance testAdvance = Advance.FirstCommon; testAdvance <= Advance.LastCommon; testAdvance++) { if (TestPlay(Protocol.sStealTech, (int)testAdvance).OK) stealable.Add(testAdvance); } if (stealable.Count > 0) OnStealAdvance(stealable.ToArray()); } /// /// INTERNAL - only call from CevoAILib classes! /// public PlayResult Play(int command, int subject, void* data) { return new PlayResult(serverCall(command, Us.ID, subject, data)); } /// /// INTERNAL - only call from CevoAILib classes! /// public PlayResult Play(int command, int subject, int data) { fixed (int* dataPtr = new int[1]) { dataPtr[0] = data; return new PlayResult(serverCall(command, Us.ID, subject, dataPtr)); } } /// /// INTERNAL - only call from CevoAILib classes! /// public PlayResult Play(int command, int subject) { return new PlayResult(serverCall(command, Us.ID, subject, null)); } /// /// INTERNAL - only call from CevoAILib classes! /// public PlayResult Play(int command) { return new PlayResult(serverCall(command, Us.ID, 0, null)); } /// /// INTERNAL - only call from CevoAILib classes! /// public PlayResult TestPlay(int command, int subject, void* data) { return new PlayResult(serverCall(command - Protocol.sExecute, Us.ID, subject, data)); } /// /// INTERNAL - only call from CevoAILib classes! /// public PlayResult TestPlay(int command, int subject, int data) { fixed (int* dataPtr = new int[1]) { dataPtr[0] = data; return new PlayResult(serverCall(command - Protocol.sExecute, Us.ID, subject, dataPtr)); } } /// /// INTERNAL - only call from CevoAILib classes! /// public PlayResult TestPlay(int command, int subject) { return new PlayResult(serverCall(command - Protocol.sExecute, Us.ID, subject, null)); } /// /// INTERNAL - only call from CevoAILib classes! /// public PlayResult TestPlay(int command) { return new PlayResult(serverCall(command - Protocol.sExecute, Us.ID, 0, null)); } /// /// INTERNAL - only call from CevoAILib classes! /// public void InvalidateAllCityReports() { foreach (City city in Cities) city.InvalidateReport(); } [Flags] public enum UpdateArea { Basic = 0x00, Units = 0x01, Cities = 0x02, ForeignCities = 0x08, All = 0xFF } /// /// INTERNAL - only call from CevoAILib classes! /// update the lists Models and ForeignModels /// and to correct the lists Units, Cities and ForeignCities /// this includes address correction of objects, adding objects for new items, /// removing objects of destroyed items and marking these objects /// public void UpdateLists(UpdateArea areas) { // models int* sharedMemoryList = (int*)address[6]; int sharedMemoryCount = address[ROReadPoint.TestFlags + 9]; for (int indexInSharedMemory = Models.Count; indexInSharedMemory < sharedMemoryCount; indexInSharedMemory++) Models.Add(new Model((Empire)this, indexInSharedMemory)); // foreign models sharedMemoryList = (int*)address[9]; sharedMemoryCount = address[ROReadPoint.TestFlags + 12]; for (int indexInSharedMemory = ForeignModels.Count; indexInSharedMemory < sharedMemoryCount; indexInSharedMemory++) ForeignModels.Add(new ForeignModel((Empire)this, indexInSharedMemory)); // cities #if DEBUG bool doCities = true; #else bool doCities = (areas & UpdateArea.Cities) != 0; #endif if (doCities) { sharedMemoryList = (int*)address[5]; sharedMemoryCount = address[ROReadPoint.TestFlags + 8]; Array.Clear(CityLookup, 0, sharedMemoryCount); foreach (City city in Cities) { int indexInSharedMemory = city.IndexInSharedMemory; // items do only move backward in shared memory int originalIndex = indexInSharedMemory; while (indexInSharedMemory >= 0 && (indexInSharedMemory >= sharedMemoryCount || // index beyond bounds (sharedMemoryList[ROReadPoint.SizeOfCity * indexInSharedMemory + 3] & 0xFFFF) != city.ID || // not the right city sharedMemoryList[ROReadPoint.SizeOfCity * indexInSharedMemory] < 0)) // LID < 0 indicates gap in list indexInSharedMemory--; if (indexInSharedMemory < 0) { // city was captured or destroyd, remove if ((areas & UpdateArea.Cities) == 0) // debug check fails throw new Exception("UpdateLists: City removal is not updated!"); city.IndexInSharedMemory = -1; Cities.RemoveCurrent(); } else { // city still exists, correct shared memory index if (indexInSharedMemory != originalIndex) { if ((areas & UpdateArea.Cities) == 0) // debug check fails throw new Exception("UpdateLists: City index change is not updated!"); city.IndexInSharedMemory = indexInSharedMemory; } CityLookup[indexInSharedMemory] = city; } } for (int indexInSharedMemory = 0; indexInSharedMemory < sharedMemoryCount; indexInSharedMemory++) { if (CityLookup[indexInSharedMemory] == null && sharedMemoryList[ROReadPoint.SizeOfCity * indexInSharedMemory] >= 0) // LID < 0 indicates gap in list { // shared memory object not in list if ((areas & UpdateArea.Cities) == 0) // debug check fails throw new Exception("UpdateLists: City creation is not updated!"); City city = new City((Empire)this, indexInSharedMemory); Cities.Add(city); CityLookup[indexInSharedMemory] = city; } } } // foreign cities #if DEBUG bool doForeignCities = true; #else bool doForeignCities = (areas & UpdateArea.ForeignCities) != 0; #endif if (doForeignCities) { sharedMemoryList = (int*)address[8]; sharedMemoryCount = address[ROReadPoint.TestFlags + 11]; Array.Clear(foreignCityLookup, 0, sharedMemoryCount); foreach (ForeignCity city in ForeignCities) { int indexInSharedMemory = city.IndexInSharedMemory; // items do only move backward in shared memory int originalIndex = indexInSharedMemory; while (indexInSharedMemory >= 0 && (indexInSharedMemory >= sharedMemoryCount || // index beyond bounds ((sharedMemoryList[ROReadPoint.SizeOfCityInfo * indexInSharedMemory + 3] >> 16) & 0xFFFF) != city.ID || // not the right city sharedMemoryList[ROReadPoint.SizeOfCityInfo * indexInSharedMemory] < 0)) // LID < 0 indicates gap in list indexInSharedMemory--; if (indexInSharedMemory < 0) { // city was captured or destroyd, remove if ((areas & UpdateArea.ForeignCities) == 0) // debug check fails throw new Exception("UpdateLists: Foreign city removal is not updated!"); city.IndexInSharedMemory = -1; ForeignCities.RemoveCurrent(); } else { // city still exists, correct shared memory index if (indexInSharedMemory != originalIndex) { if ((areas & UpdateArea.ForeignCities) == 0) // debug check fails throw new Exception("UpdateLists: Foreign city index change is not updated!"); city.IndexInSharedMemory = indexInSharedMemory; } foreignCityLookup[indexInSharedMemory] = 1; } } for (int indexInSharedMemory = 0; indexInSharedMemory < sharedMemoryCount; indexInSharedMemory++) { if (foreignCityLookup[indexInSharedMemory] == 0 && sharedMemoryList[ROReadPoint.SizeOfCityInfo * indexInSharedMemory] >= 0) // LID < 0 indicates gap in list { if ((areas & UpdateArea.ForeignCities) == 0) // debug check fails throw new Exception("UpdateLists: Foreign city creation is not updated!"); ForeignCities.Add(new ForeignCity((Empire)this, indexInSharedMemory)); } } } // units #if DEBUG bool doUnits = true; #else bool doUnits = (areas & UpdateArea.Units) != 0; #endif if (doUnits) { sharedMemoryList = (int*)address[4]; sharedMemoryCount = address[ROReadPoint.TestFlags + 7]; Array.Clear(UnitLookup, 0, sharedMemoryCount); foreach (Unit unit in Units) { int indexInSharedMemory = unit.IndexInSharedMemory; // items do only move backward in shared memory int originalIndex = indexInSharedMemory; while (indexInSharedMemory >= 0 && (indexInSharedMemory >= sharedMemoryCount || // index beyond bounds (sharedMemoryList[ROReadPoint.SizeOfUn * indexInSharedMemory + 3] & 0xFFFF) != unit.ID || // not the right unit sharedMemoryList[ROReadPoint.SizeOfUn * indexInSharedMemory] < 0)) // LID < 0 indicates gap in list indexInSharedMemory--; if (indexInSharedMemory < 0) { // unit was destroyd, remove if ((areas & UpdateArea.Units) == 0) // debug check fails throw new Exception("UpdateLists: Unit removal is not updated!"); unit.IndexInSharedMemory = -1; Units.RemoveCurrent(); } else { // unit still exists, correct shared memory index if (indexInSharedMemory != originalIndex) { if ((areas & UpdateArea.Units) == 0) // debug check fails throw new Exception("UpdateLists: Unit index change is not updated!"); unit.IndexInSharedMemory = indexInSharedMemory; } UnitLookup[indexInSharedMemory] = unit; } } for (int indexInSharedMemory = 0; indexInSharedMemory < sharedMemoryCount; indexInSharedMemory++) { if (UnitLookup[indexInSharedMemory] == null && sharedMemoryList[ROReadPoint.SizeOfUn * indexInSharedMemory] >= 0) // LID < 0 indicates gap in list { // shared memory object not in list if ((areas & UpdateArea.Units) == 0) // debug check fails throw new Exception("UpdateLists: Unit creation is not updated!"); Unit unit = new Unit((Empire)this, indexInSharedMemory); Units.Add(unit); UnitLookup[indexInSharedMemory] = unit; } } } } // internal void ForeignTurnUpdate() { UpdateLists(foreignTurnUpdateAreas); foreignTurnUpdateAreas = UpdateArea.Basic; } /// /// INTERNAL - only call from Plugin class! /// public void Process(int command, IntPtr dataPtr) { if (!called) { UpdateLists(UpdateArea.All); if (isNewGame) NewGame(); else Resume(); } called = true; int* data = (int*)dataPtr; switch (command) { case Protocol.cTurn: case Protocol.cContinue: { if (!Subsists) { Play(Protocol.sTurn); return; } if (command == Protocol.cTurn) { phase = Phase.BeginOfTurn; nationsContacted.Clear(); if (Researching != Advance.MilitaryResearch) Play(Protocol.sCreateDevModel, (int)ModelDomain.Ground); // keep blueprint current UpdateLists(UpdateArea.All); } if (command == Protocol.cContinue && possibleNegotiationWith != Nation.None) { // that means a negotiation attempt was made but rejected if (cancelTreatyIfRejected && RelationTo(possibleNegotiationWith) >= Relation.Peace) Play(Protocol.sCancelTreaty); } else nationsNotToContact.Clear(); currentNegotiation = null; possibleNegotiationWith = Nation.None; cancelTreatyIfRejected = false; InvalidateAllCityReports(); // turn begin and after negotiation while (true) { if (Government != Government.Anarchy) { foreach (Nation nation in SubsistingNations) { if (nation != Us && RelationTo(nation) != Relation.NoContact && nation.Government != Government.Anarchy && !(nationsContacted.Contains(nation)) && !(nationsNotToContact.Contains(nation)) && TestPlay(Protocol.scContact + (nation.ID << 4)).OK) { bool wantNegotiation = false; cancelTreatyIfRejected = false; OnChanceToNegotiate(phase, nation, ref wantNegotiation, ref cancelTreatyIfRejected); if (wantNegotiation) { nationsContacted.Add(nation); possibleNegotiationWith = nation; Play(Protocol.scContact + (nation.ID << 4)); return; } else nationsNotToContact.Add(nation); } } } if (phase == Phase.BeginOfTurn) { phase = Phase.Turn; OnTurn(); phase = Phase.EndOfTurn; nationsContacted.Clear(); nationsNotToContact.Clear(); } else break; } #if DEBUG UpdateLists(UpdateArea.Basic); // check for list update problems #endif foreignTurnUpdateAreas = UpdateArea.Units; // units might be disbanded in after-turn processing phase = Phase.ForeignTurn; Play(Protocol.sTurn); break; } case Protocol.scContact: { if (phase != Phase.ForeignTurn) throw new Exception("Error in logic: scDipStart should not be called in own turn!"); ForeignTurnUpdate(); possibleNegotiationWith = new Nation(this, data[0]); bool wantNegotiation = false; bool dummy = false; OnChanceToNegotiate(phase, possibleNegotiationWith, ref wantNegotiation, ref dummy); if (wantNegotiation) Play(Protocol.scDipStart); else Play(Protocol.scReject); break; } case Protocol.scDipStart: case Protocol.scDipNotice: case Protocol.scDipAccept: case Protocol.scDipCancelTreaty: case Protocol.scDipOffer: case Protocol.scDipBreak: { if (currentNegotiation == null) currentNegotiation = new Negotiation(this, phase, possibleNegotiationWith); possibleNegotiationWith = Nation.None; cancelTreatyIfRejected = false; if (command == Protocol.scDipStart) // no statements yet in this negotiation { if (phase == Phase.ForeignTurn) throw new Exception("Error in logic: scDipStart should only be called in own turn!"); currentNegotiation.SetOurNextStatement(new SuggestEnd()); } else { bool afterTrade = (command == Protocol.scDipAccept || // opponent accepted our suggested trade (currentNegotiation.History.Count > 0 && currentNegotiation.OurNextStatement is AcceptTrade)); // we accepted opponent suggested trade if (phase == Phase.ForeignTurn) { if (afterTrade) foreignTurnUpdateAreas |= UpdateArea.ForeignCities; // in case map was traded ForeignTurnUpdate(); } else { if (afterTrade) UpdateLists(UpdateArea.ForeignCities); // in case map was traded } IStatement oppenentStatement = StatementFactory.OpponentStatementFromCommand(this, currentNegotiation.Opponent, command, data); if (currentNegotiation.History.Count == 0 && phase == Phase.ForeignTurn) currentNegotiation.History.Insert(0, new ExchangeOfStatements(new SuggestEnd() /*imaginary, has not happened*/, oppenentStatement)); else currentNegotiation.History.Insert(0, new ExchangeOfStatements(currentNegotiation.OurNextStatement, oppenentStatement)); if (oppenentStatement is CancelTreaty || oppenentStatement is Break) currentNegotiation.SetOurNextStatement(new Notice()); // initialize with standard response else currentNegotiation.SetOurNextStatement(new SuggestEnd()); // initialize with standard response } OnNegotiate(currentNegotiation); if (currentNegotiation.OurNextStatement is SuggestTrade) { fixed (int* tradeData = new int[14]) { ((SuggestTrade)currentNegotiation.OurNextStatement).FillRawStream(tradeData); Play(currentNegotiation.OurNextStatement.Command, 0, tradeData); } } else Play(currentNegotiation.OurNextStatement.Command); break; } case Protocol.cShowEndContact: { currentNegotiation = null; break; } case Protocol.cShowMoving: case Protocol.cShowCapturing: { if (phase != Phase.ForeignTurn) throw new Exception("Error in logic: cShowMoving should not be called in own turn!"); foreignMoveIsCapture = (command == Protocol.cShowCapturing); Relation relationToMovingNation = (Relation)(address[ROReadPoint.Attitude + Protocol.nPl + data[0]] + 1); foreignMoveSkipped = !foreignMoveIsCapture && relationToMovingNation == Relation.Alliance; // allied movement: low relevance, high frequency, so skip if (foreignMoveSkipped) foreignTurnUpdateAreas |= UpdateArea.ForeignCities; // allies movement might gain new city information else { ForeignTurnUpdate(); MovingUnit unit = new MovingUnit(this, data); RC test = new RC((data[6] + data[7]) >> 1, (data[7] - data[6]) >> 1); Location target = unit.Location + new RC((data[6] + data[7]) >> 1, (data[7] - data[6]) >> 1); OnForeignMove(unit, target); if (foreignMoveIsCapture) OnBeforeForeignCapture(unit.Nation, target.City); } break; } case Protocol.cShowAttacking: { if (phase != Phase.ForeignTurn) throw new Exception("Error in logic: cShowAttacking should not be called in own turn!"); ForeignTurnUpdate(); MovingUnit unit = new MovingUnit(this, data); Location target = unit.Location + new RC((data[6] + data[7]) >> 1, (data[7] - data[6]) >> 1); OnBeforeForeignAttack(unit, target, new BattleOutcome(data[8], data[9])); break; } case Protocol.cShowAfterMove: { if (phase != Phase.ForeignTurn) throw new Exception("Error in logic: cShowAfterMove should not be called in own turn!"); if (foreignMoveIsCapture && !foreignMoveSkipped) { ForeignTurnUpdate(); OnAfterForeignCapture(); // cShowCityChanged was already called here } break; } case Protocol.cShowAfterAttack: { if (phase != Phase.ForeignTurn) throw new Exception("Error in logic: cShowAfterAttack should not be called in own turn!"); foreignTurnUpdateAreas |= UpdateArea.Units; // if city was destroyed by attack, cShowCityChanged was already called here ForeignTurnUpdate(); OnAfterForeignAttack(); break; } case Protocol.cShowCityChanged: { if (phase != Phase.ForeignTurn) throw new Exception("Error in logic: cShowCityChanged should not be called in own turn!"); foreignTurnUpdateAreas |= UpdateArea.All; // not called very often, so full update doesn't hurt break; } } } #endregion } unsafe struct RelationDetails { readonly AEmpire theEmpire; readonly Nation nation; public RelationDetails(AEmpire empire, Nation nation) { this.theEmpire = empire; this.nation = nation; } int* report { get { return (int*)theEmpire.address[10 + nation.ID]; } } public Attitude OurAttitudeToThem { get { return (Attitude)theEmpire.address[ROReadPoint.Attitude + nation.ID]; } } public Attitude TheirAttitudeToUs { get { return (Attitude)report[4]; } } public int TurnOfLastNegotiation { get { return report[0]; } } public int TurnOfLastCancellingTreaty { get { return theEmpire.address[ROReadPoint.nBattleHistory + 3 + nation.ID]; } } public int TurnOfPeaceEvacuationBegin { get { return theEmpire.address[ROReadPoint.Attitude + 2 * Protocol.nPl + nation.ID]; } } } unsafe struct Dossier : IDossier { readonly AEmpire theEmpire; readonly Nation nation; public Dossier(AEmpire empire, Nation nation) { this.theEmpire = empire; this.nation = nation; } public override string ToString() { if (TurnOfReport >= 0) return string.Format("{0}", TurnOfReport); else return "NA"; } public bool IsAvailable { get { return TurnOfReport >= 0; } } int* report { get { return (int*)theEmpire.address[10 + nation.ID]; } } public int TurnOfReport { get { return report[1]; } } public int Treasury { get { return report[6 + Protocol.nPl]; } } /// /// whether an advance has been completely researched /// /// the advance /// true if researched, false if not public bool Has(Advance advance) { return ((sbyte*)(report + 9 + Protocol.nPl))[(int)advance] >= 0; } /// /// whether an advance was gained from a trade with another nation or from the temple of zeus wonder /// /// the advance /// true if gained, false if not public bool HasAlmost(Advance advance) { return ((sbyte*)(report + 9 + Protocol.nPl))[(int)advance] == -1; } /// /// science points collected for current research /// public int ResearchPile { get { return report[8 + Protocol.nPl]; } } /// /// advance currently researched /// public Advance Researching { get { int ad = report[7 + Protocol.nPl]; if (ad < 0) return Advance.None; else return (Advance)ad; } } /// /// relation to specific other nation /// /// the other nation /// the relation public Relation RelationTo(Nation thirdNation) { if (thirdNation == nation) return Relation.Identity; else return (Relation)(report[5 + thirdNation.ID] + 1); } /// /// number of future technologies developed /// /// the future technology /// number public int FutureTechnology(Advance advance) { sbyte raw = ((sbyte*)(report + 9 + Protocol.nPl))[(int)advance]; if (raw <= 0) return 0; else return raw; } } unsafe struct MilitaryReport { readonly AEmpire theEmpire; readonly Nation nation; public MilitaryReport(AEmpire empire, Nation nation) { this.theEmpire = empire; this.nation = nation; } public override string ToString() { if (TurnOfReport >= 0) return string.Format("{0}", TurnOfReport); else return "NA"; } public bool IsAvailable { get { return TurnOfReport >= 0; } } int* report { get { return (int*)theEmpire.address[10 + nation.ID]; } } public int TurnOfReport { get { return report[2]; } } // todo !!! } unsafe struct BattleHistory { readonly AEmpire theEmpire; public BattleHistory(AEmpire empire) { theEmpire = empire; } // todo !!! } }