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