Browse Source

Merge branch 'develop'

main
Jonathan McCaffrey 4 years ago
parent
commit
c921003483
  1. 16
      Components/Components.csproj
  2. 2
      Components/Form/FormNumberComponent.razor
  3. 17
      Components/Form/FormToggleComponent.razor
  4. 8
      Components/Inputs/CodeLinkComponent.razor
  5. 2
      Components/Inputs/EditLinkComponent.razor
  6. 35
      Components/Inputs/LinkButtonComponent.razor
  7. 27
      Components/Inputs/SearchButtonComponent.razor
  8. 16
      Components/Inputs/SearchIconButtonComponent.razor
  9. 4
      Components/Layout/WebsiteTitleComponent.razor
  10. 14
      Components/Navigation/DesktopNavComponent.razor
  11. 10
      Components/Navigation/MobileNavComponent.razor
  12. 4
      Components/Navigation/TabletNavComponent.razor
  13. 5
      Components/Utils/Links.cs
  14. BIN
      IGP/Database.db
  15. 22
      IGP/Dialog/SearchDialogComponent.razor
  16. 5
      IGP/Index.razor
  17. 117
      IGP/Pages/Agile/AgilePage.razor
  18. 66
      IGP/Pages/Agile/AgilePage.razor.css
  19. 183
      IGP/Pages/Agile/Parts/BacklogComponent.razor
  20. 224
      IGP/Pages/Agile/Parts/SprintComponent.razor
  21. 11
      IGP/Pages/BasePage.razor
  22. 9
      IGP/Pages/BuildCalculator/BuildCalculatorPage.razor
  23. 42
      IGP/Pages/BuildCalculator/Parts/ArmyComponent.razor
  24. 15
      IGP/Pages/BuildCalculator/Parts/BuildChartComponent.razor
  25. 5
      IGP/Pages/BuildCalculator/Parts/EntityClickViewComponent.razor
  26. 32
      IGP/Pages/BuildCalculator/Parts/FilterComponent.razor
  27. 2
      IGP/Pages/BuildCalculator/Parts/HighlightsComponent.razor
  28. 165
      IGP/Pages/BuildCalculator/Parts/HotkeyViewerComponent.razor
  29. 6
      IGP/Pages/BuildCalculator/Parts/InputPanelComponent.razor
  30. 7
      IGP/Pages/BuildCalculator/Parts/OptionsComponent.razor
  31. 2
      IGP/Pages/BuildCalculator/Parts/TimingComponent.razor
  32. 133
      IGP/Pages/ChangeLogPage.razor
  33. 13
      IGP/Pages/ChangeLogPage.razor.css
  34. 106
      IGP/Pages/Comparision/ComparisionPage.razor
  35. 37
      IGP/Pages/Comparision/Parts/BuildLoaderComponent.razor
  36. 25
      IGP/Pages/Comparision/Parts/SandComponent.razor
  37. 5
      IGP/Pages/Database/DatabaseSinglePage.razor
  38. 11
      IGP/Pages/Database/Parts/EntityFilterComponent.razor
  39. 117
      IGP/Pages/Documentation/DocumentationIndexPage.razor
  40. 123
      IGP/Pages/Documentation/DocumentationPage.razor
  41. 6
      IGP/Pages/Documentation/Parts/DocumentComponent.razor
  42. 70
      IGP/Pages/Documentation/Parts/DocumentInnerNavComponent.razor
  43. 47
      IGP/Pages/Documentation/Parts/DocumentNavComponent.razor
  44. 4
      IGP/Pages/EconomyComparison/EconomyComparisonPage.razor
  45. 12
      IGP/Pages/EconomyComparison/Parts/ChartComponent.razor
  46. 15
      IGP/Pages/EconomyComparison/Parts/EconomyDifferenceComponent.razor
  47. 20
      IGP/Pages/EconomyComparison/Parts/EconomyInputComponent.razor
  48. 195
      IGP/Pages/HarassCalculatorPage.razor
  49. 8
      IGP/Pages/Home/HomePage.razor
  50. 88
      IGP/Pages/MakingOf/MakingOfPage.razor
  51. 188
      IGP/Pages/MakingOf/Parts/MakingOfColours.razor
  52. 49
      IGP/Pages/MakingOf/Parts/MakingOfColours.razor.css
  53. 16
      IGP/Pages/MakingOf/Parts/MakingOfDialogs.razor
  54. 188
      IGP/Pages/MakingOf/Parts/MakingOfDisplays.razor
  55. 58
      IGP/Pages/MakingOf/Parts/MakingOfFeedback.razor
  56. 35
      IGP/Pages/MakingOf/Parts/MakingOfForms.razor
  57. 80
      IGP/Pages/MakingOf/Parts/MakingOfNavigation.razor
  58. 7
      IGP/Pages/Notes/NotesIndexPage.razor
  59. 6
      IGP/Pages/Notes/Parts/NoteComponent.razor
  60. 9
      IGP/Pages/PermissionsPage.razor
  61. 19
      IGP/Pages/RoadMap/Parts/RoadMapComponent.razor
  62. 32
      IGP/Pages/RoadMap/RoadMapPage.razor
  63. 24
      IGP/Pages/StoragePage.razor
  64. 22
      IGP/Pages/StreamsPage.razor
  65. 1
      IGP/Portals/SearchPortal.razor
  66. 7
      IGP/_Imports.razor
  67. 2
      IGP/wwwroot/generated/WebSectionModels.json
  68. 3
      IGP/wwwroot/index.html
  69. 2
      Model/BuildOrders/BuildOrderModel.cs
  70. 2
      Model/Chart/PointModel.cs
  71. 241
      Model/Entity/Data/DATA.cs
  72. 17
      Model/Entity/Data/Ids_Entity.cs
  73. 1
      Model/Entity/Parts/EntityFactionModel.cs
  74. 12
      Model/Hotkeys/HotkeyModel.cs
  75. 4
      Model/Model.csproj
  76. 3
      Model/Website/WebSectionModel.cs
  77. 1
      Services/IServices.cs
  78. 17
      Services/Immortal/BuildOrderService.cs
  79. 5
      Services/Immortal/EntityFilterService.cs
  80. 7
      Services/Immortal/ImmortalSelectionService.cs
  81. 12
      Services/Services.csproj
  82. 25
      Services/Website/DataCollectionService.cs
  83. 11
      Services/Website/DialogService.cs
  84. 4
      Services/Website/PermissionService.cs
  85. 8
      Services/Website/SearchService.cs
  86. 1
      Services/Website/StorageService.cs
  87. 5
      TestAutomation/BaseTest.cs
  88. 34
      TestAutomation/Pages/DatabasePage.cs
  89. 15
      TestAutomation/Pages/DatabaseSinglePage.cs
  90. 8
      TestAutomation/Shared/NavigationBar.cs
  91. 3
      TestAutomation/Shared/WebsiteSearchDialog.cs
  92. 22
      TestAutomation/TestAutomation.csproj
  93. 48
      TestAutomation/TestSearchFeatures.cs
  94. 32
      TestAutomation/Utils/Website.cs

16
Components/Components.csproj

@ -15,24 +15,24 @@
</PropertyGroup>
<ItemGroup>
<SupportedPlatform Include="browser" />
<SupportedPlatform Include="browser"/>
</ItemGroup>
<ItemGroup>
<PackageReference Include="Markdig" Version="0.28.1" />
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="7.0.0-preview.2.22153.2" />
<PackageReference Include="Microsoft.Extensions.Localization" Version="7.0.0-preview.2.22153.2" />
<PackageReference Include="Markdig" Version="0.28.1"/>
<PackageReference Include="Microsoft.AspNetCore.Components.Web" Version="7.0.0-preview.2.22153.2"/>
<PackageReference Include="Microsoft.Extensions.Localization" Version="7.0.0-preview.2.22153.2"/>
</ItemGroup>
<ItemGroup>
<Folder Include="Inputs\" />
<Folder Include="Inputs\"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Model\Model.csproj" />
<ProjectReference Include="..\Services\Services.csproj" />
<ProjectReference Include="..\Model\Model.csproj"/>
<ProjectReference Include="..\Services\Services.csproj"/>
</ItemGroup>
<ItemGroup>
<None Remove="Inputs\" />
<None Remove="Inputs\"/>
</ItemGroup>
</Project>

2
Components/Form/FormNumberComponent.razor

@ -5,7 +5,7 @@
}
<div>
<input readonly="@ReadOnly"
id="@Id"
id="@Id"
class="numberInput"
type="number"
min="@Min"

17
Components/Form/FormToggleComponent.razor

@ -4,14 +4,14 @@
@Label:
</div>
<label class="switch">
<input readonly="@ReadOnly"
type="checkbox"
id="@labelId"
class="@ClassStyle"
checked="@Value"
@oninput="OnChange"/>
<input readonly="@ReadOnly"
type="checkbox"
id="@labelId"
class="@ClassStyle"
checked="@Value"
@oninput="OnChange"/>
<span class="slider"></span>
<span class="slider"></span>
</label>
</div>
@if (Info != "")
@ -116,10 +116,11 @@
private string labelId = "";
private string ClassStyle => Value ? "checked" : "";
protected override void OnInitialized()
{
base.OnInitialized();
labelId = Label.ToLower().Replace(" ", "_");
}
}

8
Components/Inputs/CodeLinkComponent.razor

@ -1,16 +1,16 @@
<a href="@Href" class="codeLinkButton">
View on GitHub
<a href="@Href" target="_blank" class="codeLinkButton">
View on GitHub <i class="fa-brands fa-github" style="font-size: 24px; margin-left: 5px;"></i>
</a>
<style>
.codeLinkButton {
color: white;
background-color: var(--info);
border: 1px solid var(--info-border);
border: 2px solid var(--info-border);
padding: 16px;
border-radius: 3px;
display: block;
width: 180px;
width: 200px;
text-align: center;
}

2
Components/Inputs/EditLinkComponent.razor

@ -1,4 +1,4 @@
<a href="@Href" class="editLinkButton">
<a href="@Href" target="_blank" class="editLinkButton">
Edit on GitHub
</a>

35
Components/Inputs/LinkButtonComponent.razor

@ -0,0 +1,35 @@
<a href="@Href" class="linkButtonContainer">
@ChildContent
</a>
<style>
.linkButtonContainer {
padding: 16px;
border: 1px solid;
border-radius: 8px;
font-weight: 800;
font-size: 1.2rem;
border-color: var(--primary);
background-color: var(--primary);
}
.linkButtonContainer:hover {
background-color: var(--primary-hover);
border-color: var(--primary-border-hover);
color: white;
}
</style>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; } = default!;
[Parameter]
public string Href { get; set; } = "";
}

27
Components/Inputs/SearchButtonComponent.razor

@ -4,10 +4,18 @@
<button id="@Id" class="searchButtonContainer" @onclick="ButtonClicked">
<div class="searchText">
Search...
<i class="fa-solid fa-magnifying-glass" style="margin-left: 3px; margin-right: 6px;"></i> Search...
</div>
<div class="searchHotkey">
@CommandKey + K
@if (IsMac)
{
<span><i class="fa-solid fa-command"></i>K</span>
}
else
{
<span>CTRL + K</span>
}
</div>
</button>
@ -28,23 +36,24 @@
}
.searchHotkey {
padding: 2px;
background-color: var(--info);
border: 2px solid var(--primary-border);
padding: 4px;
background-color: rgba(255,255,255,0.05);
border: 2px solid rgba(255,255,255,0.25);
border-radius: 4px;
}
</style>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; } = default!;
[Parameter]
public string Id { get; set; } = default!;
private string userAgent = "";
private string _userAgent = "";
string CommandKey => userAgent.Contains("Mac OS") ? "CMD" : "Ctrl";
bool IsMac => _userAgent.Contains("Mac OS");
private void ButtonClicked(EventArgs eventArgs)
{
@ -53,7 +62,7 @@
protected override async Task OnInitializedAsync()
{
userAgent = await JsRuntime.InvokeAsync<string>("getUserAgent");
_userAgent = await JsRuntime.InvokeAsync<string>("getUserAgent");
}
}

16
Components/Inputs/SearchIconButtonComponent.razor

@ -3,27 +3,32 @@
@inject IJSRuntime JsRuntime
<button id="@Id" class="searchIconButtonContainer" @onclick="ButtonClicked">
<div class="searchText">
S
<div class="searchIcon">
<i class="fa-solid fa-magnifying-glass"></i>
</div>
</button>
<style>
.searchIconButtonContainer {
background-color: var(--primary);
border: 2px solid var(--primary-border);
border-radius: 8px;
font-weight: 800;
width: 32px;
width: 100%;
padding: 5px;
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
}
.searchIcon {
font-size: 28px;
text-align: center;
margin: auto;
}
</style>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; } = default!;
@ -34,4 +39,5 @@
{
SearchService.Show();
}
}

4
Components/Layout/WebsiteTitleComponent.razor

@ -1,6 +1,6 @@
<div class="title">
<h1 class="title">
@ChildContent
</div>
</h1>
<style>

14
Components/Navigation/DesktopNavComponent.razor

@ -13,7 +13,7 @@
<div class="desktopNavContainer">
<div class="menuHeader">
<NavLink id="desktop-homeLink" href="/" class="websiteTitle">
IGP Fan Reference
<i class="fa-solid fa-house" style="margin-right: 4px;"></i> IGP Fan Reference
</NavLink>
<div class="sectionNavs">
@ -27,7 +27,14 @@
}
<div class="sectionNav">
<button onclick="@(() => { MenuClicked(webSection.Id); })" class="@sectionButtonStyle">@webSection.Name</button>
<button onclick="@(() => { MenuClicked(webSection.Id); })" class="@sectionButtonStyle">
<i class="fa-solid @webSection.Icon"></i>
@if (!webSection.OnlyIcon)
{
<span style="margin-left: 12px">@webSection.Name</span>
}
</button>
@if (isSelected)
{
<div class="navMenuPosition">
@ -95,7 +102,7 @@
.sectionNavs {
display: flex;
gap: 8px;
gap: 16px;
align-items: center;
}
@ -140,6 +147,7 @@
box-shadow: 0 2px 3px black;
background-color: var(--info);
transform: translateY(-1px) scale(1.08);
border-radius: 12px;
}
@@media only screen and (max-width: 1025px) {

10
Components/Navigation/MobileNavComponent.razor

@ -1,13 +1,10 @@

<div class="mobileFooter">
<div class="mobileFooter">
<div class="mobileNavSectionsContainer">
@foreach (var webSection in WebSections)
{
<div class="mobileNavSectionButton" @onclick="() => OnSectionClicked(webSection)" @onclick:preventDefault="true" @onclick:stopPropagation="true">
<div class="mobileNavSectionButtonText">
@webSection?.Name
<i class="fa-solid @webSection.Icon" style="font-size: 28px;"></i>
</div>
</div>
}
@ -96,6 +93,7 @@
border: 1px solid var(--primary);
width: 100%;
height: 64px;
border-radius: 2px;
display: flex;
background-color: var(--primary);
cursor: pointer;
@ -146,7 +144,7 @@
@code {
#if NO_SQL
[Parameter]
public List<WebSectionModel?> WebSections { get; set; } = default!;
public List<WebSectionModel> WebSections { get; set; } = default!;
[Parameter]
public List<WebPageModel> WebPages { get; set; } = default!;

4
Components/Navigation/TabletNavComponent.razor

@ -6,11 +6,11 @@
IGP Fan Reference
</div>
<div class="tabletButtons">
<SearchButtonComponent/>
<div class="tabletButton">
<div class="tabletMenuTitle">
Menu
<i class="fa-solid fa-bars" style="font-size: 32px; margin:auto"></i>
</div>
</div>
</div>

5
Components/Utils/Links.cs

@ -4,10 +4,7 @@ public static class Links
{
public static string GetTarget(string link)
{
if (link.StartsWith("https://"))
{
return "_blank";
}
if (link.StartsWith("https://")) return "_blank";
return "_self";
}

BIN
IGP/Database.db

Binary file not shown.

22
IGP/Dialog/SearchDialogComponent.razor

@ -1,5 +1,4 @@
@using System.Timers
@implements IDisposable;
@implements IDisposable;
@inject ISearchService searchService
@inject IJSRuntime jsRuntime
@ -41,7 +40,7 @@
</button>
@if (!searchPoint.Summary.Trim().Equals(""))
{
<i> - @searchPoint.Summary</i>
<i> - @searchPoint.Summary</i>
}
</div>
}
@ -116,8 +115,18 @@
.searchLink {
text-decoration: underline;
}
@@media only screen and (max-width: 1025px) {
.searchContainer {
height: 300px;
}
.searchBox {
height: 230px;
}
}
</style>
}
@ -131,7 +140,7 @@
protected override void OnInitialized()
{
searchService.Subscribe(OnSearchChanged);
timer = new Timer(200);
timer.Elapsed += FocusTimer;
timer.Enabled = false;
@ -143,15 +152,16 @@
jsRuntime.InvokeVoidAsync("SetFocusToElement", "searchInput");
StateHasChanged();
}
private Timer timer = null!;
private void OnSearchChanged()
{
if (timer.Enabled != searchService.IsVisible)
{
timer.Enabled = searchService.IsVisible;
}
StateHasChanged();
StateHasChanged();
}
public void Dispose()

5
IGP/Index.razor

@ -6,7 +6,8 @@
@layout PageLayout
<DevOnlyComponent>
<PermissionsPage/>
<LinkButtonComponent Href="https://github.com/JonathanMcCaffrey/IGP-Fan-Reference/blob/main/IGP/Pages/HarassCalculatorPage.razor">
View on GitHub <i class="fa-brands fa-github" style="font-size: 24px; margin-left: 5px;"></i>
</LinkButtonComponent>
</DevOnlyComponent>
<HomePage/>

117
IGP/Pages/Agile/AgilePage.razor

@ -1,117 +0,0 @@
@implements IDisposable;
@inject IAgileService AgileService;
@layout PageLayout
@inherits BasePage
@page "/agile"
@if (!AgileService.IsLoaded())
{
<LoadingComponent/>
}
else
{
<LayoutMediumContentComponent>
<WebsiteTitleComponent>Agile</WebsiteTitleComponent>
<div class="agileViewContainer">
@foreach (var sprint in AgileService.AgileSprintModels!
.OrderBy(e => e.EndDate).Reverse())
{
<details class="sprintDisplayContainer @sprint.GetSprintType().ToLower()"
open="@(sprint.GetSprintType() == SprintType.Current)">
<summary class="sprintSummary">
<div class="sprintTitle">@sprint.Name</div>
<div style="flex: 1; flex-grow: 1;"></div>
<div class="sprintDates">
<div class="sprintStartDate">
@if (sprint.StartDate != null)
{
<b>Start: </b>
@sprint.StartDate.Value.ToString("dd/MM/yyyy")
}
</div>
<div class="sprintEndDate">
@if (sprint.EndDate != null)
{
<b>End: </b>
@sprint.EndDate.Value.ToString("dd/MM/yyyy")
}
</div>
</div>
</summary>
<SprintComponent AgileSprint="sprint"></SprintComponent>
</details>
}
<details class="sprintDisplayContainer">
<summary class="sprintSummary">
<div class="sprintTitle">Backlog</div>
<div style="flex: 1; flex-grow: 1;"></div>
</summary>
<div>
<BacklogComponent Backlog=backlog></BacklogComponent>
</div>
</details>
</div>
<ContentDividerComponent></ContentDividerComponent>
<PaperComponent>
<InfoBodyComponent>
<InfoQuestionComponent>What is Agile?</InfoQuestionComponent>
<InfoAnswerComponent>
Agile is a work methodology for determing task assignment and release deadlines.
<br/><br/>
My agile practice will be creating tasks in a backlog. Assigning them to weekly sprints. And completing all tasks in the allotted time frame.
<br/><br/>
Any unfinished tasks are moved into the next sprint, or the sprint will be extended by a week.
</InfoAnswerComponent>
</InfoBodyComponent>
</PaperComponent>
</LayoutMediumContentComponent>
}
@code {
private readonly List<AgileTaskModel> backlog = new();
protected override void OnInitialized()
{
base.OnInitialized();
AgileService.Subscribe(HasChanged);
HasChanged();
}
void IDisposable.Dispose()
{
AgileService.Unsubscribe(HasChanged);
}
void HasChanged()
{
if (!AgileService.IsLoaded()) return;
backlog.Clear();
foreach (var task in AgileService.AgileTaskModels!)
{
if (task.AgileSprintModelId == null)
{
backlog.Add(task);
}
}
StateHasChanged();
}
protected override async Task OnInitializedAsync()
{
await AgileService.Load();
}
}

66
IGP/Pages/Agile/AgilePage.razor.css

@ -1,66 +0,0 @@

.agileViewContainer {
display: flex;
gap: 12px;
flex-direction: column;
}
.sprintDisplayContainer {
border: 4px solid var(--paper);
box-shadow: 0px 2px 12px rgba(0, 0, 0, 0.2);
border-radius: 2px;
padding: 25px;
margin: auto;
width: 100%;
background-color: var(--paper);
}
@media only screen and (max-width: 1025px) {
.sprintDisplayContainer {
padding: 2px;
}
}
.sprintSummary {
display: flex;
width: 100%;
}
.sprintDisplayContainer.current {
border-color: #042901;
background-color: var(--paper);
}
.sprintDisplayContainer.planned {
border-color: #2a2000;
background-color: var(--paper);
}
.sprintDisplayContainer.completed {
border-color: #2a2000;
background-color: var(--paper);
}
details .sprintSummary::before {
content: "+";
font-weight: bolder;
font-size: 1.5rem;
padding-right: 8px;
}
details[open] .sprintSummary::before {
content: "-";
}
.sprintTitle {
width: 400px;
font-size: 1.6rem;
font-weight: 800;
}
.sprintDates {
width: 160px;
text-align: right;
}

183
IGP/Pages/Agile/Parts/BacklogComponent.razor

@ -1,183 +0,0 @@
<div class="sprintContainer">
<div class="tasksContainer">
@foreach (var task in Backlog)
{
<div class="taskContainer @task.Status.ToLower()">
<div class="taskName">@task.Name</div>
<div class="taskDetails">
<LayoutRowComponent>
<LayoutColumnComponent>
<div class="taskType">
<b>Type: </b>@task.Task.Replace("_", " ")
</div>
<div class="taskStatus">
<b>Status: </b>@task.Status.Replace("_", " ")
</div>
<div class="taskPriority">
<b>Priority: </b>@task.Priority
</div>
</LayoutColumnComponent>
<LayoutColumnComponent>
@if (task.Finished != null)
{
<div class="taskFinished">
<b>Finished: </b>@task.Finished
</div>
}
<div class="taskCreated">
<b>Created: </b>@task.Created
</div>
</LayoutColumnComponent>
</LayoutRowComponent>
</div>
<div class="taskDescription">
<b>Description: </b>@task.Description
</div>
<div class="taskNotes">
<b>Notes: </b>@task.Notes
</div>
</div>
}
</div>
</div>
<style>
.sprintContainer {
display: flex;
flex-direction: column;
gap: 12px;
padding-top: 16px;
}
@@media only screen and (max-width: 1025px) {
.sprintContainer {
display: flex;
flex-direction: column;
gap: 6px;
border: none;
margin-top: 12px;
box-shadow: none;
padding: 8px;
}
}
.tasksContainer {
grid-area: tasks;
display: flex;
flex-direction: column;
gap: 20px;
padding: 25px;
align-items: stretch;
justify-content: stretch;
justify-items: stretch;
}
.taskContainer {
padding: 25px;
border: 1px dashed rgba(0,0,0,0.6);
box-shadow: 0px 2px 6px rgba(0,0,0,0.1);
}
.taskContainer.@StatusType.In_Progress.ToLower() {
border-color: #030129;
background-color: #2c3a4c;
}
.taskContainer.@StatusType.Todo.ToLower() {
border-color: #2a2000;
background-color: #ffbf0029;
}
.taskContainer.@StatusType.To_Test.ToLower() {
border-color: #030129;
background-color: #2c3a4c;
}
.taskContainer.@StatusType.Canceled.ToLower() {
border-color: #290102;
background-color: #4C2C33;
}
.taskContainer.@StatusType.Done.ToLower() {
border-color: #042901;
background-color: #2E4C2C;
}
.taskName {
font-weight: bold;
grid-area: name;
}
.taskCreated {
grid-area: created;
}
.taskFinished {
grid-area: finished;
}
.taskStatus {
grid-area: status;
}
.taskDescription {
margin-top: 10px;
grid-area: description;
}
.taskNotes {
grid-area: notes;
}
@@media only screen and (max-width: 1025px) {
.tasksContainer {
padding: 0px;
margin-top: 12px;
padding-top: 12px;
border-top: 4px solid rgba(0,0,0,0.4)
}
.taskContainer {
padding: 2px;
border: none;
box-shadow: none;
}
.taskContainer.@StatusType.In_Progress.ToLower() {
border-color: transparent;
background-color: transparent;
}
.taskContainer.@StatusType.Todo.ToLower() {
border-color: transparent;
background-color: transparent;
}
.taskContainer.@StatusType.Canceled.ToLower() {
border-color: transparent;
background-color: transparent;
}
.taskContainer.@StatusType.Done.ToLower() {
border-color: transparent;
background-color: transparent;
}
}
</style>
@code {
[Parameter]
public List<AgileTaskModel> Backlog { get; set; } = default!;
}

224
IGP/Pages/Agile/Parts/SprintComponent.razor

@ -1,224 +0,0 @@
<div class="sprintContainer">
<div class="sprintStatus">
<b>Status: </b>@AgileSprint.GetSprintType()
</div>
<div class="sprintDescription">
<b>Description: </b>@AgileSprint.Description
</div>
<div class="sprintNotes">
<b>Notes: </b>@AgileSprint.Notes
</div>
<div class="tasksContainer">
@if (AgileSprint.AgileTaskModels.Count > 0)
{
@foreach (var task in AgileSprint.AgileTaskModels.OrderBy(x => x.OrderPriority))
{
<div class="taskContainer @task.Status.ToLower() @task.Task.ToLower()">
<div class="taskName">@task.Name</div>
<div class="taskDetails">
<LayoutRowComponent>
<LayoutColumnComponent>
<div class="taskType">
<b>Type: </b>@task.Task.Replace("_", " ")
</div>
<div class="taskStatus">
<b>Status: </b>@task.Status.Replace("_", " ")
</div>
<div class="taskPriority">
<b>Priority: </b>@task.Priority
</div>
</LayoutColumnComponent>
<LayoutColumnComponent>
@if (task.Finished != null)
{
<div class="taskFinished">
<b>Finished: </b>@task.Finished.Value.ToString("dd/MM/yyyy")
</div>
}
<div class="taskCreated">
<b>Created: </b>@task.Created!.Value.ToString("dd/MM/yyyy")
</div>
</LayoutColumnComponent>
</LayoutRowComponent>
</div>
<div class="taskDescription">
<b>Description: </b>@task.Description
</div>
<div class="taskNotes">
<b>Notes: </b>@task.Notes
</div>
</div>
}
}
else
{
<div>Add Tasks...</div>
}
</div>
</div>
<style>
.sprintContainer {
display: flex;
flex-direction: column;
gap: 12px;
padding-top: 16px;
}
.sprintDescription {
grid-area: description;
}
@@media only screen and (max-width: 1025px) {
.sprintContainer {
display: flex;
flex-direction: column;
gap: 6px;
border: none;
margin-top: 12px;
box-shadow: none;
padding: 8px;
}
.sprintStartDate {
text-align: left;
}
.sprintEndDate {
text-align: left;
}
}
.tasksContainer {
grid-area: tasks;
display: flex;
flex-direction: column;
gap: 20px;
padding: 25px;
align-items: stretch;
justify-content: stretch;
justify-items: stretch;
}
.taskContainer {
padding: 25px;
border: 1px dashed rgba(0,0,0,0.6);
box-shadow: 0px 2px 6px rgba(0,0,0,0.1);
}
.taskContainer.@StatusType.In_Progress.ToLower() {
border-color: #030129;
background-color: #2c3a4c;
}
.taskContainer.@StatusType.Todo.ToLower() {
border-color: #2a2000;
background-color: #ffbf0029;
}
.taskContainer.@StatusType.To_Test.ToLower() {
border-color: #030129;
background-color: #2c3a4c;
}
.taskContainer.@StatusType.Canceled.ToLower() {
border-color: #290102;
background-color: #4C2C33;
}
.taskContainer.@StatusType.Done.ToLower() {
border-color: #042901;
background-color: #2E4C2C;
}
.taskContainer.@TaskType.Bug.ToLower() {
border-style: dotted;
border-width: 8px;
}
.taskContainer.@TaskType.Document.ToLower() {
border-style: dashed;
border-width: 2px;
}
.taskName {
font-weight: bold;
grid-area: name;
font-size: 1.2rem;
}
.taskDetails {
font-size: 0.8rem;
}
.taskCreated {
grid-area: created;
}
.taskFinished {
grid-area: finished;
}
.taskStatus {
grid-area: status;
}
.taskDescription {
margin-top: 10px;
grid-area: description;
}
.taskNotes {
grid-area: notes;
}
@@media only screen and (max-width: 1025px) {
.tasksContainer {
padding: 0px;
margin-top: 12px;
padding-top: 12px;
border-top: 4px solid rgba(0,0,0,0.4)
}
.taskContainer {
padding: 2px;
border: none;
box-shadow: none;
}
.taskContainer.@StatusType.In_Progress.ToLower() {
border-color: transparent;
background-color: transparent;
}
.taskContainer.@StatusType.Todo.ToLower() {
border-color: transparent;
background-color: transparent;
}
.taskContainer.@StatusType.Canceled.ToLower() {
border-color: transparent;
background-color: transparent;
}
.taskContainer.@StatusType.Done.ToLower() {
border-color: transparent;
background-color: transparent;
}
}
</style>
@code {
[Parameter]
public AgileSprintModel AgileSprint { get; set; } = default!;
}

11
IGP/Pages/BasePage.razor

@ -1,5 +1,4 @@
@using Services.Website
@inject IDataCollectionService DataCollectionService
@inject NavigationManager NavigationManager
@ -12,26 +11,26 @@
CollectLoadedPage();
}
private void CollectLoadedPage()
{
var skipBaseUri = NavigationManager.Uri.Substring(NavigationManager.BaseUri.Length,
NavigationManager.Uri.Length - NavigationManager.BaseUri.Length);
var splitData = skipBaseUri.Split("/");
var rootUrl = splitData.First();
if (rootUrl.Trim().Equals(""))
{
rootUrl = "home";
}
var eventData = new Dictionary<string, string> { { "page", rootUrl }};
var eventData = new Dictionary<string, string> { { "page", rootUrl } };
if (splitData.Length > 1)
{
eventData["inner-page"] = splitData.Last();
}
DataCollectionService.SendEvent(DataCollectionKeys.PageInitialized, eventData);
}

