using Model.Economy; using Model.Entity; using Model.Entity.Parts; using Model.Types; namespace Services.Immortal; public class EconomyService : IEconomyService { private List? buildEconomyOverTime; public List GetOverTime() { return buildEconomyOverTime; } public void Subscribe(Action action) { OnChange += action; } public void Unsubscribe(Action action) { OnChange -= action; } public void Calculate(IBuildOrderService buildOrder, ITimingService timing, int fromInterval) { // We don't consider things mining at zero seconds if (fromInterval == 0) fromInterval = 1; if (buildEconomyOverTime == null) { buildEconomyOverTime = []; for (var interval = 0; interval < timing.GetAttackTime(); interval++) buildEconomyOverTime.Add(new EconomyModel { Interval = interval }); } if (buildEconomyOverTime.Count > timing.GetAttackTime()) buildEconomyOverTime.RemoveRange(timing.GetAttackTime(), buildEconomyOverTime.Count - timing.GetAttackTime()); while (buildEconomyOverTime.Count < timing.GetAttackTime()) buildEconomyOverTime.Add(new EconomyModel { Interval = buildEconomyOverTime.Count - 1 }); for (var interval = fromInterval; interval < timing.GetAttackTime(); interval++) { buildEconomyOverTime[interval] = new EconomyModel(); var economyAtSecond = buildEconomyOverTime[interval]; CarryOverEconomyFromPreviousInterval(interval, economyAtSecond); SetupCurrentInterval(buildOrder, economyAtSecond, interval); AddPassivePyreGain(interval, economyAtSecond); float freeWorkers = economyAtSecond.WorkerCount - economyAtSecond.BusyWorkerCount; var workersNeeded = AddFundsFromHarvestPoints(economyAtSecond, freeWorkers); workersNeeded -= CalculateCreatingWorkerCosts(economyAtSecond); MakeNeededNewWorkersRequests(workersNeeded, economyAtSecond); SubtractFundsOnRequestedOrders(buildOrder, interval, economyAtSecond); HandledAddingNewHarvestPointsAndWorkersToEconomy(buildOrder, interval, economyAtSecond); } NotifyDataChanged(); } public EconomyModel GetEconomy(int atInterval) { return atInterval >= buildEconomyOverTime.Count ? buildEconomyOverTime.Last() : buildEconomyOverTime[atInterval]; } private static void SetupCurrentInterval(IBuildOrderService buildOrder, EconomyModel economyAtSecond, int interval) { economyAtSecond.Interval = interval; economyAtSecond.HarvestPoints = (from harvester in buildOrder.GetHarvestPointsCompletedBefore(interval) select harvester).ToList(); } private static void HandledAddingNewHarvestPointsAndWorkersToEconomy(IBuildOrderService buildOrder, int interval, EconomyModel economyAtSecond) { if (!buildOrder.CompletedOrders.TryGetValue(interval, out var completedAtInterval)) return; foreach (var newEntity in completedAtInterval) { economyAtSecond.HarvestPoints.Add(newEntity); var production = newEntity.Production(); if (production is { RequiresWorker: true }) economyAtSecond.BusyWorkerCount -= 1; } } private static void SubtractFundsOnRequestedOrders(IBuildOrderService buildOrder, int interval, EconomyModel economyAtSecond) { if (!buildOrder.StartedOrders.TryGetValue(interval, out var ordersAtTime)) return; foreach (var production in ordersAtTime.Select(order => EntityModel.GetDictionary()[order.DataType]) .Select(foundEntity => foundEntity.Production()) .OfType()) { economyAtSecond.Alloy -= production.Alloy; economyAtSecond.Ether -= production.Ether; economyAtSecond.Pyre -= production.Pyre; if (production.RequiresWorker) economyAtSecond.BusyWorkerCount += 1; if (production.ConsumesWorker) economyAtSecond.WorkerCount -= 1; } } private static void MakeNeededNewWorkersRequests(int workersNeeded, EconomyModel economyAtSecond) { if (workersNeeded <= economyAtSecond.CreatingWorkerCount) return; economyAtSecond.CreatingWorkerCount += 1; economyAtSecond.CreatingWorkerDelays.Add(20); } /** * Returns number of workers created */ private static int CalculateCreatingWorkerCosts(EconomyModel economyAtSecond) { var createdWorkers = 0; if (economyAtSecond.CreatingWorkerCount <= 0) return createdWorkers; for (var i = 0; i < economyAtSecond.CreatingWorkerDelays.Count; i++) if (economyAtSecond.CreatingWorkerDelays[i] > 0) { if (!(economyAtSecond.Alloy > 2.5f)) continue; economyAtSecond.Alloy -= 2.5f; economyAtSecond.CreatingWorkerDelays[i]--; } else { economyAtSecond.CreatingWorkerCount -= 1; economyAtSecond.WorkerCount += 1; createdWorkers++; economyAtSecond.CreatingWorkerDelays.Remove(i); i--; } return createdWorkers; } /** * Returns needed workers to maximize harvest points */ private static int AddFundsFromHarvestPoints(EconomyModel economyAtSecond, float freeWorkers) { var workersNeeded = 0; foreach (var harvesterPoint in economyAtSecond.HarvestPoints.Select(entity => entity.Harvest())) { switch (harvesterPoint.RequiresWorker) { case true: { if (harvesterPoint.Resource == ResourceType.Alloy) { var usedWorkers = Math.Min(harvesterPoint.Slots, freeWorkers); economyAtSecond.Alloy += harvesterPoint.HarvestedPerInterval * usedWorkers; economyAtSecond.AlloyIncome += harvesterPoint.HarvestedPerInterval * usedWorkers; freeWorkers -= usedWorkers; if (usedWorkers < harvesterPoint.Slots) workersNeeded += 1; } break; } case false: { switch (harvesterPoint.Resource) { case ResourceType.Ether: economyAtSecond.Ether += harvesterPoint.HarvestedPerInterval * harvesterPoint.Slots; economyAtSecond.EtherIncome += harvesterPoint.HarvestedPerInterval * harvesterPoint.Slots; break; case ResourceType.Alloy: economyAtSecond.Alloy += harvesterPoint.HarvestedPerInterval * harvesterPoint.Slots; economyAtSecond.AlloyIncome += harvesterPoint.HarvestedPerInterval * harvesterPoint.Slots; break; case ResourceType.Pyre: break; // Pyre Miner? } break; } } } return workersNeeded; } private static void AddPassivePyreGain(int interval, EconomyModel economyAtSecond) { if (interval % 3 == 0) economyAtSecond.Pyre += 1; } private void CarryOverEconomyFromPreviousInterval(int interval, EconomyModel economyAtSecond) { if (interval > 0) { economyAtSecond.Alloy = buildEconomyOverTime[interval - 1].Alloy; economyAtSecond.Ether = buildEconomyOverTime[interval - 1].Ether; economyAtSecond.Pyre = buildEconomyOverTime[interval - 1].Pyre; economyAtSecond.WorkerCount = buildEconomyOverTime[interval - 1].WorkerCount; economyAtSecond.BusyWorkerCount = buildEconomyOverTime[interval - 1].BusyWorkerCount; economyAtSecond.CreatingWorkerCount = buildEconomyOverTime[interval - 1].CreatingWorkerCount; economyAtSecond.HarvestPoints = buildEconomyOverTime[interval - 1].HarvestPoints.ToList(); economyAtSecond.CreatingWorkerDelays = buildEconomyOverTime[interval - 1].CreatingWorkerDelays.ToList(); } } private event Action OnChange = null!; private void NotifyDataChanged() { OnChange?.Invoke(); } }