diff --git a/FrEee.Assets/Data/EventTypes.txt b/FrEee.Assets/Data/EventTypes.txt index fc438edc2..d2db66691 100644 --- a/FrEee.Assets/Data/EventTypes.txt +++ b/FrEee.Assets/Data/EventTypes.txt @@ -8,7 +8,7 @@ EVENT TYPES DATA FILE Name := Ship - Damage Is Negative When := amount > 0 -Import := from FrEee.Objects.Space import Galaxy; +Import := from FrEee.Objects.GameState import Galaxy; Import := from FrEee.Objects.Vehicles import SpaceVehicle; Import := from FrEee.Interfaces import IUnit; Import := from FrEee.Interfaces import IVehicle; @@ -20,7 +20,7 @@ Action := target.TakeDamage("Skips All Shields", amount); Name := Ship - Lose Movement Is Negative When := amount > 0 -Import := from FrEee.Objects.Space import Galaxy; +Import := from FrEee.Objects.GameState import Galaxy; Import := from FrEee.Objects.Vehicles import Ship; Target Selector := Galaxy.Current.FindSpaceObjects[Ship]() Parameter := VehicleName = target.Name; @@ -30,7 +30,7 @@ Action := target.MovementRemaining -= amount; Name := Ship - Lose Supply Is Negative When := amount > 0 -Import := from FrEee.Objects.Space import Galaxy; +Import := from FrEee.Objects.GameState import Galaxy; Import := from FrEee.Objects.Vehicles import Ship; Target Selector := Galaxy.Current.FindSpaceObjects[Ship]() Parameter := VehicleName = target.Name; @@ -40,7 +40,7 @@ Action := target.SupplyRemaining -= amount; Name := Ship - Cargo Damage Is Negative When := amount > 0 -Import := from FrEee.Objects.Space import Galaxy; +Import := from FrEee.Objects.GameState import Galaxy; Import := from FrEee.Interfaces import ICargoContainer; Target Selector := Galaxy.Current.FindSpaceObjects[ICargoContainer]() Parameter := VehicleName = target.Name; @@ -50,7 +50,7 @@ Action := target.Cargo.TakeDamage("Skips All Shields", amount); Name := Ship - Moved Is Negative When := true -Import := from FrEee.Objects.Space import Galaxy; +Import := from FrEee.Objects.GameState import Galaxy; Import := from FrEee.Objects.Vehicles import SpaceVehicle; Import := from FrEee.Interfaces import IUnit; Import := from FrEee.Interfaces import IVehicle; @@ -62,7 +62,7 @@ Action := target.Sector = Galaxy.Current.PickRandomSector(); Name := Planet - Conditions Change Is Negative When := amount < 0 -Import := from FrEee.Objects.Space import Galaxy; +Import := from FrEee.Objects.GameState import Galaxy; Import := from FrEee.Objects.Space import Planet; Import := from FrEee.Interfaces import IUnit; Target Selector := Galaxy.Current.FindSpaceObjects[Planet]() @@ -73,7 +73,7 @@ Action := # planets don't have conditions yet so do nothing Name := Planet - Value Change Is Negative When := amount < 0 -Import := from FrEee.Objects.Space import Galaxy; +Import := from FrEee.Objects.GameState import Galaxy; Import := from FrEee.Objects.Space import Planet; Import := from FrEee.Interfaces import IUnit; Import := from FrEee.Utility import Resource; @@ -87,7 +87,7 @@ Action := target.Value[Resource.Radioactives] += amount; Name := Planet - Population Change Is Negative When := amount < 0 -Import := from FrEee.Objects.Space import Galaxy; +Import := from FrEee.Objects.GameState import Galaxy; Import := from FrEee.Objects.Space import Planet; Target Selector := Galaxy.Current.FindSpaceObjects[Planet]().Where(lambda p: p.Colony != None) Parameter := PlanetName = target.Name; @@ -97,18 +97,18 @@ Action := target.Colony.ChangePopulation(amount); Name := Planet - Population Rebel Is Negative When := true -Import := from FrEee.Objects.Space import Galaxy; +Import := from FrEee.Objects.GameState import Galaxy; Import := from FrEee.Objects.Space import Planet; Target Selector := Galaxy.Current.FindSpaceObjects[Planet]().Where(lambda p: p.Colony != None) Parameter := PlanetName = target.Name; Parameter := SystemName = target.StarSystem.Name; Parameter := SectorName = target.Sector.Name; -Action := from FrEee.Objects.Space import Galaxy; +Action := from FrEee.Objects.GameState import Galaxy; Action := target.Colony = source if source != None else Galaxy.Current.CreateNewEmpire(); Name := Planet - Facility Damage Is Negative When := true -Import := from FrEee.Objects.Space import Galaxy; +Import := from FrEee.Objects.GameState import Galaxy; Import := from FrEee.Objects.Space import Planet; Target Selector := Galaxy.Current.FindSpaceObjects[Planet]().Where(lambda p: p.Colony != None) Parameter := PlanetName = target.Name; @@ -118,7 +118,7 @@ Action := target.Colony.Facilities.PickRandom().Dispose() for num in range(0, Name := Planet - Cargo Damage Is Negative When := amount > 0 -Import := from FrEee.Objects.Space import Galaxy; +Import := from FrEee.Objects.GameState import Galaxy; Import := from FrEee.Objects.Space import Planet; Target Selector := Galaxy.Current.FindSpaceObjects[Planet]().Where(lambda p: p.Colony != None) Parameter := PlanetName = target.Name; @@ -137,7 +137,7 @@ Action := target.NormalizeStoredResources(); Name := Planet - Plague Is Negative When := true -Import := from FrEee.Objects.Space import Galaxy; +Import := from FrEee.Objects.GameState import Galaxy; Import := from FrEee.Objects.Space import Planet; Target Selector := Galaxy.Current.FindSpaceObjects[Planet]().Where(lambda p: p.Colony != None) Parameter := PlanetName = target.Name; @@ -147,7 +147,7 @@ Action := # TODO - implement plagues Name := Planet - Destroyed Is Negative When := true -Import := from FrEee.Objects.Space import Galaxy; +Import := from FrEee.Objects.GameState import Galaxy; Import := from FrEee.Objects.Space import Planet; Target Selector := Galaxy.Current.FindSpaceObjects[Planet]() Parameter := PlanetName = target.Name; @@ -157,7 +157,7 @@ Action := target.ConvertToAsteroidField(); Name := Star - Destroyed Is Negative When := true -Import := from FrEee.Objects.Space import Galaxy; +Import := from FrEee.Objects.GameState import Galaxy; Import := from FrEee.Objects.Space import Star; Target Selector := Galaxy.Current.FindSpaceObjects[Star]() Parameter := StarName = target.Name; @@ -167,7 +167,7 @@ Action := target.Detonate(); Name := Warp Point - Closed Is Negative When := true -Import := from FrEee.Objects.Space import Galaxy; +Import := from FrEee.Objects.GameState import Galaxy; Import := from FrEee.Objects.Space import WarpPoint; Target Selector := Galaxy.Current.FindSpaceObjects[WarpPoint]() Parameter := WarpPointName = target.Name; diff --git a/FrEee.Assets/GameSetups/Quickstart.gsu b/FrEee.Assets/GameSetups/Quickstart.gsu index 3f5ea454f..ba6e1f651 100644 --- a/FrEee.Assets/GameSetups/Quickstart.gsu +++ b/FrEee.Assets/GameSetups/Quickstart.gsu @@ -1,5 +1,5 @@ FrEee.Setup.GameSetup, FrEee.Core: -p32: +p31: AllowedTrades: :All; EmpirePlacement: @@ -11,78 +11,79 @@ p32: c1: :p10: AIName: - :""; + :"AI_Default"; Color: - :255,85,170,255; + :255,85,85,0; CultureName: - :"Scientists"; + :"Warriors"; InsigniaName: - :"Eee"; + :"XiChung"; IsPlayerEmpire: :True; LeaderName: - :"Emperor Aeiou"; + :"Queen Zebu"; LeaderPortraitName: - :"Eee"; + :"XiChung"; Name: - :"Eee Consortium"; + :"Xi'Chung Hive"; PrimaryRace: :p7: Aptitudes: FrEee.Utility.SafeDictionary`2[[System.String, System.Private.CoreLib],[System.Int32, System.Private.CoreLib]], FrEee.Core: d15: - :"Physical Strength"; - :50; - :"Intelligence"; - :135; + :"Reproduction"; + :100; + :"Refining Aptitude"; + :100; + :"Mining Aptitude"; + :100; :"Cunning"; - :50; + :100; :"Environmental Resistance"; :100; - :"Reproduction"; - :110; + :"Defensiveness"; + :100; + :"Construction Aptitude"; + :100; + :"Maintenance Aptitude"; + :100; :"Happiness"; - :120; - :"Aggressiveness"; :100; - :"Defensiveness"; - :103; - :"Political Savvy"; - :50; - :"Mining Aptitude"; - :110; :"Farming Aptitude"; - :80; - :"Refining Aptitude"; - :110; - :"Construction Aptitude"; + :100; + :"Political Savvy"; :100; :"Repair Aptitude"; :100; - :"Maintenance Aptitude"; + :"Intelligence"; + :100; + :"Aggressiveness"; + :100; + :"Physical Strength"; :100; ; happinessModel: :p1: ID: - :"Peaceful"; + :"HappinessModel Peaceful"; ; Name: - :"Eee"; + :"Xi'Chung"; NativeAtmosphere: - :"Oxygen"; + :"Hydrogen"; NativeSurface: - :"Gas Giant"; + :"Ice"; PopulationIconName: - :"Eee"; + :"XiChung"; TraitNames: System.Collections.Generic.List`1[[System.String, System.Private.CoreLib]], System.Private.CoreLib: - c1: - :"Temporal Knowledge"; + c2: + :"Propulsion Experts"; + :"Advanced Storage Techniques"; ; ; ShipsetName: - :"Eee"; + :"XiChung"; ; ; ForbiddenTechnologyNames: @@ -104,25 +105,25 @@ p32: ConstructionAbilityID: :"0"; MaxCargo: - :16000; + :8000; MaxCargoDomed: - :1000; + :800; MaxFacilities: - :25; + :20; MaxFacilitiesDomed: - :5; + :4; MaxPopulation: - :8000000000; + :4000000000; MaxPopulationDomed: - :500000000; + :400000000; ModID: - :"StellarObjectSize Huge"; + :"StellarObjectSize Large"; Name: - :"Huge"; + :"Large"; StellarObjectType: :"Planet"; StellarSize: - :Huge; + :Large; ; HomeworldsPerEmpire: :1; @@ -144,8 +145,6 @@ p32: :150; MinSpawnedAsteroidValue: :50; - RandomAIs: - :5; RemoteMiningModel: :p3: ValueBonus: @@ -158,7 +157,7 @@ p32: ResourceStorage: :50000; Seed: - :796844; + :3238972; StandardMiningModel: :p2: ValueBonus: @@ -175,7 +174,7 @@ p32: StartingResources: :20000; VictoryConditions: - System.Collections.Generic.List`1[[FrEee.Interfaces.IVictoryCondition, FrEee.Core]], System.Private.CoreLib: + System.Collections.Generic.List`1[[FrEee.Objects.VictoryConditions.IVictoryCondition, FrEee.Core]], System.Private.CoreLib: c1: FrEee.Objects.VictoryConditions.TotalEliminationVictoryCondition, FrEee.Core: p0: diff --git a/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfColonization.csx b/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfColonization.csx index 1d3bd823c..197f1cfc5 100644 --- a/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfColonization.csx +++ b/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfColonization.csx @@ -7,7 +7,7 @@ using System; using System.Linq; using System.Collections.Generic; using FrEee.Objects.Vehicles; -using FrEee.Objects.Orders; +using FrEee.Objects.Civilization.Orders; using FrEee.Extensions; /// diff --git a/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfConstruction.csx b/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfConstruction.csx index 6e1d13197..d5e6e8d03 100644 --- a/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfConstruction.csx +++ b/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfConstruction.csx @@ -5,9 +5,9 @@ using FrEee.Objects.Space; using FrEee.Utility; using FrEee.Serialization; using System; using System.Linq; -using FrEee.Objects.Orders; +using FrEee.Objects.Civilization.Orders; using FrEee.Objects.Vehicles; -using FrEee.Interfaces; + using FrEee.Extensions; using FrEee.Objects.Commands; using FrEee.Objects.LogMessages; diff --git a/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfInfrastructure.csx b/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfInfrastructure.csx index 85621f01c..2622a2ee8 100644 --- a/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfInfrastructure.csx +++ b/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfInfrastructure.csx @@ -6,9 +6,9 @@ using FrEee.Utility; using FrEee.Serialization; using System; using System.Collections.Generic; using System.Linq; -using FrEee.Objects.Orders; +using FrEee.Objects.Civilization.Orders; using FrEee.Objects.Vehicles; -using FrEee.Interfaces; + using FrEee.Extensions; using FrEee.Objects.Commands; using FrEee.Objects.Technology; diff --git a/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfShipDesign.csx b/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfShipDesign.csx index ad4b82caf..76fa20fe2 100644 --- a/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfShipDesign.csx +++ b/FrEee.Assets/Scripts/AI/Vanguard/MinistryOfShipDesign.csx @@ -2,7 +2,7 @@ using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Modding.Templates; -using FrEee.Interfaces; + using FrEee.Extensions; using FrEee.Objects.Vehicles; using System.Linq; diff --git a/FrEee.Assets/Scripts/AI/Vanguard/Plan.csx b/FrEee.Assets/Scripts/AI/Vanguard/Plan.csx index 69963cbb7..82f79f4f4 100644 --- a/FrEee.Assets/Scripts/AI/Vanguard/Plan.csx +++ b/FrEee.Assets/Scripts/AI/Vanguard/Plan.csx @@ -5,7 +5,7 @@ using FrEee.Utility; using FrEee.Serialization; using System; using System.Collections.Generic; using FrEee.Extensions; -using FrEee.Objects.Orders; +using FrEee.Objects.Civilization.Orders; using FrEee.Objects.Vehicles; using System.Linq; diff --git a/FrEee.Tests/Modding/FormulaTest.cs b/FrEee.Tests/Modding/FormulaTest.cs index fba1fb407..176982e78 100644 --- a/FrEee.Tests/Modding/FormulaTest.cs +++ b/FrEee.Tests/Modding/FormulaTest.cs @@ -1,6 +1,5 @@ using FrEee.Objects.Civilization; using FrEee.Objects.Combat; -using FrEee.Objects.Space; using FrEee.Objects.Technology; using FrEee.Objects.Vehicles; using FrEee.Modding; @@ -8,6 +7,7 @@ using NUnit.Framework; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Tests.Modding; diff --git a/FrEee.Tests/Objects/Abilities/AbilityTest.cs b/FrEee.Tests/Objects/Abilities/AbilityTest.cs index 4815e4972..c49279bfd 100644 --- a/FrEee.Tests/Objects/Abilities/AbilityTest.cs +++ b/FrEee.Tests/Objects/Abilities/AbilityTest.cs @@ -1,4 +1,3 @@ -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Objects.Space; @@ -7,6 +6,8 @@ using FrEee.Extensions; using NUnit.Framework; using System.Drawing; +using FrEee.Objects.Technology; +using FrEee.Objects.GameState; namespace FrEee.Tests.Objects.Abilities; diff --git a/FrEee.Tests/Objects/Combat/DamageTypesTest.cs b/FrEee.Tests/Objects/Combat/DamageTypesTest.cs index a365bf2ec..f40f8ec99 100644 --- a/FrEee.Tests/Objects/Combat/DamageTypesTest.cs +++ b/FrEee.Tests/Objects/Combat/DamageTypesTest.cs @@ -1,12 +1,12 @@ -using FrEee.Interfaces; -using FrEee.Objects.Combat; -using FrEee.Objects.Space; +using FrEee.Objects.Combat; using FrEee.Objects.Vehicles; using FrEee.Modding; using FrEee.Extensions; using NUnit.Framework; using System; using System.Linq; +using FrEee.Serialization; +using FrEee.Objects.GameState; namespace FrEee.Tests.Objects.Combat; diff --git a/FrEee.Tests/Objects/Orders/ConstructionTest.cs b/FrEee.Tests/Objects/Orders/ConstructionTest.cs index 7a94a22ce..8805bd79e 100644 --- a/FrEee.Tests/Objects/Orders/ConstructionTest.cs +++ b/FrEee.Tests/Objects/Orders/ConstructionTest.cs @@ -4,6 +4,7 @@ using FrEee.Modding; using FrEee.Extensions; using NUnit.Framework; +using FrEee.Objects.GameState; namespace FrEee.Tests.Objects.Orders; @@ -44,7 +45,7 @@ public void Init() colony = new Colony(); colony.Facilities.Add(new Facility(sy)); colony.Population.Add(race, (long)1e9); // 1 billion population; - colony.ConstructionQueue = new ConstructionQueue(planet); + colony.ConstructionQueue = new(planet); planet.Colony = colony; } diff --git a/FrEee.Tests/Objects/Space/MemoryTest.cs b/FrEee.Tests/Objects/Space/MemoryTest.cs index ae0b01475..6aad5c240 100644 --- a/FrEee.Tests/Objects/Space/MemoryTest.cs +++ b/FrEee.Tests/Objects/Space/MemoryTest.cs @@ -1,11 +1,11 @@ -using FrEee.Enumerations; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Objects.Vehicles; using FrEee.Modding; using FrEee.Extensions; using NUnit.Framework; using System.Drawing; +using FrEee.Objects.GameState; namespace FrEee.Tests.Objects.Space; diff --git a/FrEee.Tests/Objects/Technology/TechnologyTest.cs b/FrEee.Tests/Objects/Technology/TechnologyTest.cs index 7ef8739bf..39f6c654d 100644 --- a/FrEee.Tests/Objects/Technology/TechnologyTest.cs +++ b/FrEee.Tests/Objects/Technology/TechnologyTest.cs @@ -1,11 +1,11 @@ -using FrEee.Enumerations; using FrEee.Objects.Civilization; using FrEee.Objects.Commands; -using FrEee.Objects.Space; using FrEee.Processes; using FrEee.Modding; using FrEee.Extensions; using NUnit.Framework; +using FrEee.Objects.GameState; +using FrEee.Objects.Technology; namespace FrEee.Tests.Objects.Technology; diff --git a/FrEee.Tests/Objects/Technology/WeaponInfoTest.cs b/FrEee.Tests/Objects/Technology/WeaponInfoTest.cs index 5139e1c4d..374813a3f 100644 --- a/FrEee.Tests/Objects/Technology/WeaponInfoTest.cs +++ b/FrEee.Tests/Objects/Technology/WeaponInfoTest.cs @@ -1,8 +1,8 @@ using FrEee.Objects.Combat; -using FrEee.Objects.Space; using FrEee.Modding; using NUnit.Framework; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Tests.Objects.Technology; diff --git a/FrEee.Tests/Objects/Vehicles/CloakingTest.cs b/FrEee.Tests/Objects/Vehicles/CloakingTest.cs index f0fa1d8b3..f32fea220 100644 --- a/FrEee.Tests/Objects/Vehicles/CloakingTest.cs +++ b/FrEee.Tests/Objects/Vehicles/CloakingTest.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Abilities; +using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Objects.Vehicles; @@ -7,6 +6,8 @@ using FrEee.Extensions; using NUnit.Framework; using System.Drawing; +using FrEee.Objects.Technology; +using FrEee.Objects.GameState; namespace FrEee.Tests.Objects.Vehicles; diff --git a/FrEee.Tests/Objects/Vehicles/DamageTest.cs b/FrEee.Tests/Objects/Vehicles/DamageTest.cs index 93c275ce8..67f956448 100644 --- a/FrEee.Tests/Objects/Vehicles/DamageTest.cs +++ b/FrEee.Tests/Objects/Vehicles/DamageTest.cs @@ -9,6 +9,7 @@ using NUnit.Framework; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Tests.Objects.Vehicles; diff --git a/FrEee.Tests/Objects/Vehicles/ResupplyTest.cs b/FrEee.Tests/Objects/Vehicles/ResupplyTest.cs index cb5b76622..5370a523d 100644 --- a/FrEee.Tests/Objects/Vehicles/ResupplyTest.cs +++ b/FrEee.Tests/Objects/Vehicles/ResupplyTest.cs @@ -7,6 +7,7 @@ using FrEee.Extensions; using NUnit.Framework; using System.Drawing; +using FrEee.Objects.GameState; namespace FrEee.Tests.Objects.Vehicles; diff --git a/FrEee.Tests/Setup/GameSetupTest.cs b/FrEee.Tests/Setup/GameSetupTest.cs index 16b798efc..a8afe13f7 100644 --- a/FrEee.Tests/Setup/GameSetupTest.cs +++ b/FrEee.Tests/Setup/GameSetupTest.cs @@ -1,7 +1,7 @@ -using FrEee.Objects.Space; -using FrEee.Setup; +using FrEee.Setup; using FrEee.Modding; using NUnit.Framework; +using FrEee.Objects.GameState; namespace FrEee.Tests.Setup; diff --git a/FrEee.Tests/TestUtilities.cs b/FrEee.Tests/TestUtilities.cs index b3f578e83..065348ce5 100644 --- a/FrEee.Tests/TestUtilities.cs +++ b/FrEee.Tests/TestUtilities.cs @@ -1,9 +1,8 @@ -using FrEee.Interfaces; using FrEee.Objects.Civilization; -using FrEee.Objects.Space; using FrEee.Objects.Technology; using FrEee.Objects.Vehicles; using FrEee.Modding; +using FrEee.Objects.GameState; namespace FrEee.Tests; diff --git a/FrEee.Tests/Utility/SerializerTest.cs b/FrEee.Tests/Utility/SerializerTest.cs index c7aab91d0..220e4df06 100644 --- a/FrEee.Tests/Utility/SerializerTest.cs +++ b/FrEee.Tests/Utility/SerializerTest.cs @@ -1,6 +1,5 @@ using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; -using FrEee.Objects.Orders; using FrEee.Objects.Technology; using FrEee.Objects.Vehicles; using FrEee.Modding; @@ -10,6 +9,7 @@ using NUnit.Framework; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.Civilization.Orders; namespace FrEee.Tests.Utility; diff --git a/FrEee.WinForms/Controls/BattleView.cs b/FrEee.WinForms/Controls/BattleView.cs index 082977281..47812f3cd 100644 --- a/FrEee.WinForms/Controls/BattleView.cs +++ b/FrEee.WinForms/Controls/BattleView.cs @@ -1,4 +1,3 @@ -using FrEee.Interfaces; using FrEee.Objects.Combat; using FrEee.Objects.Combat.Grid; using FrEee.Objects.Space; diff --git a/FrEee.WinForms/Controls/Cargolist.cs b/FrEee.WinForms/Controls/Cargolist.cs index f92d15d6b..4ad38f95c 100644 --- a/FrEee.WinForms/Controls/Cargolist.cs +++ b/FrEee.WinForms/Controls/Cargolist.cs @@ -1,5 +1,3 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Modding; using FrEee.Utility; @@ -9,6 +7,8 @@ using System.Collections.Generic; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.Vehicles; namespace FrEee.WinForms.Controls; diff --git a/FrEee.WinForms/Controls/DesignReport.cs b/FrEee.WinForms/Controls/DesignReport.cs index dd525b36a..454571a9a 100644 --- a/FrEee.WinForms/Controls/DesignReport.cs +++ b/FrEee.WinForms/Controls/DesignReport.cs @@ -1,4 +1,3 @@ -using FrEee.Interfaces; using FrEee.Objects.Technology; using FrEee.Utility; using FrEee.Extensions; @@ -6,6 +5,7 @@ using FrEee.WinForms.Utility.Extensions; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Vehicles; namespace FrEee.WinForms.Controls; diff --git a/FrEee.WinForms/Controls/FleetReport.cs b/FrEee.WinForms/Controls/FleetReport.cs index 79ff5edec..041b25df0 100644 --- a/FrEee.WinForms/Controls/FleetReport.cs +++ b/FrEee.WinForms/Controls/FleetReport.cs @@ -1,4 +1,3 @@ -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Objects.Commands; using FrEee.Objects.Space; @@ -11,6 +10,7 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Civilization.Orders; namespace FrEee.WinForms.Controls; diff --git a/FrEee.WinForms/Controls/GalaxyView.cs b/FrEee.WinForms/Controls/GalaxyView.cs index bafd3e9a2..763f63a91 100644 --- a/FrEee.WinForms/Controls/GalaxyView.cs +++ b/FrEee.WinForms/Controls/GalaxyView.cs @@ -6,6 +6,7 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Controls; diff --git a/FrEee.WinForms/Controls/PlanetReport.cs b/FrEee.WinForms/Controls/PlanetReport.cs index d2fae4be5..374b14336 100644 --- a/FrEee.WinForms/Controls/PlanetReport.cs +++ b/FrEee.WinForms/Controls/PlanetReport.cs @@ -1,4 +1,3 @@ -using FrEee.Enumerations; using FrEee.Objects.Civilization; using FrEee.Objects.Commands; using FrEee.Objects.Space; @@ -11,6 +10,7 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Controls; diff --git a/FrEee.WinForms/Controls/SearchBox.cs b/FrEee.WinForms/Controls/SearchBox.cs index cf1501918..13a422932 100644 --- a/FrEee.WinForms/Controls/SearchBox.cs +++ b/FrEee.WinForms/Controls/SearchBox.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; +using FrEee.Objects.Space; using FrEee.Extensions; using FrEee.WinForms.Forms; using System; diff --git a/FrEee.WinForms/Controls/SpaceVehicleReport.cs b/FrEee.WinForms/Controls/SpaceVehicleReport.cs index 7f5b15e86..ed7107a10 100644 --- a/FrEee.WinForms/Controls/SpaceVehicleReport.cs +++ b/FrEee.WinForms/Controls/SpaceVehicleReport.cs @@ -1,4 +1,3 @@ -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Objects.Commands; using FrEee.Objects.Technology; @@ -11,6 +10,9 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.Civilization.CargoStorage; namespace FrEee.WinForms.Controls; diff --git a/FrEee.WinForms/Controls/StarSystemReport.cs b/FrEee.WinForms/Controls/StarSystemReport.cs index 8ddeacaeb..ce6d48f3b 100644 --- a/FrEee.WinForms/Controls/StarSystemReport.cs +++ b/FrEee.WinForms/Controls/StarSystemReport.cs @@ -1,4 +1,3 @@ -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Objects.Vehicles; @@ -6,6 +5,7 @@ using FrEee.WinForms.Interfaces; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Civilization.CargoStorage; namespace FrEee.WinForms.Controls; diff --git a/FrEee.WinForms/Controls/StarSystemView.cs b/FrEee.WinForms/Controls/StarSystemView.cs index e9cfe1bd8..a35ab311f 100644 --- a/FrEee.WinForms/Controls/StarSystemView.cs +++ b/FrEee.WinForms/Controls/StarSystemView.cs @@ -1,4 +1,3 @@ -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Objects.Vehicles; @@ -7,6 +6,7 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Controls; diff --git a/FrEee.WinForms/Forms/ActivateAbilityForm.cs b/FrEee.WinForms/Forms/ActivateAbilityForm.cs index 24a77a35c..a99420cde 100644 --- a/FrEee.WinForms/Forms/ActivateAbilityForm.cs +++ b/FrEee.WinForms/Forms/ActivateAbilityForm.cs @@ -1,8 +1,6 @@ -using FrEee.Interfaces; -using FrEee.Objects.Abilities; +using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Objects.Commands; -using FrEee.Objects.Orders; using FrEee.Objects.Space; using FrEee.Extensions; using FrEee.WinForms.Utility.Extensions; @@ -11,6 +9,10 @@ using System.Data; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Technology; +using FrEee.Serialization; +using FrEee.Objects.GameState; +using FrEee.Objects.Civilization.Orders; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/BattleReplayForm.cs b/FrEee.WinForms/Forms/BattleReplayForm.cs index c60b5116e..1e31d3b1a 100644 --- a/FrEee.WinForms/Forms/BattleReplayForm.cs +++ b/FrEee.WinForms/Forms/BattleReplayForm.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Combat; using FrEee.Objects.Combat.Grid; using FrEee.Objects.Space; diff --git a/FrEee.WinForms/Forms/BattleResultsForm.cs b/FrEee.WinForms/Forms/BattleResultsForm.cs index 21d3e33f3..cc7c23451 100644 --- a/FrEee.WinForms/Forms/BattleResultsForm.cs +++ b/FrEee.WinForms/Forms/BattleResultsForm.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Combat; using FrEee.Objects.Space; using FrEee.Utility; @@ -12,6 +11,7 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Vehicles; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/CargoTransferForm.cs b/FrEee.WinForms/Forms/CargoTransferForm.cs index 6a44a4a7c..71558db95 100644 --- a/FrEee.WinForms/Forms/CargoTransferForm.cs +++ b/FrEee.WinForms/Forms/CargoTransferForm.cs @@ -1,6 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.Orders; +using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Extensions; using FrEee.WinForms.Controls; @@ -10,6 +8,8 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.Civilization.Orders; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/CombatSimulatorForm.cs b/FrEee.WinForms/Forms/CombatSimulatorForm.cs index 0224ba501..433b47f0e 100644 --- a/FrEee.WinForms/Forms/CombatSimulatorForm.cs +++ b/FrEee.WinForms/Forms/CombatSimulatorForm.cs @@ -1,6 +1,4 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects; +using FrEee.Objects; using FrEee.Objects.Civilization; using FrEee.Objects.Combat; using FrEee.Objects.Combat.Grid; @@ -15,6 +13,7 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/CommandsForm.cs b/FrEee.WinForms/Forms/CommandsForm.cs index 0a11f68c4..9007ccd47 100644 --- a/FrEee.WinForms/Forms/CommandsForm.cs +++ b/FrEee.WinForms/Forms/CommandsForm.cs @@ -1,5 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; +using FrEee.Objects.GameState; using FrEee.WinForms.Utility.Extensions; using System; using System.Drawing; diff --git a/FrEee.WinForms/Forms/ConstructionQueueForm.cs b/FrEee.WinForms/Forms/ConstructionQueueForm.cs index 617feb3e9..bcea94056 100644 --- a/FrEee.WinForms/Forms/ConstructionQueueForm.cs +++ b/FrEee.WinForms/Forms/ConstructionQueueForm.cs @@ -1,7 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Commands; -using FrEee.Objects.Orders; using FrEee.Objects.Space; using FrEee.Objects.Technology; using FrEee.Modding; @@ -15,6 +13,11 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.Vehicles; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.GameState; +using FrEee.Objects.Civilization.CargoStorage; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/ConstructionQueueListForm.Designer.cs b/FrEee.WinForms/Forms/ConstructionQueueListForm.Designer.cs index 8e5eae66d..4c59c64d1 100644 --- a/FrEee.WinForms/Forms/ConstructionQueueListForm.Designer.cs +++ b/FrEee.WinForms/Forms/ConstructionQueueListForm.Designer.cs @@ -1,4 +1,6 @@ -namespace FrEee.WinForms.Forms; +using FrEee.Objects.Civilization.Construction; + +namespace FrEee.WinForms.Forms; partial class ConstructionQueueListForm { @@ -226,7 +228,7 @@ private void InitializeComponent() // constructionQueueBindingSource // this.constructionQueueBindingSource.AllowNew = false; - this.constructionQueueBindingSource.DataSource = typeof(FrEee.Objects.Civilization.ConstructionQueue); + this.constructionQueueBindingSource.DataSource = typeof(ConstructionQueue); // // dataGridViewTextBoxColumn1 // diff --git a/FrEee.WinForms/Forms/ConstructionQueueListForm.cs b/FrEee.WinForms/Forms/ConstructionQueueListForm.cs index e31937d34..26e75f255 100644 --- a/FrEee.WinForms/Forms/ConstructionQueueListForm.cs +++ b/FrEee.WinForms/Forms/ConstructionQueueListForm.cs @@ -8,6 +8,7 @@ using System.Linq; using System.Reflection; using System.Windows.Forms; +using FrEee.Objects.Civilization.Construction; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/DebugForm.cs b/FrEee.WinForms/Forms/DebugForm.cs index 37c419288..4941e8136 100644 --- a/FrEee.WinForms/Forms/DebugForm.cs +++ b/FrEee.WinForms/Forms/DebugForm.cs @@ -1,8 +1,8 @@ using FrEee.Objects.Civilization; -using FrEee.Objects.Space; using FrEee.Modding; using System; using System.Drawing; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Forms; @@ -18,7 +18,7 @@ public DebugForm() rtbOutput.AppendText("Imported Mod.\n"); rtbOutput.AppendText("Mod.Current is currently: " + OrNil(Mod.Current) + "\n"); - PythonScriptEngine.EvaluateExpression("from FrEee.Objects.Space import Galaxy"); + PythonScriptEngine.EvaluateExpression("from FrEee.Objects.GameState import Galaxy"); rtbOutput.AppendText("Imported Galaxy.\n"); rtbOutput.AppendText("Galaxy.Current is currently: " + OrNil(Galaxy.Current) + "\n"); diff --git a/FrEee.WinForms/Forms/DesignListForm.cs b/FrEee.WinForms/Forms/DesignListForm.cs index 1bc076080..e791b6dc1 100644 --- a/FrEee.WinForms/Forms/DesignListForm.cs +++ b/FrEee.WinForms/Forms/DesignListForm.cs @@ -1,8 +1,5 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Objects.Commands; -using FrEee.Objects.Space; using FrEee.Objects.Technology; using FrEee.Utility; using FrEee.Extensions; @@ -13,6 +10,9 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.Vehicles; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/DiplomacyForm.cs b/FrEee.WinForms/Forms/DiplomacyForm.cs index c2d67c6bd..0739582b9 100644 --- a/FrEee.WinForms/Forms/DiplomacyForm.cs +++ b/FrEee.WinForms/Forms/DiplomacyForm.cs @@ -1,6 +1,4 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Civilization.Diplomacy; using FrEee.Objects.Civilization.Diplomacy.Clauses; using FrEee.Objects.Commands; @@ -17,6 +15,9 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Civilization.Diplomacy.Messages; +using FrEee.Objects.Civilization.Diplomacy.Actions; +using FrEee.Objects.Abilities; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/EditorForm.cs b/FrEee.WinForms/Forms/EditorForm.cs index 353626d45..f69bdbc63 100644 --- a/FrEee.WinForms/Forms/EditorForm.cs +++ b/FrEee.WinForms/Forms/EditorForm.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Serialization; +using FrEee.Serialization; using FrEee.Extensions; using FrEee.WinForms.Interfaces; using FrEee.WinForms.Utility.Extensions; @@ -8,6 +7,7 @@ using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/EmpireListForm.cs b/FrEee.WinForms/Forms/EmpireListForm.cs index fcfa93c83..9e1a9d4de 100644 --- a/FrEee.WinForms/Forms/EmpireListForm.cs +++ b/FrEee.WinForms/Forms/EmpireListForm.cs @@ -1,7 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Commands; -using FrEee.Objects.Space; using FrEee.Utility; using FrEee.Extensions; using FrEee.WinForms.Utility.Extensions; @@ -11,6 +9,8 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Civilization.Diplomacy.Messages; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/FleetTransferForm.cs b/FrEee.WinForms/Forms/FleetTransferForm.cs index fd1ce7112..2bd75a86b 100644 --- a/FrEee.WinForms/Forms/FleetTransferForm.cs +++ b/FrEee.WinForms/Forms/FleetTransferForm.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; + using FrEee.Objects.Civilization; using FrEee.Objects.Commands; using FrEee.Objects.Space; diff --git a/FrEee.WinForms/Forms/GameSetupForm.cs b/FrEee.WinForms/Forms/GameSetupForm.cs index c78313696..a0fd5876e 100644 --- a/FrEee.WinForms/Forms/GameSetupForm.cs +++ b/FrEee.WinForms/Forms/GameSetupForm.cs @@ -1,4 +1,3 @@ -using FrEee.Enumerations; using FrEee.Objects.Space; using FrEee.Objects.Technology; using FrEee.Objects.Vehicles; @@ -19,6 +18,8 @@ using System.Reflection; using System.Threading; using System.Windows.Forms; +using FrEee.Objects.GameState; +using FrEee.Objects.Civilization.Diplomacy; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/GenericPickerForm.cs b/FrEee.WinForms/Forms/GenericPickerForm.cs index a4398b099..db61748e5 100644 --- a/FrEee.WinForms/Forms/GenericPickerForm.cs +++ b/FrEee.WinForms/Forms/GenericPickerForm.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; +using FrEee.Objects.GameState; using FrEee.WinForms.Utility.Extensions; using System; using System.Collections.Generic; diff --git a/FrEee.WinForms/Forms/HostConsoleForm.cs b/FrEee.WinForms/Forms/HostConsoleForm.cs index 967e96aea..4a52afad4 100644 --- a/FrEee.WinForms/Forms/HostConsoleForm.cs +++ b/FrEee.WinForms/Forms/HostConsoleForm.cs @@ -1,5 +1,4 @@ using FrEee.Objects.Civilization; -using FrEee.Objects.Space; using FrEee.Objects.Vehicles; using FrEee.Processes; using FrEee.Utility; @@ -12,6 +11,7 @@ using System.Linq; using System.Threading; using System.Windows.Forms; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/HullPickerForm.Designer.cs b/FrEee.WinForms/Forms/HullPickerForm.Designer.cs index db6291b4a..904ed3ded 100644 --- a/FrEee.WinForms/Forms/HullPickerForm.Designer.cs +++ b/FrEee.WinForms/Forms/HullPickerForm.Designer.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; + namespace FrEee.WinForms.Forms; partial class HullPickerForm diff --git a/FrEee.WinForms/Forms/HullPickerForm.cs b/FrEee.WinForms/Forms/HullPickerForm.cs index 0942ef1c6..7639f8a34 100644 --- a/FrEee.WinForms/Forms/HullPickerForm.cs +++ b/FrEee.WinForms/Forms/HullPickerForm.cs @@ -1,6 +1,4 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Utility; using FrEee.Extensions; using FrEee.WinForms.Utility.Extensions; @@ -9,6 +7,8 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Technology; +using FrEee.Objects.Vehicles; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/LogForm.cs b/FrEee.WinForms/Forms/LogForm.cs index 3bad33a0c..51a9cf823 100644 --- a/FrEee.WinForms/Forms/LogForm.cs +++ b/FrEee.WinForms/Forms/LogForm.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Combat; +using FrEee.Objects.Combat; using FrEee.Objects.LogMessages; using FrEee.Objects.Space; using FrEee.Objects.Technology; @@ -12,6 +11,9 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Vehicles; +using FrEee.Objects.Civilization.Diplomacy.Messages; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/MainGameForm.cs b/FrEee.WinForms/Forms/MainGameForm.cs index 3187a6f5d..9030f4c23 100644 --- a/FrEee.WinForms/Forms/MainGameForm.cs +++ b/FrEee.WinForms/Forms/MainGameForm.cs @@ -1,9 +1,5 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; -using FrEee.Objects.Civilization.Diplomacy; using FrEee.Objects.Commands; -using FrEee.Objects.Orders; using FrEee.Objects.Space; using FrEee.Objects.Technology; using FrEee.Objects.Vehicles; @@ -24,6 +20,12 @@ using System.Reflection; using System.Threading; using System.Windows.Forms; +using FrEee.Objects.Combat; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.Civilization.Diplomacy.Messages; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.GameState; +using FrEee.Objects.Civilization.CargoStorage; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/MainMenuForm.cs b/FrEee.WinForms/Forms/MainMenuForm.cs index e3f8ebcd3..56e1f1ef0 100644 --- a/FrEee.WinForms/Forms/MainMenuForm.cs +++ b/FrEee.WinForms/Forms/MainMenuForm.cs @@ -1,4 +1,3 @@ -using FrEee.Objects.Space; using FrEee.Objects.Vehicles; using FrEee.Setup; using FrEee.Modding; @@ -17,6 +16,7 @@ using System.Text.RegularExpressions; using System.Threading; using System.Windows.Forms; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/MountPickerForm.Designer.cs b/FrEee.WinForms/Forms/MountPickerForm.Designer.cs index 53d81a6ff..584416f07 100644 --- a/FrEee.WinForms/Forms/MountPickerForm.Designer.cs +++ b/FrEee.WinForms/Forms/MountPickerForm.Designer.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; + namespace FrEee.WinForms.Forms; partial class MountPickerForm diff --git a/FrEee.WinForms/Forms/MountPickerForm.cs b/FrEee.WinForms/Forms/MountPickerForm.cs index 241badbc3..cc1d69381 100644 --- a/FrEee.WinForms/Forms/MountPickerForm.cs +++ b/FrEee.WinForms/Forms/MountPickerForm.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Technology; +using FrEee.Objects.Technology; using FrEee.Modding; using FrEee.Extensions; using System; diff --git a/FrEee.WinForms/Forms/PlanetListForm.cs b/FrEee.WinForms/Forms/PlanetListForm.cs index cee18b54e..7a2363c42 100644 --- a/FrEee.WinForms/Forms/PlanetListForm.cs +++ b/FrEee.WinForms/Forms/PlanetListForm.cs @@ -1,6 +1,5 @@ -using FrEee.Interfaces; using FrEee.Objects.Civilization; -using FrEee.Objects.Orders; +using FrEee.Objects.Civilization.Orders; using FrEee.Objects.Space; using FrEee.Objects.Vehicles; using FrEee.Utility; @@ -14,6 +13,8 @@ using System.Linq; using System.Reflection; using System.Windows.Forms; +using FrEee.Objects.GameState; +using FrEee.Objects.Civilization.CargoStorage; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/RecycleForm.cs b/FrEee.WinForms/Forms/RecycleForm.cs index f7970e789..3c80f5c59 100644 --- a/FrEee.WinForms/Forms/RecycleForm.cs +++ b/FrEee.WinForms/Forms/RecycleForm.cs @@ -1,8 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Commands; -using FrEee.Objects.Orders; -using FrEee.Objects.Orders.RecycleBehaviors; using FrEee.Objects.Space; using FrEee.Objects.Technology; using FrEee.Objects.Vehicles; @@ -14,6 +11,9 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.Civilization.Orders.RecycleBehaviors; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/ResearchForm.cs b/FrEee.WinForms/Forms/ResearchForm.cs index a8cbee05b..fa46c3eda 100644 --- a/FrEee.WinForms/Forms/ResearchForm.cs +++ b/FrEee.WinForms/Forms/ResearchForm.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Commands; using FrEee.Objects.Technology; using FrEee.Modding; @@ -13,6 +12,7 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/ScoresForm.cs b/FrEee.WinForms/Forms/ScoresForm.cs index 6b35e53d9..da5ad1d14 100644 --- a/FrEee.WinForms/Forms/ScoresForm.cs +++ b/FrEee.WinForms/Forms/ScoresForm.cs @@ -1,10 +1,10 @@ -using FrEee.Objects.Space; -using FrEee.Extensions; +using FrEee.Extensions; using FrEee.WinForms.DataGridView; using System; using System.Collections.Generic; using System.Drawing; using System.Windows.Forms; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/SearchBoxResultsForm.cs b/FrEee.WinForms/Forms/SearchBoxResultsForm.cs index 02d43609b..40d6583aa 100644 --- a/FrEee.WinForms/Forms/SearchBoxResultsForm.cs +++ b/FrEee.WinForms/Forms/SearchBoxResultsForm.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; +using FrEee.Objects.Space; using FrEee.WinForms.Utility.Extensions; using System; using System.Collections.Generic; diff --git a/FrEee.WinForms/Forms/ShipListForm.cs b/FrEee.WinForms/Forms/ShipListForm.cs index b10370262..bee4c8a74 100644 --- a/FrEee.WinForms/Forms/ShipListForm.cs +++ b/FrEee.WinForms/Forms/ShipListForm.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; + using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Objects.Vehicles; @@ -13,6 +13,7 @@ using System.Linq; using System.Reflection; using System.Windows.Forms; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/SpaceObjectPickerForm.cs b/FrEee.WinForms/Forms/SpaceObjectPickerForm.cs index 7e4db8679..494fd57ad 100644 --- a/FrEee.WinForms/Forms/SpaceObjectPickerForm.cs +++ b/FrEee.WinForms/Forms/SpaceObjectPickerForm.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; +using FrEee.Objects.Space; using FrEee.WinForms.Utility.Extensions; using System; using System.Collections.Generic; diff --git a/FrEee.WinForms/Forms/TechTreeForm.cs b/FrEee.WinForms/Forms/TechTreeForm.cs index 7044c32be..82b42c33f 100644 --- a/FrEee.WinForms/Forms/TechTreeForm.cs +++ b/FrEee.WinForms/Forms/TechTreeForm.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Technology; using FrEee.Modding; using FrEee.Modding.Templates; @@ -13,6 +12,7 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/Forms/VehicleDesignForm.cs b/FrEee.WinForms/Forms/VehicleDesignForm.cs index 8fcbdb201..e7ab8fb9d 100644 --- a/FrEee.WinForms/Forms/VehicleDesignForm.cs +++ b/FrEee.WinForms/Forms/VehicleDesignForm.cs @@ -1,6 +1,4 @@ -using FrEee.Interfaces; using FrEee.Objects.Civilization; -using FrEee.Objects.Space; using FrEee.Objects.Technology; using FrEee.Modding; using FrEee.Modding.Templates; @@ -14,6 +12,8 @@ using System.Drawing; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.Vehicles; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Forms; diff --git a/FrEee.WinForms/GamePropertyGrid.cs b/FrEee.WinForms/GamePropertyGrid.cs index d64834704..0351d92df 100644 --- a/FrEee.WinForms/GamePropertyGrid.cs +++ b/FrEee.WinForms/GamePropertyGrid.cs @@ -1,7 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Modding; -using FrEee.Modding.Interfaces; +using FrEee.Modding; using FrEee.Serialization; using FrEee.Extensions; using FrEee.WinForms.Controls; @@ -12,6 +9,7 @@ using System.Data; using System.Linq; using System.Windows.Forms; +using FrEee.Objects.GameState; namespace FrEee.WinForms; diff --git a/FrEee.WinForms/Objects/GalaxyViewModes/ColoniesMode.cs b/FrEee.WinForms/Objects/GalaxyViewModes/ColoniesMode.cs index 9f92522c3..90dfc0187 100644 --- a/FrEee.WinForms/Objects/GalaxyViewModes/ColoniesMode.cs +++ b/FrEee.WinForms/Objects/GalaxyViewModes/ColoniesMode.cs @@ -5,6 +5,7 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Objects.GalaxyViewModes; diff --git a/FrEee.WinForms/Objects/GalaxyViewModes/ConstructionMode.cs b/FrEee.WinForms/Objects/GalaxyViewModes/ConstructionMode.cs index 34a0f0381..e3245d30b 100644 --- a/FrEee.WinForms/Objects/GalaxyViewModes/ConstructionMode.cs +++ b/FrEee.WinForms/Objects/GalaxyViewModes/ConstructionMode.cs @@ -1,10 +1,11 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Utility; using FrEee.Extensions; using System.Drawing; using System.Linq; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Objects.GalaxyViewModes; diff --git a/FrEee.WinForms/Objects/GalaxyViewModes/ForcesMode.cs b/FrEee.WinForms/Objects/GalaxyViewModes/ForcesMode.cs index 224b50169..c7900bb59 100644 --- a/FrEee.WinForms/Objects/GalaxyViewModes/ForcesMode.cs +++ b/FrEee.WinForms/Objects/GalaxyViewModes/ForcesMode.cs @@ -1,4 +1,5 @@ using FrEee.Objects.Civilization; +using FrEee.Objects.GameState; using FrEee.Objects.Space; using FrEee.Objects.Vehicles; using System; diff --git a/FrEee.WinForms/Objects/GalaxyViewModes/IGalaxyViewMode.cs b/FrEee.WinForms/Objects/GalaxyViewModes/IGalaxyViewMode.cs index 774b55120..b6745ab8a 100644 --- a/FrEee.WinForms/Objects/GalaxyViewModes/IGalaxyViewMode.cs +++ b/FrEee.WinForms/Objects/GalaxyViewModes/IGalaxyViewMode.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; +using FrEee.Objects.GameState; using FrEee.Objects.Space; using System.Drawing; diff --git a/FrEee.WinForms/Objects/GalaxyViewModes/PresenceMode.cs b/FrEee.WinForms/Objects/GalaxyViewModes/PresenceMode.cs index 2d27e672b..26b0a2ea5 100644 --- a/FrEee.WinForms/Objects/GalaxyViewModes/PresenceMode.cs +++ b/FrEee.WinForms/Objects/GalaxyViewModes/PresenceMode.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Space; using System; using System.Collections.Generic; diff --git a/FrEee.WinForms/Objects/GalaxyViewModes/RepairMode.cs b/FrEee.WinForms/Objects/GalaxyViewModes/RepairMode.cs index bb3ed0149..4ebfec3c3 100644 --- a/FrEee.WinForms/Objects/GalaxyViewModes/RepairMode.cs +++ b/FrEee.WinForms/Objects/GalaxyViewModes/RepairMode.cs @@ -1,9 +1,9 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Extensions; using System.Drawing; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Objects.GalaxyViewModes; diff --git a/FrEee.WinForms/Objects/GalaxyViewModes/ResearchIntelMode.cs b/FrEee.WinForms/Objects/GalaxyViewModes/ResearchIntelMode.cs index 95b7c4e4d..cd3ee676a 100644 --- a/FrEee.WinForms/Objects/GalaxyViewModes/ResearchIntelMode.cs +++ b/FrEee.WinForms/Objects/GalaxyViewModes/ResearchIntelMode.cs @@ -1,10 +1,10 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Utility; using FrEee.Extensions; using System.Drawing; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Objects.GalaxyViewModes; diff --git a/FrEee.WinForms/Objects/GalaxyViewModes/ResourcesMode.cs b/FrEee.WinForms/Objects/GalaxyViewModes/ResourcesMode.cs index e330a2744..66c801aca 100644 --- a/FrEee.WinForms/Objects/GalaxyViewModes/ResourcesMode.cs +++ b/FrEee.WinForms/Objects/GalaxyViewModes/ResourcesMode.cs @@ -1,10 +1,10 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Utility; using FrEee.Extensions; using System.Drawing; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Objects.GalaxyViewModes; diff --git a/FrEee.WinForms/Objects/GalaxyViewModes/SpaceYardMode.cs b/FrEee.WinForms/Objects/GalaxyViewModes/SpaceYardMode.cs index 338a34886..cad794606 100644 --- a/FrEee.WinForms/Objects/GalaxyViewModes/SpaceYardMode.cs +++ b/FrEee.WinForms/Objects/GalaxyViewModes/SpaceYardMode.cs @@ -1,10 +1,11 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Utility; using FrEee.Extensions; using System.Drawing; using System.Linq; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.GameState; namespace FrEee.WinForms.Objects.GalaxyViewModes; diff --git a/FrEee.WinForms/Program.cs b/FrEee.WinForms/Program.cs index bd725f69f..4554f0d2b 100644 --- a/FrEee.WinForms/Program.cs +++ b/FrEee.WinForms/Program.cs @@ -1,5 +1,4 @@ using FrEee.Objects.Civilization; -using FrEee.Objects.Space; using FrEee.Objects.Vehicles; using FrEee.Processes; using FrEee.Utility; @@ -18,6 +17,7 @@ using System.Reflection; using System.Text.RegularExpressions; using System.Windows.Forms; +using FrEee.Objects.GameState; namespace FrEee.WinForms; diff --git a/FrEee/Enumerations/AbilityTargets.cs b/FrEee/Enumerations/AbilityTargets.cs deleted file mode 100644 index aa2c055c1..000000000 --- a/FrEee/Enumerations/AbilityTargets.cs +++ /dev/null @@ -1,79 +0,0 @@ -using FrEee.Utility; -using System; - -namespace FrEee.Enumerations; - -/// -/// Specifies what object types abilities can stack to. -/// -[Flags] -public enum AbilityTargets -{ - // none - None = 0, - - // basic types - Empire = 0x1, - - Race = 0x2, - Trait = 0x4, - Component = 0x8, // includes MountedComponentTemplate and ComponentTemplate - Facility = 0x10, // includes FacilityTemplate - - // shared types - Sector = 0x20, - - StarSystem = 0x40, - Galaxy = 0x80, - - // stellar objects - [Name("Asteroid")] - [Name("Asteroids")] - [CanonicalName("Asteroid Field")] - AsteroidField = 0x100, - - Planet = 0x200, - Star = 0x400, - Storm = 0x800, - - [CanonicalName("Warp Point")] - WarpPoint = 0x1000, - - // vehicles - Base = 0x2000, - - Drone = 0x4000, - Fighter = 0x8000, - Mine = 0x10000, - Satellite = 0x20000, - Ship = 0x40000, - Troop = 0x80000, - WeaponPlatform = 0x100000, - - // fleets - Fleet = 0x200000, - - // compound types - [CanonicalName("Stellar Object")] - StellarObject = AsteroidField | Planet | Star | Storm | WarpPoint, - - [CanonicalName("Space Vehicle")] - SpaceVehicle = Base | Drone | Fighter | Mine | Satellite | Ship, - - [CanonicalName("Ground Vehicle")] - GroundVehicle = WeaponPlatform | Troop, - - Unit = Drone | Fighter | Mine | Satellite | Troop | WeaponPlatform, - Vehicle = Ship | Base | Unit, - - [CanonicalName("Space Object")] - SpaceObject = StellarObject | SpaceVehicle, - - Part = Component | Facility, - - // invalid - Invalid = 0x2000000, - - // everything! - All = int.MaxValue & ~Invalid, -} diff --git a/FrEee/Enumerations/AbilityValueRule.cs b/FrEee/Enumerations/AbilityValueRule.cs deleted file mode 100644 index c0c56e6d6..000000000 --- a/FrEee/Enumerations/AbilityValueRule.cs +++ /dev/null @@ -1,46 +0,0 @@ -using FrEee.Utility; -namespace FrEee.Enumerations; - -/// -/// Rules for grouping and stacking ability values within a group of similar abilities. -/// -public enum AbilityValueRule -{ - /// - /// Do not group or stack abilities by this value. - /// Note that this does not necessarily mean that only one instance of the ability will apply! - /// To guarantee this, use TakeHighest, TakeAverage, or TakeLowest. - /// - None, - - /// - /// Group the abilities by this value. - /// - Group, - - /// - /// Add the values within the group. Only works properly for numeric values. - /// - Add, - - /// - /// Take the highest value within the group. Only works properly for numeric values. - /// - [CanonicalName("Take Highest")] - [Name("Highest")] - TakeHighest, - - /// - /// Take the average of the group values. Only works properly for numeric values. - /// - [CanonicalName("Take Average")] - [Name("Average")] - TakeAverage, - - /// - /// Take the lowest value within the group. Only works properly for numeric values. - /// - [CanonicalName("Take Lowest")] - [Name("Lowest")] - TakeLowest -} \ No newline at end of file diff --git a/FrEee/Enumerations/AllianceLevel.cs b/FrEee/Enumerations/AllianceLevel.cs deleted file mode 100644 index 16b004ea2..000000000 --- a/FrEee/Enumerations/AllianceLevel.cs +++ /dev/null @@ -1,39 +0,0 @@ -using FrEee.Utility; -namespace FrEee.Enumerations; - -/// -/// A level of alliance. -/// -public enum AllianceLevel -{ - /// - /// No alliance. The empire will be attacked by the other empire. - /// - None = 0, - - /// - /// The receiver will not be attacked by the giver, except in systems where the giver has a colony. - /// - [CanonicalName("Neutral Zone")] - NeutralZone = 1, - - /// - /// The receiver will not be attacked by the giver. - /// - [CanonicalName("Non-Aggression")] - [Name("Non Aggression")] - [Name("Nonaggression")] - NonAggression = 2, - - /// - /// Same as Non-Aggression, but the giver will also declare war on any empire that declares war on the receiver. - /// - [CanonicalName("Defensive Pact")] - DefensivePact = 3, - - /// - /// Same as Defensive Pact, but the giver will also declare war on any empire that the receiver declares war on. - /// - [CanonicalName("Military Alliance")] - MilitaryAlliance = 4, -} \ No newline at end of file diff --git a/FrEee/Enumerations/AllowedTrades.cs b/FrEee/Enumerations/AllowedTrades.cs deleted file mode 100644 index e9d408eca..000000000 --- a/FrEee/Enumerations/AllowedTrades.cs +++ /dev/null @@ -1,21 +0,0 @@ -using FrEee.Utility; -namespace FrEee.Enumerations; - -public enum AllowedTrades -{ - /// - /// No trades or gifts are allowed. - /// - None = 0, - - /// - /// Anything except technology can be traded or gifted. - /// - [CanonicalName("All But Technology")] - AllButTechnology = 1, - - /// - /// Anything, including technology, can be traded or gifted. - /// - All = 2 -} \ No newline at end of file diff --git a/FrEee/Enumerations/Conditions.cs b/FrEee/Enumerations/Conditions.cs deleted file mode 100644 index b231bec0c..000000000 --- a/FrEee/Enumerations/Conditions.cs +++ /dev/null @@ -1,15 +0,0 @@ -namespace FrEee.Enumerations; - -/// -/// Planetary conditions. -/// -public enum Conditions -{ - Deadly = -3, - Harsh = -2, - Unpleasant = -1, - Average = 0, - Mild = 1, - Good = 2, - Optimal = 3 -} \ No newline at end of file diff --git a/FrEee/Enumerations/EmpirePlacement.cs b/FrEee/Enumerations/EmpirePlacement.cs deleted file mode 100644 index 19fa07c5e..000000000 --- a/FrEee/Enumerations/EmpirePlacement.cs +++ /dev/null @@ -1,13 +0,0 @@ -using FrEee.Utility; -namespace FrEee.Enumerations; - -public enum EmpirePlacement -{ - [CanonicalName("Can Start In Same System")] - CanStartInSameSystem, - - [CanonicalName("Different Systems")] - DifferentSystems, - - Equidistant -} \ No newline at end of file diff --git a/FrEee/Enumerations/EventMessageTarget.cs b/FrEee/Enumerations/EventMessageTarget.cs deleted file mode 100644 index 7003beaff..000000000 --- a/FrEee/Enumerations/EventMessageTarget.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace FrEee.Enumerations; - -/// -/// Determines who will receive a message when an event occurs. -/// -public enum EventMessageTarget -{ - /// - /// No one will receive a message. - /// - None = 0, - - /// - /// The owner of the affected object will receive a message. - /// - Owner = 1, - - /// - /// Whoever is in the same sector as the event will receive a message. - /// - Sector = 2, - - /// - /// Whoever is in the same sector as the event will receive a message. - /// - System = 3, - - /// - /// Everyone will receive a message. - /// - All = 4 -} \ No newline at end of file diff --git a/FrEee/Enumerations/EventSeverity.cs b/FrEee/Enumerations/EventSeverity.cs deleted file mode 100644 index b5e7a2ffb..000000000 --- a/FrEee/Enumerations/EventSeverity.cs +++ /dev/null @@ -1,13 +0,0 @@ -namespace FrEee.Enumerations; - -/// -/// The severity of a random event. -/// -public enum EventSeverity -{ - None = 0, - Low = 1, - Medium = 2, - High = 3, - Catastrophic = 4, -} \ No newline at end of file diff --git a/FrEee/Enumerations/Mood.cs b/FrEee/Enumerations/Mood.cs deleted file mode 100644 index ad87cf1d5..000000000 --- a/FrEee/Enumerations/Mood.cs +++ /dev/null @@ -1,16 +0,0 @@ -namespace FrEee.Enumerations; - -/// -/// Population mood. -/// -public enum Mood -{ - Rioting = -3, - Riot = Rioting, - Angry = -2, - Unhappy = -1, - Indifferent = 0, - Happy = 1, - Jubilant = 2, - Emotionless = 3, -} \ No newline at end of file diff --git a/FrEee/Enumerations/Relations.cs b/FrEee/Enumerations/Relations.cs deleted file mode 100644 index ca85a64d8..000000000 --- a/FrEee/Enumerations/Relations.cs +++ /dev/null @@ -1,35 +0,0 @@ -namespace FrEee.Enumerations; - -/// -/// Diplomatic relations from one empire to another. -/// Note that diplomatic relations are not necessarily mutual! -/// Also relations may vary by star system if the empires have a neutral zone treaty. -/// -public enum Relations -{ - /// - /// This empire has not encountered the other empire. - /// - Unknown = 0, - - /// - /// This empire is hostile toward the other empire and will attack on sight. - /// - Hostile = 1, - - /// - /// This empire has no formal relations toward the other empire and will only fight if provoked. - /// TODO - actually implement this, right now neutral empires are treated as hostile - /// - Neutral = 2, - - /// - /// This empire is allied with the other empire and will not attack it. - /// - Allied = 3, - - /// - /// This empire *is* the other empire. - /// - Self = 4, -} \ No newline at end of file diff --git a/FrEee/Enumerations/ScoreDisplay.cs b/FrEee/Enumerations/ScoreDisplay.cs deleted file mode 100644 index 0cfecb35c..000000000 --- a/FrEee/Enumerations/ScoreDisplay.cs +++ /dev/null @@ -1,22 +0,0 @@ -using FrEee.Utility; -using System; - -namespace FrEee.Enumerations; - -[Flags] -public enum ScoreDisplay -{ - [CanonicalName("Own Only - No Rankings")] - OwnOnlyNoRankings = 0, - - [CanonicalName("Own Only - Ranked")] - OwnOnlyRanked = 1, - - [CanonicalName("Allies Only - No Rankings")] - AlliesOnlyNoRankings = 2, - - [CanonicalName("Allies Only - Ranked")] - AlliesOnlyRanked = 3, - - All = 7 -} \ No newline at end of file diff --git a/FrEee/Enumerations/SharingPriority.cs b/FrEee/Enumerations/SharingPriority.cs deleted file mode 100644 index 519736f6a..000000000 --- a/FrEee/Enumerations/SharingPriority.cs +++ /dev/null @@ -1,29 +0,0 @@ -namespace FrEee.Enumerations; - -/// -/// Priority for sharing of abilities. -/// -public enum SharingPriority -{ - /// - /// Empires with no priority won't get the ability shared to them. - /// - None = 0, - - /// - /// Empires with low priority get last pick at the ability. - /// If high and medium priority empires used the ability up, low priority empires won't get any. - /// - Low = 1, - - /// - /// Empires with medium priority get a pick at the ability along with the owner of the ability. - /// If high priority empires used the ability up, medium priority empires won't get any. - /// - Medium = 2, - - /// - /// Empires with high priority get a pick at the ability prior to even the owner of the ability. - /// - High = 3 -} \ No newline at end of file diff --git a/FrEee/Enumerations/StartingTechnologyLevel.cs b/FrEee/Enumerations/StartingTechnologyLevel.cs deleted file mode 100644 index 11ba2a881..000000000 --- a/FrEee/Enumerations/StartingTechnologyLevel.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace FrEee.Enumerations; - -public enum StartingTechnologyLevel -{ - Low, - Medium, - High -} \ No newline at end of file diff --git a/FrEee/Enumerations/TechnologyCost.cs b/FrEee/Enumerations/TechnologyCost.cs deleted file mode 100644 index 37464fbd3..000000000 --- a/FrEee/Enumerations/TechnologyCost.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace FrEee.Enumerations; - -/// -/// Cost to research technology. -/// -public enum TechnologyCost -{ - /// - /// Cost = Level * BaseCost - /// 1x, 2x, 3x, 4x... - /// - Low = 0, - - /// - /// Cost = Level for level 1, Level ^ 2 * BaseCost / 2 for subsequent levels - /// 1x, 2x, 4.5x, 8x... - /// - Medium = 1, - - /// - /// Cost = Level ^ 2 * BaseCost - /// 1x, 4x, 9x, 16x... - /// - High = 2, -} \ No newline at end of file diff --git a/FrEee/Enumerations/VehicleTypes.cs b/FrEee/Enumerations/VehicleTypes.cs deleted file mode 100644 index 00616d15c..000000000 --- a/FrEee/Enumerations/VehicleTypes.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using FrEee.Utility; -namespace FrEee.Enumerations; - -/// -/// Vehicle types. Used for restricting component placement. -/// -[Flags] -public enum VehicleTypes -{ - None = 0x0, - - [Name("Ships")] - Ship = 0x1, - - [Name("Bases")] - Base = 0x2, - - [Name("Fighters")] - [Name("Ftr")] - Fighter = 0x4, - - [Name("Satellites")] - [Name("Sat")] - Satellite = 0x8, - - [Name("Mines")] - Mine = 0x10, - - [Name("Troops")] - [Name("Troop")] - [Name("Trp")] - Troop = 0x20, - - [Name("Drones")] - Drone = 0x40, - - [Name("WeapPlatform")] - [Name("WeapPlatforms")] - [Name("WeapPlat")] - [Name("WeapPlats")] - [Name("WeaponPlatforms")] - [CanonicalName("Weapon Platform")] - [Name("Weapon Platforms")] - WeaponPlatform = 0x80, - - [Name("Any")] - All = Ship | Base | Fighter | Satellite | Mine | Troop | Drone | WeaponPlatform, - - Invalid = 0x1000, -} \ No newline at end of file diff --git a/FrEee/Enumerations/Visibility.cs b/FrEee/Enumerations/Visibility.cs deleted file mode 100644 index 404c341c8..000000000 --- a/FrEee/Enumerations/Visibility.cs +++ /dev/null @@ -1,32 +0,0 @@ -namespace FrEee.Enumerations; - -/// -/// Visibility of space objects. -/// -public enum Visibility -{ - /// - /// Object was never visible. - /// - Unknown = 0, - - /// - /// Object was once visible but is now invisible. - /// - Fogged, - - /// - /// Object is currently visible. - /// - Visible, - - /// - /// Object is currently visible and scanned. - /// - Scanned, - - /// - /// Object is owned by the empire. - /// - Owned -} \ No newline at end of file diff --git a/FrEee/Enumerations/WeaponTargets.cs b/FrEee/Enumerations/WeaponTargets.cs deleted file mode 100644 index ab25770e9..000000000 --- a/FrEee/Enumerations/WeaponTargets.cs +++ /dev/null @@ -1,42 +0,0 @@ -using FrEee.Utility; -using System; - -namespace FrEee.Enumerations; - -/// -/// Used to limit what a weapon can fire at. -/// -[Flags] -public enum WeaponTargets -{ - None = 0x0, - - // NOTE - SE4 uses Ships as the target type for bases as well - [Name("Ships")] - Ship = 0x1, - - //[Name("Bases")] - //Base = 0x2, - - [Name("Fighters")] - [Name("Ftr")] - Fighter = 0x4, - - [Name("Satellites")] - [Name("Sat")] - Satellite = 0x8, - - [Name("Drones")] - Drone = 0x40, - - [Name("Planets")] - Planet = 0x100, - - [Name("Seekers")] - Seeker = 0x200, - - [Name("Any")] - All = Ship | /*Base |*/ Fighter | Satellite | Drone | Planet | Seeker, - - Invalid = 0x400, -} \ No newline at end of file diff --git a/FrEee/Enumerations/WeaponTypes.cs b/FrEee/Enumerations/WeaponTypes.cs deleted file mode 100644 index b9b6a6b1f..000000000 --- a/FrEee/Enumerations/WeaponTypes.cs +++ /dev/null @@ -1,69 +0,0 @@ -using System; -using FrEee.Utility; -namespace FrEee.Enumerations; - -/// -/// Types of weapons. -/// -[Flags] -public enum WeaponTypes -{ - /// - /// Not a weapon. - /// - [CanonicalName("Not a Weapon")] - [Name("Not A Weapon")] - [Name("None")] - NotAWeapon = 0x1, - - /// - /// Direct fire weapon. - /// - [CanonicalName("Direct Fire")] - [Name("Direct-Fire")] - DirectFire = 0x2, - - /// - /// Seeking weapon. - /// - Seeking = 0x4, - - /// - /// Explodes when ramming an enemy. - /// - Warhead = 0x8, - - /// - /// Weapon which fires automatically at incoming targets like a direct fire weapon. - /// - [CanonicalName("Direct Fire Point Defense")] - [Name("Direct-Fire Point-Defense")] - [Name("Point-Defense")] - [Name("Point Defense")] - DirectFirePointDefense = 0x10, - - /// - /// Weapon which fires automatically at incoming targets like a seeking weapon. - /// - [CanonicalName("Seeking Point Defense")] - [Name("Seeking Point-Defense")] - SeekingPointDefense = 0x20, - - /// - /// Explodes when ramming an enemy or when being rammed. - /// - [CanonicalName("Warhead Point Defense")] - [Name("Warhead Point-Defense")] - WarheadPointDefense = 0x40, - - /// - /// All types of weapons. Not nonweapons. - /// - [Name("Any")] - All = DirectFire | Seeking | DirectFirePointDefense | SeekingPointDefense | WarheadPointDefense, - - /// - /// Any component, including nonweapons. - /// - AnyComponent = All | NotAWeapon, -} \ No newline at end of file diff --git a/FrEee/Extensions/AbilityExtensions.cs b/FrEee/Extensions/AbilityExtensions.cs index c80e3166d..b130e4116 100644 --- a/FrEee/Extensions/AbilityExtensions.cs +++ b/FrEee/Extensions/AbilityExtensions.cs @@ -1,5 +1,3 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Objects.Civilization.Diplomacy.Clauses; @@ -10,6 +8,7 @@ using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Extensions; diff --git a/FrEee/Extensions/ChecksExtensions.cs b/FrEee/Extensions/ChecksExtensions.cs index cb7ee7c13..edb3d157e 100644 --- a/FrEee/Extensions/ChecksExtensions.cs +++ b/FrEee/Extensions/ChecksExtensions.cs @@ -1,10 +1,7 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Objects.Commands; using FrEee.Objects.Space; -using FrEee.Modding.Interfaces; using System; using System.Collections; using System.Collections.Generic; @@ -13,6 +10,12 @@ using System.Linq; using System.Reflection; using FrEee.Utility; +using FrEee.Objects.Civilization.Orders; +using FrEee.Serialization; +using FrEee.Objects.Technology; +using FrEee.Objects.GameState; +using FrEee.Objects.Combat; +using FrEee.Modding; namespace FrEee.Extensions; public static class ChecksExtensions @@ -259,11 +262,6 @@ public static void IssueOrder(this IOrderable obj, IOrder order) Empire.Current.IssueOrder(obj, order); } - public static bool IsValid(this IErrorProne obj) - { - return !obj.Errors.Any(); - } - public static bool IsWarhead(this WeaponTypes wt) { return wt == WeaponTypes.Warhead || wt == WeaponTypes.WarheadPointDefense; diff --git a/FrEee/Extensions/CommonExtensions.cs b/FrEee/Extensions/CommonExtensions.cs index 8801d328f..d6c2b51ba 100644 --- a/FrEee/Extensions/CommonExtensions.cs +++ b/FrEee/Extensions/CommonExtensions.cs @@ -9,8 +9,6 @@ using System.Runtime.Serialization; using System.Text; using System.Threading.Tasks; -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Objects.Combat; using FrEee.Objects.Combat.Grid; @@ -20,9 +18,11 @@ using FrEee.Objects.Technology; using FrEee.Objects.Vehicles; using FrEee.Modding; -using FrEee.Modding.Interfaces; using FrEee.Utility; using FrEee.Serialization; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.GameState; namespace FrEee.Extensions; diff --git a/FrEee/Extensions/ComparisonExtensions.cs b/FrEee/Extensions/ComparisonExtensions.cs index c0ceb4867..5626f33d3 100644 --- a/FrEee/Extensions/ComparisonExtensions.cs +++ b/FrEee/Extensions/ComparisonExtensions.cs @@ -1,7 +1,7 @@ -using FrEee.Objects.Space; -using FrEee.Modding.Interfaces; -using System; +using System; using System.Globalization; +using FrEee.Objects.GameState; +using FrEee.Modding; namespace FrEee.Extensions; diff --git a/FrEee/Extensions/ConversionExtensions.cs b/FrEee/Extensions/ConversionExtensions.cs index e8b022aff..dc3fd5368 100644 --- a/FrEee/Extensions/ConversionExtensions.cs +++ b/FrEee/Extensions/ConversionExtensions.cs @@ -1,4 +1,4 @@ -using FrEee.Modding.Interfaces; +using FrEee.Modding; using FrEee.Utility; using System; using System.Drawing; diff --git a/FrEee/Extensions/CopyingExtensions.cs b/FrEee/Extensions/CopyingExtensions.cs index c2808b70a..d7939fc39 100644 --- a/FrEee/Extensions/CopyingExtensions.cs +++ b/FrEee/Extensions/CopyingExtensions.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; +using FrEee.Objects.GameState; using FrEee.Objects.Technology; using FrEee.Serialization; using FrEee.Utility; diff --git a/FrEee/Extensions/GameEnumerableExtensions.cs b/FrEee/Extensions/GameEnumerableExtensions.cs index 1d0ec1199..9b924d8d9 100644 --- a/FrEee/Extensions/GameEnumerableExtensions.cs +++ b/FrEee/Extensions/GameEnumerableExtensions.cs @@ -2,12 +2,13 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Objects.Space; -using FrEee.Modding.Interfaces; using FrEee.Utility; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.GameState; +using FrEee.Modding; namespace FrEee.Extensions; /// diff --git a/FrEee/FrEee.csproj b/FrEee/FrEee.csproj index e5feea6ad..2bd73a89f 100644 --- a/FrEee/FrEee.csproj +++ b/FrEee/FrEee.csproj @@ -7,6 +7,14 @@ Open source Space Empires IV clone + + + + + + + + @@ -18,8 +26,6 @@ - - diff --git a/FrEee/Interfaces/IAbilityObject.cs b/FrEee/Interfaces/IAbilityObject.cs deleted file mode 100644 index 5057500e1..000000000 --- a/FrEee/Interfaces/IAbilityObject.cs +++ /dev/null @@ -1,45 +0,0 @@ -using FrEee.Enumerations; -using FrEee.Objects.Abilities; -using System.Collections.Generic; - -namespace FrEee.Interfaces; - -/// -/// Something which can have intrinsic abilities of its own that can be added and removed. -/// -public interface IAbilityContainer : IAbilityObject -{ - /// - /// Intrinsic abilities of this object which can be added or removed. - /// TODO - rename to IntrinsicAbilities after the current game is over - /// - IList Abilities { get; } -} - -/// -/// Something which can have abilities. -/// -public interface IAbilityObject -{ - /// - /// The type of ability target that this object represents. - /// - AbilityTargets AbilityTarget { get; } - - /// - /// Child objects that can pass up abilities to this object. - /// - IEnumerable Children { get; } - - /// - /// Abilities possessed intrinsically by this object. - /// - IEnumerable IntrinsicAbilities { get; } - - /// - /// Parent objects from which this object can inherit abilities. - /// - IEnumerable Parents { get; } -} - -public interface IReferrableAbilityObject : IReferrable, IAbilityObject { } diff --git a/FrEee/Interfaces/ICargoContainer.cs b/FrEee/Interfaces/ICargoContainer.cs deleted file mode 100644 index e7194b810..000000000 --- a/FrEee/Interfaces/ICargoContainer.cs +++ /dev/null @@ -1,63 +0,0 @@ -using FrEee.Objects.Civilization; -using System.Collections.Generic; - -namespace FrEee.Interfaces; - -/// -/// An object which can contain cargo. -/// -public interface ICargoContainer : IPictorial, INamed, ILocated -{ - /// - /// All population stored by this cargo container, whether in a colony or in cargo. - /// - IDictionary AllPopulation { get; } - - /// - /// All units that are in or part of this cargo container. - /// - IEnumerable AllUnits { get; } - - /// - /// The cargo contained by this object. - /// - Cargo Cargo { get; } - - /// - /// The total amount of cargo storage possessed by this object. - /// - int CargoStorage { get; } - - /// - /// The amount of available population storage. - /// - long PopulationStorageFree { get; } - - /// - /// Adds population. - /// - /// - /// - /// The amount of population that could not be added due to overflow. - long AddPopulation(Race race, long amount); - - /// - /// Adds a unit. - /// - /// - /// true if there was space left to add the unit, otherwise false. - bool AddUnit(IUnit unit); - - /// - /// Removes population. - /// - /// - /// The amount of population that could not be removed due to lack of population. - long RemovePopulation(Race race, long amount); - - /// - /// Removes a unit. - /// - /// - bool RemoveUnit(IUnit unit); -} \ No newline at end of file diff --git a/FrEee/Interfaces/ICombatant.cs b/FrEee/Interfaces/ICombatant.cs deleted file mode 100644 index f515f4995..000000000 --- a/FrEee/Interfaces/ICombatant.cs +++ /dev/null @@ -1,61 +0,0 @@ -using FrEee.Objects.Civilization; -using FrEee.Objects.Technology; -using System; -using System.Collections.Generic; - -namespace FrEee.Interfaces; - -/// -/// An object that can fire weapons and/or targeted by weapons. -/// -public interface ICombatant : IPictorial, ITargetable, IDisposable, IFoggable, INamed -{ - /// - /// Accuracy rating of this combatant. - /// - int Accuracy { get; } - - /// - /// How fast can this combatant move in combat? - /// - double CombatSpeed { get; } - - /// - /// Is this combatant still alive or is it destroyed/glassed? - /// - bool IsAlive { get; } - - /// - /// How many targets can this combatant fire on per round (excluding point defense weapons or warheads)? - /// - int MaxTargets { get; } - - /// - /// Any components of this combatant. - /// - IEnumerable Components { get; } - - /// - /// Any undamaged weapons this combatant is armed with. - /// - IEnumerable Weapons { get; } - - /// - /// Can this object fire on another object? - /// - /// - /// true if the target is an enemy and this combatant has weapons capable of targeting it - bool CanTarget(ITargetable target); - - bool IsHostileTo(Empire emp); - - /// - /// Does this combatant "fill" a tile (prevent other tile-filling combatants from occupying the tile)? - /// - bool FillsCombatTile { get; } - - /// - /// The size of this combatant. Used for determining who goes where in a formation. - /// - int Size { get; } -} \ No newline at end of file diff --git a/FrEee/Interfaces/ICommand.cs b/FrEee/Interfaces/ICommand.cs deleted file mode 100644 index a985ce23c..000000000 --- a/FrEee/Interfaces/ICommand.cs +++ /dev/null @@ -1,41 +0,0 @@ -using FrEee.Objects.Civilization; -using System.Collections.Generic; - -namespace FrEee.Interfaces; - -/// -/// A command to some object. -/// Commands are distinguished from orders by being instantaneous, rather than queued. -/// -public interface ICommand : IPromotable -{ - IReferrable Executor { get; } - - long ExecutorID { get; } - - /// - /// The empire issuing the command. - /// - Empire Issuer { get; } - - /// - /// Any new (from the client) objects referred to by this command. - /// - IEnumerable NewReferrables { get; } - - /// - /// Executes the command. - /// - void Execute(); -} - -/// -/// A command to some object. -/// -public interface ICommand : ICommand where T : IReferrable -{ - /// - /// The object whose queue is being manipulated. - /// - new T Executor { get; set; } -} diff --git a/FrEee/Interfaces/ICommonAbilityObject.cs b/FrEee/Interfaces/ICommonAbilityObject.cs deleted file mode 100644 index 2434966c8..000000000 --- a/FrEee/Interfaces/ICommonAbilityObject.cs +++ /dev/null @@ -1,17 +0,0 @@ -using FrEee.Objects.Civilization; -using System.Collections.Generic; - -namespace FrEee.Interfaces; - -/// -/// An object that can contain different abilities for different empires. -/// -public interface ICommonAbilityObject : IAbilityObject -{ - /// - /// Finds any child ability objects owned by an empire. - /// - /// - /// - IEnumerable GetContainedAbilityObjects(Empire emp); -} \ No newline at end of file diff --git a/FrEee/Interfaces/IConstructable.cs b/FrEee/Interfaces/IConstructable.cs deleted file mode 100644 index 8f1085e2e..000000000 --- a/FrEee/Interfaces/IConstructable.cs +++ /dev/null @@ -1,35 +0,0 @@ -using FrEee.Objects.Civilization; -using FrEee.Utility; -namespace FrEee.Interfaces; - -/// -/// Something which can be constructed at a construction queue. -/// -public interface IConstructable : INamed, IPictorial, ITransferrable -{ - /// - /// The progress toward constructing this item. - /// - ResourceQuantity ConstructionProgress { get; set; } - - /// - /// The resource cost to build this item. - /// - ResourceQuantity Cost { get; } - - /// - /// The empire which owns this item. - /// - new Empire Owner { get; set; } - - /// - /// The construction template used to build this item. - /// - IConstructionTemplate Template { get; } - - /// - /// Places the newly constructed item at a location. - /// - /// The space object which the item should be placed on or near. - void Place(ISpaceObject target); -} \ No newline at end of file diff --git a/FrEee/Interfaces/IConstructionOrder.cs b/FrEee/Interfaces/IConstructionOrder.cs deleted file mode 100644 index 72316aea4..000000000 --- a/FrEee/Interfaces/IConstructionOrder.cs +++ /dev/null @@ -1,25 +0,0 @@ -using FrEee.Utility; -namespace FrEee.Interfaces; - -public interface IConstructionOrder : IOrder, INamed -{ - /// - /// The cost of the construction. - /// - ResourceQuantity Cost { get; } - - /// - /// The item being constructed. - /// - IConstructable Item { get; } - - /// - /// The template. - /// - IConstructionTemplate Template { get; } - - /// - /// Resets this order so it can be repeated. - /// - void Reset(); -} \ No newline at end of file diff --git a/FrEee/Interfaces/IConstructionTemplate.cs b/FrEee/Interfaces/IConstructionTemplate.cs deleted file mode 100644 index 29e2d15ec..000000000 --- a/FrEee/Interfaces/IConstructionTemplate.cs +++ /dev/null @@ -1,32 +0,0 @@ -using FrEee.Objects.Civilization; -using FrEee.Utility; -namespace FrEee.Interfaces; - -/// -/// Template for constructable items. -/// -/// -public interface IConstructionTemplate : IReferrable, IPictorial, IResearchable -{ - /// - /// The cost to build it. - /// - ResourceQuantity Cost { get; } - - /// - /// Does this template require a colony to build it? - /// - bool RequiresColonyQueue { get; } - - /// - /// Does this template require a space yard to build it? - /// - bool RequiresSpaceYardQueue { get; } - - /// - /// Has the empire unlocked this construction template? - /// - /// - /// - bool HasBeenUnlockedBy(Empire emp); -} \ No newline at end of file diff --git a/FrEee/Interfaces/IConstructor.cs b/FrEee/Interfaces/IConstructor.cs deleted file mode 100644 index a9012e80e..000000000 --- a/FrEee/Interfaces/IConstructor.cs +++ /dev/null @@ -1,14 +0,0 @@ -using FrEee.Objects.Civilization; - -namespace FrEee.Interfaces; - -/// -/// Something which has a construction queue. -/// -public interface IConstructor : ISpaceObject, IOwnable -{ - /// - /// This object's construction queue, if any. - /// - ConstructionQueue ConstructionQueue { get; } -} diff --git a/FrEee/Interfaces/IDamageable.cs b/FrEee/Interfaces/IDamageable.cs deleted file mode 100644 index 939324706..000000000 --- a/FrEee/Interfaces/IDamageable.cs +++ /dev/null @@ -1,75 +0,0 @@ -using FrEee.Objects.Combat; -using FrEee.Utility; -namespace FrEee.Interfaces; - -/// -/// Something which can take damage. -/// -public interface IDamageable -{ - int ArmorHitpoints { get; } - - /// - /// The chance for this object to be hit relative to another object that could also be hit by the same shot. - /// - int HitChance { get; } - - /// - /// Current hitpoints. - /// - int Hitpoints { get; set; } - - int HullHitpoints { get; } - - /// - /// Is this object destroyed? - /// - bool IsDestroyed { get; } - - int MaxArmorHitpoints { get; } - - int MaxHitpoints { get; } - - int MaxHullHitpoints { get; } - - int MaxNormalShields { get; } - - int MaxPhasedShields { get; } - - int MaxShieldHitpoints { get; } - - /// - /// Normal shield points. - /// - int NormalShields { get; set; } - - /// - /// Phased shield points. - /// - int PhasedShields { get; set; } - - int ShieldHitpoints { get; } - - /// - /// Replenishes HP. - /// If amount is are not specified, replenishes all HP. - /// Otherwise repair points have a different effect on different objects. - /// E.g. on a ship a repair point repairs 1 component while on a component a repair point replenishes 1 HP. - /// - /// The amount of unused repair points left over, or null for infinite. - int? Repair(int? amount = null); - - /// - /// Replenishes normal and phased shields. - /// - /// How many shields to replenish, or null to replenish all of them. - void ReplenishShields(int? amount = null); - - /// - /// Takes damage. - /// - /// Leftover damage. - int TakeDamage(Hit hit, PRNG dice = null); -} - -public interface IDamageableReferrable : IDamageable, IReferrable { } \ No newline at end of file diff --git a/FrEee/Interfaces/IDesign.cs b/FrEee/Interfaces/IDesign.cs deleted file mode 100644 index be27324c9..000000000 --- a/FrEee/Interfaces/IDesign.cs +++ /dev/null @@ -1,149 +0,0 @@ -using FrEee.Enumerations; -using FrEee.Objects.Civilization; -using FrEee.Objects.Technology; -using FrEee.Modding.Templates; -using FrEee.Utility; -using System.Collections.Generic; - -namespace FrEee.Interfaces; - -/// -/// A vehicle design. -/// -public interface IDesign : INamed, IPictorial, IOwnableAbilityObject, IConstructionTemplate, IPromotable, IFoggable, IUpgradeable, ICleanable -{ - int Accuracy { get; } - - int ArmorHitpoints { get; } - - /// - /// The base name of the design, without the iteration number. - /// - string BaseName { get; set; } - - int CargoCapacity { get; } - - int CargoStorage { get; } - - /// - /// The movement speed of the design in combat. - /// - double CombatSpeed { get; } - - /// - /// The vehicle's components. - /// - IList Components { get; } - - int Evasion { get; } - - /// - /// The vehicle's hull. - /// - IHull Hull { get; set; } - - int HullHitpoints { get; } - - /// - /// Is this a newly created design on the client side that needs to be sent to the server? - /// - bool IsNew { get; set; } - - /// - /// Is this design obsolete? - /// Note that foreign designs will never be obsoleted, since you don't know when their owner obsoleted them. - /// - new bool IsObsolete { get; set; } - - /// - /// Is this design valid in the current mod? Or is it using techs from other mods? - /// - bool IsValidInMod { get; } - - int Iteration { get; set; } - - ResourceQuantity MaintenanceCost { get; } - - /// - /// The name of the design. - /// - new string Name { get; } - - /// - /// The empire which created this design. - /// - new Empire Owner { get; set; } - - /// - /// The ship's role (design type in SE4). - /// - string Role { get; set; } - - int ShieldHitpoints { get; } - - int ShieldRegeneration { get; } - - /// - /// Unused space on the design. - /// - int SpaceFree { get; } - - /// - /// The movement speed of the design, in sectors per turn. - /// - int StrategicSpeed { get; } - - int SupplyStorage { get; } - - /// - /// Supply used for each sector of movement. - /// - int SupplyUsagePerSector { get; } - - /// - /// The turn this design was created (for our designs) or discovered (for alien designs). - /// - int TurnNumber { get; set; } - - int VehiclesBuilt { get; set; } - - /// - /// The vehicle type. - /// - VehicleTypes VehicleType { get; } - - /// - /// The name of the vehicle type. - /// - string VehicleTypeName { get; } - - /// - /// Warnings that need to be resolved before the design can be saved. - /// - IEnumerable Warnings { get; } - - void AddComponent(ComponentTemplate ct, Mount m = null); - - /// - /// Creates an order to build this design. - /// - /// - IConstructionOrder CreateConstructionOrder(ConstructionQueue queue); - - /// - /// Creates a command to create this design on the server. - /// - /// - ICreateDesignCommand CreateCreationCommand(); - - IVehicle Instantiate(); - - IDesign Upgrade(); -} - -public interface IDesign : IDesign, IPictorial, IReferrable, IUpgradeable> where T : IVehicle -{ - new T Instantiate(); - - new IDesign Upgrade(); -} diff --git a/FrEee/Interfaces/IErrorProne.cs b/FrEee/Interfaces/IErrorProne.cs deleted file mode 100644 index 2a1579743..000000000 --- a/FrEee/Interfaces/IErrorProne.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Collections.Generic; - -namespace FrEee.Interfaces; - -public interface IErrorProne -{ - IEnumerable Errors { get; } -} \ No newline at end of file diff --git a/FrEee/Interfaces/IFoggable.cs b/FrEee/Interfaces/IFoggable.cs deleted file mode 100644 index 0d11abc3b..000000000 --- a/FrEee/Interfaces/IFoggable.cs +++ /dev/null @@ -1,39 +0,0 @@ -using System; -using FrEee.Enumerations; -using FrEee.Objects.Civilization; - -namespace FrEee.Interfaces; - -/// -/// Something that can be obscured by fog of war. -/// -public interface IFoggable : IDisposable -{ - /// - /// Is this object just a memory, or a real object? - /// - bool IsMemory { get; set; } - - /// - /// The time at which this object was last seen. - /// E.g. 2.5 would be halfway through the second turn. - /// - double Timestamp { get; set; } - - /// - /// The visibility of this object to an empire. - /// - Visibility CheckVisibility(Empire emp); - - /// - /// Is this object an obsolete memory? - /// Memories become obsolete when the object's last known location is visible, - /// but the object has not been seen for at least 1 full turn. - /// - bool IsObsoleteMemory(Empire emp); - - /// - /// Removes any data from this object that the specified empire cannot see. - /// - void Redact(Empire emp); -} \ No newline at end of file diff --git a/FrEee/Interfaces/IHull.cs b/FrEee/Interfaces/IHull.cs deleted file mode 100644 index 17eac9413..000000000 --- a/FrEee/Interfaces/IHull.cs +++ /dev/null @@ -1,102 +0,0 @@ -using FrEee.Enumerations; -using FrEee.Objects.Technology; -using FrEee.Modding.Interfaces; -using FrEee.Utility; -using System.Collections.Generic; -using System.Drawing; - -namespace FrEee.Interfaces; - -public interface IHull : IModObject, IResearchable, IAbilityContainer, IPictorial, IUpgradeable -{ - /// - /// Can this hull use components with the Ship Auxiliary Control ability? - /// - bool CanUseAuxiliaryControl { get; set; } - - string Code { get; set; } - ResourceQuantity Cost { get; set; } - string Description { get; set; } - - /// - /// Maximum number of engines allowed. - /// - int MaxEngines { get; set; } - - /// - /// Required number of crew quarters components. - /// - int MinCrewQuarters { get; set; } - - /// - /// Required number of life support components. - /// - int MinLifeSupport { get; set; } - - /// - /// Minimum percentage of space required to be used for cargo-storage components. - /// - int MinPercentCargoBays { get; set; } - - /// - /// Minimum percentage of space required to be used for colonizing components. - /// - int MinPercentColonyModules { get; set; } - - /// - /// Minimum percentage of space required to be used for fighter-launching components. - /// - int MinPercentFighterBays { get; set; } - - new string Name { get; set; } - - /// - /// Does this hull need a component with the Ship Bridge ability? - /// - bool NeedsBridge { get; set; } - - IList PictureNames { get; } - - string ShortName { get; set; } - - int Size { get; set; } - - /// - /// Thrust points required to move one sector per turn. - /// SE4 called this "engines per move" but this was a misnomer because engines were allowed to produce more than one thrust point. - /// - int ThrustPerMove { get; set; } - - /// - /// The vehicle type of this hull. - /// - VehicleTypes VehicleType { get; } - - string VehicleTypeName { get; } - - /// - /// Can this hull use a mount? - /// - /// - /// - bool CanUseMount(Mount m); - - Image GetIcon(string shipsetPath); - - Image GetPortrait(string shipsetPath); -} - -/// -/// A vehicle hull. -/// -public interface IHull : IHull, IUpgradeable> where T : IVehicle -{ - new bool IsObsolescent { get; } - - // to deal with ambiguity between implementing IHull and IHull - new bool IsObsolete { get; } - - new IHull LatestVersion { get; } - new IEnumerable> NewerVersions { get; } - new IEnumerable> OlderVersions { get; } -} \ No newline at end of file diff --git a/FrEee/Interfaces/IIncomeProducer.cs b/FrEee/Interfaces/IIncomeProducer.cs deleted file mode 100644 index 9be15c819..000000000 --- a/FrEee/Interfaces/IIncomeProducer.cs +++ /dev/null @@ -1,32 +0,0 @@ -using FrEee.Utility; -namespace FrEee.Interfaces; - -/// -/// Something which can produce income for an empire. -/// -public interface IIncomeProducer : IOwnableAbilityObject, ILocated -{ - /// - /// Ratio of income from this object that occurs even without a spaceport. - /// 1.0 = all of it - /// 0.5 = half of it - /// 0.0 = none of it - /// - double MerchantsRatio { get; } - - /// - /// Modifiers to remote mining due to racial aptitudes and the like. - /// - ResourceQuantity RemoteMiningIncomePercentages { get; } - - /// - /// Value for mined resources. - /// - ResourceQuantity ResourceValue { get; } - - /// - /// Modifiers to standard income due to population, happiness, racial aptitudes, lack of spaceport, etc. - /// 100% = normal income - /// - ResourceQuantity StandardIncomePercentages { get; } -} \ No newline at end of file diff --git a/FrEee/Interfaces/IMessage.cs b/FrEee/Interfaces/IMessage.cs deleted file mode 100644 index ad0895ea8..000000000 --- a/FrEee/Interfaces/IMessage.cs +++ /dev/null @@ -1,14 +0,0 @@ -using FrEee.Objects.Civilization; - -namespace FrEee.Interfaces; - -/// -/// A diplomatic message. -/// -public interface IMessage : IFoggable, IPictorial, IPromotable, IReferrable -{ - IMessage InReplyTo { get; set; } - Empire Recipient { get; set; } - string Text { get; set; } - int TurnNumber { get; set; } -} \ No newline at end of file diff --git a/FrEee/Interfaces/INamed.cs b/FrEee/Interfaces/INamed.cs deleted file mode 100644 index 8a2744c36..000000000 --- a/FrEee/Interfaces/INamed.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace FrEee.Interfaces; - -/// -/// Something that has a name. -/// -public interface INamed -{ - /// - /// The name of the object. - /// - string Name { get; } -} \ No newline at end of file diff --git a/FrEee/Interfaces/IOrder.cs b/FrEee/Interfaces/IOrder.cs deleted file mode 100644 index 270627f24..000000000 --- a/FrEee/Interfaces/IOrder.cs +++ /dev/null @@ -1,36 +0,0 @@ -using FrEee.Objects.LogMessages; -using System.Collections.Generic; - -namespace FrEee.Interfaces; - -/// -/// An order issued by a player to an object to do something. -/// Orders are distinguished from commands by being queued, rather than instantaneous. -/// -public interface IOrder : IReferrable, IPromotable -{ - /// - /// Does this order cost a movement point to execute? - /// - bool ConsumesMovement { get; } - - /// - /// Is this order done executing? - /// - bool IsComplete { get; set; } - -/// - /// Is this order done executing? - /// - bool CheckCompletion(IOrderable executor); - - /// - /// Executes the order. - /// - void Execute(IOrderable executor); - - /// - /// Validation errors. - /// - IEnumerable GetErrors(IOrderable executor); -} \ No newline at end of file diff --git a/FrEee/Interfaces/IOrderCommand.cs b/FrEee/Interfaces/IOrderCommand.cs deleted file mode 100644 index 024275022..000000000 --- a/FrEee/Interfaces/IOrderCommand.cs +++ /dev/null @@ -1,12 +0,0 @@ -namespace FrEee.Interfaces; - -/// -/// A command to manipulate an object's order queue. -/// -public interface IOrderCommand: ICommand -{ - /// - /// The specific order being manipulated. - /// - IOrder Order { get; set; } -} \ No newline at end of file diff --git a/FrEee/Interfaces/IOrderable.cs b/FrEee/Interfaces/IOrderable.cs deleted file mode 100644 index 58ca4924a..000000000 --- a/FrEee/Interfaces/IOrderable.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Generic; - -namespace FrEee.Interfaces; - -/// -/// Something which can accept orders from an empire and queue them for execution over time. -/// -public interface IOrderable : IReferrable -{ - bool AreOrdersOnHold { get; set; } - - bool AreRepeatOrdersEnabled { get; set; } - - /// - /// The queued orders. - /// - IEnumerable Orders { get; } - - void AddOrder(IOrder order); - - /// - /// Executes orders for an appropriate amount of time. - /// Some objects execute orders for an entire turn at once; others only for smaller ticks. - /// - /// true if there was anything to do this turn - bool ExecuteOrders(); - - void RearrangeOrder(IOrder order, int delta); - - void RemoveOrder(IOrder order); -} diff --git a/FrEee/Interfaces/IPathfindingOrder.cs b/FrEee/Interfaces/IPathfindingOrder.cs deleted file mode 100644 index 2cf5e4601..000000000 --- a/FrEee/Interfaces/IPathfindingOrder.cs +++ /dev/null @@ -1,51 +0,0 @@ -using FrEee.Objects.Civilization; - -namespace FrEee.Interfaces; - -/// -/// A movement order whose movement is relative to some space object. -/// -public interface IPathfindingOrder : IMovementOrder, IOwnable -{ - /// - /// Alternate target. This should be the largest ship in a fleet when a fleet is being pursued. - /// - ISpaceObject AlternateTarget { get; } - - /// - /// Should pathfinding avoid enemies? - /// - bool AvoidEnemies { get; set; } - - new bool IsComplete { get; set; } - - /// - /// Either the target itself, or the memory of the target, if it's not visible. - /// - ISpaceObject KnownTarget { get; } - - /// - /// The empire which issued the order. - /// - new Empire Owner { get; set; } - - /// - /// The target space object to pursue, evade, etc. - /// - ISpaceObject Target { get; set; } - - /// - /// A verb used to describe this order. - /// - string Verb { get; } - - /// - /// Call this when calling UpdateMemory on the target. - /// Sets the alternate target to the largest ship in a fleet, if the target is a fleet. - /// If the fleet is destroyed, sets the target to the alternate target. - /// If the target is a ship, etc., and it is destroyed, sets the target to the memory of the target, or deletes the order if there is no memory. - /// If the target is a memory, and the original object is sighted again, sets the target to the original object. - /// Otherwise sets the alternate target to the target. - /// - void UpdateAlternateTarget(); -} \ No newline at end of file diff --git a/FrEee/Interfaces/IPictorial.cs b/FrEee/Interfaces/IPictorial.cs deleted file mode 100644 index 0a747ba1e..000000000 --- a/FrEee/Interfaces/IPictorial.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; -using System.Drawing; - -namespace FrEee.Interfaces; - -/// -/// Something which has a picture. -/// -public interface IPictorial -{ - /// - /// A small picture. - /// - Image Icon { get; } - - /// - /// The icon pre-scaled to 32 pixels by 32 pixels. - /// - Image Icon32 { get; } - - /// - /// Paths with fallbacks to the icon, relative to the Pictures folder. - /// - IEnumerable IconPaths { get; } - - /// - /// A large picture. - /// - Image Portrait { get; } - - /// - /// Paths with fallbacks to the portrait, relative to the Pictures folder. - /// - IEnumerable PortraitPaths { get; } -} \ No newline at end of file diff --git a/FrEee/Interfaces/IPictorialLogMessage.cs b/FrEee/Interfaces/IPictorialLogMessage.cs deleted file mode 100644 index b047ef4c2..000000000 --- a/FrEee/Interfaces/IPictorialLogMessage.cs +++ /dev/null @@ -1,6 +0,0 @@ -namespace FrEee.Interfaces; - -public interface IPictorialLogMessage -{ - T Context { get; } -} \ No newline at end of file diff --git a/FrEee/Interfaces/IPlanetOrder.cs b/FrEee/Interfaces/IPlanetOrder.cs deleted file mode 100644 index 00ee638a0..000000000 --- a/FrEee/Interfaces/IPlanetOrder.cs +++ /dev/null @@ -1,8 +0,0 @@ -namespace FrEee.Interfaces; - -/// -/// An order that can be issued to a planet. -/// -public interface IPlanetOrder : IOrder -{ -} \ No newline at end of file diff --git a/FrEee/Interfaces/IPromotable.cs b/FrEee/Interfaces/IPromotable.cs deleted file mode 100644 index dbd1d6de1..000000000 --- a/FrEee/Interfaces/IPromotable.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Collections.Generic; - -namespace FrEee.Interfaces; - -/// -/// An object which can be "promoted" from the client side to the server side. -/// -public interface IPromotable -{ - /// - /// Replaces client-side object IDs with the newly generated server side IDs. - /// - /// - /// Any promoted objects that are already done replacing IDs. - void ReplaceClientIDs(IDictionary idmap, ISet done = null); -} \ No newline at end of file diff --git a/FrEee/Interfaces/IRecyclable.cs b/FrEee/Interfaces/IRecyclable.cs deleted file mode 100644 index b6b3769b6..000000000 --- a/FrEee/Interfaces/IRecyclable.cs +++ /dev/null @@ -1,25 +0,0 @@ -using FrEee.Utility; -namespace FrEee.Interfaces; - -/// -/// Something which can be recycled (scrapped, etc.) -/// -public interface IRecyclable : IReferrable, IPictorial -{ - /// - /// The object that's the "container" object of this object for recycling purposes. - /// - IMobileSpaceObject RecycleContainer { get; } - - /// - /// Amount of resources gained by scrapping this object. - /// - ResourceQuantity ScrapValue { get; } - - /// - /// Performs the recycling action. - /// - /// The action to perform. - /// Did the action already execute? If so, just do the object type specific logic. - void Recycle(IRecycleBehavior behavior, bool didExecute = false); -} \ No newline at end of file diff --git a/FrEee/Interfaces/IRecycleBehavior.cs b/FrEee/Interfaces/IRecycleBehavior.cs deleted file mode 100644 index 919f42647..000000000 --- a/FrEee/Interfaces/IRecycleBehavior.cs +++ /dev/null @@ -1,16 +0,0 @@ -using FrEee.Objects.LogMessages; -using System.Collections.Generic; - -namespace FrEee.Interfaces; - -/// -/// A type of recycle behavior (scrap, analyze, etc.) -/// -public interface IRecycleBehavior -{ - string Verb { get; } - - void Execute(IRecyclable target, bool didRecycle = false); - - IEnumerable GetErrors(IMobileSpaceObject executor, IRecyclable target); -} \ No newline at end of file diff --git a/FrEee/Interfaces/ISpaceObject.cs b/FrEee/Interfaces/ISpaceObject.cs deleted file mode 100644 index 7ba56fb50..000000000 --- a/FrEee/Interfaces/ISpaceObject.cs +++ /dev/null @@ -1,52 +0,0 @@ -using FrEee.Utility; -namespace FrEee.Interfaces; - -public interface ISpaceObject : IOwnableAbilityObject, IPictorial, IReferrable, IFoggable, INamed, ILocated -{ - /// - /// Can this space object be placed in a fleet? - /// - bool CanBeInFleet { get; } - - /// - /// Is this space object affeced by sight obscuration abilities? - /// This does not affect cloaking abilities, only sector/system obscuration. - /// - /// - /// true if this instance can be obscured; otherwise, false. - /// - bool CanBeObscured { get; } - - /// - /// Can this space object traverse warp points? - /// - bool CanWarp { get; } - - /// - /// Does this space object have infinite supplies? - /// TODO - make supply a resource? - /// - bool HasInfiniteSupplies { get; } - - /// - /// Is this space object idle? - /// Space objects are idle if they have no orders (but they have speed greater than zero, and are not in a fleet) or their construction queue has a fractional ETA less than 1. - /// - bool IsIdle { get; } - - /// - /// The name of this space object. - /// - new string Name { get; set; } - - /// - /// Resources stored on this space object. - /// For a ship, this could include supplies. - /// - ResourceQuantity StoredResources { get; } - - /// - /// Supply storage capacity. - /// - int SupplyStorage { get; } -} \ No newline at end of file diff --git a/FrEee/Interfaces/IStarSystemPlacementStrategy.cs b/FrEee/Interfaces/IStarSystemPlacementStrategy.cs deleted file mode 100644 index 745ed371f..000000000 --- a/FrEee/Interfaces/IStarSystemPlacementStrategy.cs +++ /dev/null @@ -1,21 +0,0 @@ -using FrEee.Objects.Space; -using FrEee.Utility; -using System.Drawing; - -namespace FrEee.Interfaces; - -/// -/// Algorithm for placing star systems on the galaxy map. -/// -public interface IStarSystemPlacementStrategy -{ - /// - /// Places a star system. - /// - /// The galaxy. - /// The minimum number of buffer squares between any two star systems. - /// Where are we allowed to place star systems? - /// How many more stars to place? - /// The location of the star system, or null if a location could not be found. - Point? PlaceStarSystem(Galaxy galaxy, int buffer, Rectangle bounds, int starsLeft, PRNG dice); -} \ No newline at end of file diff --git a/FrEee/Interfaces/IStellarObject.cs b/FrEee/Interfaces/IStellarObject.cs deleted file mode 100644 index ecc0aca23..000000000 --- a/FrEee/Interfaces/IStellarObject.cs +++ /dev/null @@ -1,38 +0,0 @@ -using FrEee.Enumerations; -using FrEee.Modding.Interfaces; - -namespace FrEee.Interfaces; - -public interface IStellarObject : ISpaceObject, IReferrable, IModObject, IAbilityContainer -{ - /// - /// A description of this stellar object. - /// - string Description { get; set; } - - /// - /// Used for naming. - /// - int Index { get; set; } - - /// - /// Used for naming. - /// - bool IsUnique { get; set; } - - /// - /// The name of this stellar object. - /// - new string Name { get; set; } - - /// - /// Name of the picture used to represent this stellar object, excluding the file extension. - /// PNG files will be searched first, then BMP. - /// - string PictureName { get; set; } - - /// - /// The stellar size of this object. - /// - StellarSize StellarSize { get; } -} \ No newline at end of file diff --git a/FrEee/Interfaces/ITargetable.cs b/FrEee/Interfaces/ITargetable.cs deleted file mode 100644 index e402cdb8c..000000000 --- a/FrEee/Interfaces/ITargetable.cs +++ /dev/null @@ -1,19 +0,0 @@ -using FrEee.Enumerations; - -namespace FrEee.Interfaces; - -/// -/// Something which can be specifically target by weapons. -/// -public interface ITargetable : IDamageableReferrable, ITransferrable -{ - /// - /// Evasion rating of this combatant. - /// - int Evasion { get; } - - /// - /// What type of object is this for weapon targeting purposes? - /// - WeaponTargets WeaponTargetType { get; } -} \ No newline at end of file diff --git a/FrEee/Interfaces/IUnlockable.cs b/FrEee/Interfaces/IUnlockable.cs deleted file mode 100644 index db099d582..000000000 --- a/FrEee/Interfaces/IUnlockable.cs +++ /dev/null @@ -1,22 +0,0 @@ -using FrEee.Objects.Civilization; -using FrEee.Modding; -using System.Collections.Generic; - -namespace FrEee.Interfaces; - -/// -/// Something which can be unlocked, either via research or at empire creation. -/// -public interface IUnlockable : INamed, IPictorial -{ - /// - /// A group to display on the research screen, such as "Components" or "Facilities". - /// Or for racial/empire traits, just "Trait". - /// - string ResearchGroup { get; } - - /// - /// The requirements of this item to be unlocked. - /// - IList> UnlockRequirements { get; } -} \ No newline at end of file diff --git a/FrEee/Interfaces/IUpgradeable.cs b/FrEee/Interfaces/IUpgradeable.cs deleted file mode 100644 index f08c20911..000000000 --- a/FrEee/Interfaces/IUpgradeable.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.Collections.Generic; - -namespace FrEee.Interfaces; - -/// -/// Something which can be upgraded to a newer version. -/// -/// -public interface IUpgradeable -{ - /// - /// Can this item be upgraded? - /// - bool IsObsolescent { get; } - - /// - /// Is this item obsolete? - /// - bool IsObsolete { get; } - - /// - /// The latest available version of this item. - /// - T LatestVersion { get; } - - /// - /// Any newer versions of this item. - /// - IEnumerable NewerVersions { get; } - - /// - /// Any older versions of this item. - /// - IEnumerable OlderVersions { get; } -} \ No newline at end of file diff --git a/FrEee/Interfaces/IVehicle.cs b/FrEee/Interfaces/IVehicle.cs deleted file mode 100644 index e2e1b4c58..000000000 --- a/FrEee/Interfaces/IVehicle.cs +++ /dev/null @@ -1,31 +0,0 @@ -using FrEee.Objects.Technology; -using FrEee.Serialization; -using FrEee.Utility; -using System.Collections.Generic; - -namespace FrEee.Interfaces; - -/// -/// A space or ground vehicle. -/// -public interface IVehicle : IConstructable, IOwnableAbilityObject, IReferrable, IDamageable, ICombatant, IRecyclable, IIncomeProducer, IUpgradeable, INameable -{ - [DoNotSerialize(false)] - new IList Components { get; } - - /// - /// Damage that has been applied to this vehicle's components. - /// - SafeDictionary> Damage { get; set; } - - /// - /// The design of this vehicle. - /// - [DoNotCopy] - IDesign Design { get; set; } - - /// - /// Cost to maintain this vehicle per turn. - /// - ResourceQuantity MaintenanceCost { get; } -} diff --git a/FrEee/Interfaces/IVictoryCondition.cs b/FrEee/Interfaces/IVictoryCondition.cs deleted file mode 100644 index b2cbd3948..000000000 --- a/FrEee/Interfaces/IVictoryCondition.cs +++ /dev/null @@ -1,34 +0,0 @@ -using FrEee.Objects.Civilization; -using System.Collections.Generic; - -namespace FrEee.Interfaces; - -/// -/// A victory condition for the game. -/// -public interface IVictoryCondition -{ - /// - /// Gets a defeat message for an empire. - /// - /// - /// - /// - string GetDefeatMessage(Empire emp, IEnumerable winners); - - /// - /// Gets the progress of an empire toward this victory condition. - /// 0 represents no progress; 1 represents completion. - /// Progress can go over 1 if the empire exceeds the requirements. - /// - /// - /// - double GetProgress(Empire emp); - - /// - /// Gets a victory message for an empire. - /// - /// - /// - string GetVictoryMessage(Empire emp); -} \ No newline at end of file diff --git a/FrEee/Modding/ComputedFormula.cs b/FrEee/Modding/ComputedFormula.cs index 806bdf655..f7bd6d46a 100644 --- a/FrEee/Modding/ComputedFormula.cs +++ b/FrEee/Modding/ComputedFormula.cs @@ -2,7 +2,6 @@ using System.Collections.Generic; using System.Globalization; using System.Linq; -using FrEee.Modding.Interfaces; using FrEee.Utility; using FrEee.Serialization; using FrEee.Extensions; diff --git a/FrEee/Modding/DamageType.cs b/FrEee/Modding/DamageType.cs index 3155cff2c..11b0d9f2b 100644 --- a/FrEee/Modding/DamageType.cs +++ b/FrEee/Modding/DamageType.cs @@ -1,5 +1,4 @@ -using FrEee.Modding.Interfaces; -using FrEee.Extensions; +using FrEee.Extensions; using System.Collections.Generic; namespace FrEee.Modding; diff --git a/FrEee/Modding/Enumerations/FormulaType.cs b/FrEee/Modding/Enumerations/FormulaType.cs deleted file mode 100644 index 9a6bef0e4..000000000 --- a/FrEee/Modding/Enumerations/FormulaType.cs +++ /dev/null @@ -1,19 +0,0 @@ -namespace FrEee.Modding.Enumerations; - -public enum FormulaType -{ - /// - /// Text should not be evaluated as a formula. - /// - Literal, - - /// - /// Formula should be evaluated once on mod load. - /// - Static, - - /// - /// Formula should be evaluated at runtime as needed. - /// - Dynamic -} \ No newline at end of file diff --git a/FrEee/Modding/Enumerations/RequirementType.cs b/FrEee/Modding/Enumerations/RequirementType.cs deleted file mode 100644 index a476e46d0..000000000 --- a/FrEee/Modding/Enumerations/RequirementType.cs +++ /dev/null @@ -1,22 +0,0 @@ -namespace FrEee.Modding.Enumerations; - -/// -/// Type of requirement. -/// -public enum RequirementType -{ - /// - /// Requirement to unlock something. - /// - Unlock, - - /// - /// Requirement to build something that has been unlocked. - /// - Build, - - /// - /// Requirement to use something that has been built. - /// - Usage -} \ No newline at end of file diff --git a/FrEee/Modding/EventMessageTarget.cs b/FrEee/Modding/EventMessageTarget.cs new file mode 100644 index 000000000..7c673334c --- /dev/null +++ b/FrEee/Modding/EventMessageTarget.cs @@ -0,0 +1,32 @@ +namespace FrEee.Modding; + +/// +/// Determines who will receive a message when an event occurs. +/// +public enum EventMessageTarget +{ + /// + /// No one will receive a message. + /// + None = 0, + + /// + /// The owner of the affected object will receive a message. + /// + Owner = 1, + + /// + /// Whoever is in the same sector as the event will receive a message. + /// + Sector = 2, + + /// + /// Whoever is in the same sector as the event will receive a message. + /// + System = 3, + + /// + /// Everyone will receive a message. + /// + All = 4 +} \ No newline at end of file diff --git a/FrEee/Modding/EventSeverity.cs b/FrEee/Modding/EventSeverity.cs new file mode 100644 index 000000000..b2f91e3e5 --- /dev/null +++ b/FrEee/Modding/EventSeverity.cs @@ -0,0 +1,13 @@ +namespace FrEee.Modding; + +/// +/// The severity of a random event. +/// +public enum EventSeverity +{ + None = 0, + Low = 1, + Medium = 2, + High = 3, + Catastrophic = 4, +} \ No newline at end of file diff --git a/FrEee/Modding/EventTemplate.cs b/FrEee/Modding/EventTemplate.cs index 77e5f277a..25d51d396 100644 --- a/FrEee/Modding/EventTemplate.cs +++ b/FrEee/Modding/EventTemplate.cs @@ -1,8 +1,6 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Events; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Events; using System.Collections.Generic; +using FrEee.Objects.GameState; namespace FrEee.Modding; diff --git a/FrEee/Modding/EventType.cs b/FrEee/Modding/EventType.cs index c8b23223c..4f318f6fd 100644 --- a/FrEee/Modding/EventType.cs +++ b/FrEee/Modding/EventType.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Modding.Interfaces; +using FrEee.Objects.GameState; using FrEee.Serialization; using System.Collections.Generic; diff --git a/FrEee/Modding/Field.cs b/FrEee/Modding/Field.cs index e3f39dd49..9f7f9f62d 100644 --- a/FrEee/Modding/Field.cs +++ b/FrEee/Modding/Field.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; +using FrEee.Serialization; using System; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Modding/Formula.cs b/FrEee/Modding/Formula.cs index 681cab3ed..2829347c8 100644 --- a/FrEee/Modding/Formula.cs +++ b/FrEee/Modding/Formula.cs @@ -1,4 +1,3 @@ -using FrEee.Modding.Interfaces; using FrEee.Extensions; using System; using System.Collections.Generic; diff --git a/FrEee/Modding/FormulaType.cs b/FrEee/Modding/FormulaType.cs new file mode 100644 index 000000000..5b5d00b8b --- /dev/null +++ b/FrEee/Modding/FormulaType.cs @@ -0,0 +1,19 @@ +namespace FrEee.Modding; + +public enum FormulaType +{ + /// + /// Text should not be evaluated as a formula. + /// + Literal, + + /// + /// Formula should be evaluated once on mod load. + /// + Static, + + /// + /// Formula should be evaluated at runtime as needed. + /// + Dynamic +} \ No newline at end of file diff --git a/FrEee/Modding/IFormula.cs b/FrEee/Modding/IFormula.cs new file mode 100644 index 000000000..8e91cf7a1 --- /dev/null +++ b/FrEee/Modding/IFormula.cs @@ -0,0 +1,28 @@ +using FrEee.Serialization; +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace FrEee.Modding; + +[DoNotCopy] +public interface IFormula : IComparable +{ + object Context { get; } + bool IsDynamic { get; } + bool IsLiteral { get; } + string Text { get; set; } + + object Value { get; } + + object Evaluate(object host, IDictionary variables = null); + + Formula ToStringFormula(CultureInfo c = null); +} + +public interface IFormula : IFormula +{ + new T Value { get; } + + new T Evaluate(object host, IDictionary variables = null); +} diff --git a/FrEee/Modding/IFormulaHost.cs b/FrEee/Modding/IFormulaHost.cs new file mode 100644 index 000000000..bcec3fb80 --- /dev/null +++ b/FrEee/Modding/IFormulaHost.cs @@ -0,0 +1,8 @@ +using System.Collections.Generic; + +namespace FrEee.Modding; + +public interface IFormulaHost +{ + IDictionary Variables { get; } +} \ No newline at end of file diff --git a/FrEee/Modding/IModObject.cs b/FrEee/Modding/IModObject.cs new file mode 100644 index 000000000..e2ca03c4f --- /dev/null +++ b/FrEee/Modding/IModObject.cs @@ -0,0 +1,22 @@ +using FrEee.Objects.GameState; +using System.Collections.Generic; + +namespace FrEee.Modding; + +/// +/// An object which can be stored in mod files. +/// +public interface IModObject : INamed +{ + bool IsDisposed { get; } + + /// + /// An ID used to represent this mod object. + /// + string ModID { get; set; } + + /// + /// Parameters from the mod meta templates. + /// + IDictionary TemplateParameters { get; set; } +} \ No newline at end of file diff --git a/FrEee/Modding/Interfaces/IFormula.cs b/FrEee/Modding/Interfaces/IFormula.cs deleted file mode 100644 index ec9912c9b..000000000 --- a/FrEee/Modding/Interfaces/IFormula.cs +++ /dev/null @@ -1,28 +0,0 @@ -using FrEee.Serialization; -using System; -using System.Collections.Generic; -using System.Globalization; - -namespace FrEee.Modding.Interfaces; - -[DoNotCopy] -public interface IFormula : IComparable -{ - object Context { get; } - bool IsDynamic { get; } - bool IsLiteral { get; } - string Text { get; set; } - - object Value { get; } - - object Evaluate(object host, IDictionary variables = null); - - Formula ToStringFormula(CultureInfo c = null); -} - -public interface IFormula : IFormula -{ - new T Value { get; } - - new T Evaluate(object host, IDictionary variables = null); -} diff --git a/FrEee/Modding/Interfaces/IFormulaHost.cs b/FrEee/Modding/Interfaces/IFormulaHost.cs deleted file mode 100644 index f79f8f6eb..000000000 --- a/FrEee/Modding/Interfaces/IFormulaHost.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System.Collections.Generic; - -namespace FrEee.Modding.Interfaces; - -public interface IFormulaHost -{ - IDictionary Variables { get; } -} \ No newline at end of file diff --git a/FrEee/Modding/Interfaces/IModObject.cs b/FrEee/Modding/Interfaces/IModObject.cs deleted file mode 100644 index 78bc574db..000000000 --- a/FrEee/Modding/Interfaces/IModObject.cs +++ /dev/null @@ -1,22 +0,0 @@ -using FrEee.Interfaces; -using System.Collections.Generic; - -namespace FrEee.Modding.Interfaces; - -/// -/// An object which can be stored in mod files. -/// -public interface IModObject : INamed -{ - bool IsDisposed { get; } - - /// - /// An ID used to represent this mod object. - /// - string ModID { get; set; } - - /// - /// Parameters from the mod meta templates. - /// - IDictionary TemplateParameters { get; set; } -} \ No newline at end of file diff --git a/FrEee/Modding/Interfaces/IScript.cs b/FrEee/Modding/Interfaces/IScript.cs deleted file mode 100644 index 4c2358fdc..000000000 --- a/FrEee/Modding/Interfaces/IScript.cs +++ /dev/null @@ -1,17 +0,0 @@ -namespace FrEee.Modding; - -/// -/// A type of script. -/// -public interface IScript -{ - /// - /// The name of this script module. This should be a valid Python module name. - /// - string ModuleName { get; set; } - - /// - /// The script text. - /// - string Text { get; set; } -} diff --git a/FrEee/Modding/Interfaces/IStellarObjectLocation.cs b/FrEee/Modding/Interfaces/IStellarObjectLocation.cs deleted file mode 100644 index 63fc325b7..000000000 --- a/FrEee/Modding/Interfaces/IStellarObjectLocation.cs +++ /dev/null @@ -1,27 +0,0 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Utility; -using System.Drawing; - -namespace FrEee.Modding.Interfaces; - -/// -/// A location that may specify either a specific sector's coordinates, or a group of sectors, from which one is chosen randomly. -/// -public interface IStellarObjectLocation -{ - /// - /// The last coordinates chosen. - /// Used for "Same As" locations. - /// - Point? LastResult { get; } - - ITemplate StellarObjectTemplate { get; set; } - - /// - /// Chooses a sector. - /// - /// The star system. - /// - Point Resolve(StarSystem sys, PRNG dice); -} \ No newline at end of file diff --git a/FrEee/Modding/Loaders/AbilityLoader.cs b/FrEee/Modding/Loaders/AbilityLoader.cs index cbdc8623e..8196f2bcf 100644 --- a/FrEee/Modding/Loaders/AbilityLoader.cs +++ b/FrEee/Modding/Loaders/AbilityLoader.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Abilities; +using FrEee.Objects.Abilities; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Modding/Loaders/AbilityRuleLoader.cs b/FrEee/Modding/Loaders/AbilityRuleLoader.cs index 507ffd3f7..e7917c501 100644 --- a/FrEee/Modding/Loaders/AbilityRuleLoader.cs +++ b/FrEee/Modding/Loaders/AbilityRuleLoader.cs @@ -1,6 +1,4 @@ -using FrEee.Enumerations; -using FrEee.Objects.Abilities; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Abilities; using System; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Modding/Loaders/ComponentLoader.cs b/FrEee/Modding/Loaders/ComponentLoader.cs index 748b80abb..fb8279fd0 100644 --- a/FrEee/Modding/Loaders/ComponentLoader.cs +++ b/FrEee/Modding/Loaders/ComponentLoader.cs @@ -1,14 +1,12 @@ -using FrEee.Enumerations; -using FrEee.Objects.Combat; +using FrEee.Objects.Combat; using FrEee.Objects.Technology; -using FrEee.Modding.Enumerations; -using FrEee.Modding.Interfaces; using FrEee.Modding.Templates; using FrEee.Utility; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.Vehicles; namespace FrEee.Modding.Loaders; diff --git a/FrEee/Modding/Loaders/CultureLoader.cs b/FrEee/Modding/Loaders/CultureLoader.cs index 66f0748b8..74f3b230a 100644 --- a/FrEee/Modding/Loaders/CultureLoader.cs +++ b/FrEee/Modding/Loaders/CultureLoader.cs @@ -1,5 +1,4 @@ using FrEee.Objects.Civilization; -using FrEee.Modding.Interfaces; using System.Collections.Generic; namespace FrEee.Modding.Loaders; diff --git a/FrEee/Modding/Loaders/DamageTypeLoader.cs b/FrEee/Modding/Loaders/DamageTypeLoader.cs index 8eefc2ad2..eccc0a9e1 100644 --- a/FrEee/Modding/Loaders/DamageTypeLoader.cs +++ b/FrEee/Modding/Loaders/DamageTypeLoader.cs @@ -1,5 +1,4 @@ -using FrEee.Modding.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; namespace FrEee.Modding.Loaders; diff --git a/FrEee/Modding/Loaders/DataFileLoader.cs b/FrEee/Modding/Loaders/DataFileLoader.cs index f8fc40971..e55efb4f1 100644 --- a/FrEee/Modding/Loaders/DataFileLoader.cs +++ b/FrEee/Modding/Loaders/DataFileLoader.cs @@ -1,5 +1,4 @@ -using FrEee.Modding.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; namespace FrEee.Modding.Loaders; diff --git a/FrEee/Modding/Loaders/DesignRoleLoader.cs b/FrEee/Modding/Loaders/DesignRoleLoader.cs index e11359126..f6689e977 100644 --- a/FrEee/Modding/Loaders/DesignRoleLoader.cs +++ b/FrEee/Modding/Loaders/DesignRoleLoader.cs @@ -1,5 +1,4 @@ -using FrEee.Modding.Interfaces; -using System; +using System; using System.Collections.Generic; namespace FrEee.Modding.Loaders; diff --git a/FrEee/Modding/Loaders/EmpireAILoader.cs b/FrEee/Modding/Loaders/EmpireAILoader.cs index caeb39986..c5ba33c78 100644 --- a/FrEee/Modding/Loaders/EmpireAILoader.cs +++ b/FrEee/Modding/Loaders/EmpireAILoader.cs @@ -1,11 +1,10 @@ using FrEee.Objects.AI; using FrEee.Objects.Civilization; -using FrEee.Objects.Space; -using FrEee.Modding.Interfaces; using FrEee.Utility; using System.Collections.Generic; using System.IO; using System.Reflection; +using FrEee.Objects.GameState; namespace FrEee.Modding.Loaders; diff --git a/FrEee/Modding/Loaders/EventLoader.cs b/FrEee/Modding/Loaders/EventLoader.cs index abcf84bf4..573ff71e1 100644 --- a/FrEee/Modding/Loaders/EventLoader.cs +++ b/FrEee/Modding/Loaders/EventLoader.cs @@ -1,6 +1,4 @@ -using FrEee.Enumerations; -using FrEee.Objects.Events; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Events; using FrEee.Extensions; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Modding/Loaders/EventTypeLoader.cs b/FrEee/Modding/Loaders/EventTypeLoader.cs index a75906341..05d52e9d1 100644 --- a/FrEee/Modding/Loaders/EventTypeLoader.cs +++ b/FrEee/Modding/Loaders/EventTypeLoader.cs @@ -1,6 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Modding.Interfaces; -using FrEee.Serialization; +using FrEee.Serialization; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Modding/Loaders/FacilityLoader.cs b/FrEee/Modding/Loaders/FacilityLoader.cs index 75b16c288..f37dce5a9 100644 --- a/FrEee/Modding/Loaders/FacilityLoader.cs +++ b/FrEee/Modding/Loaders/FacilityLoader.cs @@ -1,6 +1,4 @@ using FrEee.Objects.Technology; -using FrEee.Modding.Enumerations; -using FrEee.Modding.Interfaces; using FrEee.Utility; using System; using System.Collections.Generic; diff --git a/FrEee/Modding/Loaders/GalaxyLoader.cs b/FrEee/Modding/Loaders/GalaxyLoader.cs index bea413776..3f9808972 100644 --- a/FrEee/Modding/Loaders/GalaxyLoader.cs +++ b/FrEee/Modding/Loaders/GalaxyLoader.cs @@ -1,5 +1,4 @@ using FrEee.Setup.StarSystemPlacementStrategies; -using FrEee.Modding.Interfaces; using FrEee.Modding.Templates; using FrEee.Extensions; using System; diff --git a/FrEee/Modding/Loaders/HappinessModelLoader.cs b/FrEee/Modding/Loaders/HappinessModelLoader.cs index 121442360..578d541ee 100644 --- a/FrEee/Modding/Loaders/HappinessModelLoader.cs +++ b/FrEee/Modding/Loaders/HappinessModelLoader.cs @@ -1,5 +1,4 @@ using FrEee.Objects.Civilization; -using FrEee.Modding.Interfaces; using System.Collections.Generic; namespace FrEee.Modding.Loaders; diff --git a/FrEee/Modding/Loaders/HullLoader.cs b/FrEee/Modding/Loaders/HullLoader.cs index 2b0db4019..0b176e87c 100644 --- a/FrEee/Modding/Loaders/HullLoader.cs +++ b/FrEee/Modding/Loaders/HullLoader.cs @@ -1,8 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Objects.Technology; +using FrEee.Objects.Technology; using FrEee.Objects.Vehicles; -using FrEee.Modding.Enumerations; -using FrEee.Modding.Interfaces; using FrEee.Utility; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Modding/Loaders/ILoader.cs b/FrEee/Modding/Loaders/ILoader.cs index a066b5325..6d51f22b1 100644 --- a/FrEee/Modding/Loaders/ILoader.cs +++ b/FrEee/Modding/Loaders/ILoader.cs @@ -1,5 +1,4 @@ -using FrEee.Modding.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; namespace FrEee.Modding.Loaders; diff --git a/FrEee/Modding/Loaders/ModInfoLoader.cs b/FrEee/Modding/Loaders/ModInfoLoader.cs index 9fdaab7b5..7fd90d22c 100644 --- a/FrEee/Modding/Loaders/ModInfoLoader.cs +++ b/FrEee/Modding/Loaders/ModInfoLoader.cs @@ -1,5 +1,4 @@ -using FrEee.Modding.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Linq; using System.Reflection; diff --git a/FrEee/Modding/Loaders/ModSettingsLoader.cs b/FrEee/Modding/Loaders/ModSettingsLoader.cs index 91ea5eaa2..668c19744 100644 --- a/FrEee/Modding/Loaders/ModSettingsLoader.cs +++ b/FrEee/Modding/Loaders/ModSettingsLoader.cs @@ -1,6 +1,4 @@ -using FrEee.Enumerations; -using FrEee.Objects.Civilization; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Civilization; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Modding/Loaders/MountLoader.cs b/FrEee/Modding/Loaders/MountLoader.cs index 3ee49c13c..51724fc6c 100644 --- a/FrEee/Modding/Loaders/MountLoader.cs +++ b/FrEee/Modding/Loaders/MountLoader.cs @@ -1,10 +1,9 @@ -using FrEee.Enumerations; -using FrEee.Objects.Technology; -using FrEee.Modding.Enumerations; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Technology; using FrEee.Extensions; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.Vehicles; +using FrEee.Objects.Combat; namespace FrEee.Modding.Loaders; diff --git a/FrEee/Modding/Loaders/RequirementLoader.cs b/FrEee/Modding/Loaders/RequirementLoader.cs index 0bf2861a8..3b9a57301 100644 --- a/FrEee/Modding/Loaders/RequirementLoader.cs +++ b/FrEee/Modding/Loaders/RequirementLoader.cs @@ -1,7 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Technology; -using FrEee.Modding.Enumerations; using FrEee.Extensions; using System.Collections.Generic; diff --git a/FrEee/Modding/Loaders/ScriptLoader.cs b/FrEee/Modding/Loaders/ScriptLoader.cs index ddbcb034c..c935e9161 100644 --- a/FrEee/Modding/Loaders/ScriptLoader.cs +++ b/FrEee/Modding/Loaders/ScriptLoader.cs @@ -1,5 +1,4 @@ -using FrEee.Modding.Interfaces; -using System.Collections.Generic; +using System.Collections.Generic; using System.IO; using System.Reflection; diff --git a/FrEee/Modding/Loaders/StarSystemLoader.cs b/FrEee/Modding/Loaders/StarSystemLoader.cs index 43f2db90b..496246b97 100644 --- a/FrEee/Modding/Loaders/StarSystemLoader.cs +++ b/FrEee/Modding/Loaders/StarSystemLoader.cs @@ -1,8 +1,5 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Abilities; +using FrEee.Objects.Abilities; using FrEee.Objects.Space; -using FrEee.Modding.Interfaces; using FrEee.Modding.StellarObjectLocations; using FrEee.Modding.Templates; using FrEee.Extensions; @@ -11,7 +8,8 @@ using System.Drawing; using System.IO; using System.Linq; -using Size = FrEee.Enumerations.StellarSize; +using Size = FrEee.Objects.Space.StellarSize; +using FrEee.Objects.GameState; namespace FrEee.Modding.Loaders; diff --git a/FrEee/Modding/Loaders/StellarAbilityLoader.cs b/FrEee/Modding/Loaders/StellarAbilityLoader.cs index 7a509084c..3ffe12efd 100644 --- a/FrEee/Modding/Loaders/StellarAbilityLoader.cs +++ b/FrEee/Modding/Loaders/StellarAbilityLoader.cs @@ -1,5 +1,4 @@ using FrEee.Objects.Abilities; -using FrEee.Modding.Interfaces; using FrEee.Modding.Templates; using System; using System.Collections.Generic; diff --git a/FrEee/Modding/Loaders/StellarObjectLoader.cs b/FrEee/Modding/Loaders/StellarObjectLoader.cs index f4ecd04fb..8788374ce 100644 --- a/FrEee/Modding/Loaders/StellarObjectLoader.cs +++ b/FrEee/Modding/Loaders/StellarObjectLoader.cs @@ -1,6 +1,4 @@ -using FrEee.Enumerations; -using FrEee.Objects.Space; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Space; using System; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Modding/Loaders/StellarObjectSizeLoader.cs b/FrEee/Modding/Loaders/StellarObjectSizeLoader.cs index 6b46b59ac..90aa55b9f 100644 --- a/FrEee/Modding/Loaders/StellarObjectSizeLoader.cs +++ b/FrEee/Modding/Loaders/StellarObjectSizeLoader.cs @@ -1,5 +1,4 @@ -using FrEee.Enumerations; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Space; using System.Collections.Generic; namespace FrEee.Modding.Loaders; diff --git a/FrEee/Modding/Loaders/TechnologyLoader.cs b/FrEee/Modding/Loaders/TechnologyLoader.cs index 1fea7066d..d6f2b70d4 100644 --- a/FrEee/Modding/Loaders/TechnologyLoader.cs +++ b/FrEee/Modding/Loaders/TechnologyLoader.cs @@ -1,6 +1,4 @@ using FrEee.Objects.Technology; -using FrEee.Modding.Enumerations; -using FrEee.Modding.Interfaces; using System; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Modding/Loaders/TextLoader.cs b/FrEee/Modding/Loaders/TextLoader.cs index cbb161e41..9c337e5b0 100644 --- a/FrEee/Modding/Loaders/TextLoader.cs +++ b/FrEee/Modding/Loaders/TextLoader.cs @@ -1,5 +1,4 @@ -using FrEee.Modding.Interfaces; -using System; +using System; using System.Collections.Generic; using System.IO; using System.Reflection; diff --git a/FrEee/Modding/Loaders/TraitLoader.cs b/FrEee/Modding/Loaders/TraitLoader.cs index 2f957d6e7..2fe6321f6 100644 --- a/FrEee/Modding/Loaders/TraitLoader.cs +++ b/FrEee/Modding/Loaders/TraitLoader.cs @@ -1,6 +1,5 @@ using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; -using FrEee.Modding.Interfaces; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Modding/MetaRecord.cs b/FrEee/Modding/MetaRecord.cs index 5a2d54fcc..e4ba73acf 100644 --- a/FrEee/Modding/MetaRecord.cs +++ b/FrEee/Modding/MetaRecord.cs @@ -1,8 +1,8 @@ -using FrEee.Interfaces; -using FrEee.Extensions; +using FrEee.Extensions; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Modding; diff --git a/FrEee/Modding/Mod.cs b/FrEee/Modding/Mod.cs index 21b031dfe..85534b80e 100644 --- a/FrEee/Modding/Mod.cs +++ b/FrEee/Modding/Mod.cs @@ -3,17 +3,16 @@ using System.IO; using System.Linq; using System.Reflection; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.AI; using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Objects.Technology; -using FrEee.Modding.Interfaces; using FrEee.Modding.Loaders; using FrEee.Modding.Templates; using FrEee.Utility; using FrEee.Extensions; +using FrEee.Objects.GameState; namespace FrEee.Modding; diff --git a/FrEee/Modding/ModInfo.cs b/FrEee/Modding/ModInfo.cs index b8465d76d..4006ea1a7 100644 --- a/FrEee/Modding/ModInfo.cs +++ b/FrEee/Modding/ModInfo.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; +using FrEee.Objects.GameState; namespace FrEee.Modding; diff --git a/FrEee/Modding/ModReference.cs b/FrEee/Modding/ModReference.cs index 8308358e5..01b1a9bd9 100644 --- a/FrEee/Modding/ModReference.cs +++ b/FrEee/Modding/ModReference.cs @@ -1,10 +1,10 @@ -using FrEee.Interfaces; -using FrEee.Modding.Interfaces; using FrEee.Utility; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Serialization; +using FrEee.Objects.GameState; namespace FrEee.Modding; diff --git a/FrEee/Modding/ModSettings.cs b/FrEee/Modding/ModSettings.cs index 10b1da903..004f3377b 100644 --- a/FrEee/Modding/ModSettings.cs +++ b/FrEee/Modding/ModSettings.cs @@ -1,9 +1,10 @@ -using FrEee.Enumerations; -using FrEee.Objects.Civilization; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Objects.Civilization; +using FrEee.Utility; +using FrEee.Serialization; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.Space; namespace FrEee.Modding; diff --git a/FrEee/Modding/ObjectFormula.cs b/FrEee/Modding/ObjectFormula.cs index 72973130b..4f78bc68f 100644 --- a/FrEee/Modding/ObjectFormula.cs +++ b/FrEee/Modding/ObjectFormula.cs @@ -1,5 +1,4 @@ -using FrEee.Modding.Interfaces; -using FrEee.Utility; +using FrEee.Utility; using FrEee.Serialization; using System; using System.Collections.Generic; diff --git a/FrEee/Modding/Record.cs b/FrEee/Modding/Record.cs index 3caa89242..20cc8d524 100644 --- a/FrEee/Modding/Record.cs +++ b/FrEee/Modding/Record.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; +using FrEee.Serialization; using System; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Modding/RequirementType.cs b/FrEee/Modding/RequirementType.cs new file mode 100644 index 000000000..8ceb6c9cb --- /dev/null +++ b/FrEee/Modding/RequirementType.cs @@ -0,0 +1,22 @@ +namespace FrEee.Modding; + +/// +/// Type of requirement. +/// +public enum RequirementType +{ + /// + /// Requirement to unlock something. + /// + Unlock, + + /// + /// Requirement to build something that has been unlocked. + /// + Build, + + /// + /// Requirement to use something that has been built. + /// + Usage +} \ No newline at end of file diff --git a/FrEee/Modding/Scripts/CSScript.cs b/FrEee/Modding/Scripts/CSScript.cs index 45cfc8385..b527e6d66 100644 --- a/FrEee/Modding/Scripts/CSScript.cs +++ b/FrEee/Modding/Scripts/CSScript.cs @@ -1,4 +1,5 @@ -using FrEee.Utility; +using FrEee.Modding.Scripts; +using FrEee.Utility; using Microsoft.CodeAnalysis; using Microsoft.CodeAnalysis.CSharp.Scripting; using Microsoft.CodeAnalysis.Scripting; diff --git a/FrEee/Modding/Scripts/IScript.cs b/FrEee/Modding/Scripts/IScript.cs new file mode 100644 index 000000000..983f49047 --- /dev/null +++ b/FrEee/Modding/Scripts/IScript.cs @@ -0,0 +1,17 @@ +namespace FrEee.Modding.Scripts; + +/// +/// A type of script. +/// +public interface IScript +{ + /// + /// The name of this script module. This should be a valid Python module name. + /// + string ModuleName { get; set; } + + /// + /// The script text. + /// + string Text { get; set; } +} diff --git a/FrEee/Modding/Scripts/PythonScript.cs b/FrEee/Modding/Scripts/PythonScript.cs index 48bf04696..5e0b153bd 100644 --- a/FrEee/Modding/Scripts/PythonScript.cs +++ b/FrEee/Modding/Scripts/PythonScript.cs @@ -3,6 +3,7 @@ using System; using System.Collections.Generic; using System.IO; +using FrEee.Modding.Scripts; namespace FrEee.Modding; diff --git a/FrEee/Modding/Scripts/PythonScriptEngine.cs b/FrEee/Modding/Scripts/PythonScriptEngine.cs index 1c11e638a..d5c046d8a 100644 --- a/FrEee/Modding/Scripts/PythonScriptEngine.cs +++ b/FrEee/Modding/Scripts/PythonScriptEngine.cs @@ -1,6 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using IronPython.Hosting; using Microsoft.Scripting.Hosting; @@ -9,6 +8,7 @@ using System.Data; using System.Linq; using System.Reflection; +using FrEee.Objects.GameState; namespace FrEee.Modding; @@ -196,7 +196,7 @@ public static T RunScript(PythonScript script, IDictionary va preCommands.Add("import FrEee.Utility"); preCommands.Add("clr.ImportExtensions(FrEee.Extensions)"); preCommands.Add("from FrEee.Modding import Mod"); - preCommands.Add("from FrEee.Objects.Space import Galaxy"); + preCommands.Add("from FrEee.Objects.GameState import Galaxy"); preCommands.Add("from FrEee.Objects.Civilization import Empire"); /*if (variables != null) UpdateScope(variables); @@ -278,7 +278,7 @@ public static void RunScript(PythonScript script, IDictionary va preCommands.Add("import FrEee.Utility"); preCommands.Add("clr.ImportExtensions(FrEee.Extensions)"); preCommands.Add("from FrEee.Modding import Mod"); - preCommands.Add("from FrEee.Objects.Space import Galaxy"); + preCommands.Add("from FrEee.Objects.GameState import Galaxy"); preCommands.Add("from FrEee.Objects.Civilization import Empire"); var code = string.Join("\n", preCommands.ToArray()) + "\n" + diff --git a/FrEee/Modding/StellarObjectLocations/CircleRadiusStellarObjectLocation.cs b/FrEee/Modding/StellarObjectLocations/CircleRadiusStellarObjectLocation.cs index bfb1059ad..094551c1a 100644 --- a/FrEee/Modding/StellarObjectLocations/CircleRadiusStellarObjectLocation.cs +++ b/FrEee/Modding/StellarObjectLocations/CircleRadiusStellarObjectLocation.cs @@ -1,12 +1,11 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Space; using FrEee.Utility; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Modding.StellarObjectLocations; diff --git a/FrEee/Modding/StellarObjectLocations/CoordStellarObjectLocation.cs b/FrEee/Modding/StellarObjectLocations/CoordStellarObjectLocation.cs index d5914249d..b45ddd688 100644 --- a/FrEee/Modding/StellarObjectLocations/CoordStellarObjectLocation.cs +++ b/FrEee/Modding/StellarObjectLocations/CoordStellarObjectLocation.cs @@ -1,9 +1,8 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Space; using FrEee.Utility; using System; using System.Drawing; +using FrEee.Objects.GameState; namespace FrEee.Modding.StellarObjectLocations; diff --git a/FrEee/Modding/StellarObjectLocations/IStellarObjectLocation.cs b/FrEee/Modding/StellarObjectLocations/IStellarObjectLocation.cs new file mode 100644 index 000000000..10e36cb63 --- /dev/null +++ b/FrEee/Modding/StellarObjectLocations/IStellarObjectLocation.cs @@ -0,0 +1,27 @@ +using FrEee.Objects.GameState; +using FrEee.Objects.Space; +using FrEee.Utility; +using System.Drawing; + +namespace FrEee.Modding.StellarObjectLocations; + +/// +/// A location that may specify either a specific sector's coordinates, or a group of sectors, from which one is chosen randomly. +/// +public interface IStellarObjectLocation +{ + /// + /// The last coordinates chosen. + /// Used for "Same As" locations. + /// + Point? LastResult { get; } + + ITemplate StellarObjectTemplate { get; set; } + + /// + /// Chooses a sector. + /// + /// The star system. + /// + Point Resolve(StarSystem sys, PRNG dice); +} \ No newline at end of file diff --git a/FrEee/Modding/StellarObjectLocations/RingStellarObjectLocation.cs b/FrEee/Modding/StellarObjectLocations/RingStellarObjectLocation.cs index f8fc383fe..3f609792c 100644 --- a/FrEee/Modding/StellarObjectLocations/RingStellarObjectLocation.cs +++ b/FrEee/Modding/StellarObjectLocations/RingStellarObjectLocation.cs @@ -1,12 +1,11 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Space; using FrEee.Utility; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Modding.StellarObjectLocations; diff --git a/FrEee/Modding/StellarObjectLocations/SameAsStellarObjectLocation.cs b/FrEee/Modding/StellarObjectLocations/SameAsStellarObjectLocation.cs index f5e5678f8..2d5a2ce73 100644 --- a/FrEee/Modding/StellarObjectLocations/SameAsStellarObjectLocation.cs +++ b/FrEee/Modding/StellarObjectLocations/SameAsStellarObjectLocation.cs @@ -1,10 +1,9 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Space; using FrEee.Modding.Templates; using FrEee.Utility; using System; using System.Drawing; +using FrEee.Objects.GameState; namespace FrEee.Modding.StellarObjectLocations; diff --git a/FrEee/Modding/StellarObjectSize.cs b/FrEee/Modding/StellarObjectSize.cs index 8dc1022a0..5db4a9497 100644 --- a/FrEee/Modding/StellarObjectSize.cs +++ b/FrEee/Modding/StellarObjectSize.cs @@ -1,5 +1,4 @@ -using FrEee.Enumerations; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Space; using System; using System.Collections.Generic; diff --git a/FrEee/Modding/Templates/AsteroidFieldTemplate.cs b/FrEee/Modding/Templates/AsteroidFieldTemplate.cs index c6e525fd7..b28da4886 100644 --- a/FrEee/Modding/Templates/AsteroidFieldTemplate.cs +++ b/FrEee/Modding/Templates/AsteroidFieldTemplate.cs @@ -1,12 +1,10 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Space; using FrEee.Utility; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Modding.Templates; diff --git a/FrEee/Modding/Templates/ComponentTemplate.cs b/FrEee/Modding/Templates/ComponentTemplate.cs index 5ef2f6190..71efcbb47 100644 --- a/FrEee/Modding/Templates/ComponentTemplate.cs +++ b/FrEee/Modding/Templates/ComponentTemplate.cs @@ -1,9 +1,6 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Objects.Technology; -using FrEee.Modding.Interfaces; using FrEee.Utility; using FrEee.Serialization; using FrEee.Extensions; @@ -12,6 +9,9 @@ using System.Drawing; using System.IO; using System.Linq; +using FrEee.Objects.GameState; +using FrEee.Objects.Vehicles; +using FrEee.Objects.Combat; namespace FrEee.Modding.Templates; diff --git a/FrEee/Modding/Templates/FacilityTemplate.cs b/FrEee/Modding/Templates/FacilityTemplate.cs index ab57f897e..c51e4ff81 100644 --- a/FrEee/Modding/Templates/FacilityTemplate.cs +++ b/FrEee/Modding/Templates/FacilityTemplate.cs @@ -1,17 +1,16 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; -using FrEee.Objects.Space; using FrEee.Modding; -using FrEee.Modding.Interfaces; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.GameState; namespace FrEee.Objects.Technology; diff --git a/FrEee/Modding/Templates/GalaxyTemplate.cs b/FrEee/Modding/Templates/GalaxyTemplate.cs index cd4ff5ab9..817a31a84 100644 --- a/FrEee/Modding/Templates/GalaxyTemplate.cs +++ b/FrEee/Modding/Templates/GalaxyTemplate.cs @@ -2,12 +2,12 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; -using FrEee.Interfaces; using FrEee.Objects.Space; using FrEee.Setup; -using FrEee.Modding.Interfaces; using FrEee.Utility; using FrEee.Extensions; +using FrEee.Setup.StarSystemPlacementStrategies; +using FrEee.Objects.GameState; namespace FrEee.Modding.Templates; diff --git a/FrEee/Modding/Templates/PlanetTemplate.cs b/FrEee/Modding/Templates/PlanetTemplate.cs index 11650ec71..54d2a5df1 100644 --- a/FrEee/Modding/Templates/PlanetTemplate.cs +++ b/FrEee/Modding/Templates/PlanetTemplate.cs @@ -1,12 +1,10 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Space; using FrEee.Utility; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Modding.Templates; diff --git a/FrEee/Modding/Templates/RandomAbilityTemplate.cs b/FrEee/Modding/Templates/RandomAbilityTemplate.cs index 11147241d..ab1e641b3 100644 --- a/FrEee/Modding/Templates/RandomAbilityTemplate.cs +++ b/FrEee/Modding/Templates/RandomAbilityTemplate.cs @@ -1,10 +1,9 @@ -using FrEee.Interfaces; -using FrEee.Objects.Abilities; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Abilities; using FrEee.Utility; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Modding.Templates; diff --git a/FrEee/Modding/Templates/StarSystemTemplate.cs b/FrEee/Modding/Templates/StarSystemTemplate.cs index f79678983..5ebde1c9a 100644 --- a/FrEee/Modding/Templates/StarSystemTemplate.cs +++ b/FrEee/Modding/Templates/StarSystemTemplate.cs @@ -1,14 +1,12 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Abilities; +using FrEee.Objects.Abilities; using FrEee.Objects.Space; -using FrEee.Modding.Interfaces; using FrEee.Modding.StellarObjectLocations; using FrEee.Utility; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Modding.Templates; diff --git a/FrEee/Modding/Templates/StarTemplate.cs b/FrEee/Modding/Templates/StarTemplate.cs index 44a02466c..b7035423a 100644 --- a/FrEee/Modding/Templates/StarTemplate.cs +++ b/FrEee/Modding/Templates/StarTemplate.cs @@ -1,11 +1,9 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Space; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Modding.Templates; diff --git a/FrEee/Modding/Templates/StormTemplate.cs b/FrEee/Modding/Templates/StormTemplate.cs index 4abe77c67..cc95342bd 100644 --- a/FrEee/Modding/Templates/StormTemplate.cs +++ b/FrEee/Modding/Templates/StormTemplate.cs @@ -1,11 +1,9 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Modding.Interfaces; +using FrEee.Objects.Space; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Modding.Templates; diff --git a/FrEee/Objects/AI/AI.cs b/FrEee/Objects/AI/AI.cs index afc5f3b48..078597e7c 100644 --- a/FrEee/Objects/AI/AI.cs +++ b/FrEee/Objects/AI/AI.cs @@ -1,5 +1,5 @@ using FrEee.Modding; -using FrEee.Modding.Interfaces; +using FrEee.Modding.Scripts; using FrEee.Utility; using System; using System.Collections.Generic; diff --git a/FrEee/Objects/AI/CSAI.cs b/FrEee/Objects/AI/CSAI.cs index b13e7bb63..a3efbe1fb 100644 --- a/FrEee/Objects/AI/CSAI.cs +++ b/FrEee/Objects/AI/CSAI.cs @@ -1,10 +1,10 @@ using FrEee.Objects.Civilization; -using FrEee.Objects.Space; using FrEee.Modding; using FrEee.Utility; using Microsoft.CodeAnalysis.Scripting; using System; using System.Collections.Generic; +using FrEee.Objects.GameState; namespace FrEee.Objects.AI; diff --git a/FrEee/Objects/Abilities/Ability.cs b/FrEee/Objects/Abilities/Ability.cs index a5b1974d9..1a4a11a80 100644 --- a/FrEee/Objects/Abilities/Ability.cs +++ b/FrEee/Objects/Abilities/Ability.cs @@ -1,14 +1,12 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; -using FrEee.Objects.Space; using FrEee.Modding; -using FrEee.Modding.Interfaces; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Objects.Abilities; diff --git a/FrEee/Objects/Abilities/AbilityRule.cs b/FrEee/Objects/Abilities/AbilityRule.cs index ecfb53948..24f550c38 100644 --- a/FrEee/Objects/Abilities/AbilityRule.cs +++ b/FrEee/Objects/Abilities/AbilityRule.cs @@ -1,7 +1,4 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Modding; -using FrEee.Modding.Interfaces; using FrEee.Utility; using FrEee.Extensions; using System; diff --git a/FrEee/Objects/Abilities/AbilityTargets.cs b/FrEee/Objects/Abilities/AbilityTargets.cs new file mode 100644 index 000000000..651ca74e2 --- /dev/null +++ b/FrEee/Objects/Abilities/AbilityTargets.cs @@ -0,0 +1,79 @@ +using FrEee.Utility; +using System; + +namespace FrEee.Objects.Abilities; + +/// +/// Specifies what object types abilities can stack to. +/// +[Flags] +public enum AbilityTargets +{ + // none + None = 0, + + // basic types + Empire = 0x1, + + Race = 0x2, + Trait = 0x4, + Component = 0x8, // includes MountedComponentTemplate and ComponentTemplate + Facility = 0x10, // includes FacilityTemplate + + // shared types + Sector = 0x20, + + StarSystem = 0x40, + Galaxy = 0x80, + + // stellar objects + [Name("Asteroid")] + [Name("Asteroids")] + [CanonicalName("Asteroid Field")] + AsteroidField = 0x100, + + Planet = 0x200, + Star = 0x400, + Storm = 0x800, + + [CanonicalName("Warp Point")] + WarpPoint = 0x1000, + + // vehicles + Base = 0x2000, + + Drone = 0x4000, + Fighter = 0x8000, + Mine = 0x10000, + Satellite = 0x20000, + Ship = 0x40000, + Troop = 0x80000, + WeaponPlatform = 0x100000, + + // fleets + Fleet = 0x200000, + + // compound types + [CanonicalName("Stellar Object")] + StellarObject = AsteroidField | Planet | Star | Storm | WarpPoint, + + [CanonicalName("Space Vehicle")] + SpaceVehicle = Base | Drone | Fighter | Mine | Satellite | Ship, + + [CanonicalName("Ground Vehicle")] + GroundVehicle = WeaponPlatform | Troop, + + Unit = Drone | Fighter | Mine | Satellite | Troop | WeaponPlatform, + Vehicle = Ship | Base | Unit, + + [CanonicalName("Space Object")] + SpaceObject = StellarObject | SpaceVehicle, + + Part = Component | Facility, + + // invalid + Invalid = 0x2000000, + + // everything! + All = int.MaxValue & ~Invalid, +} diff --git a/FrEee/Objects/Abilities/AbilityValueRule.cs b/FrEee/Objects/Abilities/AbilityValueRule.cs new file mode 100644 index 000000000..40bc8f32a --- /dev/null +++ b/FrEee/Objects/Abilities/AbilityValueRule.cs @@ -0,0 +1,46 @@ +using FrEee.Utility; +namespace FrEee.Objects.Abilities; + +/// +/// Rules for grouping and stacking ability values within a group of similar abilities. +/// +public enum AbilityValueRule +{ + /// + /// Do not group or stack abilities by this value. + /// Note that this does not necessarily mean that only one instance of the ability will apply! + /// To guarantee this, use TakeHighest, TakeAverage, or TakeLowest. + /// + None, + + /// + /// Group the abilities by this value. + /// + Group, + + /// + /// Add the values within the group. Only works properly for numeric values. + /// + Add, + + /// + /// Take the highest value within the group. Only works properly for numeric values. + /// + [CanonicalName("Take Highest")] + [Name("Highest")] + TakeHighest, + + /// + /// Take the average of the group values. Only works properly for numeric values. + /// + [CanonicalName("Take Average")] + [Name("Average")] + TakeAverage, + + /// + /// Take the lowest value within the group. Only works properly for numeric values. + /// + [CanonicalName("Take Lowest")] + [Name("Lowest")] + TakeLowest +} \ No newline at end of file diff --git a/FrEee/Objects/Abilities/IAbilityObject.cs b/FrEee/Objects/Abilities/IAbilityObject.cs new file mode 100644 index 000000000..31ac68a06 --- /dev/null +++ b/FrEee/Objects/Abilities/IAbilityObject.cs @@ -0,0 +1,44 @@ +using FrEee.Serialization; +using System.Collections.Generic; + +namespace FrEee.Objects.Abilities; + +/// +/// Something which can have intrinsic abilities of its own that can be added and removed. +/// +public interface IAbilityContainer : IAbilityObject +{ + /// + /// Intrinsic abilities of this object which can be added or removed. + /// TODO - rename to IntrinsicAbilities after the current game is over + /// + IList Abilities { get; } +} + +/// +/// Something which can have abilities. +/// +public interface IAbilityObject +{ + /// + /// The type of ability target that this object represents. + /// + AbilityTargets AbilityTarget { get; } + + /// + /// Child objects that can pass up abilities to this object. + /// + IEnumerable Children { get; } + + /// + /// Abilities possessed intrinsically by this object. + /// + IEnumerable IntrinsicAbilities { get; } + + /// + /// Parent objects from which this object can inherit abilities. + /// + IEnumerable Parents { get; } +} + +public interface IReferrableAbilityObject : IReferrable, IAbilityObject { } diff --git a/FrEee/Objects/Abilities/ICommonAbilityObject.cs b/FrEee/Objects/Abilities/ICommonAbilityObject.cs new file mode 100644 index 000000000..a6057a6c3 --- /dev/null +++ b/FrEee/Objects/Abilities/ICommonAbilityObject.cs @@ -0,0 +1,17 @@ +using FrEee.Objects.Civilization; +using System.Collections.Generic; + +namespace FrEee.Objects.Abilities; + +/// +/// An object that can contain different abilities for different empires. +/// +public interface ICommonAbilityObject : IAbilityObject +{ + /// + /// Finds any child ability objects owned by an empire. + /// + /// + /// + IEnumerable GetContainedAbilityObjects(Empire emp); +} \ No newline at end of file diff --git a/FrEee/Interfaces/IOwnableAbilityObject.cs b/FrEee/Objects/Abilities/IOwnableAbilityObject.cs similarity index 67% rename from FrEee/Interfaces/IOwnableAbilityObject.cs rename to FrEee/Objects/Abilities/IOwnableAbilityObject.cs index 0edf5e4e8..45198033e 100644 --- a/FrEee/Interfaces/IOwnableAbilityObject.cs +++ b/FrEee/Objects/Abilities/IOwnableAbilityObject.cs @@ -1,4 +1,6 @@ -namespace FrEee.Interfaces; +using FrEee.Objects.Civilization; + +namespace FrEee.Objects.Abilities; /// /// Something which can have abilities and be owned. diff --git a/FrEee/Objects/Civilization/Aptitude.cs b/FrEee/Objects/Civilization/Aptitude.cs index 04d9ac095..4aa579819 100644 --- a/FrEee/Objects/Civilization/Aptitude.cs +++ b/FrEee/Objects/Civilization/Aptitude.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; +using FrEee.Objects.GameState; using System; using System.Collections.Generic; diff --git a/FrEee/Objects/Civilization/Cargo.cs b/FrEee/Objects/Civilization/Cargo.cs deleted file mode 100644 index 3c80bdc24..000000000 --- a/FrEee/Objects/Civilization/Cargo.cs +++ /dev/null @@ -1,264 +0,0 @@ -using FrEee.Interfaces; -using FrEee.Objects.Combat; -using FrEee.Modding; -using FrEee.Utility; using FrEee.Serialization; -using FrEee.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace FrEee.Objects.Civilization; - -/// -/// Cargo stored on a colony or ship/base. -/// -public class Cargo : IDamageable -{ - public Cargo() - { - Population = new SafeDictionary(); - Units = new HashSet(); - } - - public int ArmorHitpoints - { - get { return Units.Sum(u => u.ArmorHitpoints); } - } - - public int HitChance - { - get { return 1; } - } - - [DoNotSerialize(false)] - public int Hitpoints - { - get - { - double popHPPerPerson = Mod.Current.Settings.PopulationHitpoints; - return Population.Sum(kvp => (int)Math.Ceiling(kvp.Value * popHPPerPerson)) + Units.Sum(u => u.Hitpoints); - } - set - { - throw new NotSupportedException("Can't set cargo HP; it's computed."); - } - } - - public int HullHitpoints - { - get { return Units.Sum(u => u.HullHitpoints) + (int)(Population.Sum(kvp => kvp.Value) * Mod.Current.Settings.PopulationHitpoints); } - } - - public bool IsDestroyed - { - get { return Hitpoints <= 0; } - } - - public int MaxArmorHitpoints - { - get { return Units.Sum(u => u.MaxArmorHitpoints); } - } - - public int MaxHitpoints - { - get - { - double popHPPerPerson = Mod.Current.Settings.PopulationHitpoints; - return Population.Sum(kvp => (int)Math.Ceiling(kvp.Value * popHPPerPerson)) + Units.Sum(u => u.MaxHitpoints); - } - } - - public int MaxHullHitpoints - { - get { return Units.Sum(u => u.MaxHullHitpoints); } - } - - public int MaxNormalShields - { - get { return 0; } - } - - public int MaxPhasedShields - { - get { return 0; } - } - - public int MaxShieldHitpoints - { - get { return Units.Sum(u => u.MaxShieldHitpoints); } - } - - [DoNotSerialize(false)] - public int NormalShields - { - get - { - return 0; - } - set - { - throw new NotSupportedException("Cargo cannot have shields."); - } - } - - [DoNotSerialize(false)] - public int PhasedShields - { - get - { - return 0; - } - set - { - throw new NotSupportedException("Cargo cannot have shields."); - } - } - - /// - /// The population stored in cargo. - /// - public SafeDictionary Population { get; set; } - - public int ShieldHitpoints - { - get { return Units.Sum(u => u.ShieldHitpoints); } - } - - /// - /// The amount of space taken by this cargo. - /// - public int Size - { - get - { - if (fakeSize != null) - return fakeSize.Value; - - // TODO - per race population size? - return (int)Math.Round(Population.Sum(kvp => kvp.Value) * Mod.Current.Settings.PopulationSize) + Units.Sum(u => u.Design.Hull.Size); - } - } - - /// - /// The units stored in cargo. - /// - public ICollection Units { get; set; } - - private int? fakeSize { get; set; } - - public static Cargo operator +(Cargo c1, Cargo c2) - { - var result = new Cargo(); - foreach (var kvp in c1.Population) - result.Population[kvp.Key] += kvp.Value; - foreach (var kvp in c2.Population) - result.Population[kvp.Key] += kvp.Value; - foreach (var unit in c1.Units.Union(c2.Units)) - result.Units.Add(unit); - return result; - } - - /// - /// Passes repair on to units. - /// Tries to repair more-damaged units first. - /// TODO - repair priorities - /// - /// - /// - public int? Repair(int? amount = null) - { - if (amount == null) - { - foreach (var u in Units.OrderBy(u => (double)u.Hitpoints / (double)u.MaxHitpoints)) - u.Repair(amount); - } - else - { - foreach (var u in Units.OrderBy(u => (double)u.Hitpoints / (double)u.MaxHitpoints)) - amount = u.Repair(amount); - } - return amount; - } - - public void ReplenishShields(int? amount = null) - { - // do nothing - } - - /// - /// Sets this cargo's fake size to its size (or zero if cargo size can't be seen) and clears the actual population and units. - /// Used for fog of war. - /// - public void SetFakeSize(bool canSeeCargoSize) - { - if (canSeeCargoSize && fakeSize == null) - fakeSize = Size; - else if (!canSeeCargoSize) - fakeSize = 0; - - Population.Clear(); - Units.Clear(); - } - - public int TakeDamage(Hit hit, PRNG dice = null) - { - int damage = hit.NominalDamage; - if (Population.Any() && Units.Any()) - { - // for now, have a 50% chance to hit population first and a 50% chance to hit units first - // TODO - base the chance to hit population vs. units on relative HP or something? - var coin = RandomHelper.Next(2, dice); - int leftover; - if (coin == 0) - leftover = TakePopulationDamage(hit, damage, dice); - else - leftover = TakeUnitDamage(hit, damage, dice); - if (coin == 0) - return TakeUnitDamage(hit, leftover, dice); - else - return TakePopulationDamage(hit, damage, dice); - } - else if (Population.Any()) - return TakePopulationDamage(hit, damage, dice); - else if (Units.Any()) - return TakeUnitDamage(hit, damage, dice); - else - return damage; // nothing to damage - } - - private int TakePopulationDamage(Hit hit, int damage, PRNG dice = null) - { - int inflicted = 0; - for (int i = 0; i < damage; i++) - { - // pick a race and kill some population - var race = Population.PickWeighted(dice); - if (race == null) - break; // no more population - double popHPPerPerson = Mod.Current.Settings.PopulationHitpoints; - // TODO - don't ceiling the popKilled, just stack it up - int popKilled = (int)Math.Ceiling(hit.Shot.DamageType.PopulationDamage.Evaluate(hit.Shot) / popHPPerPerson); - Population[race] -= popKilled; - if (Population[race] < 0) - Population[race] = 0; - inflicted += 1; - } - // clear population that was emptied out - foreach (var race in Population.Where(kvp => kvp.Value <= 0).Select(kvp => kvp.Key).ToArray()) - Population.Remove(race); - return damage - inflicted; - } - - private int TakeUnitDamage(Hit hit, int damage, PRNG dice = null) - { - // units with more HP are more likely to get hit first, like with leaky armor - var units = Units.Where(u => !u.IsDestroyed).ToDictionary(u => u, u => u.MaxHitpoints); - while (units.Any() && damage > 0) - { - var u = units.PickWeighted(dice); - damage = u.TakeDamage(hit, dice); - units = units.Where(x => !x.Key.IsDestroyed).ToDictionary(x => x.Key, x => x.Value); - } - return damage; - } -} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/CargoDelta.cs b/FrEee/Objects/Civilization/CargoDelta.cs deleted file mode 100644 index 07ff52ad2..000000000 --- a/FrEee/Objects/Civilization/CargoDelta.cs +++ /dev/null @@ -1,126 +0,0 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Modding; -using FrEee.Utility; -using FrEee.Serialization; -using FrEee.Extensions; -using System; -using System.Collections.Generic; - -namespace FrEee.Objects.Civilization; - -/// -/// A change in cargo. -/// -public class CargoDelta : IPromotable -{ - public CargoDelta() - { - RacePopulation = new GalaxyReferenceKeyedDictionary(); - AllPopulation = false; - AnyPopulation = 0L; - Units = new GalaxyReferenceSet(); - UnitDesignTonnage = new GalaxyReferenceKeyedDictionary, int?>(); - UnitRoleTonnage = new SafeDictionary(); - UnitTypeTonnage = new SafeDictionary(); - } - - /// - /// Should we transfer as much population as possible, regardless of race? - /// - public bool AllPopulation { get; set; } - - /// - /// Amount of population to transfer where the race of the population is irrelevant. - /// - public long AnyPopulation { get; set; } - - /// - /// Estimated tonnage of the cargo delta. - /// Will return null if there is any "All" quantity specified. - /// - public int? EstimatedTonnage - { - get - { - int? tonnage = 0; - foreach (var kvp in RacePopulation) - { - if (kvp.Value == null) - return null; - tonnage += (int)Math.Ceiling(kvp.Value.Value * Mod.Current.Settings.PopulationSize); - } - if (AllPopulation) - return null; - tonnage += (int)Math.Ceiling(AnyPopulation * Mod.Current.Settings.PopulationSize); - foreach (var u in Units) - tonnage += u.Design.Hull.Size; - foreach (var d in UnitDesignTonnage) - tonnage += d.Value; - foreach (var r in UnitRoleTonnage) - tonnage += r.Value; - foreach (var t in UnitTypeTonnage) - tonnage += t.Value; - return tonnage; - } - } - - public GalaxyReferenceKeyedDictionary RacePopulation { get; private set; } - public GalaxyReferenceKeyedDictionary, int?> UnitDesignTonnage { get; private set; } - public SafeDictionary UnitRoleTonnage { get; private set; } - public GalaxyReferenceSet Units { get; private set; } - public SafeDictionary UnitTypeTonnage { get; private set; } - - public void ReplaceClientIDs(IDictionary idmap, ISet done = null) - { - if (done == null) - done = new HashSet(); - if (!done.Contains(this)) - { - done.Add(this); - RacePopulation.ReplaceClientIDs(idmap, done); - UnitDesignTonnage.ReplaceClientIDs(idmap, done); - Units.ReplaceClientIDs(idmap, done); - } - } - - public override string ToString() - { - var items = new List(); - foreach (var kvp in RacePopulation) - { - if (kvp.Value == null) - items.Add("All " + kvp.Key + " Population"); - else - items.Add(kvp.Value.ToUnitString() + " " + kvp.Key + " Population"); - } - if (AllPopulation) - items.Add("All Population"); - else if (AnyPopulation != 0) - items.Add(AnyPopulation.ToUnitString() + " Population of Any Race"); - foreach (var unit in Units) - items.Add(unit.ToString()); - foreach (var kvp in UnitDesignTonnage) - { - if (kvp.Value == null) - items.Add("All \"" + kvp.Key + "\" " + kvp.Key.VehicleTypeName + "s"); - else - items.Add(kvp.Value.Kilotons() + " of " + kvp.Key + "\" " + kvp.Key.VehicleTypeName + "s"); - } - foreach (var kvp in UnitRoleTonnage) - { - if (kvp.Value == null) - items.Add("All " + kvp.Key + " Units"); - else - items.Add(kvp.Value.Kilotons() + " of " + kvp.Key + " Units"); - } - foreach (var kvp in UnitTypeTonnage) - { - if (kvp.Value == null) - items.Add("All " + kvp.Key + "s"); - else - items.Add(kvp.Value.Kilotons() + " of " + kvp.Key + "s"); - } - return string.Join(", ", items.ToArray()); - } -} diff --git a/FrEee/Objects/Civilization/CargoStorage/Cargo.cs b/FrEee/Objects/Civilization/CargoStorage/Cargo.cs new file mode 100644 index 000000000..fdfcf7fb7 --- /dev/null +++ b/FrEee/Objects/Civilization/CargoStorage/Cargo.cs @@ -0,0 +1,265 @@ +using FrEee.Objects.Combat; +using FrEee.Modding; +using FrEee.Utility; +using FrEee.Serialization; +using FrEee.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using FrEee.Objects.Vehicles; + +namespace FrEee.Objects.Civilization.CargoStorage; + +/// +/// Cargo stored on a colony or ship/base. +/// +public class Cargo : IDamageable +{ + public Cargo() + { + Population = new SafeDictionary(); + Units = new HashSet(); + } + + public int ArmorHitpoints + { + get { return Units.Sum(u => u.ArmorHitpoints); } + } + + public int HitChance + { + get { return 1; } + } + + [DoNotSerialize(false)] + public int Hitpoints + { + get + { + double popHPPerPerson = Mod.Current.Settings.PopulationHitpoints; + return Population.Sum(kvp => (int)Math.Ceiling(kvp.Value * popHPPerPerson)) + Units.Sum(u => u.Hitpoints); + } + set + { + throw new NotSupportedException("Can't set cargo HP; it's computed."); + } + } + + public int HullHitpoints + { + get { return Units.Sum(u => u.HullHitpoints) + (int)(Population.Sum(kvp => kvp.Value) * Mod.Current.Settings.PopulationHitpoints); } + } + + public bool IsDestroyed + { + get { return Hitpoints <= 0; } + } + + public int MaxArmorHitpoints + { + get { return Units.Sum(u => u.MaxArmorHitpoints); } + } + + public int MaxHitpoints + { + get + { + double popHPPerPerson = Mod.Current.Settings.PopulationHitpoints; + return Population.Sum(kvp => (int)Math.Ceiling(kvp.Value * popHPPerPerson)) + Units.Sum(u => u.MaxHitpoints); + } + } + + public int MaxHullHitpoints + { + get { return Units.Sum(u => u.MaxHullHitpoints); } + } + + public int MaxNormalShields + { + get { return 0; } + } + + public int MaxPhasedShields + { + get { return 0; } + } + + public int MaxShieldHitpoints + { + get { return Units.Sum(u => u.MaxShieldHitpoints); } + } + + [DoNotSerialize(false)] + public int NormalShields + { + get + { + return 0; + } + set + { + throw new NotSupportedException("Cargo cannot have shields."); + } + } + + [DoNotSerialize(false)] + public int PhasedShields + { + get + { + return 0; + } + set + { + throw new NotSupportedException("Cargo cannot have shields."); + } + } + + /// + /// The population stored in cargo. + /// + public SafeDictionary Population { get; set; } + + public int ShieldHitpoints + { + get { return Units.Sum(u => u.ShieldHitpoints); } + } + + /// + /// The amount of space taken by this cargo. + /// + public int Size + { + get + { + if (fakeSize != null) + return fakeSize.Value; + + // TODO - per race population size? + return (int)Math.Round(Population.Sum(kvp => kvp.Value) * Mod.Current.Settings.PopulationSize) + Units.Sum(u => u.Design.Hull.Size); + } + } + + /// + /// The units stored in cargo. + /// + public ICollection Units { get; set; } + + private int? fakeSize { get; set; } + + public static Cargo operator +(Cargo c1, Cargo c2) + { + var result = new Cargo(); + foreach (var kvp in c1.Population) + result.Population[kvp.Key] += kvp.Value; + foreach (var kvp in c2.Population) + result.Population[kvp.Key] += kvp.Value; + foreach (var unit in c1.Units.Union(c2.Units)) + result.Units.Add(unit); + return result; + } + + /// + /// Passes repair on to units. + /// Tries to repair more-damaged units first. + /// TODO - repair priorities + /// + /// + /// + public int? Repair(int? amount = null) + { + if (amount == null) + { + foreach (var u in Units.OrderBy(u => u.Hitpoints / (double)u.MaxHitpoints)) + u.Repair(amount); + } + else + { + foreach (var u in Units.OrderBy(u => u.Hitpoints / (double)u.MaxHitpoints)) + amount = u.Repair(amount); + } + return amount; + } + + public void ReplenishShields(int? amount = null) + { + // do nothing + } + + /// + /// Sets this cargo's fake size to its size (or zero if cargo size can't be seen) and clears the actual population and units. + /// Used for fog of war. + /// + public void SetFakeSize(bool canSeeCargoSize) + { + if (canSeeCargoSize && fakeSize == null) + fakeSize = Size; + else if (!canSeeCargoSize) + fakeSize = 0; + + Population.Clear(); + Units.Clear(); + } + + public int TakeDamage(Hit hit, PRNG dice = null) + { + int damage = hit.NominalDamage; + if (Population.Any() && Units.Any()) + { + // for now, have a 50% chance to hit population first and a 50% chance to hit units first + // TODO - base the chance to hit population vs. units on relative HP or something? + var coin = RandomHelper.Next(2, dice); + int leftover; + if (coin == 0) + leftover = TakePopulationDamage(hit, damage, dice); + else + leftover = TakeUnitDamage(hit, damage, dice); + if (coin == 0) + return TakeUnitDamage(hit, leftover, dice); + else + return TakePopulationDamage(hit, damage, dice); + } + else if (Population.Any()) + return TakePopulationDamage(hit, damage, dice); + else if (Units.Any()) + return TakeUnitDamage(hit, damage, dice); + else + return damage; // nothing to damage + } + + private int TakePopulationDamage(Hit hit, int damage, PRNG dice = null) + { + int inflicted = 0; + for (int i = 0; i < damage; i++) + { + // pick a race and kill some population + var race = Population.PickWeighted(dice); + if (race == null) + break; // no more population + double popHPPerPerson = Mod.Current.Settings.PopulationHitpoints; + // TODO - don't ceiling the popKilled, just stack it up + int popKilled = (int)Math.Ceiling(hit.Shot.DamageType.PopulationDamage.Evaluate(hit.Shot) / popHPPerPerson); + Population[race] -= popKilled; + if (Population[race] < 0) + Population[race] = 0; + inflicted += 1; + } + // clear population that was emptied out + foreach (var race in Population.Where(kvp => kvp.Value <= 0).Select(kvp => kvp.Key).ToArray()) + Population.Remove(race); + return damage - inflicted; + } + + private int TakeUnitDamage(Hit hit, int damage, PRNG dice = null) + { + // units with more HP are more likely to get hit first, like with leaky armor + var units = Units.Where(u => !u.IsDestroyed).ToDictionary(u => u, u => u.MaxHitpoints); + while (units.Any() && damage > 0) + { + var u = units.PickWeighted(dice); + damage = u.TakeDamage(hit, dice); + units = units.Where(x => !x.Key.IsDestroyed).ToDictionary(x => x.Key, x => x.Value); + } + return damage; + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/CargoStorage/CargoDelta.cs b/FrEee/Objects/Civilization/CargoStorage/CargoDelta.cs new file mode 100644 index 000000000..877784fc9 --- /dev/null +++ b/FrEee/Objects/Civilization/CargoStorage/CargoDelta.cs @@ -0,0 +1,126 @@ +using FrEee.Modding; +using FrEee.Utility; +using FrEee.Serialization; +using FrEee.Extensions; +using System; +using System.Collections.Generic; +using FrEee.Objects.Vehicles; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.CargoStorage; + +/// +/// A change in cargo. +/// +public class CargoDelta : IPromotable +{ + public CargoDelta() + { + RacePopulation = new GalaxyReferenceKeyedDictionary(); + AllPopulation = false; + AnyPopulation = 0L; + Units = new GalaxyReferenceSet(); + UnitDesignTonnage = new GalaxyReferenceKeyedDictionary, int?>(); + UnitRoleTonnage = new SafeDictionary(); + UnitTypeTonnage = new SafeDictionary(); + } + + /// + /// Should we transfer as much population as possible, regardless of race? + /// + public bool AllPopulation { get; set; } + + /// + /// Amount of population to transfer where the race of the population is irrelevant. + /// + public long AnyPopulation { get; set; } + + /// + /// Estimated tonnage of the cargo delta. + /// Will return null if there is any "All" quantity specified. + /// + public int? EstimatedTonnage + { + get + { + int? tonnage = 0; + foreach (var kvp in RacePopulation) + { + if (kvp.Value == null) + return null; + tonnage += (int)Math.Ceiling(kvp.Value.Value * Mod.Current.Settings.PopulationSize); + } + if (AllPopulation) + return null; + tonnage += (int)Math.Ceiling(AnyPopulation * Mod.Current.Settings.PopulationSize); + foreach (var u in Units) + tonnage += u.Design.Hull.Size; + foreach (var d in UnitDesignTonnage) + tonnage += d.Value; + foreach (var r in UnitRoleTonnage) + tonnage += r.Value; + foreach (var t in UnitTypeTonnage) + tonnage += t.Value; + return tonnage; + } + } + + public GalaxyReferenceKeyedDictionary RacePopulation { get; private set; } + public GalaxyReferenceKeyedDictionary, int?> UnitDesignTonnage { get; private set; } + public SafeDictionary UnitRoleTonnage { get; private set; } + public GalaxyReferenceSet Units { get; private set; } + public SafeDictionary UnitTypeTonnage { get; private set; } + + public void ReplaceClientIDs(IDictionary idmap, ISet done = null) + { + if (done == null) + done = new HashSet(); + if (!done.Contains(this)) + { + done.Add(this); + RacePopulation.ReplaceClientIDs(idmap, done); + UnitDesignTonnage.ReplaceClientIDs(idmap, done); + Units.ReplaceClientIDs(idmap, done); + } + } + + public override string ToString() + { + var items = new List(); + foreach (var kvp in RacePopulation) + { + if (kvp.Value == null) + items.Add("All " + kvp.Key + " Population"); + else + items.Add(kvp.Value.ToUnitString() + " " + kvp.Key + " Population"); + } + if (AllPopulation) + items.Add("All Population"); + else if (AnyPopulation != 0) + items.Add(AnyPopulation.ToUnitString() + " Population of Any Race"); + foreach (var unit in Units) + items.Add(unit.ToString()); + foreach (var kvp in UnitDesignTonnage) + { + if (kvp.Value == null) + items.Add("All \"" + kvp.Key + "\" " + kvp.Key.VehicleTypeName + "s"); + else + items.Add(kvp.Value.Kilotons() + " of " + kvp.Key + "\" " + kvp.Key.VehicleTypeName + "s"); + } + foreach (var kvp in UnitRoleTonnage) + { + if (kvp.Value == null) + items.Add("All " + kvp.Key + " Units"); + else + items.Add(kvp.Value.Kilotons() + " of " + kvp.Key + " Units"); + } + foreach (var kvp in UnitTypeTonnage) + { + if (kvp.Value == null) + items.Add("All " + kvp.Key + "s"); + else + items.Add(kvp.Value.Kilotons() + " of " + kvp.Key + "s"); + } + return string.Join(", ", items.ToArray()); + } +} diff --git a/FrEee/Objects/Civilization/CargoStorage/ICargoContainer.cs b/FrEee/Objects/Civilization/CargoStorage/ICargoContainer.cs new file mode 100644 index 000000000..da1c07da6 --- /dev/null +++ b/FrEee/Objects/Civilization/CargoStorage/ICargoContainer.cs @@ -0,0 +1,65 @@ +using FrEee.Objects.GameState; +using FrEee.Objects.Space; +using FrEee.Objects.Vehicles; +using System.Collections.Generic; + +namespace FrEee.Objects.Civilization.CargoStorage; + +/// +/// An object which can contain cargo. +/// +public interface ICargoContainer : IPictorial, INamed, ILocated +{ + /// + /// All population stored by this cargo container, whether in a colony or in cargo. + /// + IDictionary AllPopulation { get; } + + /// + /// All units that are in or part of this cargo container. + /// + IEnumerable AllUnits { get; } + + /// + /// The cargo contained by this object. + /// + Cargo Cargo { get; } + + /// + /// The total amount of cargo storage possessed by this object. + /// + int CargoStorage { get; } + + /// + /// The amount of available population storage. + /// + long PopulationStorageFree { get; } + + /// + /// Adds population. + /// + /// + /// + /// The amount of population that could not be added due to overflow. + long AddPopulation(Race race, long amount); + + /// + /// Adds a unit. + /// + /// + /// true if there was space left to add the unit, otherwise false. + bool AddUnit(IUnit unit); + + /// + /// Removes population. + /// + /// + /// The amount of population that could not be removed due to lack of population. + long RemovePopulation(Race race, long amount); + + /// + /// Removes a unit. + /// + /// + bool RemoveUnit(IUnit unit); +} \ No newline at end of file diff --git a/FrEee/Interfaces/ICargoTransferrer.cs b/FrEee/Objects/Civilization/CargoStorage/ICargoTransferrer.cs similarity index 69% rename from FrEee/Interfaces/ICargoTransferrer.cs rename to FrEee/Objects/Civilization/CargoStorage/ICargoTransferrer.cs index 99ba501a3..608e9c8d8 100644 --- a/FrEee/Interfaces/ICargoTransferrer.cs +++ b/FrEee/Objects/Civilization/CargoStorage/ICargoTransferrer.cs @@ -1,4 +1,6 @@ -namespace FrEee.Interfaces; +using FrEee.Objects.Space; + +namespace FrEee.Objects.Civilization.CargoStorage; /// /// A space object which can contain cargo and receive cargo transfer orders. diff --git a/FrEee/Objects/Civilization/Colony.cs b/FrEee/Objects/Civilization/Colony.cs index 9efb51193..78117b4f1 100644 --- a/FrEee/Objects/Civilization/Colony.cs +++ b/FrEee/Objects/Civilization/Colony.cs @@ -1,14 +1,16 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Abilities; +using FrEee.Objects.Civilization.CargoStorage; using FrEee.Objects.Space; using FrEee.Objects.Technology; using FrEee.Modding; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.GameState; namespace FrEee.Objects.Civilization; diff --git a/FrEee/Objects/Civilization/Construction/ConstructionQueue.cs b/FrEee/Objects/Civilization/Construction/ConstructionQueue.cs new file mode 100644 index 000000000..d9afc2070 --- /dev/null +++ b/FrEee/Objects/Civilization/Construction/ConstructionQueue.cs @@ -0,0 +1,580 @@ +using FrEee.Objects.Space; +using FrEee.Objects.Technology; +using FrEee.Objects.Vehicles; +using FrEee.Modding; +using FrEee.Utility; +using FrEee.Serialization; +using FrEee.Extensions; +using System; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.Construction; + +/// +/// Something which can construct objects. +/// +[Serializable] +public class ConstructionQueue : IOrderable, IOwnable, IFoggable, IContainable +{ + public ConstructionQueue(IConstructor c) + { + Orders = new List(); + Container = c; + UnspentRate = new ResourceQuantity(); + } + + /// + /// Are this object's orders on hold? + /// + public bool AreOrdersOnHold { get; set; } + + /// + /// Should this object's orders repeat once they are completed? + /// + public bool AreRepeatOrdersEnabled { get; set; } + + /// + /// Cargo space free, counting queued items as already constructed and in cargo. + /// + public int CargoStorageFree + { + get + { + if (!(Container is ICargoContainer)) + return 0; + return ((ICargoContainer)Container).CargoStorageFree() - Orders.Select(o => o.Template).OfType>().Sum(t => t.Hull.Size); + } + } + + /// + /// Cargo space free in the entire sector, counting queued items as already constructed and in cargo. + /// + public int CargoStorageFreeInSector + { + get + { + var storage = Container.Sector.SpaceObjects.Where(sobj => sobj.Owner == Owner) + .OfType().Sum(cc => cc.CargoStorageFree()); + var queues = Container.Sector.SpaceObjects.OfType().Where + (sobj => sobj.Owner == Owner && sobj.ConstructionQueue != null) + .Select(sobj => sobj.ConstructionQueue); + return storage - queues.Sum(q => q.Orders.Select(o => o.Template).OfType>().Sum(t => t.Hull.Size)); + } + } + + /// + /// The colony (if any) associated with this queue. + /// + public Colony Colony + { + get + { + if (Container is Planet) + return ((Planet)Container).Colony; + return null; + } + } + + [DoNotCopy] + public IConstructor Container { get; set; } + + /// + /// The ETA for completion of the whole queue, in turns. + /// Null if there is nothing being built. + /// + public double? Eta + { + get + { + if (!Orders.Any()) + return null; + if (!Rate.Any(kvp => kvp.Value > 0)) + return double.PositiveInfinity; + if (!Orders.Any()) + return 0d; + var remainingCost = Orders.Select(o => o.Cost - (o.Item == null ? new ResourceQuantity() : o.Item.ConstructionProgress)).Aggregate((r1, r2) => r1 + r2); + return remainingCost.Max(kvp => kvp.Value / (double)Rate[kvp.Key]); + } + } + + /// + /// Facility slots free, counting queued items as already constructed and on the colony. + /// + public int FacilitySlotsFree + { + get + { + if (Colony == null) + return 0; + // TODO - storage racial trait + return ((Planet)Container).MaxFacilities - Colony.Facilities.Count - Orders.OfType>().Count(); + } + } + + /// + /// The ETA for completion of the first item, in turns. + /// + public double? FirstItemEta + { + get + { + if (!Orders.Any()) + return null; + var remainingCost = Orders[0].Cost - (Orders[0].Item == null ? new ResourceQuantity() : Orders[0].Item.ConstructionProgress); + return remainingCost.Max(kvp => kvp.Value / (double)Rate[kvp.Key]); + } + } + + /// + /// The icon for the item being constructed. + /// + public Image FirstItemIcon + { + get + { + if (!Orders.Any()) + return Pictures.GetSolidColorImage(Color.Transparent); + return Orders.First().Template.Icon; + } + } + + /// + /// The name of the first item. + /// + public string FirstItemName + { + get + { + if (!Orders.Any()) + return null; + return Orders[0].Template.Name; + } + } + + [DoNotSerialize] + public Image Icon + { + get + { + return (Container as ISpaceObject)?.Icon; + } + } + + public long ID + { + get; + set; + } + + /// + /// Is this a colony queue? + /// + public bool IsColonyQueue { get { return Colony != null; } } + + /// + /// Has construction been delayed this turn due to lack of resources etc? + /// For avoiding spamming log messages for every item in the queue. + /// + [DoNotSerialize] + public bool IsConstructionDelayed { get; set; } + + public bool IsDisposed { get; set; } + + public bool IsIdle + { + get + { + var unlockedHulls = Mod.Current.Hulls.OfType>().Where(h => h.IsUnlocked()); + return (Eta == null || Eta < 1 && !AreRepeatOrdersEnabled) + && (IsSpaceYardQueue || FacilitySlotsFree > 0 || unlockedHulls.Any() && unlockedHulls.Min(h => h.Size) <= CargoStorageFreeInSector); + } + } + + // TODO - make this a DoNotSerialize property after the game ends + public bool IsMemory + { + get + { + return Container?.IsMemory ?? true; + } + set + { + if (Container == null) + return; + Container.IsMemory = value; + } + } + + /// + /// Is this a space yard queue? + /// + public bool IsSpaceYardQueue { get { return Container.HasAbility("Space Yard"); } } + + public string Name + { + get { return Container.Name; } + } + + public IList Orders + { + get; + private set; + } + + IEnumerable IOrderable.Orders + => Orders; + + public Empire Owner + { + get { return Container.Owner; } + } + + /// + /// The rate at which this queue can construct. + /// + public ResourceQuantity Rate + { + get + { + if (Empire.Current != null) + { + // try to use cache, rate can't change client side! + if (rate == null) + rate = ComputeRate(); + return rate; + } + else + return ComputeRate(); + } + } + + public int RateMinerals { get { return Rate[Resource.Minerals]; } } + public int RateOrganics { get { return Rate[Resource.Organics]; } } + public int RateRadioactives { get { return Rate[Resource.Radioactives]; } } + public double Timestamp { get; set; } + + /// + /// Unspent build rate for this turn. + /// Does not update as orders are changed on the client; only during turn processing! + /// + public ResourceQuantity UnspentRate { get; set; } + + /// + /// Upcoming spending on construction this turn. + /// + public ResourceQuantity UpcomingSpending + { + get + { + var spent = new ResourceQuantity(); + if (AreOrdersOnHold) + return spent; + do + { + var spentThisRound = new ResourceQuantity(); + foreach (var o in Orders) + { + var left = o.Cost; + if (o.Item != null) + left -= o.Item.ConstructionProgress; + left = ResourceQuantity.Min(left, Rate - spent); + spent += left; + spentThisRound += left; + } + if (!spentThisRound.Any(kvp => kvp.Value > 0)) + break; + } while (AreRepeatOrdersEnabled); + return spent; + } + } + + private ResourceQuantity rate; + + public void AddOrder(IOrder order) + { + if (order == null) + Owner.Log.Append(Container.CreateLogMessage($"Can't add a null order to {this}. Probably a bug...", logMessageType: LogMessages.LogMessageType.Error)); + else if (!(order is IConstructionOrder)) + Owner.Log.Append(Container.CreateLogMessage($"Can't add a {order.GetType()} to {this}. Probably a bug...", logMessageType: LogMessages.LogMessageType.Error)); + else + { + var co = (IConstructionOrder)order; + if (co.Template == null) + Owner.Log.Append(Container.CreateLogMessage($"Can't add an order with no template to {this}. Probably a bug...", logMessageType: LogMessages.LogMessageType.Error)); + else + Orders.Add(co); + } + } + + /// + /// Can this queue construct something? + /// + /// + /// + public bool CanConstruct(IConstructionTemplate item) + { + return GetReasonForBeingUnableToConstruct(item) == null; + } + + /// + /// Only the owner of a space object can see its construction queue. + /// + /// + /// + public Visibility CheckVisibility(Empire emp) + { + if (IsMemory && this.MemoryOwner() != emp) + return Visibility.Unknown; // can't see from opponents' memories! + var vis = Container.CheckVisibility(emp); + if (vis == Visibility.Owned) + return vis; + return Visibility.Unknown; + } + + public void Dispose() + { + if (IsDisposed) + return; + if (!IsMemory && Mod.Current != null) + this.UpdateEmpireMemories(); + Galaxy.Current.UnassignID(this); + Orders.Clear(); + IsDisposed = true; + } + + /// + /// Executes orders for a turn. + /// + public bool ExecuteOrders() + { + bool didStuff = false; + + if (AreOrdersOnHold) + return didStuff; + + UnspentRate = Rate; + var empty = new ResourceQuantity(); + var builtThisTurn = new HashSet(); + bool done = false; + while (!done && Orders.Any() && (Owner.StoredResources > empty || UpcomingSpending.IsEmpty)) + { + var numOrders = Orders.Count; + var spentThisRound = new ResourceQuantity(); + + foreach (var order in Orders.Cast().ToArray()) + { + if (order == null) + { + // WTF + Orders.Remove(order); + continue; + } + var reasonForNotBuilding = GetReasonForBeingUnableToConstruct(order.Template); + if (reasonForNotBuilding != null) + { + // can't build that here! + Orders.RemoveAt(0); + Owner.Log.Add(Container.CreateLogMessage(order.Template + " cannot be built at " + this + " because " + reasonForNotBuilding, LogMessages.LogMessageType.Error)); + } + else + { + var oldProgress = new ResourceQuantity(order.Item?.ConstructionProgress); + order.Execute(this); + var newProgress = new ResourceQuantity(order.Item?.ConstructionProgress); + if (newProgress < (order.Item?.Cost ?? new ResourceQuantity()) && newProgress == oldProgress && order == Orders.Last()) + done = true; // made no progress and nothing else to try and build + if (order.CheckCompletion(this)) + { + // upgrade facility orders place their own facilities + if (!(order is UpgradeFacilityOrder)) + order.Item.Place(Container); + Orders.Remove(order); + if (AreRepeatOrdersEnabled) + { + var copy = order.Copy(); + copy.Reset(); + Orders.Add(copy); + } + builtThisTurn.Add(order.Item); + if (order.Item is Ship || order.Item is Base) + { + // trigger ship built happiness changes + Owner.TriggerHappinessChange(hm => hm.AnyShipConstructed); + if (Container is Planet p) + p.Colony.TriggerHappinessChange(hm => hm.ShipConstructed); + + } + if (order.Item is Facility) + { + // trigger facility built happiness changes + if (Container is Planet p) + p.Colony.TriggerHappinessChange(hm => hm.FacilityConstructed); + + } + } + } + } + + didStuff = true; + + if (!AreRepeatOrdersEnabled) + done = true; + } + foreach (var g in builtThisTurn.GroupBy(i => i.Template)) + { + if (g.Count() == 1) + Owner.Log.Add(g.First().CreateLogMessage(g.First() + " has been constructed at " + Name + ".", logMessageType: LogMessages.LogMessageType.ConstructionComplete)); + else + Owner.Log.Add(g.First().CreateLogMessage(g.Count() + "x " + g.Key + " have been constructed at " + Name + ".", logMessageType: LogMessages.LogMessageType.ConstructionComplete)); + } + return didStuff; + } + + /// + /// Gets the reason why this queue cannot construct an item, or null if it can be constructed. + /// + /// + /// + public string GetReasonForBeingUnableToConstruct(IConstructionTemplate item) + { + if (item == null) + return "Construction template does not exist."; + if (!item.HasBeenUnlockedBy(Owner)) + return Owner + " has not yet unlocked " + item + "."; + if (!IsSpaceYardQueue && item.RequiresSpaceYardQueue) + return item + " requires a space yard queue."; + if (!IsColonyQueue && item.RequiresColonyQueue) + return item + " requires a colony queue."; + return null; + } + + public bool IsObsoleteMemory(Empire emp) + { + return Container == null || Container.StarSystem.CheckVisibility(emp) >= Visibility.Visible && Timestamp < Galaxy.Current.Timestamp - 1; + } + + public void RearrangeOrder(IOrder order, int delta) + { + if (order != null && !(order is IConstructionOrder)) + throw new Exception("Can't rearrange a " + order.GetType() + " in a construction queue's orders."); + var o = (IConstructionOrder)order; + var newpos = Orders.IndexOf(o) + delta; + if (newpos < 0) + newpos = 0; + Orders.Remove(o); + if (newpos >= Orders.Count) + Orders.Add(o); + else + Orders.Insert(newpos, o); + } + + public void Redact(Empire emp) + { + // TODO - see first order in queue if queue is scanned? + // need to add design being built to known designs too? + if (CheckVisibility(emp) < Visibility.Owned) + { + Orders.DisposeAll(); + Orders.Clear(); + AreOrdersOnHold = false; + AreRepeatOrdersEnabled = false; + } + if (CheckVisibility(emp) < Visibility.Fogged) + Dispose(); + } + + public void RemoveOrder(IOrder order) + { + if (order == null) + Owner.Log.Add(Container.CreateLogMessage("Attempted to remove a null order from " + this + ". This is likely a game bug.", LogMessages.LogMessageType.Error)); + else if (!(order is IConstructionOrder)) + return; // order can't exist here anyway + else + Orders.Remove((IConstructionOrder)order); + } + + public override string ToString() + { + return Container + "'s construction queue"; + } + + private ResourceQuantity ComputeRate() + { + var rate = ComputeSYAbilityRate(); + if (Colony != null) + { + if (rate == null) + rate = Mod.Current.Settings.DefaultColonyConstructionRate; + + // apply population modifier + var pop = Colony.Population.Sum(p => p.Value); + if (pop == 0) + return new ResourceQuantity(); + rate *= Mod.Current.Settings.GetPopulationConstructionFactor(pop); + + // apply mood modifier + // TODO - load mood modifier from mod + var moodModifier = Colony.Mood == Mood.Rioting ? 0 : 100; + rate *= moodModifier / 100d; + + var ratios = Colony.Population.Select(p => new { Race = p.Key, Ratio = p.Value / (double)pop }); + + // apply racial trait planetary SY modifier + // TODO - should Planetary SY Rate apply only to planets that have space yards, or to all planetary construction queues? + double traitmod = 1d; + foreach (var ratio in ratios) + traitmod += ratio.Race.GetAbilityValue("Planetary SY Rate").ToDouble() / 100d * ratio.Ratio; + rate *= traitmod; + + // apply aptitude modifier + if (IsSpaceYardQueue) + { + double aptmod = 0d; + foreach (var ratio in ratios) + aptmod += ratio.Race.Aptitudes[Aptitude.Construction.Name] / 100d * ratio.Ratio; + rate *= aptmod; + + // apply culture modifier + rate *= (100d + (Owner?.Culture?.Construction ?? 0)) / 100d; + } + } + if (rate == null) + rate = new ResourceQuantity(); + if (Container is IVehicle) + { + // apply aptitude modifier for empire's primary race + rate *= Owner.PrimaryRace.Aptitudes[Aptitude.Construction.Name] / 100d; + } + + return rate; + } + + private ResourceQuantity ComputeSYAbilityRate() + { + if (Container.HasAbility("Space Yard")) + { + var rate = new ResourceQuantity(); + // TODO - moddable resources? + for (int i = 1; i <= 3; i++) + { + var amount = Container.GetAbilityValue("Space Yard", 2, true, true, a => a.Value1 == i.ToString()).ToInt(); + Resource res = null; + if (i == 1) + res = Resource.Minerals; + else if (i == 2) + res = Resource.Organics; + else if (i == 3) + res = Resource.Radioactives; + rate[res] = amount; + } + return rate; + } + else + return null; + } +} diff --git a/FrEee/Objects/Civilization/Construction/IConstructable.cs b/FrEee/Objects/Civilization/Construction/IConstructable.cs new file mode 100644 index 000000000..94181ea45 --- /dev/null +++ b/FrEee/Objects/Civilization/Construction/IConstructable.cs @@ -0,0 +1,36 @@ +using FrEee.Objects.GameState; +using FrEee.Objects.Space; +using FrEee.Utility; +namespace FrEee.Objects.Civilization.Construction; + +/// +/// Something which can be constructed at a construction queue. +/// +public interface IConstructable : INamed, IPictorial, ITransferrable +{ + /// + /// The progress toward constructing this item. + /// + ResourceQuantity ConstructionProgress { get; set; } + + /// + /// The resource cost to build this item. + /// + ResourceQuantity Cost { get; } + + /// + /// The empire which owns this item. + /// + new Empire Owner { get; set; } + + /// + /// The construction template used to build this item. + /// + IConstructionTemplate Template { get; } + + /// + /// Places the newly constructed item at a location. + /// + /// The space object which the item should be placed on or near. + void Place(ISpaceObject target); +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Construction/IConstructionOrder.cs b/FrEee/Objects/Civilization/Construction/IConstructionOrder.cs new file mode 100644 index 000000000..b97a282dc --- /dev/null +++ b/FrEee/Objects/Civilization/Construction/IConstructionOrder.cs @@ -0,0 +1,27 @@ +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.GameState; +using FrEee.Utility; +namespace FrEee.Objects.Civilization.Construction; + +public interface IConstructionOrder : IOrder, INamed +{ + /// + /// The cost of the construction. + /// + ResourceQuantity Cost { get; } + + /// + /// The item being constructed. + /// + IConstructable Item { get; } + + /// + /// The template. + /// + IConstructionTemplate Template { get; } + + /// + /// Resets this order so it can be repeated. + /// + void Reset(); +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Construction/IConstructionTemplate.cs b/FrEee/Objects/Civilization/Construction/IConstructionTemplate.cs new file mode 100644 index 000000000..d710278a0 --- /dev/null +++ b/FrEee/Objects/Civilization/Construction/IConstructionTemplate.cs @@ -0,0 +1,35 @@ +using FrEee.Objects.Civilization; +using FrEee.Objects.GameState; +using FrEee.Objects.Technology; +using FrEee.Serialization; +using FrEee.Utility; +namespace FrEee.Objects.Civilization.Construction; + +/// +/// Template for constructable items. +/// +/// +public interface IConstructionTemplate : IReferrable, IPictorial, IResearchable +{ + /// + /// The cost to build it. + /// + ResourceQuantity Cost { get; } + + /// + /// Does this template require a colony to build it? + /// + bool RequiresColonyQueue { get; } + + /// + /// Does this template require a space yard to build it? + /// + bool RequiresSpaceYardQueue { get; } + + /// + /// Has the empire unlocked this construction template? + /// + /// + /// + bool HasBeenUnlockedBy(Empire emp); +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Construction/IConstructor.cs b/FrEee/Objects/Civilization/Construction/IConstructor.cs new file mode 100644 index 000000000..c6c1b6d89 --- /dev/null +++ b/FrEee/Objects/Civilization/Construction/IConstructor.cs @@ -0,0 +1,14 @@ +using FrEee.Objects.Space; + +namespace FrEee.Objects.Civilization.Construction; + +/// +/// Something which has a construction queue. +/// +public interface IConstructor : ISpaceObject, IOwnable +{ + /// + /// This object's construction queue, if any. + /// + ConstructionQueue ConstructionQueue { get; } +} diff --git a/FrEee/Objects/Civilization/ConstructionQueue.cs b/FrEee/Objects/Civilization/ConstructionQueue.cs deleted file mode 100644 index 54ab4715b..000000000 --- a/FrEee/Objects/Civilization/ConstructionQueue.cs +++ /dev/null @@ -1,579 +0,0 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Orders; -using FrEee.Objects.Space; -using FrEee.Objects.Technology; -using FrEee.Objects.Vehicles; -using FrEee.Modding; -using FrEee.Utility; using FrEee.Serialization; -using FrEee.Extensions; -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; - -namespace FrEee.Objects.Civilization; - -/// -/// Something which can construct objects. -/// -[Serializable] -public class ConstructionQueue : IOrderable, IOwnable, IFoggable, IContainable -{ - public ConstructionQueue(IConstructor c) - { - Orders = new List(); - Container = c; - UnspentRate = new ResourceQuantity(); - } - - /// - /// Are this object's orders on hold? - /// - public bool AreOrdersOnHold { get; set; } - - /// - /// Should this object's orders repeat once they are completed? - /// - public bool AreRepeatOrdersEnabled { get; set; } - - /// - /// Cargo space free, counting queued items as already constructed and in cargo. - /// - public int CargoStorageFree - { - get - { - if (!(Container is ICargoContainer)) - return 0; - return ((ICargoContainer)Container).CargoStorageFree() - Orders.Select(o => o.Template).OfType>().Sum(t => t.Hull.Size); - } - } - - /// - /// Cargo space free in the entire sector, counting queued items as already constructed and in cargo. - /// - public int CargoStorageFreeInSector - { - get - { - var storage = Container.Sector.SpaceObjects.Where(sobj => sobj.Owner == Owner) - .OfType().Sum(cc => cc.CargoStorageFree()); - var queues = Container.Sector.SpaceObjects.OfType().Where - (sobj => sobj.Owner == Owner && sobj.ConstructionQueue != null) - .Select(sobj => sobj.ConstructionQueue); - return storage - queues.Sum(q => q.Orders.Select(o => o.Template).OfType>().Sum(t => t.Hull.Size)); - } - } - - /// - /// The colony (if any) associated with this queue. - /// - public Colony Colony - { - get - { - if (Container is Planet) - return ((Planet)Container).Colony; - return null; - } - } - - [DoNotCopy] - public IConstructor Container { get; set; } - - /// - /// The ETA for completion of the whole queue, in turns. - /// Null if there is nothing being built. - /// - public double? Eta - { - get - { - if (!Orders.Any()) - return null; - if (!Rate.Any(kvp => kvp.Value > 0)) - return double.PositiveInfinity; - if (!Orders.Any()) - return 0d; - var remainingCost = Orders.Select(o => o.Cost - (o.Item == null ? new ResourceQuantity() : o.Item.ConstructionProgress)).Aggregate((r1, r2) => r1 + r2); - return remainingCost.Max(kvp => (double)kvp.Value / (double)Rate[kvp.Key]); - } - } - - /// - /// Facility slots free, counting queued items as already constructed and on the colony. - /// - public int FacilitySlotsFree - { - get - { - if (Colony == null) - return 0; - // TODO - storage racial trait - return ((Planet)Container).MaxFacilities - Colony.Facilities.Count - Orders.OfType>().Count(); - } - } - - /// - /// The ETA for completion of the first item, in turns. - /// - public double? FirstItemEta - { - get - { - if (!Orders.Any()) - return null; - var remainingCost = Orders[0].Cost - (Orders[0].Item == null ? new ResourceQuantity() : Orders[0].Item.ConstructionProgress); - return remainingCost.Max(kvp => (double)kvp.Value / (double)Rate[kvp.Key]); - } - } - - /// - /// The icon for the item being constructed. - /// - public Image FirstItemIcon - { - get - { - if (!Orders.Any()) - return Pictures.GetSolidColorImage(Color.Transparent); - return Orders.First().Template.Icon; - } - } - - /// - /// The name of the first item. - /// - public string FirstItemName - { - get - { - if (!Orders.Any()) - return null; - return Orders[0].Template.Name; - } - } - - [DoNotSerialize] - public Image Icon - { - get - { - return (Container as ISpaceObject)?.Icon; - } - } - - public long ID - { - get; - set; - } - - /// - /// Is this a colony queue? - /// - public bool IsColonyQueue { get { return Colony != null; } } - - /// - /// Has construction been delayed this turn due to lack of resources etc? - /// For avoiding spamming log messages for every item in the queue. - /// - [DoNotSerialize] - public bool IsConstructionDelayed { get; set; } - - public bool IsDisposed { get; set; } - - public bool IsIdle - { - get - { - var unlockedHulls = Mod.Current.Hulls.OfType>().Where(h => h.IsUnlocked()); - return (Eta == null || Eta < 1 && !AreRepeatOrdersEnabled) - && (IsSpaceYardQueue || FacilitySlotsFree > 0 || unlockedHulls.Any() && unlockedHulls.Min(h => h.Size) <= CargoStorageFreeInSector); - } - } - - // TODO - make this a DoNotSerialize property after the game ends - public bool IsMemory - { - get - { - return Container?.IsMemory ?? true; - } - set - { - if (Container == null) - return; - Container.IsMemory = value; - } - } - - /// - /// Is this a space yard queue? - /// - public bool IsSpaceYardQueue { get { return Container.HasAbility("Space Yard"); } } - - public string Name - { - get { return Container.Name; } - } - - public IList Orders - { - get; - private set; - } - - IEnumerable IOrderable.Orders - => Orders; - - public Empire Owner - { - get { return Container.Owner; } - } - - /// - /// The rate at which this queue can construct. - /// - public ResourceQuantity Rate - { - get - { - if (Empire.Current != null) - { - // try to use cache, rate can't change client side! - if (rate == null) - rate = ComputeRate(); - return rate; - } - else - return ComputeRate(); - } - } - - public int RateMinerals { get { return Rate[Resource.Minerals]; } } - public int RateOrganics { get { return Rate[Resource.Organics]; } } - public int RateRadioactives { get { return Rate[Resource.Radioactives]; } } - public double Timestamp { get; set; } - - /// - /// Unspent build rate for this turn. - /// Does not update as orders are changed on the client; only during turn processing! - /// - public ResourceQuantity UnspentRate { get; set; } - - /// - /// Upcoming spending on construction this turn. - /// - public ResourceQuantity UpcomingSpending - { - get - { - var spent = new ResourceQuantity(); - if (AreOrdersOnHold) - return spent; - do - { - var spentThisRound = new ResourceQuantity(); - foreach (var o in Orders) - { - var left = o.Cost; - if (o.Item != null) - left -= o.Item.ConstructionProgress; - left = ResourceQuantity.Min(left, Rate - spent); - spent += left; - spentThisRound += left; - } - if (!spentThisRound.Any(kvp => kvp.Value > 0)) - break; - } while (AreRepeatOrdersEnabled); - return spent; - } - } - - private ResourceQuantity rate; - - public void AddOrder(IOrder order) - { - if (order == null) - Owner.Log.Append(Container.CreateLogMessage($"Can't add a null order to {this}. Probably a bug...",logMessageType: LogMessages.LogMessageType.Error)); - else if (!(order is IConstructionOrder)) - Owner.Log.Append(Container.CreateLogMessage($"Can't add a {order.GetType()} to {this}. Probably a bug...", logMessageType: LogMessages.LogMessageType.Error)); - else - { - var co = (IConstructionOrder)order; - if (co.Template == null) - Owner.Log.Append(Container.CreateLogMessage($"Can't add an order with no template to {this}. Probably a bug...", logMessageType: LogMessages.LogMessageType.Error)); - else - Orders.Add(co); - } - } - - /// - /// Can this queue construct something? - /// - /// - /// - public bool CanConstruct(IConstructionTemplate item) - { - return GetReasonForBeingUnableToConstruct(item) == null; - } - - /// - /// Only the owner of a space object can see its construction queue. - /// - /// - /// - public Visibility CheckVisibility(Empire emp) - { - if (IsMemory && this.MemoryOwner() != emp) - return Visibility.Unknown; // can't see from opponents' memories! - var vis = Container.CheckVisibility(emp); - if (vis == Visibility.Owned) - return vis; - return Visibility.Unknown; - } - - public void Dispose() - { - if (IsDisposed) - return; - if (!IsMemory && Mod.Current != null) - this.UpdateEmpireMemories(); - Galaxy.Current.UnassignID(this); - Orders.Clear(); - IsDisposed = true; - } - - /// - /// Executes orders for a turn. - /// - public bool ExecuteOrders() - { - bool didStuff = false; - - if (AreOrdersOnHold) - return didStuff; - - UnspentRate = Rate; - var empty = new ResourceQuantity(); - var builtThisTurn = new HashSet(); - bool done = false; - while (!done && Orders.Any() && (Owner.StoredResources > empty || UpcomingSpending.IsEmpty)) - { - var numOrders = Orders.Count; - var spentThisRound = new ResourceQuantity(); - - foreach (var order in Orders.Cast().ToArray()) - { - if (order == null) - { - // WTF - Orders.Remove(order); - continue; - } - var reasonForNotBuilding = GetReasonForBeingUnableToConstruct(order.Template); - if (reasonForNotBuilding != null) - { - // can't build that here! - Orders.RemoveAt(0); - Owner.Log.Add(Container.CreateLogMessage(order.Template + " cannot be built at " + this + " because " + reasonForNotBuilding, LogMessages.LogMessageType.Error)); - } - else - { - var oldProgress = new ResourceQuantity(order.Item?.ConstructionProgress); - order.Execute(this); - var newProgress = new ResourceQuantity(order.Item?.ConstructionProgress); - if (newProgress < (order.Item?.Cost ?? new ResourceQuantity()) && newProgress == oldProgress && order == Orders.Last()) - done = true; // made no progress and nothing else to try and build - if (order.CheckCompletion(this)) - { - // upgrade facility orders place their own facilities - if (!(order is UpgradeFacilityOrder)) - order.Item.Place(Container); - Orders.Remove(order); - if (AreRepeatOrdersEnabled) - { - var copy = order.Copy(); - copy.Reset(); - Orders.Add(copy); - } - builtThisTurn.Add(order.Item); - if (order.Item is Ship || order.Item is Base) - { - // trigger ship built happiness changes - Owner.TriggerHappinessChange(hm => hm.AnyShipConstructed); - if (Container is Planet p) - p.Colony.TriggerHappinessChange(hm => hm.ShipConstructed); - - } - if (order.Item is Facility) - { - // trigger facility built happiness changes - if (Container is Planet p) - p.Colony.TriggerHappinessChange(hm => hm.FacilityConstructed); - - } - } - } - } - - didStuff = true; - - if (!AreRepeatOrdersEnabled) - done = true; - } - foreach (var g in builtThisTurn.GroupBy(i => i.Template)) - { - if (g.Count() == 1) - Owner.Log.Add(g.First().CreateLogMessage(g.First() + " has been constructed at " + Name + ".", logMessageType: LogMessages.LogMessageType.ConstructionComplete)); - else - Owner.Log.Add(g.First().CreateLogMessage(g.Count() + "x " + g.Key + " have been constructed at " + Name + ".", logMessageType: LogMessages.LogMessageType.ConstructionComplete)); - } - return didStuff; - } - - /// - /// Gets the reason why this queue cannot construct an item, or null if it can be constructed. - /// - /// - /// - public string GetReasonForBeingUnableToConstruct(IConstructionTemplate item) - { - if (item == null) - return "Construction template does not exist."; - if (!item.HasBeenUnlockedBy(Owner)) - return Owner + " has not yet unlocked " + item + "."; - if (!IsSpaceYardQueue && item.RequiresSpaceYardQueue) - return item + " requires a space yard queue."; - if (!IsColonyQueue && item.RequiresColonyQueue) - return item + " requires a colony queue."; - return null; - } - - public bool IsObsoleteMemory(Empire emp) - { - return Container == null || Container.StarSystem.CheckVisibility(emp) >= Visibility.Visible && Timestamp < Galaxy.Current.Timestamp - 1; - } - - public void RearrangeOrder(IOrder order, int delta) - { - if (order != null && !(order is IConstructionOrder)) - throw new Exception("Can't rearrange a " + order.GetType() + " in a construction queue's orders."); - var o = (IConstructionOrder)order; - var newpos = Orders.IndexOf(o) + delta; - if (newpos < 0) - newpos = 0; - Orders.Remove(o); - if (newpos >= Orders.Count) - Orders.Add(o); - else - Orders.Insert(newpos, o); - } - - public void Redact(Empire emp) - { - // TODO - see first order in queue if queue is scanned? - // need to add design being built to known designs too? - if (CheckVisibility(emp) < Visibility.Owned) - { - Orders.DisposeAll(); - Orders.Clear(); - AreOrdersOnHold = false; - AreRepeatOrdersEnabled = false; - } - if (CheckVisibility(emp) < Visibility.Fogged) - Dispose(); - } - - public void RemoveOrder(IOrder order) - { - if (order == null) - Owner.Log.Add(Container.CreateLogMessage("Attempted to remove a null order from " + this + ". This is likely a game bug.", LogMessages.LogMessageType.Error)); - else if (!(order is IConstructionOrder)) - return; // order can't exist here anyway - else - Orders.Remove((IConstructionOrder)order); - } - - public override string ToString() - { - return Container + "'s construction queue"; - } - - private ResourceQuantity ComputeRate() - { - var rate = ComputeSYAbilityRate(); - if (Colony != null) - { - if (rate == null) - rate = Mod.Current.Settings.DefaultColonyConstructionRate; - - // apply population modifier - var pop = Colony.Population.Sum(p => p.Value); - if (pop == 0) - return new ResourceQuantity(); - rate *= Mod.Current.Settings.GetPopulationConstructionFactor(pop); - - // apply mood modifier - // TODO - load mood modifier from mod - var moodModifier = Colony.Mood == Mood.Rioting ? 0 : 100; - rate *= moodModifier / 100d; - - var ratios = Colony.Population.Select(p => new { Race = p.Key, Ratio = (double)p.Value / (double)pop }); - - // apply racial trait planetary SY modifier - // TODO - should Planetary SY Rate apply only to planets that have space yards, or to all planetary construction queues? - double traitmod = 1d; - foreach (var ratio in ratios) - traitmod += (ratio.Race.GetAbilityValue("Planetary SY Rate").ToDouble() / 100d) * ratio.Ratio; - rate *= traitmod; - - // apply aptitude modifier - if (IsSpaceYardQueue) - { - double aptmod = 0d; - foreach (var ratio in ratios) - aptmod += ((ratio.Race.Aptitudes[Aptitude.Construction.Name] / 100d)) * ratio.Ratio; - rate *= aptmod; - - // apply culture modifier - rate *= (100d + (Owner?.Culture?.Construction ?? 0)) / 100d; - } - } - if (rate == null) - rate = new ResourceQuantity(); - if (Container is IVehicle) - { - // apply aptitude modifier for empire's primary race - rate *= Owner.PrimaryRace.Aptitudes[Aptitude.Construction.Name] / 100d; - } - - return rate; - } - - private ResourceQuantity ComputeSYAbilityRate() - { - if (Container.HasAbility("Space Yard")) - { - var rate = new ResourceQuantity(); - // TODO - moddable resources? - for (int i = 1; i <= 3; i++) - { - var amount = Container.GetAbilityValue("Space Yard", 2, true, true, a => a.Value1 == i.ToString()).ToInt(); - Resource res = null; - if (i == 1) - res = Resource.Minerals; - else if (i == 2) - res = Resource.Organics; - else if (i == 3) - res = Resource.Radioactives; - rate[res] = amount; - } - return rate; - } - else - return null; - } -} diff --git a/FrEee/Objects/Civilization/Culture.cs b/FrEee/Objects/Civilization/Culture.cs index c59676a5b..1248cbe22 100644 --- a/FrEee/Objects/Civilization/Culture.cs +++ b/FrEee/Objects/Civilization/Culture.cs @@ -1,5 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Modding.Interfaces; +using FrEee.Modding; +using FrEee.Objects.GameState; using FrEee.Utility; using System; using System.Collections.Generic; diff --git a/FrEee/Objects/Civilization/Diplomacy/AcceptProposalAction.cs b/FrEee/Objects/Civilization/Diplomacy/AcceptProposalAction.cs deleted file mode 100644 index 3883daf7b..000000000 --- a/FrEee/Objects/Civilization/Diplomacy/AcceptProposalAction.cs +++ /dev/null @@ -1,55 +0,0 @@ -using FrEee.Interfaces; -using FrEee.Serialization; -using FrEee.Extensions; -using System.Collections.Generic; - -namespace FrEee.Objects.Civilization.Diplomacy; - -/// -/// An action that accepts a proposal. -/// -/// -public class AcceptProposalAction : Action -{ - public AcceptProposalAction(Proposal proposal) - : base(proposal.Executor) - { - Proposal = proposal; - } - - public override string Description - { - get { return "Reject " + Proposal; } - } - - /// - /// The proposal in question. - /// - [DoNotSerialize] - public Proposal Proposal { get { return proposal; } set { proposal = value; } } - - private GalaxyReference proposal { get; set; } - - public override void Execute() - { - if (Proposal.IsResolved) - Executor.Log.Add(Target.CreateLogMessage("The proposal \"" + Proposal + "\" has already been resolved and cannot be accepted now.", LogMessages.LogMessageType.Error)); - else - { - Target.Log.Add(Executor.CreateLogMessage("The " + Executor + " has accepted our proposal (" + Proposal + ").", LogMessages.LogMessageType.Generic)); - Proposal.Execute(); - } - } - - public override void ReplaceClientIDs(IDictionary idmap, ISet done = null) - { - if (done == null) - done = new HashSet(); - if (!done.Contains(this)) - { - done.Add(this); - base.ReplaceClientIDs(idmap, done); - proposal.ReplaceClientIDs(idmap, done); - } - } -} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/Action.cs b/FrEee/Objects/Civilization/Diplomacy/Action.cs deleted file mode 100644 index 7fdf22e4c..000000000 --- a/FrEee/Objects/Civilization/Diplomacy/Action.cs +++ /dev/null @@ -1,26 +0,0 @@ -using FrEee.Objects.Commands; -using FrEee.Serialization; - -namespace FrEee.Objects.Civilization.Diplomacy; - -/// -/// A unilateral diplomatic action. -/// -public abstract class Action : Command -{ - protected Action(Empire target) - : base(Empire.Current) - { - Target = target; - } - - public abstract string Description { get; } - - /// - /// The empire that is the target of this action. - /// - [DoNotSerialize] - public Empire Target { get { return target; } set { target = value; } } - - private GalaxyReference target { get; set; } -} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/ActionMessage.cs b/FrEee/Objects/Civilization/Diplomacy/ActionMessage.cs deleted file mode 100644 index e1ce58ee4..000000000 --- a/FrEee/Objects/Civilization/Diplomacy/ActionMessage.cs +++ /dev/null @@ -1,44 +0,0 @@ -using FrEee.Interfaces; -using System; -using System.Collections.Generic; - -namespace FrEee.Objects.Civilization.Diplomacy; - -/// -/// A message specifying some unilateral action. -/// -public class ActionMessage : Message -{ - public ActionMessage(Empire recipient) - : base(recipient) - { - if (Recipient == Owner) - throw new Exception("You can't perform a diplomatic action on yourself!"); - } - - /// - /// The action in question. - /// - public Action Action { get; set; } - - public override IEnumerable IconPaths - { - get - { - return Owner.IconPaths; - } - } - - public override IEnumerable PortraitPaths - { - get - { - return Owner.PortraitPaths; - } - } - - public override void ReplaceClientIDs(IDictionary idmap, ISet done = null) - { - Action.ReplaceClientIDs(idmap); - } -} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/Actions/AcceptProposalAction.cs b/FrEee/Objects/Civilization/Diplomacy/Actions/AcceptProposalAction.cs new file mode 100644 index 000000000..0efcb57ab --- /dev/null +++ b/FrEee/Objects/Civilization/Diplomacy/Actions/AcceptProposalAction.cs @@ -0,0 +1,55 @@ +using FrEee.Serialization; +using FrEee.Extensions; +using System.Collections.Generic; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.Diplomacy.Actions; + +/// +/// An action that accepts a proposal. +/// +/// +public class AcceptProposalAction : DiplomaticAction +{ + public AcceptProposalAction(Proposal proposal) + : base(proposal.Executor) + { + Proposal = proposal; + } + + public override string Description + { + get { return "Reject " + Proposal; } + } + + /// + /// The proposal in question. + /// + [DoNotSerialize] + public Proposal Proposal { get { return proposal; } set { proposal = value; } } + + private GalaxyReference proposal { get; set; } + + public override void Execute() + { + if (Proposal.IsResolved) + Executor.Log.Add(Target.CreateLogMessage("The proposal \"" + Proposal + "\" has already been resolved and cannot be accepted now.", LogMessages.LogMessageType.Error)); + else + { + Target.Log.Add(Executor.CreateLogMessage("The " + Executor + " has accepted our proposal (" + Proposal + ").", LogMessages.LogMessageType.Generic)); + Proposal.Execute(); + } + } + + public override void ReplaceClientIDs(IDictionary idmap, ISet done = null) + { + if (done == null) + done = new HashSet(); + if (!done.Contains(this)) + { + done.Add(this); + base.ReplaceClientIDs(idmap, done); + proposal.ReplaceClientIDs(idmap, done); + } + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/Actions/BreakTreatyAction.cs b/FrEee/Objects/Civilization/Diplomacy/Actions/BreakTreatyAction.cs new file mode 100644 index 000000000..4d5208820 --- /dev/null +++ b/FrEee/Objects/Civilization/Diplomacy/Actions/BreakTreatyAction.cs @@ -0,0 +1,31 @@ +using FrEee.Extensions; + +namespace FrEee.Objects.Civilization.Diplomacy.Actions; + +/// +/// Breaks a treaty with the target empire. +/// +public class BreakTreatyAction : DiplomaticAction +{ + public BreakTreatyAction(Empire target) + : base(target) + { + } + + public override string Description + { + get { return "Break Treaty"; } + } + + public override void Execute() + { + foreach (var clause in Executor.GetTreaty(Target)) + clause.Dispose(); + + Executor.Log.Add(Target.CreateLogMessage("We have broken our treaty with the " + Target + ".", LogMessages.LogMessageType.Generic)); + Target.Log.Add(Executor.CreateLogMessage("The " + Target + " has broken its treaty with us.", LogMessages.LogMessageType.Generic)); + + Executor.TriggerHappinessChange(hm => hm.TreatyNone); + Target.TriggerHappinessChange(hm => hm.TreatyNone); + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/Actions/DeclareWarAction.cs b/FrEee/Objects/Civilization/Diplomacy/Actions/DeclareWarAction.cs new file mode 100644 index 000000000..dff122a60 --- /dev/null +++ b/FrEee/Objects/Civilization/Diplomacy/Actions/DeclareWarAction.cs @@ -0,0 +1,28 @@ +using FrEee.Extensions; + +namespace FrEee.Objects.Civilization.Diplomacy.Actions; + +/// +/// Declares war on the target empire. +/// +public class DeclareWarAction : DiplomaticAction +{ + public DeclareWarAction(Empire target) + : base(target) + { + } + + public override string Description + { + get { return "Declare War"; } + } + + public override void Execute() + { + foreach (var clause in Executor.GetTreaty(Target)) + clause.Dispose(); + // TODO - some sort of formal war state + Executor.Log.Add(Target.CreateLogMessage("We have declared war on the " + Target + ".", LogMessages.LogMessageType.Diplomacy)); + Target.Log.Add(Executor.CreateLogMessage("The " + Executor + " has declared war on us!", LogMessages.LogMessageType.Diplomacy)); + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/Actions/DiplomaticAction.cs b/FrEee/Objects/Civilization/Diplomacy/Actions/DiplomaticAction.cs new file mode 100644 index 000000000..bf049eb05 --- /dev/null +++ b/FrEee/Objects/Civilization/Diplomacy/Actions/DiplomaticAction.cs @@ -0,0 +1,26 @@ +using FrEee.Objects.Commands; +using FrEee.Serialization; + +namespace FrEee.Objects.Civilization.Diplomacy.Actions; + +/// +/// A unilateral diplomatic action. +/// +public abstract class DiplomaticAction : Command +{ + protected DiplomaticAction(Empire target) + : base(Empire.Current) + { + Target = target; + } + + public abstract string Description { get; } + + /// + /// The empire that is the target of this action. + /// + [DoNotSerialize] + public Empire Target { get { return target; } set { target = value; } } + + private GalaxyReference target { get; set; } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/Actions/RejectProposalAction.cs b/FrEee/Objects/Civilization/Diplomacy/Actions/RejectProposalAction.cs new file mode 100644 index 000000000..7f701245c --- /dev/null +++ b/FrEee/Objects/Civilization/Diplomacy/Actions/RejectProposalAction.cs @@ -0,0 +1,52 @@ +using FrEee.Serialization; +using FrEee.Extensions; +using System.Collections.Generic; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.Diplomacy.Actions; + +/// +/// An action that rejects a proposal. +/// +/// +public class RejectProposalAction : DiplomaticAction +{ + public RejectProposalAction(Proposal proposal) + : base(proposal.Executor) + { + Proposal = proposal; + } + + public override string Description + { + get { return "Reject " + Proposal; } + } + + /// + /// The proposal in question. + /// + [DoNotSerialize] + public Proposal Proposal { get { return proposal; } set { proposal = value; } } + + private GalaxyReference proposal { get; set; } + + public override void Execute() + { + if (Proposal.IsResolved) + Executor.Log.Add(Target.CreateLogMessage("The proposal \"" + Proposal + "\" has already been resolved and cannot be rejected now.", LogMessages.LogMessageType.Error)); + else + Target.Log.Add(Executor.CreateLogMessage("The " + Executor + " has rejected our proposal (" + Proposal + ").", LogMessages.LogMessageType.Diplomacy)); + } + + public override void ReplaceClientIDs(IDictionary idmap, ISet done = null) + { + if (done == null) + done = new HashSet(); + base.ReplaceClientIDs(idmap, done); + if (!done.Contains(this)) + { + done.Add(this); + proposal.ReplaceClientIDs(idmap, done); + } + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/AllianceLevel.cs b/FrEee/Objects/Civilization/Diplomacy/AllianceLevel.cs new file mode 100644 index 000000000..6aa70cdfd --- /dev/null +++ b/FrEee/Objects/Civilization/Diplomacy/AllianceLevel.cs @@ -0,0 +1,39 @@ +using FrEee.Utility; +namespace FrEee.Objects.Civilization.Diplomacy; + +/// +/// A level of alliance. +/// +public enum AllianceLevel +{ + /// + /// No alliance. The empire will be attacked by the other empire. + /// + None = 0, + + /// + /// The receiver will not be attacked by the giver, except in systems where the giver has a colony. + /// + [CanonicalName("Neutral Zone")] + NeutralZone = 1, + + /// + /// The receiver will not be attacked by the giver. + /// + [CanonicalName("Non-Aggression")] + [Name("Non Aggression")] + [Name("Nonaggression")] + NonAggression = 2, + + /// + /// Same as Non-Aggression, but the giver will also declare war on any empire that declares war on the receiver. + /// + [CanonicalName("Defensive Pact")] + DefensivePact = 3, + + /// + /// Same as Defensive Pact, but the giver will also declare war on any empire that the receiver declares war on. + /// + [CanonicalName("Military Alliance")] + MilitaryAlliance = 4, +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/AllowedTrades.cs b/FrEee/Objects/Civilization/Diplomacy/AllowedTrades.cs new file mode 100644 index 000000000..53a01164b --- /dev/null +++ b/FrEee/Objects/Civilization/Diplomacy/AllowedTrades.cs @@ -0,0 +1,21 @@ +using FrEee.Utility; +namespace FrEee.Objects.Civilization.Diplomacy; + +public enum AllowedTrades +{ + /// + /// No trades or gifts are allowed. + /// + None = 0, + + /// + /// Anything except technology can be traded or gifted. + /// + [CanonicalName("All But Technology")] + AllButTechnology = 1, + + /// + /// Anything, including technology, can be traded or gifted. + /// + All = 2 +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/BreakTreatyAction.cs b/FrEee/Objects/Civilization/Diplomacy/BreakTreatyAction.cs deleted file mode 100644 index 30a856e83..000000000 --- a/FrEee/Objects/Civilization/Diplomacy/BreakTreatyAction.cs +++ /dev/null @@ -1,31 +0,0 @@ -using FrEee.Extensions; - -namespace FrEee.Objects.Civilization.Diplomacy; - -/// -/// Breaks a treaty with the target empire. -/// -public class BreakTreatyAction : Action -{ - public BreakTreatyAction(Empire target) - : base(target) - { - } - - public override string Description - { - get { return "Break Treaty"; } - } - - public override void Execute() - { - foreach (var clause in Executor.GetTreaty(Target)) - clause.Dispose(); - - Executor.Log.Add(Target.CreateLogMessage("We have broken our treaty with the " + Target + ".", LogMessages.LogMessageType.Generic)); - Target.Log.Add(Executor.CreateLogMessage("The " + Target + " has broken its treaty with us.", LogMessages.LogMessageType.Generic)); - - Executor.TriggerHappinessChange(hm => hm.TreatyNone); - Target.TriggerHappinessChange(hm => hm.TreatyNone); - } -} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/Clauses/AllianceClause.cs b/FrEee/Objects/Civilization/Diplomacy/Clauses/AllianceClause.cs index daf7ecce8..d53f17589 100644 --- a/FrEee/Objects/Civilization/Diplomacy/Clauses/AllianceClause.cs +++ b/FrEee/Objects/Civilization/Diplomacy/Clauses/AllianceClause.cs @@ -1,8 +1,9 @@ -using FrEee.Enumerations; -using FrEee.Objects.Commands; +using FrEee.Objects.Commands; using FrEee.Extensions; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.Civilization.Diplomacy.Messages; +using FrEee.Objects.Civilization.Diplomacy.Actions; namespace FrEee.Objects.Civilization.Diplomacy.Clauses; diff --git a/FrEee/Objects/Civilization/Diplomacy/Clauses/Clause.cs b/FrEee/Objects/Civilization/Diplomacy/Clauses/Clause.cs index 9078bf2dc..244e53bd0 100644 --- a/FrEee/Objects/Civilization/Diplomacy/Clauses/Clause.cs +++ b/FrEee/Objects/Civilization/Diplomacy/Clauses/Clause.cs @@ -1,6 +1,4 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Space; +using FrEee.Objects.GameState; using FrEee.Serialization; using System.Collections.Generic; diff --git a/FrEee/Objects/Civilization/Diplomacy/Clauses/ShareAbilityClause.cs b/FrEee/Objects/Civilization/Diplomacy/Clauses/ShareAbilityClause.cs index bc3dd813e..26890f444 100644 --- a/FrEee/Objects/Civilization/Diplomacy/Clauses/ShareAbilityClause.cs +++ b/FrEee/Objects/Civilization/Diplomacy/Clauses/ShareAbilityClause.cs @@ -1,5 +1,4 @@ -using FrEee.Enumerations; -using FrEee.Objects.Abilities; +using FrEee.Objects.Abilities; using FrEee.Extensions; namespace FrEee.Objects.Civilization.Diplomacy.Clauses; diff --git a/FrEee/Objects/Civilization/Diplomacy/Clauses/SharingPriority.cs b/FrEee/Objects/Civilization/Diplomacy/Clauses/SharingPriority.cs new file mode 100644 index 000000000..9d7c03e28 --- /dev/null +++ b/FrEee/Objects/Civilization/Diplomacy/Clauses/SharingPriority.cs @@ -0,0 +1,29 @@ +namespace FrEee.Objects.Civilization.Diplomacy.Clauses; + +/// +/// Priority for sharing of abilities. +/// +public enum SharingPriority +{ + /// + /// Empires with no priority won't get the ability shared to them. + /// + None = 0, + + /// + /// Empires with low priority get last pick at the ability. + /// If high and medium priority empires used the ability up, low priority empires won't get any. + /// + Low = 1, + + /// + /// Empires with medium priority get a pick at the ability along with the owner of the ability. + /// If high priority empires used the ability up, medium priority empires won't get any. + /// + Medium = 2, + + /// + /// Empires with high priority get a pick at the ability prior to even the owner of the ability. + /// + High = 3 +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/DeclareWarAction.cs b/FrEee/Objects/Civilization/Diplomacy/DeclareWarAction.cs deleted file mode 100644 index d6d24b1fe..000000000 --- a/FrEee/Objects/Civilization/Diplomacy/DeclareWarAction.cs +++ /dev/null @@ -1,28 +0,0 @@ -using FrEee.Extensions; - -namespace FrEee.Objects.Civilization.Diplomacy; - -/// -/// Declares war on the target empire. -/// -public class DeclareWarAction : Action -{ - public DeclareWarAction(Empire target) - : base(target) - { - } - - public override string Description - { - get { return "Declare War"; } - } - - public override void Execute() - { - foreach (var clause in Executor.GetTreaty(Target)) - clause.Dispose(); - // TODO - some sort of formal war state - Executor.Log.Add(Target.CreateLogMessage("We have declared war on the " + Target + ".", LogMessages.LogMessageType.Diplomacy)); - Target.Log.Add(Executor.CreateLogMessage("The " + Executor + " has declared war on us!", LogMessages.LogMessageType.Diplomacy)); - } -} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/GeneralMessage.cs b/FrEee/Objects/Civilization/Diplomacy/GeneralMessage.cs deleted file mode 100644 index ca979ff07..000000000 --- a/FrEee/Objects/Civilization/Diplomacy/GeneralMessage.cs +++ /dev/null @@ -1,36 +0,0 @@ -using FrEee.Interfaces; -using System.Collections.Generic; - -namespace FrEee.Objects.Civilization.Diplomacy; - -/// -/// A general message with no parameters. Just text. -/// -public class GeneralMessage : Message -{ - public GeneralMessage(Empire recipient) - : base(recipient) - { - } - - public override IEnumerable IconPaths - { - get - { - return Owner.IconPaths; - } - } - - public override IEnumerable PortraitPaths - { - get - { - return Owner.PortraitPaths; - } - } - - public override void ReplaceClientIDs(IDictionary idmap, ISet done) - { - // nothing to do - } -} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/Message.cs b/FrEee/Objects/Civilization/Diplomacy/Message.cs deleted file mode 100644 index ca44c3347..000000000 --- a/FrEee/Objects/Civilization/Diplomacy/Message.cs +++ /dev/null @@ -1,134 +0,0 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Commands; -using FrEee.Objects.Space; -using FrEee.Serialization; -using FrEee.Extensions; -using System.Collections.Generic; -using System.Drawing; -using System.Linq; - -namespace FrEee.Objects.Civilization.Diplomacy; - -/// -/// A diplomatic message. -/// -public abstract class Message : IMessage -{ - protected Message(Empire recipient) - { - Owner = Empire.Current; - Recipient = recipient; - TurnNumber = Galaxy.Current.TurnNumber; - } - - public Image Icon - { - get - { - return Owner == Empire.Current ? Recipient.Icon : Owner.Icon; - } - } - - public Image Icon32 => Icon.Resize(32); - - public abstract IEnumerable IconPaths { get; } - - public long ID - { - get; - set; - } - - public IMessage InReplyTo { get; set; } - - public bool IsDisposed { get; set; } - - /// - /// Messages cannot be memories, as they do not change over time. - /// - public bool IsMemory - { - get; set; - } - - /// - /// The empire sending this message. - /// - [DoNotSerialize] - public Empire Owner { get { return owner; } set { owner = value; } } - - public Image Portrait - { - get - { - return Owner == Empire.Current ? Recipient.Portrait : Owner?.Portrait; - } - } - - public abstract IEnumerable PortraitPaths { get; } - - /// - /// The empire receiving the message. - /// - [DoNotSerialize] - public Empire Recipient { get { return recipient; } set { recipient = value; } } - - /// - /// The text of the message. - /// - public string Text { get; set; } - - public double Timestamp - { - get; set; - } - - public int TurnNumber { get; set; } - - private GalaxyReference owner { get; set; } - - private GalaxyReference recipient { get; set; } - - public Visibility CheckVisibility(Empire emp) - { - // TODO - intel to spy on foreign messages or disrupt communications - if (emp == Owner) - return Visibility.Owned; - if (emp == Recipient) - return Visibility.Scanned; - return Visibility.Unknown; - } - - public void Dispose() - { - if (IsDisposed) - return; - Galaxy.Current.UnassignID(this); - if (Owner != null) - { - // HACK - how could a diplomatic message have no owner? - var cmd = Owner.Commands.OfType().SingleOrDefault(c => c.Message == this); - if (cmd != null) - Owner.Commands.Remove(cmd); - } - } - - /// - /// Messages cannot be memories, as they do not change over time. - /// - public bool IsObsoleteMemory(Empire emp) - { - return false; - } - - public void Redact(Empire emp) - { - // TODO - partial espionage of messages? - // e.g. you could tell that a type of message was sent but not the text or parameters - if (CheckVisibility(emp) < Visibility.Fogged) - Dispose(); - } - - public abstract void ReplaceClientIDs(IDictionary idmap, ISet done = null); -} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/Messages/ActionMessage.cs b/FrEee/Objects/Civilization/Diplomacy/Messages/ActionMessage.cs new file mode 100644 index 000000000..cbcb45b1b --- /dev/null +++ b/FrEee/Objects/Civilization/Diplomacy/Messages/ActionMessage.cs @@ -0,0 +1,45 @@ +using FrEee.Objects.Civilization.Diplomacy.Actions; +using FrEee.Objects.GameState; +using System; +using System.Collections.Generic; + +namespace FrEee.Objects.Civilization.Diplomacy.Messages; + +/// +/// A message specifying some unilateral action. +/// +public class ActionMessage : Message +{ + public ActionMessage(Empire recipient) + : base(recipient) + { + if (Recipient == Owner) + throw new Exception("You can't perform a diplomatic action on yourself!"); + } + + /// + /// The action in question. + /// + public DiplomaticAction Action { get; set; } + + public override IEnumerable IconPaths + { + get + { + return Owner.IconPaths; + } + } + + public override IEnumerable PortraitPaths + { + get + { + return Owner.PortraitPaths; + } + } + + public override void ReplaceClientIDs(IDictionary idmap, ISet done = null) + { + Action.ReplaceClientIDs(idmap); + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/Messages/GeneralMessage.cs b/FrEee/Objects/Civilization/Diplomacy/Messages/GeneralMessage.cs new file mode 100644 index 000000000..f2ba26a00 --- /dev/null +++ b/FrEee/Objects/Civilization/Diplomacy/Messages/GeneralMessage.cs @@ -0,0 +1,36 @@ +using FrEee.Objects.GameState; +using System.Collections.Generic; + +namespace FrEee.Objects.Civilization.Diplomacy.Messages; + +/// +/// A general message with no parameters. Just text. +/// +public class GeneralMessage : Message +{ + public GeneralMessage(Empire recipient) + : base(recipient) + { + } + + public override IEnumerable IconPaths + { + get + { + return Owner.IconPaths; + } + } + + public override IEnumerable PortraitPaths + { + get + { + return Owner.PortraitPaths; + } + } + + public override void ReplaceClientIDs(IDictionary idmap, ISet done) + { + // nothing to do + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/Messages/IMessage.cs b/FrEee/Objects/Civilization/Diplomacy/Messages/IMessage.cs new file mode 100644 index 000000000..e546a4c82 --- /dev/null +++ b/FrEee/Objects/Civilization/Diplomacy/Messages/IMessage.cs @@ -0,0 +1,15 @@ +using FrEee.Objects.GameState; +using FrEee.Serialization; + +namespace FrEee.Objects.Civilization.Diplomacy.Messages; + +/// +/// A diplomatic message. +/// +public interface IMessage : IFoggable, IPictorial, IPromotable, IReferrable +{ + IMessage InReplyTo { get; set; } + Empire Recipient { get; set; } + string Text { get; set; } + int TurnNumber { get; set; } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/Messages/Message.cs b/FrEee/Objects/Civilization/Diplomacy/Messages/Message.cs new file mode 100644 index 000000000..a627c60d3 --- /dev/null +++ b/FrEee/Objects/Civilization/Diplomacy/Messages/Message.cs @@ -0,0 +1,132 @@ +using FrEee.Objects.Commands; +using FrEee.Serialization; +using FrEee.Extensions; +using System.Collections.Generic; +using System.Drawing; +using System.Linq; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.Diplomacy.Messages; + +/// +/// A diplomatic message. +/// +public abstract class Message : IMessage +{ + protected Message(Empire recipient) + { + Owner = Empire.Current; + Recipient = recipient; + TurnNumber = Galaxy.Current.TurnNumber; + } + + public Image Icon + { + get + { + return Owner == Empire.Current ? Recipient.Icon : Owner.Icon; + } + } + + public Image Icon32 => Icon.Resize(32); + + public abstract IEnumerable IconPaths { get; } + + public long ID + { + get; + set; + } + + public IMessage InReplyTo { get; set; } + + public bool IsDisposed { get; set; } + + /// + /// Messages cannot be memories, as they do not change over time. + /// + public bool IsMemory + { + get; set; + } + + /// + /// The empire sending this message. + /// + [DoNotSerialize] + public Empire Owner { get { return owner; } set { owner = value; } } + + public Image Portrait + { + get + { + return Owner == Empire.Current ? Recipient.Portrait : Owner?.Portrait; + } + } + + public abstract IEnumerable PortraitPaths { get; } + + /// + /// The empire receiving the message. + /// + [DoNotSerialize] + public Empire Recipient { get { return recipient; } set { recipient = value; } } + + /// + /// The text of the message. + /// + public string Text { get; set; } + + public double Timestamp + { + get; set; + } + + public int TurnNumber { get; set; } + + private GalaxyReference owner { get; set; } + + private GalaxyReference recipient { get; set; } + + public Visibility CheckVisibility(Empire emp) + { + // TODO - intel to spy on foreign messages or disrupt communications + if (emp == Owner) + return Visibility.Owned; + if (emp == Recipient) + return Visibility.Scanned; + return Visibility.Unknown; + } + + public void Dispose() + { + if (IsDisposed) + return; + Galaxy.Current.UnassignID(this); + if (Owner != null) + { + // HACK - how could a diplomatic message have no owner? + var cmd = Owner.Commands.OfType().SingleOrDefault(c => c.Message == this); + if (cmd != null) + Owner.Commands.Remove(cmd); + } + } + + /// + /// Messages cannot be memories, as they do not change over time. + /// + public bool IsObsoleteMemory(Empire emp) + { + return false; + } + + public void Redact(Empire emp) + { + // TODO - partial espionage of messages? + // e.g. you could tell that a type of message was sent but not the text or parameters + if (CheckVisibility(emp) < Visibility.Fogged) + Dispose(); + } + + public abstract void ReplaceClientIDs(IDictionary idmap, ISet done = null); +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/Messages/ProposalMessage.cs b/FrEee/Objects/Civilization/Diplomacy/Messages/ProposalMessage.cs new file mode 100644 index 000000000..6c7dabb05 --- /dev/null +++ b/FrEee/Objects/Civilization/Diplomacy/Messages/ProposalMessage.cs @@ -0,0 +1,52 @@ +using FrEee.Objects.GameState; +using System; +using System.Collections.Generic; + +namespace FrEee.Objects.Civilization.Diplomacy.Messages; + +/// +/// A message with a proposal that can be accepted or rejected. +/// +/// +public class ProposalMessage : Message +{ + public ProposalMessage(Empire recipient) + : base(recipient) + { + if (Recipient == Owner) + throw new Exception("You can't send a diplomatic proposal to yourself!"); + Proposal = new Proposal(recipient); + } + + public override IEnumerable IconPaths + { + get + { + return Proposal.Owner.IconPaths; + } + } + + public override IEnumerable PortraitPaths + { + get + { + return Proposal.Owner.PortraitPaths; + } + } + + /// + /// The proposal in question. + /// + public Proposal Proposal { get; set; } + + public override void ReplaceClientIDs(IDictionary idmap, ISet done = null) + { + if (done == null) + done = new HashSet(); + if (!done.Contains(this)) + { + done.Add(this); + Proposal.ReplaceClientIDs(idmap, done); + } + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/Package.cs b/FrEee/Objects/Civilization/Diplomacy/Package.cs index 2c304a38c..01a4a6e40 100644 --- a/FrEee/Objects/Civilization/Diplomacy/Package.cs +++ b/FrEee/Objects/Civilization/Diplomacy/Package.cs @@ -1,6 +1,4 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization.Diplomacy.Clauses; +using FrEee.Objects.Civilization.Diplomacy.Clauses; using FrEee.Objects.Space; using FrEee.Utility; using FrEee.Serialization; @@ -9,6 +7,8 @@ using System.Collections.Generic; using System.Linq; using Tech = FrEee.Objects.Technology.Technology; +using FrEee.Objects.Vehicles; +using FrEee.Objects.GameState; namespace FrEee.Objects.Civilization.Diplomacy; diff --git a/FrEee/Objects/Civilization/Diplomacy/Proposal.cs b/FrEee/Objects/Civilization/Diplomacy/Proposal.cs index a1dce3b14..8e51a350e 100644 --- a/FrEee/Objects/Civilization/Diplomacy/Proposal.cs +++ b/FrEee/Objects/Civilization/Diplomacy/Proposal.cs @@ -1,11 +1,9 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Commands; -using FrEee.Objects.Space; +using FrEee.Objects.Commands; using FrEee.Serialization; using FrEee.Extensions; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Objects.Civilization.Diplomacy; diff --git a/FrEee/Objects/Civilization/Diplomacy/ProposalMessage.cs b/FrEee/Objects/Civilization/Diplomacy/ProposalMessage.cs deleted file mode 100644 index aac34a41f..000000000 --- a/FrEee/Objects/Civilization/Diplomacy/ProposalMessage.cs +++ /dev/null @@ -1,52 +0,0 @@ -using FrEee.Interfaces; -using System; -using System.Collections.Generic; - -namespace FrEee.Objects.Civilization.Diplomacy; - -/// -/// A message with a proposal that can be accepted or rejected. -/// -/// -public class ProposalMessage : Message -{ - public ProposalMessage(Empire recipient) - : base(recipient) - { - if (Recipient == Owner) - throw new Exception("You can't send a diplomatic proposal to yourself!"); - Proposal = new Proposal(recipient); - } - - public override IEnumerable IconPaths - { - get - { - return Proposal.Owner.IconPaths; - } - } - - public override IEnumerable PortraitPaths - { - get - { - return Proposal.Owner.PortraitPaths; - } - } - - /// - /// The proposal in question. - /// - public Proposal Proposal { get; set; } - - public override void ReplaceClientIDs(IDictionary idmap, ISet done = null) - { - if (done == null) - done = new HashSet(); - if (!done.Contains(this)) - { - done.Add(this); - Proposal.ReplaceClientIDs(idmap, done); - } - } -} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/RejectProposalAction.cs b/FrEee/Objects/Civilization/Diplomacy/RejectProposalAction.cs deleted file mode 100644 index 40ee4d53b..000000000 --- a/FrEee/Objects/Civilization/Diplomacy/RejectProposalAction.cs +++ /dev/null @@ -1,52 +0,0 @@ -using FrEee.Interfaces; -using FrEee.Serialization; -using FrEee.Extensions; -using System.Collections.Generic; - -namespace FrEee.Objects.Civilization.Diplomacy; - -/// -/// An action that rejects a proposal. -/// -/// -public class RejectProposalAction : Action -{ - public RejectProposalAction(Proposal proposal) - : base(proposal.Executor) - { - Proposal = proposal; - } - - public override string Description - { - get { return "Reject " + Proposal; } - } - - /// - /// The proposal in question. - /// - [DoNotSerialize] - public Proposal Proposal { get { return proposal; } set { proposal = value; } } - - private GalaxyReference proposal { get; set; } - - public override void Execute() - { - if (Proposal.IsResolved) - Executor.Log.Add(Target.CreateLogMessage("The proposal \"" + Proposal + "\" has already been resolved and cannot be rejected now.", LogMessages.LogMessageType.Error)); - else - Target.Log.Add(Executor.CreateLogMessage("The " + Executor + " has rejected our proposal (" + Proposal + ").", LogMessages.LogMessageType.Diplomacy)); - } - - public override void ReplaceClientIDs(IDictionary idmap, ISet done = null) - { - if (done == null) - done = new HashSet(); - base.ReplaceClientIDs(idmap, done); - if (!done.Contains(this)) - { - done.Add(this); - proposal.ReplaceClientIDs(idmap, done); - } - } -} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Diplomacy/Relations.cs b/FrEee/Objects/Civilization/Diplomacy/Relations.cs new file mode 100644 index 000000000..01090784b --- /dev/null +++ b/FrEee/Objects/Civilization/Diplomacy/Relations.cs @@ -0,0 +1,35 @@ +namespace FrEee.Objects.Civilization.Diplomacy; + +/// +/// Diplomatic relations from one empire to another. +/// Note that diplomatic relations are not necessarily mutual! +/// Also relations may vary by star system if the empires have a neutral zone treaty. +/// +public enum Relations +{ + /// + /// This empire has not encountered the other empire. + /// + Unknown = 0, + + /// + /// This empire is hostile toward the other empire and will attack on sight. + /// + Hostile = 1, + + /// + /// This empire has no formal relations toward the other empire and will only fight if provoked. + /// TODO - actually implement this, right now neutral empires are treated as hostile + /// + Neutral = 2, + + /// + /// This empire is allied with the other empire and will not attack it. + /// + Allied = 3, + + /// + /// This empire *is* the other empire. + /// + Self = 4, +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Empire.cs b/FrEee/Objects/Civilization/Empire.cs index 3fe851d17..89e5c32a3 100644 --- a/FrEee/Objects/Civilization/Empire.cs +++ b/FrEee/Objects/Civilization/Empire.cs @@ -1,15 +1,11 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.AI; using FrEee.Objects.Civilization.Diplomacy.Clauses; using FrEee.Objects.Commands; using FrEee.Objects.LogMessages; -using FrEee.Objects.Orders; using FrEee.Objects.Space; using FrEee.Objects.Vehicles; using FrEee.Modding; -using FrEee.Modding.Interfaces; using FrEee.Utility; using FrEee.Serialization; using FrEee.Extensions; @@ -20,6 +16,12 @@ using System.Linq; using System.Reflection; using Tech = FrEee.Objects.Technology.Technology; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.Civilization.Diplomacy.Messages; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.GameState; +using FrEee.Objects.Civilization.Diplomacy; +using FrEee.Setup; namespace FrEee.Objects.Civilization; diff --git a/FrEee/Objects/Civilization/EmpireStatus.cs b/FrEee/Objects/Civilization/EmpireStatus.cs index f1f5562c0..85097f3c6 100644 --- a/FrEee/Objects/Civilization/EmpireStatus.cs +++ b/FrEee/Objects/Civilization/EmpireStatus.cs @@ -1,4 +1,4 @@ -using FrEee.Objects.Space; +using FrEee.Objects.GameState; using System.Drawing; using System.IO; diff --git a/FrEee/Objects/Civilization/HappinessModel.cs b/FrEee/Objects/Civilization/HappinessModel.cs index 13e47c447..4a91e8c0a 100644 --- a/FrEee/Objects/Civilization/HappinessModel.cs +++ b/FrEee/Objects/Civilization/HappinessModel.cs @@ -1,5 +1,4 @@ using FrEee.Modding; -using FrEee.Modding.Interfaces; using System.Collections.Generic; namespace FrEee.Objects.Civilization; diff --git a/FrEee/Objects/Civilization/IIncomeProducer.cs b/FrEee/Objects/Civilization/IIncomeProducer.cs new file mode 100644 index 000000000..1b7940318 --- /dev/null +++ b/FrEee/Objects/Civilization/IIncomeProducer.cs @@ -0,0 +1,34 @@ +using FrEee.Objects.Abilities; +using FrEee.Objects.Space; +using FrEee.Utility; +namespace FrEee.Objects.Civilization; + +/// +/// Something which can produce income for an empire. +/// +public interface IIncomeProducer : IOwnableAbilityObject, ILocated +{ + /// + /// Ratio of income from this object that occurs even without a spaceport. + /// 1.0 = all of it + /// 0.5 = half of it + /// 0.0 = none of it + /// + double MerchantsRatio { get; } + + /// + /// Modifiers to remote mining due to racial aptitudes and the like. + /// + ResourceQuantity RemoteMiningIncomePercentages { get; } + + /// + /// Value for mined resources. + /// + ResourceQuantity ResourceValue { get; } + + /// + /// Modifiers to standard income due to population, happiness, racial aptitudes, lack of spaceport, etc. + /// 100% = normal income + /// + ResourceQuantity StandardIncomePercentages { get; } +} \ No newline at end of file diff --git a/FrEee/Interfaces/INameable.cs b/FrEee/Objects/Civilization/INameable.cs similarity index 64% rename from FrEee/Interfaces/INameable.cs rename to FrEee/Objects/Civilization/INameable.cs index d225d55a1..33748c714 100644 --- a/FrEee/Interfaces/INameable.cs +++ b/FrEee/Objects/Civilization/INameable.cs @@ -1,4 +1,7 @@ -namespace FrEee.Interfaces; +using FrEee.Objects.GameState; +using FrEee.Serialization; + +namespace FrEee.Objects.Civilization; /// /// Something which can be named by the player who owns it. @@ -6,5 +9,5 @@ /// public interface INameable : IOwnable, IReferrable, INamed { - new string Name { get; set; } + new string Name { get; set; } } \ No newline at end of file diff --git a/FrEee/Objects/Civilization/IOrderable.cs b/FrEee/Objects/Civilization/IOrderable.cs new file mode 100644 index 000000000..f75895a4f --- /dev/null +++ b/FrEee/Objects/Civilization/IOrderable.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; +using FrEee.Objects.Civilization.Orders; +using FrEee.Serialization; + +namespace FrEee.Objects.Civilization; + +/// +/// Something which can accept orders from an empire and queue them for execution over time. +/// +public interface IOrderable : IReferrable +{ + bool AreOrdersOnHold { get; set; } + + bool AreRepeatOrdersEnabled { get; set; } + + /// + /// The queued orders. + /// + IEnumerable Orders { get; } + + void AddOrder(IOrder order); + + /// + /// Executes orders for an appropriate amount of time. + /// Some objects execute orders for an entire turn at once; others only for smaller ticks. + /// + /// true if there was anything to do this turn + bool ExecuteOrders(); + + void RearrangeOrder(IOrder order, int delta); + + void RemoveOrder(IOrder order); +} diff --git a/FrEee/Interfaces/IOwnable.cs b/FrEee/Objects/Civilization/IOwnable.cs similarity index 66% rename from FrEee/Interfaces/IOwnable.cs rename to FrEee/Objects/Civilization/IOwnable.cs index 6505b4604..27c1daac5 100644 --- a/FrEee/Interfaces/IOwnable.cs +++ b/FrEee/Objects/Civilization/IOwnable.cs @@ -1,15 +1,14 @@ -using FrEee.Objects.Civilization; using FrEee.Serialization; -namespace FrEee.Interfaces; +namespace FrEee.Objects.Civilization; /// /// Something which can be owned by an empire. /// public interface IOwnable { - [DoNotCopy] - Empire Owner { get; } + [DoNotCopy] + Empire Owner { get; } } /// @@ -17,5 +16,5 @@ public interface IOwnable /// public interface ITransferrable : IOwnable { - new Empire Owner { get; set; } + new Empire Owner { get; set; } } diff --git a/FrEee/Objects/Civilization/Mood.cs b/FrEee/Objects/Civilization/Mood.cs new file mode 100644 index 000000000..f128e67e6 --- /dev/null +++ b/FrEee/Objects/Civilization/Mood.cs @@ -0,0 +1,16 @@ +namespace FrEee.Objects.Civilization; + +/// +/// Population mood. +/// +public enum Mood +{ + Rioting = -3, + Riot = Rioting, + Angry = -2, + Unhappy = -1, + Indifferent = 0, + Happy = 1, + Jubilant = 2, + Emotionless = 3, +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/ActivateAbilityOrder.cs b/FrEee/Objects/Civilization/Orders/ActivateAbilityOrder.cs new file mode 100644 index 000000000..8a4213a9c --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/ActivateAbilityOrder.cs @@ -0,0 +1,321 @@ +using FrEee.Objects.Abilities; +using FrEee.Objects.LogMessages; +using FrEee.Objects.Space; +using FrEee.Objects.Vehicles; +using FrEee.Modding; +using FrEee.Serialization; +using FrEee.Extensions; +using System.Collections.Generic; +using System.Linq; +using FrEee.Objects.Technology; +using FrEee.Objects.Combat; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.Orders; + +/// +/// An order to activate some sort of ability, like self-destruct or create a star. +/// +public class ActivateAbilityOrder : IOrder +{ + public ActivateAbilityOrder(IReferrableAbilityObject source, Ability ability, IReferrable target) + { + Owner = Empire.Current; + Source = source; + Ability = ability; + Target = target; + } + + /// + /// What ability to activate? + /// + [DoNotSerialize] + public Ability Ability { get { return ability.Value; } set { ability = value.ReferViaGalaxy(); } } + + public bool ConsumesMovement + { + get { return false; } + } + + public long ID + { + get; + set; + } + + public bool IsComplete + { + get; + set; + } + + public bool IsDisposed + { + get; + set; + } + + /// + /// The empire which issued the order. + /// + [DoNotSerialize] + public Empire Owner { get { return owner; } set { owner = value; } } + + /// + /// The source of the ability. Probably a component, facility, or hull. + /// TODO - fix this now that Component/Facility/Hull are no longer referrable + /// + [DoNotSerialize] + public IReferrableAbilityObject Source { get { return source.Value; } set { source = value.ReferViaGalaxy(); } } + + /// + /// What are we activating the ability "against"? Like, what warp point are we destroying, or whatever? Or null if there's no relevant target + /// + [DoNotSerialize] + public IReferrable Target { get { return target.Value; } set { target = value.ReferViaGalaxy(); } } + + private GalaxyReference ability { get; set; } + private GalaxyReference owner { get; set; } + private GalaxyReference source { get; set; } + private GalaxyReference target { get; set; } + + public bool CheckCompletion(IOrderable executor) + { + return IsComplete; + } + + public void Dispose() + { + if (IsDisposed) + return; + foreach (var sobj in Galaxy.Current.Referrables.OfType()) + sobj.RemoveOrder(this); + Galaxy.Current.UnassignID(this); + } + + public void Execute(IOrderable ord) + { + if (ord is IMobileSpaceObject executor) + { + // error checking + var errors = GetErrors(executor); + if (executor.Owner != null) + { + foreach (var error in errors) + executor.Owner.Log.Add(error); + } + + if (!errors.Any()) + { + // TODO - custom activatable abilities using scripts + if (Ability.Rule.Matches("Emergency Resupply")) + { + executor.SupplyRemaining += Ability.Value1.Value.ToInt(); + // TODO - normalize supplies on other stuff, not just space vehicles + if (executor is SpaceVehicle) + (executor as SpaceVehicle).NormalizeSupplies(); + Owner.RecordLog(executor, executor + " has activated its emergency resupply pod and now has " + executor.SupplyRemaining.ToUnitString(true) + " supplies.", LogMessageType.Generic); + } + else if (Ability.Rule.Matches("Emergency Energy")) + { + if (executor is Vehicle) + { + var v = executor as Vehicle; + v.EmergencySpeed += Ability.Value1.Value.ToInt(); + Owner.RecordLog(executor, executor + " has activated its emergency propulsion and has had its speed boosted to " + v.StrategicSpeed + " temporarily.", LogMessageType.Generic); + } + else + { + Owner.RecordLog(executor, executor + " cannot activate emergency propulsion because it is not a vehicle.", LogMessageType.Error); + return; + } + } + else if (Ability.Rule.Matches("Self-Destruct")) + { + // destroy ship/planet/whatever BOOM! + // TODO - make sure units with self destruct ability don't allow a ship or planet carrying them in cargo to self destruct... + Owner.RecordLog(executor, executor + " has successfully self-destructed.", LogMessageType.Generic); + executor.DisposeAndLog("The {0}'s {2} has apparently self-destructed.".F(Owner, executor), Owner); + } + else if (Ability.Rule.Matches("Open Warp Point")) + { + var fromSector = executor.Sector; + var fromSys = executor.StarSystem; + if (fromSector == null || fromSys == null) + { + Owner.RecordLog(executor, executor + " cannot open a warp point because it is not located in space.", LogMessageType.Warning); + return; + } + var toSys = Target as StarSystem; + if (toSys == null && Target is ILocated) + toSys = (Target as ILocated).StarSystem; + if (toSys == null) + { + Owner.RecordLog(executor, executor + " cannot open a warp point because no target system is specified.", LogMessageType.Error); + return; + } + if (fromSys.Coordinates.EightWayDistance(toSys.Coordinates) > Ability.Value1.Value.ToInt()) + { + Owner.RecordLog(executor, executor + " cannot open a warp point to " + toSys + " because " + toSys + " is too far away.", LogMessageType.Warning); + return; + } + // TODO - limit to one warp point between any two systems? + if (Ability.BurnSupplies()) + { + // find suitable warp point templates + var wpt1 = Mod.Current.StellarObjectTemplates.OfType().Where(wp => !wp.IsUnusual).PickRandom(); + var wpt2 = Mod.Current.StellarObjectTemplates.OfType().Where(wp => !wp.IsUnusual).PickRandom(); + + // figure out where the warp point goes, according to our game setup's warp point placement strategy + // only in the target system - in the source system we get a warp point at the sector where the WP opener was + var toSector = Galaxy.Current.WarpPointPlacementStrategy.GetWarpPointSector(fromSys.Location, toSys.Location); + + // create the warp points + var wp1 = wpt1.Instantiate(); + var wp2 = wpt2.Instantiate(); + + // configure the warp points + wp1.IsOneWay = false; + wp1.Name = "Warp Point to " + toSys; + wp1.Target = toSector; + fromSector.Place(wp1); + wp2.IsOneWay = false; + wp2.Name = "Warp Point to " + fromSys; + wp2.Target = fromSector; + toSector.Place(wp2); + + // let empires know that warp points were created + Owner.RecordLog(wp1, "{0} has opened a warp point connecting {1} to {2}.".F(executor, fromSys, toSys), LogMessageType.Generic); + wp1.UpdateEmpireMemories("A new warp point to {0} has been created by the {1} in the {2} system.".F(toSys, Owner, fromSys), Owner); + wp2.UpdateEmpireMemories("A new warp point to {0} has appeared in the {1} system.".F(fromSys, toSys), Owner); + } + else + { + Owner.RecordLog(executor, executor + " cannot open a warp point because it lacks the necessary supplies.", LogMessageType.Warning); + return; + } + } + else if (Ability.Rule.Matches("Close Warp Point")) + { + var wp = Target as WarpPoint; + + // sanity checking, make sure we have a valid warp point to close here + if (wp == null) + { + Owner.RecordLog(executor, executor + " cannot close a warp point because no warp point is specified to close.", LogMessageType.Error); + return; + } + if (wp.Sector != executor.Sector) + { + Owner.RecordLog(executor, executor + " cannot close " + wp + " because " + wp + " is not in the same sector as " + executor + ".", LogMessageType.Warning); + return; + } + + // need to close warp points on the other side too + var otherwps = wp.Target.SpaceObjects.OfType().Where(w => w.Target == wp.Sector); + + // check for supplies and close WP + if (Ability.BurnSupplies()) + { + Owner.RecordLog(wp, "We have successfully closed the warp point connecting {0} to {1}.".F(wp.StarSystem, wp.Target.StarSystem), LogMessageType.Generic); + wp.DisposeAndLog("The {0} has closed the warp point connecting {1} to {2}.", Owner); + foreach (var otherwp in otherwps) + otherwp.DisposeAndLog("The warp point connecting {0} to {1} has closed.", Owner); + } + else + { + Owner.RecordLog(executor, executor + " cannot close " + wp + " because it lacks the necessary supplies.", LogMessageType.Warning); + } + } + else if (Ability.Rule.Matches("Create Planet")) + { + } + else if (Ability.Rule.Matches("Destroy Planet")) + { + } + else if (Ability.Rule.Matches("Create Star")) + { + } + else if (Ability.Rule.Matches("Destroy Star")) + { + } + else if (Ability.Rule.Matches("Create Storm")) + { + } + else if (Ability.Rule.Matches("Destroy Storm")) + { + } + else if (Ability.Rule.Matches("Create Nebula")) + { + } + else if (Ability.Rule.Matches("Destroy Nebula")) + { + } + else if (Ability.Rule.Matches("Create Black Hole")) + { + } + else if (Ability.Rule.Matches("Destroy Black Hole")) + { + } + else if (Ability.Rule.Matches("Create Constructed Planet From Star")) + { + } + else if (Ability.Rule.Matches("Create Constructed Planet From Planet")) + { + } + else if (Ability.Rule.Matches("Create Constructed Planet From Storm")) + { + } + else if (Ability.Rule.Matches("Create Constructed Planet From Warp Point")) + { + } + else if (Ability.Rule.Matches("Create Constructed Planet From Asteroids")) + { + } + else if (Ability.Rule.Matches("Create Constructed Planet From Space")) + { + } + + // destroy component/etc. if necessary + if (Source.HasAbility("Destroyed On Use")) + { + // TODO - log destruction + if (Source is IDamageable) + (Source as IDamageable).Hitpoints = 0; + if (Source is IHull) + executor.Dispose(); // hull destruction kills the whole ship! + } + + // destroy entire space object if necessary + if (Source.HasAbility("Space Object Destroyed On Use")) + { + // TODO - log destruction + if (executor is Planet) + { + var p = executor as Planet; + p.ConvertToAsteroidField(); + } + else + { + executor.Dispose(); + } + } + } + + } + } + + public IEnumerable GetErrors(IOrderable executor) + { + if (!Source.IntrinsicAbilities.Contains(Ability)) + yield return executor.CreateLogMessage(executor + " does not intrinsically possess the ability \"" + Ability + "\" with ID=" + Ability.ID + ".", LogMessageType.Error); + if (!Ability.Rule.IsActivatable) + yield return executor.CreateLogMessage("The ability \"" + Ability + "\" cannot be activated. It is a passive ability.", LogMessageType.Error); + if (Source is IDamageable && (Source as IDamageable).IsDestroyed) + yield return executor.CreateLogMessage(executor + " cannot activate " + Source + "'s ability because " + Source + " is destroyed.", LogMessageType.Error); + } + public void ReplaceClientIDs(IDictionary idmap, ISet done = null) + { + // no new client objects here, nothing to do + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/ColonizeOrder.cs b/FrEee/Objects/Civilization/Orders/ColonizeOrder.cs new file mode 100644 index 000000000..9885305d0 --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/ColonizeOrder.cs @@ -0,0 +1,240 @@ +using FrEee.Objects.LogMessages; +using FrEee.Objects.Space; +using FrEee.Objects.Vehicles; +using FrEee.Modding; +using FrEee.Serialization; +using FrEee.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.Orders; + +/// +/// An order to colonize an uninhabited planet. +/// +[Serializable] +public class ColonizeOrder : IOrder +{ + public ColonizeOrder(Planet planet) + { + Owner = Empire.Current; + Planet = planet; + } + + public bool ConsumesMovement + { + get { return true; } + } + + public long ID { get; set; } + + public bool IsComplete + { + get; + set; + } + + public bool IsDisposed { get; set; } + + /// + /// The empire which issued the order. + /// + [DoNotSerialize] + public Empire Owner { get { return owner; } set { owner = value; } } + + /// + /// The planet we are colonizing. + /// + [DoNotSerialize] + public Planet Planet { get { return planet; } set { planet = value; } } + + private GalaxyReference owner { get; set; } + private GalaxyReference planet { get; set; } + + public bool CheckCompletion(IOrderable v) + { + return IsComplete; + } + + /// + /// Orders are visible only to their owners. + /// + /// + /// + public Visibility CheckVisibility(Empire emp) + { + if (emp == Owner) + return Visibility.Visible; + return Visibility.Unknown; + } + + public void Dispose() + { + if (IsDisposed) + return; + foreach (var v in Galaxy.Current.Referrables.OfType()) + { + if (v is SpaceVehicle sv) + sv.Orders.Remove(this); + else if (v is Fleet f) + f.Orders.Remove(this); + else if (v is Planet p) + p.Orders.Remove(this); + } + Galaxy.Current.UnassignID(this); + } + + public void Execute(IOrderable ord) + { + if (ord is IMobileSpaceObject sobj) + { + // error checking + var errors = GetErrors(sobj); + foreach (var error in errors) + sobj.Owner.Log.Add(error); + + // let only one colony ship from a fleet colonize + if (sobj is Fleet f) + sobj = f.LeafVehicles.FirstOrDefault(q => q.HasAbility(Planet.ColonizationAbilityName)); + + if (!errors.Any()) + { + // colonize now!!! + Planet.Colony = new Colony { Owner = sobj.Owner }; + Owner.TriggerHappinessChange(hm => hm.PlanetColonized); + Planet.Colony.ConstructionQueue = new ConstructionQueue(Planet); + if (sobj is ICargoContainer cc) + { + foreach (var kvp in cc.Cargo.Population) + { + // place population on planet + Planet.AddPopulation(kvp.Key, kvp.Value); + } + foreach (var unit in cc.Cargo.Units) + { + // planet unit on planet + Planet.AddUnit(unit); + } + } + + // ruins? + var ruinsTechs = Planet.GetAbilityValue("Ancient Ruins").ToInt(); + for (int i = 0; i < ruinsTechs; i++) + { + var msg = "We have discovered new technology from the ancient ruins on " + Planet + "."; + // pick a random tech that's unlocked but not fully researched and level it up + var tech = Mod.Current.Technologies.Where(t => sobj.Owner.HasUnlocked(t) && sobj.Owner.ResearchedTechnologies[t] < t.MaximumLevel).PickRandom(); + if (tech == null) + msg = "We have discovered ancient ruins on " + Planet + ", but there is nothing left for us to learn."; + else + { + var oldlvl = sobj.Owner.ResearchedTechnologies[tech]; + var newStuff = tech.GetExpectedResults(sobj.Owner); + sobj.Owner.ResearchedTechnologies[tech]++; + var newlvl = sobj.Owner.ResearchedTechnologies[tech]; + var progress = sobj.Owner.ResearchProgress.SingleOrDefault(p => p.Item == tech); + if (progress != null) + progress.Value = 0; + sobj.Owner.Log.Add(tech.CreateLogMessage("We have advanced from level " + oldlvl + " to level " + newlvl + " in " + tech + "!", LogMessageType.ResearchComplete)); + foreach (var item in newStuff) + sobj.Owner.Log.Add(item.CreateLogMessage("We have unlocked a new " + item.ResearchGroup.ToLower() + ", the " + item + "!", LogMessageType.ResearchComplete)); + } + if (i == 0) + sobj.Owner.Log.Add(Planet.CreateLogMessage(msg, LogMessageType.PlanetColonised)); + } + + // unique ruins? + foreach (var abil in Planet.Abilities().Where(a => a.Rule.Name == "Ancient Ruins Unique")) + { + if (sobj.Owner.UniqueTechsFound.Contains(abil.Value1)) + sobj.Owner.Log.Add(Planet.CreateLogMessage("We have discovered \"unique\" technology from the ancient ruins on " + Planet + ", but it appears we have already found this one elsewhere. Perhaps it was not as unique as we had thought...", LogMessageType.ResearchComplete)); + else + { + sobj.Owner.Log.Add(Planet.CreateLogMessage("We have discovered new unique technology from the ancient ruins on " + Planet + ".", LogMessageType.ResearchComplete)); + sobj.Owner.UniqueTechsFound.Add(abil.Value1); + foreach (var tech in Mod.Current.Technologies.Where(t => t.UniqueTechID == abil.Value1 && sobj.Owner.HasUnlocked(t))) + sobj.Owner.Log.Add(tech.CreateLogMessage("We have unlocked a new " + tech.ResearchGroup.ToLower() + ", the " + tech + "!", LogMessageType.ResearchComplete)); + } + } + + // delete ruins and unique ruins abilities + foreach (var a in Planet.IntrinsicAbilities.Where(a => a.Rule.Name == "Ancient Ruins" || a.Rule.Name == "Ancient Ruins Unique").ToArray()) + Planet.IntrinsicAbilities.Remove(a); + + // log it! + sobj.Owner.Log.Add(Planet.CreateLogMessage(sobj + " has founded a new colony on " + Planet + ".", LogMessageType.PlanetColonised)); + + // update pursue/evade orders to target planet now instead of ship + foreach (var o in Galaxy.Current.Referrables.OfType().Where(q => q.Target == sobj)) + { + if (o.Owner.CanSee(sobj) && o.Owner.CanSee(Planet)) + o.Target = Planet; + } + foreach (var o in Galaxy.Current.Referrables.OfType().Where(q => q.Target == sobj)) + { + if (o.Owner.CanSee(sobj) && o.Owner.CanSee(Planet)) + o.Target = Planet; + } + + // bye bye colony ship + sobj.Dispose(); + } + + // either done colonizing, or we failed + IsComplete = true; + + // spend time + sobj.SpendTime(sobj.TimePerMove); + } + else + Owner.Log.Append(Owner.CreateLogMessage($"Could not assign a colonize order to ${ord} because it is not a mobile space object.", LogMessageType.Error)); + } + + public IEnumerable GetErrors(IOrderable o) + { + if (o is IMobileSpaceObject sobj) + { + if (sobj.Sector != Planet.Sector) + { + // can't colonize here, maybe the GUI should have issued a move order? + yield return sobj.CreateLogMessage(sobj + " cannot colonize " + Planet + " because it is not currently located at the planet.", LogMessageType.Warning); + } + if (Planet.Colony != null) + { + // planet is already colonized! + yield return Planet.CreateLogMessage(Planet + " cannot be colonized by " + sobj + " because there is already a colony there belonging to the " + Planet.Colony.Owner + ".", LogMessageType.Warning); + } + if (!(sobj.HasAbility(Planet.ColonizationAbilityName) || sobj is Fleet f && f.LeafVehicles.Any(v => v.HasAbility(Planet.ColonizationAbilityName)))) + { + // no such colony module + yield return sobj.CreateLogMessage(sobj + " cannot colonize " + Planet + " because it lacks a " + Planet.Surface + " colony module.", LogMessageType.Warning); + } + if (Galaxy.Current.CanColonizeOnlyBreathable && Planet.Atmosphere != sobj.Owner.PrimaryRace.NativeAtmosphere) + { + // can only colonize breathable atmosphere (due to game setup option) + yield return sobj.CreateLogMessage(sobj + " cannot colonize " + Planet + " because we can only colonize " + sobj.Owner.PrimaryRace.NativeAtmosphere + " planets.", LogMessageType.Warning); + } + if (Galaxy.Current.CanColonizeOnlyHomeworldSurface && Planet.Surface != sobj.Owner.PrimaryRace.NativeSurface) + { + // can only colonize breathable atmosphere (due to game setup option) + yield return sobj.CreateLogMessage(sobj + " cannot colonize " + Planet + " because we can only colonize " + sobj.Owner.PrimaryRace.NativeSurface + " planets.", LogMessageType.Warning); + } + } + else + yield return o.CreateLogMessage($"{o} cannot colonize {Planet} because it is not a mobile space object.", LogMessageType.Error); + } + + public void ReplaceClientIDs(IDictionary idmap, ISet done = null) + { + // This type does not use client objects, so nothing to do here. + } + + public override string ToString() + { + return "Colonize " + Planet.Name; + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/ConstructionOrder.cs b/FrEee/Objects/Civilization/Orders/ConstructionOrder.cs new file mode 100644 index 000000000..b15a9432b --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/ConstructionOrder.cs @@ -0,0 +1,227 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using FrEee.Objects.LogMessages; +using FrEee.Objects.Technology; +using FrEee.Objects.Vehicles; +using FrEee.Modding; +using FrEee.Utility; +using FrEee.Serialization; +using FrEee.Extensions; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.Orders; + +/// +/// An order for a construction queue to build something. +/// +[Serializable] +public class ConstructionOrder : IConstructionOrder + where T : IConstructable + where TTemplate : ITemplate, IReferrable, IConstructionTemplate +{ + public ConstructionOrder() + { + Owner = Empire.Current; + } + + public bool ConsumesMovement + { + get { return false; } + } + + public ResourceQuantity Cost + { + get { return Template?.Cost ?? new ResourceQuantity(); } + } + + public long ID { get; set; } + + public bool IsComplete + { + get + { + if (isComplete == null) + return false; // haven't checked completion yet, so it's probably safe to say it's incomplete + return isComplete.Value; + } + set + { + isComplete = value; + } + } + + public bool IsDisposed { get; set; } + + /// + /// The item being built. + /// + public T Item { get; set; } + + IConstructable IConstructionOrder.Item + { + get { return Item; } + } + + public string Name + { + get + { + return Template.Name; + } + } + + /// + /// The empire which issued the order. + /// + [DoNotSerialize] + public Empire Owner { get { return owner; } set { owner = value; } } + + /// + /// The construction template. + /// + [DoNotSerialize] + public TTemplate Template + { + get { return template.Value; } + set + { + if (value is IModObject mo) + template = GetModReference(mo.ReferViaMod().ID); + else if (value is IReferrable r) + template = new GalaxyReference(r.ReferViaGalaxy().ID); + else if (value == null) + template = null; + else + throw new Exception($"{value} is not referrable in the galaxy or the mod."); + } + } + + private IReference GetModReference(string id) + { + // since T is not guaranteed to be a compile time IModObject implementation + var type = typeof(ModReference<>).MakeGenericType(typeof(U)); + var r = (IReference)Activator.CreateInstance(type); + r.SetPropertyValue("ID", id); + return r; + } + + IConstructionTemplate IConstructionOrder.Template { get { return template.Value; } } + + [DoNotSerialize] + private bool? isComplete + { + get; + set; + } + + private GalaxyReference owner { get; set; } + private IReference template { get; set; } + + public bool CheckCompletion(IOrderable q) + { + var queue = (ConstructionQueue)q; + isComplete = Item.ConstructionProgress >= Item.Cost || GetErrors(queue).Any(); + return IsComplete; + } + + /// + /// Orders are visible only to their owners. + /// + /// + /// + public Visibility CheckVisibility(Empire emp) + { + if (emp == Owner) + return Visibility.Visible; + return Visibility.Unknown; + } + + public void Dispose() + { + if (IsDisposed) + return; + foreach (var q in Galaxy.Current.Referrables.OfType()) + q.Orders.Remove(this); + Galaxy.Current.UnassignID(this); + } + + /// + /// Does 1 turn's worth of building. + /// + public void Execute(IOrderable q) + { + var queue = (ConstructionQueue)q; + var errors = GetErrors(queue); + foreach (var error in errors) + queue.Owner.Log.Add(error); + + if (!errors.Any()) + { + // create item if needed + if (Item == null) + { + Item = Template.Instantiate(); + if (!(Item is Facility)) + Item.Owner = queue.Owner; + if (Item is SpaceVehicle) + { + // space vehicles need their supplies filled up + var sv = (SpaceVehicle)(IConstructable)Item; + sv.SupplyRemaining = sv.SupplyStorage; + } + } + + // apply build rate + var costLeft = Item.Cost - Item.ConstructionProgress; + var spending = ResourceQuantity.Min(costLeft, queue.UnspentRate); + if (!(spending <= queue.Owner.StoredResources)) + { + spending = ResourceQuantity.Min(spending, queue.Owner.StoredResources); + if (spending.IsEmpty) + { + if (!queue.IsConstructionDelayed) // don't spam messages! + Owner.Log.Add(queue.Container.CreateLogMessage("Construction of " + Template + " at " + queue.Container + " was paused due to lack of resources.", LogMessageType.Generic)); + } + else + { + Owner.Log.Add(queue.Container.CreateLogMessage("Construction of " + Template + " at " + queue.Container + " was slowed due to lack of resources.", LogMessageType.Generic)); + } + queue.IsConstructionDelayed = true; + } + queue.Owner.StoredResources -= spending; + queue.UnspentRate -= spending; + Item.ConstructionProgress += spending; + } + } + + public IEnumerable GetErrors(IOrderable q) + { + var queue = (ConstructionQueue)q; + + // do we have a valid template? + if (Template == null) + yield return Owner.CreateLogMessage($"{queue.Container} cannot build a nonexistent template; skipping it. Probably a bug...", LogMessageType.Error); + + // validate that what's being built is unlocked + if (!queue.Owner.HasUnlocked(Template)) + yield return Template.CreateLogMessage(Template + " cannot be built at " + queue.Container + " because we have not yet researched it.", LogMessageType.Warning); + } + + public void ReplaceClientIDs(IDictionary idmap, ISet done = null) + { + if (done == null) + done = new HashSet(); + if (!done.Contains(this)) + { + done.Add(this); + template.ReplaceClientIDs(idmap, done); + } + } + + public void Reset() + { + Item = default; + } +} diff --git a/FrEee/Objects/Civilization/Orders/EvadeOrder.cs b/FrEee/Objects/Civilization/Orders/EvadeOrder.cs new file mode 100644 index 000000000..57189d3da --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/EvadeOrder.cs @@ -0,0 +1,87 @@ + +using FrEee.Objects.Space; +using FrEee.Utility; +using FrEee.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; + +namespace FrEee.Objects.Civilization.Orders; + +/// +/// An order to move a mobile space object away from another space object. +/// The direction will be chosen at random +/// +[Serializable] +public class EvadeOrder : PathfindingOrder +{ + public EvadeOrder(ISpaceObject target, bool avoidEnemies) + : base(target, avoidEnemies) + { + } + + public override string Verb + { + get { return "evade"; } + } + + /// + /// Finds the path for executing this order. + /// + /// The space object executing the order. + /// + public override IEnumerable Pathfind(IMobileSpaceObject me, Sector start) + { + if (Target is IMobileSpaceObject) + { + if (me.CanWarp && !Target.CanWarp) + { + // warping via any warp point that leads outside the system should be safe, so prioritize those! + var sys = me.FindStarSystem(); + var paths = sys.FindSpaceObjects() + .Where(wp => wp.TargetStarSystemLocation == null || wp.TargetStarSystemLocation.Item != sys) + .Select(wp => new { WarpPoint = wp, Path = Pathfinder.Pathfind(me, start, wp.Sector, AvoidEnemies, true, me.DijkstraMap) }); + if (paths.Any()) + { + // found a warp point to flee to! + var shortest = paths.WithMin(path => path.Path.Count()).PickRandom(); + return shortest.Path.Concat(new Sector[] { shortest.WarpPoint.Target }); + } + } + + // see how he can reach us, and go somewhere away from him (that would take longer for him to get to than + var dijkstraMap = Pathfinder.CreateDijkstraMap((IMobileSpaceObject)Target, Target.Sector, me.FindSector(), false, true); + var canMoveTo = Pathfinder.GetPossibleMoves(me.Sector, me.CanWarp, me.Owner); + var goodMoves = canMoveTo.Where(s => !dijkstraMap.Values.SelectMany(set => set).Any(n => n.Location == s)); + + if (goodMoves.Any()) + { + // just go there and recompute the path next time we can move - the enemy may have moved too + return new Sector[] { goodMoves.PickRandom() }; + } + else + { + // trapped... + return Enumerable.Empty(); + } + } + else + { + // target is immobile! no need to flee, unless it's in the same sector + if (Target.Sector == me.FindSector()) + { + // don't need to go through warp points to evade it, the warp points might be one way! + var moves = Pathfinder.GetPossibleMoves(me.Sector, false, me.Owner); + return new Sector[] { moves.PickRandom() }; + } + else + return Enumerable.Empty(); + } + } + + protected override bool AreWeThereYet(IMobileSpaceObject me) + { + // gotta keep on running... + return Target == null || Target.IsDisposed; + } +} \ No newline at end of file diff --git a/FrEee/Interfaces/IMovementOrder.cs b/FrEee/Objects/Civilization/Orders/IMovementOrder.cs similarity index 92% rename from FrEee/Interfaces/IMovementOrder.cs rename to FrEee/Objects/Civilization/Orders/IMovementOrder.cs index 570b01bc8..3cbb868d6 100644 --- a/FrEee/Interfaces/IMovementOrder.cs +++ b/FrEee/Objects/Civilization/Orders/IMovementOrder.cs @@ -1,8 +1,9 @@ -using FrEee.Objects.Space; + +using FrEee.Objects.Space; using FrEee.Utility; using System.Collections.Generic; -namespace FrEee.Interfaces; +namespace FrEee.Objects.Civilization.Orders; /// /// An order which moves a space object. diff --git a/FrEee/Objects/Civilization/Orders/IOrder.cs b/FrEee/Objects/Civilization/Orders/IOrder.cs new file mode 100644 index 000000000..2f2ac15b4 --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/IOrder.cs @@ -0,0 +1,38 @@ +using FrEee.Objects.GameState; +using FrEee.Objects.LogMessages; +using FrEee.Serialization; +using System.Collections.Generic; + +namespace FrEee.Objects.Civilization.Orders; + +/// +/// An order issued by a player to an object to do something. +/// Orders are distinguished from commands by being queued, rather than instantaneous. +/// +public interface IOrder : IReferrable, IPromotable +{ + /// + /// Does this order cost a movement point to execute? + /// + bool ConsumesMovement { get; } + + /// + /// Is this order done executing? + /// + bool IsComplete { get; set; } + + /// + /// Is this order done executing? + /// + bool CheckCompletion(IOrderable executor); + + /// + /// Executes the order. + /// + void Execute(IOrderable executor); + + /// + /// Validation errors. + /// + IEnumerable GetErrors(IOrderable executor); +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/IPathfindingOrder.cs b/FrEee/Objects/Civilization/Orders/IPathfindingOrder.cs new file mode 100644 index 000000000..3abdec9b2 --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/IPathfindingOrder.cs @@ -0,0 +1,52 @@ +using FrEee.Objects.Civilization; +using FrEee.Objects.Space; + +namespace FrEee.Objects.Civilization.Orders; + +/// +/// A movement order whose movement is relative to some space object. +/// +public interface IPathfindingOrder : IMovementOrder, IOwnable +{ + /// + /// Alternate target. This should be the largest ship in a fleet when a fleet is being pursued. + /// + ISpaceObject AlternateTarget { get; } + + /// + /// Should pathfinding avoid enemies? + /// + bool AvoidEnemies { get; set; } + + new bool IsComplete { get; set; } + + /// + /// Either the target itself, or the memory of the target, if it's not visible. + /// + ISpaceObject KnownTarget { get; } + + /// + /// The empire which issued the order. + /// + new Empire Owner { get; set; } + + /// + /// The target space object to pursue, evade, etc. + /// + ISpaceObject Target { get; set; } + + /// + /// A verb used to describe this order. + /// + string Verb { get; } + + /// + /// Call this when calling UpdateMemory on the target. + /// Sets the alternate target to the largest ship in a fleet, if the target is a fleet. + /// If the fleet is destroyed, sets the target to the alternate target. + /// If the target is a ship, etc., and it is destroyed, sets the target to the memory of the target, or deletes the order if there is no memory. + /// If the target is a memory, and the original object is sighted again, sets the target to the original object. + /// Otherwise sets the alternate target to the target. + /// + void UpdateAlternateTarget(); +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/IRecyclable.cs b/FrEee/Objects/Civilization/Orders/IRecyclable.cs new file mode 100644 index 000000000..0d0e33b89 --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/IRecyclable.cs @@ -0,0 +1,28 @@ +using FrEee.Objects.GameState; +using FrEee.Objects.Space; +using FrEee.Serialization; +using FrEee.Utility; +namespace FrEee.Objects.Civilization.Orders; + +/// +/// Something which can be recycled (scrapped, etc.) +/// +public interface IRecyclable : IReferrable, IPictorial +{ + /// + /// The object that's the "container" object of this object for recycling purposes. + /// + IMobileSpaceObject RecycleContainer { get; } + + /// + /// Amount of resources gained by scrapping this object. + /// + ResourceQuantity ScrapValue { get; } + + /// + /// Performs the recycling action. + /// + /// The action to perform. + /// Did the action already execute? If so, just do the object type specific logic. + void Recycle(IRecycleBehavior behavior, bool didExecute = false); +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/IRecycleBehavior.cs b/FrEee/Objects/Civilization/Orders/IRecycleBehavior.cs new file mode 100644 index 000000000..1cc0415f3 --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/IRecycleBehavior.cs @@ -0,0 +1,17 @@ +using FrEee.Objects.LogMessages; +using FrEee.Objects.Space; +using System.Collections.Generic; + +namespace FrEee.Objects.Civilization.Orders; + +/// +/// A type of recycle behavior (scrap, analyze, etc.) +/// +public interface IRecycleBehavior +{ + string Verb { get; } + + void Execute(IRecyclable target, bool didRecycle = false); + + IEnumerable GetErrors(IMobileSpaceObject executor, IRecyclable target); +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/MoveOrder.cs b/FrEee/Objects/Civilization/Orders/MoveOrder.cs new file mode 100644 index 000000000..22004ab21 --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/MoveOrder.cs @@ -0,0 +1,216 @@ +using FrEee.Objects.Combat; +using FrEee.Objects.LogMessages; +using FrEee.Objects.Space; +using FrEee.Utility; +using FrEee.Serialization; +using FrEee.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.Orders; + +/// +/// An order to move a mobile space object to a new location. +/// +[Serializable] +public class MoveOrder : IMovementOrder +{ + public MoveOrder(Sector destination, bool avoidEnemies) + { + Owner = Empire.Current; + Destination = destination; + AvoidEnemies = avoidEnemies; + // TODO - add flag for "avoid damaging sectors"? but how to specify in UI? + } + + /// + /// Should pathfinding avoid enemies? + /// + public bool AvoidEnemies { get; set; } + + public bool ConsumesMovement + { + get { return true; } + } + + /// + /// The sector we are moving to. + /// + public Sector Destination { get; set; } + + public long ID { get; set; } + + public bool IsComplete + { + get; + set; + } + + public bool IsDisposed { get; set; } + + /// + /// Did we already log a pathfinding error this turn? + /// + [DoNotSerialize] + public bool LoggedPathfindingError { get; private set; } + + /// + /// The empire which issued the order. + /// + [DoNotSerialize] + public Empire Owner { get { return owner; } set { owner = value; } } + + /// + /// Any pathfinding error that we might have found. + /// + [DoNotSerialize] + public LogMessage PathfindingError { get; private set; } + + private GalaxyReference owner { get; set; } + + public bool CheckCompletion(IOrderable v) + { + return IsComplete; + } + + /// + /// Orders are visible only to their owners. + /// + /// + /// + public Visibility CheckVisibility(Empire emp) + { + if (emp == Owner) + return Visibility.Visible; + return Visibility.Unknown; + } + + /// + /// Creates a Dijkstra map for this order's movement. + /// + /// + /// + /// + public IDictionary, ISet>> CreateDijkstraMap(IMobileSpaceObject me, Sector start) + { + return Pathfinder.CreateDijkstraMap(me, start, Destination, AvoidEnemies, true); + } + + public void Dispose() + { + if (IsDisposed) + return; + foreach (var v in Galaxy.Current.Referrables.OfType()) + v.RemoveOrder(this); + Galaxy.Current.UnassignID(this); + } + + public void Execute(IOrderable ord) + { + if (ord is IMobileSpaceObject sobj) + { + // TODO - movement logs + if (sobj.Sector == Destination) + { + IsComplete = true; + return; + } + else + { + var gotoSector = Pathfind(sobj, sobj.Sector).FirstOrDefault(); + if (gotoSector != null) + { + // move + sobj.Sector = gotoSector; + sobj.RefreshDijkstraMap(); + + // consume supplies + sobj.BurnMovementSupplies(); + + // resupply space vehicles + // either this vehicle from other space objects, or other vehicles from this one + // TODO - this should really be done AFTER battles... + if (gotoSector.HasAbility("Supply Generation", sobj.Owner)) + { + foreach (var v in gotoSector.SpaceObjects.OfType().Where(v => v.Owner == sobj.Owner)) + v.SupplyRemaining = v.SupplyStorage; + } + if (gotoSector.StarSystem.HasAbility("Supply Generation - System", sobj.Owner) || gotoSector.StarSystem.HasAbility("Supply Generation - System")) + { + foreach (var v in gotoSector.StarSystem.FindSpaceObjects().Where(v => v.Owner == sobj.Owner)) + v.SupplyRemaining = v.SupplyStorage; + } + + // is it done? + if (gotoSector == Destination) + IsComplete = true; + + // apply damage from damaging sectors + // TODO - apply damage from damaging systems too + // TODO - move this out into the Place method so it applies to all movement-type orders and newly constructed vehicles + foreach (var damager in gotoSector.SpaceObjects.Where(dsobj => dsobj.HasAbility("Sector - Damage"))) + { + var damage = damager.GetAbilityValue("Sector - Damage").ToInt(); + // TODO - let sector damage have special damage types? + var shot = new Shot(null, null, sobj, 0); + var hit = new Hit(shot, sobj, damage); + sobj.TakeDamage(hit, null); + sobj.Owner.Log.Add(sobj.CreateLogMessage(sobj + " took " + damage + " damage from entering " + damager + "'s sector.", LogMessageType.Generic)); + sobj.ReplenishShields(); + } + } + else if (!LoggedPathfindingError) + { + // log pathfinding error + string reason; + if (sobj.StrategicSpeed <= 0) + reason = sobj + " is immobile"; + else + reason = "there is no available path leading toward " + Destination; + PathfindingError = sobj.CreateLogMessage(sobj + " could not move to " + Destination + " because " + reason + ".", LogMessageType.Warning); + sobj.Owner?.Log.Add(PathfindingError); + LoggedPathfindingError = true; + } + } + + // spend time + sobj.SpendTime(sobj.TimePerMove); + } + else + ord.Owner.RecordLog(ord, $"{ord} cannot be ordered to move because it is not a mobile space object.", LogMessageType.Error); + } + + public IEnumerable GetErrors(IOrderable v) + { + if (PathfindingError != null) + yield return PathfindingError; + } + + /// + /// Finds the path for executing this order. + /// + /// The space object executing the order. + /// + public IEnumerable Pathfind(IMobileSpaceObject me, Sector start) + { + return Pathfinder.Pathfind(me, start, Destination, AvoidEnemies, true, me.DijkstraMap); + } + + public void ReplaceClientIDs(IDictionary idmap, ISet done) + { + // This type does not use client objects, so nothing to do here. + } + + public override string ToString() + { + var coords = Destination.Coordinates; + if (Destination == null || Destination.StarSystem == null) + return "(Unknown Move Order)"; + if (AvoidEnemies) + return "Move to " + Destination.StarSystem.Name + " (" + coords.X + ", " + coords.Y + ")"; + else + return "Attack " + Destination.StarSystem.Name + " (" + coords.X + ", " + coords.Y + ")"; + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/PathfindingOrder.cs b/FrEee/Objects/Civilization/Orders/PathfindingOrder.cs new file mode 100644 index 000000000..1ba195725 --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/PathfindingOrder.cs @@ -0,0 +1,282 @@ +using FrEee.Objects.LogMessages; +using FrEee.Objects.Space; +using FrEee.Utility; +using FrEee.Serialization; +using FrEee.Extensions; +using System.Collections.Generic; +using System.Linq; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.Orders; + +/// +/// An order to pathfind relative to a target. +/// +/// +public abstract class PathfindingOrder + : IPathfindingOrder +{ + protected PathfindingOrder(ISpaceObject target, bool avoidEnemies) + { + Owner = Empire.Current; + Target = target; + AvoidEnemies = avoidEnemies; + // TODO - add flag for "avoid damaging sectors"? but how to specify in UI? + } + + /// + /// Alternate target. This should be the largest ship in a fleet when a fleet is being pursued. + /// + [DoNotSerialize] + public ISpaceObject AlternateTarget + { + get; + private set; + } + + /// + /// Should pathfinding avoid enemies? + /// + public bool AvoidEnemies { get; set; } + + public bool ConsumesMovement + { + get { return true; } + } + + public Sector Destination + { + get { return KnownTarget?.Sector; } + } + + public long ID { get; set; } + + public bool IsComplete + { + get; + set; + } + + public bool IsDisposed { get; set; } + + /// + /// Either the target itself, or the memory of the target, if it's not visible. + /// + public ISpaceObject KnownTarget + { + get + { + if (Target == null) + return null; + if (Target.CheckVisibility(Owner) >= Visibility.Visible) + return Target; + return Owner?.Recall(Target); + } + } + + /// + /// Did we already log a pathfinding error this turn? + /// + [DoNotSerialize] + public bool LoggedPathfindingError { get; private set; } + + /// + /// The empire which issued the order. + /// + [DoNotSerialize] + public Empire Owner { get { return owner; } set { owner = value; } } + + /// + /// Any pathfinding error that we might have found. + /// + [DoNotSerialize] + public LogMessage PathfindingError { get; private set; } + + /// + /// The target we are pursuing. + /// + [DoNotSerialize] + public ISpaceObject Target { get { return target?.Value; } set { target = value.ReferViaGalaxy(); } } + + /// + /// A verb used to describe this order. + /// + public abstract string Verb { get; } + + private GalaxyReference owner { get; set; } + private GalaxyReference target { get; set; } + + public bool CheckCompletion(IOrderable v) + { + return IsComplete; + } + + /// + /// Orders are visible only to their owners. + /// + /// + /// + public Visibility CheckVisibility(Empire emp) + { + if (emp == Owner) + return Visibility.Visible; + return Visibility.Unknown; + } + + public IDictionary, ISet>> CreateDijkstraMap(IMobileSpaceObject me, Sector start) + { + return Pathfinder.CreateDijkstraMap(me, start, Destination, AvoidEnemies, true); + } + + public void Dispose() + { + if (IsDisposed) + return; + foreach (var v in Galaxy.Current.FindSpaceObjects()) + v.RemoveOrder(this); + Galaxy.Current.UnassignID(this); + } + + public void Execute(IOrderable ord) + { + if (ord is IMobileSpaceObject sobj) + { + // TODO - movement logs + if (KnownTarget == null) + IsComplete = true; // target is known to be dead + else if (AreWeThereYet(sobj)) + IsComplete = true; // we've arrived at the target + else + { + var gotoSector = Pathfind(sobj, sobj.Sector).FirstOrDefault(); + if (gotoSector != null) + { + // move + if (gotoSector == null) + { + // try to warp through an unexplored warp point + var wps = sobj.Sector.SpaceObjects.OfType().Where(w => !w.TargetStarSystemLocation.Item.ExploredByEmpires.Contains(sobj.Owner)); + var wp = wps.PickRandom(); + if (wp != null) + { + // warp through the unexplored warp point + sobj.Sector = wp.Target; + } + else if (!LoggedPathfindingError) + { + // no warp points to explore and we haven'IMobileSpaceObject told the player yet + PathfindingError = sobj.CreateLogMessage("{0} found no unexplored warp points at {1} to enter.".F(sobj, sobj.Sector), LogMessageType.Warning); + sobj.Owner.Log.Add(PathfindingError); + LoggedPathfindingError = true; + } + } + else + { + sobj.Sector = gotoSector; + sobj.RefreshDijkstraMap(); + + // consume supplies + sobj.BurnMovementSupplies(); + + // are we there yet, Dad? + if (AreWeThereYet(sobj)) + IsComplete = true; // we've arrived at the target + + // resupply space vehicles + // either this vehicle from other space objects, or other vehicles from this one + // TODO - this should really be done AFTER battles... + if (gotoSector.HasAbility("Supply Generation", sobj.Owner)) + { + foreach (var v in gotoSector.SpaceObjects.OfType().Where(v => v.Owner == sobj.Owner)) + v.SupplyRemaining = v.SupplyStorage; + } + if (gotoSector.StarSystem.HasAbility("Supply Generation - System", sobj.Owner) || gotoSector.StarSystem.HasAbility("Supply Generation - System")) + { + foreach (var v in gotoSector.StarSystem.FindSpaceObjects().Where(v => v.Owner == sobj.Owner)) + v.SupplyRemaining = v.SupplyStorage; + } + } + } + else if (!LoggedPathfindingError) + { + // log pathfinding error + string reason; + if (sobj.StrategicSpeed <= 0) + reason = sobj + " is immobile"; + else + reason = "there is no available path leading toward " + Destination; + PathfindingError = sobj.CreateLogMessage(sobj + " could not " + Verb + " " + KnownTarget + " because " + reason + ".", LogMessageType.Warning); + sobj.Owner.Log.Add(PathfindingError); + LoggedPathfindingError = true; + } + } + + // spend time + sobj.SpendTime(sobj.TimePerMove); + } + else + ord.Owner.RecordLog(ord, $"{ord} cannot pathfind because it is not a mobile space object.", LogMessageType.Error); + } + + public IEnumerable GetErrors(IOrderable v) + { + if (PathfindingError != null) + yield return PathfindingError; + } + + /// + /// Finds the path for executing this order. + /// + /// The space object executing the order. + /// The start location (need not be the current location, in case there are prior orders queued). + /// + public abstract IEnumerable Pathfind(IMobileSpaceObject me, Sector start); + + public void ReplaceClientIDs(IDictionary idmap, ISet done) + { + // This type does not use client objects, so nothing to do here. + } + + public override string ToString() + { + if (KnownTarget == null) + return "Unknown " + Verb + " order"; + return Verb.Capitalize() + " " + KnownTarget; + } + + /// + /// Call this when calling UpdateMemory on the target. + /// Sets the alternate target to the largest ship in a fleet, if the target is a fleet. + /// If the fleet is destroyed, sets the target to the alternate target. + /// If the target is a ship, etc., and it is destroyed, sets the target to the memory of the target, or deletes the order if there is no memory. + /// If the target is a memory, and the original object is sighted again, sets the target to the original object. + /// Otherwise sets the alternate target to the target. + /// + public void UpdateAlternateTarget() + { + if (Target is Fleet) + { + var f = (Fleet)Target; + if (!f.IsDestroyed) + AlternateTarget = f.LeafVehicles.Largest(); + else + Target = AlternateTarget; + } + else if (Target is IMobileSpaceObject) + { + var sobj = (IMobileSpaceObject)Target; + if (sobj.IsMemory && sobj.FindOriginalObject(Owner) != null) + Target = (ISpaceObject)sobj.FindOriginalObject(Owner); + if (!sobj.IsDestroyed) + AlternateTarget = Target; + else if (Owner.Memory[Target.ID] != null) + Target = (ISpaceObject)Owner.Memory[Target.ID]; + else + Dispose(); + } + else + AlternateTarget = Target; + } + + protected abstract bool AreWeThereYet(IMobileSpaceObject me); +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/PursueOrder.cs b/FrEee/Objects/Civilization/Orders/PursueOrder.cs new file mode 100644 index 000000000..17d61ed54 --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/PursueOrder.cs @@ -0,0 +1,48 @@ + +using FrEee.Objects.Combat; +using FrEee.Objects.Space; +using FrEee.Utility; +using System; +using System.Collections.Generic; + +namespace FrEee.Objects.Civilization.Orders; + +/// +/// An order to move a mobile space object toward another space object. +/// +[Serializable] +public class PursueOrder : PathfindingOrder +{ + public PursueOrder(ISpaceObject target, bool avoidEnemies) + : base(target, avoidEnemies) + { + } + + public override string Verb + { + get + { + if (KnownTarget == null) + return "pursue"; + else if (AvoidEnemies && KnownTarget.Owner != null && (!(KnownTarget is ICombatant) || !(KnownTarget as ICombatant).IsHostileTo(Owner))) + return "escort"; + else + return "pursue"; + } + } + + /// + /// Finds the path for executing this order. + /// + /// The space object executing the order. + /// + public override IEnumerable Pathfind(IMobileSpaceObject me, Sector start) + { + return Pathfinder.Pathfind(me, start, Destination, AvoidEnemies, true, me.DijkstraMap); + } + + protected override bool AreWeThereYet(IMobileSpaceObject me) + { + return me.Sector == Destination; + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/RecycleBehaviors/ScrapBehavior.cs b/FrEee/Objects/Civilization/Orders/RecycleBehaviors/ScrapBehavior.cs new file mode 100644 index 000000000..d851583da --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/RecycleBehaviors/ScrapBehavior.cs @@ -0,0 +1,53 @@ + +using FrEee.Objects.LogMessages; +using FrEee.Objects.Space; +using FrEee.Objects.Vehicles; +using FrEee.Extensions; +using System.Collections.Generic; +using System.Linq; + +namespace FrEee.Objects.Civilization.Orders.RecycleBehaviors; + +/// +/// Scraps a recyclable object, returning resources equal to its scrap value. +/// If there is anything in cargo, it will be scrapped as well. +/// +public class ScrapBehavior : IRecycleBehavior +{ + public string Verb { get { return "Scrap"; } } + + public void Execute(IRecyclable target, bool didRecycle = false) + { + // don't scrap stuff that's already been scrapped due to it being in cargo of something else being scrapped! + if (!target.IsDisposed) + { + var val = target.ScrapValue; + if (target.Owner != null) // if not, it's already scrapped? + { + target.Owner.StoredResources += val; + target.Owner.Log.Add(target.CreateLogMessage("We have scrapped " + target + " and reclaimed " + val + ".", LogMessageType.Generic)); + } + target.Dispose(); + + if (!didRecycle) + target.Recycle(this, true); + } + } + + public IEnumerable GetErrors(IMobileSpaceObject executor, IRecyclable target) + { + if (target == null) + { + yield return new GenericLogMessage("A scrap order was issued for a nonexistent target."); + yield break; + } + if (target.IsDisposed) + yield return target.CreateLogMessage($"{target} cannot be scrapped because it is already destroyed.", LogMessageType.Error); + if (target.RecycleContainer != executor) + yield return target.CreateLogMessage(target + " cannot be scrapped by " + executor + " because " + target + " does not belong to " + executor + ".", LogMessageType.Error); + if ((target is Ship || target is Base) && !executor.Sector.SpaceObjects.Any(sobj => sobj.Owner == executor.Owner && sobj.HasAbility("Space Yard"))) + yield return target.CreateLogMessage(target + " cannot be scrapped at " + executor.Sector + " because there is no space yard present in that sector.", LogMessageType.Error); + if (target is IUnit && !executor.Sector.SpaceObjects.Any(sobj => sobj.Owner == executor.Owner && (sobj is Planet || sobj.HasAbility("Space Yard")))) + yield return target.CreateLogMessage(target + " cannot be scrapped at " + executor.Sector + " because there is no space yard or colony present in that sector.", LogMessageType.Error); + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/RecycleFacilityOrCargoOrder.cs b/FrEee/Objects/Civilization/Orders/RecycleFacilityOrCargoOrder.cs new file mode 100644 index 000000000..9245f324e --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/RecycleFacilityOrCargoOrder.cs @@ -0,0 +1,119 @@ +using FrEee.Objects.LogMessages; +using FrEee.Objects.Space; +using FrEee.Serialization; +using FrEee.Extensions; +using System.Collections.Generic; +using System.Linq; +using FrEee.Objects.Combat; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.Orders; + +public class RecycleFacilityOrCargoOrder : IOrder +{ + public RecycleFacilityOrCargoOrder(IRecycleBehavior behavior, IRecyclable target) + { + Behavior = behavior; + Target = target; + } + + public IRecycleBehavior Behavior { get; private set; } + + public bool ConsumesMovement + { + get { return false; } + } + + public long ID + { + get; + set; + } + + public bool IsComplete + { + get; + set; + } + + public bool IsDisposed + { + get; + set; + } + + /// + /// The empire which issued the order. + /// + [DoNotSerialize] + public Empire Owner { get { return owner; } set { owner = value; } } + + /// + /// The facility or unit in cargo to recycle. + /// + [DoNotSerialize] + public IRecyclable Target { get { return target.Value; } set { target = value.ReferViaGalaxy(); } } + + private GalaxyReference owner { get; set; } + private GalaxyReference target { get; set; } + + public bool CheckCompletion(IOrderable executor) + { + return IsComplete; + } + + public void Dispose() + { + if (IsDisposed) + return; + foreach (var v in Galaxy.Current.Referrables.OfType()) + v.RemoveOrder(this); + Galaxy.Current.UnassignID(this); + } + + public void Execute(IOrderable x) + { + if (x is IMobileSpaceObject executor) + { + var errors = GetErrors(executor); + if (errors.Any()) + { + if (Owner != null) + { + foreach (var e in errors) + Owner.Log.Add(e); + } + else + IsComplete = true; + return; + } + } + + Behavior.Execute(Target); + IsComplete = true; + } + + public IEnumerable GetErrors(IOrderable executor) + { + return Behavior.GetErrors(executor as IMobileSpaceObject, Target).Concat(SelfErrors); + } + + private IEnumerable SelfErrors + { + get + { + if (Target is ICombatant c && c.IsHostileTo(Owner)) + yield return c.CreateLogMessage($"You can't {Behavior.Verb} {c} because it belongs to a hostile empire.", LogMessageType.Error); + } + } + + public void ReplaceClientIDs(IDictionary idmap, ISet done = null) + { + // This type does not use client objects, so nothing to do here. + } + + public override string ToString() + { + return Behavior.Verb + " " + Target; + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/RecycleVehicleInSpaceOrder.cs b/FrEee/Objects/Civilization/Orders/RecycleVehicleInSpaceOrder.cs new file mode 100644 index 000000000..1c43f5347 --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/RecycleVehicleInSpaceOrder.cs @@ -0,0 +1,93 @@ +using FrEee.Objects.GameState; +using FrEee.Objects.LogMessages; +using FrEee.Objects.Space; +using FrEee.Objects.Vehicles; +using FrEee.Serialization; +using System.Collections.Generic; +using System.Linq; + +namespace FrEee.Objects.Civilization.Orders; + +public class RecycleVehicleInSpaceOrder : IOrder +{ + public RecycleVehicleInSpaceOrder(IRecycleBehavior behavior) + { + Behavior = behavior; + } + + public IRecycleBehavior Behavior { get; private set; } + + public bool ConsumesMovement + { + get { return false; } + } + + public long ID + { + get; + set; + } + + public bool IsComplete + { + get; + set; + } + + public bool IsDisposed + { + get; + set; + } + + /// + /// The empire which issued the order. + /// + [DoNotSerialize] + public Empire Owner { get { return owner; } set { owner = value; } } + + private GalaxyReference owner { get; set; } + + public bool CheckCompletion(IOrderable executor) + { + return IsComplete; + } + + public void Dispose() + { + if (IsDisposed) + return; + foreach (var v in Galaxy.Current.Referrables.OfType()) + v.Orders.Remove(this); + Galaxy.Current.UnassignID(this); + } + + public void Execute(IOrderable executor) + { + var errors = GetErrors(executor); + if (errors.Any() && Owner != null) + { + foreach (var e in errors) + Owner.Log.Add(e); + return; + } + + Behavior.Execute((IRecyclable)executor); + IsComplete = true; + } + + public IEnumerable GetErrors(IOrderable executor) + { + return Behavior.GetErrors(executor as IMobileSpaceObject, executor as IRecyclable); + } + + public void ReplaceClientIDs(IDictionary idmap, ISet done = null) + { + // This type does not use client objects, so nothing to do here. + } + + public override string ToString() + { + return Behavior.Verb; + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/SentryOrder.cs b/FrEee/Objects/Civilization/Orders/SentryOrder.cs new file mode 100644 index 000000000..dc149ec5f --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/SentryOrder.cs @@ -0,0 +1,104 @@ +using FrEee.Objects.LogMessages; +using FrEee.Objects.Space; +using FrEee.Objects.Vehicles; +using FrEee.Serialization; +using FrEee.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using FrEee.Objects.Combat; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.Orders; + +/// +/// An order for a mobile space object to hold position until enemies are sighted in the system. +/// +[Serializable] +public class SentryOrder : IOrder +{ + public SentryOrder() + { + Owner = Empire.Current; + } + + public bool ConsumesMovement + { + get { return true; } + } + + public long ID { get; set; } + + public bool IsComplete + { + get; + set; + } + + public bool IsDisposed { get; set; } + + /// + /// The empire which issued the order. + /// + [DoNotSerialize] + public Empire Owner { get { return owner; } set { owner = value; } } + + private GalaxyReference owner { get; set; } + + public bool CheckCompletion(IOrderable v) + { + return IsComplete; + } + + /// + /// Orders are visible only to their owners. + /// + /// + /// + public Visibility CheckVisibility(Empire emp) + { + if (emp == Owner) + return Visibility.Visible; + return Visibility.Unknown; + } + + public void Dispose() + { + if (IsDisposed) + return; + foreach (var v in Galaxy.Current.Referrables.OfType()) + v.Orders.Remove(this); + Galaxy.Current.UnassignID(this); + } + + public void Execute(IOrderable ord) + { + if (ord is IMobileSpaceObject sobj) + { + // if hostiles in system, we are done sentrying + if (sobj.FindStarSystem().FindSpaceObjects(s => s.IsHostileTo(sobj.Owner)).Any()) + IsComplete = true; + + // spend time + sobj.SpendTime(sobj.TimePerMove); + } + else + ord.Owner.RecordLog(ord, $"{ord} cannot sentry because it is not a mobile space object.", LogMessageType.Error); + } + + public IEnumerable GetErrors(IOrderable executor) + { + // this order doesn't error + yield break; + } + + public void ReplaceClientIDs(IDictionary idmap, ISet done = null) + { + // This type does not use client objects, so nothing to do here. + } + + public override string ToString() + { + return "Sentry"; + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/TransferCargoOrder.cs b/FrEee/Objects/Civilization/Orders/TransferCargoOrder.cs new file mode 100644 index 000000000..acb2a75f1 --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/TransferCargoOrder.cs @@ -0,0 +1,140 @@ +using FrEee.Objects.LogMessages; +using FrEee.Serialization; +using FrEee.Extensions; +using System.Collections.Generic; +using System.Linq; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.Orders; + +/// +/// An order to transfer cargo from one object to another. +/// +public class TransferCargoOrder : IOrder +{ + public TransferCargoOrder(bool isLoadOrder, CargoDelta cargoDelta, ICargoTransferrer target) + { + Owner = Empire.Current; + IsLoadOrder = isLoadOrder; + CargoDelta = cargoDelta; + Target = target; + } + + /// + /// The cargo delta, which specifies what cargo is to be transferred. + /// + public CargoDelta CargoDelta { get; set; } + + public bool ConsumesMovement + { + get { return false; } + } + + public long ID { get; set; } + + public bool IsComplete + { + get; + set; + } + + public bool IsDisposed { get; set; } + + /// + /// The empire which issued the order. + /// + [DoNotSerialize] + public Empire Owner { get { return owner; } set { owner = value; } } + + /// + /// The cargo transferrer to which the cargo will be transferred, or null to launch/recover to/from space. + /// + [DoNotSerialize] + public ICargoTransferrer Target { get { return target?.Value; } set { target = value.ReferViaGalaxy(); } } + + /// + /// True if this is a load order, false if it is a drop order. + /// + private bool IsLoadOrder { get; set; } + + private GalaxyReference owner { get; set; } + private GalaxyReference target { get; set; } + + public bool CheckCompletion(IOrderable v) + { + return IsComplete; + } + + public void Dispose() + { + if (IsDisposed) + return; + foreach (var v in Galaxy.Current.Referrables.OfType()) + v.RemoveOrder(this); + Galaxy.Current.UnassignID(this); + } + + public void Execute(IOrderable ord) + { + if (ord is ICargoTransferrer executor) + { + var errors = GetErrors(executor); + if (executor.Owner != null) + { + foreach (var error in errors) + executor.Owner.Log.Add(error); + } + + if (!errors.Any()) + { + if (IsLoadOrder) + Target.TransferCargo(CargoDelta, executor, executor.Owner); + else + executor.TransferCargo(CargoDelta, Target, executor.Owner); + } + IsComplete = true; + } + } + + public IEnumerable GetErrors(IOrderable executor) + { + if (executor is ICargoTransferrer t) + { + if (Target != null && t.Sector != Target.Sector) + yield return t.CreateLogMessage(executor + " cannot transfer cargo to " + Target + " because they are not in the same sector.", LogMessageType.Warning); + } + else + yield return executor.CreateLogMessage($"{executor} cannot transfer cargo.", LogMessageType.Warning); + } + + public void ReplaceClientIDs(IDictionary idmap, ISet done = null) + { + if (done == null) + done = new HashSet(); + if (!done.Contains(this)) + { + done.Add(this); + target?.ReplaceClientIDs(idmap, done); + owner.ReplaceClientIDs(idmap, done); + } + } + + public override string ToString() + { + if (Target == null) + { + if (IsLoadOrder) + return "Recover " + CargoDelta; + else + return "Launch " + CargoDelta; + } + else + { + if (IsLoadOrder) + return "Load " + CargoDelta + " from " + Target; + else + return "Drop " + CargoDelta + " at " + Target; + } + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/UpgradeFacilityOrder.cs b/FrEee/Objects/Civilization/Orders/UpgradeFacilityOrder.cs new file mode 100644 index 000000000..702eb6938 --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/UpgradeFacilityOrder.cs @@ -0,0 +1,199 @@ +using FrEee.Objects.LogMessages; +using FrEee.Objects.Space; +using FrEee.Objects.Technology; +using FrEee.Utility; +using FrEee.Serialization; +using FrEee.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.Orders; + +/// +/// An order for a construction queue to upgrade a facility. +/// +[Serializable] +public class UpgradeFacilityOrder : IConstructionOrder +{ + public UpgradeFacilityOrder(FacilityUpgrade fu) + { + Owner = Empire.Current; + Upgrade = fu; + } + + public bool ConsumesMovement + { + get { return false; } + } + + public ResourceQuantity Cost + { + get { return Upgrade.Cost; } + } + + public long ID { get; set; } + + public bool IsComplete + { + get + { + if (isComplete == null) + return false; // haven't checked completion yet, so it's probably safe to say it's incomplete + return isComplete.Value; + } + set + { isComplete = value; } + } + + public bool IsDisposed { get; set; } + + IConstructable IConstructionOrder.Item + { + get { return NewFacility; } + } + + public string Name + { + get + { + return "Upgrade to " + Upgrade.New.Name; + } + } + + /// + /// The facility being built. + /// + public Facility NewFacility { get; set; } + + /// + /// The empire which issued the order. + /// + [DoNotSerialize] + public Empire Owner { get { return owner; } set { owner = value; } } + + IConstructionTemplate IConstructionOrder.Template { get { return Upgrade.New; } } + + /// + /// The upgrade to perform. + /// + public FacilityUpgrade Upgrade { get; set; } + + [DoNotSerialize] + private bool? isComplete + { + get; + set; + } + + private GalaxyReference owner { get; set; } + + public bool CheckCompletion(IOrderable queue) + { + if (NewFacility == null) + return false; + isComplete = NewFacility.ConstructionProgress >= Cost || GetErrors(queue).Any(); + return IsComplete; + } + + /// + /// Orders are visible only to their owners. + /// + /// + /// + public Visibility CheckVisibility(Empire emp) + { + if (emp == Owner) + return Visibility.Visible; + return Visibility.Unknown; + } + + public void Dispose() + { + if (IsDisposed) + return; + foreach (var v in Galaxy.Current.Referrables.OfType()) + v.Orders.Remove(this); + Galaxy.Current.UnassignID(this); + } + + /// + /// Does 1 turn's worth of building. + /// + public void Execute(IOrderable ord) + { + if (ord is ConstructionQueue queue) + { + var errors = GetErrors(queue); + foreach (var error in errors) + queue.Owner.Log.Add(error); + + if (!errors.Any()) + { + // create item if needed + if (NewFacility == null) + NewFacility = Upgrade.New.Instantiate(); + + // apply build rate + var costLeft = Cost - NewFacility.ConstructionProgress; + var spending = ResourceQuantity.Min(costLeft, queue.UnspentRate); + if (spending < queue.Owner.StoredResources) + { + spending = ResourceQuantity.Min(spending, queue.Owner.StoredResources); + queue.Container.CreateLogMessage("Construction of " + Upgrade.New + " at " + queue.Container + " was delayed due to lack of resources.", LogMessageType.Generic); + } + queue.Owner.StoredResources -= spending; + queue.UnspentRate -= spending; + NewFacility.ConstructionProgress += spending; + + // if we're done, delete the old facility and replace it with this one + if (CheckCompletion(queue)) + { + var planet = (Planet)queue.Container; + planet.Colony.Facilities.Where(f => f.Template.ModID == Upgrade.Old.ModID).First().Dispose(); // HACK - why are we getting duplicate facility templates? + planet.Colony.Facilities.Add(NewFacility); + } + } + } + } + + public IEnumerable GetErrors(IOrderable ord) + { + if (ord is ConstructionQueue queue) + { + // validate that new facility is unlocked + if (!queue.Owner.HasUnlocked(Upgrade.New)) + yield return Upgrade.Old.CreateLogMessage(Upgrade.Old + " on " + queue.Container + " could not be upgraded to a " + Upgrade.New + " because we have not yet researched the " + Upgrade.New + ".", LogMessageType.Error); + + // validate that new and old facilities are in the same family + if (Upgrade.New.Family.Value != Upgrade.Old.Family.Value) + yield return Upgrade.Old.CreateLogMessage(Upgrade.Old + " on " + queue.Container + " could not be upgraded to a " + Upgrade.New + " because facilities cannot be upgraded to facilities of a different family.", LogMessageType.Error); + + // validate that there is a facility to upgrade + var planet = (Planet)queue.Container; + var colony = planet.Colony; + if (!colony.Facilities.Any(f => f.Template.ModID == Upgrade.Old.ModID)) // HACK - why are we getting duplicate facility templates? + yield return planet.CreateLogMessage("There are no " + Upgrade.Old + "s on " + planet + " to upgrade.", LogMessageType.Error); + } + else + yield return ord.CreateLogMessage($"{ord} cannot upgrade facilities because it is not a construction queue.", LogMessageType.Error); + } + + public void ReplaceClientIDs(IDictionary idmap, ISet done = null) + { + if (done == null) + done = new HashSet(); + if (!done.Contains(this)) + { + done.Add(this); + Upgrade.ReplaceClientIDs(idmap, done); + } + } + + public void Reset() + { + NewFacility = null; + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/Orders/WarpOrder.cs b/FrEee/Objects/Civilization/Orders/WarpOrder.cs new file mode 100644 index 000000000..517ce27fe --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/WarpOrder.cs @@ -0,0 +1,171 @@ +using FrEee.Objects.LogMessages; +using FrEee.Objects.Space; +using FrEee.Utility; +using FrEee.Serialization; +using FrEee.Extensions; +using System; +using System.Collections.Generic; +using System.Linq; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.Orders; + +/// +/// An order to warp a mobile space object via a warp point. +/// TODO - make this also pursue the warp point first +/// +[Serializable] +public class WarpOrder : IOrder +{ + public WarpOrder(WarpPoint warpPoint) + { + Owner = Empire.Current; + WarpPoint = warpPoint; + } + + public bool ConsumesMovement + { + get { return true; } + } + + public Sector Destination + { + get { return WarpPoint.Target; } + } + + public long ID { get; set; } + + public bool IsComplete + { + get; + set; + } + + public bool IsDisposed { get; set; } + + [DoNotSerialize] + public bool LoggedPathfindingError + { + get; + private set; + } + + /// + /// The empire which issued the order. + /// + [DoNotSerialize] + public Empire Owner { get { return owner; } set { owner = value; } } + + /// + /// The warp point we are using. + /// + [DoNotSerialize] + public WarpPoint WarpPoint { get { return warpPoint; } set { warpPoint = value; } } + + private GalaxyReference owner { get; set; } + private GalaxyReference warpPoint { get; set; } + + public bool CheckCompletion(IOrderable v) + { + return IsComplete; + } + + /// + /// Orders are visible only to their owners. + /// + /// + /// + public Visibility CheckVisibility(Empire emp) + { + if (emp == Owner) + return Visibility.Visible; + return Visibility.Unknown; + } + + public IDictionary, ISet>> CreateDijkstraMap(IMobileSpaceObject me, Sector start) + { + return Pathfinder.CreateDijkstraMap(me, start, Destination, false, true); + } + + public void Dispose() + { + if (IsDisposed) + return; + foreach (var v in Galaxy.Current.Referrables.OfType()) + v.RemoveOrder(this); + Galaxy.Current.UnassignID(this); + } + + public void Execute(IOrderable ord) + { + var errors = GetErrors(ord); + foreach (var error in errors) + Owner.Log.Add(error); + + var sobj = (IMobileSpaceObject)ord; + if (!errors.Any()) + { + var here = sobj.Sector; + if (here == WarpPoint.Sector) + { + // warp now!!! + here.Remove(sobj); + + // warp point turbulence damage? + if (WarpPoint.HasAbility("Warp Point - Turbulence")) + { + var dmg = WarpPoint.GetAbilityValue("Warp Point - Turbulence").ToInt(); + sobj.TakeNormalDamage(dmg); + if (sobj.IsDestroyed) + { + sobj.Owner.Log.Add(sobj.CreateLogMessage(sobj + " was destroyed by turbulence when traversing " + WarpPoint + ".", LogMessageType.Generic)); + IsComplete = true; + return; + } + else + sobj.Owner.Log.Add(sobj.CreateLogMessage(sobj + " took " + dmg + " points of damage from turbulence when traversing " + WarpPoint + ".", LogMessageType.Generic)); + } + + sobj.Sector = WarpPoint.Target; + sobj.RefreshDijkstraMap(); + + // mark system explored + WarpPoint.TargetStarSystemLocation.Item.MarkAsExploredBy(((ISpaceObject)sobj).Owner); + + // done warping + IsComplete = true; + } + else + { + // can'IMobileSpaceObject warp here, maybe the GUI should have issued a move order? + ((ISpaceObject)sobj).Owner.Log.Add(sobj.CreateLogMessage(sobj + " cannot warp via " + WarpPoint + " because it is not currently located at the warp point.", LogMessageType.Warning)); + } + } + + // spend time + sobj.SpendTime(sobj.TimePerMove); + } + + public IEnumerable GetErrors(IOrderable executor) + { + // this order doesn't error + yield break; + } + + public IEnumerable Pathfind(IMobileSpaceObject me, Sector start) + { + return Pathfinder.Pathfind(me, start, Destination, false, true, me.DijkstraMap); + } + + public void ReplaceClientIDs(IDictionary idmap, ISet done = null) + { + // This type does not use client objects, so nothing to do here. + } + + public override string ToString() + { + if (WarpPoint == null) + return "Invalid Warp Order"; + return "Warp via " + WarpPoint.Name + " in " + WarpPoint.FindStarSystem(); + } +} diff --git a/FrEee/Objects/Civilization/Orders/WaypointOrder.cs b/FrEee/Objects/Civilization/Orders/WaypointOrder.cs new file mode 100644 index 000000000..4d41caaba --- /dev/null +++ b/FrEee/Objects/Civilization/Orders/WaypointOrder.cs @@ -0,0 +1,209 @@ +using FrEee.Objects.LogMessages; +using FrEee.Objects.Space; +using FrEee.Utility; +using FrEee.Serialization; +using FrEee.Extensions; +using System.Collections.Generic; +using System.Linq; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Civilization.Orders; + +/// +/// An order to move to a waypoint. +/// +/// +public class WaypointOrder : IMovementOrder +{ + public WaypointOrder(Waypoint target, bool avoidEnemies) + { + Owner = Empire.Current; + Target = target; + AvoidEnemies = avoidEnemies; + // TODO - add flag for "avoid damaging sectors"? but how to specify in UI? + } + + /// + /// Should pathfinding avoid enemies? + /// + public bool AvoidEnemies { get; set; } + + public bool ConsumesMovement + { + get { return true; } + } + + public Sector Destination + { + get { return Target.Sector; } + } + + public long ID { get; set; } + + public bool IsComplete + { + get; + set; + } + + public bool IsDisposed { get; set; } + + /// + /// Did we already log a pathfinding error this turn? + /// + [DoNotSerialize] + public bool LoggedPathfindingError { get; private set; } + + /// + /// The empire which issued the order. + /// + [DoNotSerialize] + public Empire Owner { get { return owner; } set { owner = value; } } + + /// + /// Any pathfinding error that we might have found. + /// + [DoNotSerialize] + public LogMessage PathfindingError { get; private set; } + + /// + /// The target we are pursuing. + /// + [DoNotSerialize] + public Waypoint Target { get { return target.Value; } set { target = value.ReferViaGalaxy(); } } + + /// + /// A verb used to describe this order. + /// + public string Verb + { + get + { + if (AvoidEnemies) + return "navigate to"; + else + return "patrol"; + } + } + + private GalaxyReference owner { get; set; } + private GalaxyReference target { get; set; } + + public bool CheckCompletion(IOrderable v) + { + return IsComplete; + } + + /// + /// Orders are visible only to their owners. + /// + /// + /// + public Visibility CheckVisibility(Empire emp) + { + if (emp == Owner) + return Visibility.Visible; + return Visibility.Unknown; + } + + public IDictionary, ISet>> CreateDijkstraMap(IMobileSpaceObject me, Sector start) + { + return Pathfinder.CreateDijkstraMap(me, start, Destination, AvoidEnemies, true); + } + + public void Dispose() + { + if (IsDisposed) + return; + foreach (var v in Galaxy.Current.FindSpaceObjects()) + v.RemoveOrder(this); + Galaxy.Current.UnassignID(this); + } + + public void Execute(IOrderable ord) + { + if (ord is IMobileSpaceObject sobj) + { + // TODO - movement logs + if (Target == null) + IsComplete = true; // target waypoint doesn't exist anymore + else if (sobj.Sector == Target.Sector) + IsComplete = true; // we've arrived at the target + else + { + var gotoSector = Pathfind(sobj, sobj.Sector).FirstOrDefault(); + if (gotoSector != null) + { + // move + sobj.Sector = gotoSector; + sobj.RefreshDijkstraMap(); + + // consume supplies + sobj.BurnMovementSupplies(); + + // resupply space vehicles + // either this vehicle from other space objects, or other vehicles from this one + // TODO - this should really be done AFTER battles... + if (gotoSector.HasAbility("Supply Generation", sobj.Owner)) + { + foreach (var v in gotoSector.SpaceObjects.OfType().Where(v => v.Owner == sobj.Owner)) + v.SupplyRemaining = v.SupplyStorage; + } + if (gotoSector.StarSystem.HasAbility("Supply Generation - System", sobj.Owner) || gotoSector.StarSystem.HasAbility("Supply Generation - System")) + { + foreach (var v in gotoSector.StarSystem.FindSpaceObjects().Where(v => v.Owner == sobj.Owner)) + v.SupplyRemaining = v.SupplyStorage; + } + } + else if (!LoggedPathfindingError) + { + // log pathfinding error + string reason; + if (sobj.StrategicSpeed <= 0) + reason = sobj + " is immobile"; + else + reason = "there is no available path leading toward " + Destination; + PathfindingError = sobj.CreateLogMessage(sobj + " could not " + Verb + " " + Target + " because " + reason + ".", LogMessageType.Warning); + sobj.Owner.Log.Add(PathfindingError); + LoggedPathfindingError = true; + } + } + + // spend time + sobj.SpendTime(sobj.TimePerMove); + } + else + ord.RecordLog($"{ord} cannot move to a waypoint because it is not a mobile space object.", LogMessageType.Error); + } + + public IEnumerable GetErrors(IOrderable v) + { + if (!(v is IMobileSpaceObject)) + yield return v.CreateLogMessage($"{v} cannot move to a waypoint because it is not a mobile space object.", LogMessageType.Error); + if (PathfindingError != null) + yield return PathfindingError; + } + + /// + /// Finds the path for executing this order. + /// + /// The space object executing the order. + /// The start location (need not be the current location, in case there are prior orders queued). + /// + public IEnumerable Pathfind(IMobileSpaceObject me, Sector start) + { + return Pathfinder.Pathfind(me, start, Destination, AvoidEnemies, true, me.DijkstraMap); + } + + public void ReplaceClientIDs(IDictionary idmap, ISet done) + { + target.ReplaceClientIDs(idmap, done); + } + + public override string ToString() + { + if (Target == null) + return "Unknown " + Verb + " order"; + return Verb.Capitalize() + " " + Target; + } +} \ No newline at end of file diff --git a/FrEee/Objects/Civilization/PlayerInfo.cs b/FrEee/Objects/Civilization/PlayerInfo.cs index 3c82de402..12ae645b8 100644 --- a/FrEee/Objects/Civilization/PlayerInfo.cs +++ b/FrEee/Objects/Civilization/PlayerInfo.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; +using FrEee.Objects.GameState; using System.Collections.Generic; namespace FrEee.Objects.Civilization; diff --git a/FrEee/Objects/Civilization/Race.cs b/FrEee/Objects/Civilization/Race.cs index a7971507e..e90b1664d 100644 --- a/FrEee/Objects/Civilization/Race.cs +++ b/FrEee/Objects/Civilization/Race.cs @@ -1,15 +1,15 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.Space; using FrEee.Modding; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Objects.Civilization; diff --git a/FrEee/Objects/Civilization/SpaceObjectWaypoint.cs b/FrEee/Objects/Civilization/SpaceObjectWaypoint.cs index b1ecef8c1..58278cb2c 100644 --- a/FrEee/Objects/Civilization/SpaceObjectWaypoint.cs +++ b/FrEee/Objects/Civilization/SpaceObjectWaypoint.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; +using FrEee.Objects.Space; using FrEee.Serialization; using FrEee.Extensions; using System; diff --git a/FrEee/Objects/Civilization/Trait.cs b/FrEee/Objects/Civilization/Trait.cs index 6955a5147..62b2ecb11 100644 --- a/FrEee/Objects/Civilization/Trait.cs +++ b/FrEee/Objects/Civilization/Trait.cs @@ -1,12 +1,10 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Abilities; +using FrEee.Objects.Abilities; using FrEee.Modding; -using FrEee.Modding.Interfaces; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; +using FrEee.Objects.GameState; namespace FrEee.Objects.Civilization; diff --git a/FrEee/Objects/Civilization/Waypoint.cs b/FrEee/Objects/Civilization/Waypoint.cs index 410721114..1603b82d4 100644 --- a/FrEee/Objects/Civilization/Waypoint.cs +++ b/FrEee/Objects/Civilization/Waypoint.cs @@ -1,6 +1,5 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Orders; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.GameState; using FrEee.Objects.Space; using FrEee.Serialization; using System.Collections.Generic; diff --git a/FrEee/Objects/Combat2/Battle_Space.cs b/FrEee/Objects/Combat/Combat2/Battle_Space.cs similarity index 100% rename from FrEee/Objects/Combat2/Battle_Space.cs rename to FrEee/Objects/Combat/Combat2/Battle_Space.cs diff --git a/FrEee/Objects/Combat2/CombatPlanet.cs b/FrEee/Objects/Combat/Combat2/CombatPlanet.cs similarity index 100% rename from FrEee/Objects/Combat2/CombatPlanet.cs rename to FrEee/Objects/Combat/Combat2/CombatPlanet.cs diff --git a/FrEee/Objects/Combat2/CombatReplayLog.cs b/FrEee/Objects/Combat/Combat2/CombatReplayLog.cs similarity index 100% rename from FrEee/Objects/Combat2/CombatReplayLog.cs rename to FrEee/Objects/Combat/Combat2/CombatReplayLog.cs diff --git a/FrEee/Objects/Combat2/CombatSeeker.cs b/FrEee/Objects/Combat/Combat2/CombatSeeker.cs similarity index 100% rename from FrEee/Objects/Combat2/CombatSeeker.cs rename to FrEee/Objects/Combat/Combat2/CombatSeeker.cs diff --git a/FrEee/Objects/Combat2/CombatShot.cs b/FrEee/Objects/Combat/Combat2/CombatShot.cs similarity index 100% rename from FrEee/Objects/Combat2/CombatShot.cs rename to FrEee/Objects/Combat/Combat2/CombatShot.cs diff --git a/FrEee/Objects/Combat2/CombatVehicle.cs b/FrEee/Objects/Combat/Combat2/CombatVehicle.cs similarity index 100% rename from FrEee/Objects/Combat2/CombatVehicle.cs rename to FrEee/Objects/Combat/Combat2/CombatVehicle.cs diff --git a/FrEee/Objects/Combat2/CombatWeapon.cs b/FrEee/Objects/Combat/Combat2/CombatWeapon.cs similarity index 100% rename from FrEee/Objects/Combat2/CombatWeapon.cs rename to FrEee/Objects/Combat/Combat2/CombatWeapon.cs diff --git a/FrEee/Objects/Combat2/ControlledCombatObject.cs b/FrEee/Objects/Combat/Combat2/ControlledCombatObject.cs similarity index 100% rename from FrEee/Objects/Combat2/ControlledCombatObject.cs rename to FrEee/Objects/Combat/Combat2/ControlledCombatObject.cs diff --git a/FrEee/Objects/Combat2/StrategyBlocks.cs b/FrEee/Objects/Combat/Combat2/StrategyBlocks.cs similarity index 100% rename from FrEee/Objects/Combat2/StrategyBlocks.cs rename to FrEee/Objects/Combat/Combat2/StrategyBlocks.cs diff --git a/FrEee/Objects/Combat2/StrategyObject.cs b/FrEee/Objects/Combat/Combat2/StrategyObject.cs similarity index 100% rename from FrEee/Objects/Combat2/StrategyObject.cs rename to FrEee/Objects/Combat/Combat2/StrategyObject.cs diff --git a/FrEee/Objects/Combat2/Tactics/Tactic.cs b/FrEee/Objects/Combat/Combat2/Tactics/Tactic.cs similarity index 100% rename from FrEee/Objects/Combat2/Tactics/Tactic.cs rename to FrEee/Objects/Combat/Combat2/Tactics/Tactic.cs diff --git a/FrEee/Objects/Combat2/Tactics/TacticBlock.cs b/FrEee/Objects/Combat/Combat2/Tactics/TacticBlock.cs similarity index 100% rename from FrEee/Objects/Combat2/Tactics/TacticBlock.cs rename to FrEee/Objects/Combat/Combat2/Tactics/TacticBlock.cs diff --git a/FrEee/Objects/Combat2/Tactics/TacticNode.cs b/FrEee/Objects/Combat/Combat2/Tactics/TacticNode.cs similarity index 100% rename from FrEee/Objects/Combat2/Tactics/TacticNode.cs rename to FrEee/Objects/Combat/Combat2/Tactics/TacticNode.cs diff --git a/FrEee/Objects/Combat2/combatObjects.cs b/FrEee/Objects/Combat/Combat2/combatObjects.cs similarity index 100% rename from FrEee/Objects/Combat2/combatObjects.cs rename to FrEee/Objects/Combat/Combat2/combatObjects.cs diff --git a/FrEee/Objects/Combat2/lib/FixMath.dll b/FrEee/Objects/Combat/Combat2/lib/FixMath.dll similarity index 100% rename from FrEee/Objects/Combat2/lib/FixMath.dll rename to FrEee/Objects/Combat/Combat2/lib/FixMath.dll diff --git a/FrEee/Objects/Combat2/lib/FixMath.pdb b/FrEee/Objects/Combat/Combat2/lib/FixMath.pdb similarity index 100% rename from FrEee/Objects/Combat2/lib/FixMath.pdb rename to FrEee/Objects/Combat/Combat2/lib/FixMath.pdb diff --git a/FrEee/Objects/Combat2/lib/NewtMath.dll b/FrEee/Objects/Combat/Combat2/lib/NewtMath.dll similarity index 100% rename from FrEee/Objects/Combat2/lib/NewtMath.dll rename to FrEee/Objects/Combat/Combat2/lib/NewtMath.dll diff --git a/FrEee/Objects/Combat2/lib/NewtMath.pdb b/FrEee/Objects/Combat/Combat2/lib/NewtMath.pdb similarity index 100% rename from FrEee/Objects/Combat2/lib/NewtMath.pdb rename to FrEee/Objects/Combat/Combat2/lib/NewtMath.pdb diff --git a/FrEee/Objects/Combat2/lib/nunit.framework.dll b/FrEee/Objects/Combat/Combat2/lib/nunit.framework.dll similarity index 100% rename from FrEee/Objects/Combat2/lib/nunit.framework.dll rename to FrEee/Objects/Combat/Combat2/lib/nunit.framework.dll diff --git a/FrEee/Objects/Combat2/lib/nunit.framework.xml b/FrEee/Objects/Combat/Combat2/lib/nunit.framework.xml similarity index 100% rename from FrEee/Objects/Combat2/lib/nunit.framework.xml rename to FrEee/Objects/Combat/Combat2/lib/nunit.framework.xml diff --git a/FrEee/Objects/Combat/Grid/Battle.cs b/FrEee/Objects/Combat/Grid/Battle.cs index 22c7db6d8..9296a3e04 100644 --- a/FrEee/Objects/Combat/Grid/Battle.cs +++ b/FrEee/Objects/Combat/Grid/Battle.cs @@ -1,5 +1,3 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Objects.LogMessages; using FrEee.Objects.Space; @@ -14,6 +12,8 @@ using System.Drawing; using System.Linq; using static System.Math; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.GameState; namespace FrEee.Objects.Combat.Grid; diff --git a/FrEee/Objects/Combat/Grid/BattleEvent.cs b/FrEee/Objects/Combat/Grid/BattleEvent.cs index 6a38c1a28..00ac7ffc4 100644 --- a/FrEee/Objects/Combat/Grid/BattleEvent.cs +++ b/FrEee/Objects/Combat/Grid/BattleEvent.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Utility; +using FrEee.Utility; using FrEee.Serialization; using FrEee.Extensions; diff --git a/FrEee/Objects/Combat/Grid/CombatantAppearsEvent.cs b/FrEee/Objects/Combat/Grid/CombatantAppearsEvent.cs index 523656f81..1a0b0d6dc 100644 --- a/FrEee/Objects/Combat/Grid/CombatantAppearsEvent.cs +++ b/FrEee/Objects/Combat/Grid/CombatantAppearsEvent.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Utility; +using FrEee.Utility; using System.Linq; namespace FrEee.Objects.Combat.Grid; diff --git a/FrEee/Objects/Combat/Grid/CombatantDestroyedEvent.cs b/FrEee/Objects/Combat/Grid/CombatantDestroyedEvent.cs index fc8bf27c8..c5a91f88b 100644 --- a/FrEee/Objects/Combat/Grid/CombatantDestroyedEvent.cs +++ b/FrEee/Objects/Combat/Grid/CombatantDestroyedEvent.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Utility; +using FrEee.Utility; namespace FrEee.Objects.Combat.Grid; public class CombatantDestroyedEvent : BattleEvent diff --git a/FrEee/Objects/Combat/Grid/CombatantDisappearsEvent.cs b/FrEee/Objects/Combat/Grid/CombatantDisappearsEvent.cs index 6424e1dda..66fceb41e 100644 --- a/FrEee/Objects/Combat/Grid/CombatantDisappearsEvent.cs +++ b/FrEee/Objects/Combat/Grid/CombatantDisappearsEvent.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Utility; +using FrEee.Utility; using System; namespace FrEee.Objects.Combat.Grid; diff --git a/FrEee/Objects/Combat/Grid/CombatantLaunchedEvent.cs b/FrEee/Objects/Combat/Grid/CombatantLaunchedEvent.cs index 8bfd3654c..991792445 100644 --- a/FrEee/Objects/Combat/Grid/CombatantLaunchedEvent.cs +++ b/FrEee/Objects/Combat/Grid/CombatantLaunchedEvent.cs @@ -1,5 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; namespace FrEee.Objects.Combat.Grid; diff --git a/FrEee/Objects/Combat/Grid/CombatantMovesEvent.cs b/FrEee/Objects/Combat/Grid/CombatantMovesEvent.cs index 6921ab939..6bab57402 100644 --- a/FrEee/Objects/Combat/Grid/CombatantMovesEvent.cs +++ b/FrEee/Objects/Combat/Grid/CombatantMovesEvent.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Utility; +using FrEee.Utility; namespace FrEee.Objects.Combat.Grid; public class CombatantMovesEvent : BattleEvent diff --git a/FrEee/Objects/Combat/Grid/CombatantsCollideEvent.cs b/FrEee/Objects/Combat/Grid/CombatantsCollideEvent.cs index 64e30d208..28a4df429 100644 --- a/FrEee/Objects/Combat/Grid/CombatantsCollideEvent.cs +++ b/FrEee/Objects/Combat/Grid/CombatantsCollideEvent.cs @@ -1,5 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; namespace FrEee.Objects.Combat.Grid; diff --git a/FrEee/Objects/Combat/Grid/GroundBattle.cs b/FrEee/Objects/Combat/Grid/GroundBattle.cs index 2b3d45f3a..d448f3c52 100644 --- a/FrEee/Objects/Combat/Grid/GroundBattle.cs +++ b/FrEee/Objects/Combat/Grid/GroundBattle.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Objects.Vehicles; using FrEee.Modding; @@ -8,6 +7,7 @@ using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Objects.Combat.Grid; diff --git a/FrEee/Objects/Combat/Grid/IBattleEvent.cs b/FrEee/Objects/Combat/Grid/IBattleEvent.cs index 923b398fe..55a303e33 100644 --- a/FrEee/Objects/Combat/Grid/IBattleEvent.cs +++ b/FrEee/Objects/Combat/Grid/IBattleEvent.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Utility; +using FrEee.Utility; namespace FrEee.Objects.Combat.Grid; public interface IBattleEvent diff --git a/FrEee/Objects/Combat/Grid/SpaceBattle.cs b/FrEee/Objects/Combat/Grid/SpaceBattle.cs index 52554d2b7..b7a315fd0 100644 --- a/FrEee/Objects/Combat/Grid/SpaceBattle.cs +++ b/FrEee/Objects/Combat/Grid/SpaceBattle.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; +using FrEee.Objects.Space; using FrEee.Modding; using FrEee.Utility; using FrEee.Extensions; @@ -7,6 +6,7 @@ using System.Collections.Generic; using System.Linq; using static System.Math; +using FrEee.Objects.GameState; namespace FrEee.Objects.Combat.Grid; diff --git a/FrEee/Objects/Combat/Grid/WeaponFiresEvent.cs b/FrEee/Objects/Combat/Grid/WeaponFiresEvent.cs index 0cda31c2b..7a2ce8525 100644 --- a/FrEee/Objects/Combat/Grid/WeaponFiresEvent.cs +++ b/FrEee/Objects/Combat/Grid/WeaponFiresEvent.cs @@ -1,6 +1,6 @@ -using FrEee.Interfaces; -using FrEee.Objects.Technology; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Objects.Technology; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; namespace FrEee.Objects.Combat.Grid; diff --git a/FrEee/Objects/Combat/Hit.cs b/FrEee/Objects/Combat/Hit.cs index d5b8a733d..d4e36760d 100644 --- a/FrEee/Objects/Combat/Hit.cs +++ b/FrEee/Objects/Combat/Hit.cs @@ -1,8 +1,7 @@ using System.Collections.Generic; -using FrEee.Interfaces; using FrEee.Modding; -using FrEee.Modding.Interfaces; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; namespace FrEee.Objects.Combat; diff --git a/FrEee/Objects/Combat/IBattle.cs b/FrEee/Objects/Combat/IBattle.cs index 67f2f97b0..2581b549b 100644 --- a/FrEee/Objects/Combat/IBattle.cs +++ b/FrEee/Objects/Combat/IBattle.cs @@ -1,6 +1,7 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; +using FrEee.Objects.GameState; using FrEee.Objects.LogMessages; +using FrEee.Objects.Space; using FrEee.Utility; using System.Collections.Generic; diff --git a/FrEee/Interfaces/ICombatSpaceObject.cs b/FrEee/Objects/Combat/ICombatSpaceObject.cs similarity index 70% rename from FrEee/Interfaces/ICombatSpaceObject.cs rename to FrEee/Objects/Combat/ICombatSpaceObject.cs index 55af7a372..0f81195ea 100644 --- a/FrEee/Interfaces/ICombatSpaceObject.cs +++ b/FrEee/Objects/Combat/ICombatSpaceObject.cs @@ -1,4 +1,6 @@ -namespace FrEee.Interfaces; +using FrEee.Objects.Space; + +namespace FrEee.Objects.Combat; /// /// A space object that can participate in combat. diff --git a/FrEee/Objects/Combat/ICombatant.cs b/FrEee/Objects/Combat/ICombatant.cs new file mode 100644 index 000000000..86e56828f --- /dev/null +++ b/FrEee/Objects/Combat/ICombatant.cs @@ -0,0 +1,62 @@ +using FrEee.Objects.Civilization; +using FrEee.Objects.GameState; +using FrEee.Objects.Technology; +using System; +using System.Collections.Generic; + +namespace FrEee.Objects.Combat; + +/// +/// An object that can fire weapons and/or targeted by weapons. +/// +public interface ICombatant : IPictorial, ITargetable, IDisposable, IFoggable, INamed +{ + /// + /// Accuracy rating of this combatant. + /// + int Accuracy { get; } + + /// + /// How fast can this combatant move in combat? + /// + double CombatSpeed { get; } + + /// + /// Is this combatant still alive or is it destroyed/glassed? + /// + bool IsAlive { get; } + + /// + /// How many targets can this combatant fire on per round (excluding point defense weapons or warheads)? + /// + int MaxTargets { get; } + + /// + /// Any components of this combatant. + /// + IEnumerable Components { get; } + + /// + /// Any undamaged weapons this combatant is armed with. + /// + IEnumerable Weapons { get; } + + /// + /// Can this object fire on another object? + /// + /// + /// true if the target is an enemy and this combatant has weapons capable of targeting it + bool CanTarget(ITargetable target); + + bool IsHostileTo(Empire emp); + + /// + /// Does this combatant "fill" a tile (prevent other tile-filling combatants from occupying the tile)? + /// + bool FillsCombatTile { get; } + + /// + /// The size of this combatant. Used for determining who goes where in a formation. + /// + int Size { get; } +} \ No newline at end of file diff --git a/FrEee/Objects/Combat/IDamageable.cs b/FrEee/Objects/Combat/IDamageable.cs new file mode 100644 index 000000000..896a5305a --- /dev/null +++ b/FrEee/Objects/Combat/IDamageable.cs @@ -0,0 +1,75 @@ +using FrEee.Serialization; +using FrEee.Utility; +namespace FrEee.Objects.Combat; + +/// +/// Something which can take damage. +/// +public interface IDamageable +{ + int ArmorHitpoints { get; } + + /// + /// The chance for this object to be hit relative to another object that could also be hit by the same shot. + /// + int HitChance { get; } + + /// + /// Current hitpoints. + /// + int Hitpoints { get; set; } + + int HullHitpoints { get; } + + /// + /// Is this object destroyed? + /// + bool IsDestroyed { get; } + + int MaxArmorHitpoints { get; } + + int MaxHitpoints { get; } + + int MaxHullHitpoints { get; } + + int MaxNormalShields { get; } + + int MaxPhasedShields { get; } + + int MaxShieldHitpoints { get; } + + /// + /// Normal shield points. + /// + int NormalShields { get; set; } + + /// + /// Phased shield points. + /// + int PhasedShields { get; set; } + + int ShieldHitpoints { get; } + + /// + /// Replenishes HP. + /// If amount is are not specified, replenishes all HP. + /// Otherwise repair points have a different effect on different objects. + /// E.g. on a ship a repair point repairs 1 component while on a component a repair point replenishes 1 HP. + /// + /// The amount of unused repair points left over, or null for infinite. + int? Repair(int? amount = null); + + /// + /// Replenishes normal and phased shields. + /// + /// How many shields to replenish, or null to replenish all of them. + void ReplenishShields(int? amount = null); + + /// + /// Takes damage. + /// + /// Leftover damage. + int TakeDamage(Hit hit, PRNG dice = null); +} + +public interface IDamageableReferrable : IDamageable, IReferrable { } \ No newline at end of file diff --git a/FrEee/Objects/Combat/ITargetable.cs b/FrEee/Objects/Combat/ITargetable.cs new file mode 100644 index 000000000..fd2a2155f --- /dev/null +++ b/FrEee/Objects/Combat/ITargetable.cs @@ -0,0 +1,19 @@ +using FrEee.Objects.Civilization; + +namespace FrEee.Objects.Combat; + +/// +/// Something which can be specifically target by weapons. +/// +public interface ITargetable : IDamageableReferrable, ITransferrable +{ + /// + /// Evasion rating of this combatant. + /// + int Evasion { get; } + + /// + /// What type of object is this for weapon targeting purposes? + /// + WeaponTargets WeaponTargetType { get; } +} \ No newline at end of file diff --git a/FrEee/Objects/Combat/Seeker.cs b/FrEee/Objects/Combat/Seeker.cs index e762aebb7..115e97bdd 100644 --- a/FrEee/Objects/Combat/Seeker.cs +++ b/FrEee/Objects/Combat/Seeker.cs @@ -1,16 +1,16 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Objects.Technology; using FrEee.Modding; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Objects.Combat; diff --git a/FrEee/Objects/Combat/Shot.cs b/FrEee/Objects/Combat/Shot.cs index 36d726c16..378a2e453 100644 --- a/FrEee/Objects/Combat/Shot.cs +++ b/FrEee/Objects/Combat/Shot.cs @@ -1,9 +1,8 @@ using System.Collections.Generic; -using FrEee.Interfaces; using FrEee.Objects.Technology; using FrEee.Modding; -using FrEee.Modding.Interfaces; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; namespace FrEee.Objects.Combat; diff --git a/FrEee/Objects/Combat/WeaponDisplayEffect.cs b/FrEee/Objects/Combat/WeaponDisplayEffect.cs index 82749c97e..4f621faaf 100644 --- a/FrEee/Objects/Combat/WeaponDisplayEffect.cs +++ b/FrEee/Objects/Combat/WeaponDisplayEffect.cs @@ -1,10 +1,10 @@ -using FrEee.Interfaces; -using FrEee.Utility; +using FrEee.Utility; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.IO; +using FrEee.Objects.GameState; namespace FrEee.Objects.Combat; diff --git a/FrEee/Objects/Combat/WeaponTargets.cs b/FrEee/Objects/Combat/WeaponTargets.cs new file mode 100644 index 000000000..89e08d0bf --- /dev/null +++ b/FrEee/Objects/Combat/WeaponTargets.cs @@ -0,0 +1,42 @@ +using FrEee.Utility; +using System; + +namespace FrEee.Objects.Combat; + +/// +/// Used to limit what a weapon can fire at. +/// +[Flags] +public enum WeaponTargets +{ + None = 0x0, + + // NOTE - SE4 uses Ships as the target type for bases as well + [Name("Ships")] + Ship = 0x1, + + //[Name("Bases")] + //Base = 0x2, + + [Name("Fighters")] + [Name("Ftr")] + Fighter = 0x4, + + [Name("Satellites")] + [Name("Sat")] + Satellite = 0x8, + + [Name("Drones")] + Drone = 0x40, + + [Name("Planets")] + Planet = 0x100, + + [Name("Seekers")] + Seeker = 0x200, + + [Name("Any")] + All = Ship | /*Base |*/ Fighter | Satellite | Drone | Planet | Seeker, + + Invalid = 0x400, +} \ No newline at end of file diff --git a/FrEee/Objects/Combat/WeaponTypes.cs b/FrEee/Objects/Combat/WeaponTypes.cs new file mode 100644 index 000000000..9f62197c3 --- /dev/null +++ b/FrEee/Objects/Combat/WeaponTypes.cs @@ -0,0 +1,69 @@ +using System; +using FrEee.Utility; +namespace FrEee.Objects.Combat; + +/// +/// Types of weapons. +/// +[Flags] +public enum WeaponTypes +{ + /// + /// Not a weapon. + /// + [CanonicalName("Not a Weapon")] + [Name("Not A Weapon")] + [Name("None")] + NotAWeapon = 0x1, + + /// + /// Direct fire weapon. + /// + [CanonicalName("Direct Fire")] + [Name("Direct-Fire")] + DirectFire = 0x2, + + /// + /// Seeking weapon. + /// + Seeking = 0x4, + + /// + /// Explodes when ramming an enemy. + /// + Warhead = 0x8, + + /// + /// Weapon which fires automatically at incoming targets like a direct fire weapon. + /// + [CanonicalName("Direct Fire Point Defense")] + [Name("Direct-Fire Point-Defense")] + [Name("Point-Defense")] + [Name("Point Defense")] + DirectFirePointDefense = 0x10, + + /// + /// Weapon which fires automatically at incoming targets like a seeking weapon. + /// + [CanonicalName("Seeking Point Defense")] + [Name("Seeking Point-Defense")] + SeekingPointDefense = 0x20, + + /// + /// Explodes when ramming an enemy or when being rammed. + /// + [CanonicalName("Warhead Point Defense")] + [Name("Warhead Point-Defense")] + WarheadPointDefense = 0x40, + + /// + /// All types of weapons. Not nonweapons. + /// + [Name("Any")] + All = DirectFire | Seeking | DirectFirePointDefense | SeekingPointDefense | WarheadPointDefense, + + /// + /// Any component, including nonweapons. + /// + AnyComponent = All | NotAWeapon, +} \ No newline at end of file diff --git a/FrEee/Objects/Commands/AddOrderCommand.cs b/FrEee/Objects/Commands/AddOrderCommand.cs index 11cea92bf..20ca38ab4 100644 --- a/FrEee/Objects/Commands/AddOrderCommand.cs +++ b/FrEee/Objects/Commands/AddOrderCommand.cs @@ -1,6 +1,9 @@ -using FrEee.Interfaces; +using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.GameState; using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; +using FrEee.Serialization; using System; using System.Collections.Generic; diff --git a/FrEee/Objects/Commands/ClearPrivateNameCommand.cs b/FrEee/Objects/Commands/ClearPrivateNameCommand.cs index 76447e114..b51d88e82 100644 --- a/FrEee/Objects/Commands/ClearPrivateNameCommand.cs +++ b/FrEee/Objects/Commands/ClearPrivateNameCommand.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Serialization; using FrEee.Extensions; diff --git a/FrEee/Objects/Commands/Command.cs b/FrEee/Objects/Commands/Command.cs index 7f9b76c14..684cf9960 100644 --- a/FrEee/Objects/Commands/Command.cs +++ b/FrEee/Objects/Commands/Command.cs @@ -1,5 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; +using FrEee.Objects.GameState; using FrEee.Serialization; using System; using System.Collections.Generic; diff --git a/FrEee/Objects/Commands/CreateDesignCommand.cs b/FrEee/Objects/Commands/CreateDesignCommand.cs index 2c62a2e6e..5e57dc5a3 100644 --- a/FrEee/Objects/Commands/CreateDesignCommand.cs +++ b/FrEee/Objects/Commands/CreateDesignCommand.cs @@ -1,9 +1,10 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.Vehicles; +using FrEee.Serialization; namespace FrEee.Objects.Commands; diff --git a/FrEee/Objects/Commands/CreateFleetCommand.cs b/FrEee/Objects/Commands/CreateFleetCommand.cs index aae6905f9..a53400bf5 100644 --- a/FrEee/Objects/Commands/CreateFleetCommand.cs +++ b/FrEee/Objects/Commands/CreateFleetCommand.cs @@ -1,6 +1,7 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; +using FrEee.Objects.GameState; using FrEee.Objects.Space; +using FrEee.Serialization; using System.Collections.Generic; namespace FrEee.Objects.Commands; diff --git a/FrEee/Objects/Commands/CreateWaypointCommand.cs b/FrEee/Objects/Commands/CreateWaypointCommand.cs index 9590598b1..d0af2150a 100644 --- a/FrEee/Objects/Commands/CreateWaypointCommand.cs +++ b/FrEee/Objects/Commands/CreateWaypointCommand.cs @@ -1,5 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; +using FrEee.Serialization; using System.Collections.Generic; namespace FrEee.Objects.Commands; diff --git a/FrEee/Objects/Commands/DeleteMessageCommand.cs b/FrEee/Objects/Commands/DeleteMessageCommand.cs index 6759fbdd2..4cef4b7c1 100644 --- a/FrEee/Objects/Commands/DeleteMessageCommand.cs +++ b/FrEee/Objects/Commands/DeleteMessageCommand.cs @@ -1,7 +1,7 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Serialization; using FrEee.Extensions; +using FrEee.Objects.Civilization.Diplomacy.Messages; namespace FrEee.Objects.Commands; diff --git a/FrEee/Objects/Commands/HotkeyWaypointCommand.cs b/FrEee/Objects/Commands/HotkeyWaypointCommand.cs index 2ec19d1e6..6757989a8 100644 --- a/FrEee/Objects/Commands/HotkeyWaypointCommand.cs +++ b/FrEee/Objects/Commands/HotkeyWaypointCommand.cs @@ -1,9 +1,10 @@ -using FrEee.Interfaces; + using FrEee.Objects.Civilization; -using FrEee.Objects.Orders; using FrEee.Objects.Space; using FrEee.Extensions; using System; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.GameState; namespace FrEee.Objects.Commands; diff --git a/FrEee/Objects/Commands/ICommand.cs b/FrEee/Objects/Commands/ICommand.cs new file mode 100644 index 000000000..9d339487d --- /dev/null +++ b/FrEee/Objects/Commands/ICommand.cs @@ -0,0 +1,43 @@ +using FrEee.Objects.Civilization; +using FrEee.Objects.GameState; +using FrEee.Serialization; +using System.Collections.Generic; + +namespace FrEee.Objects.Commands; + +/// +/// A command to some object. +/// Commands are distinguished from orders by being instantaneous, rather than queued. +/// +public interface ICommand : IPromotable +{ + IReferrable Executor { get; } + + long ExecutorID { get; } + + /// + /// The empire issuing the command. + /// + Empire Issuer { get; } + + /// + /// Any new (from the client) objects referred to by this command. + /// + IEnumerable NewReferrables { get; } + + /// + /// Executes the command. + /// + void Execute(); +} + +/// +/// A command to some object. +/// +public interface ICommand : ICommand where T : IReferrable +{ + /// + /// The object whose queue is being manipulated. + /// + new T Executor { get; set; } +} diff --git a/FrEee/Interfaces/ICreateDesignCommand.cs b/FrEee/Objects/Commands/ICreateDesignCommand.cs similarity index 65% rename from FrEee/Interfaces/ICreateDesignCommand.cs rename to FrEee/Objects/Commands/ICreateDesignCommand.cs index 686c470ad..38a1e7709 100644 --- a/FrEee/Interfaces/ICreateDesignCommand.cs +++ b/FrEee/Objects/Commands/ICreateDesignCommand.cs @@ -1,11 +1,12 @@ using FrEee.Objects.Civilization; +using FrEee.Objects.Vehicles; -namespace FrEee.Interfaces; +namespace FrEee.Objects.Commands; /// /// A command for an empire to create a design. /// public interface ICreateDesignCommand : ICommand { - IDesign Design { get; } + IDesign Design { get; } } \ No newline at end of file diff --git a/FrEee/Objects/Commands/IOrderCommand.cs b/FrEee/Objects/Commands/IOrderCommand.cs new file mode 100644 index 000000000..822298006 --- /dev/null +++ b/FrEee/Objects/Commands/IOrderCommand.cs @@ -0,0 +1,14 @@ +using FrEee.Objects.Civilization.Orders; + +namespace FrEee.Objects.Commands; + +/// +/// A command to manipulate an object's order queue. +/// +public interface IOrderCommand : ICommand +{ + /// + /// The specific order being manipulated. + /// + IOrder Order { get; set; } +} \ No newline at end of file diff --git a/FrEee/Objects/Commands/JoinFleetCommand.cs b/FrEee/Objects/Commands/JoinFleetCommand.cs index 4ef6bd322..6a1654ad1 100644 --- a/FrEee/Objects/Commands/JoinFleetCommand.cs +++ b/FrEee/Objects/Commands/JoinFleetCommand.cs @@ -1,8 +1,8 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; +using FrEee.Objects.Space; using FrEee.Serialization; using FrEee.Extensions; using System.Collections.Generic; +using FrEee.Objects.GameState; namespace FrEee.Objects.Commands; diff --git a/FrEee/Objects/Commands/LeaveFleetCommand.cs b/FrEee/Objects/Commands/LeaveFleetCommand.cs index b22e6eb9f..f203edc21 100644 --- a/FrEee/Objects/Commands/LeaveFleetCommand.cs +++ b/FrEee/Objects/Commands/LeaveFleetCommand.cs @@ -1,5 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Extensions; +using FrEee.Extensions; +using FrEee.Objects.Space; namespace FrEee.Objects.Commands; diff --git a/FrEee/Objects/Commands/MinisterToggleCommand.cs b/FrEee/Objects/Commands/MinisterToggleCommand.cs index 4a495866b..d5770dc73 100644 --- a/FrEee/Objects/Commands/MinisterToggleCommand.cs +++ b/FrEee/Objects/Commands/MinisterToggleCommand.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Serialization; using FrEee.Extensions; diff --git a/FrEee/Objects/Commands/OrderCommand.cs b/FrEee/Objects/Commands/OrderCommand.cs index 8515c7239..ca4523048 100644 --- a/FrEee/Objects/Commands/OrderCommand.cs +++ b/FrEee/Objects/Commands/OrderCommand.cs @@ -1,8 +1,10 @@ -using FrEee.Interfaces; -using FrEee.Serialization; +using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.Civilization; +using FrEee.Objects.GameState; namespace FrEee.Objects.Commands; diff --git a/FrEee/Objects/Commands/RearrangeOrdersCommand.cs b/FrEee/Objects/Commands/RearrangeOrdersCommand.cs index 605fa408f..430ef9318 100644 --- a/FrEee/Objects/Commands/RearrangeOrdersCommand.cs +++ b/FrEee/Objects/Commands/RearrangeOrdersCommand.cs @@ -1,6 +1,7 @@ -using FrEee.Interfaces; +using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.GameState; using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; using System; namespace FrEee.Objects.Commands; diff --git a/FrEee/Objects/Commands/RemoveOrderCommand.cs b/FrEee/Objects/Commands/RemoveOrderCommand.cs index 9117f79b6..780afe39d 100644 --- a/FrEee/Objects/Commands/RemoveOrderCommand.cs +++ b/FrEee/Objects/Commands/RemoveOrderCommand.cs @@ -1,8 +1,9 @@ -using FrEee.Interfaces; -using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; +using FrEee.Objects.LogMessages; using FrEee.Extensions; using System; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.Civilization; +using FrEee.Objects.GameState; namespace FrEee.Objects.Commands; diff --git a/FrEee/Objects/Commands/SendMessageCommand.cs b/FrEee/Objects/Commands/SendMessageCommand.cs index b1a9ca7c7..e34fb66d3 100644 --- a/FrEee/Objects/Commands/SendMessageCommand.cs +++ b/FrEee/Objects/Commands/SendMessageCommand.cs @@ -1,8 +1,9 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.Civilization.Diplomacy; +using FrEee.Objects.Civilization; using FrEee.Extensions; using System.Collections.Generic; +using FrEee.Objects.Civilization.Diplomacy.Messages; +using FrEee.Serialization; +using FrEee.Objects.GameState; namespace FrEee.Objects.Commands; diff --git a/FrEee/Objects/Commands/SetObsoleteFlagCommand.cs b/FrEee/Objects/Commands/SetObsoleteFlagCommand.cs index 25a8919de..10c9ee6d3 100644 --- a/FrEee/Objects/Commands/SetObsoleteFlagCommand.cs +++ b/FrEee/Objects/Commands/SetObsoleteFlagCommand.cs @@ -1,7 +1,8 @@ -using FrEee.Interfaces; -using FrEee.Serialization; +using FrEee.Serialization; using FrEee.Extensions; using System.Collections.Generic; +using FrEee.Objects.Vehicles; +using FrEee.Objects.GameState; namespace FrEee.Objects.Commands; diff --git a/FrEee/Objects/Commands/SetPlayerInfoCommand.cs b/FrEee/Objects/Commands/SetPlayerInfoCommand.cs index 6d4dcb5d3..cee96549f 100644 --- a/FrEee/Objects/Commands/SetPlayerInfoCommand.cs +++ b/FrEee/Objects/Commands/SetPlayerInfoCommand.cs @@ -1,5 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; +using FrEee.Serialization; using System; using System.Collections.Generic; diff --git a/FrEee/Objects/Commands/SetPlayerNoteCommand.cs b/FrEee/Objects/Commands/SetPlayerNoteCommand.cs index d7c08e9d1..622feb8f1 100644 --- a/FrEee/Objects/Commands/SetPlayerNoteCommand.cs +++ b/FrEee/Objects/Commands/SetPlayerNoteCommand.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Serialization; using FrEee.Extensions; diff --git a/FrEee/Objects/Commands/SetPrivateNameCommand.cs b/FrEee/Objects/Commands/SetPrivateNameCommand.cs index f7149a3ab..0282c364a 100644 --- a/FrEee/Objects/Commands/SetPrivateNameCommand.cs +++ b/FrEee/Objects/Commands/SetPrivateNameCommand.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Serialization; using FrEee.Extensions; diff --git a/FrEee/Objects/Commands/SetPublicNameCommand.cs b/FrEee/Objects/Commands/SetPublicNameCommand.cs index f5a0b4511..265a2ad1f 100644 --- a/FrEee/Objects/Commands/SetPublicNameCommand.cs +++ b/FrEee/Objects/Commands/SetPublicNameCommand.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; +using FrEee.Objects.Civilization; namespace FrEee.Objects.Commands; diff --git a/FrEee/Objects/Commands/ToggleOrdersOnHoldCommand.cs b/FrEee/Objects/Commands/ToggleOrdersOnHoldCommand.cs index f0167cc29..36095c557 100644 --- a/FrEee/Objects/Commands/ToggleOrdersOnHoldCommand.cs +++ b/FrEee/Objects/Commands/ToggleOrdersOnHoldCommand.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; +using FrEee.Objects.Civilization; namespace FrEee.Objects.Commands; diff --git a/FrEee/Objects/Commands/ToggleRepeatOrdersCommand.cs b/FrEee/Objects/Commands/ToggleRepeatOrdersCommand.cs index 4c1eccfe6..de67f1458 100644 --- a/FrEee/Objects/Commands/ToggleRepeatOrdersCommand.cs +++ b/FrEee/Objects/Commands/ToggleRepeatOrdersCommand.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; +using FrEee.Objects.Civilization; namespace FrEee.Objects.Commands; diff --git a/FrEee/Objects/Events/Event.cs b/FrEee/Objects/Events/Event.cs index a324a3522..28b076ba3 100644 --- a/FrEee/Objects/Events/Event.cs +++ b/FrEee/Objects/Events/Event.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Modding; using FrEee.Utility; @@ -9,6 +8,8 @@ using System.IO; using System.Linq; using System.Text.RegularExpressions; +using FrEee.Serialization; +using FrEee.Objects.GameState; namespace FrEee.Objects.Events; diff --git a/FrEee/Objects/Events/EventMessage.cs b/FrEee/Objects/Events/EventMessage.cs index 6b1f37bf3..020d58b4a 100644 --- a/FrEee/Objects/Events/EventMessage.cs +++ b/FrEee/Objects/Events/EventMessage.cs @@ -1,4 +1,4 @@ -using FrEee.Modding.Interfaces; +using FrEee.Modding; namespace FrEee.Objects.Events; diff --git a/FrEee/Objects/GameState/Galaxy.cs b/FrEee/Objects/GameState/Galaxy.cs new file mode 100644 index 000000000..bf02f89a0 --- /dev/null +++ b/FrEee/Objects/GameState/Galaxy.cs @@ -0,0 +1,1309 @@ +using FrEee.Objects.Abilities; +using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization.Diplomacy.Clauses; +using FrEee.Objects.Combat; +using FrEee.Objects.Combat.Grid; +using FrEee.Objects.Commands; +using FrEee.Objects.Events; +using FrEee.Objects.LogMessages; +using FrEee.Setup; +using FrEee.Setup.WarpPointPlacementStrategies; +using FrEee.Modding; +using FrEee.Utility; +using FrEee.Serialization; +using FrEee.Extensions; +using Microsoft.Scripting.Utils; +using System; +using System.Collections; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Reflection; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.Vehicles; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.VictoryConditions; +using FrEee.Objects.Space; +using FrEee.Objects.Civilization.Diplomacy; +using FrEee.Objects.Technology; + +namespace FrEee.Objects.GameState; + +/// +/// Prevents IDs from being assigned to objects when calling AssignIDs. +/// TODO - move to utility namespace? +/// +public class DoNotAssignIDAttribute : Attribute +{ + public DoNotAssignIDAttribute(bool recurse = true) + { + Recurse = recurse; + } + + /// + /// Should the "don't assign ID" rule be recursive? + /// + public bool Recurse { get; private set; } +} + +/// +/// A galaxy in which the game is played. +/// +[Serializable] +public class Galaxy : ICommonAbilityObject +{ + public Galaxy() + { + Current = this; + if (Mod.Current != null) + ModPath = Mod.Current.RootPath; + StarSystemLocations = new List>(); + Empires = new List(); + Name = "Unnamed"; + TurnNumber = 1; + referrables = new Dictionary(); + VictoryConditions = new List(); + AbilityCache = new SafeDictionary>(); + CommonAbilityCache = new SafeDictionary, IEnumerable>(); + SharedAbilityCache = new SafeDictionary, IEnumerable>(); + GivenTreatyClauseCache = new SafeDictionary>(); + ReceivedTreatyClauseCache = new SafeDictionary>(); + Battles = new HashSet(); + ScriptNotes = new DynamicDictionary(); + /*if (Mod.Current != null) + { + foreach (var q in Mod.Current.Objects.OfType()) + AssignID(q); + }*/ + } + + /// + /// The current galaxy. Shouldn't change except at loading a game or turn processing. + /// + public static Galaxy? Current { get; set; } + + public AbilityTargets AbilityTarget + { + get { return AbilityTargets.Galaxy; } + } + + /// + /// Allowed trades in this game. + /// + public AllowedTrades AllowedTrades { get; set; } + + /// + /// Should players have sensor data for all systems from the get-go? + /// + public bool AllSystemsExploredFromStart { get; set; } + + /// + /// The battles which have taken place this turn. + /// + public ICollection Battles { get; private set; } + + public bool CanColonizeOnlyBreathable { get; set; } + + public bool CanColonizeOnlyHomeworldSurface { get; set; } + + public IEnumerable Children + { + get { return StarSystemLocations.Select(l => l.Item); } + } + + public string CommandFileName + { + get + { + if (PlayerNumber > 0) + return Name + "_" + TurnNumber + "_" + PlayerNumber + FrEeeConstants.PlayerCommandsSaveGameExtension; + else + throw new InvalidOperationException("The game host does not have a command file."); + } + } + + /// + /// The empire whose turn it is. + /// + public Empire CurrentEmpire { get; set; } + + /// + /// The current tick in turn processing. 0 = start of turn, 1 = end of turn. + /// + public double CurrentTick { get; set; } + + /// + /// The empires participating in the game. + /// + public IList Empires { get; private set; } + + /// + /// Per mille chance of a random event occurring, per turn, per player. + /// + public double EventFrequency { get; set; } + + public string GameFileName + { + get + { + if (PlayerNumber > 0) + return Name + "_" + TurnNumber + "_" + PlayerNumber + FrEeeConstants.SaveGameExtension; + else + return Name + "_" + TurnNumber + FrEeeConstants.SaveGameExtension; + } + } + + public int Height + { + get; + set; + } + + public IEnumerable IntrinsicAbilities + { + // TODO - galaxy wide abilities? + get { yield break; } + } + + /// + /// Is the ability cache enabled? + /// Always enabled on the client side; only when a flag is set on the server side. + /// + public bool IsAbilityCacheEnabled + { + get + { + return Empire.Current != null || isAbilityCacheEnabled; + } + } + + public bool IsAnalysisAllowed { get; set; } + + /// + /// Is this a "humans vs. AI" game? + /// + public bool IsHumansVsAI { get; set; } + + public bool IsIntelligenceAllowed { get; set; } + + /// + /// Is this a single player game? If so, autoprocess the turn after the player takes his turn. + /// + public bool IsSinglePlayer { get; set; } + + public bool IsSurrenderAllowed { get; set; } + + /// + /// The maximum event severity in this game. + /// + public EventSeverity MaximumEventSeverity { get; set; } + + public int MaxPlanetValue { get; set; } + + public int MaxSpawnedAsteroidValue { get; set; } + + public int MaxSpawnedPlanetValue { get; set; } + + public int MaxX + { + get { return StarSystemLocations.MaxOrDefault(ssl => ssl.Location.X); } + } + + public int MaxY + { + get { return StarSystemLocations.MaxOrDefault(ssl => ssl.Location.Y); } + } + + public int MinAsteroidValue { get; set; } + + public int MinPlanetValue { get; set; } + + public int MinSpawnedAsteroidValue { get; set; } + + public int MinSpawnedPlanetValue { get; set; } + + public int MinX + { + get { return StarSystemLocations.MinOrDefault(ssl => ssl.Location.X); } + } + + public int MinY + { + get { return StarSystemLocations.MinOrDefault(ssl => ssl.Location.Y); } + } + + private string modPath; + + /// + /// The mod being played. + /// + [SerializationPriority(1)] + [ForceSerializationWhenDefaultValue] + public string ModPath + { + get => modPath; + set + { + if (Mod.Current == null || Mod.Current.RootPath != value) + Mod.Load(value); + modPath = value; + } + } + + /// + /// The game name. + /// + public string Name { get; set; } + + /// + /// The next tick size, for ship movement. + /// + public double NextTickSize { get; internal set; } + + /// + /// Should players have an omniscient view of all explored systems? + /// Does not prevent cloaking from working; this is just basic sight. + /// Also does not give battle reports for other empires' battles. + /// + public bool OmniscientView { get; set; } + + public IEnumerable Parents + { + get + { + yield break; + } + } + + /// + /// Events which have been warned of and are pending execution. + /// + public ICollection PendingEvents { get; private set; } = new List(); + + /// + /// The current player number (1 is the first player, 0 is the game host). + /// + public int PlayerNumber + { + get { return Empires.IndexOf(CurrentEmpire) + 1; } + } + + public IEnumerable Referrables { get { return referrables.Values; } } + + /// + /// Model to use for remote mining. + /// + public MiningModel RemoteMiningModel { get; set; } + + /// + /// Who can view empire scores? + /// + public ScoreDisplay ScoreDisplay { get; set; } + + /// + /// Notes that mod scripts can play with. + /// + public DynamicDictionary ScriptNotes { get; private set; } + + /// + /// Model to use for standard planetary mining. + /// + public MiningModel StandardMiningModel { get; set; } + + /// + /// The current stardate. Advances 0.1 years per turn. + /// + public string Stardate + { + get + { + return TurnNumber.ToStardate(); + } + } + + /// + /// The locations of the star systems in the galaxy. + /// + public ICollection> StarSystemLocations { get; private set; } + + /// + /// Technology research cost formula. + /// Low = Level * BaseCost + /// Medium = BaseCost for level 1, Level ^ 2 * BaseCost / 2 otherwise + /// Hight = Level ^ 2 * BaseCost + /// + public TechnologyCost TechnologyCost { get; set; } + + /// + /// Zero means normal tech cost; positive values make researching techs that other players know already harder; negative makes it easier. + /// + public int TechnologyUniqueness { get; set; } + + /// + /// Current time equals turn number plus tick minus 1. + /// + public double Timestamp { get { return TurnNumber + CurrentTick - 1; } } + + /// + /// The current turn number. + /// + public int TurnNumber { get; set; } + + /// + /// Number of turns of uninterrupted galactic peace (Non-Aggression or better between all surviving empires). + /// + public int TurnsOfPeace + { + get + { + // TODO - treaties + return 0; + } + } + + /// + /// Vertical space occupied by star systems. + /// + public int UsedHeight + { + get + { + if (!StarSystemLocations.Any()) + return 0; + return StarSystemLocations.Max(ssl => ssl.Location.Y) - StarSystemLocations.Min(ssl => ssl.Location.Y) + 1; + } + } + + /// + /// Horizontal space occuped by star systems. + /// + public int UsedWidth + { + get + { + if (!StarSystemLocations.Any()) + return 0; + return StarSystemLocations.Max(ssl => ssl.Location.X) - StarSystemLocations.Min(ssl => ssl.Location.X) + 1; + } + } + + /// + /// Game victory conditions. + /// + public IList VictoryConditions { get; private set; } + + /// + /// Delay in turns before victory conditions take effect. + /// + public int VictoryDelay { get; set; } + + public WarpPointPlacementStrategy WarpPointPlacementStrategy { get; set; } + + public int Width + { + get; + set; + } + + /// + /// Cache of abilities belonging to game objects. + /// + [DoNotSerialize] + internal SafeDictionary> AbilityCache { get; private set; } + + /// + /// Cache of abilities belonging to common game objects that can have different abilities for each empire. + /// + [DoNotSerialize] + internal SafeDictionary, IEnumerable> CommonAbilityCache { get; private set; } + + /// + /// Cache of treaty clauses given by empires. + /// + [DoNotSerialize] + internal SafeDictionary> GivenTreatyClauseCache { get; set; } + + /// + /// Cache of treaty clauses received by empires. + /// + [DoNotSerialize] + internal SafeDictionary> ReceivedTreatyClauseCache { get; set; } + + /// + /// Anything in the game that can be referenced from the client side + /// using a Reference object instead of passing whole objects around. + /// Stuff needs to be registered to be found though! + /// + [SerializationPriority(2)] + internal IDictionary referrables { get; set; } + + /// + /// Cache of abilities that are shared to empires from other objects due to treaties. + /// + [DoNotSerialize] + internal SafeDictionary, IEnumerable> SharedAbilityCache { get; private set; } + + /// + /// Serialized string value of the galaxy at the beginning of the turn. + /// + [DoNotSerialize] + internal string StringValue + { + get + { + if (stringValue == null) + StringValue = SaveToString(false); + return stringValue; + } + set + { + stringValue = value; + } + } + + private bool isAbilityCacheEnabled; + + private IDictionary lastBattleTimestamps = new SafeDictionary(); + + private string stringValue; + + public static string GetEmpireCommandsSavePath(string gameName, int turnNumber, int empireNumber) + { + return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Savegame", string.Format("{0}_{1}_{2:d4}{3}", gameName, turnNumber, empireNumber, FrEeeConstants.PlayerCommandsSaveGameExtension)); + } + + public static string GetGameSavePath(string gameName, int turnNumber, int empireNumber) + { + return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Savegame", empireNumber < 1 ? + string.Format("{0}_{1}{2}", gameName, turnNumber, FrEeeConstants.SaveGameExtension) : + string.Format("{0}_{1}_{2:d4}{3}", gameName, turnNumber, empireNumber, FrEeeConstants.SaveGameExtension)); + } + + /// + /// Initializes a new game. Sets Galaxy.Current. + /// + /// if there is no mod loaded. + /// A status object to report status back to the GUI. + /// How much progress should we report back to the GUI when we're done initializing the galaxy? 1.0 means all done with everything that needs to be done. + public static void Initialize(GameSetup gsu, PRNG dice, Status status = null, double desiredProgress = 1.0) + { + if (Mod.Current == null) + throw new InvalidOperationException("Cannot initialize a galaxy without a mod. Load a mod into Mod.Current first."); + + if (dice == null) + dice = new PRNG(DateTime.Now.Millisecond + 1000 * DateTime.Now.Second + 60000 * DateTime.Now.Minute); + + var startProgress = status == null ? 0d : status.Progress; + var progressPerStep = (desiredProgress - startProgress) / 4d; + + // create the game + if (Current == null) + { + var galtemp = gsu.GalaxyTemplate; + galtemp.GameSetup = gsu; + Current = galtemp.Instantiate(status, startProgress + progressPerStep, dice); + } + if (status != null) + status.Message = "Populating galaxy"; + gsu.PopulateGalaxy(Current, dice); + Current.SaveTechLevelsForUniqueness(); + if (status != null) + status.Progress += progressPerStep; + + // set single player flag + Current.IsSinglePlayer = gsu.IsSinglePlayer; + + // run init script + if (status != null) + status.Message = "Executing script"; + PythonScriptEngine.RunScript(Mod.Current.GameInitScript); + if (status != null) + status.Progress += progressPerStep; + + // save the game + if (status != null) + status.Message = "Saving game"; + SaveAll(status, desiredProgress); + } + + /// + /// Loads a savegame from the Savegame folder. + /// Note that if it was renamed, it might have different game name, turn number, player number, etc. than the filename indicates. + /// + /// + public static void Load(string filename) + { + var fs = new FileStream(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), FrEeeConstants.SaveGameDirectory, filename), FileMode.Open); + Current = Serializer.Deserialize(fs); + if (Current.ModPath == null) + Mod.Load(null); // skipped in deserialization because it is null but the mod needs to be loaded! + if (Empire.Current != null) + { + // load library of designs, strategies, etc. + Library.Load(); + } + fs.Close(); + fs.Dispose(); + Current.PopulatePropertyValues(); + } + + /// + /// Loads a host savegame from the Savegame folder. + /// + /// + /// + public static void Load(string gameName, int turnNumber) + { + Load(gameName + "_" + turnNumber + FrEeeConstants.SaveGameExtension); + } + + /// + /// Loads a player savegame from the Savegame folder. + /// + /// + /// + /// + public static void Load(string gameName, int turnNumber, int playerNumber) + { + Load(gameName + "_" + turnNumber + "_" + playerNumber.ToString("d4") + FrEeeConstants.SaveGameExtension); + } + + /// + /// Loads from a string in memory. + /// + /// + public static void LoadFromString(string serializedData) + { + Current = Serializer.DeserializeFromString(serializedData); + //Current.SpaceObjectIDCheck("after loading from memory"); + + + if (Current.ModPath == null) + Mod.Load(null); // skipped in deserialization because it is null but the mod needs to be loaded! + + if (Empire.Current != null) + { + // initialize IronPython galaxy on load + Current.StringValue = serializedData; + var formula = new ComputedFormula("Galaxy.Current.TurnNumber", null, true); + var turn = formula.Value; + + // load library of designs, strategies, etc. + Library.Load(); + } + + Current.PopulatePropertyValues(); + } + + /// + /// Populates property values specified by . + /// + private void PopulatePropertyValues() + { + // TODO: cache list of properties to populate when deserializing? + // enumerate all referrables + foreach (var referrable in Referrables) + { + // find referrable's properties + var props = referrable.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); + foreach (var prop in props) + { + // search property's attributes for PopulateAttribute + foreach (var att in prop.GetCustomAttributes()) + { + if (att.GetType().IsGenericType && att.GetType().GetGenericTypeDefinition() == typeof(PopulateAttribute<>)) + { + // found PopulateAttribute + // create populator + var populatorType = att.GetType().GetGenericArguments()[0]; + var populator = (IPopulator)populatorType.Instantiate(); + + // get value from populator and save it into the referrable's property + prop.SetValue(referrable, populator.Populate(referrable)); + } + } + } + } + } + + /// + /// Saves all empires' tech levels in the other empires for uniqueness calculations. + /// + internal void SaveTechLevelsForUniqueness() + { + if (Current.TechnologyUniqueness != 0) + { + foreach (var emp in Current.Empires) + { + emp.OtherPlayersTechLevels.Clear(); + foreach (var emp2 in Current.Empires.ExceptSingle(emp)) + { + foreach (var tech in Mod.Current.Technologies) + { + if (emp.OtherPlayersTechLevels[tech] == null) + emp.OtherPlayersTechLevels[tech] = new List(); + emp.OtherPlayersTechLevels[tech].Add(emp2.ResearchedTechnologies[tech]); + } + } + } + } + } + + /// + /// Saves the master view and all players' views of the galaxy, unless single player, in which case only the first player's view is saved. + /// + /// if CurrentEmpire is not null. + public static void SaveAll(Status status = null, double desiredProgress = 1d) + { + if (Current.CurrentEmpire != null) + throw new InvalidOperationException("Can only save player galaxy views from the master galaxy view."); + + var progressPerSaveLoad = (desiredProgress - (status == null ? 0d : status.Progress)) / (Current.IsSinglePlayer ? 3 : Current.Empires.Count + 2); + //Current.SpaceObjectIDCheck("before saving"); + + // save master view + if (status != null) + status.Message = "Saving game (host)"; + var gamname = Current.Save(); + if (status != null) + status.Progress += progressPerSaveLoad; + //Current.SpaceObjectIDCheck("after saving master view to disk"); + + // save player views + for (int i = 0; i < Current.Empires.Count; i++) + { + Load(gamname); + if (Current.Empires[i].IsPlayerEmpire) + { + if (status != null) + status.Message = "Saving game (player " + (i + 1) + ")"; + Current.CurrentEmpire = Current.Empires[i]; + Current.Redact(); + //Current.SpaceObjectIDCheck("after creating player view for " + Current.Empires[i]); + Current.Save(false); // already asssigned IDs in the redact phase + if (status != null) + status.Progress += progressPerSaveLoad; + } + } + + // TODO - only reload master view if we really need to + if (status != null) + status.Message = "Saving game"; + Load(gamname); + if (status != null) + status.Progress += progressPerSaveLoad; + + //Current.SpaceObjectIDCheck("after reloading master view"); + } + + /// + /// Assigns an ID to an object. + /// Will dispose of an object that has a negative ID if it hasn't already been disposed of. + /// + /// The object. + /// The ID, or 0 to generate a new ID (unless the ID is already valid). + /// The new ID. + public long AssignID(IReferrable r, long id = 0) + { + if (r.ID < 0 || r.IsDisposed) + { + if (!r.IsDisposed) + r.Dispose(); + return r.ID; + } + + if (r.HasValidID()) + return r.ID; // no need to reassign ID + else if (referrables.ContainsKey(r.ID)) + { + // HACK - already exists, just log an error but don't overwrite anything + // we need to fix start combatants having the same IDs as the real objects... + Console.Error.WriteLine("The galaxy thinks that " + referrables[r.ID] + " has the ID " + r.ID + " but " + r + " claims to have that ID as well."); + return r.ID; + } + + var oldid = r.ID; + long newid = oldid <= 0 ? id : oldid; + + while (newid <= 0 || referrables.ContainsKey(newid)) + { + newid = RandomHelper.Range(1L, long.MaxValue); + } + r.ID = newid; + referrables.Add(newid, r); + + // clean up old IDs + if (oldid > 0 && referrables.ContainsKey(oldid) && oldid != newid) + referrables.Remove(oldid); + + return newid; + } + + /// + /// Assigns IDs to referrable objects in the galaxy and purges disposed objects. + /// Doesn't assign IDs to objects via DoNotAssignID properties, or to memories (or sub-objects of them). + /// + public void CleanGameState() + { + var parser = new ObjectGraphParser(); + bool canAssign = true; + foreach (var kvp in referrables.ToArray()) + { + if (kvp.Value.IsDisposed) + referrables.Remove(kvp.Key); + } + parser.Property += (pname, o, val) => + { + var prop = o.GetType().FindProperty(pname); + var isMemory = val is IFoggable && (val as IFoggable).IsMemory; + canAssign = !prop.HasAttribute() && !isMemory; + if (isMemory) + return false; // no recursion! + if (prop.GetAttributes().Any(a => a.Recurse)) + return false; // no recursion! + else + return true; + }; + var colls = new List(); + parser.StartObject += o => + { + if (o is IReferrable && canAssign) + { + var r = (IReferrable)o; + AssignID(r); + } + if (o is IEnumerable) + { + colls.Add((IEnumerable)o); + } + }; + parser.EndObject += o => + { + if (o is IEnumerable) + { + colls.Remove((IEnumerable)o); + } + }; + parser.Parse(this); + foreach (var l in StarSystemLocations.ToArray()) + { + if (l.Item == null) + StarSystemLocations.Remove(l); + else + { + foreach (var l2 in l.Item.SpaceObjectLocations.ToArray()) + { + if (l2.Item == null || l2.Item.IsDisposed) + l.Item.SpaceObjectLocations.Remove(l2); + } + } + } + } + + public void ComputeNextTickSize() + { + var objs = FindSpaceObjects().Where(obj => obj.Orders.Any()); + objs = objs.Where(obj => !obj.IsMemory); + if (objs.Where(v => v.TimeToNextMove > 0).Any() && CurrentTick < 1.0) + { + // HACK - why are objects getting zero time to next move?! + var nextTickSize = objs.Where(v => v.TimeToNextMove > 0).Min(v => v.TimeToNextMove); + NextTickSize = Math.Min(1.0 - CurrentTick, nextTickSize); + } + else if (objs.Any()) + { + NextTickSize = objs.Min(v => v.TimePerMove); + } + else + NextTickSize = double.PositiveInfinity; + } + + /// + /// Creates a new empire. + /// + /// + public Empire CreateNewEmpire() + { + var emp = new Empire(); + emp.Name = "Randomly Generated Empire"; // TODO - load from EmpireNames.txt / EmpireTypes.txt + + // TODO - assign AI and primary race to empire + Empires.Add(emp); + return emp; + } + + /// + /// Disables the server side ability cache. + /// + public void DisableAbilityCache() + { + isAbilityCacheEnabled = false; + AbilityCache.Clear(); + CommonAbilityCache.Clear(); + SharedAbilityCache.Clear(); + } + + /// + /// Enables the server side ability cache. + /// + public void EnableAbilityCache() + { + isAbilityCacheEnabled = true; + } + + /// + /// Finds referrable objects in the galaxy. + /// + /// + /// + /// + public IEnumerable Find(Func condition = null) where T : IReferrable + { + if (condition == null) + condition = t => true; + return Referrables.OfType().Where(r => condition(r)); + } + + /// + /// Searches for space objects matching criteria. + /// + /// The type of space object. + /// The criteria. + /// The matching space objects. + public IEnumerable FindSpaceObjects(Func criteria = null) + { + return StarSystemLocations.SelectMany(l => l.Item.FindSpaceObjects(criteria)); + } + + public IEnumerable GetContainedAbilityObjects(Empire emp) + { + return StarSystemLocations.Select(ssl => ssl.Item).Concat(StarSystemLocations.SelectMany(ssl => ssl.Item.GetContainedAbilityObjects(emp))); + } + + public string GetEmpireCommandsSavePath(Empire emp) + { + return GetEmpireCommandsSavePath(Name, TurnNumber, Empires.IndexOf(emp) + 1); + } + + public string GetGameSavePath(Empire emp = null) + { + if (emp == null) + emp = CurrentEmpire; + return GetGameSavePath(Name, TurnNumber, emp == null ? 0 : Empires.IndexOf(emp) + 1); + } + + public IReferrable GetReferrable(long key) + { + if (!referrables.ContainsKey(key)) + return null; + return referrables[key]; + } + + /// + /// Finds the real version of a fake referrable. + /// + /// + /// The fake referrable. + /// + public T GetReferrable(T fakeobj) + where T : IReferrable + { + return (T)GetReferrable(fakeobj.ID); + } + + /// + /// Loads player commands into the current game state. + /// If this is the host view, commands will be loaded for all players. + /// If this is the player view, commands will be immediately executed so as to provide the player with a fresh game state. + /// + /// Player empires which did not submit commands and are not defeated. + public IEnumerable LoadCommands() + { + // whose commands are we loading? + var emps = new List(); + if (CurrentEmpire == null) + emps.AddRange(Empires); + else + emps.Add(CurrentEmpire); + + var noCmds = new List(); + + foreach (var emp in emps) + { + var plrfile = GetEmpireCommandsSavePath(emp); + if (File.Exists(plrfile)) + { + var fs = new FileStream(plrfile, FileMode.Open); + var cmds = DeserializeCommands(fs); + LoadCommands(emp, cmds); + fs.Close(); fs.Dispose(); + } + else if (emp.IsPlayerEmpire) + noCmds.Add(emp); + } + + if (CurrentEmpire != null) + { + foreach (var cmd in CurrentEmpire.Commands) + { + if (cmd.Executor == null) + CurrentEmpire.Log.Add(CurrentEmpire.CreateLogMessage($"{cmd} cannot be issued because its executor does not exist. Probably a bug...", LogMessageType.Error)); + else if (cmd.Issuer != cmd.Executor.Owner && cmd.Issuer != cmd.Executor) + CurrentEmpire.Log.Add(CurrentEmpire.CreateLogMessage("We cannot issue commands to " + cmd.Executor + " because it does not belong to us!", LogMessageType.Error)); + else + cmd.Execute(); + } + } + + return noCmds; + } + + /// + /// Only public for unit tests. You should probably call ProcessTurn instead. + /// + public void MoveShips() + { + var vlist = FindSpaceObjects().Where(sobj => sobj.Container == null && !sobj.IsMemory).Shuffle(); + foreach (var v in vlist) + { + // mark system explored if not already + var sys = v.StarSystem; + if (sys == null) + continue; // space object is dead, or not done being built + + if (CurrentTick == 0d && !v.Orders.OfType().Any()) + v.DealWithMines(); + + bool didStuff = v.ExecuteOrders(); + sys.MarkAsExploredBy(v.Owner); + + // update memory sight after movement + if (didStuff) + { + v.UpdateEmpireMemories(); + if (v.StarSystem != null && v.Owner != null) + { + foreach (var sobj in v.StarSystem.FindSpaceObjects().Where(sobj => sobj != v && !sobj.IsMemory && v.Owner.CanSee(sobj)).ToArray()) + v.Owner.UpdateMemory(sobj); + } + + // replenish shields after moving (who knows, we might be out of supplies, or about to hit a minefield) + v.ReplenishShields(); + } + + // check for battles + var sector = v.Sector; + if (v.Owner != null && // unowned objects can't pick fights + sector != null && // can't fight nowhere + sector.SpaceObjects.OfType().Any( + sobj => + (sobj.IsHostileTo(v.Owner) && sobj.Weapons.Any() || v.IsHostileTo(sobj.Owner) && v.Weapons.Any()) // any enemies? + && (sobj.Owner.CanSee(v) || v.Owner.CanSee(sobj)) // enemies are visible? + && (!lastBattleTimestamps.ContainsKey(sector) || lastBattleTimestamps[sector] < Timestamp - (v.StrategicSpeed == 0 ? 1d : 1d / v.StrategicSpeed)))) // have we fought here too recently? + { + // resolve the battle + var battle = new SpaceBattle(sector); + battle.Resolve(); + Battles.Add(battle); + foreach (var emp in battle.Empires) + emp.Log.Add(battle.CreateLogMessage(battle.NameFor(emp), LogMessageType.Battle)); + lastBattleTimestamps[sector] = Current.Timestamp; + } + } + } + + public Sector PickRandomSector(PRNG prng = null) + { + return StarSystemLocations.PickRandom(prng).Item.PickRandomSector(prng); + } + + /// + /// Removes any space objects, etc. that the current empire cannot see. + /// + public void Redact() + { + // save off empire scores first, before data is removed + foreach (var emp in Empires) + { + emp.Scores[TurnNumber] = emp.ComputeScore(Empire.Current); + } + + // the galaxy data itself + if (Empire.Current != null) + ScriptNotes.Clear(); + + // redact sub objects + var parser = new ObjectGraphParser(); + parser.StartObject += redactParser_StartObject; + parser.Parse(this); + + // clean up redacted objects that are not IFoggable + foreach (var x in StarSystemLocations.Where(x => x.Item.IsDisposed).ToArray()) + StarSystemLocations.Remove(x); + + // delete memories since they've been copied to "physical" objects already + foreach (var kvp in Empire.Current.Memory.ToArray()) + { + kvp.Value.Dispose(); + Empire.Current.Memory.Remove(kvp); + } + } + + public void Save(Stream stream, bool assignIDs = true) + { + if (assignIDs) + CleanGameState(); + foreach (var kvp in referrables.Where(kvp => kvp.Value.IsDisposed).ToArray()) + referrables.Remove(kvp); + Serializer.Serialize(this, stream); + } + + /// + /// Saves the game to an appropriately named file in the Savegame folder. + /// Files are named GameName_TurnNumber_PlayerNumber.gam for players (PlayerNumber is 1-indexed) + /// and GameName_TurnNumber.gam for the host. + /// + /// The filename saved to without the folder name (which is Savegame). + public string Save(bool assignIDs = true) + { + if (assignIDs) + CleanGameState(); + foreach (var kvp in referrables.Where(kvp => kvp.Value.ID < 0).ToArray()) + referrables.Remove(kvp); + string filename; + if (CurrentEmpire == null) + filename = Name + "_" + TurnNumber + ".gam"; + else + filename = Name + "_" + TurnNumber + "_" + (Empires.IndexOf(CurrentEmpire) + 1).ToString("d4") + ".gam"; + if (!Directory.Exists(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), FrEeeConstants.SaveGameDirectory))) + Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), FrEeeConstants.SaveGameDirectory)); + var fs = new FileStream(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), FrEeeConstants.SaveGameDirectory, filename), FileMode.Create); + Serializer.Serialize(this, fs); + fs.Close(); fs.Dispose(); + return filename; + } + + /// + /// Saves the player's commands to an appropriately named file in the Savegame folder. + /// Files are named GameName_TurnNumber_PlayerNumber.plr. (PlayerNumber is 1-indexed and padded to 4 digits with zeroes) + /// This doesn't make sense for the host view, so an exception will be thrown if there is no current empire. + /// + /// The filename saved to without the folder name (which is Savegame). + /// if there is no current empire. + public string SaveCommands() + { + CleanGameState(); + if (CurrentEmpire == null) + throw new InvalidOperationException("Can't save commands without a current empire."); + foreach (var c in Empire.Current.Commands.OfType().ToArray()) + Empire.Current.Commands.Remove(c); + Empire.Current.Commands.Add(new SetPlayerInfoCommand(Empire.Current) { PlayerInfo = Empire.Current.PlayerInfo }); + if (!Directory.Exists(FrEeeConstants.SaveGameDirectory)) + Directory.CreateDirectory(FrEeeConstants.SaveGameDirectory); + var filename = GetEmpireCommandsSavePath(CurrentEmpire); + var fs = new FileStream(filename, FileMode.Create); + SerializeCommands(fs); + fs.Close(); fs.Dispose(); + + // save library of designs, commands, etc. + Library.Save(); + + return filename; + } + + public string SaveToString(bool assignIDs = true) + { + if (assignIDs) + CleanGameState(); + return Serializer.SerializeToString(this); + } + + public override string ToString() + { + if (CurrentEmpire == null) + return Name; + return Name + " - " + CurrentEmpire.Name + " - " + CurrentEmpire.LeaderName + " - " + Stardate; + } + + public void UnassignID(long id) + { + if (referrables.ContainsKey(id)) + { + var r = referrables[id]; + r.ID = -1; + referrables.Remove(id); + } + } + + public void UnassignID(IReferrable r) + { + if (r == null || r.ID < 0) + return; // nothing to do + if (referrables.ContainsKey(r.ID)) + { + if (referrables[r.ID] == r) + referrables.Remove(r.ID); + else + { + var galaxyThinksTheIDIs = referrables.SingleOrDefault(kvp => kvp.Value == r); + referrables.Remove(galaxyThinksTheIDIs); + } + } + else if (referrables.Values.Contains(r)) + { + try + { + referrables.Remove(referrables.Single(kvp => kvp.Value == r)); + } + catch (InvalidOperationException ex) + { + // HACK - why is the item not being found? sequence contains no matching element? it's right there! + Console.Error.WriteLine(ex); + } + } + //r.ID = -1; + } + + internal void SpaceObjectIDCheck(string when) + { + foreach (var sobj in FindSpaceObjects().ToArray()) + { + if (!referrables.ContainsKey(sobj.ID)) + AssignID(sobj); + if (sobj.ID > 0) + { + var r = referrables[sobj.ID]; + if (r != sobj) + { + // HACK - assume the space object that's actually in space is "real" + referrables[sobj.ID] = sobj; + Console.Error.WriteLine("Space object identity mismatch " + when + " for ID=" + sobj.ID + ". " + sobj + " is actually in space so it is replacing " + r + " in the referrables collection."); + } + } + } + } + + /// + /// Deserializes the player's commands. + /// + /// + /// + private static IList DeserializeCommands(Stream stream) + { + var cmds = Serializer.Deserialize>(stream); + + // check for client safety + foreach (var cmd in cmds.Where(cmd => cmd != null)) + { + cmd.CheckForClientSafety(); + } + + return cmds; + } + + internal void LoadCommands(Empire emp, IList cmds) + { + cmds = cmds.Where(cmd => cmd != null).Distinct().ToList(); // HACK - why would we have null/duplicate commands in a plr file?! + emp.Commands.Clear(); + var idmap = new Dictionary(); + foreach (var cmd in cmds) + { + if (cmd.NewReferrables.Any(r => r.IsDisposed)) + { + emp.Log.Add(new GenericLogMessage("Command \"" + cmd + "\" contained a reference to deleted object \"" + cmd.NewReferrables.First(r => r.IsDisposed) + "\" and will be ignored. This may be a game bug.")); + continue; + } + emp.Commands.Add(cmd); + foreach (var r in cmd.NewReferrables) + { + var clientid = r.ID; + var serverid = AssignID(r); + if (idmap.ContainsKey(clientid)) + { + if (idmap[clientid] != serverid) + throw new InvalidOperationException($"Adding {r} with ID {serverid} to client ID {clientid} for {emp} when that client ID is already mapped to server ID {idmap[clientid]}."); + // else do nothing + } + else + idmap.Add(clientid, serverid); + } + } + foreach (var cmd in cmds) + cmd.ReplaceClientIDs(idmap); // convert client IDs to server IDs + } + + private void redactParser_StartObject(object o) + { + if (o is IReferrable) + AssignID(o as IReferrable); + if (o is IFoggable obj && o is IReferrable r) + { + if (!obj.IsMemory) + { + var id = r.ID; + var vis = obj.CheckVisibility(CurrentEmpire); + if (vis < Visibility.Fogged) + obj.Dispose(); + if (vis == Visibility.Fogged && CurrentEmpire.Memory.ContainsKey(id)) + { + var mem = (IReferrable)CurrentEmpire.Memory[id]; + mem.CopyToExceptID(r, IDCopyBehavior.PreserveDestination); // memory sight! + if (mem is ILocated l1 && r is ILocated l2) + l2.Sector = l1.Sector; // hack to copy location just now without always copying it when an object is copied + obj.IsMemory = true; + } + obj.Redact(Empire.Current); + } + else + { + // memories are only visible to the empire which is seeing them! + // well unless we add some sort of intel project to see them or something... + if (!CurrentEmpire.Memory.Values.Contains(obj)) + obj.Dispose(); + } + } + //SpaceObjectIDCheck("when redacting " + o); + } + + /// + /// Serializes the player's commands. + /// + /// if no current empire + /// + private void SerializeCommands(Stream stream) + { + if (CurrentEmpire == null) + throw new InvalidOperationException("Can't serialize commands if there is no current empire."); + + Serializer.Serialize(CurrentEmpire.Commands, stream); + } + + /// + /// Disposes of any space objects that aren't in space, under construction, or part of the mod definition. + /// + private void SpaceObjectCleanup() + { + foreach (var sobj in Referrables.OfType().ToArray()) + { + bool dispose = true; + if (sobj.Sector != null) + dispose = false; // save space objects that are in space + else if (this is IUnit u && u.FindContainer() != null) // save units that are in cargo + dispose = false; + else if (Mod.Current.StellarObjectTemplates.Contains(sobj as StellarObject)) + dispose = false; // save stellar objects that are part of the mod templates + else if (Referrables.OfType().Any(q => q.Orders.Any(o => o.Item == sobj as IConstructable))) + dispose = false; // save constructable space objects under construction + if (dispose) + sobj.Dispose(); + } + } + + // TODO - replace all those duplicate properties with a reference to the game setup + /* +/// +/// The game setup used to create this galaxy. +/// +public GameSetup GameSetup { get; set; } +*/ +} diff --git a/FrEee/Interfaces/ICleanable.cs b/FrEee/Objects/GameState/ICleanable.cs similarity index 70% rename from FrEee/Interfaces/ICleanable.cs rename to FrEee/Objects/GameState/ICleanable.cs index 0ec8fa859..289e1831c 100644 --- a/FrEee/Interfaces/ICleanable.cs +++ b/FrEee/Objects/GameState/ICleanable.cs @@ -1,9 +1,9 @@ -namespace FrEee.Interfaces; +namespace FrEee.Objects.GameState; /// /// For classes that need extra processing after being copied or whatnot. /// public interface ICleanable { - void Clean(); + void Clean(); } \ No newline at end of file diff --git a/FrEee/Interfaces/IContainable.cs b/FrEee/Objects/GameState/IContainable.cs similarity index 50% rename from FrEee/Interfaces/IContainable.cs rename to FrEee/Objects/GameState/IContainable.cs index 0426a4e56..91d6bfd6d 100644 --- a/FrEee/Interfaces/IContainable.cs +++ b/FrEee/Objects/GameState/IContainable.cs @@ -1,15 +1,15 @@ using FrEee.Serialization; -namespace FrEee.Interfaces; +namespace FrEee.Objects.GameState; /// /// Something which can be contained by another object. /// public interface IContainable { - /// - /// The container of this object. - /// - [DoNotCopy] - TContainer Container { get; } + /// + /// The container of this object. + /// + [DoNotCopy] + TContainer Container { get; } } diff --git a/FrEee/Objects/GameState/IFoggable.cs b/FrEee/Objects/GameState/IFoggable.cs new file mode 100644 index 000000000..f28095122 --- /dev/null +++ b/FrEee/Objects/GameState/IFoggable.cs @@ -0,0 +1,38 @@ +using System; +using FrEee.Objects.Civilization; + +namespace FrEee.Objects.GameState; + +/// +/// Something that can be obscured by fog of war. +/// +public interface IFoggable : IDisposable +{ + /// + /// Is this object just a memory, or a real object? + /// + bool IsMemory { get; set; } + + /// + /// The time at which this object was last seen. + /// E.g. 2.5 would be halfway through the second turn. + /// + double Timestamp { get; set; } + + /// + /// The visibility of this object to an empire. + /// + Visibility CheckVisibility(Empire emp); + + /// + /// Is this object an obsolete memory? + /// Memories become obsolete when the object's last known location is visible, + /// but the object has not been seen for at least 1 full turn. + /// + bool IsObsoleteMemory(Empire emp); + + /// + /// Removes any data from this object that the specified empire cannot see. + /// + void Redact(Empire emp); +} \ No newline at end of file diff --git a/FrEee/Objects/GameState/INamed.cs b/FrEee/Objects/GameState/INamed.cs new file mode 100644 index 000000000..6d57303e8 --- /dev/null +++ b/FrEee/Objects/GameState/INamed.cs @@ -0,0 +1,12 @@ +namespace FrEee.Objects.GameState; + +/// +/// Something that has a name. +/// +public interface INamed +{ + /// + /// The name of the object. + /// + string Name { get; } +} \ No newline at end of file diff --git a/FrEee/Objects/GameState/IPictorial.cs b/FrEee/Objects/GameState/IPictorial.cs new file mode 100644 index 000000000..bdd1fe83c --- /dev/null +++ b/FrEee/Objects/GameState/IPictorial.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using System.Drawing; + +namespace FrEee.Objects.GameState; + +/// +/// Something which has a picture. +/// +public interface IPictorial +{ + /// + /// A small picture. + /// + Image Icon { get; } + + /// + /// The icon pre-scaled to 32 pixels by 32 pixels. + /// + Image Icon32 { get; } + + /// + /// Paths with fallbacks to the icon, relative to the Pictures folder. + /// + IEnumerable IconPaths { get; } + + /// + /// A large picture. + /// + Image Portrait { get; } + + /// + /// Paths with fallbacks to the portrait, relative to the Pictures folder. + /// + IEnumerable PortraitPaths { get; } +} \ No newline at end of file diff --git a/FrEee/Objects/GameState/IPromotable.cs b/FrEee/Objects/GameState/IPromotable.cs new file mode 100644 index 000000000..d44c759dc --- /dev/null +++ b/FrEee/Objects/GameState/IPromotable.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace FrEee.Objects.GameState; + +/// +/// An object which can be "promoted" from the client side to the server side. +/// +public interface IPromotable +{ + /// + /// Replaces client-side object IDs with the newly generated server side IDs. + /// + /// + /// Any promoted objects that are already done replacing IDs. + void ReplaceClientIDs(IDictionary idmap, ISet done = null); +} \ No newline at end of file diff --git a/FrEee/Interfaces/ITemplate.cs b/FrEee/Objects/GameState/ITemplate.cs similarity index 54% rename from FrEee/Interfaces/ITemplate.cs rename to FrEee/Objects/GameState/ITemplate.cs index c9258f18c..a8f943a82 100644 --- a/FrEee/Interfaces/ITemplate.cs +++ b/FrEee/Objects/GameState/ITemplate.cs @@ -1,4 +1,4 @@ -namespace FrEee.Interfaces; +namespace FrEee.Objects.GameState; /// /// A template for instantiating in-game objects. @@ -6,9 +6,9 @@ namespace FrEee.Interfaces; /// The type of object which can be instantiated. public interface ITemplate { - /// - /// Instantiates an object. - /// - /// The new object. - T Instantiate(); + /// + /// Instantiates an object. + /// + /// The new object. + T Instantiate(); } \ No newline at end of file diff --git a/FrEee/Objects/GameState/IUnlockable.cs b/FrEee/Objects/GameState/IUnlockable.cs new file mode 100644 index 000000000..5b37c0add --- /dev/null +++ b/FrEee/Objects/GameState/IUnlockable.cs @@ -0,0 +1,22 @@ +using FrEee.Objects.Civilization; +using FrEee.Modding; +using System.Collections.Generic; + +namespace FrEee.Objects.GameState; + +/// +/// Something which can be unlocked, either via research or at empire creation. +/// +public interface IUnlockable : INamed, IPictorial +{ + /// + /// A group to display on the research screen, such as "Components" or "Facilities". + /// Or for racial/empire traits, just "Trait". + /// + string ResearchGroup { get; } + + /// + /// The requirements of this item to be unlocked. + /// + IList> UnlockRequirements { get; } +} \ No newline at end of file diff --git a/FrEee/Objects/GameState/Visibility.cs b/FrEee/Objects/GameState/Visibility.cs new file mode 100644 index 000000000..95e2c3cf6 --- /dev/null +++ b/FrEee/Objects/GameState/Visibility.cs @@ -0,0 +1,32 @@ +namespace FrEee.Objects.GameState; + +/// +/// Visibility of space objects. +/// +public enum Visibility +{ + /// + /// Object was never visible. + /// + Unknown = 0, + + /// + /// Object was once visible but is now invisible. + /// + Fogged, + + /// + /// Object is currently visible. + /// + Visible, + + /// + /// Object is currently visible and scanned. + /// + Scanned, + + /// + /// Object is owned by the empire. + /// + Owned +} \ No newline at end of file diff --git a/FrEee/Objects/LogMessages/IPictorialLogMessage.cs b/FrEee/Objects/LogMessages/IPictorialLogMessage.cs new file mode 100644 index 000000000..70cc63eb3 --- /dev/null +++ b/FrEee/Objects/LogMessages/IPictorialLogMessage.cs @@ -0,0 +1,6 @@ +namespace FrEee.Objects.LogMessages; + +public interface IPictorialLogMessage +{ + T Context { get; } +} \ No newline at end of file diff --git a/FrEee/Objects/LogMessages/LogMessage.cs b/FrEee/Objects/LogMessages/LogMessage.cs index 33aa9c085..7ec0ab9f9 100644 --- a/FrEee/Objects/LogMessages/LogMessage.cs +++ b/FrEee/Objects/LogMessages/LogMessage.cs @@ -1,4 +1,4 @@ -using FrEee.Objects.Space; +using FrEee.Objects.GameState; using System; using System.Drawing; diff --git a/FrEee/Objects/LogMessages/PictorialLogMessage.cs b/FrEee/Objects/LogMessages/PictorialLogMessage.cs index 4a7fb87db..20114187d 100644 --- a/FrEee/Objects/LogMessages/PictorialLogMessage.cs +++ b/FrEee/Objects/LogMessages/PictorialLogMessage.cs @@ -1,4 +1,4 @@ -using FrEee.Interfaces; +using FrEee.Objects.GameState; using System; using System.Drawing; diff --git a/FrEee/Objects/Orders/ActivateAbilityOrder.cs b/FrEee/Objects/Orders/ActivateAbilityOrder.cs deleted file mode 100644 index 04ee2341b..000000000 --- a/FrEee/Objects/Orders/ActivateAbilityOrder.cs +++ /dev/null @@ -1,320 +0,0 @@ -using FrEee.Interfaces; -using FrEee.Objects.Abilities; -using FrEee.Objects.Civilization; -using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; -using FrEee.Objects.Vehicles; -using FrEee.Modding; -using FrEee.Serialization; -using FrEee.Extensions; -using System.Collections.Generic; -using System.Linq; - -namespace FrEee.Objects.Orders; - -/// -/// An order to activate some sort of ability, like self-destruct or create a star. -/// -public class ActivateAbilityOrder : IOrder -{ - public ActivateAbilityOrder(IReferrableAbilityObject source, Ability ability, IReferrable target) - { - Owner = Empire.Current; - Source = source; - Ability = ability; - Target = target; - } - - /// - /// What ability to activate? - /// - [DoNotSerialize] - public Ability Ability { get { return ability.Value; } set { ability = value.ReferViaGalaxy(); } } - - public bool ConsumesMovement - { - get { return false; } - } - - public long ID - { - get; - set; - } - - public bool IsComplete - { - get; - set; - } - - public bool IsDisposed - { - get; - set; - } - - /// - /// The empire which issued the order. - /// - [DoNotSerialize] - public Empire Owner { get { return owner; } set { owner = value; } } - - /// - /// The source of the ability. Probably a component, facility, or hull. - /// TODO - fix this now that Component/Facility/Hull are no longer referrable - /// - [DoNotSerialize] - public IReferrableAbilityObject Source { get { return source.Value; } set { source = value.ReferViaGalaxy(); } } - - /// - /// What are we activating the ability "against"? Like, what warp point are we destroying, or whatever? Or null if there's no relevant target - /// - [DoNotSerialize] - public IReferrable Target { get { return target.Value; } set { target = value.ReferViaGalaxy(); } } - - private GalaxyReference ability { get; set; } - private GalaxyReference owner { get; set; } - private GalaxyReference source { get; set; } - private GalaxyReference target { get; set; } - - public bool CheckCompletion(IOrderable executor) - { - return IsComplete; - } - - public void Dispose() - { - if (IsDisposed) - return; - foreach (var sobj in Galaxy.Current.Referrables.OfType()) - sobj.RemoveOrder(this); - Galaxy.Current.UnassignID(this); - } - - public void Execute(IOrderable ord) - { - if (ord is IMobileSpaceObject executor) - { - // error checking - var errors = GetErrors(executor); - if (executor.Owner != null) - { - foreach (var error in errors) - executor.Owner.Log.Add(error); - } - - if (!errors.Any()) - { - // TODO - custom activatable abilities using scripts - if (Ability.Rule.Matches("Emergency Resupply")) - { - executor.SupplyRemaining += Ability.Value1.Value.ToInt(); - // TODO - normalize supplies on other stuff, not just space vehicles - if (executor is SpaceVehicle) - (executor as SpaceVehicle).NormalizeSupplies(); - Owner.RecordLog(executor, executor + " has activated its emergency resupply pod and now has " + executor.SupplyRemaining.ToUnitString(true) + " supplies.", LogMessageType.Generic); - } - else if (Ability.Rule.Matches("Emergency Energy")) - { - if (executor is Vehicle) - { - var v = executor as Vehicle; - v.EmergencySpeed += Ability.Value1.Value.ToInt(); - Owner.RecordLog(executor, executor + " has activated its emergency propulsion and has had its speed boosted to " + v.StrategicSpeed + " temporarily.", LogMessageType.Generic); - } - else - { - Owner.RecordLog(executor, executor + " cannot activate emergency propulsion because it is not a vehicle.", LogMessageType.Error); - return; - } - } - else if (Ability.Rule.Matches("Self-Destruct")) - { - // destroy ship/planet/whatever BOOM! - // TODO - make sure units with self destruct ability don't allow a ship or planet carrying them in cargo to self destruct... - Owner.RecordLog(executor, executor + " has successfully self-destructed.", LogMessageType.Generic); - executor.DisposeAndLog("The {0}'s {2} has apparently self-destructed.".F(Owner, executor), Owner); - } - else if (Ability.Rule.Matches("Open Warp Point")) - { - var fromSector = executor.Sector; - var fromSys = executor.StarSystem; - if (fromSector == null || fromSys == null) - { - Owner.RecordLog(executor, executor + " cannot open a warp point because it is not located in space.", LogMessageType.Warning); - return; - } - var toSys = Target as StarSystem; - if (toSys == null && Target is ILocated) - toSys = (Target as ILocated).StarSystem; - if (toSys == null) - { - Owner.RecordLog(executor, executor + " cannot open a warp point because no target system is specified.", LogMessageType.Error); - return; - } - if (fromSys.Coordinates.EightWayDistance(toSys.Coordinates) > Ability.Value1.Value.ToInt()) - { - Owner.RecordLog(executor, executor + " cannot open a warp point to " + toSys + " because " + toSys + " is too far away.", LogMessageType.Warning); - return; - } - // TODO - limit to one warp point between any two systems? - if (Ability.BurnSupplies()) - { - // find suitable warp point templates - var wpt1 = Mod.Current.StellarObjectTemplates.OfType().Where(wp => !wp.IsUnusual).PickRandom(); - var wpt2 = Mod.Current.StellarObjectTemplates.OfType().Where(wp => !wp.IsUnusual).PickRandom(); - - // figure out where the warp point goes, according to our game setup's warp point placement strategy - // only in the target system - in the source system we get a warp point at the sector where the WP opener was - var toSector = Galaxy.Current.WarpPointPlacementStrategy.GetWarpPointSector(fromSys.Location, toSys.Location); - - // create the warp points - var wp1 = wpt1.Instantiate(); - var wp2 = wpt2.Instantiate(); - - // configure the warp points - wp1.IsOneWay = false; - wp1.Name = "Warp Point to " + toSys; - wp1.Target = toSector; - fromSector.Place(wp1); - wp2.IsOneWay = false; - wp2.Name = "Warp Point to " + fromSys; - wp2.Target = fromSector; - toSector.Place(wp2); - - // let empires know that warp points were created - Owner.RecordLog(wp1, "{0} has opened a warp point connecting {1} to {2}.".F(executor, fromSys, toSys), LogMessageType.Generic); - wp1.UpdateEmpireMemories("A new warp point to {0} has been created by the {1} in the {2} system.".F(toSys, Owner, fromSys), Owner); - wp2.UpdateEmpireMemories("A new warp point to {0} has appeared in the {1} system.".F(fromSys, toSys), Owner); - } - else - { - Owner.RecordLog(executor, executor + " cannot open a warp point because it lacks the necessary supplies.", LogMessageType.Warning); - return; - } - } - else if (Ability.Rule.Matches("Close Warp Point")) - { - var wp = Target as WarpPoint; - - // sanity checking, make sure we have a valid warp point to close here - if (wp == null) - { - Owner.RecordLog(executor, executor + " cannot close a warp point because no warp point is specified to close.", LogMessageType.Error); - return; - } - if (wp.Sector != executor.Sector) - { - Owner.RecordLog(executor, executor + " cannot close " + wp + " because " + wp + " is not in the same sector as " + executor + ".", LogMessageType.Warning); - return; - } - - // need to close warp points on the other side too - var otherwps = wp.Target.SpaceObjects.OfType().Where(w => w.Target == wp.Sector); - - // check for supplies and close WP - if (Ability.BurnSupplies()) - { - Owner.RecordLog(wp, "We have successfully closed the warp point connecting {0} to {1}.".F(wp.StarSystem, wp.Target.StarSystem), LogMessageType.Generic); - wp.DisposeAndLog("The {0} has closed the warp point connecting {1} to {2}.", Owner); - foreach (var otherwp in otherwps) - otherwp.DisposeAndLog("The warp point connecting {0} to {1} has closed.", Owner); - } - else - { - Owner.RecordLog(executor, executor + " cannot close " + wp + " because it lacks the necessary supplies.", LogMessageType.Warning); - } - } - else if (Ability.Rule.Matches("Create Planet")) - { - } - else if (Ability.Rule.Matches("Destroy Planet")) - { - } - else if (Ability.Rule.Matches("Create Star")) - { - } - else if (Ability.Rule.Matches("Destroy Star")) - { - } - else if (Ability.Rule.Matches("Create Storm")) - { - } - else if (Ability.Rule.Matches("Destroy Storm")) - { - } - else if (Ability.Rule.Matches("Create Nebula")) - { - } - else if (Ability.Rule.Matches("Destroy Nebula")) - { - } - else if (Ability.Rule.Matches("Create Black Hole")) - { - } - else if (Ability.Rule.Matches("Destroy Black Hole")) - { - } - else if (Ability.Rule.Matches("Create Constructed Planet From Star")) - { - } - else if (Ability.Rule.Matches("Create Constructed Planet From Planet")) - { - } - else if (Ability.Rule.Matches("Create Constructed Planet From Storm")) - { - } - else if (Ability.Rule.Matches("Create Constructed Planet From Warp Point")) - { - } - else if (Ability.Rule.Matches("Create Constructed Planet From Asteroids")) - { - } - else if (Ability.Rule.Matches("Create Constructed Planet From Space")) - { - } - - // destroy component/etc. if necessary - if (Source.HasAbility("Destroyed On Use")) - { - // TODO - log destruction - if (Source is IDamageable) - (Source as IDamageable).Hitpoints = 0; - if (Source is IHull) - executor.Dispose(); // hull destruction kills the whole ship! - } - - // destroy entire space object if necessary - if (Source.HasAbility("Space Object Destroyed On Use")) - { - // TODO - log destruction - if (executor is Planet) - { - var p = executor as Planet; - p.ConvertToAsteroidField(); - } - else - { - executor.Dispose(); - } - } - } - - } - } - - public IEnumerable GetErrors(IOrderable executor) - { - if (!Source.IntrinsicAbilities.Contains(Ability)) - yield return executor.CreateLogMessage(executor + " does not intrinsically possess the ability \"" + Ability + "\" with ID=" + Ability.ID + ".", LogMessageType.Error); - if (!Ability.Rule.IsActivatable) - yield return executor.CreateLogMessage("The ability \"" + Ability + "\" cannot be activated. It is a passive ability.", LogMessageType.Error); - if (Source is IDamageable && (Source as IDamageable).IsDestroyed) - yield return executor.CreateLogMessage(executor + " cannot activate " + Source + "'s ability because " + Source + " is destroyed.", LogMessageType.Error); - } - public void ReplaceClientIDs(IDictionary idmap, ISet done = null) - { - // no new client objects here, nothing to do - } -} \ No newline at end of file diff --git a/FrEee/Objects/Orders/ColonizeOrder.cs b/FrEee/Objects/Orders/ColonizeOrder.cs deleted file mode 100644 index 7035ee81d..000000000 --- a/FrEee/Objects/Orders/ColonizeOrder.cs +++ /dev/null @@ -1,240 +0,0 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; -using FrEee.Objects.Vehicles; -using FrEee.Modding; -using FrEee.Serialization; -using FrEee.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace FrEee.Objects.Orders; - -/// -/// An order to colonize an uninhabited planet. -/// -[Serializable] -public class ColonizeOrder : IOrder -{ - public ColonizeOrder(Planet planet) - { - Owner = Empire.Current; - Planet = planet; - } - - public bool ConsumesMovement - { - get { return true; } - } - - public long ID { get; set; } - - public bool IsComplete - { - get; - set; - } - - public bool IsDisposed { get; set; } - - /// - /// The empire which issued the order. - /// - [DoNotSerialize] - public Empire Owner { get { return owner; } set { owner = value; } } - - /// - /// The planet we are colonizing. - /// - [DoNotSerialize] - public Planet Planet { get { return planet; } set { planet = value; } } - - private GalaxyReference owner { get; set; } - private GalaxyReference planet { get; set; } - - public bool CheckCompletion(IOrderable v) - { - return IsComplete; - } - - /// - /// Orders are visible only to their owners. - /// - /// - /// - public Visibility CheckVisibility(Empire emp) - { - if (emp == Owner) - return Visibility.Visible; - return Visibility.Unknown; - } - - public void Dispose() - { - if (IsDisposed) - return; - foreach (var v in Galaxy.Current.Referrables.OfType()) - { - if (v is SpaceVehicle sv) - sv.Orders.Remove(this); - else if (v is Fleet f) - f.Orders.Remove(this); - else if (v is Planet p) - p.Orders.Remove(this); - } - Galaxy.Current.UnassignID(this); - } - - public void Execute(IOrderable ord) - { - if (ord is IMobileSpaceObject sobj) - { - // error checking - var errors = GetErrors(sobj); - foreach (var error in errors) - sobj.Owner.Log.Add(error); - - // let only one colony ship from a fleet colonize - if (sobj is Fleet f) - sobj = f.LeafVehicles.FirstOrDefault(q => q.HasAbility(Planet.ColonizationAbilityName)); - - if (!errors.Any()) - { - // colonize now!!! - Planet.Colony = new Colony { Owner = sobj.Owner }; - Owner.TriggerHappinessChange(hm => hm.PlanetColonized); - Planet.Colony.ConstructionQueue = new ConstructionQueue(Planet); - if (sobj is ICargoContainer cc) - { - foreach (var kvp in cc.Cargo.Population) - { - // place population on planet - Planet.AddPopulation(kvp.Key, kvp.Value); - } - foreach (var unit in cc.Cargo.Units) - { - // planet unit on planet - Planet.AddUnit(unit); - } - } - - // ruins? - var ruinsTechs = Planet.GetAbilityValue("Ancient Ruins").ToInt(); - for (int i = 0; i < ruinsTechs; i++) - { - var msg = "We have discovered new technology from the ancient ruins on " + Planet + "."; - // pick a random tech that's unlocked but not fully researched and level it up - var tech = Mod.Current.Technologies.Where(t => sobj.Owner.HasUnlocked(t) && sobj.Owner.ResearchedTechnologies[t] < t.MaximumLevel).PickRandom(); - if (tech == null) - msg = "We have discovered ancient ruins on " + Planet + ", but there is nothing left for us to learn."; - else - { - var oldlvl = sobj.Owner.ResearchedTechnologies[tech]; - var newStuff = tech.GetExpectedResults(sobj.Owner); - sobj.Owner.ResearchedTechnologies[tech]++; - var newlvl = sobj.Owner.ResearchedTechnologies[tech]; - var progress = sobj.Owner.ResearchProgress.SingleOrDefault(p => p.Item == tech); - if (progress != null) - progress.Value = 0; - sobj.Owner.Log.Add(tech.CreateLogMessage("We have advanced from level " + oldlvl + " to level " + newlvl + " in " + tech + "!", LogMessageType.ResearchComplete)); - foreach (var item in newStuff) - sobj.Owner.Log.Add(item.CreateLogMessage("We have unlocked a new " + item.ResearchGroup.ToLower() + ", the " + item + "!", LogMessageType.ResearchComplete)); - } - if (i == 0) - sobj.Owner.Log.Add(Planet.CreateLogMessage(msg, LogMessageType.PlanetColonised)); - } - - // unique ruins? - foreach (var abil in Planet.Abilities().Where(a => a.Rule.Name == "Ancient Ruins Unique")) - { - if (sobj.Owner.UniqueTechsFound.Contains(abil.Value1)) - sobj.Owner.Log.Add(Planet.CreateLogMessage("We have discovered \"unique\" technology from the ancient ruins on " + Planet + ", but it appears we have already found this one elsewhere. Perhaps it was not as unique as we had thought...", LogMessageType.ResearchComplete)); - else - { - sobj.Owner.Log.Add(Planet.CreateLogMessage("We have discovered new unique technology from the ancient ruins on " + Planet + ".", LogMessageType.ResearchComplete)); - sobj.Owner.UniqueTechsFound.Add(abil.Value1); - foreach (var tech in Mod.Current.Technologies.Where(t => t.UniqueTechID == abil.Value1 && sobj.Owner.HasUnlocked(t))) - sobj.Owner.Log.Add(tech.CreateLogMessage("We have unlocked a new " + tech.ResearchGroup.ToLower() + ", the " + tech + "!", LogMessageType.ResearchComplete)); - } - } - - // delete ruins and unique ruins abilities - foreach (var a in Planet.IntrinsicAbilities.Where(a => a.Rule.Name == "Ancient Ruins" || a.Rule.Name == "Ancient Ruins Unique").ToArray()) - Planet.IntrinsicAbilities.Remove(a); - - // log it! - sobj.Owner.Log.Add(Planet.CreateLogMessage(sobj + " has founded a new colony on " + Planet + ".", LogMessageType.PlanetColonised)); - - // update pursue/evade orders to target planet now instead of ship - foreach (var o in Galaxy.Current.Referrables.OfType().Where(q => q.Target == sobj)) - { - if (o.Owner.CanSee(sobj) && o.Owner.CanSee(Planet)) - o.Target = Planet; - } - foreach (var o in Galaxy.Current.Referrables.OfType().Where(q => q.Target == sobj)) - { - if (o.Owner.CanSee(sobj) && o.Owner.CanSee(Planet)) - o.Target = Planet; - } - - // bye bye colony ship - sobj.Dispose(); - } - - // either done colonizing, or we failed - IsComplete = true; - - // spend time - sobj.SpendTime(sobj.TimePerMove); - } - else - Owner.Log.Append(Owner.CreateLogMessage($"Could not assign a colonize order to ${ord} because it is not a mobile space object.", LogMessageType.Error)); - } - - public IEnumerable GetErrors(IOrderable o) - { - if (o is IMobileSpaceObject sobj) - { - if (sobj.Sector != Planet.Sector) - { - // can't colonize here, maybe the GUI should have issued a move order? - yield return sobj.CreateLogMessage(sobj + " cannot colonize " + Planet + " because it is not currently located at the planet.", LogMessageType.Warning); - } - if (Planet.Colony != null) - { - // planet is already colonized! - yield return Planet.CreateLogMessage(Planet + " cannot be colonized by " + sobj + " because there is already a colony there belonging to the " + Planet.Colony.Owner + ".", LogMessageType.Warning); - } - if (!(sobj.HasAbility(Planet.ColonizationAbilityName) || sobj is Fleet f && f.LeafVehicles.Any(v => v.HasAbility(Planet.ColonizationAbilityName)))) - { - // no such colony module - yield return sobj.CreateLogMessage(sobj + " cannot colonize " + Planet + " because it lacks a " + Planet.Surface + " colony module.", LogMessageType.Warning); - } - if (Galaxy.Current.CanColonizeOnlyBreathable && Planet.Atmosphere != sobj.Owner.PrimaryRace.NativeAtmosphere) - { - // can only colonize breathable atmosphere (due to game setup option) - yield return sobj.CreateLogMessage(sobj + " cannot colonize " + Planet + " because we can only colonize " + sobj.Owner.PrimaryRace.NativeAtmosphere + " planets.", LogMessageType.Warning); - } - if (Galaxy.Current.CanColonizeOnlyHomeworldSurface && Planet.Surface != sobj.Owner.PrimaryRace.NativeSurface) - { - // can only colonize breathable atmosphere (due to game setup option) - yield return sobj.CreateLogMessage(sobj + " cannot colonize " + Planet + " because we can only colonize " + sobj.Owner.PrimaryRace.NativeSurface + " planets.", LogMessageType.Warning); - } - } - else - yield return o.CreateLogMessage($"{o} cannot colonize {Planet} because it is not a mobile space object.", LogMessageType.Error); - } - - public void ReplaceClientIDs(IDictionary idmap, ISet done = null) - { - // This type does not use client objects, so nothing to do here. - } - - public override string ToString() - { - return "Colonize " + Planet.Name; - } -} \ No newline at end of file diff --git a/FrEee/Objects/Orders/ConstructionOrder.cs b/FrEee/Objects/Orders/ConstructionOrder.cs deleted file mode 100644 index ff81b3779..000000000 --- a/FrEee/Objects/Orders/ConstructionOrder.cs +++ /dev/null @@ -1,229 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; -using FrEee.Objects.Technology; -using FrEee.Objects.Vehicles; -using FrEee.Modding; -using FrEee.Modding.Interfaces; -using FrEee.Utility; using FrEee.Serialization; -using FrEee.Extensions; - -namespace FrEee.Objects.Orders; - -/// -/// An order for a construction queue to build something. -/// -[Serializable] -public class ConstructionOrder : IConstructionOrder - where T : IConstructable - where TTemplate : ITemplate, IReferrable, IConstructionTemplate -{ - public ConstructionOrder() - { - Owner = Empire.Current; - } - - public bool ConsumesMovement - { - get { return false; } - } - - public ResourceQuantity Cost - { - get { return Template?.Cost ?? new ResourceQuantity(); } - } - - public long ID { get; set; } - - public bool IsComplete - { - get - { - if (isComplete == null) - return false; // haven't checked completion yet, so it's probably safe to say it's incomplete - return isComplete.Value; - } - set - { - isComplete = value; - } - } - - public bool IsDisposed { get; set; } - - /// - /// The item being built. - /// - public T Item { get; set; } - - IConstructable IConstructionOrder.Item - { - get { return Item; } - } - - public string Name - { - get - { - return Template.Name; - } - } - - /// - /// The empire which issued the order. - /// - [DoNotSerialize] - public Empire Owner { get { return owner; } set { owner = value; } } - - /// - /// The construction template. - /// - [DoNotSerialize] - public TTemplate Template - { - get { return template.Value; } - set - { - if (value is IModObject mo) - template = GetModReference(mo.ReferViaMod().ID); - else if (value is IReferrable r) - template = new GalaxyReference(r.ReferViaGalaxy().ID); - else if (value == null) - template = null; - else - throw new Exception($"{value} is not referrable in the galaxy or the mod."); - } - } - - private IReference GetModReference(string id) - { - // since T is not guaranteed to be a compile time IModObject implementation - var type = typeof(ModReference<>).MakeGenericType(typeof(U)); - var r = (IReference)Activator.CreateInstance(type); - r.SetPropertyValue("ID", id); - return r; - } - - IConstructionTemplate IConstructionOrder.Template { get { return template.Value; } } - - [DoNotSerialize] - private bool? isComplete - { - get; - set; - } - - private GalaxyReference owner { get; set; } - private IReference template { get; set; } - - public bool CheckCompletion(IOrderable q) - { - var queue = (ConstructionQueue)q; - isComplete = Item.ConstructionProgress >= Item.Cost || GetErrors(queue).Any(); - return IsComplete; - } - - /// - /// Orders are visible only to their owners. - /// - /// - /// - public Visibility CheckVisibility(Empire emp) - { - if (emp == Owner) - return Visibility.Visible; - return Visibility.Unknown; - } - - public void Dispose() - { - if (IsDisposed) - return; - foreach (var q in Galaxy.Current.Referrables.OfType()) - q.Orders.Remove(this); - Galaxy.Current.UnassignID(this); - } - - /// - /// Does 1 turn's worth of building. - /// - public void Execute(IOrderable q) - { - var queue = (ConstructionQueue)q; - var errors = GetErrors(queue); - foreach (var error in errors) - queue.Owner.Log.Add(error); - - if (!errors.Any()) - { - // create item if needed - if (Item == null) - { - Item = Template.Instantiate(); - if (!(Item is Facility)) - Item.Owner = queue.Owner; - if (Item is SpaceVehicle) - { - // space vehicles need their supplies filled up - var sv = (SpaceVehicle)(IConstructable)Item; - sv.SupplyRemaining = sv.SupplyStorage; - } - } - - // apply build rate - var costLeft = Item.Cost - Item.ConstructionProgress; - var spending = ResourceQuantity.Min(costLeft, queue.UnspentRate); - if (!(spending <= queue.Owner.StoredResources)) - { - spending = ResourceQuantity.Min(spending, queue.Owner.StoredResources); - if (spending.IsEmpty) - { - if (!queue.IsConstructionDelayed) // don't spam messages! - Owner.Log.Add(queue.Container.CreateLogMessage("Construction of " + Template + " at " + queue.Container + " was paused due to lack of resources.", LogMessageType.Generic)); - } - else - { - Owner.Log.Add(queue.Container.CreateLogMessage("Construction of " + Template + " at " + queue.Container + " was slowed due to lack of resources.", LogMessageType.Generic)); - } - queue.IsConstructionDelayed = true; - } - queue.Owner.StoredResources -= spending; - queue.UnspentRate -= spending; - Item.ConstructionProgress += spending; - } - } - - public IEnumerable GetErrors(IOrderable q) - { - var queue = (ConstructionQueue)q; - - // do we have a valid template? - if (Template == null) - yield return Owner.CreateLogMessage($"{queue.Container} cannot build a nonexistent template; skipping it. Probably a bug...", LogMessageType.Error); - - // validate that what's being built is unlocked - if (!queue.Owner.HasUnlocked(Template)) - yield return Template.CreateLogMessage(Template + " cannot be built at " + queue.Container + " because we have not yet researched it.", LogMessageType.Warning); - } - - public void ReplaceClientIDs(IDictionary idmap, ISet done = null) - { - if (done == null) - done = new HashSet(); - if (!done.Contains(this)) - { - done.Add(this); - template.ReplaceClientIDs(idmap, done); - } - } - - public void Reset() - { - Item = default(T); - } -} diff --git a/FrEee/Objects/Orders/EvadeOrder.cs b/FrEee/Objects/Orders/EvadeOrder.cs deleted file mode 100644 index 1f381af0d..000000000 --- a/FrEee/Objects/Orders/EvadeOrder.cs +++ /dev/null @@ -1,87 +0,0 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Utility; -using FrEee.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace FrEee.Objects.Orders; - -/// -/// An order to move a mobile space object away from another space object. -/// The direction will be chosen at random -/// -[Serializable] -public class EvadeOrder : PathfindingOrder -{ - public EvadeOrder(ISpaceObject target, bool avoidEnemies) - : base(target, avoidEnemies) - { - } - - public override string Verb - { - get { return "evade"; } - } - - /// - /// Finds the path for executing this order. - /// - /// The space object executing the order. - /// - public override IEnumerable Pathfind(IMobileSpaceObject me, Sector start) - { - if (Target is IMobileSpaceObject) - { - if (me.CanWarp && !Target.CanWarp) - { - // warping via any warp point that leads outside the system should be safe, so prioritize those! - var sys = me.FindStarSystem(); - var paths = sys.FindSpaceObjects() - .Where(wp => wp.TargetStarSystemLocation == null || wp.TargetStarSystemLocation.Item != sys) - .Select(wp => new { WarpPoint = wp, Path = Pathfinder.Pathfind(me, start, wp.Sector, AvoidEnemies, true, me.DijkstraMap) }); - if (paths.Any()) - { - // found a warp point to flee to! - var shortest = paths.WithMin(path => path.Path.Count()).PickRandom(); - return shortest.Path.Concat(new Sector[] { shortest.WarpPoint.Target }); - } - } - - // see how he can reach us, and go somewhere away from him (that would take longer for him to get to than - var dijkstraMap = Pathfinder.CreateDijkstraMap((IMobileSpaceObject)Target, Target.Sector, me.FindSector(), false, true); - var canMoveTo = Pathfinder.GetPossibleMoves(me.Sector, me.CanWarp, me.Owner); - var goodMoves = canMoveTo.Where(s => !dijkstraMap.Values.SelectMany(set => set).Any(n => n.Location == s)); - - if (goodMoves.Any()) - { - // just go there and recompute the path next time we can move - the enemy may have moved too - return new Sector[] { goodMoves.PickRandom() }; - } - else - { - // trapped... - return Enumerable.Empty(); - } - } - else - { - // target is immobile! no need to flee, unless it's in the same sector - if (Target.Sector == me.FindSector()) - { - // don't need to go through warp points to evade it, the warp points might be one way! - var moves = Pathfinder.GetPossibleMoves(me.Sector, false, me.Owner); - return new Sector[] { moves.PickRandom() }; - } - else - return Enumerable.Empty(); - } - } - - protected override bool AreWeThereYet(IMobileSpaceObject me) - { - // gotta keep on running... - return Target == null || Target.IsDisposed; - } -} \ No newline at end of file diff --git a/FrEee/Objects/Orders/MoveOrder.cs b/FrEee/Objects/Orders/MoveOrder.cs deleted file mode 100644 index fdd70d13f..000000000 --- a/FrEee/Objects/Orders/MoveOrder.cs +++ /dev/null @@ -1,217 +0,0 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.Combat; -using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; -using FrEee.Utility; using FrEee.Serialization; -using FrEee.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace FrEee.Objects.Orders; - -/// -/// An order to move a mobile space object to a new location. -/// -[Serializable] -public class MoveOrder : IMovementOrder -{ - public MoveOrder(Sector destination, bool avoidEnemies) - { - Owner = Empire.Current; - Destination = destination; - AvoidEnemies = avoidEnemies; - // TODO - add flag for "avoid damaging sectors"? but how to specify in UI? - } - - /// - /// Should pathfinding avoid enemies? - /// - public bool AvoidEnemies { get; set; } - - public bool ConsumesMovement - { - get { return true; } - } - - /// - /// The sector we are moving to. - /// - public Sector Destination { get; set; } - - public long ID { get; set; } - - public bool IsComplete - { - get; - set; - } - - public bool IsDisposed { get; set; } - - /// - /// Did we already log a pathfinding error this turn? - /// - [DoNotSerialize] - public bool LoggedPathfindingError { get; private set; } - - /// - /// The empire which issued the order. - /// - [DoNotSerialize] - public Empire Owner { get { return owner; } set { owner = value; } } - - /// - /// Any pathfinding error that we might have found. - /// - [DoNotSerialize] - public LogMessage PathfindingError { get; private set; } - - private GalaxyReference owner { get; set; } - - public bool CheckCompletion(IOrderable v) - { - return IsComplete; - } - - /// - /// Orders are visible only to their owners. - /// - /// - /// - public Visibility CheckVisibility(Empire emp) - { - if (emp == Owner) - return Visibility.Visible; - return Visibility.Unknown; - } - - /// - /// Creates a Dijkstra map for this order's movement. - /// - /// - /// - /// - public IDictionary, ISet>> CreateDijkstraMap(IMobileSpaceObject me, Sector start) - { - return Pathfinder.CreateDijkstraMap(me, start, Destination, AvoidEnemies, true); - } - - public void Dispose() - { - if (IsDisposed) - return; - foreach (var v in Galaxy.Current.Referrables.OfType()) - v.RemoveOrder(this); - Galaxy.Current.UnassignID(this); - } - - public void Execute(IOrderable ord) - { - if (ord is IMobileSpaceObject sobj) - { - // TODO - movement logs - if (sobj.Sector == Destination) - { - IsComplete = true; - return; - } - else - { - var gotoSector = Pathfind(sobj, sobj.Sector).FirstOrDefault(); - if (gotoSector != null) - { - // move - sobj.Sector = gotoSector; - sobj.RefreshDijkstraMap(); - - // consume supplies - sobj.BurnMovementSupplies(); - - // resupply space vehicles - // either this vehicle from other space objects, or other vehicles from this one - // TODO - this should really be done AFTER battles... - if (gotoSector.HasAbility("Supply Generation", sobj.Owner)) - { - foreach (var v in gotoSector.SpaceObjects.OfType().Where(v => v.Owner == sobj.Owner)) - v.SupplyRemaining = v.SupplyStorage; - } - if (gotoSector.StarSystem.HasAbility("Supply Generation - System", sobj.Owner) || gotoSector.StarSystem.HasAbility("Supply Generation - System")) - { - foreach (var v in gotoSector.StarSystem.FindSpaceObjects().Where(v => v.Owner == sobj.Owner)) - v.SupplyRemaining = v.SupplyStorage; - } - - // is it done? - if (gotoSector == Destination) - IsComplete = true; - - // apply damage from damaging sectors - // TODO - apply damage from damaging systems too - // TODO - move this out into the Place method so it applies to all movement-type orders and newly constructed vehicles - foreach (var damager in gotoSector.SpaceObjects.Where(dsobj => dsobj.HasAbility("Sector - Damage"))) - { - var damage = damager.GetAbilityValue("Sector - Damage").ToInt(); - // TODO - let sector damage have special damage types? - var shot = new Shot(null, null, sobj, 0); - var hit = new Hit(shot, sobj, damage); - sobj.TakeDamage(hit, null); - sobj.Owner.Log.Add(sobj.CreateLogMessage(sobj + " took " + damage + " damage from entering " + damager + "'s sector.", LogMessageType.Generic)); - sobj.ReplenishShields(); - } - } - else if (!LoggedPathfindingError) - { - // log pathfinding error - string reason; - if (sobj.StrategicSpeed <= 0) - reason = sobj + " is immobile"; - else - reason = "there is no available path leading toward " + Destination; - PathfindingError = sobj.CreateLogMessage(sobj + " could not move to " + Destination + " because " + reason + ".", LogMessageType.Warning); - sobj.Owner?.Log.Add(PathfindingError); - LoggedPathfindingError = true; - } - } - - // spend time - sobj.SpendTime(sobj.TimePerMove); - } - else - ord.Owner.RecordLog(ord, $"{ord} cannot be ordered to move because it is not a mobile space object.", LogMessageType.Error); - } - - public IEnumerable GetErrors(IOrderable v) - { - if (PathfindingError != null) - yield return PathfindingError; - } - - /// - /// Finds the path for executing this order. - /// - /// The space object executing the order. - /// - public IEnumerable Pathfind(IMobileSpaceObject me, Sector start) - { - return Pathfinder.Pathfind(me, start, Destination, AvoidEnemies, true, me.DijkstraMap); - } - - public void ReplaceClientIDs(IDictionary idmap, ISet done) - { - // This type does not use client objects, so nothing to do here. - } - - public override string ToString() - { - var coords = Destination.Coordinates; - if (Destination == null || Destination.StarSystem == null) - return "(Unknown Move Order)"; - if (AvoidEnemies) - return "Move to " + Destination.StarSystem.Name + " (" + coords.X + ", " + coords.Y + ")"; - else - return "Attack " + Destination.StarSystem.Name + " (" + coords.X + ", " + coords.Y + ")"; - } -} \ No newline at end of file diff --git a/FrEee/Objects/Orders/PathfindingOrder.cs b/FrEee/Objects/Orders/PathfindingOrder.cs deleted file mode 100644 index 58ccfef0c..000000000 --- a/FrEee/Objects/Orders/PathfindingOrder.cs +++ /dev/null @@ -1,284 +0,0 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; -using FrEee.Utility; -using FrEee.Serialization; -using FrEee.Extensions; -using System.Collections.Generic; -using System.Linq; - -namespace FrEee.Objects.Orders; - -/// -/// An order to pathfind relative to a target. -/// -/// -public abstract class PathfindingOrder - : IPathfindingOrder -{ - protected PathfindingOrder(ISpaceObject target, bool avoidEnemies) - { - Owner = Empire.Current; - Target = target; - AvoidEnemies = avoidEnemies; - // TODO - add flag for "avoid damaging sectors"? but how to specify in UI? - } - - /// - /// Alternate target. This should be the largest ship in a fleet when a fleet is being pursued. - /// - [DoNotSerialize] - public ISpaceObject AlternateTarget - { - get; - private set; - } - - /// - /// Should pathfinding avoid enemies? - /// - public bool AvoidEnemies { get; set; } - - public bool ConsumesMovement - { - get { return true; } - } - - public Sector Destination - { - get { return KnownTarget?.Sector; } - } - - public long ID { get; set; } - - public bool IsComplete - { - get; - set; - } - - public bool IsDisposed { get; set; } - - /// - /// Either the target itself, or the memory of the target, if it's not visible. - /// - public ISpaceObject KnownTarget - { - get - { - if (Target == null) - return null; - if (Target.CheckVisibility(Owner) >= Visibility.Visible) - return Target; - return Owner?.Recall(Target); - } - } - - /// - /// Did we already log a pathfinding error this turn? - /// - [DoNotSerialize] - public bool LoggedPathfindingError { get; private set; } - - /// - /// The empire which issued the order. - /// - [DoNotSerialize] - public Empire Owner { get { return owner; } set { owner = value; } } - - /// - /// Any pathfinding error that we might have found. - /// - [DoNotSerialize] - public LogMessage PathfindingError { get; private set; } - - /// - /// The target we are pursuing. - /// - [DoNotSerialize] - public ISpaceObject Target { get { return target?.Value; } set { target = value.ReferViaGalaxy(); } } - - /// - /// A verb used to describe this order. - /// - public abstract string Verb { get; } - - private GalaxyReference owner { get; set; } - private GalaxyReference target { get; set; } - - public bool CheckCompletion(IOrderable v) - { - return IsComplete; - } - - /// - /// Orders are visible only to their owners. - /// - /// - /// - public Visibility CheckVisibility(Empire emp) - { - if (emp == Owner) - return Visibility.Visible; - return Visibility.Unknown; - } - - public IDictionary, ISet>> CreateDijkstraMap(IMobileSpaceObject me, Sector start) - { - return Pathfinder.CreateDijkstraMap(me, start, Destination, AvoidEnemies, true); - } - - public void Dispose() - { - if (IsDisposed) - return; - foreach (var v in Galaxy.Current.FindSpaceObjects()) - v.RemoveOrder(this); - Galaxy.Current.UnassignID(this); - } - - public void Execute(IOrderable ord) - { - if (ord is IMobileSpaceObject sobj) - { - // TODO - movement logs - if (KnownTarget == null) - IsComplete = true; // target is known to be dead - else if (AreWeThereYet(sobj)) - IsComplete = true; // we've arrived at the target - else - { - var gotoSector = Pathfind(sobj, sobj.Sector).FirstOrDefault(); - if (gotoSector != null) - { - // move - if (gotoSector == null) - { - // try to warp through an unexplored warp point - var wps = sobj.Sector.SpaceObjects.OfType().Where(w => !w.TargetStarSystemLocation.Item.ExploredByEmpires.Contains(sobj.Owner)); - var wp = wps.PickRandom(); - if (wp != null) - { - // warp through the unexplored warp point - sobj.Sector = wp.Target; - } - else if (!LoggedPathfindingError) - { - // no warp points to explore and we haven'IMobileSpaceObject told the player yet - PathfindingError = sobj.CreateLogMessage("{0} found no unexplored warp points at {1} to enter.".F(sobj, sobj.Sector), LogMessageType.Warning); - sobj.Owner.Log.Add(PathfindingError); - LoggedPathfindingError = true; - } - } - else - { - sobj.Sector = gotoSector; - sobj.RefreshDijkstraMap(); - - // consume supplies - sobj.BurnMovementSupplies(); - - // are we there yet, Dad? - if (AreWeThereYet(sobj)) - IsComplete = true; // we've arrived at the target - - // resupply space vehicles - // either this vehicle from other space objects, or other vehicles from this one - // TODO - this should really be done AFTER battles... - if (gotoSector.HasAbility("Supply Generation", sobj.Owner)) - { - foreach (var v in gotoSector.SpaceObjects.OfType().Where(v => v.Owner == sobj.Owner)) - v.SupplyRemaining = v.SupplyStorage; - } - if (gotoSector.StarSystem.HasAbility("Supply Generation - System", sobj.Owner) || gotoSector.StarSystem.HasAbility("Supply Generation - System")) - { - foreach (var v in gotoSector.StarSystem.FindSpaceObjects().Where(v => v.Owner == sobj.Owner)) - v.SupplyRemaining = v.SupplyStorage; - } - } - } - else if (!LoggedPathfindingError) - { - // log pathfinding error - string reason; - if (sobj.StrategicSpeed <= 0) - reason = sobj + " is immobile"; - else - reason = "there is no available path leading toward " + Destination; - PathfindingError = sobj.CreateLogMessage(sobj + " could not " + Verb + " " + KnownTarget + " because " + reason + ".", LogMessageType.Warning); - sobj.Owner.Log.Add(PathfindingError); - LoggedPathfindingError = true; - } - } - - // spend time - sobj.SpendTime(sobj.TimePerMove); - } - else - ord.Owner.RecordLog(ord, $"{ord} cannot pathfind because it is not a mobile space object.", LogMessageType.Error); - } - - public IEnumerable GetErrors(IOrderable v) - { - if (PathfindingError != null) - yield return PathfindingError; - } - - /// - /// Finds the path for executing this order. - /// - /// The space object executing the order. - /// The start location (need not be the current location, in case there are prior orders queued). - /// - public abstract IEnumerable Pathfind(IMobileSpaceObject me, Sector start); - - public void ReplaceClientIDs(IDictionary idmap, ISet done) - { - // This type does not use client objects, so nothing to do here. - } - - public override string ToString() - { - if (KnownTarget == null) - return "Unknown " + Verb + " order"; - return Verb.Capitalize() + " " + KnownTarget; - } - - /// - /// Call this when calling UpdateMemory on the target. - /// Sets the alternate target to the largest ship in a fleet, if the target is a fleet. - /// If the fleet is destroyed, sets the target to the alternate target. - /// If the target is a ship, etc., and it is destroyed, sets the target to the memory of the target, or deletes the order if there is no memory. - /// If the target is a memory, and the original object is sighted again, sets the target to the original object. - /// Otherwise sets the alternate target to the target. - /// - public void UpdateAlternateTarget() - { - if (Target is Fleet) - { - var f = (Fleet)Target; - if (!f.IsDestroyed) - AlternateTarget = f.LeafVehicles.Largest(); - else - Target = AlternateTarget; - } - else if (Target is IMobileSpaceObject) - { - var sobj = (IMobileSpaceObject)Target; - if (sobj.IsMemory && sobj.FindOriginalObject(Owner) != null) - Target = (ISpaceObject)sobj.FindOriginalObject(Owner); - if (!sobj.IsDestroyed) - AlternateTarget = Target; - else if (Owner.Memory[Target.ID] != null) - Target = (ISpaceObject)Owner.Memory[Target.ID]; - else - Dispose(); - } - else - AlternateTarget = Target; - } - - protected abstract bool AreWeThereYet(IMobileSpaceObject me); -} \ No newline at end of file diff --git a/FrEee/Objects/Orders/PursueOrder.cs b/FrEee/Objects/Orders/PursueOrder.cs deleted file mode 100644 index 6ff209905..000000000 --- a/FrEee/Objects/Orders/PursueOrder.cs +++ /dev/null @@ -1,47 +0,0 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Utility; -using System; -using System.Collections.Generic; - -namespace FrEee.Objects.Orders; - -/// -/// An order to move a mobile space object toward another space object. -/// -[Serializable] -public class PursueOrder : PathfindingOrder -{ - public PursueOrder(ISpaceObject target, bool avoidEnemies) - : base(target, avoidEnemies) - { - } - - public override string Verb - { - get - { - if (KnownTarget == null) - return "pursue"; - else if (AvoidEnemies && KnownTarget.Owner != null && (!(KnownTarget is ICombatant) || !(KnownTarget as ICombatant).IsHostileTo(Owner))) - return "escort"; - else - return "pursue"; - } - } - - /// - /// Finds the path for executing this order. - /// - /// The space object executing the order. - /// - public override IEnumerable Pathfind(IMobileSpaceObject me, Sector start) - { - return Pathfinder.Pathfind(me, start, Destination, AvoidEnemies, true, me.DijkstraMap); - } - - protected override bool AreWeThereYet(IMobileSpaceObject me) - { - return me.Sector == Destination; - } -} \ No newline at end of file diff --git a/FrEee/Objects/Orders/RecycleBehaviors/ScrapBehavior.cs b/FrEee/Objects/Orders/RecycleBehaviors/ScrapBehavior.cs deleted file mode 100644 index 7bf9ba9ae..000000000 --- a/FrEee/Objects/Orders/RecycleBehaviors/ScrapBehavior.cs +++ /dev/null @@ -1,53 +0,0 @@ -using FrEee.Interfaces; -using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; -using FrEee.Objects.Vehicles; -using FrEee.Extensions; -using System.Collections.Generic; -using System.Linq; - -namespace FrEee.Objects.Orders.RecycleBehaviors; - -/// -/// Scraps a recyclable object, returning resources equal to its scrap value. -/// If there is anything in cargo, it will be scrapped as well. -/// -public class ScrapBehavior : IRecycleBehavior -{ - public string Verb { get { return "Scrap"; } } - - public void Execute(IRecyclable target, bool didRecycle = false) - { - // don't scrap stuff that's already been scrapped due to it being in cargo of something else being scrapped! - if (!target.IsDisposed) - { - var val = target.ScrapValue; - if (target.Owner != null) // if not, it's already scrapped? - { - target.Owner.StoredResources += val; - target.Owner.Log.Add(target.CreateLogMessage("We have scrapped " + target + " and reclaimed " + val + ".", LogMessageType.Generic)); - } - target.Dispose(); - - if (!didRecycle) - target.Recycle(this, true); - } - } - - public IEnumerable GetErrors(IMobileSpaceObject executor, IRecyclable target) - { - if (target == null) - { - yield return new GenericLogMessage("A scrap order was issued for a nonexistent target."); - yield break; - } - if (target.IsDisposed) - yield return target.CreateLogMessage($"{target} cannot be scrapped because it is already destroyed.", LogMessageType.Error); - if (target.RecycleContainer != executor) - yield return target.CreateLogMessage(target + " cannot be scrapped by " + executor + " because " + target + " does not belong to " + executor + ".", LogMessageType.Error); - if ((target is Ship || target is Base) && !executor.Sector.SpaceObjects.Any(sobj => sobj.Owner == executor.Owner && sobj.HasAbility("Space Yard"))) - yield return target.CreateLogMessage(target + " cannot be scrapped at " + executor.Sector + " because there is no space yard present in that sector.", LogMessageType.Error); - if ((target is IUnit) && !executor.Sector.SpaceObjects.Any(sobj => sobj.Owner == executor.Owner && (sobj is Planet || sobj.HasAbility("Space Yard")))) - yield return target.CreateLogMessage(target + " cannot be scrapped at " + executor.Sector + " because there is no space yard or colony present in that sector.", LogMessageType.Error); - } -} \ No newline at end of file diff --git a/FrEee/Objects/Orders/RecycleFacilityOrCargoOrder.cs b/FrEee/Objects/Orders/RecycleFacilityOrCargoOrder.cs deleted file mode 100644 index 906025a73..000000000 --- a/FrEee/Objects/Orders/RecycleFacilityOrCargoOrder.cs +++ /dev/null @@ -1,119 +0,0 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; -using FrEee.Serialization; -using FrEee.Extensions; -using System.Collections.Generic; -using System.Linq; - -namespace FrEee.Objects.Orders; - -public class RecycleFacilityOrCargoOrder : IOrder -{ - public RecycleFacilityOrCargoOrder(IRecycleBehavior behavior, IRecyclable target) - { - Behavior = behavior; - Target = target; - } - - public IRecycleBehavior Behavior { get; private set; } - - public bool ConsumesMovement - { - get { return false; } - } - - public long ID - { - get; - set; - } - - public bool IsComplete - { - get; - set; - } - - public bool IsDisposed - { - get; - set; - } - - /// - /// The empire which issued the order. - /// - [DoNotSerialize] - public Empire Owner { get { return owner; } set { owner = value; } } - - /// - /// The facility or unit in cargo to recycle. - /// - [DoNotSerialize] - public IRecyclable Target { get { return target.Value; } set { target = value.ReferViaGalaxy(); } } - - private GalaxyReference owner { get; set; } - private GalaxyReference target { get; set; } - - public bool CheckCompletion(IOrderable executor) - { - return IsComplete; - } - - public void Dispose() - { - if (IsDisposed) - return; - foreach (var v in Galaxy.Current.Referrables.OfType()) - v.RemoveOrder(this); - Galaxy.Current.UnassignID(this); - } - - public void Execute(IOrderable x) - { - if (x is IMobileSpaceObject executor) - { - var errors = GetErrors(executor); - if (errors.Any()) - { - if (Owner != null) - { - foreach (var e in errors) - Owner.Log.Add(e); - } - else - IsComplete = true; - return; - } - } - - Behavior.Execute(Target); - IsComplete = true; - } - - public IEnumerable GetErrors(IOrderable executor) - { - return Behavior.GetErrors(executor as IMobileSpaceObject, Target).Concat(SelfErrors); - } - - private IEnumerable SelfErrors - { - get - { - if (Target is ICombatant c && c.IsHostileTo(Owner)) - yield return c.CreateLogMessage($"You can't {Behavior.Verb} {c} because it belongs to a hostile empire.", LogMessageType.Error); - } - } - - public void ReplaceClientIDs(IDictionary idmap, ISet done = null) - { - // This type does not use client objects, so nothing to do here. - } - - public override string ToString() - { - return Behavior.Verb + " " + Target; - } -} \ No newline at end of file diff --git a/FrEee/Objects/Orders/RecycleVehicleInSpaceOrder.cs b/FrEee/Objects/Orders/RecycleVehicleInSpaceOrder.cs deleted file mode 100644 index 759657e03..000000000 --- a/FrEee/Objects/Orders/RecycleVehicleInSpaceOrder.cs +++ /dev/null @@ -1,94 +0,0 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; -using FrEee.Objects.Vehicles; -using FrEee.Serialization; -using System.Collections.Generic; -using System.Linq; - -namespace FrEee.Objects.Orders; - -public class RecycleVehicleInSpaceOrder : IOrder -{ - public RecycleVehicleInSpaceOrder(IRecycleBehavior behavior) - { - Behavior = behavior; - } - - public IRecycleBehavior Behavior { get; private set; } - - public bool ConsumesMovement - { - get { return false; } - } - - public long ID - { - get; - set; - } - - public bool IsComplete - { - get; - set; - } - - public bool IsDisposed - { - get; - set; - } - - /// - /// The empire which issued the order. - /// - [DoNotSerialize] - public Empire Owner { get { return owner; } set { owner = value; } } - - private GalaxyReference owner { get; set; } - - public bool CheckCompletion(IOrderable executor) - { - return IsComplete; - } - - public void Dispose() - { - if (IsDisposed) - return; - foreach (var v in Galaxy.Current.Referrables.OfType()) - v.Orders.Remove(this); - Galaxy.Current.UnassignID(this); - } - - public void Execute(IOrderable executor) - { - var errors = GetErrors(executor); - if (errors.Any() && Owner != null) - { - foreach (var e in errors) - Owner.Log.Add(e); - return; - } - - Behavior.Execute((IRecyclable)executor); - IsComplete = true; - } - - public IEnumerable GetErrors(IOrderable executor) - { - return Behavior.GetErrors(executor as IMobileSpaceObject, executor as IRecyclable); - } - - public void ReplaceClientIDs(IDictionary idmap, ISet done = null) - { - // This type does not use client objects, so nothing to do here. - } - - public override string ToString() - { - return Behavior.Verb; - } -} \ No newline at end of file diff --git a/FrEee/Objects/Orders/SentryOrder.cs b/FrEee/Objects/Orders/SentryOrder.cs deleted file mode 100644 index c3d5ee98f..000000000 --- a/FrEee/Objects/Orders/SentryOrder.cs +++ /dev/null @@ -1,105 +0,0 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; -using FrEee.Objects.Vehicles; -using FrEee.Serialization; -using FrEee.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace FrEee.Objects.Orders; - -/// -/// An order for a mobile space object to hold position until enemies are sighted in the system. -/// -[Serializable] -public class SentryOrder : IOrder -{ - public SentryOrder() - { - Owner = Empire.Current; - } - - public bool ConsumesMovement - { - get { return true; } - } - - public long ID { get; set; } - - public bool IsComplete - { - get; - set; - } - - public bool IsDisposed { get; set; } - - /// - /// The empire which issued the order. - /// - [DoNotSerialize] - public Empire Owner { get { return owner; } set { owner = value; } } - - private GalaxyReference owner { get; set; } - - public bool CheckCompletion(IOrderable v) - { - return IsComplete; - } - - /// - /// Orders are visible only to their owners. - /// - /// - /// - public Visibility CheckVisibility(Empire emp) - { - if (emp == Owner) - return Visibility.Visible; - return Visibility.Unknown; - } - - public void Dispose() - { - if (IsDisposed) - return; - foreach (var v in Galaxy.Current.Referrables.OfType()) - v.Orders.Remove(this); - Galaxy.Current.UnassignID(this); - } - - public void Execute(IOrderable ord) - { - if (ord is IMobileSpaceObject sobj) - { - // if hostiles in system, we are done sentrying - if (sobj.FindStarSystem().FindSpaceObjects(s => s.IsHostileTo(sobj.Owner)).Any()) - IsComplete = true; - - // spend time - sobj.SpendTime(sobj.TimePerMove); - } - else - ord.Owner.RecordLog(ord, $"{ord} cannot sentry because it is not a mobile space object.", LogMessageType.Error); - } - - public IEnumerable GetErrors(IOrderable executor) - { - // this order doesn't error - yield break; - } - - public void ReplaceClientIDs(IDictionary idmap, ISet done = null) - { - // This type does not use client objects, so nothing to do here. - } - - public override string ToString() - { - return "Sentry"; - } -} \ No newline at end of file diff --git a/FrEee/Objects/Orders/TransferCargoOrder.cs b/FrEee/Objects/Orders/TransferCargoOrder.cs deleted file mode 100644 index 96c86b32c..000000000 --- a/FrEee/Objects/Orders/TransferCargoOrder.cs +++ /dev/null @@ -1,141 +0,0 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; -using FrEee.Serialization; -using FrEee.Extensions; -using System.Collections.Generic; -using System.Linq; - -namespace FrEee.Objects.Orders; - -/// -/// An order to transfer cargo from one object to another. -/// -public class TransferCargoOrder : IOrder -{ - public TransferCargoOrder(bool isLoadOrder, CargoDelta cargoDelta, ICargoTransferrer target) - { - Owner = Empire.Current; - IsLoadOrder = isLoadOrder; - CargoDelta = cargoDelta; - Target = target; - } - - /// - /// The cargo delta, which specifies what cargo is to be transferred. - /// - public CargoDelta CargoDelta { get; set; } - - public bool ConsumesMovement - { - get { return false; } - } - - public long ID { get; set; } - - public bool IsComplete - { - get; - set; - } - - public bool IsDisposed { get; set; } - - /// - /// The empire which issued the order. - /// - [DoNotSerialize] - public Empire Owner { get { return owner; } set { owner = value; } } - - /// - /// The cargo transferrer to which the cargo will be transferred, or null to launch/recover to/from space. - /// - [DoNotSerialize] - public ICargoTransferrer Target { get { return target?.Value; } set { target = value.ReferViaGalaxy(); } } - - /// - /// True if this is a load order, false if it is a drop order. - /// - private bool IsLoadOrder { get; set; } - - private GalaxyReference owner { get; set; } - private GalaxyReference target { get; set; } - - public bool CheckCompletion(IOrderable v) - { - return IsComplete; - } - - public void Dispose() - { - if (IsDisposed) - return; - foreach (var v in Galaxy.Current.Referrables.OfType()) - v.RemoveOrder(this); - Galaxy.Current.UnassignID(this); - } - - public void Execute(IOrderable ord) - { - if (ord is ICargoTransferrer executor) - { - var errors = GetErrors(executor); - if (executor.Owner != null) - { - foreach (var error in errors) - executor.Owner.Log.Add(error); - } - - if (!errors.Any()) - { - if (IsLoadOrder) - Target.TransferCargo(CargoDelta, executor, executor.Owner); - else - executor.TransferCargo(CargoDelta, Target, executor.Owner); - } - IsComplete = true; - } - } - - public IEnumerable GetErrors(IOrderable executor) - { - if (executor is ICargoTransferrer t) - { - if (Target != null && t.Sector != Target.Sector) - yield return t.CreateLogMessage(executor + " cannot transfer cargo to " + Target + " because they are not in the same sector.", LogMessageType.Warning); - } - else - yield return executor.CreateLogMessage($"{executor} cannot transfer cargo.", LogMessageType.Warning); - } - - public void ReplaceClientIDs(IDictionary idmap, ISet done = null) - { - if (done == null) - done = new HashSet(); - if (!done.Contains(this)) - { - done.Add(this); - target?.ReplaceClientIDs(idmap, done); - owner.ReplaceClientIDs(idmap, done); - } - } - - public override string ToString() - { - if (Target == null) - { - if (IsLoadOrder) - return "Recover " + CargoDelta; - else - return "Launch " + CargoDelta; - } - else - { - if (IsLoadOrder) - return "Load " + CargoDelta + " from " + Target; - else - return "Drop " + CargoDelta + " at " + Target; - } - } -} \ No newline at end of file diff --git a/FrEee/Objects/Orders/UpgradeFacilityOrder.cs b/FrEee/Objects/Orders/UpgradeFacilityOrder.cs deleted file mode 100644 index 199692e61..000000000 --- a/FrEee/Objects/Orders/UpgradeFacilityOrder.cs +++ /dev/null @@ -1,199 +0,0 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; -using FrEee.Objects.Technology; -using FrEee.Utility; using FrEee.Serialization; -using FrEee.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace FrEee.Objects.Orders; - -/// -/// An order for a construction queue to upgrade a facility. -/// -[Serializable] -public class UpgradeFacilityOrder : IConstructionOrder -{ - public UpgradeFacilityOrder(FacilityUpgrade fu) - { - Owner = Empire.Current; - Upgrade = fu; - } - - public bool ConsumesMovement - { - get { return false; } - } - - public ResourceQuantity Cost - { - get { return Upgrade.Cost; } - } - - public long ID { get; set; } - - public bool IsComplete - { - get - { - if (isComplete == null) - return false; // haven't checked completion yet, so it's probably safe to say it's incomplete - return isComplete.Value; - } - set - { isComplete = value; } - } - - public bool IsDisposed { get; set; } - - IConstructable IConstructionOrder.Item - { - get { return NewFacility; } - } - - public string Name - { - get - { - return "Upgrade to " + Upgrade.New.Name; - } - } - - /// - /// The facility being built. - /// - public Facility NewFacility { get; set; } - - /// - /// The empire which issued the order. - /// - [DoNotSerialize] - public Empire Owner { get { return owner; } set { owner = value; } } - - IConstructionTemplate IConstructionOrder.Template { get { return Upgrade.New; } } - - /// - /// The upgrade to perform. - /// - public FacilityUpgrade Upgrade { get; set; } - - [DoNotSerialize] - private bool? isComplete - { - get; - set; - } - - private GalaxyReference owner { get; set; } - - public bool CheckCompletion(IOrderable queue) - { - if (NewFacility == null) - return false; - isComplete = NewFacility.ConstructionProgress >= Cost || GetErrors(queue).Any(); - return IsComplete; - } - - /// - /// Orders are visible only to their owners. - /// - /// - /// - public Visibility CheckVisibility(Empire emp) - { - if (emp == Owner) - return Visibility.Visible; - return Visibility.Unknown; - } - - public void Dispose() - { - if (IsDisposed) - return; - foreach (var v in Galaxy.Current.Referrables.OfType()) - v.Orders.Remove(this); - Galaxy.Current.UnassignID(this); - } - - /// - /// Does 1 turn's worth of building. - /// - public void Execute(IOrderable ord) - { - if (ord is ConstructionQueue queue) - { - var errors = GetErrors(queue); - foreach (var error in errors) - queue.Owner.Log.Add(error); - - if (!errors.Any()) - { - // create item if needed - if (NewFacility == null) - NewFacility = Upgrade.New.Instantiate(); - - // apply build rate - var costLeft = Cost - NewFacility.ConstructionProgress; - var spending = ResourceQuantity.Min(costLeft, queue.UnspentRate); - if (spending < queue.Owner.StoredResources) - { - spending = ResourceQuantity.Min(spending, queue.Owner.StoredResources); - queue.Container.CreateLogMessage("Construction of " + Upgrade.New + " at " + queue.Container + " was delayed due to lack of resources.", LogMessageType.Generic); - } - queue.Owner.StoredResources -= spending; - queue.UnspentRate -= spending; - NewFacility.ConstructionProgress += spending; - - // if we're done, delete the old facility and replace it with this one - if (CheckCompletion(queue)) - { - var planet = (Planet)queue.Container; - planet.Colony.Facilities.Where(f => f.Template.ModID == Upgrade.Old.ModID).First().Dispose(); // HACK - why are we getting duplicate facility templates? - planet.Colony.Facilities.Add(NewFacility); - } - } - } - } - - public IEnumerable GetErrors(IOrderable ord) - { - if (ord is ConstructionQueue queue) - { - // validate that new facility is unlocked - if (!queue.Owner.HasUnlocked(Upgrade.New)) - yield return Upgrade.Old.CreateLogMessage(Upgrade.Old + " on " + queue.Container + " could not be upgraded to a " + Upgrade.New + " because we have not yet researched the " + Upgrade.New + ".", LogMessageType.Error); - - // validate that new and old facilities are in the same family - if (Upgrade.New.Family.Value != Upgrade.Old.Family.Value) - yield return Upgrade.Old.CreateLogMessage(Upgrade.Old + " on " + queue.Container + " could not be upgraded to a " + Upgrade.New + " because facilities cannot be upgraded to facilities of a different family.", LogMessageType.Error); - - // validate that there is a facility to upgrade - var planet = (Planet)queue.Container; - var colony = planet.Colony; - if (!colony.Facilities.Any(f => f.Template.ModID == Upgrade.Old.ModID)) // HACK - why are we getting duplicate facility templates? - yield return planet.CreateLogMessage("There are no " + Upgrade.Old + "s on " + planet + " to upgrade.", LogMessageType.Error); - } - else - yield return ord.CreateLogMessage($"{ord} cannot upgrade facilities because it is not a construction queue.", LogMessageType.Error); - } - - public void ReplaceClientIDs(IDictionary idmap, ISet done = null) - { - if (done == null) - done = new HashSet(); - if (!done.Contains(this)) - { - done.Add(this); - Upgrade.ReplaceClientIDs(idmap, done); - } - } - - public void Reset() - { - NewFacility = null; - } -} \ No newline at end of file diff --git a/FrEee/Objects/Orders/WarpOrder.cs b/FrEee/Objects/Orders/WarpOrder.cs deleted file mode 100644 index 743e7f4a4..000000000 --- a/FrEee/Objects/Orders/WarpOrder.cs +++ /dev/null @@ -1,172 +0,0 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; -using FrEee.Utility; using FrEee.Serialization; -using FrEee.Extensions; -using System; -using System.Collections.Generic; -using System.Linq; - -namespace FrEee.Objects.Orders; - -/// -/// An order to warp a mobile space object via a warp point. -/// TODO - make this also pursue the warp point first -/// -[Serializable] -public class WarpOrder : IOrder -{ - public WarpOrder(WarpPoint warpPoint) - { - Owner = Empire.Current; - WarpPoint = warpPoint; - } - - public bool ConsumesMovement - { - get { return true; } - } - - public Sector Destination - { - get { return WarpPoint.Target; } - } - - public long ID { get; set; } - - public bool IsComplete - { - get; - set; - } - - public bool IsDisposed { get; set; } - - [DoNotSerialize] - public bool LoggedPathfindingError - { - get; - private set; - } - - /// - /// The empire which issued the order. - /// - [DoNotSerialize] - public Empire Owner { get { return owner; } set { owner = value; } } - - /// - /// The warp point we are using. - /// - [DoNotSerialize] - public WarpPoint WarpPoint { get { return warpPoint; } set { warpPoint = value; } } - - private GalaxyReference owner { get; set; } - private GalaxyReference warpPoint { get; set; } - - public bool CheckCompletion(IOrderable v) - { - return IsComplete; - } - - /// - /// Orders are visible only to their owners. - /// - /// - /// - public Visibility CheckVisibility(Empire emp) - { - if (emp == Owner) - return Visibility.Visible; - return Visibility.Unknown; - } - - public IDictionary, ISet>> CreateDijkstraMap(IMobileSpaceObject me, Sector start) - { - return Pathfinder.CreateDijkstraMap(me, start, Destination, false, true); - } - - public void Dispose() - { - if (IsDisposed) - return; - foreach (var v in Galaxy.Current.Referrables.OfType()) - v.RemoveOrder(this); - Galaxy.Current.UnassignID(this); - } - - public void Execute(IOrderable ord) - { - var errors = GetErrors(ord); - foreach (var error in errors) - Owner.Log.Add(error); - - var sobj = (IMobileSpaceObject)ord; - if (!errors.Any()) - { - var here = sobj.Sector; - if (here == WarpPoint.Sector) - { - // warp now!!! - here.Remove(sobj); - - // warp point turbulence damage? - if (WarpPoint.HasAbility("Warp Point - Turbulence")) - { - var dmg = WarpPoint.GetAbilityValue("Warp Point - Turbulence").ToInt(); - sobj.TakeNormalDamage(dmg); - if (sobj.IsDestroyed) - { - sobj.Owner.Log.Add(sobj.CreateLogMessage(sobj + " was destroyed by turbulence when traversing " + WarpPoint + ".", LogMessageType.Generic)); - IsComplete = true; - return; - } - else - sobj.Owner.Log.Add(sobj.CreateLogMessage(sobj + " took " + dmg + " points of damage from turbulence when traversing " + WarpPoint + ".", LogMessageType.Generic)); - } - - sobj.Sector = WarpPoint.Target; - sobj.RefreshDijkstraMap(); - - // mark system explored - WarpPoint.TargetStarSystemLocation.Item.MarkAsExploredBy(((ISpaceObject)sobj).Owner); - - // done warping - IsComplete = true; - } - else - { - // can'IMobileSpaceObject warp here, maybe the GUI should have issued a move order? - ((ISpaceObject)sobj).Owner.Log.Add(sobj.CreateLogMessage(sobj + " cannot warp via " + WarpPoint + " because it is not currently located at the warp point.", LogMessageType.Warning)); - } - } - - // spend time - sobj.SpendTime(sobj.TimePerMove); - } - - public IEnumerable GetErrors(IOrderable executor) - { - // this order doesn't error - yield break; - } - - public IEnumerable Pathfind(IMobileSpaceObject me, Sector start) - { - return Pathfinder.Pathfind(me, start, Destination, false, true, me.DijkstraMap); - } - - public void ReplaceClientIDs(IDictionary idmap, ISet done = null) - { - // This type does not use client objects, so nothing to do here. - } - - public override string ToString() - { - if (WarpPoint == null) - return "Invalid Warp Order"; - return "Warp via " + WarpPoint.Name + " in " + WarpPoint.FindStarSystem(); - } -} diff --git a/FrEee/Objects/Orders/WaypointOrder.cs b/FrEee/Objects/Orders/WaypointOrder.cs deleted file mode 100644 index 2a2dcb7d2..000000000 --- a/FrEee/Objects/Orders/WaypointOrder.cs +++ /dev/null @@ -1,210 +0,0 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.LogMessages; -using FrEee.Objects.Space; -using FrEee.Utility; using FrEee.Serialization; -using FrEee.Extensions; -using System.Collections.Generic; -using System.Linq; - -namespace FrEee.Objects.Orders; - -/// -/// An order to move to a waypoint. -/// -/// -public class WaypointOrder : IMovementOrder -{ - public WaypointOrder(Waypoint target, bool avoidEnemies) - { - Owner = Empire.Current; - Target = target; - AvoidEnemies = avoidEnemies; - // TODO - add flag for "avoid damaging sectors"? but how to specify in UI? - } - - /// - /// Should pathfinding avoid enemies? - /// - public bool AvoidEnemies { get; set; } - - public bool ConsumesMovement - { - get { return true; } - } - - public Sector Destination - { - get { return Target.Sector; } - } - - public long ID { get; set; } - - public bool IsComplete - { - get; - set; - } - - public bool IsDisposed { get; set; } - - /// - /// Did we already log a pathfinding error this turn? - /// - [DoNotSerialize] - public bool LoggedPathfindingError { get; private set; } - - /// - /// The empire which issued the order. - /// - [DoNotSerialize] - public Empire Owner { get { return owner; } set { owner = value; } } - - /// - /// Any pathfinding error that we might have found. - /// - [DoNotSerialize] - public LogMessage PathfindingError { get; private set; } - - /// - /// The target we are pursuing. - /// - [DoNotSerialize] - public Waypoint Target { get { return target.Value; } set { target = value.ReferViaGalaxy(); } } - - /// - /// A verb used to describe this order. - /// - public string Verb - { - get - { - if (AvoidEnemies) - return "navigate to"; - else - return "patrol"; - } - } - - private GalaxyReference owner { get; set; } - private GalaxyReference target { get; set; } - - public bool CheckCompletion(IOrderable v) - { - return IsComplete; - } - - /// - /// Orders are visible only to their owners. - /// - /// - /// - public Visibility CheckVisibility(Empire emp) - { - if (emp == Owner) - return Visibility.Visible; - return Visibility.Unknown; - } - - public IDictionary, ISet>> CreateDijkstraMap(IMobileSpaceObject me, Sector start) - { - return Pathfinder.CreateDijkstraMap(me, start, Destination, AvoidEnemies, true); - } - - public void Dispose() - { - if (IsDisposed) - return; - foreach (var v in Galaxy.Current.FindSpaceObjects()) - v.RemoveOrder(this); - Galaxy.Current.UnassignID(this); - } - - public void Execute(IOrderable ord) - { - if (ord is IMobileSpaceObject sobj) - { - // TODO - movement logs - if (Target == null) - IsComplete = true; // target waypoint doesn't exist anymore - else if (sobj.Sector == Target.Sector) - IsComplete = true; // we've arrived at the target - else - { - var gotoSector = Pathfind(sobj, sobj.Sector).FirstOrDefault(); - if (gotoSector != null) - { - // move - sobj.Sector = gotoSector; - sobj.RefreshDijkstraMap(); - - // consume supplies - sobj.BurnMovementSupplies(); - - // resupply space vehicles - // either this vehicle from other space objects, or other vehicles from this one - // TODO - this should really be done AFTER battles... - if (gotoSector.HasAbility("Supply Generation", sobj.Owner)) - { - foreach (var v in gotoSector.SpaceObjects.OfType().Where(v => v.Owner == sobj.Owner)) - v.SupplyRemaining = v.SupplyStorage; - } - if (gotoSector.StarSystem.HasAbility("Supply Generation - System", sobj.Owner) || gotoSector.StarSystem.HasAbility("Supply Generation - System")) - { - foreach (var v in gotoSector.StarSystem.FindSpaceObjects().Where(v => v.Owner == sobj.Owner)) - v.SupplyRemaining = v.SupplyStorage; - } - } - else if (!LoggedPathfindingError) - { - // log pathfinding error - string reason; - if (sobj.StrategicSpeed <= 0) - reason = sobj + " is immobile"; - else - reason = "there is no available path leading toward " + Destination; - PathfindingError = sobj.CreateLogMessage(sobj + " could not " + Verb + " " + Target + " because " + reason + ".", LogMessageType.Warning); - sobj.Owner.Log.Add(PathfindingError); - LoggedPathfindingError = true; - } - } - - // spend time - sobj.SpendTime(sobj.TimePerMove); - } - else - ord.RecordLog($"{ord} cannot move to a waypoint because it is not a mobile space object.", LogMessageType.Error); - } - - public IEnumerable GetErrors(IOrderable v) - { - if (!(v is IMobileSpaceObject)) - yield return v.CreateLogMessage($"{v} cannot move to a waypoint because it is not a mobile space object.", LogMessageType.Error); - if (PathfindingError != null) - yield return PathfindingError; - } - - /// - /// Finds the path for executing this order. - /// - /// The space object executing the order. - /// The start location (need not be the current location, in case there are prior orders queued). - /// - public IEnumerable Pathfind(IMobileSpaceObject me, Sector start) - { - return Pathfinder.Pathfind(me, start, Destination, AvoidEnemies, true, me.DijkstraMap); - } - - public void ReplaceClientIDs(IDictionary idmap, ISet done) - { - target.ReplaceClientIDs(idmap, done); - } - - public override string ToString() - { - if (Target == null) - return "Unknown " + Verb + " order"; - return Verb.Capitalize() + " " + Target; - } -} \ No newline at end of file diff --git a/FrEee/Objects/SimulatedObjectWrappers.cs b/FrEee/Objects/SimulatedObjectWrappers.cs index 0f790d0a7..9c85b5254 100644 --- a/FrEee/Objects/SimulatedObjectWrappers.cs +++ b/FrEee/Objects/SimulatedObjectWrappers.cs @@ -1,8 +1,9 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Extensions; using System; using System.Collections.Generic; +using FrEee.Objects.Combat; +using FrEee.Objects.Vehicles; namespace FrEee.Objects; diff --git a/FrEee/Objects/Space/AsteroidField.cs b/FrEee/Objects/Space/AsteroidField.cs index c19099eda..af4b868a1 100644 --- a/FrEee/Objects/Space/AsteroidField.cs +++ b/FrEee/Objects/Space/AsteroidField.cs @@ -1,10 +1,11 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Modding; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; +using FrEee.Objects.GameState; +using FrEee.Objects.Abilities; namespace FrEee.Objects.Space; diff --git a/FrEee/Objects/Space/Conditions.cs b/FrEee/Objects/Space/Conditions.cs new file mode 100644 index 000000000..b9b52ed1b --- /dev/null +++ b/FrEee/Objects/Space/Conditions.cs @@ -0,0 +1,15 @@ +namespace FrEee.Objects.Space; + +/// +/// Planetary conditions. +/// +public enum Conditions +{ + Deadly = -3, + Harsh = -2, + Unpleasant = -1, + Average = 0, + Mild = 1, + Good = 2, + Optimal = 3 +} \ No newline at end of file diff --git a/FrEee/Objects/Space/Fleet.cs b/FrEee/Objects/Space/Fleet.cs index 5590d41b3..1d12c946c 100644 --- a/FrEee/Objects/Space/Fleet.cs +++ b/FrEee/Objects/Space/Fleet.cs @@ -1,5 +1,3 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Objects.Combat; @@ -14,6 +12,10 @@ using System.Drawing; using System.IO; using System.Linq; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.GameState; namespace FrEee.Objects.Space; diff --git a/FrEee/Objects/Space/Galaxy.cs b/FrEee/Objects/Space/Galaxy.cs deleted file mode 100644 index ab5e2bc5e..000000000 --- a/FrEee/Objects/Space/Galaxy.cs +++ /dev/null @@ -1,1304 +0,0 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Abilities; -using FrEee.Objects.Civilization; -using FrEee.Objects.Civilization.Diplomacy.Clauses; -using FrEee.Objects.Combat; -using FrEee.Objects.Combat.Grid; -using FrEee.Objects.Commands; -using FrEee.Objects.Events; -using FrEee.Objects.LogMessages; -using FrEee.Setup; -using FrEee.Setup.WarpPointPlacementStrategies; -using FrEee.Modding; -using FrEee.Utility; -using FrEee.Serialization; -using FrEee.Extensions; -using Microsoft.Scripting.Utils; -using System; -using System.Collections; -using System.Collections.Generic; -using System.IO; -using System.Linq; -using System.Reflection; - -namespace FrEee.Objects.Space; - -/// -/// Prevents IDs from being assigned to objects when calling AssignIDs. -/// TODO - move to utility namespace? -/// -public class DoNotAssignIDAttribute : Attribute - { - public DoNotAssignIDAttribute(bool recurse = true) - { - Recurse = recurse; - } - - /// - /// Should the "don't assign ID" rule be recursive? - /// - public bool Recurse { get; private set; } - } - - /// - /// A galaxy in which the game is played. - /// - [Serializable] - public class Galaxy : ICommonAbilityObject - { - public Galaxy() - { - Galaxy.Current = this; - if (Mod.Current != null) - ModPath = Mod.Current.RootPath; - StarSystemLocations = new List>(); - Empires = new List(); - Name = "Unnamed"; - TurnNumber = 1; - referrables = new Dictionary(); - VictoryConditions = new List(); - AbilityCache = new SafeDictionary>(); - CommonAbilityCache = new SafeDictionary, IEnumerable>(); - SharedAbilityCache = new SafeDictionary, IEnumerable>(); - GivenTreatyClauseCache = new SafeDictionary>(); - ReceivedTreatyClauseCache = new SafeDictionary>(); - Battles = new HashSet(); - ScriptNotes = new DynamicDictionary(); - /*if (Mod.Current != null) - { - foreach (var q in Mod.Current.Objects.OfType()) - AssignID(q); - }*/ - } - - /// - /// The current galaxy. Shouldn't change except at loading a game or turn processing. - /// - public static Galaxy? Current { get; set; } - - public AbilityTargets AbilityTarget - { - get { return AbilityTargets.Galaxy; } - } - - /// - /// Allowed trades in this game. - /// - public AllowedTrades AllowedTrades { get; set; } - - /// - /// Should players have sensor data for all systems from the get-go? - /// - public bool AllSystemsExploredFromStart { get; set; } - - /// - /// The battles which have taken place this turn. - /// - public ICollection Battles { get; private set; } - - public bool CanColonizeOnlyBreathable { get; set; } - - public bool CanColonizeOnlyHomeworldSurface { get; set; } - - public IEnumerable Children - { - get { return StarSystemLocations.Select(l => l.Item); } - } - - public string CommandFileName - { - get - { - if (PlayerNumber > 0) - return Name + "_" + TurnNumber + "_" + PlayerNumber + FrEeeConstants.PlayerCommandsSaveGameExtension; - else - throw new InvalidOperationException("The game host does not have a command file."); - } - } - - /// - /// The empire whose turn it is. - /// - public Empire CurrentEmpire { get; set; } - - /// - /// The current tick in turn processing. 0 = start of turn, 1 = end of turn. - /// - public double CurrentTick { get; set; } - - /// - /// The empires participating in the game. - /// - public IList Empires { get; private set; } - - /// - /// Per mille chance of a random event occurring, per turn, per player. - /// - public double EventFrequency { get; set; } - - public string GameFileName - { - get - { - if (PlayerNumber > 0) - return Name + "_" + TurnNumber + "_" + PlayerNumber + FrEeeConstants.SaveGameExtension; - else - return Name + "_" + TurnNumber + FrEeeConstants.SaveGameExtension; - } - } - - public int Height - { - get; - set; - } - - public IEnumerable IntrinsicAbilities - { - // TODO - galaxy wide abilities? - get { yield break; } - } - - /// - /// Is the ability cache enabled? - /// Always enabled on the client side; only when a flag is set on the server side. - /// - public bool IsAbilityCacheEnabled - { - get - { - return Empire.Current != null || isAbilityCacheEnabled; - } - } - - public bool IsAnalysisAllowed { get; set; } - - /// - /// Is this a "humans vs. AI" game? - /// - public bool IsHumansVsAI { get; set; } - - public bool IsIntelligenceAllowed { get; set; } - - /// - /// Is this a single player game? If so, autoprocess the turn after the player takes his turn. - /// - public bool IsSinglePlayer { get; set; } - - public bool IsSurrenderAllowed { get; set; } - - /// - /// The maximum event severity in this game. - /// - public EventSeverity MaximumEventSeverity { get; set; } - - public int MaxPlanetValue { get; set; } - - public int MaxSpawnedAsteroidValue { get; set; } - - public int MaxSpawnedPlanetValue { get; set; } - - public int MaxX - { - get { return StarSystemLocations.MaxOrDefault(ssl => ssl.Location.X); } - } - - public int MaxY - { - get { return StarSystemLocations.MaxOrDefault(ssl => ssl.Location.Y); } - } - - public int MinAsteroidValue { get; set; } - - public int MinPlanetValue { get; set; } - - public int MinSpawnedAsteroidValue { get; set; } - - public int MinSpawnedPlanetValue { get; set; } - - public int MinX - { - get { return StarSystemLocations.MinOrDefault(ssl => ssl.Location.X); } - } - - public int MinY - { - get { return StarSystemLocations.MinOrDefault(ssl => ssl.Location.Y); } - } - - private string modPath; - - /// - /// The mod being played. - /// - [SerializationPriority(1)] - [ForceSerializationWhenDefaultValue] - public string ModPath - { - get => modPath; - set - { - if (Mod.Current == null || Mod.Current.RootPath != value) - Mod.Load(value); - modPath = value; - } - } - - /// - /// The game name. - /// - public string Name { get; set; } - - /// - /// The next tick size, for ship movement. - /// - public double NextTickSize { get; internal set; } - - /// - /// Should players have an omniscient view of all explored systems? - /// Does not prevent cloaking from working; this is just basic sight. - /// Also does not give battle reports for other empires' battles. - /// - public bool OmniscientView { get; set; } - - public IEnumerable Parents - { - get - { - yield break; - } - } - - /// - /// Events which have been warned of and are pending execution. - /// - public ICollection PendingEvents { get; private set; } = new List(); - - /// - /// The current player number (1 is the first player, 0 is the game host). - /// - public int PlayerNumber - { - get { return Empires.IndexOf(CurrentEmpire) + 1; } - } - - public IEnumerable Referrables { get { return referrables.Values; } } - - /// - /// Model to use for remote mining. - /// - public MiningModel RemoteMiningModel { get; set; } - - /// - /// Who can view empire scores? - /// - public ScoreDisplay ScoreDisplay { get; set; } - - /// - /// Notes that mod scripts can play with. - /// - public DynamicDictionary ScriptNotes { get; private set; } - - /// - /// Model to use for standard planetary mining. - /// - public MiningModel StandardMiningModel { get; set; } - - /// - /// The current stardate. Advances 0.1 years per turn. - /// - public string Stardate - { - get - { - return TurnNumber.ToStardate(); - } - } - - /// - /// The locations of the star systems in the galaxy. - /// - public ICollection> StarSystemLocations { get; private set; } - - /// - /// Technology research cost formula. - /// Low = Level * BaseCost - /// Medium = BaseCost for level 1, Level ^ 2 * BaseCost / 2 otherwise - /// Hight = Level ^ 2 * BaseCost - /// - public TechnologyCost TechnologyCost { get; set; } - - /// - /// Zero means normal tech cost; positive values make researching techs that other players know already harder; negative makes it easier. - /// - public int TechnologyUniqueness { get; set; } - - /// - /// Current time equals turn number plus tick minus 1. - /// - public double Timestamp { get { return TurnNumber + CurrentTick - 1; } } - - /// - /// The current turn number. - /// - public int TurnNumber { get; set; } - - /// - /// Number of turns of uninterrupted galactic peace (Non-Aggression or better between all surviving empires). - /// - public int TurnsOfPeace - { - get - { - // TODO - treaties - return 0; - } - } - - /// - /// Vertical space occupied by star systems. - /// - public int UsedHeight - { - get - { - if (!StarSystemLocations.Any()) - return 0; - return StarSystemLocations.Max(ssl => ssl.Location.Y) - StarSystemLocations.Min(ssl => ssl.Location.Y) + 1; - } - } - - /// - /// Horizontal space occuped by star systems. - /// - public int UsedWidth - { - get - { - if (!StarSystemLocations.Any()) - return 0; - return StarSystemLocations.Max(ssl => ssl.Location.X) - StarSystemLocations.Min(ssl => ssl.Location.X) + 1; - } - } - - /// - /// Game victory conditions. - /// - public IList VictoryConditions { get; private set; } - - /// - /// Delay in turns before victory conditions take effect. - /// - public int VictoryDelay { get; set; } - - public WarpPointPlacementStrategy WarpPointPlacementStrategy { get; set; } - - public int Width - { - get; - set; - } - - /// - /// Cache of abilities belonging to game objects. - /// - [DoNotSerialize] - internal SafeDictionary> AbilityCache { get; private set; } - - /// - /// Cache of abilities belonging to common game objects that can have different abilities for each empire. - /// - [DoNotSerialize] - internal SafeDictionary, IEnumerable> CommonAbilityCache { get; private set; } - - /// - /// Cache of treaty clauses given by empires. - /// - [DoNotSerialize] - internal SafeDictionary> GivenTreatyClauseCache { get; set; } - - /// - /// Cache of treaty clauses received by empires. - /// - [DoNotSerialize] - internal SafeDictionary> ReceivedTreatyClauseCache { get; set; } - - /// - /// Anything in the game that can be referenced from the client side - /// using a Reference object instead of passing whole objects around. - /// Stuff needs to be registered to be found though! - /// - [SerializationPriority(2)] - internal IDictionary referrables { get; set; } - - /// - /// Cache of abilities that are shared to empires from other objects due to treaties. - /// - [DoNotSerialize] - internal SafeDictionary, IEnumerable> SharedAbilityCache { get; private set; } - - /// - /// Serialized string value of the galaxy at the beginning of the turn. - /// - [DoNotSerialize] - internal string StringValue - { - get - { - if (stringValue == null) - StringValue = SaveToString(false); - return stringValue; - } - set - { - stringValue = value; - } - } - - private bool isAbilityCacheEnabled; - - private IDictionary lastBattleTimestamps = new SafeDictionary(); - - private string stringValue; - - public static string GetEmpireCommandsSavePath(string gameName, int turnNumber, int empireNumber) - { - return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Savegame", String.Format("{0}_{1}_{2:d4}{3}", gameName, turnNumber, empireNumber, FrEeeConstants.PlayerCommandsSaveGameExtension)); - } - - public static string GetGameSavePath(string gameName, int turnNumber, int empireNumber) - { - return Path.Combine(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location), "Savegame", empireNumber < 1 ? - String.Format("{0}_{1}{2}", gameName, turnNumber, FrEeeConstants.SaveGameExtension) : - String.Format("{0}_{1}_{2:d4}{3}", gameName, turnNumber, empireNumber, FrEeeConstants.SaveGameExtension)); - } - - /// - /// Initializes a new game. Sets Galaxy.Current. - /// - /// if there is no mod loaded. - /// A status object to report status back to the GUI. - /// How much progress should we report back to the GUI when we're done initializing the galaxy? 1.0 means all done with everything that needs to be done. - public static void Initialize(GameSetup gsu, PRNG dice, Status status = null, double desiredProgress = 1.0) - { - if (Mod.Current == null) - throw new InvalidOperationException("Cannot initialize a galaxy without a mod. Load a mod into Mod.Current first."); - - if (dice == null) - dice = new PRNG(DateTime.Now.Millisecond + 1000 * DateTime.Now.Second + 60000 * DateTime.Now.Minute); - - var startProgress = status == null ? 0d : status.Progress; - var progressPerStep = (desiredProgress - startProgress) / 4d; - - // create the game - if (Galaxy.Current == null) - { - var galtemp = gsu.GalaxyTemplate; - galtemp.GameSetup = gsu; - Current = galtemp.Instantiate(status, startProgress + progressPerStep, dice); - } - if (status != null) - status.Message = "Populating galaxy"; - gsu.PopulateGalaxy(Current, dice); - Current.SaveTechLevelsForUniqueness(); - if (status != null) - status.Progress += progressPerStep; - - // set single player flag - Current.IsSinglePlayer = gsu.IsSinglePlayer; - - // run init script - if (status != null) - status.Message = "Executing script"; - PythonScriptEngine.RunScript(Mod.Current.GameInitScript); - if (status != null) - status.Progress += progressPerStep; - - // save the game - if (status != null) - status.Message = "Saving game"; - Galaxy.SaveAll(status, desiredProgress); - } - - /// - /// Loads a savegame from the Savegame folder. - /// Note that if it was renamed, it might have different game name, turn number, player number, etc. than the filename indicates. - /// - /// - public static void Load(string filename) - { - var fs = new FileStream(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), FrEeeConstants.SaveGameDirectory, filename), FileMode.Open); - Current = Serializer.Deserialize(fs); - if (Current.ModPath == null) - Mod.Load(null); // skipped in deserialization because it is null but the mod needs to be loaded! - if (Empire.Current != null) - { - // load library of designs, strategies, etc. - Library.Load(); - } - fs.Close(); - fs.Dispose(); - Current.PopulatePropertyValues(); - } - - /// - /// Loads a host savegame from the Savegame folder. - /// - /// - /// - public static void Load(string gameName, int turnNumber) - { - Load(gameName + "_" + turnNumber + FrEeeConstants.SaveGameExtension); - } - - /// - /// Loads a player savegame from the Savegame folder. - /// - /// - /// - /// - public static void Load(string gameName, int turnNumber, int playerNumber) - { - Load(gameName + "_" + turnNumber + "_" + playerNumber.ToString("d4") + FrEeeConstants.SaveGameExtension); - } - - /// - /// Loads from a string in memory. - /// - /// - public static void LoadFromString(string serializedData) - { - Galaxy.Current = Serializer.DeserializeFromString(serializedData); - //Current.SpaceObjectIDCheck("after loading from memory"); - - - if (Current.ModPath == null) - Mod.Load(null); // skipped in deserialization because it is null but the mod needs to be loaded! - - if (Empire.Current != null) - { - // initialize IronPython galaxy on load - Current.StringValue = serializedData; - var formula = new ComputedFormula("Galaxy.Current.TurnNumber", null, true); - var turn = formula.Value; - - // load library of designs, strategies, etc. - Library.Load(); - } - - Current.PopulatePropertyValues(); - } - - /// - /// Populates property values specified by . - /// - private void PopulatePropertyValues() - { - // TODO: cache list of properties to populate when deserializing? - // enumerate all referrables - foreach (var referrable in Referrables) - { - // find referrable's properties - var props = referrable.GetType().GetProperties(BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic); - foreach (var prop in props) - { - // search property's attributes for PopulateAttribute - foreach (var att in prop.GetCustomAttributes()) - { - if (att.GetType().IsGenericType && att.GetType().GetGenericTypeDefinition() == typeof(PopulateAttribute<>)) - { - // found PopulateAttribute - // create populator - var populatorType = att.GetType().GetGenericArguments()[0]; - var populator = (IPopulator)populatorType.Instantiate(); - - // get value from populator and save it into the referrable's property - prop.SetValue(referrable, populator.Populate(referrable)); - } - } - } - } - } - - /// - /// Saves all empires' tech levels in the other empires for uniqueness calculations. - /// - internal void SaveTechLevelsForUniqueness() - { - if (Current.TechnologyUniqueness != 0) - { - foreach (var emp in Current.Empires) - { - emp.OtherPlayersTechLevels.Clear(); - foreach (var emp2 in Current.Empires.ExceptSingle(emp)) - { - foreach (var tech in Mod.Current.Technologies) - { - if (emp.OtherPlayersTechLevels[tech] == null) - emp.OtherPlayersTechLevels[tech] = new List(); - emp.OtherPlayersTechLevels[tech].Add(emp2.ResearchedTechnologies[tech]); - } - } - } - } - } - - /// - /// Saves the master view and all players' views of the galaxy, unless single player, in which case only the first player's view is saved. - /// - /// if CurrentEmpire is not null. - public static void SaveAll(Status status = null, double desiredProgress = 1d) - { - if (Current.CurrentEmpire != null) - throw new InvalidOperationException("Can only save player galaxy views from the master galaxy view."); - - var progressPerSaveLoad = (desiredProgress - (status == null ? 0d : status.Progress)) / (Current.IsSinglePlayer ? 3 : (Current.Empires.Count + 2)); - //Current.SpaceObjectIDCheck("before saving"); - - // save master view - if (status != null) - status.Message = "Saving game (host)"; - var gamname = Galaxy.Current.Save(); - if (status != null) - status.Progress += progressPerSaveLoad; - //Current.SpaceObjectIDCheck("after saving master view to disk"); - - // save player views - for (int i = 0; i < Current.Empires.Count; i++) - { - Load(gamname); - if (Current.Empires[i].IsPlayerEmpire) - { - if (status != null) - status.Message = "Saving game (player " + (i + 1) + ")"; - Current.CurrentEmpire = Current.Empires[i]; - Current.Redact(); - //Current.SpaceObjectIDCheck("after creating player view for " + Current.Empires[i]); - Current.Save(false); // already asssigned IDs in the redact phase - if (status != null) - status.Progress += progressPerSaveLoad; - } - } - - // TODO - only reload master view if we really need to - if (status != null) - status.Message = "Saving game"; - Load(gamname); - if (status != null) - status.Progress += progressPerSaveLoad; - - //Current.SpaceObjectIDCheck("after reloading master view"); - } - - /// - /// Assigns an ID to an object. - /// Will dispose of an object that has a negative ID if it hasn't already been disposed of. - /// - /// The object. - /// The ID, or 0 to generate a new ID (unless the ID is already valid). - /// The new ID. - public long AssignID(IReferrable r, long id = 0) - { - if (r.ID < 0 || r.IsDisposed) - { - if (!r.IsDisposed) - r.Dispose(); - return r.ID; - } - - if (r.HasValidID()) - return r.ID; // no need to reassign ID - else if (referrables.ContainsKey(r.ID)) - { - // HACK - already exists, just log an error but don't overwrite anything - // we need to fix start combatants having the same IDs as the real objects... - Console.Error.WriteLine("The galaxy thinks that " + referrables[r.ID] + " has the ID " + r.ID + " but " + r + " claims to have that ID as well."); - return r.ID; - } - - var oldid = r.ID; - long newid = oldid <= 0 ? id : oldid; - - while (newid <= 0 || referrables.ContainsKey(newid)) - { - newid = RandomHelper.Range(1L, long.MaxValue); - } - r.ID = newid; - referrables.Add(newid, r); - - // clean up old IDs - if (oldid > 0 && referrables.ContainsKey(oldid) && oldid != newid) - referrables.Remove(oldid); - - return newid; - } - - /// - /// Assigns IDs to referrable objects in the galaxy and purges disposed objects. - /// Doesn't assign IDs to objects via DoNotAssignID properties, or to memories (or sub-objects of them). - /// - public void CleanGameState() - { - var parser = new ObjectGraphParser(); - bool canAssign = true; - foreach (var kvp in referrables.ToArray()) - { - if (kvp.Value.IsDisposed) - referrables.Remove(kvp.Key); - } - parser.Property += (pname, o, val) => - { - var prop = o.GetType().FindProperty(pname); - var isMemory = val is IFoggable && (val as IFoggable).IsMemory; - canAssign = !prop.HasAttribute() && !isMemory; - if (isMemory) - return false; // no recursion! - if (prop.GetAttributes().Any(a => a.Recurse)) - return false; // no recursion! - else - return true; - }; - var colls = new List(); - parser.StartObject += o => - { - if (o is IReferrable && canAssign) - { - var r = (IReferrable)o; - AssignID(r); - } - if (o is IEnumerable) - { - colls.Add((IEnumerable)o); - } - }; - parser.EndObject += o => - { - if (o is IEnumerable) - { - colls.Remove((IEnumerable)o); - } - }; - parser.Parse(this); - foreach (var l in StarSystemLocations.ToArray()) - { - if (l.Item == null) - StarSystemLocations.Remove(l); - else - { - foreach (var l2 in l.Item.SpaceObjectLocations.ToArray()) - { - if (l2.Item == null || l2.Item.IsDisposed) - l.Item.SpaceObjectLocations.Remove(l2); - } - } - } - } - - public void ComputeNextTickSize() - { - var objs = FindSpaceObjects().Where(obj => obj.Orders.Any()); - objs = objs.Where(obj => !obj.IsMemory); - if (objs.Where(v => v.TimeToNextMove > 0).Any() && CurrentTick < 1.0) - { - // HACK - why are objects getting zero time to next move?! - var nextTickSize = objs.Where(v => v.TimeToNextMove > 0).Min(v => v.TimeToNextMove); - NextTickSize = Math.Min(1.0 - CurrentTick, nextTickSize); - } - else if (objs.Any()) - { - NextTickSize = objs.Min(v => v.TimePerMove); - } - else - NextTickSize = double.PositiveInfinity; - } - - /// - /// Creates a new empire. - /// - /// - public Empire CreateNewEmpire() - { - var emp = new Empire(); - emp.Name = "Randomly Generated Empire"; // TODO - load from EmpireNames.txt / EmpireTypes.txt - - // TODO - assign AI and primary race to empire - Empires.Add(emp); - return emp; - } - - /// - /// Disables the server side ability cache. - /// - public void DisableAbilityCache() - { - isAbilityCacheEnabled = false; - AbilityCache.Clear(); - CommonAbilityCache.Clear(); - SharedAbilityCache.Clear(); - } - - /// - /// Enables the server side ability cache. - /// - public void EnableAbilityCache() - { - isAbilityCacheEnabled = true; - } - - /// - /// Finds referrable objects in the galaxy. - /// - /// - /// - /// - public IEnumerable Find(Func condition = null) where T : IReferrable - { - if (condition == null) - condition = t => true; - return Referrables.OfType().Where(r => condition(r)); - } - - /// - /// Searches for space objects matching criteria. - /// - /// The type of space object. - /// The criteria. - /// The matching space objects. - public IEnumerable FindSpaceObjects(Func criteria = null) - { - return StarSystemLocations.SelectMany(l => l.Item.FindSpaceObjects(criteria)); - } - - public IEnumerable GetContainedAbilityObjects(Empire emp) - { - return StarSystemLocations.Select(ssl => ssl.Item).Concat(StarSystemLocations.SelectMany(ssl => ssl.Item.GetContainedAbilityObjects(emp))); - } - - public string GetEmpireCommandsSavePath(Empire emp) - { - return GetEmpireCommandsSavePath(Name, TurnNumber, Empires.IndexOf(emp) + 1); - } - - public string GetGameSavePath(Empire emp = null) - { - if (emp == null) - emp = CurrentEmpire; - return GetGameSavePath(Name, TurnNumber, emp == null ? 0 : (Empires.IndexOf(emp) + 1)); - } - - public IReferrable GetReferrable(long key) - { - if (!referrables.ContainsKey(key)) - return null; - return referrables[key]; - } - - /// - /// Finds the real version of a fake referrable. - /// - /// - /// The fake referrable. - /// - public T GetReferrable(T fakeobj) - where T : IReferrable - { - return (T)GetReferrable(fakeobj.ID); - } - - /// - /// Loads player commands into the current game state. - /// If this is the host view, commands will be loaded for all players. - /// If this is the player view, commands will be immediately executed so as to provide the player with a fresh game state. - /// - /// Player empires which did not submit commands and are not defeated. - public IEnumerable LoadCommands() - { - // whose commands are we loading? - var emps = new List(); - if (CurrentEmpire == null) - emps.AddRange(Empires); - else - emps.Add(CurrentEmpire); - - var noCmds = new List(); - - foreach (var emp in emps) - { - var plrfile = GetEmpireCommandsSavePath(emp); - if (File.Exists(plrfile)) - { - var fs = new FileStream(plrfile, FileMode.Open); - var cmds = DeserializeCommands(fs); - LoadCommands(emp, cmds); - fs.Close(); fs.Dispose(); - } - else if (emp.IsPlayerEmpire) - noCmds.Add(emp); - } - - if (CurrentEmpire != null) - { - foreach (var cmd in CurrentEmpire.Commands) - { - if (cmd.Executor == null) - CurrentEmpire.Log.Add(CurrentEmpire.CreateLogMessage($"{cmd} cannot be issued because its executor does not exist. Probably a bug...", LogMessageType.Error)); - else if (cmd.Issuer != cmd.Executor.Owner && cmd.Issuer != cmd.Executor) - CurrentEmpire.Log.Add(CurrentEmpire.CreateLogMessage("We cannot issue commands to " + cmd.Executor + " because it does not belong to us!", LogMessageType.Error)); - else - cmd.Execute(); - } - } - - return noCmds; - } - - /// - /// Only public for unit tests. You should probably call ProcessTurn instead. - /// - public void MoveShips() - { - var vlist = FindSpaceObjects().Where(sobj => sobj.Container == null && !sobj.IsMemory).Shuffle(); - foreach (var v in vlist) - { - // mark system explored if not already - var sys = v.StarSystem; - if (sys == null) - continue; // space object is dead, or not done being built - - if (CurrentTick == 0d && !v.Orders.OfType().Any()) - v.DealWithMines(); - - bool didStuff = v.ExecuteOrders(); - sys.MarkAsExploredBy(v.Owner); - - // update memory sight after movement - if (didStuff) - { - v.UpdateEmpireMemories(); - if (v.StarSystem != null && v.Owner != null) - { - foreach (var sobj in v.StarSystem.FindSpaceObjects().Where(sobj => sobj != v && !sobj.IsMemory && v.Owner.CanSee(sobj)).ToArray()) - v.Owner.UpdateMemory(sobj); - } - - // replenish shields after moving (who knows, we might be out of supplies, or about to hit a minefield) - v.ReplenishShields(); - } - - // check for battles - var sector = v.Sector; - if (v.Owner != null && // unowned objects can't pick fights - sector != null && // can't fight nowhere - sector.SpaceObjects.OfType().Any( - sobj => - (sobj.IsHostileTo(v.Owner) && sobj.Weapons.Any() || v.IsHostileTo(sobj.Owner) && v.Weapons.Any()) // any enemies? - && (sobj.Owner.CanSee(v) || v.Owner.CanSee(sobj)) // enemies are visible? - && (!lastBattleTimestamps.ContainsKey(sector) || lastBattleTimestamps[sector] < Timestamp - (v.StrategicSpeed == 0 ? 1d : 1d / v.StrategicSpeed)))) // have we fought here too recently? - { - // resolve the battle - var battle = new SpaceBattle(sector); - battle.Resolve(); - Battles.Add(battle); - foreach (var emp in battle.Empires) - emp.Log.Add(battle.CreateLogMessage(battle.NameFor(emp), LogMessageType.Battle)); - lastBattleTimestamps[sector] = Current.Timestamp; - } - } - } - - public Sector PickRandomSector(PRNG prng = null) - { - return StarSystemLocations.PickRandom(prng).Item.PickRandomSector(prng); - } - - /// - /// Removes any space objects, etc. that the current empire cannot see. - /// - public void Redact() - { - // save off empire scores first, before data is removed - foreach (var emp in Empires) - { - emp.Scores[TurnNumber] = emp.ComputeScore(Empire.Current); - } - - // the galaxy data itself - if (Empire.Current != null) - ScriptNotes.Clear(); - - // redact sub objects - var parser = new ObjectGraphParser(); - parser.StartObject += redactParser_StartObject; - parser.Parse(this); - - // clean up redacted objects that are not IFoggable - foreach (var x in StarSystemLocations.Where(x => x.Item.IsDisposed).ToArray()) - StarSystemLocations.Remove(x); - - // delete memories since they've been copied to "physical" objects already - foreach (var kvp in Empire.Current.Memory.ToArray()) - { - kvp.Value.Dispose(); - Empire.Current.Memory.Remove(kvp); - } - } - - public void Save(Stream stream, bool assignIDs = true) - { - if (assignIDs) - CleanGameState(); - foreach (var kvp in referrables.Where(kvp => kvp.Value.IsDisposed).ToArray()) - referrables.Remove(kvp); - Serializer.Serialize(this, stream); - } - - /// - /// Saves the game to an appropriately named file in the Savegame folder. - /// Files are named GameName_TurnNumber_PlayerNumber.gam for players (PlayerNumber is 1-indexed) - /// and GameName_TurnNumber.gam for the host. - /// - /// The filename saved to without the folder name (which is Savegame). - public string Save(bool assignIDs = true) - { - if (assignIDs) - CleanGameState(); - foreach (var kvp in referrables.Where(kvp => kvp.Value.ID < 0).ToArray()) - referrables.Remove(kvp); - string filename; - if (CurrentEmpire == null) - filename = Name + "_" + TurnNumber + ".gam"; - else - filename = Name + "_" + TurnNumber + "_" + (Empires.IndexOf(CurrentEmpire) + 1).ToString("d4") + ".gam"; - if (!Directory.Exists(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), FrEeeConstants.SaveGameDirectory))) - Directory.CreateDirectory(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), FrEeeConstants.SaveGameDirectory)); - var fs = new FileStream(Path.Combine(Path.GetDirectoryName(Assembly.GetEntryAssembly().Location), FrEeeConstants.SaveGameDirectory, filename), FileMode.Create); - Serializer.Serialize(this, fs); - fs.Close(); fs.Dispose(); - return filename; - } - - /// - /// Saves the player's commands to an appropriately named file in the Savegame folder. - /// Files are named GameName_TurnNumber_PlayerNumber.plr. (PlayerNumber is 1-indexed and padded to 4 digits with zeroes) - /// This doesn't make sense for the host view, so an exception will be thrown if there is no current empire. - /// - /// The filename saved to without the folder name (which is Savegame). - /// if there is no current empire. - public string SaveCommands() - { - CleanGameState(); - if (CurrentEmpire == null) - throw new InvalidOperationException("Can't save commands without a current empire."); - foreach (var c in Empire.Current.Commands.OfType().ToArray()) - Empire.Current.Commands.Remove(c); - Empire.Current.Commands.Add(new SetPlayerInfoCommand(Empire.Current) { PlayerInfo = Empire.Current.PlayerInfo }); - if (!Directory.Exists(FrEeeConstants.SaveGameDirectory)) - Directory.CreateDirectory(FrEeeConstants.SaveGameDirectory); - var filename = GetEmpireCommandsSavePath(CurrentEmpire); - var fs = new FileStream(filename, FileMode.Create); - SerializeCommands(fs); - fs.Close(); fs.Dispose(); - - // save library of designs, commands, etc. - Library.Save(); - - return filename; - } - - public string SaveToString(bool assignIDs = true) - { - if (assignIDs) - CleanGameState(); - return Serializer.SerializeToString(this); - } - - public override string ToString() - { - if (CurrentEmpire == null) - return Name; - return Name + " - " + CurrentEmpire.Name + " - " + CurrentEmpire.LeaderName + " - " + Stardate; - } - - public void UnassignID(long id) - { - if (referrables.ContainsKey(id)) - { - var r = referrables[id]; - r.ID = -1; - referrables.Remove(id); - } - } - - public void UnassignID(IReferrable r) - { - if (r == null || r.ID < 0) - return; // nothing to do - if (referrables.ContainsKey(r.ID)) - { - if (referrables[r.ID] == r) - referrables.Remove(r.ID); - else - { - var galaxyThinksTheIDIs = referrables.SingleOrDefault(kvp => kvp.Value == r); - referrables.Remove(galaxyThinksTheIDIs); - } - } - else if (referrables.Values.Contains(r)) - { - try - { - referrables.Remove(referrables.Single(kvp => kvp.Value == r)); - } - catch (InvalidOperationException ex) - { - // HACK - why is the item not being found? sequence contains no matching element? it's right there! - Console.Error.WriteLine(ex); - } - } - //r.ID = -1; - } - - internal void SpaceObjectIDCheck(string when) - { - foreach (var sobj in FindSpaceObjects().ToArray()) - { - if (!referrables.ContainsKey(sobj.ID)) - AssignID(sobj); - if (sobj.ID > 0) - { - var r = referrables[sobj.ID]; - if (r != sobj) - { - // HACK - assume the space object that's actually in space is "real" - referrables[sobj.ID] = sobj; - Console.Error.WriteLine("Space object identity mismatch " + when + " for ID=" + sobj.ID + ". " + sobj + " is actually in space so it is replacing " + r + " in the referrables collection."); - } - } - } - } - - /// - /// Deserializes the player's commands. - /// - /// - /// - private static IList DeserializeCommands(Stream stream) - { - var cmds = Serializer.Deserialize>(stream); - - // check for client safety - foreach (var cmd in cmds.Where(cmd => cmd != null)) - { - cmd.CheckForClientSafety(); - } - - return cmds; - } - - internal void LoadCommands(Empire emp, IList cmds) - { - cmds = cmds.Where(cmd => cmd != null).Distinct().ToList(); // HACK - why would we have null/duplicate commands in a plr file?! - emp.Commands.Clear(); - var idmap = new Dictionary(); - foreach (var cmd in cmds) - { - if (cmd.NewReferrables.Any(r => r.IsDisposed)) - { - emp.Log.Add(new GenericLogMessage("Command \"" + cmd + "\" contained a reference to deleted object \"" + cmd.NewReferrables.First(r => r.IsDisposed) + "\" and will be ignored. This may be a game bug.")); - continue; - } - emp.Commands.Add(cmd); - foreach (var r in cmd.NewReferrables) - { - var clientid = r.ID; - var serverid = AssignID(r); - if (idmap.ContainsKey(clientid)) - { - if (idmap[clientid] != serverid) - throw new InvalidOperationException($"Adding {r} with ID {serverid} to client ID {clientid} for {emp} when that client ID is already mapped to server ID {idmap[clientid]}."); - // else do nothing - } - else - idmap.Add(clientid, serverid); - } - } - foreach (var cmd in cmds) - cmd.ReplaceClientIDs(idmap); // convert client IDs to server IDs - } - - private void redactParser_StartObject(object o) - { - if (o is IReferrable) - AssignID(o as IReferrable); - if (o is IFoggable obj && o is IReferrable r) - { - if (!obj.IsMemory) - { - var id = r.ID; - var vis = obj.CheckVisibility(CurrentEmpire); - if (vis < Visibility.Fogged) - obj.Dispose(); - if (vis == Visibility.Fogged && CurrentEmpire.Memory.ContainsKey(id)) - { - var mem = (IReferrable)CurrentEmpire.Memory[id]; - mem.CopyToExceptID(r, IDCopyBehavior.PreserveDestination); // memory sight! - if (mem is ILocated l1 && r is ILocated l2) - l2.Sector = l1.Sector; // hack to copy location just now without always copying it when an object is copied - obj.IsMemory = true; - } - obj.Redact(Empire.Current); - } - else - { - // memories are only visible to the empire which is seeing them! - // well unless we add some sort of intel project to see them or something... - if (!CurrentEmpire.Memory.Values.Contains(obj)) - obj.Dispose(); - } - } - //SpaceObjectIDCheck("when redacting " + o); - } - - /// - /// Serializes the player's commands. - /// - /// if no current empire - /// - private void SerializeCommands(Stream stream) - { - if (CurrentEmpire == null) - throw new InvalidOperationException("Can't serialize commands if there is no current empire."); - - Serializer.Serialize(CurrentEmpire.Commands, stream); - } - - /// - /// Disposes of any space objects that aren't in space, under construction, or part of the mod definition. - /// - private void SpaceObjectCleanup() - { - foreach (var sobj in Referrables.OfType().ToArray()) - { - bool dispose = true; - if (sobj.Sector != null) - dispose = false; // save space objects that are in space - else if (this is IUnit u && u.FindContainer() != null) // save units that are in cargo - dispose = false; - else if (Mod.Current.StellarObjectTemplates.Contains(sobj as StellarObject)) - dispose = false; // save stellar objects that are part of the mod templates - else if (Referrables.OfType().Any(q => q.Orders.Any(o => o.Item == sobj as IConstructable))) - dispose = false; // save constructable space objects under construction - if (dispose) - sobj.Dispose(); - } - } - - // TODO - replace all those duplicate properties with a reference to the game setup - /* - /// - /// The game setup used to create this galaxy. - /// - public GameSetup GameSetup { get; set; } - */ - } diff --git a/FrEee/Interfaces/ILocated.cs b/FrEee/Objects/Space/ILocated.cs similarity index 51% rename from FrEee/Interfaces/ILocated.cs rename to FrEee/Objects/Space/ILocated.cs index f3ca8a93e..0d6cf1fab 100644 --- a/FrEee/Interfaces/ILocated.cs +++ b/FrEee/Objects/Space/ILocated.cs @@ -1,13 +1,11 @@ -using FrEee.Objects.Space; - -namespace FrEee.Interfaces; +namespace FrEee.Objects.Space; /// /// Something which is located in a sector, or is a sector itself. /// public interface ILocated { - Sector Sector { get; set; } + Sector Sector { get; set; } - StarSystem StarSystem { get; } + StarSystem StarSystem { get; } } \ No newline at end of file diff --git a/FrEee/Interfaces/IMineableSpaceObject.cs b/FrEee/Objects/Space/IMineableSpaceObject.cs similarity index 86% rename from FrEee/Interfaces/IMineableSpaceObject.cs rename to FrEee/Objects/Space/IMineableSpaceObject.cs index ffe8f07d7..010b8d8ed 100644 --- a/FrEee/Interfaces/IMineableSpaceObject.cs +++ b/FrEee/Objects/Space/IMineableSpaceObject.cs @@ -1,5 +1,5 @@ using FrEee.Utility; -namespace FrEee.Interfaces; +namespace FrEee.Objects.Space; /// /// A space object with a resource value. diff --git a/FrEee/Interfaces/IMobileSpaceObject.cs b/FrEee/Objects/Space/IMobileSpaceObject.cs similarity index 86% rename from FrEee/Interfaces/IMobileSpaceObject.cs rename to FrEee/Objects/Space/IMobileSpaceObject.cs index 88db6b853..4445ac49d 100644 --- a/FrEee/Interfaces/IMobileSpaceObject.cs +++ b/FrEee/Objects/Space/IMobileSpaceObject.cs @@ -1,8 +1,11 @@ -using FrEee.Objects.Space; +using FrEee.Objects.Civilization; +using FrEee.Objects.Combat; +using FrEee.Objects.GameState; +using FrEee.Objects.Space; using FrEee.Utility; using System.Collections.Generic; -namespace FrEee.Interfaces; +namespace FrEee.Objects.Space; /// /// An object that can move about in space and/or receive orders. diff --git a/FrEee/Objects/Space/ISpaceObject.cs b/FrEee/Objects/Space/ISpaceObject.cs new file mode 100644 index 000000000..b77a49542 --- /dev/null +++ b/FrEee/Objects/Space/ISpaceObject.cs @@ -0,0 +1,55 @@ +using FrEee.Objects.Abilities; +using FrEee.Objects.GameState; +using FrEee.Serialization; +using FrEee.Utility; +namespace FrEee.Objects.Space; + +public interface ISpaceObject : IOwnableAbilityObject, IPictorial, IReferrable, IFoggable, INamed, ILocated +{ + /// + /// Can this space object be placed in a fleet? + /// + bool CanBeInFleet { get; } + + /// + /// Is this space object affeced by sight obscuration abilities? + /// This does not affect cloaking abilities, only sector/system obscuration. + /// + /// + /// true if this instance can be obscured; otherwise, false. + /// + bool CanBeObscured { get; } + + /// + /// Can this space object traverse warp points? + /// + bool CanWarp { get; } + + /// + /// Does this space object have infinite supplies? + /// TODO - make supply a resource? + /// + bool HasInfiniteSupplies { get; } + + /// + /// Is this space object idle? + /// Space objects are idle if they have no orders (but they have speed greater than zero, and are not in a fleet) or their construction queue has a fractional ETA less than 1. + /// + bool IsIdle { get; } + + /// + /// The name of this space object. + /// + new string Name { get; set; } + + /// + /// Resources stored on this space object. + /// For a ship, this could include supplies. + /// + ResourceQuantity StoredResources { get; } + + /// + /// Supply storage capacity. + /// + int SupplyStorage { get; } +} \ No newline at end of file diff --git a/FrEee/Objects/Space/IStellarObject.cs b/FrEee/Objects/Space/IStellarObject.cs new file mode 100644 index 000000000..d65f6614a --- /dev/null +++ b/FrEee/Objects/Space/IStellarObject.cs @@ -0,0 +1,39 @@ +using FrEee.Modding; +using FrEee.Objects.Abilities; +using FrEee.Serialization; + +namespace FrEee.Objects.Space; + +public interface IStellarObject : ISpaceObject, IReferrable, IModObject, IAbilityContainer +{ + /// + /// A description of this stellar object. + /// + string Description { get; set; } + + /// + /// Used for naming. + /// + int Index { get; set; } + + /// + /// Used for naming. + /// + bool IsUnique { get; set; } + + /// + /// The name of this stellar object. + /// + new string Name { get; set; } + + /// + /// Name of the picture used to represent this stellar object, excluding the file extension. + /// PNG files will be searched first, then BMP. + /// + string PictureName { get; set; } + + /// + /// The stellar size of this object. + /// + StellarSize StellarSize { get; } +} \ No newline at end of file diff --git a/FrEee/Objects/Space/Planet.cs b/FrEee/Objects/Space/Planet.cs index e2a332195..8ad22346a 100644 --- a/FrEee/Objects/Space/Planet.cs +++ b/FrEee/Objects/Space/Planet.cs @@ -1,5 +1,3 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Objects.Combat; @@ -7,13 +5,18 @@ using FrEee.Objects.Vehicles; using FrEee.Modding; using FrEee.Modding.Templates; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.GameState; namespace FrEee.Objects.Space; diff --git a/FrEee/Objects/Space/Sector.cs b/FrEee/Objects/Space/Sector.cs index 27936e714..535aaeb19 100644 --- a/FrEee/Objects/Space/Sector.cs +++ b/FrEee/Objects/Space/Sector.cs @@ -1,14 +1,16 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; using System.Text.RegularExpressions; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.Vehicles; +using FrEee.Objects.GameState; namespace FrEee.Objects.Space; diff --git a/FrEee/Objects/Space/Star.cs b/FrEee/Objects/Space/Star.cs index 2be0fcf3a..cc897480d 100644 --- a/FrEee/Objects/Space/Star.cs +++ b/FrEee/Objects/Space/Star.cs @@ -1,9 +1,10 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; +using FrEee.Objects.GameState; +using FrEee.Objects.Abilities; namespace FrEee.Objects.Space; diff --git a/FrEee/Objects/Space/StarSystem.cs b/FrEee/Objects/Space/StarSystem.cs index 702dc5498..54b2959b8 100644 --- a/FrEee/Objects/Space/StarSystem.cs +++ b/FrEee/Objects/Space/StarSystem.cs @@ -3,8 +3,6 @@ using System.Drawing; using System.IO; using System.Linq; -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Modding; @@ -12,6 +10,7 @@ using FrEee.Utility; using FrEee.Serialization; using FrEee.Extensions; +using FrEee.Objects.GameState; namespace FrEee.Objects.Space; diff --git a/FrEee/Objects/Space/StellarObject.cs b/FrEee/Objects/Space/StellarObject.cs index 2c69c1995..d7b5d62dd 100644 --- a/FrEee/Objects/Space/StellarObject.cs +++ b/FrEee/Objects/Space/StellarObject.cs @@ -1,14 +1,15 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Modding; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.IO; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.GameState; namespace FrEee.Objects.Space; diff --git a/FrEee/Enumerations/StellarSize.cs b/FrEee/Objects/Space/StellarSize.cs similarity index 50% rename from FrEee/Enumerations/StellarSize.cs rename to FrEee/Objects/Space/StellarSize.cs index d9563efe9..6856e6fc8 100644 --- a/FrEee/Enumerations/StellarSize.cs +++ b/FrEee/Objects/Space/StellarSize.cs @@ -1,13 +1,13 @@ -namespace FrEee.Enumerations; +namespace FrEee.Objects.Space; /// /// The size of a stellar object. /// public enum StellarSize { - Tiny = 1, - Small, - Medium, - Large, - Huge + Tiny = 1, + Small, + Medium, + Large, + Huge } \ No newline at end of file diff --git a/FrEee/Objects/Space/Storm.cs b/FrEee/Objects/Space/Storm.cs index 76f463379..459568be9 100644 --- a/FrEee/Objects/Space/Storm.cs +++ b/FrEee/Objects/Space/Storm.cs @@ -1,9 +1,10 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; +using FrEee.Objects.GameState; +using FrEee.Objects.Abilities; namespace FrEee.Objects.Space; diff --git a/FrEee/Objects/Space/WarpPoint.cs b/FrEee/Objects/Space/WarpPoint.cs index 34f749666..f6f8b7a7e 100644 --- a/FrEee/Objects/Space/WarpPoint.cs +++ b/FrEee/Objects/Space/WarpPoint.cs @@ -1,10 +1,11 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; using System.Linq; +using FrEee.Objects.GameState; +using FrEee.Objects.Abilities; namespace FrEee.Objects.Space; diff --git a/FrEee/Objects/Technology/Component.cs b/FrEee/Objects/Technology/Component.cs index 8988bc828..29de421a3 100644 --- a/FrEee/Objects/Technology/Component.cs +++ b/FrEee/Objects/Technology/Component.cs @@ -1,17 +1,17 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Abilities; +using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Objects.Combat; using FrEee.Objects.Space; using FrEee.Modding; -using FrEee.Modding.Interfaces; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using FrEee.Objects.Vehicles; +using FrEee.Objects.GameState; namespace FrEee.Objects.Technology; diff --git a/FrEee/Objects/Technology/ComponentReference.cs b/FrEee/Objects/Technology/ComponentReference.cs index f4ee4fc99..d3d92330a 100644 --- a/FrEee/Objects/Technology/ComponentReference.cs +++ b/FrEee/Objects/Technology/ComponentReference.cs @@ -1,7 +1,8 @@ using System.Collections.Generic; -using FrEee.Interfaces; using FrEee.Serialization; using FrEee.Extensions; +using FrEee.Objects.Vehicles; +using FrEee.Objects.GameState; namespace FrEee.Objects.Technology; diff --git a/FrEee/Objects/Technology/DirectFireWeaponInfo.cs b/FrEee/Objects/Technology/DirectFireWeaponInfo.cs index 079c2d311..f641d4271 100644 --- a/FrEee/Objects/Technology/DirectFireWeaponInfo.cs +++ b/FrEee/Objects/Technology/DirectFireWeaponInfo.cs @@ -1,4 +1,5 @@ using FrEee.Modding; +using FrEee.Objects.Combat; using System; namespace FrEee.Objects.Technology; @@ -11,14 +12,14 @@ public class DirectFireWeaponInfo : WeaponInfo /// public Formula AccuracyModifier { get; set; } - public override Enumerations.WeaponTypes WeaponType + public override WeaponTypes WeaponType { get { if (IsPointDefense) - return Enumerations.WeaponTypes.DirectFirePointDefense; + return WeaponTypes.DirectFirePointDefense; else - return Enumerations.WeaponTypes.DirectFire; + return WeaponTypes.DirectFire; } } } \ No newline at end of file diff --git a/FrEee/Objects/Technology/Facility.cs b/FrEee/Objects/Technology/Facility.cs index 92c23de91..4682daf1c 100644 --- a/FrEee/Objects/Technology/Facility.cs +++ b/FrEee/Objects/Technology/Facility.cs @@ -1,17 +1,18 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Abilities; +using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Objects.Combat; using FrEee.Objects.Space; using FrEee.Modding; -using FrEee.Modding.Interfaces; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.GameState; namespace FrEee.Objects.Technology; diff --git a/FrEee/Objects/Technology/FacilityUpgrade.cs b/FrEee/Objects/Technology/FacilityUpgrade.cs index bcca796ce..38b1a50d8 100644 --- a/FrEee/Objects/Technology/FacilityUpgrade.cs +++ b/FrEee/Objects/Technology/FacilityUpgrade.cs @@ -1,10 +1,11 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Modding; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Modding; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.GameState; namespace FrEee.Objects.Technology; diff --git a/FrEee/Objects/Technology/Hull.cs b/FrEee/Objects/Technology/Hull.cs index 93d470f80..6b9b87463 100644 --- a/FrEee/Objects/Technology/Hull.cs +++ b/FrEee/Objects/Technology/Hull.cs @@ -1,6 +1,4 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Abilities; +using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Objects.Vehicles; using FrEee.Modding; @@ -11,6 +9,7 @@ using System.Collections.Generic; using System.Drawing; using System.IO; +using FrEee.Objects.GameState; namespace FrEee.Objects.Technology; diff --git a/FrEee/Objects/Technology/IHull.cs b/FrEee/Objects/Technology/IHull.cs new file mode 100644 index 000000000..b95e522de --- /dev/null +++ b/FrEee/Objects/Technology/IHull.cs @@ -0,0 +1,103 @@ +using FrEee.Utility; +using System.Collections.Generic; +using System.Drawing; +using FrEee.Objects.Abilities; +using FrEee.Objects.Vehicles; +using FrEee.Objects.GameState; +using FrEee.Modding; + +namespace FrEee.Objects.Technology; + +public interface IHull : IModObject, IResearchable, IAbilityContainer, IPictorial, IUpgradeable +{ + /// + /// Can this hull use components with the Ship Auxiliary Control ability? + /// + bool CanUseAuxiliaryControl { get; set; } + + string Code { get; set; } + ResourceQuantity Cost { get; set; } + string Description { get; set; } + + /// + /// Maximum number of engines allowed. + /// + int MaxEngines { get; set; } + + /// + /// Required number of crew quarters components. + /// + int MinCrewQuarters { get; set; } + + /// + /// Required number of life support components. + /// + int MinLifeSupport { get; set; } + + /// + /// Minimum percentage of space required to be used for cargo-storage components. + /// + int MinPercentCargoBays { get; set; } + + /// + /// Minimum percentage of space required to be used for colonizing components. + /// + int MinPercentColonyModules { get; set; } + + /// + /// Minimum percentage of space required to be used for fighter-launching components. + /// + int MinPercentFighterBays { get; set; } + + new string Name { get; set; } + + /// + /// Does this hull need a component with the Ship Bridge ability? + /// + bool NeedsBridge { get; set; } + + IList PictureNames { get; } + + string ShortName { get; set; } + + int Size { get; set; } + + /// + /// Thrust points required to move one sector per turn. + /// SE4 called this "engines per move" but this was a misnomer because engines were allowed to produce more than one thrust point. + /// + int ThrustPerMove { get; set; } + + /// + /// The vehicle type of this hull. + /// + VehicleTypes VehicleType { get; } + + string VehicleTypeName { get; } + + /// + /// Can this hull use a mount? + /// + /// + /// + bool CanUseMount(Mount m); + + Image GetIcon(string shipsetPath); + + Image GetPortrait(string shipsetPath); +} + +/// +/// A vehicle hull. +/// +public interface IHull : IHull, IUpgradeable> where T : IVehicle +{ + new bool IsObsolescent { get; } + + // to deal with ambiguity between implementing IHull and IHull + new bool IsObsolete { get; } + + new IHull LatestVersion { get; } + new IEnumerable> NewerVersions { get; } + new IEnumerable> OlderVersions { get; } +} \ No newline at end of file diff --git a/FrEee/Interfaces/IResearchable.cs b/FrEee/Objects/Technology/IResearchable.cs similarity index 71% rename from FrEee/Interfaces/IResearchable.cs rename to FrEee/Objects/Technology/IResearchable.cs index 8df6f29c2..8dbed9e9b 100644 --- a/FrEee/Interfaces/IResearchable.cs +++ b/FrEee/Objects/Technology/IResearchable.cs @@ -1,4 +1,6 @@ -namespace FrEee.Interfaces; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Technology; /// /// Something that can be unlocked in-game, as opposed to only in empire setup. diff --git a/FrEee/Objects/Technology/IUpgradeable.cs b/FrEee/Objects/Technology/IUpgradeable.cs new file mode 100644 index 000000000..be65d7793 --- /dev/null +++ b/FrEee/Objects/Technology/IUpgradeable.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; + +namespace FrEee.Objects.Technology; + +/// +/// Something which can be upgraded to a newer version. +/// +/// +public interface IUpgradeable +{ + /// + /// Can this item be upgraded? + /// + bool IsObsolescent { get; } + + /// + /// Is this item obsolete? + /// + bool IsObsolete { get; } + + /// + /// The latest available version of this item. + /// + T LatestVersion { get; } + + /// + /// Any newer versions of this item. + /// + IEnumerable NewerVersions { get; } + + /// + /// Any older versions of this item. + /// + IEnumerable OlderVersions { get; } +} \ No newline at end of file diff --git a/FrEee/Objects/Technology/Mount.cs b/FrEee/Objects/Technology/Mount.cs index 1a9f56087..7f227266b 100644 --- a/FrEee/Objects/Technology/Mount.cs +++ b/FrEee/Objects/Technology/Mount.cs @@ -1,14 +1,14 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Abilities; +using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Modding; -using FrEee.Modding.Interfaces; using FrEee.Utility; using FrEee.Extensions; using System.Collections.Generic; using System.Drawing; using System.IO; +using FrEee.Objects.GameState; +using FrEee.Objects.Vehicles; +using FrEee.Objects.Combat; namespace FrEee.Objects.Technology; diff --git a/FrEee/Objects/Technology/MountedComponentTemplate.cs b/FrEee/Objects/Technology/MountedComponentTemplate.cs index 13f3d68d3..0008ecd1b 100644 --- a/FrEee/Objects/Technology/MountedComponentTemplate.cs +++ b/FrEee/Objects/Technology/MountedComponentTemplate.cs @@ -1,19 +1,17 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Objects.Combat; -using FrEee.Objects.Space; using FrEee.Objects.Vehicles; using FrEee.Modding; -using FrEee.Modding.Interfaces; using FrEee.Modding.Templates; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Objects.Technology; diff --git a/FrEee/Objects/Technology/MountedComponentTemplateContainerPopulator.cs b/FrEee/Objects/Technology/MountedComponentTemplateContainerPopulator.cs index 06a2d8a42..00b781501 100644 --- a/FrEee/Objects/Technology/MountedComponentTemplateContainerPopulator.cs +++ b/FrEee/Objects/Technology/MountedComponentTemplateContainerPopulator.cs @@ -1,6 +1,6 @@ using System.Linq; -using FrEee.Interfaces; -using FrEee.Objects.Space; +using FrEee.Objects.GameState; +using FrEee.Objects.Vehicles; using FrEee.Utility; namespace FrEee.Objects.Technology; diff --git a/FrEee/Objects/Technology/SeekingWeaponInfo.cs b/FrEee/Objects/Technology/SeekingWeaponInfo.cs index 015e472e4..512e717fd 100644 --- a/FrEee/Objects/Technology/SeekingWeaponInfo.cs +++ b/FrEee/Objects/Technology/SeekingWeaponInfo.cs @@ -1,4 +1,5 @@ using FrEee.Modding; +using FrEee.Objects.Combat; using System; namespace FrEee.Objects.Technology; @@ -16,14 +17,14 @@ public class SeekingWeaponInfo : WeaponInfo /// public Formula SeekerSpeed { get; set; } - public override Enumerations.WeaponTypes WeaponType + public override WeaponTypes WeaponType { get { if (IsPointDefense) - return Enumerations.WeaponTypes.SeekingPointDefense; + return WeaponTypes.SeekingPointDefense; else - return Enumerations.WeaponTypes.Seeking; + return WeaponTypes.Seeking; } } } \ No newline at end of file diff --git a/FrEee/Objects/Technology/Technology.cs b/FrEee/Objects/Technology/Technology.cs index 52f90d390..000f93957 100644 --- a/FrEee/Objects/Technology/Technology.cs +++ b/FrEee/Objects/Technology/Technology.cs @@ -1,15 +1,12 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; -using FrEee.Objects.Space; using FrEee.Modding; -using FrEee.Modding.Interfaces; using FrEee.Utility; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Objects.Technology; diff --git a/FrEee/Objects/Technology/TechnologyCost.cs b/FrEee/Objects/Technology/TechnologyCost.cs new file mode 100644 index 000000000..7414966db --- /dev/null +++ b/FrEee/Objects/Technology/TechnologyCost.cs @@ -0,0 +1,25 @@ +namespace FrEee.Objects.Technology; + +/// +/// Cost to research technology. +/// +public enum TechnologyCost +{ + /// + /// Cost = Level * BaseCost + /// 1x, 2x, 3x, 4x... + /// + Low = 0, + + /// + /// Cost = Level for level 1, Level ^ 2 * BaseCost / 2 for subsequent levels + /// 1x, 2x, 4.5x, 8x... + /// + Medium = 1, + + /// + /// Cost = Level ^ 2 * BaseCost + /// 1x, 4x, 9x, 16x... + /// + High = 2, +} \ No newline at end of file diff --git a/FrEee/Objects/Technology/TechnologyRequirement.cs b/FrEee/Objects/Technology/TechnologyRequirement.cs index 5f1ce7c54..863b7ba94 100644 --- a/FrEee/Objects/Technology/TechnologyRequirement.cs +++ b/FrEee/Objects/Technology/TechnologyRequirement.cs @@ -1,8 +1,8 @@ -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Modding; using FrEee.Serialization; using System; +using FrEee.Objects.GameState; namespace FrEee.Objects.Technology; diff --git a/FrEee/Objects/Technology/WarheadWeaponInfo.cs b/FrEee/Objects/Technology/WarheadWeaponInfo.cs index ff63735a9..a66ef90ee 100644 --- a/FrEee/Objects/Technology/WarheadWeaponInfo.cs +++ b/FrEee/Objects/Technology/WarheadWeaponInfo.cs @@ -1,18 +1,19 @@ using System; +using FrEee.Objects.Combat; namespace FrEee.Objects.Technology; [Serializable] public class WarheadWeaponInfo : WeaponInfo { - public override Enumerations.WeaponTypes WeaponType + public override WeaponTypes WeaponType { get { if (IsPointDefense) - return Enumerations.WeaponTypes.WarheadPointDefense; + return WeaponTypes.WarheadPointDefense; else - return Enumerations.WeaponTypes.Warhead; + return WeaponTypes.Warhead; } } } \ No newline at end of file diff --git a/FrEee/Objects/Technology/WeaponInfo.cs b/FrEee/Objects/Technology/WeaponInfo.cs index f7e12247f..4d941ef64 100644 --- a/FrEee/Objects/Technology/WeaponInfo.cs +++ b/FrEee/Objects/Technology/WeaponInfo.cs @@ -1,5 +1,4 @@ -using FrEee.Enumerations; -using FrEee.Objects.Combat; +using FrEee.Objects.Combat; using FrEee.Modding; using System; diff --git a/FrEee/Objects/Vehicles/Base.cs b/FrEee/Objects/Vehicles/Base.cs index d233ca217..4d308c852 100644 --- a/FrEee/Objects/Vehicles/Base.cs +++ b/FrEee/Objects/Vehicles/Base.cs @@ -1,5 +1,5 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; +using FrEee.Objects.Abilities; +using FrEee.Objects.Space; using System; namespace FrEee.Objects.Vehicles; diff --git a/FrEee/Objects/Vehicles/Design.cs b/FrEee/Objects/Vehicles/Design.cs index 31a780485..7f6373b3a 100644 --- a/FrEee/Objects/Vehicles/Design.cs +++ b/FrEee/Objects/Vehicles/Design.cs @@ -1,20 +1,21 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Objects.Commands; -using FrEee.Objects.Orders; -using FrEee.Objects.Space; using FrEee.Objects.Technology; using FrEee.Modding; using FrEee.Modding.Templates; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.GameState; namespace FrEee.Objects.Vehicles; diff --git a/FrEee/Objects/Vehicles/Drone.cs b/FrEee/Objects/Vehicles/Drone.cs index b04534340..6e963470f 100644 --- a/FrEee/Objects/Vehicles/Drone.cs +++ b/FrEee/Objects/Vehicles/Drone.cs @@ -1,8 +1,11 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Extensions; using System; +using FrEee.Objects.Space; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.GameState; +using FrEee.Objects.Abilities; +using FrEee.Objects.Combat; namespace FrEee.Objects.Vehicles; @@ -39,9 +42,9 @@ public override bool RequiresSpaceYardQueue get { return false; } } - public override Enumerations.WeaponTargets WeaponTargetType + public override WeaponTargets WeaponTargetType { - get { return Enumerations.WeaponTargets.Drone; } + get { return WeaponTargets.Drone; } } public override Visibility CheckVisibility(Empire emp) diff --git a/FrEee/Objects/Vehicles/Fighter.cs b/FrEee/Objects/Vehicles/Fighter.cs index 3ded53e59..811a0686e 100644 --- a/FrEee/Objects/Vehicles/Fighter.cs +++ b/FrEee/Objects/Vehicles/Fighter.cs @@ -1,9 +1,12 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Serialization; using FrEee.Extensions; using System; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.Space; +using FrEee.Objects.GameState; +using FrEee.Objects.Abilities; +using FrEee.Objects.Combat; namespace FrEee.Objects.Vehicles; @@ -40,9 +43,9 @@ public override bool RequiresSpaceYardQueue get { return false; } } - public override Enumerations.WeaponTargets WeaponTargetType + public override WeaponTargets WeaponTargetType { - get { return Enumerations.WeaponTargets.Fighter; } + get { return WeaponTargets.Fighter; } } public override Visibility CheckVisibility(Empire emp) diff --git a/FrEee/Objects/Vehicles/IDesign.cs b/FrEee/Objects/Vehicles/IDesign.cs new file mode 100644 index 000000000..cc5c71611 --- /dev/null +++ b/FrEee/Objects/Vehicles/IDesign.cs @@ -0,0 +1,153 @@ +using FrEee.Objects.Civilization; +using FrEee.Objects.Technology; +using FrEee.Modding.Templates; +using FrEee.Utility; +using System.Collections.Generic; +using FrEee.Objects.Abilities; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.Commands; +using FrEee.Serialization; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Vehicles; + +/// +/// A vehicle design. +/// +public interface IDesign : INamed, IPictorial, IOwnableAbilityObject, IConstructionTemplate, IPromotable, IFoggable, IUpgradeable, ICleanable +{ + int Accuracy { get; } + + int ArmorHitpoints { get; } + + /// + /// The base name of the design, without the iteration number. + /// + string BaseName { get; set; } + + int CargoCapacity { get; } + + int CargoStorage { get; } + + /// + /// The movement speed of the design in combat. + /// + double CombatSpeed { get; } + + /// + /// The vehicle's components. + /// + IList Components { get; } + + int Evasion { get; } + + /// + /// The vehicle's hull. + /// + IHull Hull { get; set; } + + int HullHitpoints { get; } + + /// + /// Is this a newly created design on the client side that needs to be sent to the server? + /// + bool IsNew { get; set; } + + /// + /// Is this design obsolete? + /// Note that foreign designs will never be obsoleted, since you don't know when their owner obsoleted them. + /// + new bool IsObsolete { get; set; } + + /// + /// Is this design valid in the current mod? Or is it using techs from other mods? + /// + bool IsValidInMod { get; } + + int Iteration { get; set; } + + ResourceQuantity MaintenanceCost { get; } + + /// + /// The name of the design. + /// + new string Name { get; } + + /// + /// The empire which created this design. + /// + new Empire Owner { get; set; } + + /// + /// The ship's role (design type in SE4). + /// + string Role { get; set; } + + int ShieldHitpoints { get; } + + int ShieldRegeneration { get; } + + /// + /// Unused space on the design. + /// + int SpaceFree { get; } + + /// + /// The movement speed of the design, in sectors per turn. + /// + int StrategicSpeed { get; } + + int SupplyStorage { get; } + + /// + /// Supply used for each sector of movement. + /// + int SupplyUsagePerSector { get; } + + /// + /// The turn this design was created (for our designs) or discovered (for alien designs). + /// + int TurnNumber { get; set; } + + int VehiclesBuilt { get; set; } + + /// + /// The vehicle type. + /// + VehicleTypes VehicleType { get; } + + /// + /// The name of the vehicle type. + /// + string VehicleTypeName { get; } + + /// + /// Warnings that need to be resolved before the design can be saved. + /// + IEnumerable Warnings { get; } + + void AddComponent(ComponentTemplate ct, Mount m = null); + + /// + /// Creates an order to build this design. + /// + /// + IConstructionOrder CreateConstructionOrder(ConstructionQueue queue); + + /// + /// Creates a command to create this design on the server. + /// + /// + ICreateDesignCommand CreateCreationCommand(); + + IVehicle Instantiate(); + + IDesign Upgrade(); +} + +public interface IDesign : IDesign, IPictorial, IReferrable, IUpgradeable> where T : IVehicle +{ + new T Instantiate(); + + new IDesign Upgrade(); +} diff --git a/FrEee/Interfaces/IUnit.cs b/FrEee/Objects/Vehicles/IUnit.cs similarity index 55% rename from FrEee/Interfaces/IUnit.cs rename to FrEee/Objects/Vehicles/IUnit.cs index d5d37258e..b0ddb4496 100644 --- a/FrEee/Interfaces/IUnit.cs +++ b/FrEee/Objects/Vehicles/IUnit.cs @@ -1,4 +1,7 @@ -namespace FrEee.Interfaces; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.GameState; + +namespace FrEee.Objects.Vehicles; /// /// A vehicle which can be contained in cargo. diff --git a/FrEee/Objects/Vehicles/IVehicle.cs b/FrEee/Objects/Vehicles/IVehicle.cs new file mode 100644 index 000000000..c7f709b86 --- /dev/null +++ b/FrEee/Objects/Vehicles/IVehicle.cs @@ -0,0 +1,36 @@ +using FrEee.Objects.Abilities; +using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.Combat; +using FrEee.Objects.Technology; +using FrEee.Serialization; +using FrEee.Utility; +using System.Collections.Generic; + +namespace FrEee.Objects.Vehicles; + +/// +/// A space or ground vehicle. +/// +public interface IVehicle : IConstructable, IOwnableAbilityObject, IReferrable, IDamageable, ICombatant, IRecyclable, IIncomeProducer, IUpgradeable, INameable +{ + [DoNotSerialize(false)] + new IList Components { get; } + + /// + /// Damage that has been applied to this vehicle's components. + /// + SafeDictionary> Damage { get; set; } + + /// + /// The design of this vehicle. + /// + [DoNotCopy] + IDesign Design { get; set; } + + /// + /// Cost to maintain this vehicle per turn. + /// + ResourceQuantity MaintenanceCost { get; } +} diff --git a/FrEee/Objects/Vehicles/MajorSpaceVehicle.cs b/FrEee/Objects/Vehicles/MajorSpaceVehicle.cs index 45c23f4eb..1c0739516 100644 --- a/FrEee/Objects/Vehicles/MajorSpaceVehicle.cs +++ b/FrEee/Objects/Vehicles/MajorSpaceVehicle.cs @@ -1,12 +1,14 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Modding; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.GameState; +using FrEee.Objects.Combat; namespace FrEee.Objects.Vehicles; diff --git a/FrEee/Objects/Vehicles/Mine.cs b/FrEee/Objects/Vehicles/Mine.cs index ea1923baf..1e2b7eb07 100644 --- a/FrEee/Objects/Vehicles/Mine.cs +++ b/FrEee/Objects/Vehicles/Mine.cs @@ -1,7 +1,10 @@ -using FrEee.Enumerations; using FrEee.Extensions; -using FrEee.Interfaces; +using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.Combat; +using FrEee.Objects.GameState; +using FrEee.Objects.Space; using System; namespace FrEee.Objects.Vehicles; @@ -39,10 +42,10 @@ public override bool RequiresSpaceYardQueue get { return false; } } - public override Enumerations.WeaponTargets WeaponTargetType + public override WeaponTargets WeaponTargetType { // mines cannot be targeted in space combat - get { return Enumerations.WeaponTargets.Invalid; } + get { return WeaponTargets.Invalid; } } /// diff --git a/FrEee/Objects/Vehicles/Satellite.cs b/FrEee/Objects/Vehicles/Satellite.cs index cd88b92ce..641d579a9 100644 --- a/FrEee/Objects/Vehicles/Satellite.cs +++ b/FrEee/Objects/Vehicles/Satellite.cs @@ -1,8 +1,11 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Extensions; using System; +using FrEee.Objects.Space; +using FrEee.Objects.GameState; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.Abilities; +using FrEee.Objects.Combat; namespace FrEee.Objects.Vehicles; @@ -39,9 +42,9 @@ public override bool RequiresSpaceYardQueue get { return false; } } - public override Enumerations.WeaponTargets WeaponTargetType + public override WeaponTargets WeaponTargetType { - get { return Enumerations.WeaponTargets.Satellite; } + get { return WeaponTargets.Satellite; } } public override Visibility CheckVisibility(Empire emp) diff --git a/FrEee/Objects/Vehicles/Ship.cs b/FrEee/Objects/Vehicles/Ship.cs index 88c577760..099a902a5 100644 --- a/FrEee/Objects/Vehicles/Ship.cs +++ b/FrEee/Objects/Vehicles/Ship.cs @@ -1,4 +1,5 @@ -using FrEee.Enumerations; +using FrEee.Objects.Abilities; +using FrEee.Objects.Combat; using System; namespace FrEee.Objects.Vehicles; diff --git a/FrEee/Objects/Vehicles/SpaceVehicle.cs b/FrEee/Objects/Vehicles/SpaceVehicle.cs index 818aef961..88bead052 100644 --- a/FrEee/Objects/Vehicles/SpaceVehicle.cs +++ b/FrEee/Objects/Vehicles/SpaceVehicle.cs @@ -1,13 +1,15 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Modding; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.Abilities; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.GameState; namespace FrEee.Objects.Vehicles; diff --git a/FrEee/Objects/Vehicles/Troop.cs b/FrEee/Objects/Vehicles/Troop.cs index b057f9400..0f61ae59a 100644 --- a/FrEee/Objects/Vehicles/Troop.cs +++ b/FrEee/Objects/Vehicles/Troop.cs @@ -1,12 +1,14 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.Abilities; +using FrEee.Objects.GameState; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.Combat; namespace FrEee.Objects.Vehicles; @@ -97,16 +99,15 @@ public override bool IsObsoleteMemory(Empire emp) public override void Place(ISpaceObject target) { - if (target is ICargoContainer) + if (target is ICargoContainer cc1) { - var cc = (ICargoContainer)target; - if (cc.AddUnit(this)) + if (cc1.AddUnit(this)) return; } // cargo was full? then try other space objects - foreach (var cc in target.Sector.SpaceObjects.Where(sobj => sobj.Owner == target.Owner).OfType()) + foreach (var cc2 in target.Sector.SpaceObjects.Where(sobj => sobj.Owner == target.Owner).OfType()) { - if (cc.AddUnit(this)) + if (cc2.AddUnit(this)) return; } target.Owner.Log.Add(this.CreateLogMessage(this + " could not be placed in cargo at " + target + " because there is not enough cargo space available.", LogMessages.LogMessageType.Generic)); diff --git a/FrEee/Objects/Vehicles/Vehicle.cs b/FrEee/Objects/Vehicles/Vehicle.cs index d1a07ef28..5762f50f6 100644 --- a/FrEee/Objects/Vehicles/Vehicle.cs +++ b/FrEee/Objects/Vehicles/Vehicle.cs @@ -2,16 +2,19 @@ using System.Collections.Generic; using System.Drawing; using System.Linq; -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Abilities; using FrEee.Objects.Civilization; using FrEee.Objects.Combat; using FrEee.Objects.Space; using FrEee.Objects.Technology; using FrEee.Modding; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.GameState; +using FrEee.Objects.Civilization.CargoStorage; namespace FrEee.Objects.Vehicles; diff --git a/FrEee/Objects/Vehicles/VehicleTypes.cs b/FrEee/Objects/Vehicles/VehicleTypes.cs new file mode 100644 index 000000000..a49b0cd0c --- /dev/null +++ b/FrEee/Objects/Vehicles/VehicleTypes.cs @@ -0,0 +1,51 @@ +using System; +using FrEee.Utility; +namespace FrEee.Objects.Vehicles; + +/// +/// Vehicle types. Used for restricting component placement. +/// +[Flags] +public enum VehicleTypes +{ + None = 0x0, + + [Name("Ships")] + Ship = 0x1, + + [Name("Bases")] + Base = 0x2, + + [Name("Fighters")] + [Name("Ftr")] + Fighter = 0x4, + + [Name("Satellites")] + [Name("Sat")] + Satellite = 0x8, + + [Name("Mines")] + Mine = 0x10, + + [Name("Troops")] + [Name("Troop")] + [Name("Trp")] + Troop = 0x20, + + [Name("Drones")] + Drone = 0x40, + + [Name("WeapPlatform")] + [Name("WeapPlatforms")] + [Name("WeapPlat")] + [Name("WeapPlats")] + [Name("WeaponPlatforms")] + [CanonicalName("Weapon Platform")] + [Name("Weapon Platforms")] + WeaponPlatform = 0x80, + + [Name("Any")] + All = Ship | Base | Fighter | Satellite | Mine | Troop | Drone | WeaponPlatform, + + Invalid = 0x1000, +} \ No newline at end of file diff --git a/FrEee/Objects/Vehicles/WeaponPlatform.cs b/FrEee/Objects/Vehicles/WeaponPlatform.cs index f6d4b2c70..090039e55 100644 --- a/FrEee/Objects/Vehicles/WeaponPlatform.cs +++ b/FrEee/Objects/Vehicles/WeaponPlatform.cs @@ -1,11 +1,13 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Serialization; using FrEee.Extensions; using System; using System.Collections.Generic; +using FrEee.Objects.Abilities; +using FrEee.Objects.GameState; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.Combat; namespace FrEee.Objects.Vehicles; @@ -67,10 +69,10 @@ public override StarSystem StarSystem get { return Container?.StarSystem; } } - public override Enumerations.WeaponTargets WeaponTargetType + public override WeaponTargets WeaponTargetType { // weapon platforms cannot be targeted in space combat - get { return Enumerations.WeaponTargets.Invalid; } + get { return WeaponTargets.Invalid; } } public override Visibility CheckVisibility(Empire emp) diff --git a/FrEee/Objects/VictoryConditions/IVictoryCondition.cs b/FrEee/Objects/VictoryConditions/IVictoryCondition.cs new file mode 100644 index 000000000..87c1bcc9f --- /dev/null +++ b/FrEee/Objects/VictoryConditions/IVictoryCondition.cs @@ -0,0 +1,34 @@ +using FrEee.Objects.Civilization; +using System.Collections.Generic; + +namespace FrEee.Objects.VictoryConditions; + +/// +/// A victory condition for the game. +/// +public interface IVictoryCondition +{ + /// + /// Gets a defeat message for an empire. + /// + /// + /// + /// + string GetDefeatMessage(Empire emp, IEnumerable winners); + + /// + /// Gets the progress of an empire toward this victory condition. + /// 0 represents no progress; 1 represents completion. + /// Progress can go over 1 if the empire exceeds the requirements. + /// + /// + /// + double GetProgress(Empire emp); + + /// + /// Gets a victory message for an empire. + /// + /// + /// + string GetVictoryMessage(Empire emp); +} \ No newline at end of file diff --git a/FrEee/Objects/VictoryConditions/MajorEmpireEliminationVictoryCondition.cs b/FrEee/Objects/VictoryConditions/MajorEmpireEliminationVictoryCondition.cs index cc015e278..d185f350c 100644 --- a/FrEee/Objects/VictoryConditions/MajorEmpireEliminationVictoryCondition.cs +++ b/FrEee/Objects/VictoryConditions/MajorEmpireEliminationVictoryCondition.cs @@ -1,6 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.Space; +using FrEee.Objects.Civilization; +using FrEee.Objects.GameState; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Objects/VictoryConditions/PeaceVictoryCondition.cs b/FrEee/Objects/VictoryConditions/PeaceVictoryCondition.cs index 9697e07b3..f02eed2b9 100644 --- a/FrEee/Objects/VictoryConditions/PeaceVictoryCondition.cs +++ b/FrEee/Objects/VictoryConditions/PeaceVictoryCondition.cs @@ -1,6 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.Space; +using FrEee.Objects.Civilization; +using FrEee.Objects.GameState; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Objects/VictoryConditions/ScorePercentageVictoryCondition.cs b/FrEee/Objects/VictoryConditions/ScorePercentageVictoryCondition.cs index 8e6866274..fd3511013 100644 --- a/FrEee/Objects/VictoryConditions/ScorePercentageVictoryCondition.cs +++ b/FrEee/Objects/VictoryConditions/ScorePercentageVictoryCondition.cs @@ -1,6 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.Space; +using FrEee.Objects.Civilization; +using FrEee.Objects.GameState; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Objects/VictoryConditions/ScoreVictoryCondition.cs b/FrEee/Objects/VictoryConditions/ScoreVictoryCondition.cs index 2e3ab1bee..ffd6386ad 100644 --- a/FrEee/Objects/VictoryConditions/ScoreVictoryCondition.cs +++ b/FrEee/Objects/VictoryConditions/ScoreVictoryCondition.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Objects/VictoryConditions/SurvivalVictoryCondition.cs b/FrEee/Objects/VictoryConditions/SurvivalVictoryCondition.cs index c0c249555..3de3e0a5f 100644 --- a/FrEee/Objects/VictoryConditions/SurvivalVictoryCondition.cs +++ b/FrEee/Objects/VictoryConditions/SurvivalVictoryCondition.cs @@ -1,6 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.Space; +using FrEee.Objects.Civilization; +using FrEee.Objects.GameState; using System.Collections.Generic; namespace FrEee.Objects.VictoryConditions; diff --git a/FrEee/Objects/VictoryConditions/TechnologyVictoryCondition.cs b/FrEee/Objects/VictoryConditions/TechnologyVictoryCondition.cs index 538f84d04..256a0e2de 100644 --- a/FrEee/Objects/VictoryConditions/TechnologyVictoryCondition.cs +++ b/FrEee/Objects/VictoryConditions/TechnologyVictoryCondition.cs @@ -1,5 +1,4 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Modding; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Objects/VictoryConditions/TotalEliminationVictoryCondition.cs b/FrEee/Objects/VictoryConditions/TotalEliminationVictoryCondition.cs index 69f211735..5ffc01b29 100644 --- a/FrEee/Objects/VictoryConditions/TotalEliminationVictoryCondition.cs +++ b/FrEee/Objects/VictoryConditions/TotalEliminationVictoryCondition.cs @@ -1,6 +1,5 @@ -using FrEee.Interfaces; -using FrEee.Objects.Civilization; -using FrEee.Objects.Space; +using FrEee.Objects.Civilization; +using FrEee.Objects.GameState; using System.Collections.Generic; using System.Linq; diff --git a/FrEee/Processes/TurnProcessor.cs b/FrEee/Processes/TurnProcessor.cs index 887c1ef52..c07c26f60 100644 --- a/FrEee/Processes/TurnProcessor.cs +++ b/FrEee/Processes/TurnProcessor.cs @@ -1,7 +1,6 @@ using System; using System.Collections.Generic; using System.Linq; -using FrEee.Interfaces; using FrEee.Objects.Civilization.Diplomacy.Clauses; using FrEee.Objects.Civilization; using FrEee.Objects.Combat.Grid; @@ -13,7 +12,11 @@ using FrEee.Modding; using FrEee.Extensions; using FrEee.Utility; -using FrEee.Enumerations; +using FrEee.Objects.Commands; +using FrEee.Objects.Civilization.Construction; +using FrEee.Objects.Civilization.Orders; +using FrEee.Objects.Civilization.CargoStorage; +using FrEee.Objects.GameState; namespace FrEee.Processes; diff --git a/FrEee/Serialization/DataReference.cs b/FrEee/Serialization/DataReference.cs index 123ae71d3..bb5bebd8b 100644 --- a/FrEee/Serialization/DataReference.cs +++ b/FrEee/Serialization/DataReference.cs @@ -1,8 +1,8 @@ -using FrEee.Interfaces; using FrEee.Extensions; using Newtonsoft.Json; using System; using System.Collections.Generic; +using FrEee.Objects.GameState; namespace FrEee.Serialization; diff --git a/FrEee/Serialization/GalaxyReference.cs b/FrEee/Serialization/GalaxyReference.cs index 49d23a50f..0509920c5 100644 --- a/FrEee/Serialization/GalaxyReference.cs +++ b/FrEee/Serialization/GalaxyReference.cs @@ -1,9 +1,8 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; using FrEee.Extensions; using System; using System.Collections.Generic; using FrEee.Utility; +using FrEee.Objects.GameState; namespace FrEee.Serialization; /// diff --git a/FrEee/Interfaces/IReference.cs b/FrEee/Serialization/IReference.cs similarity index 70% rename from FrEee/Interfaces/IReference.cs rename to FrEee/Serialization/IReference.cs index 958fcdcb7..45399e121 100644 --- a/FrEee/Interfaces/IReference.cs +++ b/FrEee/Serialization/IReference.cs @@ -1,7 +1,8 @@ using System; using System.Runtime.Serialization; +using FrEee.Objects.GameState; -namespace FrEee.Interfaces; +namespace FrEee.Serialization; /// /// A lightweight reference to some object in some context (e.g. the current mod or galaxy). @@ -11,8 +12,8 @@ namespace FrEee.Interfaces; /// public interface IReference : IPromotable { - bool HasValue { get; } - TValue Value { get; } + bool HasValue { get; } + TValue Value { get; } } /// @@ -23,19 +24,19 @@ public interface IReference : IPromotable /// public interface IReference : IReference { - TID ID { get; } + TID ID { get; } } [Serializable] public class ReferenceException : Exception, ISerializable { - public ReferenceException(string message, TID id = default) - : base(message) - { - ID = id; - } + public ReferenceException(string message, TID id = default) + : base(message) + { + ID = id; + } - public TID ID { get; private set; } + public TID ID { get; private set; } - public Type Type { get { return typeof(TValue); } } + public Type Type { get { return typeof(TValue); } } } \ No newline at end of file diff --git a/FrEee/Interfaces/IReferenceEnumerable.cs b/FrEee/Serialization/IReferenceEnumerable.cs similarity index 84% rename from FrEee/Interfaces/IReferenceEnumerable.cs rename to FrEee/Serialization/IReferenceEnumerable.cs index 7b501ee36..b613c64a4 100644 --- a/FrEee/Interfaces/IReferenceEnumerable.cs +++ b/FrEee/Serialization/IReferenceEnumerable.cs @@ -1,4 +1,4 @@ -namespace FrEee.Interfaces; +namespace FrEee.Serialization; /// /// Flag interface for enumerables that should not be serialized as enumerables because they contain references. diff --git a/FrEee/Interfaces/IReferrable.cs b/FrEee/Serialization/IReferrable.cs similarity index 58% rename from FrEee/Interfaces/IReferrable.cs rename to FrEee/Serialization/IReferrable.cs index 7c450b235..5bdf88623 100644 --- a/FrEee/Interfaces/IReferrable.cs +++ b/FrEee/Serialization/IReferrable.cs @@ -1,13 +1,14 @@ using System; +using FrEee.Objects.Civilization; -namespace FrEee.Interfaces; +namespace FrEee.Serialization; /// /// Something that can be referred to from the client side using an ID. /// public interface IReferrable : IDisposable, IOwnable { - long ID { get; set; } + long ID { get; set; } - bool IsDisposed { get; set; } + bool IsDisposed { get; set; } } \ No newline at end of file diff --git a/FrEee/Serialization/LegacySerializer.cs b/FrEee/Serialization/LegacySerializer.cs index 5e4cba6f5..3cdc923c6 100644 --- a/FrEee/Serialization/LegacySerializer.cs +++ b/FrEee/Serialization/LegacySerializer.cs @@ -1,4 +1,3 @@ -using FrEee.Interfaces; using FrEee.Extensions; using FrEee.Serialization.Stringifiers; using System; diff --git a/FrEee/Serialization/ReferenceKeyedDictionary.cs b/FrEee/Serialization/ReferenceKeyedDictionary.cs index fb23520d3..36ddd0ef1 100644 --- a/FrEee/Serialization/ReferenceKeyedDictionary.cs +++ b/FrEee/Serialization/ReferenceKeyedDictionary.cs @@ -1,12 +1,10 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Modding; -using FrEee.Modding.Interfaces; +using FrEee.Modding; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Linq; using FrEee.Utility; +using FrEee.Objects.GameState; namespace FrEee.Serialization; diff --git a/FrEee/Serialization/ReferenceList.cs b/FrEee/Serialization/ReferenceList.cs index dd838a69a..a0d5244a4 100644 --- a/FrEee/Serialization/ReferenceList.cs +++ b/FrEee/Serialization/ReferenceList.cs @@ -1,9 +1,8 @@ using System.Collections.Generic; using System.Linq; -using FrEee.Interfaces; using FrEee.Modding; -using FrEee.Modding.Interfaces; using FrEee.Extensions; +using FrEee.Objects.GameState; namespace FrEee.Serialization; diff --git a/FrEee/Serialization/ReferenceSet.cs b/FrEee/Serialization/ReferenceSet.cs index 80f643f55..69eafdc2e 100644 --- a/FrEee/Serialization/ReferenceSet.cs +++ b/FrEee/Serialization/ReferenceSet.cs @@ -1,10 +1,9 @@ -using FrEee.Interfaces; -using FrEee.Modding; -using FrEee.Modding.Interfaces; +using FrEee.Modding; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Serialization; diff --git a/FrEee/Setup/EmpirePlacement.cs b/FrEee/Setup/EmpirePlacement.cs new file mode 100644 index 000000000..726ee01e0 --- /dev/null +++ b/FrEee/Setup/EmpirePlacement.cs @@ -0,0 +1,13 @@ +using FrEee.Utility; +namespace FrEee.Setup; + +public enum EmpirePlacement +{ + [CanonicalName("Can Start In Same System")] + CanStartInSameSystem, + + [CanonicalName("Different Systems")] + DifferentSystems, + + Equidistant +} \ No newline at end of file diff --git a/FrEee/Setup/EmpireTemplate.cs b/FrEee/Setup/EmpireTemplate.cs index 042b77536..d61c6a8ca 100644 --- a/FrEee/Setup/EmpireTemplate.cs +++ b/FrEee/Setup/EmpireTemplate.cs @@ -1,12 +1,13 @@ -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Modding; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; using System.Collections.Generic; using System.Drawing; using System.IO; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Setup; diff --git a/FrEee/Setup/GameSetup.cs b/FrEee/Setup/GameSetup.cs index f8131b382..8aaa1c528 100644 --- a/FrEee/Setup/GameSetup.cs +++ b/FrEee/Setup/GameSetup.cs @@ -3,8 +3,6 @@ using System.Drawing; using System.IO; using System.Linq; -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Objects.Technology; @@ -12,8 +10,11 @@ using FrEee.Setup.WarpPointPlacementStrategies; using FrEee.Modding; using FrEee.Modding.Templates; -using FrEee.Utility; using FrEee.Serialization; +using FrEee.Utility; +using FrEee.Serialization; using FrEee.Extensions; +using FrEee.Objects.GameState; +using FrEee.Objects.Civilization.Diplomacy; namespace FrEee.Setup; @@ -611,7 +612,7 @@ private void PlaceEmpire(Galaxy gal, Empire emp, PRNG dice) hw.Colony = new Colony { Owner = emp, - ConstructionQueue = new ConstructionQueue(hw), + ConstructionQueue = new(hw), IsHomeworld = true, }; hw.AddPopulation(emp.PrimaryRace, hw.Size.MaxPopulation); diff --git a/FrEee/Setup/ScoreDisplay.cs b/FrEee/Setup/ScoreDisplay.cs new file mode 100644 index 000000000..a3a12fe2b --- /dev/null +++ b/FrEee/Setup/ScoreDisplay.cs @@ -0,0 +1,22 @@ +using FrEee.Utility; +using System; + +namespace FrEee.Setup; + +[Flags] +public enum ScoreDisplay +{ + [CanonicalName("Own Only - No Rankings")] + OwnOnlyNoRankings = 0, + + [CanonicalName("Own Only - Ranked")] + OwnOnlyRanked = 1, + + [CanonicalName("Allies Only - No Rankings")] + AlliesOnlyNoRankings = 2, + + [CanonicalName("Allies Only - Ranked")] + AlliesOnlyRanked = 3, + + All = 7 +} \ No newline at end of file diff --git a/FrEee/Setup/StarSystemPlacementStrategies/ClusteredStarSystemPlacementStrategy.cs b/FrEee/Setup/StarSystemPlacementStrategies/ClusteredStarSystemPlacementStrategy.cs index 3ba463d9e..ba425af30 100644 --- a/FrEee/Setup/StarSystemPlacementStrategies/ClusteredStarSystemPlacementStrategy.cs +++ b/FrEee/Setup/StarSystemPlacementStrategies/ClusteredStarSystemPlacementStrategy.cs @@ -1,11 +1,10 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Utility; +using FrEee.Utility; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Setup.StarSystemPlacementStrategies; diff --git a/FrEee/Setup/StarSystemPlacementStrategies/DiffuseStarSystemPlacementStrategy.cs b/FrEee/Setup/StarSystemPlacementStrategies/DiffuseStarSystemPlacementStrategy.cs index ec58d7422..9a1d26b00 100644 --- a/FrEee/Setup/StarSystemPlacementStrategies/DiffuseStarSystemPlacementStrategy.cs +++ b/FrEee/Setup/StarSystemPlacementStrategies/DiffuseStarSystemPlacementStrategy.cs @@ -1,10 +1,9 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Utility; +using FrEee.Utility; using FrEee.Extensions; using System; using System.Drawing; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Setup.StarSystemPlacementStrategies; diff --git a/FrEee/Setup/StarSystemPlacementStrategies/GridStarSystemPlacementStrategy.cs b/FrEee/Setup/StarSystemPlacementStrategies/GridStarSystemPlacementStrategy.cs index 077abc3aa..7ac470283 100644 --- a/FrEee/Setup/StarSystemPlacementStrategies/GridStarSystemPlacementStrategy.cs +++ b/FrEee/Setup/StarSystemPlacementStrategies/GridStarSystemPlacementStrategy.cs @@ -1,10 +1,9 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Utility; +using FrEee.Utility; using FrEee.Extensions; using System; using System.Drawing; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Setup.StarSystemPlacementStrategies; diff --git a/FrEee/Setup/StarSystemPlacementStrategies/IStarSystemPlacementStrategy.cs b/FrEee/Setup/StarSystemPlacementStrategies/IStarSystemPlacementStrategy.cs new file mode 100644 index 000000000..483ec0dcd --- /dev/null +++ b/FrEee/Setup/StarSystemPlacementStrategies/IStarSystemPlacementStrategy.cs @@ -0,0 +1,21 @@ +using FrEee.Objects.GameState; +using FrEee.Utility; +using System.Drawing; + +namespace FrEee.Setup.StarSystemPlacementStrategies; + +/// +/// Algorithm for placing star systems on the galaxy map. +/// +public interface IStarSystemPlacementStrategy +{ + /// + /// Places a star system. + /// + /// The galaxy. + /// The minimum number of buffer squares between any two star systems. + /// Where are we allowed to place star systems? + /// How many more stars to place? + /// The location of the star system, or null if a location could not be found. + Point? PlaceStarSystem(Galaxy galaxy, int buffer, Rectangle bounds, int starsLeft, PRNG dice); +} \ No newline at end of file diff --git a/FrEee/Setup/StarSystemPlacementStrategies/RandomStarSystemPlacementStrategy.cs b/FrEee/Setup/StarSystemPlacementStrategies/RandomStarSystemPlacementStrategy.cs index 159915f67..1896ea991 100644 --- a/FrEee/Setup/StarSystemPlacementStrategies/RandomStarSystemPlacementStrategy.cs +++ b/FrEee/Setup/StarSystemPlacementStrategies/RandomStarSystemPlacementStrategy.cs @@ -1,10 +1,9 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Utility; +using FrEee.Utility; using FrEee.Extensions; using System; using System.Drawing; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Setup.StarSystemPlacementStrategies; diff --git a/FrEee/Setup/StarSystemPlacementStrategies/SpiralStarSystemPlacementStrategy.cs b/FrEee/Setup/StarSystemPlacementStrategies/SpiralStarSystemPlacementStrategy.cs index 916a9669e..ce8fe9335 100644 --- a/FrEee/Setup/StarSystemPlacementStrategies/SpiralStarSystemPlacementStrategy.cs +++ b/FrEee/Setup/StarSystemPlacementStrategies/SpiralStarSystemPlacementStrategy.cs @@ -1,11 +1,10 @@ -using FrEee.Interfaces; -using FrEee.Objects.Space; -using FrEee.Utility; +using FrEee.Utility; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Setup.StarSystemPlacementStrategies; diff --git a/FrEee/Setup/StartingTechnologyLevel.cs b/FrEee/Setup/StartingTechnologyLevel.cs new file mode 100644 index 000000000..e482b710e --- /dev/null +++ b/FrEee/Setup/StartingTechnologyLevel.cs @@ -0,0 +1,8 @@ +namespace FrEee.Setup; + +public enum StartingTechnologyLevel +{ + Low, + Medium, + High +} \ No newline at end of file diff --git a/FrEee/Setup/WarpPointPlacementStrategies/WarpPointPlacementStrategy.cs b/FrEee/Setup/WarpPointPlacementStrategies/WarpPointPlacementStrategy.cs index 96882d9a0..7afb2f48f 100644 --- a/FrEee/Setup/WarpPointPlacementStrategies/WarpPointPlacementStrategy.cs +++ b/FrEee/Setup/WarpPointPlacementStrategies/WarpPointPlacementStrategy.cs @@ -1,10 +1,10 @@ -using FrEee.Interfaces; -using FrEee.Objects.Abilities; +using FrEee.Objects.Abilities; using FrEee.Objects.Space; using FrEee.Modding; using FrEee.Extensions; using System.Collections.Generic; using System.Linq; +using FrEee.Objects.GameState; namespace FrEee.Setup.WarpPointPlacementStrategies; diff --git a/FrEee/Utility/Pathfinder.cs b/FrEee/Utility/Pathfinder.cs index c317321b4..c2908ccb0 100644 --- a/FrEee/Utility/Pathfinder.cs +++ b/FrEee/Utility/Pathfinder.cs @@ -1,12 +1,12 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; -using FrEee.Objects.Civilization; +using FrEee.Objects.Civilization; using FrEee.Objects.Space; using FrEee.Extensions; using System; using System.Collections.Generic; using System.Drawing; using System.Linq; +using FrEee.Objects.Combat; +using FrEee.Objects.GameState; namespace FrEee.Utility; diff --git a/FrEee/Utility/Pictures.cs b/FrEee/Utility/Pictures.cs index ad18ec1c6..f589c43b2 100644 --- a/FrEee/Utility/Pictures.cs +++ b/FrEee/Utility/Pictures.cs @@ -1,5 +1,3 @@ -using FrEee.Enumerations; -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Objects.Combat; using FrEee.Objects.Space; diff --git a/FrEee/Utility/Progress.cs b/FrEee/Utility/Progress.cs index b4a45fa2d..cf79773e7 100644 --- a/FrEee/Utility/Progress.cs +++ b/FrEee/Utility/Progress.cs @@ -1,6 +1,4 @@ -using FrEee.Interfaces; using FrEee.Modding; -using FrEee.Modding.Interfaces; using FrEee.Extensions; using System; using FrEee.Serialization; diff --git a/FrEee/Utility/Resource.cs b/FrEee/Utility/Resource.cs index 92d2b2d40..841b82ab0 100644 --- a/FrEee/Utility/Resource.cs +++ b/FrEee/Utility/Resource.cs @@ -1,4 +1,3 @@ -using FrEee.Interfaces; using FrEee.Objects.Civilization; using FrEee.Extensions; using System; @@ -7,6 +6,7 @@ using System.IO; using System.Linq; using FrEee.Serialization; +using FrEee.Objects.GameState; namespace FrEee.Utility;