From 800dcf23ba1a537d013f4376af4dfec8bd8cf6b7 Mon Sep 17 00:00:00 2001 From: Torben Nehmer Date: Tue, 24 Sep 2024 20:17:49 +0200 Subject: [PATCH] Create Game Engine, update race model with defaults. --- Stars Assistant/Model/Planet.cs | 91 +---------- Stars Assistant/Model/Race.cs | 30 ++-- Stars Assistant/Services/Game.cs | 1 + Stars Assistant/Services/GameEngine.cs | 154 ++++++++++++++++++ Stars Assistant/ViewModels/PlanetViewModel.cs | 4 + 5 files changed, 181 insertions(+), 99 deletions(-) diff --git a/Stars Assistant/Model/Planet.cs b/Stars Assistant/Model/Planet.cs index b7ab232..08ea465 100644 --- a/Stars Assistant/Model/Planet.cs +++ b/Stars Assistant/Model/Planet.cs @@ -7,8 +7,6 @@ public class Planet { #region Persistant and derived propeties - public const int MinEffectiveValue = 5; - [Key] public required string Name { get; set; } @@ -90,94 +88,19 @@ public class Planet #region Derived Properties - public static int EffectiveValue(int? value) { - if (value == null) return 0; - if (value < MinEffectiveValue) return MinEffectiveValue; - return (int) value; - } + // public int EffectiveValue() => EffectiveValue(Value); - public int EffectiveValue() => EffectiveValue(Value); + // public int MaxPopOnPlanet() => MaxPopOnPlanet(Value); - public static int MaxPopOnPlanet (int? value) { - return Services.Game.Player.MaxPopOnPlanet * EffectiveValue(value) / 100; - } + // public int PopGrowth() => PopGrowth(Population ?? 0, MaxPopOnPlanet(), Value ?? 0); - public int MaxPopOnPlanet() => MaxPopOnPlanet(Value); + // public int MaxFact() => MaxFact(Population ?? 0); - public int PopGrowth(int currentPop, int maxPop, int value) { - // What to do with non player races? - if (OwnerId != Services.Game.Player.Name) return 0; + // public int MaxMines() => MaxMines(Population ?? 0); - double popRatio = currentPop / maxPop; - int growth = 0; - if (value < 0) - growth = currentPop * value / 10; - else if (popRatio <= 0.25) - growth = currentPop * value * (Services.Game.Player.GrowthRatePercent ?? 0) / 100; - else if (popRatio <= 1) - growth = currentPop * value * (Services.Game.Player.GrowthRatePercent ?? 0) /100 * 16 / 9 * ((int) Math.Pow(1 - popRatio, 2)); - else - growth = (int) (currentPop * (popRatio - 1) * 0.04); + // public int ResourcesFromPop() => ResourcesFromPop(Population ?? 0, Value ?? 0); - return growth / 100 * 100; - } - - public int PopGrowth() => PopGrowth(Population ?? 0, MaxPopOnPlanet(), Value ?? 0); - - public int MaxFact(int currentPop) { - int effectivePop = Math.Min(currentPop, MaxPopOnPlanet()); - double tmp = (double) effectivePop / 10000 * Services.Game.Player.FactoryNumberPer10k ?? 0; - return (int) tmp; - } - - public int MaxFact() => MaxFact(Population ?? 0); - - public int MaxMines(int currentPop) { - int effectivePop = Math.Min(currentPop, MaxPopOnPlanet()); - double tmp = (double) effectivePop / 10000 * Services.Game.Player.MineNumberPer10k ?? 0; - return (int) tmp; - } - - public int MaxMines() => MaxMines(Population ?? 0); - - public static int ResourcesFromPop(int currentPop, int value) { - if (Services.Game.Player.ColonistsPerResource == null - || Services.Game.Player.ColonistsPerResource == 0) - { - return 0; - } - - int maxPop = MaxPopOnPlanet(value); - int popFullEfficiency = 0; - int popHalfEfficiency = 0; - if (currentPop <= maxPop) { - popFullEfficiency = currentPop; - } - else if (currentPop <= 3 * maxPop) { - popFullEfficiency = maxPop; - popHalfEfficiency = currentPop - maxPop; - } - else { - popFullEfficiency = maxPop; - popHalfEfficiency = 2 * maxPop; - } - int popEffective = popFullEfficiency + (popHalfEfficiency / 2); - return (popEffective / Services.Game.Player.ColonistsPerResource) ?? 0; - } - - public int ResourcesFromPop() => ResourcesFromPop(Population ?? 0, Value ?? 0); - - public static int ResourcesFromFact(int currentPop, int factories, int value) { - int maxPop = MaxPopOnPlanet(value); - int effectivePop = Math.Min(currentPop, maxPop); - double tmp = (double) effectivePop / 10000 * Services.Game.Player.FactoryNumberPer10k ?? 0; - int maxOperableFactories = (int) tmp; - int operableFactories = Math.Min(factories, maxOperableFactories); - tmp = (double) operableFactories / 10 * Services.Game.Player.FactoryResPer10 ?? 0; - return (int) tmp; - } - - public int ResourcesFromFact() => ResourcesFromFact(Population ?? 0, Factories ?? 0, Value ?? 0); + // public int ResourcesFromFact() => ResourcesFromFact(Population ?? 0, Factories ?? 0, Value ?? 0); #endregion diff --git a/Stars Assistant/Model/Race.cs b/Stars Assistant/Model/Race.cs index 691ce4b..2bdf7b6 100644 --- a/Stars Assistant/Model/Race.cs +++ b/Stars Assistant/Model/Race.cs @@ -24,9 +24,9 @@ public class Race public int? PlayerFileId { get; set; } - public int? ColonistsPerResource { get; set; } + public int ColonistsPerResource { get; set; } = 1_000; - public int? GrowthRatePercent { get; set; } + public int GrowthRatePercent { get; set; } = 15; private PRT _PRT = PRT.Other; public PRT PRT { @@ -37,8 +37,8 @@ public class Race } } - private bool? _hasOBRM; - public bool? HasOBRM { + private bool _hasOBRM = false; + public bool HasOBRM { get => _hasOBRM; set { _hasOBRM = value; @@ -55,29 +55,29 @@ public class Race [NotMapped] public int MaxEffectivePopOnGreen { get; private set; } - private bool? _factoryCost3; - public bool? FactoryCost3 { + private bool _factoryCost3 = false; + public bool FactoryCost3 { get => _factoryCost3; set { _factoryCost3 = value; - FactoryGerCost = (_factoryCost3 ?? false) ? 3 : 4; + FactoryGerCost = _factoryCost3 ? 3 : 4; } } [NotMapped] public int FactoryGerCost { get; private set; } - public int? FactoryNumberPer10k { get; set; } + public int FactoryNumberPer10k { get; set; } = 10; - public int? FactoryResCost { get; set; } + public int FactoryResCost { get; set; } = 10; - public int? FactoryResPer10 { get; set; } + public int FactoryResPer10 { get; set; } = 10; - public int? MineResCost { get; set; } + public int MineResCost { get; set; } = 5; - public int? MineNumberPer10k { get; set; } + public int MineNumberPer10k { get; set; } = 10; - public int? MineMineralsPer10 { get; set; } + public int MineMineralsPer10 { get; set; } = 10; #endregion @@ -101,9 +101,9 @@ public class Race PRT.JOAT => 1.2, _ => 1, }; - double obrmMod = (HasOBRM ?? false) ? 1.1 : 1.0; + double obrmMod = HasOBRM ? 1.1 : 1.0; MaxPopOnPlanet = Convert.ToInt32(basePop * prtMod * obrmMod); - MaxEffectivePopOnRed = MaxPopOnPlanet * Planet.MinEffectiveValue / 100 * 3; + MaxEffectivePopOnRed = MaxPopOnPlanet * Services.GameEngine.MinEffectiveValue / 100 * 3; MaxEffectivePopOnGreen = MaxPopOnPlanet * 3; } diff --git a/Stars Assistant/Services/Game.cs b/Stars Assistant/Services/Game.cs index 795aa54..e44edfc 100644 --- a/Stars Assistant/Services/Game.cs +++ b/Stars Assistant/Services/Game.cs @@ -120,6 +120,7 @@ public class Game public void RegisterServicesForGame() { Locator.CurrentMutable.RegisterConstant(this, typeof(Services.Game)); + GameEngine.Game = this; Locator.CurrentMutable.RegisterConstant(new CSVDataLoader(), typeof(CSVDataLoader)); Locator.CurrentMutable.Register(() => new StarsDatabase(DatabaseFileName), typeof(StarsDatabase)); diff --git a/Stars Assistant/Services/GameEngine.cs b/Stars Assistant/Services/GameEngine.cs index b6b9fda..cfb9e89 100644 --- a/Stars Assistant/Services/GameEngine.cs +++ b/Stars Assistant/Services/GameEngine.cs @@ -1,8 +1,162 @@ using System; +using Splat; namespace StarsAssistant.Services; public class GameEngine { + /// + /// Helper reference to the Game, populated during service registration for the + /// loaded game. + /// + // TODO: check for better options here. + public static Game Game { get; set; } = null!; + + /// + /// Minimum effective value of a planet for purposes of maximum population etc. + /// + public const int MinEffectiveValue = 5; + + /// + /// Normalize a planet value for minimum effetive value. + /// + /// Real planet value + /// Effective plante value + public static int EffectivePlanetValue(int value) => (value < MinEffectiveValue) ? MinEffectiveValue : value; + + /// + /// Compute the maximum regular (non-overcrowded) population on a planet for the active player. + /// + /// Real planet value + /// effective maximum population + public static int MaxPopOnPlanetForPlayer(int value) => Game.Player.MaxPopOnPlanet * EffectivePlanetValue(value) / 100; + + /// + /// Compute population growth for player planets. + /// + /// Current population on planet + /// Real planet value + /// population growth per turn + public static int PlanetPopGrowthForPlayer(int currentPop, int value) + { + int maxPop = MaxPopOnPlanetForPlayer(value); + int growth; + + if (value < 0) + { + growth = currentPop * value / 10; + } + else + { + double popRatio = currentPop / maxPop; + if (popRatio <= 0.25) + growth = currentPop * value * Game.Player.GrowthRatePercent / 100; + else if (popRatio <= 1) + growth = currentPop * value * Game.Player.GrowthRatePercent /100 * 16 / 9 * ((int) Math.Pow(1 - popRatio, 2)); + else + growth = (int) (currentPop * (popRatio - 1) * 0.04); + } + + // round to nearest 100 + return growth / 100 * 100; + } + + /// + /// Returns the maximum operable factories on a given player planet for a given value. + /// + /// Population on planet + /// Real planet value + /// Maximum operable factories for currentPop + public static int MaxOperableFactoriesForPlayer(int currentPop, int value) + { + int effectivePop = Math.Min(currentPop, MaxPopOnPlanetForPlayer(value)); + double tmp = (double) effectivePop / 10000 * Game.Player.FactoryNumberPer10k; + return (int) tmp; + } + + /// + /// Maximum buildable factories on a player planet with a given value. + /// + /// Real Planet Value + /// Maximum buildable factories + public static int MaxBuildableFactoriesForPlayer(int value) + { + double tmp = (double) MaxPopOnPlanetForPlayer(value) / 10000 * Game.Player.FactoryNumberPer10k; + return (int) tmp; + } + + /// + /// Returns the maximum operable mines on a given player planet for a given value. + /// + /// Population on planet + /// Real planet value + /// Maximum operable mines for currentPop + public static int MaxOperableMinesForPlayer(int currentPop, int value) + { + int effectivePop = Math.Min(currentPop, MaxPopOnPlanetForPlayer(value)); + double tmp = (double) effectivePop / 10000 * Game.Player.MineNumberPer10k; + return (int) tmp; + } + + /// + /// Maximum buildable factories on a player planet with a given value. + /// + /// Real Planet Value + /// Maximum buildable factories + public static int MaxBuildableMinesForPlayer(int value) + { + double tmp = (double) MaxPopOnPlanetForPlayer(value) / 10000 * Game.Player.MineNumberPer10k; + return (int) tmp; + } + + /// + /// Ressources produced by population on a player planet with a given value. Takes + /// less effective overcrowding pop into account. + /// + /// Current planetary population + /// Real planet value + /// Resources generated by populationa + public static int ResourcesFromPopForPlayer(int currentPop, int value) + { + if (Game.Player.ColonistsPerResource == 0) + return 0; + + int maxPop = MaxPopOnPlanetForPlayer(value); + int popFullEfficiency; + int popHalfEfficiency; + if (currentPop <= maxPop) { + popHalfEfficiency = 0; + popFullEfficiency = currentPop; + } + else if (currentPop <= 3 * maxPop) { + popFullEfficiency = maxPop; + popHalfEfficiency = currentPop - maxPop; + } + else { + popFullEfficiency = maxPop; + popHalfEfficiency = 2 * maxPop; + } + int popEffective = popFullEfficiency + (popHalfEfficiency / 2); + return popEffective / Services.Game.Player.ColonistsPerResource; + } + + /// + /// Ressources generated by factories on a player planet for a given value an + /// population. Takes maximum operable factories into account. + /// + /// Current planetary population + /// Number of factories built + /// Real planet value + /// Resources generated by factories + public static int ResourcesFromFactForPlayer(int currentPop, int factories, int value) + { + int maxPop = MaxPopOnPlanetForPlayer(value); + int effectivePop = Math.Min(currentPop, maxPop); + double tmp = (double) effectivePop / 10000 * Game.Player.FactoryNumberPer10k; + int maxOperableFactories = (int) tmp; + int operableFactories = Math.Min(factories, maxOperableFactories); + tmp = (double) operableFactories / 10 * Services.Game.Player.FactoryResPer10; + return (int) tmp; + } } diff --git a/Stars Assistant/ViewModels/PlanetViewModel.cs b/Stars Assistant/ViewModels/PlanetViewModel.cs index 9ed7cf3..cdd39b2 100644 --- a/Stars Assistant/ViewModels/PlanetViewModel.cs +++ b/Stars Assistant/ViewModels/PlanetViewModel.cs @@ -38,6 +38,10 @@ public partial class PlanetViewModel : ViewModelBase public int SurfaceGermanium => Planet.SurfaceGermanium ?? 0; + + + // ### TESTING CODE ### + public static ObservableCollection LoadAll() { using var db = Locator.Current.GetService()!;