30 changed files with 1005 additions and 269 deletions
Binary file not shown.
@ -1,21 +0,0 @@ |
|||||||
@page "/economy-comparison" |
|
||||||
|
|
||||||
@layout PageLayout |
|
||||||
|
|
||||||
|
|
||||||
<LayoutMediumContentComponent> |
|
||||||
<AlertComponent> |
|
||||||
<Title>Not Implemented</Title> |
|
||||||
<Message></Message> |
|
||||||
</AlertComponent> |
|
||||||
|
|
||||||
<PaperComponent> |
|
||||||
TODO |
|
||||||
</PaperComponent> |
|
||||||
|
|
||||||
<ContentDividerComponent/> |
|
||||||
|
|
||||||
<PaperComponent> |
|
||||||
|
|
||||||
</PaperComponent> |
|
||||||
</LayoutMediumContentComponent> |
|
||||||
@ -0,0 +1,95 @@ |
|||||||
|
@page "/economy-comparison" |
||||||
|
|
||||||
|
@implements IDisposable |
||||||
|
@inject IEconomyComparisonService economyComparisonService |
||||||
|
@layout PageLayout |
||||||
|
|
||||||
|
|
||||||
|
<LayoutMediumContentComponent> |
||||||
|
|
||||||
|
<AlertComponent Type="@SeverityType.Error"> |
||||||
|
<Title>Contains Bugs</Title> |
||||||
|
<Message>None of these calculations and results have been verified. Use with caution. </Message> |
||||||
|
</AlertComponent> |
||||||
|
|
||||||
|
<DevOnlyComponent> |
||||||
|
@foreach (var buildToCompare in economyComparisonService.BuildsToCompare) |
||||||
|
{ |
||||||
|
foreach (var ordersAtTime in buildToCompare.BuildOrderModel.StartedOrders) |
||||||
|
{ |
||||||
|
foreach (var order in ordersAtTime.Value) |
||||||
|
{ |
||||||
|
<div>@ordersAtTime.Key - @order.Info().Name</div> |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
@{ |
||||||
|
float alloyHighest = 0; |
||||||
|
} |
||||||
|
@foreach (var buildToCompare in economyComparisonService.BuildsToCompare) |
||||||
|
{ |
||||||
|
foreach (var economy in buildToCompare.EconomyOverTimeModel) |
||||||
|
{ |
||||||
|
if (economy.Alloy > alloyHighest) |
||||||
|
{ |
||||||
|
alloyHighest = economy.Alloy; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
<div>@alloyHighest</div> |
||||||
|
|
||||||
|
</DevOnlyComponent> |
||||||
|
|
||||||
|
<PaperComponent> |
||||||
|
<div>You</div> |
||||||
|
<EconomyInputComponent ForPlayer="0"/> |
||||||
|
</PaperComponent> |
||||||
|
|
||||||
|
<PaperComponent> |
||||||
|
<div>Them</div> |
||||||
|
<EconomyInputComponent ForPlayer="1"/> |
||||||
|
</PaperComponent> |
||||||
|
|
||||||
|
<PaperComponent> |
||||||
|
<EconomyDifferenceComponent/> |
||||||
|
</PaperComponent> |
||||||
|
|
||||||
|
<PaperComponent> |
||||||
|
<ChartComponent/> |
||||||
|
</PaperComponent> |
||||||
|
|
||||||
|
<ContentDividerComponent/> |
||||||
|
|
||||||
|
<PaperComponent> |
||||||
|
|
||||||
|
<InfoBodyComponent> |
||||||
|
<InfoQuestionComponent> |
||||||
|
What is this tool for? |
||||||
|
</InfoQuestionComponent> |
||||||
|
<InfoAnswerComponent> |
||||||
|
Compare two economies together to determine best attack timing windows. |
||||||
|
</InfoAnswerComponent> |
||||||
|
</InfoBodyComponent> |
||||||
|
</PaperComponent> |
||||||
|
</LayoutMediumContentComponent> |
||||||
|
|
||||||
|
@code { |
||||||
|
|
||||||
|
|
||||||
|
protected override void OnInitialized() |
||||||
|
{ |
||||||
|
economyComparisonService.Subscribe(StateHasChanged); |
||||||
|
|
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void IDisposable.Dispose() |
||||||
|
{ |
||||||
|
economyComparisonService.Unsubscribe(StateHasChanged); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,199 @@ |
|||||||
|
@inject IEconomyComparisonService economyComparisonService |
||||||
|
@inject IJSRuntime jsRuntime; |
||||||
|
@using Model.BuildOrders |
||||||
|
@implements IDisposable |
||||||
|
|
||||||
|
<div class="chartsContainer"> |
||||||
|
|
||||||
|
@{ |
||||||
|
var index = 0; |
||||||
|
} |
||||||
|
@foreach (var chart in charts) |
||||||
|
{ |
||||||
|
index++; |
||||||
|
|
||||||
|
<div style="width: 0; height: @chart.ValueDisplayMax.ToString()px"> |
||||||
|
<div style="left: calc(-@width.ToString()px / 2); position: relative; border: 2px solid gray; border-radius:2px; width: @chart.IntervalDisplayMax.ToString()px; height: @chart.ValueDisplayMax.ToString()px"> |
||||||
|
@foreach (var point in chart.Points) |
||||||
|
{ |
||||||
|
var xCoord = point.GetInterval(chart.HighestIntervalPoint, chart.IntervalDisplayMax); |
||||||
|
|
||||||
|
var show = int.Parse(xCoord) / 6 % 2; |
||||||
|
var player = index - 1; |
||||||
|
|
||||||
|
if (show == player) |
||||||
|
{ |
||||||
|
<div style="position: absolute; |
||||||
|
bottom:@point.GetValue(highestAlloyPoint, chart.ValueDisplayMax)px; |
||||||
|
left:@point.GetInterval(chart.HighestIntervalPoint, chart.IntervalDisplayMax)px; |
||||||
|
width: 0px; |
||||||
|
height: 0px;"> |
||||||
|
<div style="width:1px; height: 1px; border-top-right-radius:10px; border-top-left-radius:10px; border: 2px solid @chart.ChartColor; background-color:@chart.ChartColor"> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
} |
||||||
|
} |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
} |
||||||
|
</div> |
||||||
|
|
||||||
|
<style> |
||||||
|
.chartsContainer { |
||||||
|
position: relative; |
||||||
|
display: flex; |
||||||
|
flex-wrap: wrap; |
||||||
|
justify-content: center; |
||||||
|
margin-bottom: 20px; |
||||||
|
} |
||||||
|
|
||||||
|
</style> |
||||||
|
|
||||||
|
@code { |
||||||
|
|
||||||
|
private readonly int width = 800; |
||||||
|
private readonly int height = 700; |
||||||
|
|
||||||
|
private List<ChartModel> charts = new(); |
||||||
|
|
||||||
|
|
||||||
|
float highestAlloyPoint = 0; |
||||||
|
|
||||||
|
|
||||||
|
protected override void OnInitialized() |
||||||
|
{ |
||||||
|
economyComparisonService.Subscribe(OnBuilderOrderChanged); |
||||||
|
|
||||||
|
OnBuilderOrderChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
int lastRequestedRefreshIndex; |
||||||
|
|
||||||
|
void IDisposable.Dispose() |
||||||
|
{ |
||||||
|
economyComparisonService.Unsubscribe(OnBuilderOrderChanged); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
void OnBuilderOrderChanged() |
||||||
|
{ |
||||||
|
|
||||||
|
charts = new List<ChartModel>(); |
||||||
|
var index = 0; |
||||||
|
|
||||||
|
|
||||||
|
highestAlloyPoint = 0; |
||||||
|
|
||||||
|
foreach (var buildToCompare in economyComparisonService.BuildsToCompare) |
||||||
|
{ |
||||||
|
GenerateChart(index++, buildToCompare); |
||||||
|
} |
||||||
|
|
||||||
|
StateHasChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
protected override bool ShouldRender() |
||||||
|
{ |
||||||
|
#if DEBUG |
||||||
|
jsRuntime.InvokeVoidAsync("console.time", "ChartComponent"); |
||||||
|
#endif |
||||||
|
|
||||||
|
return true; |
||||||
|
} |
||||||
|
|
||||||
|
protected override void OnAfterRender(bool firstRender) |
||||||
|
{ |
||||||
|
#if DEBUG |
||||||
|
jsRuntime.InvokeVoidAsync("console.timeEnd", "ChartComponent"); |
||||||
|
#endif |
||||||
|
} |
||||||
|
|
||||||
|
void GenerateChart(int index, BuildToCompareModel buildToCompareModel) |
||||||
|
{ |
||||||
|
var economyOverTime = buildToCompareModel.EconomyOverTimeModel; |
||||||
|
|
||||||
|
|
||||||
|
var alloyChart = new ChartModel |
||||||
|
{ |
||||||
|
IntervalDisplayMax = width, |
||||||
|
ValueDisplayMax = height, |
||||||
|
ChartColor = buildToCompareModel.ChartColor |
||||||
|
}; |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
for (var interval = 0; interval < economyOverTime.Count(); interval++) |
||||||
|
{ |
||||||
|
var alloyPoint = new PointModel { Interval = interval }; |
||||||
|
|
||||||
|
var economyAtSecond = economyOverTime[interval]; |
||||||
|
|
||||||
|
var alloyWorkerHarvesters = from harvester in economyAtSecond.Harvesters |
||||||
|
where harvester.Harvest() != null |
||||||
|
where harvester.Harvest().RequiresWorker |
||||||
|
where harvester.Harvest().Resource == ResourceType.Alloy |
||||||
|
select harvester; |
||||||
|
|
||||||
|
var alloyAutomaticHarvesters = from harvester in economyAtSecond.Harvesters |
||||||
|
where harvester.Harvest() != null |
||||||
|
where harvester.Harvest().RequiresWorker == false |
||||||
|
where harvester.Harvest().Resource == ResourceType.Alloy |
||||||
|
select harvester; |
||||||
|
|
||||||
|
|
||||||
|
float autoAlloy = 0; |
||||||
|
float workerSlots = 0; |
||||||
|
float workerAlloy = 0; |
||||||
|
|
||||||
|
float economySpending = 0; |
||||||
|
|
||||||
|
foreach (var alloyAutoHarvester in alloyAutomaticHarvesters) |
||||||
|
{ |
||||||
|
autoAlloy += alloyAutoHarvester.Harvest().Slots * alloyAutoHarvester.Harvest().HarvestedPerInterval; |
||||||
|
var production = alloyAutoHarvester.Production(); |
||||||
|
if (production != null) |
||||||
|
{ |
||||||
|
economySpending += production.Alloy; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
foreach (var alloyWorkerHarvester in alloyWorkerHarvesters) |
||||||
|
{ |
||||||
|
workerSlots += alloyWorkerHarvester.Harvest().Slots; |
||||||
|
var production = alloyWorkerHarvester.Production(); |
||||||
|
if (production != null) |
||||||
|
{ |
||||||
|
economySpending += production.Alloy; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
economySpending += (economyAtSecond.WorkerCount - 6) * 50; |
||||||
|
|
||||||
|
workerAlloy = Math.Min(economyAtSecond.WorkerCount - economyAtSecond.BusyWorkerCount, workerSlots); |
||||||
|
|
||||||
|
|
||||||
|
alloyPoint.TempValue = workerAlloy + autoAlloy; |
||||||
|
|
||||||
|
|
||||||
|
if (interval > 0) |
||||||
|
{ |
||||||
|
alloyPoint.TempValue += alloyChart.Points.Last().TempValue; |
||||||
|
} |
||||||
|
|
||||||
|
alloyPoint.Value = alloyPoint.TempValue - economySpending; |
||||||
|
|
||||||
|
highestAlloyPoint = Math.Max(highestAlloyPoint, alloyPoint.Value); |
||||||
|
|
||||||
|
alloyChart.Points.Add(alloyPoint); |
||||||
|
} |
||||||
|
|
||||||
|
alloyChart.HighestValuePoint = highestAlloyPoint; |
||||||
|
|
||||||
|
alloyChart.HighestIntervalPoint = economyOverTime.Count(); |
||||||
|
|
||||||
|
charts.Add(alloyChart); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,126 @@ |
|||||||
|
@inject IEconomyComparisonService economyComparisonService |
||||||
|
@implements IDisposable |
||||||
|
|
||||||
|
<div class="differences"> |
||||||
|
<div class="differenceContainer"> |
||||||
|
<div class="differenceTitle"> |
||||||
|
Starting Advantage |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
At Time: @StartingAdvantageAtTime | T @Interval.ToTime(StartingAdvantageAtTime) |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="differenceContainer"> |
||||||
|
<div class="differenceTitle"> |
||||||
|
Peak Advantage |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
By Alloy: @PeakAdvantageByAlloy |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
At Time: @PeakAdvantageAtTime | T @Interval.ToTime(PeakAdvantageAtTime) |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="differenceContainer"> |
||||||
|
<div class="differenceTitle"> |
||||||
|
Worsening Time |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
At Time: @WorseningTime | T @Interval.ToTime(WorseningTime) |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
<div class="differenceContainer"> |
||||||
|
<div class="differenceTitle"> |
||||||
|
Miracle Time |
||||||
|
</div> |
||||||
|
<div> |
||||||
|
At Time: @MiracleTime | T @Interval.ToTime(MiracleTime) |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
</div> |
||||||
|
|
||||||
|
|
||||||
|
<style> |
||||||
|
.differences { |
||||||
|
display: flex; |
||||||
|
flex-direction: column; |
||||||
|
gap: 12px; |
||||||
|
padding: 12px; |
||||||
|
} |
||||||
|
|
||||||
|
.differenceTitle { |
||||||
|
font-size: 1.2em; |
||||||
|
font-weight: 800; |
||||||
|
} |
||||||
|
|
||||||
|
.differenceContainer { |
||||||
|
|
||||||
|
} |
||||||
|
</style> |
||||||
|
|
||||||
|
@code { |
||||||
|
private int StartingAdvantageAtTime = 0; |
||||||
|
|
||||||
|
private int PeakAdvantageByAlloy = 0; |
||||||
|
private int PeakAdvantageAtTime = 0; |
||||||
|
|
||||||
|
private int WorseningTime = 0; |
||||||
|
private int MiracleTime = 0; |
||||||
|
|
||||||
|
protected override void OnInitialized() |
||||||
|
{ |
||||||
|
economyComparisonService.Subscribe(CalculateDifferences); |
||||||
|
} |
||||||
|
|
||||||
|
void IDisposable.Dispose() |
||||||
|
{ |
||||||
|
economyComparisonService.Unsubscribe(CalculateDifferences); |
||||||
|
} |
||||||
|
|
||||||
|
void CalculateDifferences() |
||||||
|
{ |
||||||
|
PeakAdvantageByAlloy = 0; |
||||||
|
StartingAdvantageAtTime = 0; |
||||||
|
WorseningTime = 0; |
||||||
|
|
||||||
|
for (int interval = 0; interval < economyComparisonService.BuildsToCompare[0].EconomyOverTimeModel.Count; interval++) |
||||||
|
{ |
||||||
|
var yourEconomy = economyComparisonService.BuildsToCompare[0].EconomyOverTimeModel[interval]; |
||||||
|
var theirEconomy = economyComparisonService.BuildsToCompare[1].EconomyOverTimeModel[interval]; |
||||||
|
|
||||||
|
var deltaEconomy = yourEconomy.Alloy - theirEconomy.Alloy; |
||||||
|
if (deltaEconomy >= 0) |
||||||
|
{ |
||||||
|
if (deltaEconomy > PeakAdvantageByAlloy) |
||||||
|
{ |
||||||
|
if (StartingAdvantageAtTime == 0) |
||||||
|
{ |
||||||
|
StartingAdvantageAtTime = interval; |
||||||
|
} |
||||||
|
|
||||||
|
PeakAdvantageByAlloy = (int)deltaEconomy; |
||||||
|
PeakAdvantageAtTime = interval; |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
|
||||||
|
if (PeakAdvantageByAlloy > 0 && WorseningTime == 0) |
||||||
|
{ |
||||||
|
WorseningTime = interval; |
||||||
|
} |
||||||
|
|
||||||
|
if (deltaEconomy < 1000 && MiracleTime != 0) |
||||||
|
{ |
||||||
|
MiracleTime = interval; |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
StateHasChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
@ -0,0 +1,85 @@ |
|||||||
|
@inject IEconomyComparisonService economyComparisonService |
||||||
|
@implements IDisposable |
||||||
|
|
||||||
|
<FormLayoutComponent> |
||||||
|
<FormSelectComponent OnChange="@OnFactionChanged"> |
||||||
|
<FormLabelComponent>Faction</FormLabelComponent> |
||||||
|
<ChildContent> |
||||||
|
<option value="@DataType.FACTION_Aru" selected="@IsSelected(DataType.FACTION_Aru)">Aru</option> |
||||||
|
<option value="@DataType.FACTION_QRath" selected="@IsSelected(DataType.FACTION_QRath)">Q'Rath</option> |
||||||
|
</ChildContent> |
||||||
|
</FormSelectComponent> |
||||||
|
<ContentDividerComponent/> |
||||||
|
<FormNumberComponent Value="@TownHallCount" OnChange="ChangeTownHallNumber"> |
||||||
|
<FormLabelComponent>Number of TownHall Expansions</FormLabelComponent> |
||||||
|
</FormNumberComponent> |
||||||
|
<ContentDividerComponent/> |
||||||
|
|
||||||
|
@{ |
||||||
|
var index = 0; |
||||||
|
} |
||||||
|
@foreach (var timing in TownHallTimings) |
||||||
|
{ |
||||||
|
index++; |
||||||
|
|
||||||
|
<FormNumberComponent Value="@timing" OnChange="(e)=> ChangeBuildTime(e, index - 1)"> |
||||||
|
<FormLabelComponent> |
||||||
|
TownHall build time |
||||||
|
</FormLabelComponent> |
||||||
|
</FormNumberComponent> |
||||||
|
} |
||||||
|
<ContentDividerComponent/> |
||||||
|
<FormTextComponent Label="Chart Color" Value="@ChartColor" OnChange="ChangeColor" /> |
||||||
|
</FormLayoutComponent> |
||||||
|
|
||||||
|
|
||||||
|
<style> |
||||||
|
|
||||||
|
</style> |
||||||
|
|
||||||
|
@code { |
||||||
|
|
||||||
|
[Parameter] |
||||||
|
public int ForPlayer { get; set; } |
||||||
|
|
||||||
|
private int TownHallCount => economyComparisonService.GetTownHallCount(ForPlayer); |
||||||
|
private string ChartColor => economyComparisonService.GetColor(ForPlayer); |
||||||
|
private string Faction => economyComparisonService.GetFaction(ForPlayer); |
||||||
|
private List<int> TownHallTimings => economyComparisonService.GetTownHallBuildTimes(ForPlayer); |
||||||
|
|
||||||
|
protected override void OnInitialized() |
||||||
|
{ |
||||||
|
economyComparisonService.Subscribe(StateHasChanged); |
||||||
|
} |
||||||
|
|
||||||
|
void IDisposable.Dispose() |
||||||
|
{ |
||||||
|
economyComparisonService.Unsubscribe(StateHasChanged); |
||||||
|
} |
||||||
|
|
||||||
|
private void OnFactionChanged(ChangeEventArgs obj) |
||||||
|
{ |
||||||
|
throw new NotImplementedException(); |
||||||
|
} |
||||||
|
|
||||||
|
private bool IsSelected(string factionType) |
||||||
|
{ |
||||||
|
return Faction.Equals(factionType); |
||||||
|
} |
||||||
|
|
||||||
|
private void ChangeColor(ChangeEventArgs obj) |
||||||
|
{ |
||||||
|
economyComparisonService.ChangeColor(ForPlayer, obj.Value!.ToString()!); |
||||||
|
} |
||||||
|
|
||||||
|
private void ChangeTownHallNumber(ChangeEventArgs obj) |
||||||
|
{ |
||||||
|
economyComparisonService.ChangeNumberOfTownHalls(ForPlayer, (int)obj.Value!); |
||||||
|
} |
||||||
|
|
||||||
|
private void ChangeBuildTime(ChangeEventArgs obj, int index) |
||||||
|
{ |
||||||
|
economyComparisonService.ChangeTownHallTiming(ForPlayer, index, (int)obj.Value!); |
||||||
|
} |
||||||
|
|
||||||
|
} |
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@ -1 +1 @@ |
|||||||
[{"Id":1,"Name":"Database UX Patch","Date":"2022-03-13T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":2,"Name":"Thrum Stats Hotfix","Date":"2022-03-12T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":3,"Name":"Memory Tester Patch","Date":"2022-03-01T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":4,"Name":"Hide Pyre Hotfix","Date":"2022-02-20T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":5,"Name":"Stream Patch","Date":"2022-02-20T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":6,"Name":"Agile UI Hotfix","Date":"2022-02-20T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":7,"Name":"Armor Patch","Date":"2022-02-19T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":8,"Name":"Home Page Patch","Date":"2022-02-19T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":9,"Name":"Mobile Menu Hotfix 2","Date":"2022-02-19T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":10,"Name":"Mobile Menu Hotfix","Date":"2022-02-19T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":11,"Name":"Mobile Menu Patch","Date":"2022-02-19T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":12,"Name":"0.0.6.8375a Patch","Date":"2022-02-18T00:00:00","GitChangeModels":[],"Important":"True"},{"Id":13,"Name":"Google Tracking Hotfix","Date":"2022-02-18T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":14,"Name":"Privacy Policy Patch","Date":"2022-02-17T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":15,"Name":"Home Page Quick Hotfix","Date":"2022-02-16T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":16,"Name":"Early Agile Patch","Date":"2022-02-16T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":17,"Name":"Form Text Rendering Hotfix","Date":"2022-02-15T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":18,"Name":"Reducing Timing Interval Hotfix","Date":"2022-02-15T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":19,"Name":"Changelog Patch","Date":"2022-02-14T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":20,"Name":"SQL Patch","Date":"2022-03-26T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":21,"Name":"Stream Patch","Date":"2022-03-30T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":22,"Name":"0.0.6.8900a Patch","Date":"2022-03-30T00:00:00","GitChangeModels":[],"Important":"True"},{"Id":23,"Name":"Database Links Patch","Date":"2022-04-01T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":24,"Name":"Open Source Patch","Date":"2022-04-03T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":25,"Name":"Stream Patch","Date":"2022-04-03T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":26,"Name":"Notes/Docs Patch","Date":"2022-04-10T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":27,"Name":"Stream Patch","Date":"2022-04-10T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":28,"Name":"Passive Patch","Date":"2022-04-12T00:00:00","GitChangeModels":[],"Important":"True"},{"Id":29,"Name":"0.0.6.9121a Patch","Date":"2022-04-13T00:00:00","GitChangeModels":[],"Important":"True"},{"Id":30,"Name":"Stream Patch","Date":"2022-04-13T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":31,"Name":"BuildCalc Hotfix","Date":"2022-04-13T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":32,"Name":"Search Patch","Date":"2022-04-16T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":33,"Name":"Search Hotfix","Date":"2022-04-16T00:00:00","GitChangeModels":[],"Important":"False"}] |
[{"Id":1,"Name":"Database UX Patch","Date":"2022-03-13T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":2,"Name":"Thrum Stats Hotfix","Date":"2022-03-12T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":3,"Name":"Memory Tester Patch","Date":"2022-03-01T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":4,"Name":"Hide Pyre Hotfix","Date":"2022-02-20T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":5,"Name":"Stream Patch","Date":"2022-02-20T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":6,"Name":"Agile UI Hotfix","Date":"2022-02-20T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":7,"Name":"Armor Patch","Date":"2022-02-19T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":8,"Name":"Home Page Patch","Date":"2022-02-19T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":9,"Name":"Mobile Menu Hotfix 2","Date":"2022-02-19T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":10,"Name":"Mobile Menu Hotfix","Date":"2022-02-19T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":11,"Name":"Mobile Menu Patch","Date":"2022-02-19T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":12,"Name":"0.0.6.8375a Patch","Date":"2022-02-18T00:00:00","GitChangeModels":[],"Important":"True"},{"Id":13,"Name":"Google Tracking Hotfix","Date":"2022-02-18T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":14,"Name":"Privacy Policy Patch","Date":"2022-02-17T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":15,"Name":"Home Page Quick Hotfix","Date":"2022-02-16T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":16,"Name":"Early Agile Patch","Date":"2022-02-16T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":17,"Name":"Form Text Rendering Hotfix","Date":"2022-02-15T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":18,"Name":"Reducing Timing Interval Hotfix","Date":"2022-02-15T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":19,"Name":"Changelog Patch","Date":"2022-02-14T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":20,"Name":"SQL Patch","Date":"2022-03-26T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":21,"Name":"Stream Patch","Date":"2022-03-30T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":22,"Name":"0.0.6.8900a Patch","Date":"2022-03-30T00:00:00","GitChangeModels":[],"Important":"True"},{"Id":23,"Name":"Database Links Patch","Date":"2022-04-01T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":24,"Name":"Open Source Patch","Date":"2022-04-03T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":25,"Name":"Stream Patch","Date":"2022-04-03T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":26,"Name":"Notes/Docs Patch","Date":"2022-04-10T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":27,"Name":"Stream Patch","Date":"2022-04-10T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":28,"Name":"Passive Patch","Date":"2022-04-12T00:00:00","GitChangeModels":[],"Important":"True"},{"Id":29,"Name":"0.0.6.9121a Patch","Date":"2022-04-13T00:00:00","GitChangeModels":[],"Important":"True"},{"Id":30,"Name":"Stream Patch","Date":"2022-04-13T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":31,"Name":"BuildCalc Hotfix","Date":"2022-04-13T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":32,"Name":"Search Patch","Date":"2022-04-16T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":33,"Name":"Search Hotfix","Date":"2022-04-16T00:00:00","GitChangeModels":[],"Important":"False"},{"Id":34,"Name":"Stream Patch","Date":"2022-04-17T00:00:00","GitChangeModels":[],"Important":"False"}] |
||||||
@ -1,13 +0,0 @@ |
|||||||
using System.Collections.Generic; |
|
||||||
|
|
||||||
namespace Model.BuildOrders; |
|
||||||
|
|
||||||
public class BuildComparisonModel |
|
||||||
{ |
|
||||||
public List<BuildOrderModel> Builds { get; set; } = new() |
|
||||||
{ |
|
||||||
new BuildOrderModel(), |
|
||||||
new BuildOrderModel(), |
|
||||||
new BuildOrderModel() |
|
||||||
}; |
|
||||||
} |
|
||||||
@ -0,0 +1,54 @@ |
|||||||
|
using System; |
||||||
|
using System.Collections.Generic; |
||||||
|
using System.Linq; |
||||||
|
using Model.Economy; |
||||||
|
using Model.Entity; |
||||||
|
using Model.Entity.Data; |
||||||
|
|
||||||
|
namespace Model.BuildOrders; |
||||||
|
|
||||||
|
public class BuildToCompareModel |
||||||
|
{ |
||||||
|
public string Faction { get; set; } |
||||||
|
public EntityModel GetTownHallEntity => DATA.Get()[ |
||||||
|
Faction.Equals(DataType.FACTION_QRath) ? DataType.BUILDING_Acropolis |
||||||
|
: DataType.BUILDING_GroveHeart]; |
||||||
|
|
||||||
|
public EntityModel GetTownHallMining2Entity => DATA.Get()[ |
||||||
|
Faction.Equals(DataType.FACTION_QRath) ? DataType.BUPGRADE_MiningLevel2_QRath |
||||||
|
: DataType.BUPGRADE_MiningLevel2_Aru]; |
||||||
|
|
||||||
|
public EntityModel GetTownHallMining3Entity => DATA.Get()[ |
||||||
|
Faction.Equals(DataType.FACTION_QRath) ? DataType.BUPGRADE_MiningLevel3_QRath |
||||||
|
: DataType.BUPGRADE_MiningLevel3_Aru]; |
||||||
|
|
||||||
|
|
||||||
|
private int numberOfTownHallExpansions = 0; |
||||||
|
public int NumberOfTownHallExpansions |
||||||
|
{ |
||||||
|
get => numberOfTownHallExpansions; |
||||||
|
set |
||||||
|
{ |
||||||
|
if (value >= 0 && value < 6 && value != numberOfTownHallExpansions) |
||||||
|
{ |
||||||
|
numberOfTownHallExpansions = value; |
||||||
|
while (TimeToBuildTownHall.Count < numberOfTownHallExpansions) |
||||||
|
{ |
||||||
|
TimeToBuildTownHall.Add((TimeToBuildTownHall.Count + 1) * 30); |
||||||
|
} |
||||||
|
while (TimeToBuildTownHall.Count > numberOfTownHallExpansions) |
||||||
|
{ |
||||||
|
TimeToBuildTownHall.Remove(TimeToBuildTownHall.Last()); |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
public List<int> TimeToBuildTownHall { get; set; } = new(); |
||||||
|
|
||||||
|
public List<EconomyModel> EconomyOverTimeModel { get; set; } = new(); |
||||||
|
|
||||||
|
public BuildOrderModel BuildOrderModel { get; set; } = new(); |
||||||
|
|
||||||
|
public string ChartColor { get; set; } |
||||||
|
} |
||||||
@ -0,0 +1,284 @@ |
|||||||
|
using Model.BuildOrders; |
||||||
|
using Model.Economy; |
||||||
|
using Model.Entity; |
||||||
|
using Model.Entity.Data; |
||||||
|
using Model.Types; |
||||||
|
|
||||||
|
namespace Services.Immortal; |
||||||
|
|
||||||
|
public class EconomyComparisionService : IEconomyComparisonService |
||||||
|
{ |
||||||
|
public List<BuildToCompareModel> BuildsToCompare { get; set; } |
||||||
|
|
||||||
|
public EconomyComparisionService() |
||||||
|
{ |
||||||
|
BuildsToCompare = new List<BuildToCompareModel>() |
||||||
|
{ |
||||||
|
new BuildToCompareModel { NumberOfTownHallExpansions = 0, Faction = DataType.FACTION_Aru, ChartColor = "green"}, |
||||||
|
new BuildToCompareModel { NumberOfTownHallExpansions = 0, Faction = DataType.FACTION_Aru, ChartColor = "red"} |
||||||
|
}; |
||||||
|
|
||||||
|
BuildsToCompare[0].EconomyOverTimeModel = CalculateEconomy(BuildsToCompare[0], 0); |
||||||
|
BuildsToCompare[1].EconomyOverTimeModel = CalculateEconomy(BuildsToCompare[1], 0); |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
void CalculateBuildOrder(BuildToCompareModel buildToCompare) |
||||||
|
{ |
||||||
|
buildToCompare.BuildOrderModel = new BuildOrderModel(buildToCompare.Faction); |
||||||
|
|
||||||
|
foreach (var time in buildToCompare.TimeToBuildTownHall) |
||||||
|
{ |
||||||
|
var townHall = buildToCompare.GetTownHallEntity; |
||||||
|
var townHallMining2 = buildToCompare.GetTownHallMining2Entity; |
||||||
|
var townHallMining3 = buildToCompare.GetTownHallMining3Entity; |
||||||
|
|
||||||
|
Add(townHall, buildToCompare, time); |
||||||
|
Add(townHallMining2, buildToCompare, time + townHall.Production()!.BuildTime); |
||||||
|
Add(townHallMining3, buildToCompare, time + townHall.Production()!.BuildTime + townHallMining2.Production()!.BuildTime); |
||||||
|
} |
||||||
|
|
||||||
|
CalculateEconomy(buildToCompare, 0); |
||||||
|
} |
||||||
|
|
||||||
|
public void Add(EntityModel entityModel, BuildToCompareModel buildToCompare, int atInterval) |
||||||
|
{ |
||||||
|
BuildOrderModel buildOrder = buildToCompare.BuildOrderModel; |
||||||
|
|
||||||
|
|
||||||
|
if (!buildOrder.StartedOrders.ContainsKey(atInterval)) |
||||||
|
buildOrder.StartedOrders.Add(atInterval, new List<EntityModel>()); |
||||||
|
|
||||||
|
var production = entityModel.Production(); |
||||||
|
|
||||||
|
var completedTime = atInterval; |
||||||
|
if (production != null) completedTime += production.BuildTime; |
||||||
|
|
||||||
|
if (!buildOrder.CompletedOrders.ContainsKey(completedTime)) |
||||||
|
buildOrder.CompletedOrders.Add(completedTime, new List<EntityModel>()); |
||||||
|
|
||||||
|
buildOrder.StartedOrders[atInterval].Add(entityModel.Clone()); |
||||||
|
buildOrder.CompletedOrders[completedTime].Add(entityModel.Clone()); |
||||||
|
|
||||||
|
NotifyDataChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
private int IntervalMax = 1024; |
||||||
|
|
||||||
|
private List<EconomyModel> CalculateEconomy(BuildToCompareModel buildToCompare, int fromInterval = 0) |
||||||
|
{ |
||||||
|
BuildOrderModel buildOrder = buildToCompare.BuildOrderModel; |
||||||
|
|
||||||
|
List<EconomyModel> buildEconomyOverTime = buildToCompare.EconomyOverTimeModel; |
||||||
|
|
||||||
|
while (buildEconomyOverTime.Count < IntervalMax) |
||||||
|
buildEconomyOverTime.Add(new EconomyModel { Interval = buildEconomyOverTime.Count - 1 }); |
||||||
|
|
||||||
|
for (var interval = fromInterval; interval < IntervalMax; interval++) |
||||||
|
{ |
||||||
|
var economyAtSecond = buildEconomyOverTime[interval]; |
||||||
|
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.Harvesters = buildEconomyOverTime[interval - 1].Harvesters.ToList(); |
||||||
|
economyAtSecond.CreatingWorkerDelays = buildEconomyOverTime[interval - 1].CreatingWorkerDelays.ToList(); |
||||||
|
} |
||||||
|
|
||||||
|
economyAtSecond.Interval = interval; |
||||||
|
|
||||||
|
// Add funds |
||||||
|
float freeWorkers = economyAtSecond.WorkerCount - economyAtSecond.BusyWorkerCount; |
||||||
|
var workersNeeded = 0; |
||||||
|
|
||||||
|
economyAtSecond.Harvesters = |
||||||
|
(from harvester in buildOrder.GetHarvestersCompletedBefore(interval) |
||||||
|
select harvester).ToList(); |
||||||
|
|
||||||
|
// Add funds |
||||||
|
economyAtSecond.Pyre += 1; |
||||||
|
|
||||||
|
// Add funds |
||||||
|
foreach (var entity in economyAtSecond.Harvesters) |
||||||
|
{ |
||||||
|
var harvester = entity.Harvest(); |
||||||
|
if (harvester.RequiresWorker) |
||||||
|
if (harvester.Resource == ResourceType.Alloy) |
||||||
|
{ |
||||||
|
var usedWorkers = Math.Min(harvester.Slots, freeWorkers); |
||||||
|
economyAtSecond.Alloy += harvester.HarvestedPerInterval * usedWorkers; |
||||||
|
freeWorkers -= usedWorkers; |
||||||
|
|
||||||
|
if (usedWorkers < harvester.Slots) workersNeeded += 1; |
||||||
|
} |
||||||
|
|
||||||
|
if (harvester.RequiresWorker == false) |
||||||
|
{ |
||||||
|
if (harvester.Resource == ResourceType.Ether) |
||||||
|
economyAtSecond.Ether += harvester.HarvestedPerInterval * harvester.Slots; |
||||||
|
|
||||||
|
if (harvester.Resource == ResourceType.Alloy) |
||||||
|
economyAtSecond.Alloy += harvester.HarvestedPerInterval * harvester.Slots; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Create new worker |
||||||
|
if (economyAtSecond.CreatingWorkerCount > 0) |
||||||
|
for (var i = 0; i < economyAtSecond.CreatingWorkerDelays.Count; i++) |
||||||
|
if (economyAtSecond.CreatingWorkerDelays[i] > 0) |
||||||
|
{ |
||||||
|
if (economyAtSecond.Alloy > 2.5f) |
||||||
|
{ |
||||||
|
economyAtSecond.Alloy -= 2.5f; |
||||||
|
economyAtSecond.CreatingWorkerDelays[i]--; |
||||||
|
} |
||||||
|
} |
||||||
|
else |
||||||
|
{ |
||||||
|
economyAtSecond.CreatingWorkerCount -= 1; |
||||||
|
economyAtSecond.WorkerCount += 1; |
||||||
|
economyAtSecond.CreatingWorkerDelays.Remove(i); |
||||||
|
i--; |
||||||
|
} |
||||||
|
|
||||||
|
if (workersNeeded > economyAtSecond.CreatingWorkerCount) |
||||||
|
{ |
||||||
|
economyAtSecond.CreatingWorkerCount += 1; |
||||||
|
economyAtSecond.CreatingWorkerDelays.Add(50); |
||||||
|
} |
||||||
|
|
||||||
|
// Remove Funds from Build Order |
||||||
|
|
||||||
|
if (buildOrder.StartedOrders.TryGetValue(interval, out var ordersAtTime)) |
||||||
|
foreach (var order in ordersAtTime) |
||||||
|
{ |
||||||
|
var foundEntity = EntityModel.GetDictionary()[order.DataType]; |
||||||
|
var production = foundEntity.Production(); |
||||||
|
|
||||||
|
if (production != null) |
||||||
|
{ |
||||||
|
economyAtSecond.Alloy -= production.Alloy; |
||||||
|
economyAtSecond.Ether -= production.Ether; |
||||||
|
economyAtSecond.Pyre -= production.Pyre; |
||||||
|
var finishedAt = interval + production.BuildTime; |
||||||
|
|
||||||
|
if (production.RequiresWorker) economyAtSecond.BusyWorkerCount += 1; |
||||||
|
|
||||||
|
if (production.ConsumesWorker) economyAtSecond.WorkerCount -= 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
// Handle new entities |
||||||
|
if (buildOrder.CompletedOrders.TryGetValue(interval, out var completedAtInterval)) |
||||||
|
foreach (var newEntity in completedAtInterval) |
||||||
|
{ |
||||||
|
var harvest = newEntity; |
||||||
|
if (harvest != null) economyAtSecond.Harvesters.Add(harvest); |
||||||
|
|
||||||
|
var production = newEntity.Production(); |
||||||
|
if (production != null && production.RequiresWorker) economyAtSecond.BusyWorkerCount -= 1; |
||||||
|
} |
||||||
|
} |
||||||
|
|
||||||
|
return buildEconomyOverTime; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
public void ChangeNumberOfTownHalls(int forPlayer, int toCount) |
||||||
|
{ |
||||||
|
if (BuildsToCompare[forPlayer].NumberOfTownHallExpansions == toCount) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
BuildsToCompare[forPlayer].NumberOfTownHallExpansions = toCount; |
||||||
|
|
||||||
|
CalculateBuildOrder(BuildsToCompare[forPlayer]); |
||||||
|
|
||||||
|
NotifyDataChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
public void ChangeTownHallTiming(int forPlayer, int forTownHall, int toTiming) |
||||||
|
{ |
||||||
|
if (BuildsToCompare[forPlayer].TimeToBuildTownHall[forTownHall] == toTiming) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
BuildsToCompare[forPlayer].TimeToBuildTownHall[forTownHall] = toTiming; |
||||||
|
|
||||||
|
CalculateBuildOrder(BuildsToCompare[forPlayer]); |
||||||
|
|
||||||
|
NotifyDataChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
public int GetTownHallCount(int forPlayer) |
||||||
|
{ |
||||||
|
return BuildsToCompare[forPlayer].NumberOfTownHallExpansions; |
||||||
|
} |
||||||
|
|
||||||
|
public int GetTownHallBuildTime(int forPlayer, int forTownHall) |
||||||
|
{ |
||||||
|
return BuildsToCompare[forPlayer].TimeToBuildTownHall[forTownHall]; |
||||||
|
} |
||||||
|
|
||||||
|
public List<int> GetTownHallBuildTimes(int forPlayer) |
||||||
|
{ |
||||||
|
return BuildsToCompare[forPlayer].TimeToBuildTownHall; |
||||||
|
} |
||||||
|
|
||||||
|
public void ChangeFaction(int forPlayer, string toFaction) |
||||||
|
{ |
||||||
|
if (BuildsToCompare[forPlayer].Faction.Equals(toFaction)) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
BuildsToCompare[forPlayer].Faction = toFaction; |
||||||
|
NotifyDataChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
public string GetFaction(int forPlayer) |
||||||
|
{ |
||||||
|
return BuildsToCompare[forPlayer].Faction; |
||||||
|
} |
||||||
|
|
||||||
|
public void ChangeColor(int forPlayer, string toColor) |
||||||
|
{ |
||||||
|
if (BuildsToCompare[forPlayer].ChartColor.Equals(toColor)) |
||||||
|
{ |
||||||
|
return; |
||||||
|
} |
||||||
|
|
||||||
|
BuildsToCompare[forPlayer].ChartColor = toColor; |
||||||
|
NotifyDataChanged(); |
||||||
|
} |
||||||
|
|
||||||
|
public string GetColor(int forPlayer) |
||||||
|
{ |
||||||
|
return BuildsToCompare[forPlayer].ChartColor; |
||||||
|
} |
||||||
|
|
||||||
|
public void Subscribe(Action action) |
||||||
|
{ |
||||||
|
OnChange += action; |
||||||
|
} |
||||||
|
|
||||||
|
public void Unsubscribe(Action action) |
||||||
|
{ |
||||||
|
OnChange -= action; |
||||||
|
} |
||||||
|
|
||||||
|
|
||||||
|
private event Action OnChange = null!; |
||||||
|
|
||||||
|
private void NotifyDataChanged() |
||||||
|
{ |
||||||
|
OnChange(); |
||||||
|
} |
||||||
|
} |
||||||
Loading…
Reference in new issue