9
IGP/Pages/BuildCalculator/BuildCalculatorPage.razor

@ -13,7 +13,6 @@
@inject IDataCollectionService DataCollectionService
@page "/build-calculator"
@using Services.Website
@implements IDisposable
@ -221,14 +220,14 @@
protected override void OnInitialized()
{
base.OnInitialized();
EconomyService.Calculate(BuildOrderService, TimingService, 0);
KeyService.Subscribe(HandleClick);
DataCollectionService.SendEvent(
DataCollectionKeys.PageInitialized,
new Dictionary<string, string> {{"page", "build-calculator"}}
DataCollectionKeys.PageInitialized,
new Dictionary<string, string> { { "page", "build-calculator" } }
);
}

42
IGP/Pages/BuildCalculator/Parts/ArmyComponent.razor

@ -19,7 +19,8 @@
<FormDisplayComponent Label="Army units built">
<Display>
<div class="armyCardsContainer">
@foreach (var unit in armyCount) {
@foreach (var unit in armyCount)
{
<div class="armyCard">
<div class="armyCountPosition">
<div class="armyCount">@unit.Value.ToString()x</div>
@ -72,33 +73,39 @@
List<EntityModel> army = new();
protected override void OnInitialized() {
protected override void OnInitialized()
{
base.OnInitialized();
buildOrder.Subscribe(OnBuildOrderChanged);
timingService.Subscribe(StateHasChanged);
}
void IDisposable.Dispose() {
void IDisposable.Dispose()
{
buildOrder.Unsubscribe(OnBuildOrderChanged);
timingService.Unsubscribe(StateHasChanged);
}
protected override bool ShouldRender() {
protected override bool ShouldRender()
{
#if DEBUG
jsRuntime.InvokeVoidAsync("console.time", "ArmyComponent");
#endif
return true;
}
protected override void OnAfterRender(bool firstRender) {
protected override void OnAfterRender(bool firstRender)
{
#if DEBUG
jsRuntime.InvokeVoidAsync("console.timeEnd", "ArmyComponent");
#endif
}
void OnBuildOrderChanged() {
void OnBuildOrderChanged()
{
var armyCountWas = 0;
foreach (var army in armyCount) {
foreach (var army in armyCount)
{
armyCountWas += army.Value;
}
@ -108,14 +115,19 @@
var entitiesOverTime = buildOrder.GetOrders();
foreach (var entitiesAtTime in entitiesOverTime) {
foreach (var entity in entitiesAtTime.Value) {
if (entity.EntityType == EntityType.Army) {
if (!armyCount.TryAdd(entity.Info().Name, 1)) {
foreach (var entitiesAtTime in entitiesOverTime)
{
foreach (var entity in entitiesAtTime.Value)
{
if (entity.EntityType == EntityType.Army)
{
if (!armyCount.TryAdd(entity.Info().Name, 1))
{
armyCount[entity.Info().Name]++;
}
if (entity.Production() != null && entity.Production().BuildTime + entitiesAtTime.Key > lastInterval) {
if (entity.Production() != null && entity.Production().BuildTime + entitiesAtTime.Key > lastInterval)
{
lastInterval = entity.Production().BuildTime + entitiesAtTime.Key;
}
}
@ -124,12 +136,14 @@
//TODO Better
var armyCountIs = 0;
foreach (var army in armyCount) {
foreach (var army in armyCount)
{
armyCountIs += army.Value;
}
if (armyCountWas != armyCountIs) {
if (armyCountWas != armyCountIs)
{
StateHasChanged();
}
}

15
IGP/Pages/BuildCalculator/Parts/BuildChartComponent.razor

@ -13,20 +13,19 @@ else
<div class="chartsContainer">
@foreach (var chart in charts)
{
Dictionary<int, bool> takenPixels = new Dictionary<int, bool>();
var takenPixels = new Dictionary<int, bool>();
<div style="width: @chart.IntervalDisplayMax.ToString()px; height: @chart.ValueDisplayMax.ToString()px">
<div style="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 x = int.Parse(point.GetInterval(chart.HighestIntervalPoint, chart.IntervalDisplayMax));
var x = int.Parse(point.GetInterval(chart.HighestIntervalPoint, chart.IntervalDisplayMax));
if (takenPixels.ContainsKey(x)) continue;
takenPixels.Add(x, true);
<div style="position: absolute;
bottom:@point.GetValue(chart.HighestValuePoint, chart.ValueDisplayMax)px;
left:@point.GetInterval(chart.HighestIntervalPoint, chart.IntervalDisplayMax)px;

5
IGP/Pages/BuildCalculator/Parts/EntityClickViewComponent.razor

@ -3,7 +3,6 @@
@inject IImmortalSelectionService FilterService
@inject IBuildOrderService BuildOrderService
@inject IStorageService StorageService
@using Services.Website
@implements IDisposable
@ -39,10 +38,10 @@
void IDisposable.Dispose()
{
KeyService.Unsubscribe(HandleClick);
StorageService.Unsubscribe(RefreshDefaults);
}
void RefreshDefaults()
{

32
IGP/Pages/BuildCalculator/Parts/FilterComponent.razor

@ -5,10 +5,14 @@
<FormSelectComponent OnChange="@OnFactionChanged">
<FormLabelComponent>Faction</FormLabelComponent>
<ChildContent>
<option value="@DataType.FACTION_Aru"
selected="@(FilterService.GetFaction().Equals(DataType.FACTION_Aru))">Aru</option>
<option value="@DataType.FACTION_QRath"
selected="@(FilterService.GetFaction().Equals(DataType.FACTION_QRath))">Q'Rath</option>
<option value="@DataType.FACTION_Aru"
selected="@(FilterService.GetFaction().Equals(DataType.FACTION_Aru))">
Aru
</option>
<option value="@DataType.FACTION_QRath"
selected="@(FilterService.GetFaction().Equals(DataType.FACTION_QRath))">
Q'Rath
</option>
</ChildContent>
</FormSelectComponent>
@ -17,17 +21,25 @@
<ChildContent>
@if (FilterService.GetFaction() == DataType.FACTION_QRath)
{
<option value="@DataType.IMMORTAL_Orzum"
selected="@(FilterService.GetImmortal().Equals(DataType.IMMORTAL_Orzum))">Orzum</option>
<option value="@DataType.IMMORTAL_Orzum"
selected="@(FilterService.GetImmortal().Equals(DataType.IMMORTAL_Orzum))">
Orzum
</option>
<option value="@DataType.IMMORTAL_Ajari"
selected="@(FilterService.GetImmortal().Equals(DataType.IMMORTAL_Ajari))">Ajari</option>
selected="@(FilterService.GetImmortal().Equals(DataType.IMMORTAL_Ajari))">
Ajari
</option>
}
@if (FilterService.GetFaction() == DataType.FACTION_Aru)
{
<option value="@DataType.IMMORTAL_Mala"
selected="@(FilterService.GetImmortal().Equals(DataType.IMMORTAL_Mala))">Mala</option>
<option value="@DataType.IMMORTAL_Mala"
selected="@(FilterService.GetImmortal().Equals(DataType.IMMORTAL_Mala))">
Mala
</option>
<option value="@DataType.IMMORTAL_Xol"
selected="@(FilterService.GetImmortal().Equals(DataType.IMMORTAL_Xol))">Xol</option>
selected="@(FilterService.GetImmortal().Equals(DataType.IMMORTAL_Xol))">
Xol
</option>
}
</ChildContent>
</FormSelectComponent>

2
IGP/Pages/BuildCalculator/Parts/HighlightsComponent.razor

@ -8,7 +8,7 @@
<div class="highlightsContainer">
<div>
<div>Requested</div>
@foreach (var ordersAtTime in buildOrderService.StartedOrders.Reverse())
{
foreach (var order in ordersAtTime.Value)

165
IGP/Pages/BuildCalculator/Parts/HotkeyViewerComponent.razor

@ -13,8 +13,10 @@
<InputPanelComponent>
<div class="keyContainer">
@foreach (var hotkey in hotkeys) {
if (hotkey.IsHidden) {
@foreach (var hotkey in hotkeys)
{
if (hotkey.IsHidden)
{
continue;
}
@ -31,14 +33,17 @@
var borderRadius = hotkey.PositionY == 0 ? 12 : 0;
var border = "1px solid black";
if (hotkey.KeyText.Equals(key)) {
if (hotkey.KeyText.Equals(key))
{
border = "5px solid black";
}
if (hotkey.KeyText.Equals(controlGroup)) {
if (hotkey.KeyText.Equals(controlGroup))
{
color = "#257525";
}
if (hotkey.KeyText.Equals("SPACE") && KeyService.IsHoldingSpace()) {
if (hotkey.KeyText.Equals("SPACE") && KeyService.IsHoldingSpace())
{
border = "5px solid green";
}
@ -77,23 +82,28 @@
<div @onclick="e => ButtonClicked(e, hotkey)" style="@usedStyle">
@keyText
@foreach (var entity in data.Values) {
if (InvalidKey(entity, hotkey) || InvalidKeyGroup(entity, hotkey) || InvalidHoldSpace(entity)) {
@foreach (var entity in data.Values)
{
if (InvalidKey(entity, hotkey) || InvalidKeyGroup(entity, hotkey) || InvalidHoldSpace(entity))
{
continue;
}
if (InvalidFaction(entity)) {
if (InvalidFaction(entity))
{
continue;
}
if (InvalidVanguard(entity) || InvalidNonVanguard(entity)) {
if (InvalidVanguard(entity) || InvalidNonVanguard(entity))
{
continue;
}
var isVanguard = entity.VanguardAdded() != null;
var style = isVanguard ? "font-weight: bold;" : "";
if (BuildOrderService.WillMeetRequirements(entity) == null) {
if (BuildOrderService.WillMeetRequirements(entity) == null)
{
style += "color:gray; font-style: italic;";
}
@ -137,7 +147,8 @@
private string controlGroup = "C";
private string key = "";
protected override void OnInitialized() {
protected override void OnInitialized()
{
base.OnInitialized();
KeyService.Subscribe(OnKeyPressed);
@ -145,7 +156,8 @@
BuildOrderService.Subscribe(OnBuilderOrderChanged);
}
void IDisposable.Dispose() {
void IDisposable.Dispose()
{
KeyService.Unsubscribe(OnKeyPressed);
FilterService.Unsubscribe(StateHasChanged);
BuildOrderService.Unsubscribe(OnBuilderOrderChanged);
@ -153,14 +165,17 @@
int completedTimeCount = 0;
void OnBuilderOrderChanged() {
if (BuildOrderService.UniqueCompletedTimes.Count != completedTimeCount) {
void OnBuilderOrderChanged()
{
if (BuildOrderService.UniqueCompletedTimes.Count != completedTimeCount)
{
completedTimeCount = BuildOrderService.UniqueCompletedTimes.Count;
StateHasChanged();
}
}
protected override bool ShouldRender() {
protected override bool ShouldRender()
{
#if DEBUG
JsRuntime.InvokeVoidAsync("console.time", "HotKeyViewerComponent");
#endif
@ -168,15 +183,18 @@
return true;
}
protected override void OnAfterRender(bool firstRender) {
protected override void OnAfterRender(bool firstRender)
{
#if DEBUG
JsRuntime.InvokeVoidAsync("console.timeEnd", "HotKeyViewerComponent");
#endif
}
// Move to Filter Service
bool InvalidFaction(EntityModel entity) {
if (entity.Faction() != null && entity.Faction()?.Faction != FilterService.GetFaction() && FilterService.GetFaction() != DataType.Any) {
bool InvalidFaction(EntityModel entity)
{
if (entity.Faction() != null && entity.Faction()?.Faction != FilterService.GetFaction() && FilterService.GetFaction() != DataType.Any)
{
return true;
}
@ -184,10 +202,12 @@
}
// Move to Filter Service
bool InvalidVanguard(EntityModel entity) {
bool InvalidVanguard(EntityModel entity)
{
if (entity.VanguardAdded() != null
&& entity.VanguardAdded()?.ImmortalId != FilterService.GetImmortal()
&& FilterService.GetImmortal() != DataType.Any) {
&& FilterService.GetImmortal() != DataType.Any)
{
return true;
}
@ -195,10 +215,14 @@
}
// Move to Filter Service
bool InvalidNonVanguard(EntityModel entity) {
if (entity.Replaceds().Count > 0) {
foreach (var replaced in entity.Replaceds()) {
if (FilterService.GetImmortal() == replaced.ImmortalId) {
bool InvalidNonVanguard(EntityModel entity)
{
if (entity.Replaceds().Count > 0)
{
foreach (var replaced in entity.Replaceds())
{
if (FilterService.GetImmortal() == replaced.ImmortalId)
{
return true;
}
}
@ -207,90 +231,114 @@
return false;
}
bool InvalidKey(EntityModel entity, HotkeyModel key) {
if (entity.Hotkey()?.Hotkey == key.KeyText) {
bool InvalidKey(EntityModel entity, HotkeyModel key)
{
if (entity.Hotkey()?.Hotkey == key.KeyText)
{
return false;
}
return true;
}
bool InvalidKeyGroup(EntityModel entity, HotkeyModel key) {
if (entity.Hotkey()?.HotkeyGroup == controlGroup) {
bool InvalidKeyGroup(EntityModel entity, HotkeyModel key)
{
if (entity.Hotkey()?.HotkeyGroup == controlGroup)
{
return false;
}
return true;
}
bool InvalidKey(EntityModel entity) {
if (entity.Hotkey()?.Hotkey == key) {
bool InvalidKey(EntityModel entity)
{
if (entity.Hotkey()?.Hotkey == key)
{
return false;
}
return true;
}
bool InvalidKeyGroup(EntityModel entity) {
if (entity.Hotkey()?.HotkeyGroup == controlGroup) {
bool InvalidKeyGroup(EntityModel entity)
{
if (entity.Hotkey()?.HotkeyGroup == controlGroup)
{
return false;
}
return true;
}
bool InvalidHoldSpace(EntityModel entity) {
if (entity.Hotkey()?.HoldSpace == KeyService.IsHoldingSpace()) {
bool InvalidHoldSpace(EntityModel entity)
{
if (entity.Hotkey()?.HoldSpace == KeyService.IsHoldingSpace())
{
return false;
}
return true;
}
void OnKeyPressed() {
void OnKeyPressed()
{
var controlGroupWas = controlGroup;
var keyWas = key;
if (KeyService.GetAllPressedKeys().Contains("Z")) {
if (KeyService.GetAllPressedKeys().Contains("Z"))
{
controlGroup = "Z";
}
if (KeyService.GetAllPressedKeys().Contains("TAB")) {
if (KeyService.GetAllPressedKeys().Contains("TAB"))
{
controlGroup = "TAB";
}
if (KeyService.GetAllPressedKeys().Contains("C")) {
if (KeyService.GetAllPressedKeys().Contains("C"))
{
controlGroup = "C";
}
if (KeyService.GetAllPressedKeys().Contains("D")) {
if (KeyService.GetAllPressedKeys().Contains("D"))
{
controlGroup = "D";
}
if (KeyService.GetAllPressedKeys().Contains("V")) {
if (KeyService.GetAllPressedKeys().Contains("V"))
{
controlGroup = "V";
}
if (KeyService.GetAllPressedKeys().Contains("ALT")) {
if (KeyService.GetAllPressedKeys().Contains("ALT"))
{
controlGroup = "ALT";
}
if (KeyService.GetAllPressedKeys().Contains("SHIFT")) {
if (KeyService.GetAllPressedKeys().Contains("SHIFT"))
{
controlGroup = "SHIFT";
}
if (KeyService.GetAllPressedKeys().Contains("CONTROL")) {
if (KeyService.GetAllPressedKeys().Contains("CONTROL"))
{
controlGroup = "CONTROL";
}
if (KeyService.GetAllPressedKeys().Count > 0) {
if (KeyService.GetAllPressedKeys().Count > 0)
{
key = KeyService.GetAllPressedKeys().First();
}
if (controlGroupWas != controlGroup || keyWas != key) {
if (controlGroupWas != controlGroup || keyWas != key)
{
StateHasChanged();
}
}
private void HandleClick() {
private void HandleClick()
{
var hotkey = KeyService.GetHotkey();
if (hotkey == "") {
if (hotkey == "")
{
return;
}
if (hotkey == "`") {
if (hotkey == "`")
{
BuildOrderService.RemoveLast();
EconomyService.Calculate(BuildOrderService, TimingService, BuildOrderService.GetLastRequestInterval());
return;
@ -303,31 +351,38 @@
var entity = EntityModel.GetFrom(hotkey!, hotkeyGroup, isHoldSpace, faction, immortal);
if (entity == null) {
if (entity == null)
{
return;
}
if (BuildOrderService.Add(entity, EconomyService)) {
if (BuildOrderService.Add(entity, EconomyService))
{
EconomyService.Calculate(BuildOrderService, TimingService, BuildOrderService.GetLastRequestInterval());
}
}
private void ButtonClicked(MouseEventArgs mouseEventArgs, HotkeyModel hotkey) {
private void ButtonClicked(MouseEventArgs mouseEventArgs, HotkeyModel hotkey)
{
DataCollectionService.SendEvent(
DataCollectionKeys.BuildCalcInput,
new Dictionary<string, string> { { "key", hotkey.KeyText.ToLower() }, { "input-source", "mouse" } }
);
if (hotkey.KeyText.Equals(HotKeyType.SPACE.ToString())) {
if (KeyService.IsHoldingSpace()) {
if (hotkey.KeyText.Equals(HotKeyType.SPACE.ToString()))
{
if (KeyService.IsHoldingSpace())
{
KeyService.RemovePressedKey(hotkey.KeyText);
}
else {
else
{
KeyService.AddPressedKey(hotkey.KeyText);
}
}
else {
else
{
KeyService.AddPressedKey(hotkey.KeyText);
KeyService.RemovePressedKey(hotkey.KeyText);
}

6
IGP/Pages/BuildCalculator/Parts/InputPanelComponent.razor

@ -22,10 +22,10 @@
private void HandleKeyDown(KeyboardEventArgs e)
{
DataCollectionService.SendEvent(
DataCollectionKeys.BuildCalcInput,
new Dictionary<string, string> {{"key", e.Key.ToLower()}, {"input-source", "keyboard"}}
DataCollectionKeys.BuildCalcInput,
new Dictionary<string, string> { { "key", e.Key.ToLower() }, { "input-source", "keyboard" } }
);
KeyService.AddPressedKey(e.Key);
}

7
IGP/Pages/BuildCalculator/Parts/OptionsComponent.razor

@ -4,8 +4,6 @@
@inject IEconomyService EconomyService
@inject IToastService ToastService
@inject ITimingService TimingService
@using System.Data
@implements IDisposable
<FormLayoutComponent>
@ -57,7 +55,7 @@
RefreshDefaults();
}
void IDisposable.Dispose()
{
TimingService.Unsubscribe(RefreshDefaults);
@ -70,7 +68,6 @@
WaitTo = TimingService.WaitTo;
StateHasChanged();
}
void OnBuildingInputDelayChanged(ChangeEventArgs changeEventArgs)
@ -83,7 +80,7 @@
TimingService.WaitTime = (int)changeEventArgs.Value!;
WaitTime = (int)changeEventArgs.Value!;
}
void OnWaitToChanged(ChangeEventArgs changeEventArgs)
{
TimingService.WaitTo = (int)changeEventArgs.Value!;

2
IGP/Pages/BuildCalculator/Parts/TimingComponent.razor

@ -36,7 +36,7 @@
base.OnInitialized();
timingService.Subscribe(StateHasChanged);
}
void IDisposable.Dispose()
{
timingService.Unsubscribe(StateHasChanged);

133
IGP/Pages/ChangeLogPage.razor

@ -1,133 +0,0 @@
@page "/changelog"
@implements IDisposable;
@inject IGitService GitService;
@inherits BasePage
@inject IDataCollectionService DataCollectionService
@layout PageLayout
@if (GitService.IsLoaded())
{
<LayoutMediumContentComponent>
<WebsiteTitleComponent>Change Log</WebsiteTitleComponent>
<PaperComponent>
<FormLayoutComponent>
<FormCheckboxComponent Label="Show Important"
Info="Only show important patches. Like database updates to the latest game patch."
Value="@isViewImportant"
OnChange="OnChangeClicked">
</FormCheckboxComponent>
</FormLayoutComponent>
</PaperComponent>
<PaperComponent>
@foreach (var patch in Patches.OrderBy(x => x.Date).Reverse())
{
@if (!patch.Important.Equals("True") && isViewImportant)
{
continue;
}
var daysAgo = Math.Floor(DateTime.Now.Subtract(patch.Date).TotalDays);
<div class="patchContainer">
<div style="display: flex; justify-content: space-between;">
<div style="font-size: 1.2rem; font-weight: bolder; margin-bottom:4px;">
@patch.Name
</div>
<div>
@if (daysAgo == 0)
{
<i>Today</i>
}
else if (daysAgo < 8)
{
<i>@daysAgo days ago</i>
}
else
{
<i>@patch.Date.ToString("dd/MM/yyyy")</i>
}
</div>
</div>
<div>
@foreach (var change in Changes.FindAll(e => e.GitPatchModelId == patch.Id))
{
@if (!change.Important.Equals("True") && isViewImportant)
{
continue;
}
<div style="display: flex; justify-content: space-between; ">
<div>
<div>
<b>
<span>- @change.Name </span>
@if (change.Commit != CommitType.None)
{
<span>(@change.Commit)</span>
}
<span>: </span>
</b> @((MarkupString)change.Description)
</div>
</div>
</div>
}
</div>
</div>
}
</PaperComponent>
</LayoutMediumContentComponent>
}
else
{
<LoadingComponent/>
}
@code {
private IEnumerable<GitPatchModel> Patches => GitService.GitPatchModels;
private List<GitChangeModel> Changes => GitService.GitChangeModels;
private bool isViewImportant = true;
protected override void OnInitialized()
{
base.OnInitialized();
GitService.Subscribe(HasChanged);
}
void IDisposable.Dispose()
{
GitService.Unsubscribe(HasChanged);
}
void OnChangeClicked(ChangeEventArgs changeEventArgs)
{
isViewImportant = (bool)changeEventArgs.Value!;
StateHasChanged();
}
void HasChanged()
{
StateHasChanged();
}
protected override async Task OnInitializedAsync()
{
await GitService.Load();
}
}

13
IGP/Pages/ChangeLogPage.razor.css

@ -1,13 +0,0 @@
.patchContainer {
padding: 16px;
}
@media only screen and (max-width: 1025px) {
.patchContainer {
border: none;
padding-left: 8px;
padding-right: 8px;
padding-top: 16px;
padding-bottom: 16px;
}
}

106
IGP/Pages/Comparision/ComparisionPage.razor

@ -1,106 +0,0 @@
@layout PageLayout
@inherits BasePage
@implements IDisposable
@inject IToastService ToastService
<div style="display:grid; gap: 8px;padding: 16px; height: 94vh; width: 90vw; margin: auto; margin-top: 32px;
grid-template-columns: 27% 25% 25% 23%; grid-template-rows: auto;
grid-template-areas: 'loader sand compare compare' ;">
<div style="grid-area: loader; border: 2px solid black; padding: 20px;">
Comparision Loader
<BuildLoaderComponent></BuildLoaderComponent>
</div>
<div style="grid-area: sand; border: 2px solid black; padding: 20px;">
Sand
<SandComponent></SandComponent>
</div>
<div style="grid-area: compare; border: 2px solid black; padding: 20px;">
Comparision Charts
</div>
</div>
@code {
[Inject]
IKeyService KeyService { get; set; } = default!;
[Inject]
IImmortalSelectionService FilterService { get; set; } = default!;
[Inject]
IBuildOrderService BuildOrderService { get; set; } = default!;
[Inject]
IEconomyService EconomyService { get; set; } = default!;
[Inject]
ITimingService TimingService { get; set; } = default!;
Dictionary<int, List<EntityModel>> completedEntities = new();
List<EntityModel> entities = EntityModel.GetListOnlyHotkey();
protected override void OnInitialized()
{
base.OnInitialized();
KeyService.Subscribe(HandleClick);
FilterService.Subscribe(StateHasChanged);
EconomyService.Subscribe(StateHasChanged);
TimingService.Subscribe(HandleTimingChanged);
EconomyService.Calculate(BuildOrderService, TimingService, 0);
}
void IDisposable.Dispose()
{
KeyService.Unsubscribe(HandleClick);
FilterService.Unsubscribe(StateHasChanged);
TimingService.Unsubscribe(StateHasChanged);
EconomyService.Unsubscribe(StateHasChanged);
}
protected void HandleTimingChanged()
{
EconomyService.Calculate(BuildOrderService, TimingService, BuildOrderService.GetLastRequestInterval());
}
protected void HandleClick()
{
var hotkey = KeyService.GetHotkey();
var hotkeyGroup = KeyService.GetHotkeyGroup();
var isHoldSpace = KeyService.IsHoldingSpace();
var faction = FilterService.GetFaction();
var immortal = FilterService.GetImmortal();
if (hotkey == "`")
{
BuildOrderService.RemoveLast();
EconomyService.Calculate(BuildOrderService, TimingService, BuildOrderService.GetLastRequestInterval());
StateHasChanged();
return;
}
var entity = EntityModel.GetFrom(hotkey!, hotkeyGroup, isHoldSpace, faction, immortal);
if (entity == null)
{
return;
}
if (BuildOrderService.Add(entity, EconomyService))
{
EconomyService.Calculate(BuildOrderService, TimingService, BuildOrderService.GetLastRequestInterval());
}
}
}

37
IGP/Pages/Comparision/Parts/BuildLoaderComponent.razor

@ -1,37 +0,0 @@
@implements IDisposable
<div>
<Button Color="Color.Primary" @onclick="OnLoad">Load</Button>
<button @onclick="OnLoad">Load</button>
</div>
<div>
<textarea @bind="buildData" @bind:event="oninput"
style="background-color: #36393F; width: 330px; height: 400px;">
</textarea>
</div>
@code {
string buildData = "";
[Inject]
IBuildComparisonService BuildComparisionService { get; set; } = default!;
protected override void OnInitialized()
{
base.OnInitialized();
BuildComparisionService.Subscribe(StateHasChanged);
}
void IDisposable.Dispose()
{
BuildComparisionService.Unsubscribe(StateHasChanged);
}
void OnLoad()
{
BuildComparisionService.LoadJson(buildData);
}
}

25
IGP/Pages/Comparision/Parts/SandComponent.razor

@ -1,25 +0,0 @@
@implements IDisposable
<div>
<textarea readonly style="background-color: #36393F; width: 330px; height: 400px;">
@BuildComparisonService.AsJson()
</textarea>
</div>
@code {
[Inject]
IBuildComparisonService BuildComparisonService { get; set; } = default!;
protected override void OnInitialized()
{
base.OnInitialized();
BuildComparisonService.Subscribe(StateHasChanged);
}
void IDisposable.Dispose()
{
BuildComparisonService.Unsubscribe(StateHasChanged);
}
}

5
IGP/Pages/Database/DatabaseSinglePage.razor

@ -11,7 +11,6 @@
<LayoutLargeContentComponent>
<PaperComponent>
<FormDisplayComponent Label="Patch">
<Display>
@ -76,10 +75,10 @@
{
base.OnParametersSet();
base.OnInitialized();
FocusEntity();
}
private void FocusEntity()
{
foreach (var e in DATA.Get().Values)

11
IGP/Pages/Database/Parts/EntityFilterComponent.razor

@ -9,11 +9,12 @@
{
styleClass = "selected";
}
<button @onclick="@(e => OnChangeFaction(choice))"
class="choiceButton @styleClass">@(choice == DataType.Any
? DataType.Any
: DATA.Get()[choice].Info().Name)
</button>
<button @onclick="@(e => OnChangeFaction(choice))"
class="choiceButton @styleClass">
@(choice == DataType.Any
? DataType.Any
: DATA.Get()[choice].Info().Name)
</button>
}
</div>

117
IGP/Pages/Documentation/DocumentationIndexPage.razor

@ -1,117 +0,0 @@
@layout PageLayout
@inherits BasePage
@inject IDocumentationService DocumentationService
@implements IDisposable
@page "/docs"
@if (!DocumentationService.IsLoaded())
{
<LoadingComponent/>
}
else
{
<LayoutMediumContentComponent>
<PaperComponent>
@foreach (var docSection in DocumentationService.DocSectionModels)
{
<div class="docSectionContainer">
<div class="docSectionTitle">@docSection.Name</div>
<div class="docContentContainer">
@foreach (var docContent in docSection.DocumentationModels)
{
<NavLink class="docContentLink" href="@docContent.GetDocLink()">
<div class="docContentName">@docContent.Name</div>
<div class="docContentDescription">@docContent.Description</div>
</NavLink>
}
</div>
</div>
}
</PaperComponent>
</LayoutMediumContentComponent>
}
<style>
.docSectionContainer {
width: 100%;
padding: 8px;
}
.docSectionTitle {
font-size: 3rem;
font-weight: bold;
text-align: center;
margin-bottom: 32px;
}
.docContentContainer {
display: grid;
gap: 12px;
grid-template-columns: 1fr 1fr;
}
@@media only screen and (max-width: 1025px) {
.docContentContainer {
grid-template-columns: 1fr;
}
}
.docContentName {
font-weight: bold;
font-size: 1.6rem;
}
.docContentDescription {
font-weight: normal;
}
.docContentLink {
background-color: var(--paper);
border: 1px solid var(--paper-border);
border-radius: 2px;
padding-left: 12px;
padding-right: 12px;
padding-top: 24px;
padding-bottom: 24px;
color: white;
display: flex;
flex-direction: column;
gap: 12px;
text-align: center;
margin-left: 12px;
margin-right: 12px;
}
.docContentLink:hover {
background-color: var(--paper-hover);
border-color: var(--paper-border-hover);
text-decoration: none;
box-shadow: 0 4px 6px rgba(0,0,0,0.6);
transform: translateY(-2px) scale(1.01);
}
</style>
@code {
[Parameter]
public string? Text { get; set; }
protected override void OnInitialized()
{
base.OnInitialized();
DocumentationService.Subscribe(StateHasChanged);
DocumentationService.Load();
}
void IDisposable.Dispose()
{
DocumentationService.Unsubscribe(StateHasChanged);
}
}

123
IGP/Pages/Documentation/DocumentationPage.razor

@ -1,123 +0,0 @@
@layout PageLayout
@inherits BasePage
@inject IDocumentationService DocumentationService
@implements IDisposable
@page "/docs/{href1}/{href2?}/{href3?}/{href4?}/{href5?}"
@if (!DocumentationService.IsLoaded())
{
<LoadingComponent/>
}
else
{
<LayoutWithSidebarComponent>
<Sidebar>
<DocumentNavComponent
Connections="DocumentationService.DocConnectionModels"
Documents="DocumentationService.DocContentModels"/>
</Sidebar>
<Content>
<PaperComponent>
@foreach (var doc in DocumentationService.DocContentModels)
{
if (!doc.Href.Equals(Href))
{
continue;
}
<DocumentComponent DocContentModel="doc"/>
}
</PaperComponent>
</Content>
</LayoutWithSidebarComponent>
}
<style>
pre code {
color: white;
}
h1 {
display: block;
font-size: 2em;
margin-top: 0.67em;
margin-bottom: 0.67em;
margin-left: 0;
margin-right: 0;
font-weight: bold;
}
h2 {
display: block;
font-size: 1.5em;
margin-top: 0.83em;
margin-bottom: 0.83em;
margin-left: 0;
margin-right: 0;
font-weight: bold;
}
li {
display: list-item;
}
p {
display: block;
margin-top: 1em;
margin-bottom: 1em;
margin-left: 0;
margin-right: 0;
}
ul {
display: block;
list-style-type: disc;
margin-top: 1em;
margin-bottom: 1em;
margin-left: 0;
margin-right: 0;
padding-left: 40px;
}
pre {
background: black;
padding: 2px;
}
</style>
@code {
[Parameter]
public string? Href1 { get; set; }
[Parameter]
public string? Href2 { get; set; }
[Parameter]
public string? Href3 { get; set; }
[Parameter]
public string? Href4 { get; set; }
[Parameter]
public string? Href5 { get; set; }
private string Href => Href5 ?? Href4 ?? Href3 ?? Href2 ?? Href1 ?? "";
protected override void OnInitialized()
{
base.OnInitialized();
DocumentationService.Subscribe(StateHasChanged);
DocumentationService.Load();
}
void IDisposable.Dispose()
{
DocumentationService.Unsubscribe(StateHasChanged);
}
}

6
IGP/Pages/Documentation/Parts/DocumentComponent.razor

@ -16,7 +16,9 @@ else
</div>
<div class="docContent">@((MarkupString)Markdown.ToHtml(content, Pipeline))</div>
<div class="docFooter">
<EditLinkComponent Href="@GitUrl"></EditLinkComponent>
<LinkButtonComponent Href="@GitUrl">
Edit on GitHub <i class="fa-brands fa-github" style="font-size: 24px; margin-left: 5px;"></i>
</LinkButtonComponent>
</div>
</div>
}
@ -57,7 +59,7 @@ else
public DocContentModel DocContentModel { get; set; } = default!;
DocFrontMatterModel docFrontMatter = null!;
private string? content = null;
private string? content;
private string Filepath => $"content/docs/{DocContentModel.Content}.md";
private string GitUrl => $"{Project.GitResourcesUrl}/{Filepath}";

70
IGP/Pages/Documentation/Parts/DocumentInnerNavComponent.razor

@ -1,70 +0,0 @@
@if (Document!.DocumentationModels.Count > 0)
{
<div class="docInnerNavContainer">
@foreach (var innerDoc in Document.DocumentationModels)
{
var linkStyle = $"docInnerNavButton inner_{Layers}";
<NavLink class="@linkStyle" href="@innerDoc.GetDocLink()">@innerDoc.Name</NavLink>
<DocumentInnerNavComponent Document="@innerDoc" Layers="@IncrementLayers()"/>
}
</div>
}
<style>
.docInnerNavContainer {
display: flex;
flex-direction: column;
}
.docInnerNavButton a {
color: white;
}
.docInnerNavButton a:hover {
color: white;
}
.docInnerNavButton {
padding: 8px;
margin-left: 8px;
color: white;
}
.inner_1 {
margin-left: 8px;
}
.inner_2 {
margin-left: 16px;
}
.inner_3 {
margin-left: 24px;
}
</style>
@code {
[Parameter]
public DocContentModel? Document { get; set; } = default!;
[Parameter]
public int Layers { get; set; } = 1;
public int IncrementLayers()
{
return Layers + 1;
}
private string GetLink(DocContentModel doc)
{
return $"docs/{doc.Href}";
}
}

47
IGP/Pages/Documentation/Parts/DocumentNavComponent.razor

@ -1,47 +0,0 @@
<div class="docNavContainer">
@foreach (var doc in Documents)
{
if (doc.Parent == null)
{
<NavLink class="docNavButton" href="@doc.GetDocLink()">@doc.Name</NavLink>
<DocumentInnerNavComponent Document="@doc"/>
}
}
</div>
<style>
.docNavContainer {
display: flex;
flex-direction: column;
gap: 8px;
}
.docNavButton a {
color: white;
}
.docNavButton a:hover {
color: white;
}
.docNavButton {
padding: 8px;
color: white;
}
</style>
@code {
[Parameter]
public List<DocContentModel> Documents { get; set; } = default!;
[Parameter]
public List<DocConnectionModel> Connections { get; set; } = default!;
private string GetLink(DocContentModel doc)
{
return $"docs/{doc.Href}";
}
}

4
IGP/Pages/EconomyComparison/EconomyComparisonPage.razor

@ -7,6 +7,8 @@
@layout PageLayout
<LayoutMediumContentComponent>
<WebsiteTitleComponent>Economy Comparision</WebsiteTitleComponent>
<PaperComponent>
<div>You</div>
<EconomyInputComponent ForPlayer="0"/>
@ -40,6 +42,7 @@
</LayoutMediumContentComponent>
@code {
protected override void OnInitialized()
{
base.OnInitialized();
@ -50,4 +53,5 @@
{
EconomyComparisonService.Unsubscribe(StateHasChanged);
}
}

12
IGP/Pages/EconomyComparison/Parts/ChartComponent.razor

@ -17,7 +17,7 @@
@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;
@ -79,18 +79,17 @@
void OnBuilderOrderChanged()
{
charts = new List<ChartModel>();
var index = 0;
highestAlloyPoint = 0;
foreach (var buildToCompare in economyComparisonService.BuildsToCompare)
{
GenerateChart(index++, buildToCompare);
}
StateHasChanged();
}
@ -124,7 +123,6 @@
};
for (var interval = 0; interval < economyOverTime.Count(); interval++)
{
var alloyPoint = new PointModel { Interval = interval };

15
IGP/Pages/EconomyComparison/Parts/EconomyDifferenceComponent.razor

@ -63,13 +63,13 @@
@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()
{
base.OnInitialized();
@ -87,8 +87,8 @@
StartingAdvantageAtTime = 0;
WorseningTime = 0;
MiracleTime = 0;
for (int interval = 0; interval < economyComparisonService.BuildsToCompare[0].EconomyOverTimeModel.Count; interval++)
for (var interval = 0; interval < economyComparisonService.BuildsToCompare[0].EconomyOverTimeModel.Count; interval++)
{
var yourEconomy = economyComparisonService.BuildsToCompare[0].EconomyOverTimeModel[interval];
var theirEconomy = economyComparisonService.BuildsToCompare[1].EconomyOverTimeModel[interval];
@ -109,16 +109,15 @@
}
else
{
if (PeakAdvantageByAlloy > 0 && WorseningTime == 0)
{
WorseningTime = interval;
}
}
if (deltaEconomy < -1000 && MiracleTime == 0)
{
MiracleTime = interval;
}
}
}
}

20
IGP/Pages/EconomyComparison/Parts/EconomyInputComponent.razor

@ -14,22 +14,22 @@
<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>
}
<FormNumberComponent Value="@timing" OnChange="e => ChangeBuildTime(e, index - 1)">
<FormLabelComponent>
TownHall build time
</FormLabelComponent>
</FormNumberComponent>
}
<ContentDividerComponent/>
<FormTextComponent Label="Chart Color" Value="@ChartColor" OnChange="ChangeColor" />
<FormTextComponent Label="Chart Color" Value="@ChartColor" OnChange="ChangeColor"/>
</FormLayoutComponent>
@ -46,7 +46,7 @@
private string ChartColor => economyComparisonService.GetColor(ForPlayer);
private string Faction => economyComparisonService.GetFaction(ForPlayer);
private List<int> TownHallTimings => economyComparisonService.GetTownHallBuildTimes(ForPlayer);
protected override void OnInitialized()
{
base.OnInitialized();

195
IGP/Pages/HarassCalculatorPage.razor

@ -12,7 +12,7 @@
<PaperComponent>
Credit to Zard for deriving the formula.
</PaperComponent>
<PaperComponent>
<LayoutRowComponent>
<LayoutColumnComponent>
@ -41,29 +41,31 @@
</FormNumberComponent>
<FormNumberComponent Min="1"
Id="numberOfTownHallsExisting"
Id="numberOfTownHallsExisting"
Value="@((int)NumberOfTownHallsExisting)"
OnChange="OnTownHallsChanged">
<FormLabelComponent>Number of townhalls you have</FormLabelComponent>
</FormNumberComponent>
<div id="numberOfTownHallTravelTimes">
@{
var index = 0;
}
@foreach (var travelTime in TravelTimes) {
index++;
if (index == 1) {
continue;
@{
var index = 0;
}
@foreach (var travelTime in TravelTimes)
{
index++;
if (index == 1)
{
continue;
}
var id = $"numberOfTownHallsExisting_{index}";
<FormNumberComponent Min="0"
Id="@id"
Value="@((int)travelTime.Value)"
OnChange="e => { OnTownHallTravelTimeChanged(e, travelTime); }">
<FormLabelComponent>Worker travel time from other base @(travelTime.Index + 1)</FormLabelComponent>
</FormNumberComponent>
}
var id = $"numberOfTownHallsExisting_{index}";
<FormNumberComponent Min="0"
Id="@id"
Value="@((int)travelTime.Value)"
OnChange="e => { OnTownHallTravelTimeChanged(e, travelTime); }">
<FormLabelComponent>Worker travel time from other base @(travelTime.Index + 1)</FormLabelComponent>
</FormNumberComponent>
}
</div>
@ -71,7 +73,7 @@
<Display>
<div style="font-size: 1.5rem; font-weight: 800;">
<span id="totalAlloyHarassment">
@TotalAlloyHarassment
@TotalAlloyHarassment
</span>
</div>
</Display>
@ -90,7 +92,7 @@
<div>
(<b>Average travel time:</b> <span id="getAverageTravelTime">@GetAverageTravelTime()</span>)
</div>
</LayoutColumnComponent>
</LayoutRowComponent>
</PaperComponent>
@ -104,9 +106,12 @@
</InfoQuestionComponent>
<InfoAnswerComponent>
The Harass Calculator allows you to calculate damage done to an enemy alloy line. For example, if you
were to attack with Ichors, and kill 6 enemy workers, you can set the <b>Number of workers lost to
harass</b> to 6. This would determine a loss of <span id="exampleTotalAlloyLoss">@ExampleTotalAlloyLoss</span> alloy. Quite
the large number.
were to attack with Ichors, and kill 6 enemy workers, you can set the
<b>
Number of workers lost to
harass
</b> to 6. This would determine a loss of <span id="exampleTotalAlloyLoss">@ExampleTotalAlloyLoss</span> alloy. Quite
the large number.
</InfoAnswerComponent>
</InfoBodyComponent>
@ -115,21 +120,27 @@
What can I learn from this?
</InfoQuestionComponent>
<InfoAnswerComponent>
Well, let's assume you lost a full alloy line of workers, and have to take that
@ExampleTotalAlloyLoss alloy cost (<span id="exampleWorkerCost">@ExampleWorkerCost</span>
to rebuy the workers, and <span id="exampleMiningTimeCost">@ExampleMiningTimeCost</span> in lost mining
Well, let's assume you lost a full alloy line of workers, and have to take that
@ExampleTotalAlloyLoss alloy cost (<span id="exampleWorkerCost">@ExampleWorkerCost</span>
to rebuy the workers, and <span id="exampleMiningTimeCost">@ExampleMiningTimeCost</span> in lost mining
time.)
<br/><br/>
If you were to set the <b>Number of townhalls you have</b> to 2, the calculator will consider worker
transfer micro. Allowing you to cut the total cost by roughly
<span id="exampleTotalAlloyLossDifference">@ExampleTotalAlloyLossDifference</span> alloy. However, that number isn't
If you were to set the <b>Number of townhalls you have</b> to 2, the calculator will consider worker
transfer micro. Allowing you to cut the total cost by roughly
<span id="exampleTotalAlloyLossDifference">@ExampleTotalAlloyLossDifference</span> alloy. However, that number isn't
entirely accurate, you are also going to have to bump up the <b>Worker travel time to alloy</b> to account for the time it takes the transferred workers to arrive at the decimated alloy line.
<br/><br/>
Let's say it takes 10 seconds for workers to transfer from your second base. Let's enter that for the
second base travel time for the more accurate loss of <span
id="exampleTotalAlloyLossAccurate">@ExampleTotalAlloyLossAccurate</span> alloy
(saving you <span id="exampleTotalAlloyLossAccurateDifference">@ExampleTotalAlloyLossAccurateDifference</span> alloy.) <i>Which is
much better than not transferring workers!</i>
second base travel time for the more accurate loss of
<span
id="exampleTotalAlloyLossAccurate">
@ExampleTotalAlloyLossAccurate
</span> alloy
(saving you <span id="exampleTotalAlloyLossAccurateDifference">@ExampleTotalAlloyLossAccurateDifference</span> alloy.)
<i>
Which is
much better than not transferring workers!
</i>
</InfoAnswerComponent>
</InfoBodyComponent>
@ -180,7 +191,10 @@
Can I see the code for the calculation?
</InfoQuestionComponent>
<InfoAnswerComponent>
<CodeLinkComponent Href="https://github.com/JonathanMcCaffrey/IGP-Fan-Reference/blob/main/IGP/Pages/HarassCalculatorPage.razor"/>
<br/>
<LinkButtonComponent Href="https://github.com/JonathanMcCaffrey/IGP-Fan-Reference/blob/main/IGP/Pages/HarassCalculatorPage.razor">
View on GitHub <i class="fa-brands fa-github" style="font-size: 24px; margin-left: 5px;"></i>
</LinkButtonComponent>
</InfoAnswerComponent>
</InfoBodyComponent>
</PaperComponent>
@ -210,30 +224,30 @@
@code {
// Example calcs
float ExampleTotalAlloyLoss => Calculate(
WorkerReplacementCost(6),
SimultaneousProductionFloor(1,6),
6,
WorkerReplacementCost(6),
SimultaneousProductionFloor(1, 6),
6,
new List<float> { 0 });
float ExampleWorkerCost => WorkerReplacementCost(6);
float ExampleMiningTimeCost => ExampleTotalAlloyLoss - ExampleWorkerCost;
float ExampleTotalAlloyLossDifference => ExampleTotalAlloyLoss - Calculate(
WorkerReplacementCost(6),
SimultaneousProductionFloor(2,6),
6,
WorkerReplacementCost(6),
SimultaneousProductionFloor(2, 6),
6,
new List<float> { 0, 0 });
float ExampleTotalAlloyLossAccurate => Calculate(
WorkerReplacementCost(6),
SimultaneousProductionFloor(2,6),
6,
WorkerReplacementCost(6),
SimultaneousProductionFloor(2, 6),
6,
new List<float> { 0, 10 });
float ExampleTotalAlloyLossAccurateDifference => ExampleTotalAlloyLoss - ExampleTotalAlloyLossAccurate;
float TotalAlloyHarassment = 0;
float TotalAlloyHarassment;
readonly float CostOfWorker = 50;
readonly float AlloyMinedPerSecondByWorker = 1;
@ -241,14 +255,17 @@
float NumberOfWorkersLostToHarass = 1;
float NumberOfTownHallsExisting = 1;
float GetAverageTravelTime() {
if (TravelTimes.Count == 0) {
float GetAverageTravelTime()
{
if (TravelTimes.Count == 0)
{
return 0;
}
float sum = 0;
foreach (var travelTime in TravelTimes) {
foreach (var travelTime in TravelTimes)
{
sum += travelTime.Value;
}
@ -256,62 +273,73 @@
}
float SimultaneousProductionFloor() {
if (NumberOfTownHallsExisting <= 0 || NumberOfWorkersLostToHarass <= 0) {
float SimultaneousProductionFloor()
{
if (NumberOfTownHallsExisting <= 0 || NumberOfWorkersLostToHarass <= 0)
{
return 0;
}
return NumberOfWorkersLostToHarass / Math.Min(NumberOfTownHallsExisting, NumberOfWorkersLostToHarass);
}
float SimultaneousProductionFloor(float existingTownHalls, float numberOfWorkersLost) {
if (existingTownHalls <= 0 || numberOfWorkersLost <= 0) {
float SimultaneousProductionFloor(float existingTownHalls, float numberOfWorkersLost)
{
if (existingTownHalls <= 0 || numberOfWorkersLost <= 0)
{
return 0;
}
return numberOfWorkersLost / Math.Min(existingTownHalls, numberOfWorkersLost);
}
float WorkerReplacementCost() {
float WorkerReplacementCost()
{
return CostOfWorker * NumberOfWorkersLostToHarass;
}
float WorkerReplacementCost(int numberOfWorkersLostToHarass) {
float WorkerReplacementCost(int numberOfWorkersLostToHarass)
{
return CostOfWorker * numberOfWorkersLostToHarass;
}
float DelayedMiningCost() {
float DelayedMiningCost()
{
return TotalAlloyHarassment - WorkerReplacementCost();
}
void Calculate() {
TotalAlloyHarassment = Calculate(WorkerReplacementCost(),
SimultaneousProductionFloor(),
NumberOfWorkersLostToHarass,
void Calculate()
{
TotalAlloyHarassment = Calculate(WorkerReplacementCost(),
SimultaneousProductionFloor(),
NumberOfWorkersLostToHarass,
TravelTimes.Select(x => x.Value).ToList(),
TimeToProduceWorker,
TimeToProduceWorker,
AlloyMinedPerSecondByWorker);
}
float Calculate(float workerReplacementCost,
float simultaneousProductionFloor,
float numberOfWorkersLostToHarass,
IList<float> travelTimes,
float timeToProduceWorker = 20,
float alloyMinedPerSecondByWorker = 1) {
float totalAlloyHarassment = workerReplacementCost;
for (var workerProductionIndex = 0; workerProductionIndex < simultaneousProductionFloor; workerProductionIndex++) {
float Calculate(float workerReplacementCost,
float simultaneousProductionFloor,
float numberOfWorkersLostToHarass,
IList<float> travelTimes,
float timeToProduceWorker = 20,
float alloyMinedPerSecondByWorker = 1)
{
var totalAlloyHarassment = workerReplacementCost;
for (var workerProductionIndex = 0; workerProductionIndex < simultaneousProductionFloor; workerProductionIndex++)
{
totalAlloyHarassment += alloyMinedPerSecondByWorker * timeToProduceWorker * (workerProductionIndex + 1);
}
var remainder = (int)(numberOfWorkersLostToHarass % simultaneousProductionFloor);
for (var remainderIndex = 0; remainderIndex < remainder; remainderIndex++) {
for (var remainderIndex = 0; remainderIndex < remainder; remainderIndex++)
{
totalAlloyHarassment += alloyMinedPerSecondByWorker * timeToProduceWorker * (simultaneousProductionFloor + 1);
}
for (var travelTimeIndex = 0; travelTimeIndex < numberOfWorkersLostToHarass; travelTimeIndex++) {
for (var travelTimeIndex = 0; travelTimeIndex < numberOfWorkersLostToHarass; travelTimeIndex++)
{
var townHallIndex = travelTimeIndex % travelTimes.Count;
totalAlloyHarassment += alloyMinedPerSecondByWorker * travelTimes[townHallIndex];
}
@ -319,7 +347,8 @@
return totalAlloyHarassment;
}
protected override void OnInitialized() {
protected override void OnInitialized()
{
base.OnInitialized();
Calculate();
}
@ -327,18 +356,20 @@
public List<TravelTime> TravelTimes { get; set; } = new() { new TravelTime(0, 0) };
private void OnTownHallsChanged(ChangeEventArgs obj) {
private void OnTownHallsChanged(ChangeEventArgs obj)
{
NumberOfTownHallsExisting = int.Parse(obj.Value!.ToString()!);
while (TravelTimes.Count > NumberOfTownHallsExisting)
TravelTimes.Remove(TravelTimes.Last());
while (TravelTimes.Count < NumberOfTownHallsExisting)
TravelTimes.Add(new TravelTime(TravelTimes.Count, 10 * (TravelTimes.Count)));
TravelTimes.Add(new TravelTime(TravelTimes.Count, 10 * TravelTimes.Count));
Calculate();
}
private void OnTownHallTravelTimeChanged(ChangeEventArgs obj, TravelTime travelTime) {
private void OnTownHallTravelTimeChanged(ChangeEventArgs obj, TravelTime travelTime)
{
travelTime.Value = (int)obj.Value!;
Calculate();

8
IGP/Pages/Home/HomePage.razor

@ -48,14 +48,6 @@
</div>
</PaperComponent>
<ContentDividerComponent></ContentDividerComponent>
<AlertComponent>
<Title>Under Construction</Title>
<Message>Website is still being made. Check out <NavLink Href="/roadmap">Road Map</NavLink> for future plans, <NavLink Href="/agile">Agile</NavLink> for present tasks, and <NavLink Href="/changelog">Change Log</NavLink> for past changes.</Message>
</AlertComponent>
</LayoutMediumContentComponent>
<style>

88
IGP/Pages/MakingOf/MakingOfPage.razor

@ -1,88 +0,0 @@
@page "/makingof"
@inherits BasePage
@inject IDataCollectionService DataCollectionService
@layout PageLayout
<LayoutLargeContentComponent>
<WebsiteTitleComponent>Making Of</WebsiteTitleComponent>
<AlertComponent Type="@SeverityType.Warning">
<Title>Under Construction</Title>
<Message>This page is still being worked on. It will list the tech and design choices made for this website. It's is strictly for educational and reference purposes, and it has nothing to do with IMMORTAL: Gates of Pyre.</Message>
</AlertComponent>
<ContentDividerComponent></ContentDividerComponent>
<FormLayoutComponent>
<FormDisplayComponent Label="Tech Stack">
<Display>Blazor (C# and HTML)</Display>
</FormDisplayComponent>
<FormDisplayComponent Label="Stack Details">
<Display>This is a Static Single Page Application hosted on Azure.</Display>
</FormDisplayComponent>
</FormLayoutComponent>
<ContentDividerComponent></ContentDividerComponent>
<MakingOfColours></MakingOfColours>
<ContentDividerComponent></ContentDividerComponent>
<DevOnlyComponent>
<MakingOfSectionComponent Title="Empty">
<MakingOfComponent>
<Title>
Empty
</Title>
<Description>
Empty
</Description>
<Example>
Empty
</Example>
<Usage>
<CodeComponent>Empty</CodeComponent>
</Usage>
<Code>
<CodeComponent>Empty</CodeComponent>
</Code>
</MakingOfComponent>
</MakingOfSectionComponent>
</DevOnlyComponent>
<DevOnlyComponent>
<MakingOfSectionComponent Title="Dialogs">
<MakingOfDialogs></MakingOfDialogs>
</MakingOfSectionComponent>
</DevOnlyComponent>
<DevOnlyComponent>
<FormLayoutComponent>
<FormEscapeCodeComponent></FormEscapeCodeComponent>
</FormLayoutComponent>
</DevOnlyComponent>
<MakingOfSectionComponent Title="Displays">
<MakingOfDisplays></MakingOfDisplays>
</MakingOfSectionComponent>
<DevOnlyComponent>
<MakingOfSectionComponent Title="Navigation">
<MakingOfNavigation></MakingOfNavigation>
</MakingOfSectionComponent>
</DevOnlyComponent>
<MakingOfSectionComponent Title="Feedback">
<MakingOfFeedback></MakingOfFeedback>
</MakingOfSectionComponent>
<MakingOfSectionComponent Title="Forms">
<MakingOfForms></MakingOfForms>
</MakingOfSectionComponent>
</LayoutLargeContentComponent>

188
IGP/Pages/MakingOf/Parts/MakingOfColours.razor

@ -1,188 +0,0 @@
<div>Colors</div>
<div class="colorContainer">
<CodeComponent>
--accent: @accent;
--primary: @primary;
--primary-border: @primary_border;
--primary-hover: @primary_hover;
--primary-border-hover: @primary_border_hover;
--background: @background;
--secondary: @secondary;
--secondary-hover: @secondary_hover;
--secondary-border-hover: @secondary_border_hover;
--paper: @paper;
--paper-border: @paper_border;
--info: @info;
--info-border: @info_border;
--info-secondary: @info_secondary;
--info-secondary-border: @info_secondary_border;
</CodeComponent>
<br/>
<div class="color accent">
<div>Accent</div>
<div>
Base: <input type="color" value="@accent" @onchange="e => accent = e.Value!.ToString()!"/>
</div>
</div>
<div class="color primary">
<div>Primary</div>
<div>
Base: <input type="color" value="@primary" @onchange="e => primary = e.Value!.ToString()!"/>
</div>
<div>
Border: <input type="color" value="@primary_border" @onchange="e => primary_border = e.Value!.ToString()!"/>
</div>
<div>
Hover Base: <input type="color" value="@primary_hover" @onchange="e => primary_hover = e.Value!.ToString()!"/>
</div>
<div>
Hover Border: <input type="color" value="@primary_border_hover" @onchange="e => primary_border_hover = e.Value!.ToString()!"/>
</div>
</div>
<div class="color secondary">
<div>Secondary</div>
<div>
Base: <input type="color" value="@secondary" @onchange="e => secondary = e.Value!.ToString()!"/>
</div>
<div>
Hover Base: <input type="color" value="@secondary_hover" @onchange="e => secondary_hover = e.Value!.ToString()!"/>
</div>
<div>
Hover Border: <input type="color" value="@secondary_border_hover" @onchange="e => secondary_border_hover = e.Value!.ToString()!"/>
</div>
</div>
<div class="color paper">
<div>Paper</div>
<div>
Base: <input type="color" value="@paper" @onchange="e => paper = e.Value!.ToString()!"/>
</div>
<div>
Border: <input type="color" value="@paper_border" @onchange="e => paper_border = e.Value!.ToString()!"/>
</div>
</div>
<div class="color background">
<div>Background</div>
<div>
Base: <input type="color" value="@background" @onchange="e => background = e.Value!.ToString()!"/>
</div>
</div>
<div class="color info">
<div>Info</div>
<div>
Base: <input type="color" value="@info" @onchange="e => info = e.Value!.ToString()!"/>
</div>
<div>
Border: <input type="color" value="@info_border" @onchange="e => info_border = e.Value!.ToString()!"/>
</div>
</div>
<div class="color info_secondary">
<div>Info Secondary</div>
<div>
Base: <input type="color" value="@info_secondary" @onchange="e => info_secondary = e.Value!.ToString()!"/>
</div>
<div>
Border: <input type="color" value="@info_secondary_border" @onchange="e => info_secondary_border = e.Value!.ToString()!"/>
</div>
</div>
</div>
<style>
:root {
--accent: @accent;
--primary: @primary;
--primary-border: @primary_border;
--primary-hover: @primary_hover;
--primary-border-hover: @primary_border_hover;
--background: @background;
--secondary: @secondary;
--secondary-hover: @secondary_hover;
--secondary-border-hover: @secondary_border_hover;
--paper: @paper;
--paper-border: @paper_border;
--info: @info;
--info-border: @info_border;
--info-secondary: @info_secondary;
--info-secondary-border: @info_secondary_border;
}
.colorContainer {
display: flex;
flex-direction: column;
}
.color {
padding: 12px;
display: flex;
flex-direction: row;
gap: 32px;
align-items: center;
}
.accent {
background-color: var(--accent);
}
.primary {
background-color: var(--primary);
border: 1px solid var(--primary-border);
}
.primary:hover {
background-color: var(--primary-hover);
border-color: var(--primary-border-hover);
}
.secondary {
background-color: var(--secondary);
border: 1px solid var(--secondary);
}
.secondary:hover {
background-color: var(--secondary-hover);
border-color: var(--secondary-border-hover);
}
.paper {
background-color: var(--paper);
border: 4px solid var(--paper-border);
}
.background {
background-color: var(--background);
}
.info {
background-color: var(--info);
border: 1px solid var(--info-border);
}
.info_secondary {
background-color: var(--info-secondary);
border: 1px solid var(--info-secondary-border);
}
</style>
@code {
string accent = "#432462";
string primary = "#4308a3";
string primary_border = "#2c0b62";
string primary_hover = "#5e00f7";
string primary_border_hover = "#a168ff";
string background = "#161618";
string secondary = "#23133e";
string secondary_hover = "#2a0070";
string secondary_border_hover = "#a168ff";
string paper = "#252526";
string paper_border = "#151516";
string info = "#451376";
string info_border = "#210b36";
string info_secondary = "#4c3e59";
string info_secondary_border = "#7e58a2";
}

49
IGP/Pages/MakingOf/Parts/MakingOfColours.razor.css

@ -1,49 +0,0 @@
.colorContainer {
display: flex;
flex-direction: column;
}
.color {
padding: 12px;
display: flex;
flex-direction: row;
gap: 32px;
align-items: center;
}
.accent {
background-color: var(--accent);
}
.primary {
background-color: var(--primary);
border: 1px solid var(--primary-border);
}
.primary:hover {
background-color: var(--primary-hover);
border-color: var(--primary-border-hover);
}
.secondary {
background-color: var(--secondary);
border: 1px solid var(--secondary);
}
.secondary:hover {
background-color: var(--secondary-hover);
border-color: var(--secondary-border-hover);
}
.paper {
background-color: var(--paper);
border: 4px solid var(--paper-border);
}
.background {
background-color: var(--background);
}
.info {
background-color: var(--info);
}

16
IGP/Pages/MakingOf/Parts/MakingOfDialogs.razor

@ -1,16 +0,0 @@
<MakingOfComponent>
<Title>Dialog</Title>
<Description>...</Description>
<Example>
</Example>
<Usage>
//TODO
</Usage>
<Code>
//TODO
</Code>
</MakingOfComponent>
@code {
}

188
IGP/Pages/MakingOf/Parts/MakingOfDisplays.razor

@ -1,188 +0,0 @@
<MakingOfComponent>
<Title>
Entity Display
</Title>
<Description>
Display element for holding entity information.
</Description>
<Example>
<LayoutRowComponent>
<EntityDisplayComponent Title="Example Entity Info">
<div>
Example Entity Content
</div>
@for (var i = 0; i < 1; i++)
{
<div>
-@i Example Entity Content
</div>
}
<div>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</div>
</EntityDisplayComponent>
<EntityDisplayComponent Title="Example Entity Info">
<div>
Example Entity Content
</div>
@for (var i = 0; i < 2; i++)
{
<div>
-@i Example Entity Content
</div>
}
<div>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation.
</div>
</EntityDisplayComponent>
<EntityDisplayComponent Title="Example Entity Info">
<div>
Example Entity Content
</div>
@for (var i = 0; i < 1; i++)
{
<div>
-@i Example Entity Content
</div>
}
<div>
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
</div>
</EntityDisplayComponent>
</LayoutRowComponent>
</Example>
<Usage>
<CodeComponent>
&lt;LayoutRowComponent&gt;
&lt;EntityDisplayComponent Title=&quot;Example Entity Info&quot;&gt;
&lt;div&gt;
Example Entity Content
&lt;/div&gt;
@@for (var i = 0; i &lt; 1; i++) {
&lt;div&gt;
-@@i Example Entity Content
&lt;/div&gt;
}
&lt;div&gt;
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
&lt;/div&gt;
&lt;/EntityDisplayComponent&gt;
&lt;EntityDisplayComponent Title=&quot;Example Entity Info&quot;&gt;
&lt;div&gt;
Example Entity Content
&lt;/div&gt;
@@for (var i = 0; i &lt; 2; i++) {
&lt;div&gt;
-@@i Example Entity Content
&lt;/div&gt;
}
&lt;div&gt;
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation.
&lt;/div&gt;
&lt;/EntityDisplayComponent&gt;
&lt;EntityDisplayComponent Title=&quot;Example Entity Info&quot;&gt;
&lt;div&gt;
Example Entity Content
&lt;/div&gt;
@@for (var i = 0; i &lt; 1; i++) {
&lt;div&gt;
-@@i Example Entity Content
&lt;/div&gt;
}
&lt;div&gt;
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.
&lt;/div&gt;
&lt;/EntityDisplayComponent&gt;
&lt;/LayoutRowComponent&gt;
</CodeComponent>
</Usage>
<Code>
<CodeComponent>
&lt;div class=&quot;entityDisplaySection&quot;&gt;
&lt;div class=&quot;entityDisplayHeader&quot;&gt;
&lt;div class=&quot;entityDisplayTitle&quot;&gt;
@@Title
&lt;/div&gt;
&lt;div class=&quot;entityDisplayBorder&quot;&gt;
&lt;/div&gt;
&lt;/div&gt;
@@ChildContent
&lt;/div&gt;
&lt;style&gt;
.entityDisplaySection {
position: relative;
padding: 8px;
display: flex;
gap: 12px;
flex-direction: column;
margin-top: 14px;
margin-top: 20px;
padding: 12px;
background-color: var(--info);
border-top-right-radius: 12px;
border-bottom-left-radius: 2px;
border-bottom-right-radius: 2px;
}
.entityDisplayHeader {
bottom: 100%;
position: absolute;
white-space: pre;
width: 100%;
line-height: 0px;
right: 0px;
top: -4px;
display: flex;
}
.entityDisplayTitle {
font-weight: 800;
font-size: 1.4rem;
padding-right: 8px;
text-shadow: 3px 0 0 var(--info), -3px 0 0 var(--info), 0 3px 0 var(--info), 0 -3px 0 var(--info), 2px 2px var(--info), -2px -2px 0 var(--info), 2px -2px 0 var(--info), -2px 2px 0 var(--info);
}
@@@@media only screen and (max-width: 1025px) {
.entityDisplayHeader {
position: inherit;
width: 100%;
margin: 0px;
}
.entityDisplaySection {
position: inherit;
width: 100%;
margin: 0px;
max-width: none;
border-top-right-radius: 0px;
border-bottom-left-radius: 0px;
border-bottom-right-radius: 0px;
}
.entityDisplayTitle {
position: inherit;
margin: 0px;
}
}
&lt;/style&gt;
@@code {
[Parameter] public RenderFragment ChildContent { get; set; }
[Parameter] public string Title { get; set; }
}
</CodeComponent>
</Code>
</MakingOfComponent>

58
IGP/Pages/MakingOf/Parts/MakingOfFeedback.razor

@ -1,58 +0,0 @@
<WebsiteTitleComponent>
Feedback
</WebsiteTitleComponent>
<MakingOfComponent>
<Title>
Loading
</Title>
<Description>
Indicates a component is being loaded (a component that relies on JSON)
</Description>
<Example>
<div style="width: 300px; height: 450px;">
<LoadingComponent/>
</div>
</Example>
<Usage>
<CodeComponent>Empty</CodeComponent>
</Usage>
<Code>
<CodeComponent>Empty</CodeComponent>
</Code>
</MakingOfComponent>
<MakingOfComponent>
<Title>Alert Message</Title>
<Description>Used to convey important information to the viewer. Comes in Yellow (Warning), Blue (Information), Red (Error), and Green (Success). Mostly used to warn viewers of pre-alpha or incomplete content being viewed.</Description>
<Example>
<AlertComponent Type="@SeverityType.Warning">
<Title>Warning Alert Title</Title>
<Message>Warning Alert Message</Message>
</AlertComponent>
<AlertComponent Type="@SeverityType.Information">
<Title>Information Alert Title</Title>
<Message>Information Alert Message</Message>
</AlertComponent>
<AlertComponent Type="@SeverityType.Error">
<Title>Error Alert Title</Title>
<Message>Error Alert Message</Message>
</AlertComponent>
<AlertComponent Type="@SeverityType.Success">
<Title>Succsess Alert Title</Title>
<Message>Succsess Alert Message</Message>
</AlertComponent>
</Example>
<Usage>
<CodeComponent>&lt;AlertComponent Type=SeverityType.Warning&gt;<br/> &lt;Title&gt;Warning Alert Title&lt;/Title&gt;<br/> &lt;Message&gt;Warning Alert Message&lt;/Message&gt;<br/>&lt;/AlertComponent&gt;<br/>&lt;AlertComponent Type=SeverityType.Information&gt;<br/> &lt;Title&gt;Information Alert Title&lt;/Title&gt;<br/> &lt;Message&gt;Information Alert Message&lt;/Message&gt;<br/>&lt;/AlertComponent&gt;<br/>&lt;AlertComponent Type=SeverityType.Error&gt;<br/> &lt;Title&gt;Error Alert Title&lt;/Title&gt;<br/> &lt;Message&gt;Error Alert Message&lt;/Message&gt;<br/>&lt;/AlertComponent&gt;<br/>&lt;AlertComponent Type=SeverityType.Success&gt;<br/> &lt;Title&gt;Succsess Alert Title&lt;/Title&gt;<br/> &lt;Message&gt;Succsess Alert Message&lt;/Message&gt;<br/>&lt;/AlertComponent&gt;<br/></CodeComponent>
</Usage>
<Code>
<CodeComponent>@@using Components.Pages.Utils<br/><br/>&lt;div class=&quot;alertContainer @@Type.ToString().ToLower()&quot;&gt;<br/> @@if (Title != null) {<br/> &lt;div class=&quot;alertTitle&quot;&gt;<br/> @@Title<br/> &lt;/div&gt;<br/> }<br/> @@if (Message != null) {<br/> &lt;div&gt;<br/> @@Message<br/> &lt;/div&gt;<br/><br/> }<br/>&lt;/div&gt;<br/>&lt;style&gt;<br/> .alertContainer {<br/> border: 4px solid;<br/> border-radius: 4px;<br/> padding: 16px;<br/> display: flex;<br/> flex-direction: column;<br/> justify-items: stretch;<br/> width: 100%;<br/> }<br/><br/> .alertContainer.@@SeverityType.Warning.ToString().ToLower() {<br/> border-color: #2a2000;<br/> background-color: #ffbf0029;<br/> }<br/><br/> .alertContainer.@@SeverityType.Error.ToString().ToLower() {<br/> border-color: #290102;<br/> background-color: #4C2C33;<br/> }<br/><br/> .alertContainer.@@SeverityType.Information.ToString().ToLower() {<br/> border-color: #030129;<br/> background-color: #2c3a4c;<br/> }<br/><br/> .alertContainer.@@SeverityType.Success.ToString().ToLower() {<br/> border-color: #042901;<br/> background-color: #2E4C2C;<br/> }<br/><br/> .alertTitle {<br/> font-weight: 800;<br/> }<br/><br/>&lt;/style&gt;<br/>@@code {<br/> [Parameter] public RenderFragment? Title { get; set; }<br/> [Parameter] public RenderFragment? Message { get; set; }<br/> [Parameter] public SeverityType Type { get; set; } = SeverityType.Warning;<br/>}</CodeComponent>
</Code>
</MakingOfComponent>
@code {
}

35
IGP/Pages/MakingOf/Parts/MakingOfForms.razor

@ -1,35 +0,0 @@
<MakingOfComponent>
<Title>Form Text</Title>
<Description>Add Text Input</Description>
<Example>
<FormLayoutComponent>
<FormTextComponent Label="Label Text" Info="Info Text" Placeholder="Placeholder Text..." Value="Value Text"/>
<FormTextAreaComponent Label="Label Text" Info="Info Text" Placeholder="Placeholder Text..." Value="Value Text"/>
</FormLayoutComponent>
</Example>
<Usage>
//TODO
</Usage>
<Code>
//TODO
</Code>
</MakingOfComponent>
<MakingOfComponent>
<Title>Form Area</Title>
<Description>Add Text Body Input</Description>
<Example>
<FormTextAreaComponent Label="Label Text" Info="Info Text" Placeholder="Placeholder Text..." Value="Value Text"/>
</Example>
<Usage>
//TODO
</Usage>
<Code>
//TODO
</Code>
</MakingOfComponent>
@code {
}

80
IGP/Pages/MakingOf/Parts/MakingOfNavigation.razor

@ -1,80 +0,0 @@
<MakingOfComponent>
<Title>
Nav Section with Nav Links
</Title>
<Description>
Group specfic webpages. Each webpage link is used to navigate to that specific webpage. If on the webpage, the link turns dark gray. It can be clicked again to leave the page and return to home.
</Description>
<Example>
<DesktopNavSectionComponent Section=@(new WebSectionModel { Name = "Example Section" })
Children=@(new List<WebPageModel> { new() { Name = "Example Page", Href = "immortal-makingof", IsPrivate = "False" }, new() { Name = "Database", Href = "immortal-database", IsPrivate = "False" } })>
</DesktopNavSectionComponent>
</Example>
<Usage>
<CodeComponent>
&lt;DesktopNavSectionComponent Section=@@(new WebSectionModel{Name = &quot;Example Section&quot;})
Children=@@(new List&lt;WebPageModel&gt;{new WebPageModel{Name=&quot;Example Page&quot;, Href = &quot;immortal-makingof&quot;, IsPrivate = false}, new WebPageModel{Name=&quot;Database&quot;, Href = &quot;immortal-database&quot;, IsPrivate = false}})&gt;
&lt;/DesktopNavSectionComponent&gt;
</CodeComponent>
</Usage>
<Code>
<CodeComponent>
@@using Model.Website;
@@using Model.Website.Enums;
&lt;div class=&quot;sectionContainer&quot;&gt;
&lt;div class=&quot;sectionHeader&quot;&gt;
&lt;div class=&quot;sectionTitle&quot;&gt;
@@Section.Name
&lt;/div&gt;
&lt;/div&gt;
@@foreach (var childPage in Children) {
if (childPage.IsPrivate) {
continue;
}
&lt;NavLinkComponent Page=childPage&gt;&lt;/NavLinkComponent&gt;
}
&lt;/div&gt;
&lt;style&gt;
.sectionContainer {
display: flex;
flex-direction: column;
gap: 4px;
justify-content: flex-start;
position: relative;
margin-top: 12px;
padding: 18px;
width: 300px;
margin-left: 4px;
}
.sectionHeader {
bottom: 100%;
position: absolute;
top: 0px;
left: -8px;
padding-right: 12px;
padding-left: 4px;
width: 100%;
display: flex;
line-height: 0px;
}
.sectionTitle {
font-weight: bold;
padding-right: 8px;
margin-top: -2px;
white-space: pre;
}
&lt;/style&gt;
@@code {
[Parameter] public WebSectionModel? Section { get; set; }
[Parameter] public List&lt;WebPageModel&gt;? Children { get; set; }
}
</CodeComponent>
</Code>
</MakingOfComponent>

7
IGP/Pages/Notes/NotesIndexPage.razor

@ -18,6 +18,8 @@ else
{
<LayoutMediumContentComponent>
<WebsiteTitleComponent>Notes</WebsiteTitleComponent>
<PaperComponent>
@foreach (var noteSection in NoteService.NoteSectionModels)
{
@ -40,7 +42,10 @@ else
<style>
.noteSectionContainer {
width: 100%;
padding: 8px;
padding-left: 8px;
padding-right: 8px;
padding-top: 8px;
padding-bottom: 64px;
}
.noteSectionTitle {

6
IGP/Pages/Notes/Parts/NoteComponent.razor

@ -8,7 +8,7 @@ else
{
<div class="note">
<div class="noteHeader">
<div class="noteTitle">@noteFrontMatter.Title</div>
<h1 class="noteTitle">@noteFrontMatter.Title</h1>
<div class="noteDates">
<div class="noteDateUpdated"><b>Updated</b>: @noteFrontMatter.UpdatedDate.ToString("MM/dd/yyyy")</div>
<div class="noteDateCreated"><b>Created</b>: @noteFrontMatter.CreatedDate.ToString("MM/dd/yyyy")</div>
@ -16,7 +16,9 @@ else
</div>
<div class="noteContent">@((MarkupString)Markdown.ToHtml(content, Pipeline))</div>
<div class="noteFooter">
<EditLinkComponent Href="@GitUrl"></EditLinkComponent>
<LinkButtonComponent Href="@GitUrl">
Edit on GitHub <i class="fa-brands fa-github" style="font-size: 24px; margin-left: 5px;"></i>
</LinkButtonComponent>
</div>
</div>
}

9
IGP/Pages/PermissionsPage.razor

@ -47,10 +47,11 @@
<InfoBodyComponent>
<InfoQuestionComponent>What data does this website collect?</InfoQuestionComponent>
<InfoAnswerComponent>This website usages Google Analytics to collect data on usage of this website.
<InfoAnswerComponent>
This website usages Google Analytics to collect data on usage of this website.
<br/><br/>
Items include: if people use keyboard or mouse in build calculator, what pages people visit, and other usages.
</InfoAnswerComponent>
</InfoAnswerComponent>
</InfoBodyComponent>
<InfoBodyComponent>
@ -62,8 +63,8 @@
@code {
private bool _storageEnabled = false;
private bool _dataCollectionEnabled = false;
private bool _storageEnabled;
private bool _dataCollectionEnabled;
protected override void OnInitialized()
{

19
IGP/Pages/RoadMap/Parts/RoadMapComponent.razor

@ -1,19 +0,0 @@
<PanelComponent>
<div class="roadMapTitle">@RoadMap.Name</div>
<div>@((MarkupString)RoadMap.Description)</div>
</PanelComponent>
<style>
.roadMapTitle {
font-weight: 800;
font-size: 1.2rem;
}
</style>
@code {
[Parameter]
public ImmortalRoadMapModel? RoadMap { get; set; }
}

32
IGP/Pages/RoadMap/RoadMapPage.razor

@ -1,32 +0,0 @@
@layout PageLayout
@inherits BasePage
@inject IDataCollectionService DataCollectionService
@page "/roadmap"
<LayoutMediumContentComponent>
<WebsiteTitleComponent>Road Map</WebsiteTitleComponent>
<div class="roadMapsContainer">
@foreach (var roadMap in data)
{
<RoadMapComponent RoadMap=roadMap/>
}
</div>
</LayoutMediumContentComponent>
<style>
.roadMapsContainer {
display: flex;
gap: 20px;
flex-wrap: wrap;
align-items: stretch;
justify-content: center;
}
</style>
@code {
readonly List<ImmortalRoadMapModel> data = ImmortalRoadMapModel.Data;
}

24
IGP/Pages/StoragePage.razor

@ -28,14 +28,14 @@
<DevOnlyComponent>
<FormLayoutComponent>
<FormToggleComponent
Label="Is Dynamic Formatting"
Info="Should [Attacks Per Second/Seconds Between Attack] match in-game values?"
Value="@_isDynamicFormatting"
OnChange="DynamicFormattingChanged"/>
</FormLayoutComponent>
<FormToggleComponent
Label="Is Dynamic Formatting"
Info="Should [Attacks Per Second/Seconds Between Attack] match in-game values?"
Value="@_isDynamicFormatting"
OnChange="DynamicFormattingChanged"/>
</FormLayoutComponent>
</DevOnlyComponent>
</PaperComponent>
<PaperComponent>
@ -148,7 +148,7 @@
protected override void OnInitialized()
{
base.OnInitialized();
_enabledPermissions = StorageService.GetValue<bool>(StorageKeys.EnabledStorage);
RefreshDefaults();
@ -161,11 +161,11 @@
StorageService.Unsubscribe(RefreshDefaults);
}
private int? _attackTime = null;
private int? _travelTime = null;
private int? _attackTime;
private int? _travelTime;
private string? _faction = null;
private string? _immortal = null;
private string? _faction;
private string? _immortal;
private string? Faction => _faction == null ? DataType.FACTION_QRath : _faction;
private string? Immortal => _immortal == null ? DataType.IMMORTAL_Orzum : _immortal;

22
IGP/Pages/StreamsPage.razor

@ -14,21 +14,25 @@
</InfoBodyComponent>
<InfoBodyComponent>
<InfoQuestionComponent>What exactly are you streaming?</InfoQuestionComponent>
<InfoAnswerComponent>The plan will be sprint planning and general development of this website.
<InfoAnswerComponent>
The plan will be sprint planning and general development of this website.
<br/><br/>
Feel free to jump into the stream to ask questions or make feature requests for the website.</InfoAnswerComponent>
Feel free to jump into the stream to ask questions or make feature requests for the website.
</InfoAnswerComponent>
</InfoBodyComponent>
<InfoBodyComponent>
<InfoQuestionComponent>Why should you watch these streams?</InfoQuestionComponent>
<InfoAnswerComponent>You shouldn't. By nature of being live coding streams, I think they will have little entertainment and educational value.
<InfoAnswerComponent>
You shouldn't. By nature of being live coding streams, I think they will have little entertainment and educational value.
<br/><br/>
The most reason (that comes to mind) is to see a coding day of one software developer. Although please note that I stream content that I think is easy (to look smart), so it's not a "truly random" coding day. For example, you won't find any vods of the five-week sprint where I (figuratively) bash my head against the table trying to get SQL working in Blazor WASM before giving up and moving on. </InfoAnswerComponent>
The most reason (that comes to mind) is to see a coding day of one software developer. Although please note that I stream content that I think is easy (to look smart), so it's not a "truly random" coding day. For example, you won't find any vods of the five-week sprint where I (figuratively) bash my head against the table trying to get SQL working in Blazor WASM before giving up and moving on.
</InfoAnswerComponent>
</InfoBodyComponent>
<InfoBodyComponent>
<InfoQuestionComponent>Anything else I should know?</InfoQuestionComponent>
<InfoAnswerComponent>I'll be streaming under the "<a href="https://www.twitch.tv/directory/game/Software%20and%20Game%20Development" target="_blank">Twitch, Software and Game Development</a>" category. If you are looking to see some actual IGP gameplay, there are better and more focused streamers to provide said content. Check out the "<a href="https://www.twitch.tv/directory/game/IMMORTAL%3A%20Gates%20of%20Pyre/videos/all" target="_blank">Twitch, IMMORTAL: Gates of Pyre</a>" category for some examples.</InfoAnswerComponent>
</InfoBodyComponent>
<InfoQuestionComponent>Anything else I should know?</InfoQuestionComponent>
<InfoAnswerComponent>I'll be streaming under the "<a href="https://www.twitch.tv/directory/game/Software%20and%20Game%20Development" target="_blank">Twitch, Software and Game Development</a>" category. If you are looking to see some actual IGP gameplay, there are better and more focused streamers to provide said content. Check out the "<a href="https://www.twitch.tv/directory/game/IMMORTAL%3A%20Gates%20of%20Pyre/videos/all" target="_blank">Twitch, IMMORTAL: Gates of Pyre</a>" category for some examples.</InfoAnswerComponent>
</InfoBodyComponent>
</PaperComponent>
</LayoutMediumContentComponent>

1
IGP/Portals/SearchPortal.razor

@ -18,7 +18,6 @@
await searchService.Load();
await jsRuntime.InvokeVoidAsync("SetDotnetReference", DotNetObjectReference.Create(this));
}
public void Dispose()

7
IGP/_Imports.razor

@ -8,25 +8,18 @@
@using Components.Shared
@using IGP.Dialog
@using IGP.Pages
@using IGP.Pages.Agile.Parts
@using IGP.Pages.BuildCalculator
@using IGP.Pages.BuildCalculator.Parts
@using IGP.Pages.Comparision
@using IGP.Pages.Comparision.Parts
@using IGP.Pages.Database.Entity
@using IGP.Pages.Database.Entity.Parts
@using IGP.Pages.Database.Parts
@using IGP.Pages.Documentation
@using IGP.Pages.Documentation.Parts
@using IGP.Pages.EconomyComparison
@using IGP.Pages.EconomyComparison.Parts
@using IGP.Pages.Home
@using IGP.Pages.Home.Parts
@using IGP.Pages.MakingOf.Parts
@using IGP.Pages.MemoryTester.Parts
@using IGP.Pages.Notes
@using IGP.Pages.Notes.Parts
@using IGP.Pages.RoadMap.Parts
@using IGP.Portals
@using IGP.Utils
@using Markdig

2
IGP/wwwroot/generated/WebSectionModels.json

@ -1 +1 @@
[{"Id":1,"Name":"Tools","Description":"Tools Stuff","Order":1,"IsPrivate":"False","WebPageModels":[]},{"Id":2,"Name":"Resources","Description":"Resources Stuff","Order":2,"IsPrivate":"False","WebPageModels":[]},{"Id":3,"Name":"General","Description":"About Stuff","Order":3,"IsPrivate":"False","WebPageModels":[]},{"Id":4,"Name":"Development","Description":"Development Stuff","Order":4,"IsPrivate":"False","WebPageModels":[]},{"Id":5,"Name":"Settings","Description":"Settings Stuff","Order":5,"IsPrivate":"False","WebPageModels":[]}]
[{"Id":1,"Name":"Tools","Description":"Tools Stuff","Order":1,"IsPrivate":"False","Icon":"fa-screwdriver-wrench","OnlyIcon":false,"WebPageModels":[]},{"Id":2,"Name":"Resources","Description":"Resources Stuff","Order":2,"IsPrivate":"False","Icon":"fa-toolbox","OnlyIcon":false,"WebPageModels":[]},{"Id":3,"Name":"General","Description":"About Stuff","Order":3,"IsPrivate":"False","Icon":"fa-circle-info","OnlyIcon":false,"WebPageModels":[]},{"Id":4,"Name":"Development","Description":"Development Stuff","Order":4,"IsPrivate":"False","Icon":"fa-code","OnlyIcon":false,"WebPageModels":[]},{"Id":5,"Name":"Settings","Description":"Settings Stuff","Order":5,"IsPrivate":"False","Icon":"fa-gear","OnlyIcon":false,"WebPageModels":[]}]

3
IGP/wwwroot/index.html

@ -5,6 +5,8 @@
<meta charset="utf-8"/>
<meta content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" name="viewport"/>
<title>IGP Fan Reference</title>
<meta name="description" content='"IMMORTAL: Gates of Pyre" Fan Reference Community Tool, such as build calculator, unit database and gameplay notes.'/>
<meta name="keywords" content="Immortal Build Calculator, Immortal Calculator, Immortal Database, Immortal Harass Calculator, Immortal Tools, IMMORTAL: Gates of Pyre Build Calculator, IMMORTAL: Gates of Pyre Calculator, IMMORTAL: Gates of Pyre Database, IMMORTAL: Gates of Pyre Harass Calculator, IMMORTAL: Gates of Pyre Tools"/>
<base href="/"/>
<link crossorigin="anonymous" href="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/css/bootstrap.min.css"
integrity="sha384-zCbKRCUGaJDkqS1kPbPd7TveP5iyJE0EjAuZQTgFLD2ylzuqKfdKlfG/eSrtxUkn" rel="stylesheet">
@ -32,6 +34,7 @@
src="https://cdn.jsdelivr.net/npm/bootstrap@4.6.1/dist/js/bootstrap.min.js"></script>
<script src="javascript/download.js"></script>
<script src="_content/Blazor-Analytics/blazor-analytics.js"></script>
<script src="https://kit.fontawesome.com/c77aa98347.js" crossorigin="anonymous"></script>
<script>
Blazor.start({
applicationCulture: 'en-US'

2
Model/BuildOrders/BuildOrderModel.cs

@ -28,7 +28,7 @@ public class BuildOrderModel
public Dictionary<string, int> UniqueCompletedTimes { get; set; } = new();
public Dictionary<string, int> UniqueCompletedCount { get; set; } = new();
public Dictionary<int, int> SupplyCountTimes { get; set; } = new();
public Dictionary<string, List<EntityModel>> UniqueCompleted { get; set; } = new();
public List<TrainingCapacityUsedModel> TrainingCapacityUsed { get; set; } = new();

2
Model/Chart/PointModel.cs

@ -11,7 +11,7 @@ public class PointModel
var display = Interval / highestInterval * displayScale;
return ((int)display).ToString();
}
public string GetValue(float highestValue, float displayScale)
{

241
Model/Entity/Data/DATA.cs

@ -60,12 +60,11 @@ public class DATA
Notes = @"Doesn't take up a scout slot."
})
.AddPart(new EntityRequirementModel { Id = DataType.TEAPOT_Teapot })
.AddPart(new EntitySupplyModel{Takes = 1})
.AddPart(new EntitySupplyModel { Takes = 1 })
.AddPart(new EntityProductionModel { Alloy = 100, Ether = 50 })
.AddPart(new EntityVitalityModel { Health = 100, DefenseLayer = 30, Armor = ArmorType.Light })
.AddPart(new EntityMovementModel { Speed = 280, Movement = MovementType.Air })
.AddPart(new EntityIdPassiveModel{ Id = DataType.PASSIVE_Detection})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Detection })
},
// Families
@ -448,7 +447,8 @@ public class DATA
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityVanguardAddedModel
{ ReplaceId = DataType.UNIT_Sipari, ImmortalId = DataType.IMMORTAL_Orzum })
.AddPart(new EntityProductionModel { Alloy = 100, BuildTime = 24, ProducedBy = DataType.BUILDING_LegionHall})
.AddPart(new EntityProductionModel
{ Alloy = 100, BuildTime = 24, ProducedBy = DataType.BUILDING_LegionHall })
.AddPart(new EntitySupplyModel { Takes = 4 })
.AddPart(new EntityVitalityModel { Health = 180, DefenseLayer = 100, Armor = ArmorType.Light })
.AddPart(new EntityWeaponModel
@ -477,7 +477,8 @@ public class DATA
.AddPart(new EntityTierModel { Tier = 3 })
.AddPart(new EntityHotkeyModel { Hotkey = "R", HoldSpace = true, HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 200, Ether = 125, BuildTime = 40, ProducedBy = DataType.BUILDING_Angelarium })
.AddPart(new EntityProductionModel
{ Alloy = 200, Ether = 125, BuildTime = 40, ProducedBy = DataType.BUILDING_Angelarium })
.AddPart(new EntityVitalityModel { Health = 350, DefenseLayer = 120, Armor = ArmorType.Heavy })
.AddPart(new EntitySupplyModel { Takes = 6 })
.AddPart(new EntityWeaponModel
@ -516,7 +517,8 @@ public class DATA
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityVanguardAddedModel
{ ReplaceId = DataType.UNIT_Magi, ImmortalId = DataType.IMMORTAL_Ajari })
.AddPart(new EntityProductionModel { Alloy = 75, Ether = 75, BuildTime = 35, ProducedBy = DataType.BUILDING_LegionHall })
.AddPart(new EntityProductionModel
{ Alloy = 75, Ether = 75, BuildTime = 35, ProducedBy = DataType.BUILDING_LegionHall })
.AddPart(new EntitySupplyModel { Takes = 4 })
.AddPart(new EntityVitalityModel
{ Health = 140, DefenseLayer = 100, Armor = ArmorType.Light, IsEtheric = true })
@ -550,7 +552,8 @@ public class DATA
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityVanguardAddedModel
{ ReplaceId = DataType.UNIT_Hallower, ImmortalId = DataType.IMMORTAL_Ajari })
.AddPart(new EntityProductionModel { Alloy = 150, Ether = 100, BuildTime = 40, ProducedBy = DataType.BUILDING_SoulFoundry })
.AddPart(new EntityProductionModel
{ Alloy = 150, Ether = 100, BuildTime = 40, ProducedBy = DataType.BUILDING_SoulFoundry })
.AddPart(new EntitySupplyModel { Takes = 5 })
.AddPart(new EntityVitalityModel
{ Energy = 100, Health = 100, DefenseLayer = 100, Armor = ArmorType.Medium, IsEtheric = true })
@ -586,7 +589,8 @@ public class DATA
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityVanguardAddedModel
{ ReplaceId = DataType.UNIT_Underspine, ImmortalId = DataType.IMMORTAL_Mala })
.AddPart(new EntityProductionModel { Alloy = 175, Ether = 50, BuildTime = 35, ProducedBy = DataType.BUILDING_AmberWomb })
.AddPart(new EntityProductionModel
{ Alloy = 175, Ether = 50, BuildTime = 35, ProducedBy = DataType.BUILDING_AmberWomb })
.AddPart(new EntitySupplyModel { Takes = 5 })
.AddPart(new EntityVitalityModel
{ Health = 160, DefenseLayer = 40, Armor = ArmorType.Medium, IsEtheric = false })
@ -615,7 +619,8 @@ public class DATA
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityVanguardAddedModel
{ ReplaceId = DataType.UNIT_RedSeer, ImmortalId = DataType.IMMORTAL_Mala })
.AddPart(new EntityProductionModel { Alloy = 60, Ether = 150, BuildTime = 45, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntityProductionModel
{ Alloy = 60, Ether = 150, BuildTime = 45, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntitySupplyModel { Takes = 4 })
.AddPart(new EntityVitalityModel
{
@ -645,7 +650,8 @@ public class DATA
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityVanguardAddedModel
{ ReplaceId = DataType.UNIT_MaskedHunter, ImmortalId = DataType.IMMORTAL_Xol })
.AddPart(new EntityProductionModel { Alloy = 50, Ether = 0, BuildTime = 20, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntityProductionModel
{ Alloy = 50, Ether = 0, BuildTime = 20, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntitySupplyModel { Takes = 2 })
.AddPart(new EntityVitalityModel
{ Health = 85, DefenseLayer = 10, Armor = ArmorType.Light, IsEtheric = false })
@ -674,7 +680,8 @@ public class DATA
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityVanguardAddedModel
{ ReplaceId = DataType.UNIT_Bloodbound, ImmortalId = DataType.IMMORTAL_Xol })
.AddPart(new EntityProductionModel { Alloy = 80, Ether = 80, BuildTime = 35, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntityProductionModel
{ Alloy = 80, Ether = 80, BuildTime = 35, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntitySupplyModel { Takes = 4 })
.AddPart(new EntityVitalityModel
{
@ -689,7 +696,6 @@ public class DATA
})
.AddPart(new EntityIdAbilityModel { Id = DataType.ABILITY_LethalBond })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_CastingFromBlood })
},
// Units
// Q'Rath
@ -719,7 +725,8 @@ public class DATA
.AddPart(new EntityVanguardReplacedModel
{ ImmortalId = DataType.IMMORTAL_Orzum, ReplacedById = DataType.VANGUARD_Zentari_Orzum })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 75, BuildTime = 25, ProducedBy = DataType.BUILDING_LegionHall })
.AddPart(new EntityProductionModel
{ Alloy = 75, BuildTime = 25, ProducedBy = DataType.BUILDING_LegionHall })
.AddPart(new EntitySupplyModel { Takes = 3 })
.AddPart(new EntityVitalityModel { Health = 140, DefenseLayer = 70, Armor = ArmorType.Light })
.AddPart(new EntityRequirementModel
@ -750,7 +757,8 @@ public class DATA
.AddPart(new EntityTierModel { Tier = 1.5f })
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 75, Ether = 75, BuildTime = 35, ProducedBy = DataType.BUILDING_LegionHall })
.AddPart(new EntityProductionModel
{ Alloy = 75, Ether = 75, BuildTime = 35, ProducedBy = DataType.BUILDING_LegionHall })
.AddPart(new EntitySupplyModel { Takes = 3 })
.AddPart(new EntityVanguardReplacedModel
{ ImmortalId = DataType.IMMORTAL_Ajari, ReplacedById = DataType.VANGUARD_Saoshin_Ajari })
@ -788,7 +796,8 @@ public class DATA
.AddPart(new EntityTierModel { Tier = 1.5f })
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 100, Ether = 40, BuildTime = 30, ProducedBy = DataType.BUILDING_SoulFoundry })
.AddPart(new EntityProductionModel
{ Alloy = 100, Ether = 40, BuildTime = 30, ProducedBy = DataType.BUILDING_SoulFoundry })
.AddPart(new EntitySupplyModel { Takes = 4 })
.AddPart(new EntityRequirementModel
{
@ -823,7 +832,8 @@ public class DATA
.AddPart(new EntityTierModel { Tier = 2 })
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 125, Ether = 10, BuildTime = 35, ProducedBy = DataType.BUILDING_SoulFoundry })
.AddPart(new EntityProductionModel
{ Alloy = 125, Ether = 10, BuildTime = 35, ProducedBy = DataType.BUILDING_SoulFoundry })
.AddPart(new EntitySupplyModel { Takes = 4 })
.AddPart(new EntityVitalityModel { Health = 120, DefenseLayer = 100, Armor = ArmorType.Medium })
.AddPart(new EntityRequirementModel
@ -855,7 +865,8 @@ public class DATA
.AddPart(new EntityTierModel { Tier = 2 })
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 150, Ether = 75, BuildTime = 35, ProducedBy = DataType.BUILDING_SoulFoundry })
.AddPart(new EntityProductionModel
{ Alloy = 150, Ether = 75, BuildTime = 35, ProducedBy = DataType.BUILDING_SoulFoundry })
.AddPart(new EntitySupplyModel { Takes = 5 })
.AddPart(new EntityVitalityModel { Health = 175, DefenseLayer = 150, Armor = ArmorType.Medium })
.AddPart(new EntityRequirementModel
@ -890,7 +901,8 @@ public class DATA
.AddPart(new EntityTierModel { Tier = 2 })
.AddPart(new EntityHotkeyModel { Hotkey = "W", HoldSpace = true, HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 190, Ether = 75, BuildTime = 35, ProducedBy = DataType.BUILDING_SoulFoundry })
.AddPart(new EntityProductionModel
{ Alloy = 190, Ether = 75, BuildTime = 35, ProducedBy = DataType.BUILDING_SoulFoundry })
.AddPart(new EntitySupplyModel { Takes = 5 })
.AddPart(new EntityVitalityModel { Health = 200, DefenseLayer = 100, Armor = ArmorType.Heavy })
.AddPart(new EntityMovementModel { Speed = 340, Movement = MovementType.Ground })
@ -924,7 +936,8 @@ public class DATA
.AddPart(new EntityVanguardReplacedModel
{ ImmortalId = DataType.IMMORTAL_Ajari, ReplacedById = DataType.VANGUARD_ArkMother_Ajari })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 150, Ether = 75, BuildTime = 35, ProducedBy = DataType.BUILDING_SoulFoundry })
.AddPart(new EntityProductionModel
{ Alloy = 150, Ether = 75, BuildTime = 35, ProducedBy = DataType.BUILDING_SoulFoundry })
.AddPart(new EntitySupplyModel { Takes = 5 })
.AddPart(new EntityRequirementModel
{
@ -956,7 +969,8 @@ public class DATA
.AddPart(new EntityTierModel { Tier = 3 })
.AddPart(new EntityHotkeyModel { Hotkey = "E", HoldSpace = true, HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 150, Ether = 75, BuildTime = 35, ProducedBy = DataType.BUILDING_Angelarium })
.AddPart(new EntityProductionModel
{ Alloy = 150, Ether = 75, BuildTime = 35, ProducedBy = DataType.BUILDING_Angelarium })
.AddPart(new EntitySupplyModel { Takes = 5 })
.AddPart(new EntityVitalityModel { Health = 150, DefenseLayer = 100, Armor = ArmorType.Medium })
.AddPart(new EntityMovementModel { Speed = 525, Movement = MovementType.Air })
@ -982,7 +996,8 @@ public class DATA
.AddPart(new EntityTierModel { Tier = 3 })
.AddPart(new EntityHotkeyModel { Hotkey = "A", HoldSpace = true, HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 250, Ether = 100, BuildTime = 50, ProducedBy = DataType.BUILDING_Angelarium })
.AddPart(new EntityProductionModel
{ Alloy = 250, Ether = 100, BuildTime = 50, ProducedBy = DataType.BUILDING_Angelarium })
.AddPart(new EntitySupplyModel { Takes = 9 })
.AddPart(new EntityVitalityModel { Health = 350, DefenseLayer = 200, Armor = ArmorType.Heavy })
.AddPart(new EntityRequirementModel
@ -1020,7 +1035,8 @@ public class DATA
.AddPart(new EntityVanguardReplacedModel
{ ImmortalId = DataType.IMMORTAL_Orzum, ReplacedById = DataType.VANGUARD_Sceptre_Orzum })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 175, Ether = 100, BuildTime = 40, ProducedBy = DataType.BUILDING_Angelarium })
.AddPart(new EntityProductionModel
{ Alloy = 175, Ether = 100, BuildTime = 40, ProducedBy = DataType.BUILDING_Angelarium })
.AddPart(new EntitySupplyModel { Takes = 6 })
.AddPart(new EntityRequirementModel
{
@ -1042,7 +1058,8 @@ public class DATA
.AddPart(new EntityTierModel { Tier = 3.5f })
.AddPart(new EntityHotkeyModel { Hotkey = "F", HoldSpace = true, HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 110, Ether = 250, BuildTime = 55, ProducedBy = DataType.BUILDING_Angelarium })
.AddPart(new EntityProductionModel
{ Alloy = 110, Ether = 250, BuildTime = 55, ProducedBy = DataType.BUILDING_Angelarium })
.AddPart(new EntitySupplyModel { Takes = 6 })
.AddPart(new EntityRequirementModel
{
@ -1102,7 +1119,8 @@ public class DATA
Requirement = RequirementType.Production_Building
})
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityProductionModel { Alloy = 50, BuildTime = 20, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntityProductionModel
{ Alloy = 50, BuildTime = 20, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntitySupplyModel { Takes = 2 })
.AddPart(new EntityVitalityModel
{ Health = 85, DefenseLayer = 25, Defense = DefenseType.Overgrowth, Armor = ArmorType.Light })
@ -1130,7 +1148,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityProductionModel { Alloy = 75, Ether = 30, BuildTime = 25, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntityProductionModel
{ Alloy = 75, Ether = 30, BuildTime = 25, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntitySupplyModel { Takes = 3 })
.AddPart(new EntityVitalityModel { Health = 160, DefenseLayer = 70, Armor = ArmorType.Heavy })
.AddPart(new EntityMovementModel { Speed = 378, Movement = MovementType.Ground })
@ -1161,7 +1180,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityProductionModel { Alloy = 80, Ether = 80, BuildTime = 30, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntityProductionModel
{ Alloy = 80, Ether = 80, BuildTime = 30, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntitySupplyModel { Takes = 4 })
.AddPart(new EntityVitalityModel
{ Energy = 60, Health = 100, DefenseLayer = 40, Armor = ArmorType.Light })
@ -1174,7 +1194,6 @@ public class DATA
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_QuenchingScythes })
.AddPart(new EntityIdPassiveModel { Id = DataType.ABILITY_CullingStrike })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_CastingFromBlood })
},
{
DataType.UNIT_RedSeer,
@ -1195,7 +1214,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "F", HoldSpace = true, HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityProductionModel { Alloy = 40, Ether = 140, BuildTime = 40, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntityProductionModel
{ Alloy = 40, Ether = 140, BuildTime = 40, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntitySupplyModel { Takes = 3 })
.AddPart(new EntityVitalityModel
{
@ -1233,7 +1253,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HoldSpace = true, HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityProductionModel { Alloy = 70, Ether = 50, BuildTime = 25, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntityProductionModel
{ Alloy = 70, Ether = 50, BuildTime = 25, ProducedBy = DataType.BUILDING_AltarOfTheWorthy })
.AddPart(new EntitySupplyModel { Takes = 3 })
.AddPart(new EntityVitalityModel { Health = 140, DefenseLayer = 40, Armor = ArmorType.Medium })
.AddPart(new EntityMovementModel { Speed = 350, Movement = MovementType.Ground })
@ -1263,7 +1284,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityProductionModel { Alloy = 95, Ether = 20, BuildTime = 30, ProducedBy = DataType.BUILDING_AmberWomb })
.AddPart(new EntityProductionModel
{ Alloy = 95, Ether = 20, BuildTime = 30, ProducedBy = DataType.BUILDING_AmberWomb })
.AddPart(new EntitySupplyModel { Takes = 4 })
.AddPart(new EntityVitalityModel { Health = 100, DefenseLayer = 40, Armor = ArmorType.Medium })
.AddPart(new EntityMovementModel { Speed = 382, Movement = MovementType.Ground })
@ -1293,7 +1315,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityProductionModel { Alloy = 150, Ether = 80, BuildTime = 40, ProducedBy = DataType.BUILDING_AmberWomb })
.AddPart(new EntityProductionModel
{ Alloy = 150, Ether = 80, BuildTime = 40, ProducedBy = DataType.BUILDING_AmberWomb })
.AddPart(new EntitySupplyModel { Takes = 5 })
.AddPart(new EntityVitalityModel { Health = 175, DefenseLayer = 60, Armor = ArmorType.Heavy })
.AddPart(new EntityMovementModel { Speed = 350, Movement = MovementType.Ground })
@ -1328,7 +1351,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "E", HoldSpace = true, HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityProductionModel { Alloy = 40, Ether = 40, BuildTime = 30, ProducedBy = DataType.BUILDING_BoneCanopy })
.AddPart(new EntityProductionModel
{ Alloy = 40, Ether = 40, BuildTime = 30, ProducedBy = DataType.BUILDING_BoneCanopy })
.AddPart(new EntitySupplyModel { Takes = 2 })
.AddPart(new EntityVitalityModel { Health = 35, DefenseLayer = 10, Armor = ArmorType.Light })
.AddPart(new EntityMovementModel { Speed = 532, Movement = MovementType.Air })
@ -1353,7 +1377,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "R", HoldSpace = true, HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityProductionModel { Alloy = 80, Ether = 50, BuildTime = 30, ProducedBy = DataType.BUILDING_BoneCanopy })
.AddPart(new EntityProductionModel
{ Alloy = 80, Ether = 50, BuildTime = 30, ProducedBy = DataType.BUILDING_BoneCanopy })
.AddPart(new EntitySupplyModel { Takes = 3 })
.AddPart(new EntityVitalityModel { Health = 120, DefenseLayer = 40, Armor = ArmorType.Light })
.AddPart(new EntityMovementModel { Speed = 525, Movement = MovementType.Air })
@ -1375,7 +1400,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "W", HoldSpace = true, HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityProductionModel { Alloy = 80, Ether = 30, BuildTime = 30, ProducedBy = DataType.BUILDING_AmberWomb })
.AddPart(new EntityProductionModel
{ Alloy = 80, Ether = 30, BuildTime = 30, ProducedBy = DataType.BUILDING_AmberWomb })
.AddPart(new EntitySupplyModel { Takes = 3 })
.AddPart(new EntityVitalityModel { Health = 120, DefenseLayer = 45, Armor = ArmorType.Medium })
.AddPart(new EntityMovementModel { Speed = 350, Movement = MovementType.Ground })
@ -1403,7 +1429,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "A", HoldSpace = true, HotkeyGroup = "Z" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityProductionModel { Alloy = 190, Ether = 150, BuildTime = 50, ProducedBy = DataType.BUILDING_BoneCanopy })
.AddPart(new EntityProductionModel
{ Alloy = 190, Ether = 150, BuildTime = 50, ProducedBy = DataType.BUILDING_BoneCanopy })
.AddPart(new EntitySupplyModel { Takes = 8 })
.AddPart(new EntityVitalityModel { Health = 350, DefenseLayer = 100, Armor = ArmorType.Heavy })
.AddPart(new EntityMovementModel { Speed = 210, Movement = MovementType.Air })
@ -1438,7 +1465,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "TAB" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 100, Ether = 100, BuildTime = 100, ProducedBy = DataType.BUILDING_Reliquary })
.AddPart(new EntityProductionModel
{ Alloy = 100, Ether = 100, BuildTime = 100, ProducedBy = DataType.BUILDING_Reliquary })
.AddPart(new EntityRequirementModel
{
Id = DataType.BUILDING_Reliquary,
@ -1457,7 +1485,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "R", HoldSpace = true, HotkeyGroup = "TAB" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 80, Ether = 80, BuildTime = 34, ProducedBy = DataType.BUILDING_HouseOfFadingSaints })
.AddPart(new EntityProductionModel
{ Alloy = 80, Ether = 80, BuildTime = 34, ProducedBy = DataType.BUILDING_HouseOfFadingSaints })
.AddPart(new EntityRequirementModel
{
Id = DataType.BUILDING_HouseOfFadingSaints,
@ -1475,7 +1504,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "TAB", HoldSpace = true })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 100, Ether = 100, BuildTime = 43, ProducedBy = DataType.BUILDING_EyeOfAros })
.AddPart(new EntityProductionModel
{ Alloy = 100, Ether = 100, BuildTime = 43, ProducedBy = DataType.BUILDING_EyeOfAros })
.AddPart(new EntityRequirementModel
{
Id = DataType.BUILDING_EyeOfAros,
@ -1495,7 +1525,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "TAB" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 50, Ether = 100, BuildTime = 60, ProducedBy = DataType.BUILDING_Reliquary })
.AddPart(new EntityProductionModel
{ Alloy = 50, Ether = 100, BuildTime = 60, ProducedBy = DataType.BUILDING_Reliquary })
.AddPart(new EntityRequirementModel
{ Id = DataType.BUILDING_Reliquary, Requirement = RequirementType.Research_Building })
.AddPart(new EntityVanguardAddedModel
@ -1511,7 +1542,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "TAB", HoldSpace = true })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 75, Ether = 75, BuildTime = 29, ProducedBy = DataType.BUILDING_HouseOfFadingSaints })
.AddPart(new EntityProductionModel
{ Alloy = 75, Ether = 75, BuildTime = 29, ProducedBy = DataType.BUILDING_HouseOfFadingSaints })
.AddPart(new EntityRequirementModel
{
Id = DataType.BUILDING_HouseOfFadingSaints,
@ -1525,7 +1557,8 @@ public class DATA
{ Name = "Windstep", Descriptive = DescriptiveType.Upgrade, Description = "Unlocks windstep." })
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "TAB" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 50, Ether = 75, BuildTime = 55, ProducedBy = DataType.BUILDING_Reliquary })
.AddPart(new EntityProductionModel
{ Alloy = 50, Ether = 75, BuildTime = 55, ProducedBy = DataType.BUILDING_Reliquary })
.AddPart(new EntityRequirementModel
{ Id = DataType.BUILDING_Reliquary, Requirement = RequirementType.Production_Building })
},
@ -1539,7 +1572,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "TAB" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 150, Ether = 100, BuildTime = 43, ProducedBy = DataType.BUILDING_Reliquary })
.AddPart(new EntityProductionModel
{ Alloy = 150, Ether = 100, BuildTime = 43, ProducedBy = DataType.BUILDING_Reliquary })
.AddPart(new EntityRequirementModel
{ Id = DataType.BUILDING_Reliquary, Requirement = RequirementType.Research_Building })
.AddPart(new EntityIdUpgradeModel { Id = DataType.UPGRADE_WindStep })
@ -1555,7 +1589,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "TAB" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 75, Ether = 100, BuildTime = 60, ProducedBy = DataType.BUILDING_HouseOfFadingSaints })
.AddPart(new EntityProductionModel
{ Alloy = 75, Ether = 100, BuildTime = 60, ProducedBy = DataType.BUILDING_HouseOfFadingSaints })
.AddPart(new EntityRequirementModel
{
Id = DataType.BUILDING_HouseOfFadingSaints,
@ -1572,7 +1607,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HoldSpace = true, HotkeyGroup = "TAB" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 100, Ether = 100, BuildTime = 43, ProducedBy = DataType.BUILDING_EyeOfAros })
.AddPart(new EntityProductionModel
{ Alloy = 100, Ether = 100, BuildTime = 43, ProducedBy = DataType.BUILDING_EyeOfAros })
.AddPart(new EntityRequirementModel
{
Id = DataType.BUILDING_EyeOfAros,
@ -1591,7 +1627,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "A", HoldSpace = true, HotkeyGroup = "TAB" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 100, Ether = 75, BuildTime = 45, ProducedBy = DataType.BUILDING_BearerOfTheCrown })
.AddPart(new EntityProductionModel
{ Alloy = 100, Ether = 75, BuildTime = 45, ProducedBy = DataType.BUILDING_BearerOfTheCrown })
.AddPart(new EntityRequirementModel
{
Id = DataType.BUILDING_BearerOfTheCrown, Requirement = RequirementType.Production_Building
@ -1607,7 +1644,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "R", HoldSpace = true, HotkeyGroup = "TAB" })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityProductionModel { Alloy = 150, Ether = 100, BuildTime = 30, ProducedBy = DataType.BUILDING_EyeOfAros })
.AddPart(new EntityProductionModel
{ Alloy = 150, Ether = 100, BuildTime = 30, ProducedBy = DataType.BUILDING_EyeOfAros })
.AddPart(new EntityRequirementModel
{
Id = DataType.BUILDING_EyeOfAros, Requirement = RequirementType.Production_Building
@ -1638,7 +1676,8 @@ public class DATA
Id = DataType.BUILDING_Neurocyte,
Requirement = RequirementType.Production_Building
})
.AddPart(new EntityProductionModel { Alloy = 100, Ether = 80, BuildTime = 60, ProducedBy = DataType.BUILDING_Neurocyte })
.AddPart(new EntityProductionModel
{ Alloy = 100, Ether = 80, BuildTime = 60, ProducedBy = DataType.BUILDING_Neurocyte })
},
{
DataType.UPGRADE_BloodMothersFevor,
@ -1659,7 +1698,8 @@ public class DATA
Id = DataType.BUILDING_AltarOfTheWorthy,
Requirement = RequirementType.Research_Building
})
.AddPart(new EntityProductionModel { Alloy = 100, Ether = 150, BuildTime = 80, ProducedBy = DataType.BUILDING_RedVale })
.AddPart(new EntityProductionModel
{ Alloy = 100, Ether = 150, BuildTime = 80, ProducedBy = DataType.BUILDING_RedVale })
},
{
DataType.UPGRADE_DenInstinct,
@ -1672,7 +1712,8 @@ public class DATA
Id = DataType.BUILDING_Neurocyte,
Requirement = RequirementType.Research_Building
})
.AddPart(new EntityProductionModel { Alloy = 100, Ether = 120, BuildTime = 45, ProducedBy = DataType.BUILDING_Neurocyte })
.AddPart(new EntityProductionModel
{ Alloy = 100, Ether = 120, BuildTime = 45, ProducedBy = DataType.BUILDING_Neurocyte })
},
{
DataType.UPGRADE_PursuitLigaments,
@ -1685,7 +1726,8 @@ public class DATA
Id = DataType.BUILDING_Neurocyte,
Requirement = RequirementType.Research_Building
})
.AddPart(new EntityProductionModel { Alloy = 75, Ether = 100, BuildTime = 45, ProducedBy = DataType.BUILDING_Neurocyte })
.AddPart(new EntityProductionModel
{ Alloy = 75, Ether = 100, BuildTime = 45, ProducedBy = DataType.BUILDING_Neurocyte })
},
{
DataType.UPGRADE_ResinantDeploy,
@ -1703,7 +1745,8 @@ public class DATA
Id = DataType.BUILDING_AmberWomb,
Requirement = RequirementType.Research_Building
})
.AddPart(new EntityProductionModel { Alloy = 50, Ether = 100, BuildTime = 43, ProducedBy = DataType.BUILDING_Neurocyte })
.AddPart(new EntityProductionModel
{ Alloy = 50, Ether = 100, BuildTime = 43, ProducedBy = DataType.BUILDING_Neurocyte })
},
{
DataType.UPGRADE_XacalDamage,
@ -1716,7 +1759,8 @@ public class DATA
Id = DataType.BUILDING_Neurocyte,
Requirement = RequirementType.Research_Building
})
.AddPart(new EntityProductionModel { Alloy = 100, Ether = 75, BuildTime = 60, ProducedBy = DataType.BUILDING_Neurocyte })
.AddPart(new EntityProductionModel
{ Alloy = 100, Ether = 75, BuildTime = 60, ProducedBy = DataType.BUILDING_Neurocyte })
},
{
DataType.UPGRADE_BehemothCapacity,
@ -1729,7 +1773,8 @@ public class DATA
Id = DataType.BUILDING_DeepNest,
Requirement = RequirementType.Research_Building
})
.AddPart(new EntityProductionModel { Alloy = 150, Ether = 150, BuildTime = 46, ProducedBy = DataType.BUILDING_DeepNest })
.AddPart(new EntityProductionModel
{ Alloy = 150, Ether = 150, BuildTime = 46, ProducedBy = DataType.BUILDING_DeepNest })
},
{
DataType.UPGRADE_WraithBowRange,
@ -1747,7 +1792,8 @@ public class DATA
Id = DataType.BUILDING_Neurocyte,
Requirement = RequirementType.Research_Building
})
.AddPart(new EntityProductionModel { Alloy = 50, Ether = 75, BuildTime = 29, ProducedBy = DataType.BUILDING_Neurocyte })
.AddPart(new EntityProductionModel
{ Alloy = 50, Ether = 75, BuildTime = 29, ProducedBy = DataType.BUILDING_Neurocyte })
},
{
@ -1766,7 +1812,8 @@ public class DATA
Id = DataType.BUILDING_Neurocyte,
Requirement = RequirementType.Research_Building
})
.AddPart(new EntityProductionModel { Alloy = 100, Ether = 80, BuildTime = 60, ProducedBy = DataType.BUILDING_Neurocyte })
.AddPart(new EntityProductionModel
{ Alloy = 100, Ether = 80, BuildTime = 60, ProducedBy = DataType.BUILDING_Neurocyte })
.AddPart(new EntityVanguardAddedModel
{ ReplaceId = DataType.UPGRADE_Offering, ImmortalId = DataType.IMMORTAL_Xol })
},
@ -1782,7 +1829,8 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "CAPSLOCK", HotkeyGroup = "TAB", HoldSpace = false })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
.AddPart(new EntityProductionModel { Alloy = 100, Ether = 125, BuildTime = 80, ProducedBy = DataType.BUILDING_RedVale })
.AddPart(new EntityProductionModel
{ Alloy = 100, Ether = 125, BuildTime = 80, ProducedBy = DataType.BUILDING_RedVale })
.AddPart(new EntityRequirementModel
{
Id = DataType.BUILDING_RedVale,
@ -1808,7 +1856,8 @@ public class DATA
Id = DataType.BUILDING_RedVale,
Requirement = RequirementType.Research_Building
})
.AddPart(new EntityProductionModel { Alloy = 75, Ether = 120, BuildTime = 80, ProducedBy = DataType.BUILDING_RedVale })
.AddPart(new EntityProductionModel
{ Alloy = 75, Ether = 120, BuildTime = 80, ProducedBy = DataType.BUILDING_RedVale })
.AddPart(new EntityVanguardReplacedModel
{ ImmortalId = DataType.IMMORTAL_Xol, ReplacedById = DataType.ABILITY_BirthingStorm })
},
@ -1823,26 +1872,27 @@ public class DATA
Id = DataType.BUILDING_RedVale,
Requirement = RequirementType.Research_Building
})
.AddPart(new EntityProductionModel { Alloy = 75, Ether = 120, BuildTime = 80, ProducedBy = DataType.BUILDING_RedVale })
.AddPart(new EntityProductionModel
{ Alloy = 75, Ether = 120, BuildTime = 80, ProducedBy = DataType.BUILDING_RedVale })
.AddPart(new EntityVanguardAddedModel
{ ImmortalId = DataType.IMMORTAL_Mala, ReplaceId = DataType.ABILITY_BloodPlague })
},
// Passives
// Neutral
{
DataType.PASSIVE_Detection,
new EntityModel(DataType.PASSIVE_Detection, EntityType.Passive)
.AddPart(new EntityInfoModel
{
Name = "Detection", Descriptive = DescriptiveType.Passive,
Description =
@"Unit can see all hidden units in its detection radius."
})
.AddPart(new EntityFactionModel { Faction = DataType.Any })
},
{
DataType.PASSIVE_Detection,
new EntityModel(DataType.PASSIVE_Detection, EntityType.Passive)
.AddPart(new EntityInfoModel
{
Name = "Detection", Descriptive = DescriptiveType.Passive,
Description =
@"Unit can see all hidden units in its detection radius."
})
.AddPart(new EntityFactionModel { Faction = DataType.Any })
},
{
DataType.PASSIVE_BastionPassives,
new EntityModel(DataType.PASSIVE_BastionPassives, EntityType.Passive)
@ -2036,7 +2086,7 @@ public class DATA
})
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
},
{
DataType.PASSIVE_HallowedWeapons,
new EntityModel(DataType.PASSIVE_HallowedWeapons, EntityType.Passive)
@ -2289,20 +2339,20 @@ public class DATA
{ Id = DataType.UPGRADE_XacalDamage, Requirement = RequirementType.Research_Upgrade })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
},
{
DataType.PASSIVE_CastingFromBlood,
new EntityModel(DataType.PASSIVE_CastingFromBlood, EntityType.Passive)
.AddPart(new EntityInfoModel
{
Name = "Cast From Blood", Descriptive = DescriptiveType.Ability,
Description =
@"This unit can spend life to cast abilities when it doesn't have enough energy.",
Notes = "They must have at least one remaining hitpoint after to perform Cast From Blood."
})
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
},
DataType.PASSIVE_CastingFromBlood,
new EntityModel(DataType.PASSIVE_CastingFromBlood, EntityType.Passive)
.AddPart(new EntityInfoModel
{
Name = "Cast From Blood", Descriptive = DescriptiveType.Ability,
Description =
@"This unit can spend life to cast abilities when it doesn't have enough energy.",
Notes = "They must have at least one remaining hitpoint after to perform Cast From Blood."
})
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
},
{
DataType.PASSIVE_OssifyingSwarm,
new EntityModel(DataType.PASSIVE_OssifyingSwarm, EntityType.Passive)
@ -2401,8 +2451,7 @@ public class DATA
@"Spawns a mine that reveals enemy units, slows them, and makes them take increased damage for a duration."
})
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "D", HoldSpace = true})
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "D", HoldSpace = true })
.AddPart(new EntityProductionModel { DefensiveLayer = 30, Cooldown = 40 })
.AddPart(new EntityRequirementModel { Id = DataType.UPGRADE_RadiantWard })
.AddPart(new EntityVitalityModel
@ -2591,7 +2640,7 @@ public class DATA
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "D" })
.AddPart(new EntityVanguardAddedModel
{ ImmortalId = DataType.IMMORTAL_Xol, ReplaceId = DataType.ABILITY_CullingStrike })
.AddPart(new EntityProductionModel { Energy = 40, Cooldown = 4})
.AddPart(new EntityProductionModel { Energy = 40, Cooldown = 4 })
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
},
{
@ -2686,8 +2735,11 @@ public class DATA
{ ImmortalId = DataType.IMMORTAL_Mala, ReplaceId = DataType.UNIT_Acaaluk })
.AddPart(new EntityProductionModel { Energy = 80, BuildTime = 10, Cooldown = 30 })
.AddPart(new EntitySupplyModel { Takes = 0 })
.AddPart(new EntityVitalityModel { Health = 300, DefenseLayer = 100, Armor = ArmorType.Heavy,
Lasts = 75, Vision = 1000})
.AddPart(new EntityVitalityModel
{
Health = 300, DefenseLayer = 100, Armor = ArmorType.Heavy,
Lasts = 75, Vision = 1000
})
.AddPart(new EntityMovementModel { Speed = 0, Movement = MovementType.Ground })
.AddPart(new EntityWeaponModel
{
@ -3021,9 +3073,12 @@ public class DATA
Id = DataType.BUILDING_KeeperOfTheHardenedFlames,
Requirement = RequirementType.Research_Building
})
.AddPart(new EntityWeaponModel {Damage = 14, Range = 700, SecondsBetweenAttacks = 1.8f, Targets
= TargetType.All})
.AddPart(new EntityIdPassiveModel {Id = DataType.PASSIVE_HallowedWeapons})
.AddPart(new EntityWeaponModel
{
Damage = 14, Range = 700, SecondsBetweenAttacks = 1.8f, Targets
= TargetType.All
})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedWeapons })
.AddPart(new EntityVitalityModel
{ Health = 300, DefenseLayer = 150, Armor = ArmorType.Heavy, IsStructure = true })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_PsalmOfFire })

17
Model/Entity/Data/Ids_Entity.cs

@ -4,7 +4,7 @@ public static class DataType
{
public static string Any = "Any";
public static string None = "None";
public static string PYREEVENT_TowerKilled = "9a923928-b016-49f2-8c7d-950abf09e287";
public static string PYREEVENT_CampTaken = "cc27a9b2-69e2-4322-8102-7a9f8bea7871";
public static string PYREEVENT_MinerTaken = "5b158cf2-2810-4a2a-8131-c4fe4b392ce9";
@ -17,7 +17,7 @@ public static class DataType
public static string FAMILY_Rae = "ce8d60f3-b590-4619-ad90-27e65f77312b";
public static string FAMILY_Demonic = "f61a3630-9474-4ec3-bc71-997cacc52bc1";
public static string FAMILY_NazRa = "56cc934f-57a9-442c-909a-25690f836679";
public static string FACTION_Neutral = "95da29af-99da-45fe-80f6-4ae1cc0d0f47";
public static string FACTION_Aru = "fb103962-7518-48df-b7d9-83906a009db8";
public static string FACTION_Iratek = "dbc12bda-b4f2-4fa0-8270-18dc1646d62d";
@ -55,8 +55,7 @@ public static class DataType
public static string ISPELL_RedTithe = "57008163-5e3a-4b95-98f3-d00b54e18684";
public static string ISPELL_RainOfBlood = "792df385-c66a-4710-9f75-97731897a565";
public static string IPASSIVE_HealingGround = "3ec17526-8dc5-4592-9c15-ef1d9b1ca2f6";
public static string IPASSIVE_Expansionist = "b6cd4335-2165-44c3-b3dc-4500c0111870";
@ -126,7 +125,7 @@ public static class DataType
public static string UPGRADE_Stalk = "36fbc2c0-e9e3-4f54-a79a-981db908c25c";
public static string UPGRADE_Ambush = "6cf83dc9-717f-4fa9-b417-a3371474a1da";
public static string PASSIVE_Detection = "434468fa-83b2-4fc9-a38c-1a3d00bcf055";
public static string PASSIVE_WraithBowRange = "196dd8a6-2044-44e1-aac4-fbaa40552699";
@ -160,9 +159,9 @@ public static class DataType
public static string PASSIVE_HallowingRites = "9c8ae47b-954e-4a17-8f35-f128c9114b61";
public static string PASSIVE_RegentsWrath = "f111f004-6548-4430-9d13-ef44ab108ae7";
public static string PASSIVE_PsalmOfFire = "d28f6b7c-d319-4fb8-bdd4-92ede40a0751";
public static string PASSIVE_HallowedWeapons = "f9ac4b3e-d02d-42d4-8d9d-beb9c5d7edcb";
public static string PASSIVE_Zeal = "62c4942b-5578-422d-8d4e-d1789f4efa68";
public static string PASSIVE_HallowedGround = "bdb28984-246f-4642-84ab-9e83c02b3e2e";
public static string PASSIVE_Rootway = "46768d4a-5047-4973-b5ca-995cda25ee8d";
@ -181,9 +180,9 @@ public static class DataType
public static string PASSIVE_OssifyingSwarm = "b8897247-8393-416e-b246-409a6b3263c2";
public static string PASSIVE_CastingFromBlood = "c97d1cf1-67d9-402b-9fa1-1abb9bfd7bfd";
public static string PASSIVE_QuenchingScythes = "dbf07db4-e7b6-4f81-9f8e-e5391850eead";
public static string PASSIVE_AaroxBurn = "921fe250-2b97-40c0-9765-9e6c1e766dd5";

1
Model/Entity/Parts/EntityFactionModel.cs

@ -1,5 +1,4 @@
using Model.Entity.Data;
using Model.Types;
namespace Model.Entity.Parts;

12
Model/Hotkeys/HotkeyModel.cs

@ -62,8 +62,8 @@ public class HotkeyModel
PositionX = 4,
PositionY = 0
},
new()
{
KeyText = "`",
@ -71,7 +71,7 @@ public class HotkeyModel
PositionX = 0,
PositionY = 1
},
new()
{
KeyText = "Q",
@ -100,7 +100,7 @@ public class HotkeyModel
PositionX = 4,
PositionY = 1
},
new()
{
KeyText = "CAPSLOCK",
@ -108,7 +108,7 @@ public class HotkeyModel
PositionX = 0,
PositionY = 2
},
new()
{
KeyText = "A",
@ -130,7 +130,7 @@ public class HotkeyModel
PositionX = 3,
PositionY = 2
},
new()
{
KeyText = "F",

4
Model/Model.csproj

@ -4,7 +4,7 @@
<TargetFramework>net7.0</TargetFramework>
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Newtonsoft.Json" Version="13.0.1" />
<PackageReference Include="YamlDotNet" Version="11.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.1"/>
<PackageReference Include="YamlDotNet" Version="11.2.1"/>
</ItemGroup>
</Project>

3
Model/Website/WebSectionModel.cs

@ -11,5 +11,8 @@ public class WebSectionModel
public int Order { get; set; } = 0;
public string IsPrivate { get; set; } = "True";
public string Icon { get; set; } = "fa-icons";
public bool OnlyIcon { get; set; } = false;
[NotMapped] public List<WebPageModel> WebPageModels { get; set; } = new();
}

1
Services/IServices.cs

@ -29,7 +29,6 @@ public interface IToastService
public interface IDataCollectionService
{
public void SendEvent<T>(string eventName, T eventData);
}
public interface IStorageService

17
Services/Immortal/BuildOrderService.cs

@ -343,16 +343,10 @@ public class BuildOrderService : IBuildOrderService
var checkedInterval = _lastInterval;
if (supply == null || production == null || supply.Takes.Equals(0))
{
return 1;
}
if (supply == null || production == null || supply.Takes.Equals(0)) return 1;
var producedBy = production.ProducedBy;
if (producedBy == null)
{
return 1;
}
if (producedBy == null) return 1;
var uniqueCompleted = _buildOrder.UniqueCompleted[producedBy];
@ -372,7 +366,7 @@ public class BuildOrderService : IBuildOrderService
usedSlots += used.UsedSlots;
var duration = used.StopUsageTime - used.StartingUsageTime;
if (duration < shortestIncrement) shortestIncrement = duration;
}
}
if (usedSlots + supply.Takes <= trainingSlots)
{
@ -389,10 +383,7 @@ public class BuildOrderService : IBuildOrderService
checkedInterval += shortestIncrement;
didDelay = true;
if (shortestIncrement == int.MaxValue)
{
return null;
}
if (shortestIncrement == int.MaxValue) return null;
}
}

5
Services/Immortal/EntityFilterService.cs

@ -1,5 +1,4 @@
using Model.Entity.Data;
using Model.Types;
using static Services.IEntityFilterService;
namespace Services.Immortal;
@ -16,7 +15,9 @@ public class EntityFilterService : IEntityFilterService
{
private readonly List<string> _entityChoices = new();
private readonly List<string> _factionChoices = new() { DataType.Any, DataType.FACTION_QRath, DataType.FACTION_Aru };
private readonly List<string> _factionChoices = new()
{ DataType.Any, DataType.FACTION_QRath, DataType.FACTION_Aru };
private readonly List<string> _immortalChoices = new();
private string _entityType = EntityType.Army;
private string _searchText = "";

7
Services/Immortal/ImmortalSelectionService.cs

@ -5,15 +5,14 @@ namespace Services.Immortal;
public class ImmortalSelectionService : IImmortalSelectionService, IDisposable
{
private readonly IStorageService _storageService;
private string _selectedFaction = DataType.FACTION_QRath;
private string _selectedImmortal = DataType.IMMORTAL_Orzum;
private readonly IStorageService _storageService;
public ImmortalSelectionService(IStorageService storageService)
{
_storageService = storageService;
_storageService.Subscribe(RefreshDefaults);
RefreshDefaults();
@ -73,7 +72,7 @@ public class ImmortalSelectionService : IImmortalSelectionService, IDisposable
if (foundFaction != null) _selectedFaction = foundFaction;
if (foundImmortal != null) _selectedImmortal = foundImmortal;
NotifyDataChanged();
}

12
Services/Services.csproj

@ -15,15 +15,15 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Blazor-Analytics" Version="3.11.0" />
<PackageReference Include="Blazored.LocalStorage" Version="4.3.0-preview.1" />
<PackageReference Include="Microsoft.AspNetCore.Components.ProtectedBrowserStorage" Version="5.0.0-rc.1.20451.17" />
<PackageReference Include="Microsoft.JSInterop" Version="7.0.0-preview.2.22153.2" />
<PackageReference Include="YamlDotNet" Version="11.2.1" />
<PackageReference Include="Blazor-Analytics" Version="3.11.0"/>
<PackageReference Include="Blazored.LocalStorage" Version="4.3.0-preview.1"/>
<PackageReference Include="Microsoft.AspNetCore.Components.ProtectedBrowserStorage" Version="5.0.0-rc.1.20451.17"/>
<PackageReference Include="Microsoft.JSInterop" Version="7.0.0-preview.2.22153.2"/>
<PackageReference Include="YamlDotNet" Version="11.2.1"/>
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\Model\Model.csproj" />
<ProjectReference Include="..\Model\Model.csproj"/>
</ItemGroup>
</Project>

25
Services/Website/DataCollectionService.cs

@ -1,6 +1,4 @@
using Blazor.Analytics;
using Blazored.LocalStorage;
using Model.Feedback;
namespace Services.Website;
@ -14,37 +12,34 @@ public class DataCollectionKeys
public class DataCollectionService : IDataCollectionService, IDisposable
{
private readonly IAnalytics _globalTracking;
private readonly IStorageService _storageService;
private bool _isEnabled = false;
private readonly IAnalytics _globalTracking;
private bool _isEnabled;
public DataCollectionService(IAnalytics globalTracking,
IStorageService storageService)
{
_globalTracking = globalTracking;
_storageService = storageService;
_storageService.Subscribe(Refresh);
Refresh();
}
public void SendEvent<T>(string eventName, T eventData)
{
if (_isEnabled) _globalTracking.TrackEvent(eventName, eventData);
}
void IDisposable.Dispose()
{
_storageService.Unsubscribe(Refresh);
}
private void Refresh()
{
_isEnabled = _storageService.GetValue<bool>(StorageKeys.EnabledDataCollection);
}
public void SendEvent<T>(string eventName, T eventData)
{
if (_isEnabled)
{
_globalTracking.TrackEvent(eventName, eventData);
}
}
}

11
Services/Website/DialogService.cs

@ -1,7 +1,4 @@
using Microsoft.AspNetCore.Components;
using Microsoft.AspNetCore.Components.Web;
using Model.Entity.Data;
using Model.Website;
namespace Services.Website;
@ -18,11 +15,7 @@ public class DialogService : IDialogService
{
private DialogContents _dialogContents;
public DialogService()
{
}
public bool IsVisible { get; set; }
public bool IsVisible { get; set; }
public void Subscribe(Action action)
{
@ -38,7 +31,7 @@ public class DialogService : IDialogService
{
_dialogContents = dialogContents;
IsVisible = true;
NotifyDataChanged();
}

4
Services/Website/PermissionService.cs

@ -4,8 +4,8 @@ namespace Services.Website;
public class PermissionService : IPermissionService, IDisposable
{
private IJSRuntime _jsRuntime;
private readonly IStorageService _storageService;
private IJSRuntime _jsRuntime;
private IToastService _toastService;
private bool isLoaded;
private bool isStorageEnabled = false;
@ -15,7 +15,7 @@ public class PermissionService : IPermissionService, IDisposable
_jsRuntime = jsRuntime;
_toastService = toastService;
_storageService = storageService;
_storageService.Subscribe(NotifyDataChanged);
}

8
Services/Website/SearchService.cs

@ -61,7 +61,7 @@ public class SearchService : ISearchService
{
Title = webPage.Name,
PointType = "WebPage",
Summary = $"{webPage.Description}",
Summary = $"{webPage.Description}",
Href = webPage.Href
});
@ -73,7 +73,7 @@ public class SearchService : ISearchService
SearchPoints.Add(new SearchPointModel
{
Title = note.Name,
PointType = "Note",
PointType = "Note",
Href = note.GetNoteLink(),
Summary = note.Description
});
@ -84,13 +84,13 @@ public class SearchService : ISearchService
foreach (var entity in DATA.Get().Values)
{
var summary =
var summary =
entity.Info().Description.Length > 35
? entity.Info().Description.Substring(0, 30).Trim() + "..."
: entity.Info().Description.Length > 0
? entity.Info().Description
: "";
SearchPoints.Add(new SearchPointModel
{
Title = entity.Info().Name,

1
Services/Website/StorageService.cs

@ -93,6 +93,7 @@ public class StorageService : IStorageService
}
private event Action OnChange = null!;
private void NotifyDataChanged()
{
OnChange();

5
TestAutomation/BaseTest.cs

@ -35,10 +35,7 @@ public class BaseTest
options.AcceptInsecureCertificates = true;
if (DeploymentType.Equals(DeploymentType.Dev))
{
options.AddArgument("--headless");
}
if (DeploymentType.Equals(DeploymentType.Dev)) options.AddArgument("--headless");
options.AddArgument("--ignore-certificate-errors");
options.AddArgument("--start-maximized");
options.AddArgument("--test-type");

34
TestAutomation/Pages/DatabasePage.cs

@ -6,39 +6,41 @@ namespace TestAutomation.Pages;
public class DatabasePage : BaseElement
{
public DatabasePage(Website website) : base(website)
{
}
private IWebElement FilterNameInput => Website.Find("filterName");
private ReadOnlyCollection<IWebElement> EntityNames() =>
Website.FindAll("entityName");
private IWebElement EntityName(string entityType, string entityName) =>
Website.Find("entityName",
private ReadOnlyCollection<IWebElement> EntityNames()
{
return Website.FindAll("entityName");
}
private IWebElement EntityName(string entityType, string entityName)
{
return Website.Find("entityName",
$"{entityType.ToLower()}-{entityName.ToLower()}");
public DatabasePage(Website website) : base(website) { }
}
public DatabasePage FilterName(string name)
{
Website.EnterInput(FilterNameInput, name);
return this;
}
public DatabasePage GetEntityName(string entityType, string entityName, out string result)
{
result = EntityName(entityType, entityName).Text;
return this;
}
public DatabasePage GetEntityName(int index,out string result)
public DatabasePage GetEntityName(int index, out string result)
{
result = EntityNames()[index].Text;
return this;
}
}

15
TestAutomation/Pages/DatabaseSinglePage.cs

@ -5,13 +5,15 @@ namespace TestAutomation.Pages;
public class DatabaseSinglePage : BaseElement
{
public DatabaseSinglePage(Website website) : base(website)
{
}
private IWebElement EntityName => Website.Find("entityName");
private IWebElement EntityHealth => Website.Find("entityHealth");
private IWebElement InvalidSearch => Website.Find("invalidSearch");
private IWebElement ValidSearch => Website.Find("validSearch");
public DatabaseSinglePage(Website website) : base(website) { }
public DatabaseSinglePage GetEntityName(out string result)
@ -19,23 +21,22 @@ public class DatabaseSinglePage : BaseElement
result = EntityName.Text;
return this;
}
public DatabaseSinglePage GetEntityHealth(out string result)
{
result = EntityHealth.Text;
return this;
}
public DatabaseSinglePage GetInvalidSearch(out string result)
{
result = InvalidSearch.Text;
return this;
}
public DatabaseSinglePage GetValidSearch(out string result)
{
result = ValidSearch.Text;
return this;
}
}

8
TestAutomation/Shared/NavigationBar.cs

@ -1,13 +1,13 @@
using TestAutomation.Enums;
using TestAutomation.Utils;
namespace TestAutomation.Shared;
public class NavigationBar : BaseElement
{
public NavigationBar(Website website) : base(website) { }
public NavigationBar(Website website) : base(website)
{
}
private IWebElement HomeLink => Website.FindScreenSpecific("homeLink");
private IWebElement SearchButton => Website.FindScreenSpecific("searchButton");

3
TestAutomation/Shared/WebsiteSearchDialog.cs

@ -1,4 +1,3 @@
using Discord.Rest;
using TestAutomation.Utils;
namespace TestAutomation.Shared;
@ -10,7 +9,7 @@ public class WebsiteSearchDialog : BaseElement
}
public IWebElement SearchBackground => Website.Find("searchBackground");
public IWebElement SearchInput => Website.Find("searchInput");
public NavigationBar CloseDialog()

22
TestAutomation/TestAutomation.csproj

@ -9,20 +9,20 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="Discord.Net.Webhook" Version="3.6.0" />
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0-preview.2.22152.2" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0" />
<PackageReference Include="NUnit" Version="3.13.2" />
<PackageReference Include="NUnit3TestAdapter" Version="4.2.0" />
<PackageReference Include="NUnit.Analyzers" Version="3.2.0" />
<PackageReference Include="coverlet.collector" Version="3.1.0" />
<PackageReference Include="Selenium.WebDriver" Version="4.1.0" />
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="101.0.4951.4100" />
<PackageReference Include="Selenium.WebDriver.GeckoDriver" Version="0.31.0" />
<PackageReference Include="Discord.Net.Webhook" Version="3.6.0"/>
<PackageReference Include="Microsoft.Extensions.Configuration" Version="7.0.0-preview.2.22152.2"/>
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0"/>
<PackageReference Include="NUnit" Version="3.13.2"/>
<PackageReference Include="NUnit3TestAdapter" Version="4.2.0"/>
<PackageReference Include="NUnit.Analyzers" Version="3.2.0"/>
<PackageReference Include="coverlet.collector" Version="3.1.0"/>
<PackageReference Include="Selenium.WebDriver" Version="4.1.0"/>
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="101.0.4951.4100"/>
<PackageReference Include="Selenium.WebDriver.GeckoDriver" Version="0.31.0"/>
</ItemGroup>
<ItemGroup>
<Folder Include="Pages\" />
<Folder Include="Pages\"/>
</ItemGroup>
</Project>

48
TestAutomation/TestSearchFeatures.cs

@ -17,7 +17,7 @@ public class TestSearchFeatures : BaseTest
.CloseDialog()
.ClickHomeLink();
}
[Test]
public void DesktopSearchForThrone()
{
@ -31,56 +31,58 @@ public class TestSearchFeatures : BaseTest
.SelectSearchEntity("Throne")
.GetEntityName(out var name)
.GetEntityHealth(out var health);
TestReport.CheckPassed(name.Equals("Throne"), new TestMessage(){ Description = "Couldn't find Throne via search."});
TestReport.CheckPassed(!health.Trim().Equals(""), new TestMessage(){ Description = "Throne has no visible health!"});
TestReport.CheckPassed(name.Equals("Throne"),
new TestMessage { Description = "Couldn't find Throne via search." });
TestReport.CheckPassed(!health.Trim().Equals(""),
new TestMessage { Description = "Throne has no visible health!" });
}
[Test]
public void DesktopFilterForThrone()
{
TestReport.CreateTest();
Website.WebDriver.Navigate().GoToUrl(WebsiteUrl + "/database");
Website.DatabasePage
.FilterName("Throne")
.GetEntityName(0, out var name);
TestReport.CheckPassed(name.Equals("Throne"),
new TestMessage(){ Description = "Couldn't find Throne via filter."});
TestReport.CheckPassed(name.Equals("Throne"),
new TestMessage { Description = "Couldn't find Throne via filter." });
}
[Test]
public void SeeThroneByDefault()
{
TestReport.CreateTest();
Website.WebDriver.Navigate().GoToUrl(WebsiteUrl + "/database");
Website.DatabasePage
.GetEntityName( "army", "throne", out var name);
TestReport.CheckPassed(name.Equals("Throne"),
new TestMessage(){ Description = "Couldn't find Throne on the page by default."});
.GetEntityName("army", "throne", out var name);
TestReport.CheckPassed(name.Equals("Throne"),
new TestMessage { Description = "Couldn't find Throne on the page by default." });
}
[Test]
public void DirectLinkNotThroneFailure()
{
TestReport.CreateTest();
Website.WebDriver.Navigate().GoToUrl(WebsiteUrl + "/database/not throne");
Website.DatabaseSinglePage
.GetInvalidSearch(out var invalidSearch)
.GetValidSearch(out var validSearch);
TestReport.CheckPassed(invalidSearch.Equals("not throne"),
new TestMessage(){ Description = "Couldn't find invalid search text on the page."});
TestReport.CheckPassed(validSearch.Equals("Throne"),
new TestMessage(){ Description = "Couldn't find valid search text on the page."});
TestReport.CheckPassed(invalidSearch.Equals("not throne"),
new TestMessage { Description = "Couldn't find invalid search text on the page." });
TestReport.CheckPassed(validSearch.Equals("Throne"),
new TestMessage { Description = "Couldn't find valid search text on the page." });
Website.WebDriver.Navigate().GoToUrl(WebsiteUrl + "/database/not throne");
}
}

32
TestAutomation/Utils/Website.cs

@ -8,7 +8,7 @@ namespace TestAutomation.Utils;
public class Website
{
public readonly ScreenType ScreenType = ScreenType.Desktop;
public Website(IWebDriver webDriver)
{
WebDriver = webDriver;
@ -17,10 +17,10 @@ public class Website
HarassCalculatorPage = new HarassCalculatorPage(this);
DatabasePage = new DatabasePage(this);
DatabaseSinglePage = new DatabaseSinglePage(this);
// Navigation
NavigationBar = new NavigationBar(this);
// Dialogs
WebsiteSearchDialog = new WebsiteSearchDialog(this);
}
@ -36,7 +36,7 @@ public class Website
public IWebElement FindScreenSpecific(string byId)
{
var screenSpecificId = $"{ScreenType.ToString().ToLower()}-{byId}";
try
{
return WebDriver.FindElement(By.Id(screenSpecificId));
@ -44,11 +44,11 @@ public class Website
catch (Exception e)
{
throw new Exception($"Couldn't find {screenSpecificId}. Element does not exist on current page. " +
$"\n\nPerhaps an Id is missing.");
"\n\nPerhaps an Id is missing.");
}
}
public IWebElement Find(string byId, string withParentId)
{
IWebElement parent;
@ -60,9 +60,9 @@ public class Website
catch (Exception e)
{
throw new Exception($"Couldn't find parent {withParentId}. Element does not exist on current page. " +
$"\n\nPerhaps an Id is missing.");
"\n\nPerhaps an Id is missing.");
}
try
{
return parent.FindElement(By.Id(byId));
@ -70,10 +70,10 @@ public class Website
catch (Exception e)
{
throw new Exception($"Couldn't find {byId}. Element does not exist on current page. " +
$"\n\nPerhaps an Id is missing.");
"\n\nPerhaps an Id is missing.");
}
}
public IWebElement Find(string byId)
{
try
@ -83,10 +83,10 @@ public class Website
catch (Exception e)
{
throw new Exception($"Couldn't find {byId}. Element does not exist on current page. " +
$"\n\nPerhaps an Id is missing.");
"\n\nPerhaps an Id is missing.");
}
}
public ReadOnlyCollection<IWebElement> FindAll(string byId)
{
try
@ -96,11 +96,11 @@ public class Website
catch (Exception e)
{
throw new Exception($"Couldn't find {byId}. Element does not exist on current page. " +
$"\n\nPerhaps an Id is missing.");
"\n\nPerhaps an Id is missing.");
}
}
public IWebElement FindButtonWithLabel(string label)
{
try
@ -112,7 +112,7 @@ public class Website
throw new Exception($"Couldn't find with label: {label}. Element does not exist on current page. ");
}
}
//@FindBy(xpath = "//div[@label='First Name']")
public IList<IWebElement> FindChildren(string ofId, string tagname)
@ -139,7 +139,7 @@ public class Website
.Click()
.Perform();
}
public IWebElement Click(IWebElement element)
{
try

Loading…
Cancel
Save