Browse Source

feature(BuildCalc) Added reset button, can change micro delay, and can alter timing interval again

main
Jonathan McCaffrey 4 years ago
parent
commit
04c1718259
  1. 6
      Components/Display/DevOnlyComponent.razor
  2. 21
      Components/Display/PanelComponent.razor
  3. 9
      Components/Feedback/AlertComponent.razor
  4. 6
      Components/Form/FormCheckboxComponent.razor
  5. 6
      Components/Form/FormDisplayComponent.razor
  6. 3
      Components/Form/FormEscapeCodeComponent.razor
  7. 45
      Components/Form/FormGuessComponent.razor
  8. 6
      Components/Form/FormNumberComponent.razor
  9. 6
      Components/Form/FormSelectComponent.razor
  10. 9
      Components/Form/FormTextAreaComponent.razor
  11. 9
      Components/Form/FormTextComponent.razor
  12. 6
      Components/Info/InfoBodyComponent.razor
  13. 3
      Components/Inputs/ButtonComponent.razor
  14. 11
      Components/Inputs/ButtonGroupComponent.razor
  15. 3
      Components/Inputs/ButtonType.cs
  16. 1
      Components/Inputs/CodeLinkComponent.razor
  17. 1
      Components/Inputs/EditLinkComponent.razor
  18. 8
      Components/Inputs/EntityLabelComponent.razor
  19. 5
      Components/Layout/LayoutWithSidebarComponent.razor
  20. 10
      Components/MarkdownContent/MarkdownContent.razor
  21. 15
      Components/Navigation/DesktopNavLinkComponent.razor
  22. 10
      Components/Navigation/DesktopNavSectionComponent.razor
  23. 26
      Components/Navigation/MobileNavComponent.razor
  24. 16
      Components/Navigation/TabletNavComponent.razor
  25. 10
      Components/Shared/DisplayableContent.razor
  26. 10
      Components/Shared/DisplayableRoute.razor
  27. 9
      Contexts/DatabaseContext.cs
  28. BIN
      IGP/Database.db
  29. 16
      IGP/Index.razor
  30. 6
      IGP/Localizations.Designer.cs
  31. 6
      IGP/Localizations.resx
  32. 103
      IGP/Pages/BuildCalculator/BuildCalculatorPage.razor
  33. 15
      IGP/Pages/BuildCalculator/Parts/ArmyComponent.razor
  34. 2
      IGP/Pages/BuildCalculator/Parts/BuildOrderComponent.razor
  35. 6
      IGP/Pages/BuildCalculator/Parts/ChartComponent.razor
  36. 3
      IGP/Pages/BuildCalculator/Parts/EntityClickViewComponent.razor
  37. 13
      IGP/Pages/BuildCalculator/Parts/FilterComponent.razor
  38. 25
      IGP/Pages/BuildCalculator/Parts/HighlightsComponent.razor
  39. 14
      IGP/Pages/BuildCalculator/Parts/HotkeyViewerComponent.razor
  40. 2
      IGP/Pages/BuildCalculator/Parts/InputPanelComponent.razor
  41. 41
      IGP/Pages/BuildCalculator/Parts/OptionsComponent.razor
  42. 25
      IGP/Pages/BuildCalculator/Parts/TimelineComponent.razor
  43. 13
      IGP/Pages/BuildCalculator/Parts/TimingComponent.razor
  44. 1
      IGP/Pages/Database/Entity/Parts/EntityPassivesComponent.razor
  45. 29
      IGP/Pages/Documentation/Parts/DocumentComponent.razor
  46. 8
      IGP/Pages/MakingOf/Parts/MakingOfNavigation.razor
  47. 22
      IGP/Pages/Notes/Parts/NoteComponent.razor
  48. 3
      IGP/Portals/ToastPortal.razor
  49. 3
      IGP/Program.cs
  50. 6
      IGP/Utils/Interval.cs
  51. 7
      IGP/Utils/Markdown.cs
  52. 7
      IGP/Utils/Project.cs
  53. 14
      IGP/_Imports.razor
  54. 6
      IGP/wwwroot/content/docs/cheat-sheet.md
  55. 15
      IGP/wwwroot/content/docs/project-data.md
  56. 29
      IGP/wwwroot/content/docs/setup.md
  57. 36
      IGP/wwwroot/content/notes/coop/holdout.md
  58. 35
      IGP/wwwroot/content/notes/settings/hotkeys.md
  59. 36
      IGP/wwwroot/content/notes/the-basics/armor-types.md
  60. 6
      IGP/wwwroot/content/notes/the-basics/economy-overview.md
  61. 6
      IGP/wwwroot/content/notes/the-basics/families-factions-and-immortal-vanguards.md
  62. 6
      IGP/wwwroot/content/notes/the-basics/immortal-spells-and-pyre.md
  63. 5
      IGP/wwwroot/content/notes/the-basics/timing-and-scouting.md
  64. 2
      IGP/wwwroot/generated/AgileTaskModels.json
  65. 3
      IGP_Convert/Program.cs
  66. 73
      Model/BuildOrders/BuildOrderModel.cs
  67. 5
      Model/Doc/DocContentModel.cs
  68. 5
      Model/Economy/EconomyOverTimeModel.cs
  69. 196
      Model/Entity/Data/DATA.cs
  70. 8
      Model/Entity/EntityModel.cs
  71. 4
      Model/Git/GitChangeModel.cs
  72. 7
      Model/Notes/NoteContentModel.cs
  73. 2
      Model/Website/WebSectionModel.cs
  74. 11
      Services/Development/DocumentationService.cs
  75. 37
      Services/Development/GitService.cs
  76. 77
      Services/Development/NoteService.cs
  77. 60
      Services/IServices.cs
  78. 53
      Services/Immortal/BuildComparisionService.cs
  79. 81
      Services/Immortal/BuildOrderService.cs
  80. 47
      Services/Immortal/EconomyService.cs
  81. 26
      Services/Immortal/EntityDisplayService.cs
  82. 87
      Services/Immortal/EntityFilterService.cs
  83. 6
      Services/Immortal/EntityService.cs
  84. 25
      Services/Immortal/ImmortalSelectionService.cs
  85. 53
      Services/Immortal/KeyService.cs
  86. 52
      Services/Immortal/MemoryTesterService.cs
  87. 26
      Services/Immortal/TimingService.cs
  88. 28
      Services/Website/EntityDialogService.cs
  89. 56
      Services/Website/NavigationService.cs
  90. 34
      Services/Website/ToastService.cs
  91. 53
      Services/Website/WebsiteService.cs

6
Components/Display/DevOnlyComponent.razor

@ -1,4 +1,5 @@
@if (isOnDev) {
@if (isOnDev)
{
<div class="devOnlyContainer">
<div class="devOnlyTitleContainer">
<div class="devOnlyTitle">
@ -65,7 +66,8 @@
bool isOnDev;
protected override void OnInitialized() {
protected override void OnInitialized()
{
isOnDev = NavigationManager.BaseUri.Contains("https://localhost");
}

21
Components/Display/PanelComponent.razor

@ -0,0 +1,21 @@
<div class="panelDisplay">
@ChildContent
</div>
<style>
.panelDisplay {
border: 2px solid var(--paper-border);
padding: 20px;
background-color: var(--paper);
display: flex;
flex-direction: column;
flex-grow: 1;
}
</style>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; } = default!;
}

9
Components/Feedback/AlertComponent.razor

@ -1,11 +1,12 @@
@using Model.Feedback
<div class="alertContainer @Type.ToLower()">
@if (Title != null) {
<div class="alertContainer @Type.ToLower()">
@if (Title != null)
{
<div class="alertTitle">
@Title
</div>
}
@if (Message != null) {
@if (Message != null)
{
<div>
@Message
</div>

6
Components/Form/FormCheckboxComponent.razor

@ -10,7 +10,8 @@
checked="@Value
"@onchange="OnChange"/>
</div>
@if (Info != "") {
@if (Info != "")
{
<div class="formInfo">
@Info
</div>
@ -66,7 +67,8 @@
private string labelId = "";
protected override void OnInitialized() {
protected override void OnInitialized()
{
labelId = Label.ToLower().Replace(" ", "_");
}

6
Components/Form/FormDisplayComponent.razor

@ -1,5 +1,6 @@
<div class="displayContainer">
@if (Label != "") {
@if (Label != "")
{
<div class="formLabel">
@Label
</div>
@ -7,7 +8,8 @@
<div class="displayContent">
@Display
</div>
@if (Info != "") {
@if (Info != "")
{
<div class="formInfo">
@Info
</div>

3
Components/Form/FormEscapeCodeComponent.razor

@ -20,7 +20,8 @@
@code {
string Output = "";
public void OnChange(ChangeEventArgs changeEventArgs) {
public void OnChange(ChangeEventArgs changeEventArgs)
{
var encoded = HttpUtility.HtmlEncode(changeEventArgs.Value!.ToString());
Output = encoded?.Replace("@", "@@")!;
Output = Output.Replace("\n", "<br />");

45
Components/Form/FormGuessComponent.razor

@ -1,12 +1,12 @@
@using Services
@using Model.MemoryTester
@using Services.Immortal
@using Model.MemoryTester
@implements IDisposable
@inject IMemoryTesterService MemoryTesterService
<div class="formGuessContainer">
@if (MemoryQuestion.Name != "") {
@if (MemoryQuestion.Name != "")
{
<div class="formLabel">
@MemoryQuestion.Name
</div>
@ -94,46 +94,57 @@
private string labelId = "";
protected override void OnInitialized() {
protected override void OnInitialized()
{
labelId = Label.ToLower().Replace(" ", "_") + MemoryQuestion.Id;
MemoryTesterService.Subscribe(OnMemoryEvent);
if (MemoryQuestion.IsRevealed) {
if (MemoryQuestion.IsRevealed)
{
guess = MemoryQuestion.Answer.ToString();
}
OnRefresh();
}
void IDisposable.Dispose() {
void IDisposable.Dispose()
{
MemoryTesterService.Unsubscribe(OnMemoryEvent);
}
void OnMemoryEvent(MemoryTesterEvent memoryTesterEvent) {
if (memoryTesterEvent == MemoryTesterEvent.OnVerify) {
void OnMemoryEvent(MemoryTesterEvent memoryTesterEvent)
{
if (memoryTesterEvent == MemoryTesterEvent.OnVerify)
{
OnVerify();
}
if (memoryTesterEvent == MemoryTesterEvent.OnRefresh) {
if (memoryTesterEvent == MemoryTesterEvent.OnRefresh)
{
OnRefresh();
}
}
protected override void OnAfterRender(bool firstRender) {
if (MemoryQuestion.IsRevealed) {
protected override void OnAfterRender(bool firstRender)
{
if (MemoryQuestion.IsRevealed)
{
guess = MemoryQuestion.Answer.ToString();
}
}
void OnVerify() {
void OnVerify()
{
IsSubmitted = true;
}
void OnRefresh() {
void OnRefresh()
{
guess = "";
if (MemoryQuestion.IsRevealed) {
if (MemoryQuestion.IsRevealed)
{
guess = MemoryQuestion.Answer.ToString();
}
@ -141,10 +152,12 @@
IsSubmitted = false;
}
void OnGuessChanged(ChangeEventArgs changeEventArgs) {
void OnGuessChanged(ChangeEventArgs changeEventArgs)
{
guess = changeEventArgs.Value!.ToString()!;
OnChange.InvokeAsync(new AnswerEventArgs {
OnChange.InvokeAsync(new AnswerEventArgs
{
Name = MemoryQuestion.Name,
IsCorrect = int.Parse(guess) == MemoryQuestion.Answer,
Guess = int.Parse(guess)

6
Components/Form/FormNumberComponent.razor

@ -1,5 +1,6 @@
<div class="formNumberContainer">
@if (FormLabelComponent != null) {
@if (FormLabelComponent != null)
{
<FormLabelComponent>@FormLabelComponent</FormLabelComponent>
}
<div>
@ -11,7 +12,8 @@
value="@Value"
@onchange="OnChange"/>
</div>
@if (FormInfoComponent != null) {
@if (FormInfoComponent != null)
{
<FormInfoComponent>@FormInfoComponent</FormInfoComponent>
}
</div>

6
Components/Form/FormSelectComponent.razor

@ -1,5 +1,6 @@
<div style="display: flex; width: 100%; flex-direction: column; gap:6px;">
@if (FormLabelComponent != null) {
@if (FormLabelComponent != null)
{
<FormLabelComponent>@FormLabelComponent</FormLabelComponent>
}
<select style="background-color: #2C2E33; width: 100%; border:3px solid #A8ADB9; border-radius:1px; padding: 8px;"
@ -7,7 +8,8 @@
@ChildContent
</select>
@if (FormInfoComponent != null) {
@if (FormInfoComponent != null)
{
<FormInfoComponent>@FormInfoComponent</FormInfoComponent>
}
</div>

9
Components/Form/FormTextAreaComponent.razor

@ -1,5 +1,6 @@
<div class="form-text-container">
@if (Label != "") {
@if (Label != "")
{
<div class="form-label">
@Label
</div>
@ -12,7 +13,8 @@
value="@Value"
@onchange="OnChange" />
</div>
@if (Info != "") {
@if (Info != "")
{
<div class="form-info">
@Info
</div>
@ -83,7 +85,8 @@
private string labelId = "";
protected override void OnInitialized() {
protected override void OnInitialized()
{
labelId = Label.ToLower().Replace(" ", "_");
}

9
Components/Form/FormTextComponent.razor

@ -1,5 +1,6 @@
<div class="formContainer">
@if (Label != "") {
@if (Label != "")
{
<div class="formLabel">
@Label
</div>
@ -13,7 +14,8 @@
id="@labelId"
@onchange="OnChange"/>
</div>
@if (Info != "") {
@if (Info != "")
{
<div class="formInfo">
@Info
</div>
@ -68,7 +70,8 @@
private string labelId = "";
protected override void OnInitialized() {
protected override void OnInitialized()
{
labelId = Label.ToLower().Replace(" ", "_");
}

6
Components/Info/InfoBodyComponent.razor

@ -1,8 +1,10 @@
<div class="infoContainer">
@if (InfoQuestionComponent != null) {
@if (InfoQuestionComponent != null)
{
<div class="infoTitle">@InfoQuestionComponent</div>
}
@if (InfoAnswerComponent != null) {
@if (InfoAnswerComponent != null)
{
<div>@InfoAnswerComponent</div>
}
</div>

3
Components/Inputs/ButtonComponent.razor

@ -45,7 +45,8 @@
[Parameter]
public ButtonType ButtonType { get; set; } = default!;
private void ButtonClicked(EventArgs eventArgs) {
private void ButtonClicked(EventArgs eventArgs)
{
OnClick.InvokeAsync(eventArgs);
}

11
Components/Inputs/ButtonGroupComponent.razor

@ -1,8 +1,10 @@
<div class="groupButtonContainerContainer">
<div class="groupButtonContainer">
@foreach (var choice in Choices) {
@foreach (var choice in Choices)
{
var styleClass = "";
if (choice.Equals(Choice)) {
if (choice.Equals(Choice))
{
styleClass = "selected";
}
<button @onclick="@(e => OnChangeChoice(choice))" class="groupChoiceButton @styleClass">@choice</button>
@ -77,11 +79,14 @@
public EventCallback<string> OnClick { get; set; }
protected override void OnInitialized() { }
protected override void OnInitialized()
{
}
void OnChangeChoice(string choice)
{
Choice = choice;
OnClick.InvokeAsync(choice);
}
}

3
Components/Inputs/ButtonType.cs

@ -1,6 +1,7 @@
namespace Components.Inputs;
public enum ButtonType {
public enum ButtonType
{
Primary, // Positive Actions
Secondary // Destruction Action
}

1
Components/Inputs/CodeLinkComponent.razor

@ -25,4 +25,5 @@
[Parameter]
public string Href { get; set; } = "";
}

1
Components/Inputs/EditLinkComponent.razor

@ -25,4 +25,5 @@
[Parameter]
public string Href { get; set; } = "";
}

8
Components/Inputs/EntityLabelComponent.razor

@ -1,9 +1,5 @@
@using Model.Entity
@using Services.Website
@using System.ComponentModel.DataAnnotations
@using Model.Entity.Data
@using Services
@inject IEntityDialogService entityDialogService
@ -18,7 +14,8 @@ else
@code {
[Parameter] public string EntityId { get; set; } = default!;
[Parameter]
public string EntityId { get; set; } = default!;
private EntityModel Entity => DATA.Get()[EntityId];
@ -26,4 +23,5 @@ else
{
entityDialogService.AddDialog(EntityId);
}
}

5
Components/Layout/LayoutWithSidebarComponent.razor

@ -1,5 +1,4 @@

<div class="layoutWithSidebar">
<div class="layoutWithSidebar">
<div class="layoutSidebar">
@Sidebar
</div>
@ -50,9 +49,11 @@
</style>
@code {
[Parameter]
public RenderFragment Sidebar { get; set; } = default!;
[Parameter]
public RenderFragment Content { get; set; } = default!;
}

10
Components/MarkdownContent/MarkdownContent.razor

@ -1,6 +1,8 @@
@((MarkupString)MarkdownText)
@using Markdig
@((MarkupString)MarkdownText)
@code {
[Inject]
protected HttpClient Http { get; set; } = default!;
@ -9,7 +11,9 @@
private string MarkdownText { get; set; } = "";
protected override async Task OnInitializedAsync() {
MarkdownText = Markdig.Markdown.ToHtml(await Http.GetStringAsync($"markdown/{MarkdownFileName}.md"));
protected override async Task OnInitializedAsync()
{
MarkdownText = Markdown.ToHtml(await Http.GetStringAsync($"markdown/{MarkdownFileName}.md"));
}
}

15
Components/Navigation/DesktopNavLinkComponent.razor

@ -1,14 +1,16 @@
@inject INavigationService navigationService;
@inject NavigationManager navigationManager;
@if (isOnPage) {
@if (isOnPage)
{
<NavLink href="@Page.Href" class="desktopNavLink navSelected">
<div class="navName">
@Page.Name
</div>
</NavLink>
}
else {
else
{
<NavLink @onclick="() => navigationService.ChangeNavigationState(NavigationStateType.Default)" href="@Page.Href" class="desktopNavLink">
<div class="navName">
@Page.Name
@ -67,7 +69,8 @@ else {
bool isOnPage = false;
protected override Task OnParametersSetAsync() {
protected override Task OnParametersSetAsync()
{
var uri = navigationManager.Uri.Remove(0, navigationManager.BaseUri.Count()).ToLower();
isOnPage = Page.Href.ToLower().Equals(uri);
@ -75,11 +78,13 @@ else {
}
void OnNavigationChanged() {
void OnNavigationChanged()
{
StateHasChanged();
}
void OnBack() {
void OnBack()
{
navigationService.Back();
}

10
Components/Navigation/DesktopNavSectionComponent.razor

@ -1,8 +1,8 @@
@using Model.Website
<div class="sectionContainer">
@foreach (var childPage in Section.WebPageModels) {
if (childPage.IsPrivate.Equals("True")) {
<div class="sectionContainer">
@foreach (var childPage in Section.WebPageModels)
{
if (childPage.IsPrivate.Equals("True"))
{
continue;
}
<DesktopNavLinkComponent Page=childPage></DesktopNavLinkComponent>

26
Components/Navigation/MobileNavComponent.razor

@ -1,9 +1,7 @@
@using Model.Website
@using Microsoft.EntityFrameworkCore
<div class="mobileFooter">
<div class="mobileFooter">
<div class="mobileNavSectionsContainer">
@foreach (var webSection in WebSections) {
@foreach (var webSection in WebSections)
{
<div class="mobileNavSectionButton" @onclick="() => OnSectionClicked(webSection)" @onclick:preventDefault="true" @onclick:stopPropagation="true">
<div class="mobileNavSectionButtonText">
@webSection?.Name
@ -15,14 +13,17 @@
<div class="fullPageButton @(selectedSection != null)" @onclick="OnPageClicked" @onclick:stopPropagation="false" @onclick:preventDefault="false">
</div>
@if (selectedSection != null) {
@if (selectedSection != null)
{
List<WebPageModel?> webPages = (from page in WebPages
where page.WebSectionModelId == selectedSection.Id
select page).ToList()!;
<div class="mobileNavPagesContainer">
@foreach (var webPage in webPages) {
if (webPage!.IsPrivate.Equals("True")) {
@foreach (var webPage in webPages)
{
if (webPage!.IsPrivate.Equals("True"))
{
continue;
}
<div class="mobileNavPageButton" @onclick="() => OnPageLinkClicked(webPage)" @onclick:preventDefault="true" @onclick:stopPropagation="true">
@ -160,18 +161,21 @@
private WebPageModel? selectedPage;
void OnSectionClicked(WebSectionModel? webSection) {
void OnSectionClicked(WebSectionModel? webSection)
{
selectedSection = webSection;
}
void OnPageLinkClicked(WebPageModel? webPage) {
void OnPageLinkClicked(WebPageModel? webPage)
{
selectedPage = webPage;
selectedSection = null;
NavigationManager.NavigateTo(webPage?.Href!);
}
void OnPageClicked(EventArgs eventArgs) {
void OnPageClicked(EventArgs eventArgs)
{
selectedPage = null;
selectedSection = null;
}

16
Components/Navigation/TabletNavComponent.razor

@ -19,7 +19,8 @@
<div class="tabletNav @navOpen">
@foreach (var webSection in WebSections) {
@foreach (var webSection in WebSections)
{
var pages = (from page in WebPages
where page.WebSectionModelId == webSection.Id
select page).ToList();
@ -30,8 +31,10 @@
</div>
<div class="tabletNavItems">
@foreach (var webPage in pages) {
if (webPage.IsPrivate.Equals("True")) {
@foreach (var webPage in pages)
{
if (webPage.IsPrivate.Equals("True"))
{
continue;
}
<NavLink href="@webPage.Href" class="tabletNavItem" @onclick="OnPageClicked">
@ -156,15 +159,16 @@
#endif
bool navOpen = true;
void OnNavClicked(EventArgs eventArgs) {
void OnNavClicked(EventArgs eventArgs)
{
navOpen = !navOpen;
}
void OnPageClicked(EventArgs eventArgs) {
void OnPageClicked(EventArgs eventArgs)
{
navOpen = false;
}

10
Components/Shared/DisplayableContent.razor

@ -1,6 +1,5 @@
@using Model.Website.Enums
@using Model.Website
@if (isDisplayable) {
@if (isDisplayable)
{
@ChildContent
}
@ -10,14 +9,15 @@
public RenderFragment ChildContent { get; set; } = default!;
[Parameter]
public WebDeploymentType DeploymentType { get; set; } = default!;
public WebDeploymentType DeploymentType { get; set; }
[Inject]
public NavigationManager MyNavigationManager { get; set; } = default!;
bool isDisplayable;
protected override void OnInitialized() {
protected override void OnInitialized()
{
isDisplayable = DeploymentType == WebDeploymentModel.DeploymentType;
}

10
Components/Shared/DisplayableRoute.razor

@ -1,8 +1,9 @@
@using Model.Website
@if (isDisplayable) {
@if (isDisplayable)
{
@ChildContent
}
else {
else
{
<LayoutView Layout="@typeof(MainLayout)">
<p>Sorry, there's nothing at this address.</p>
</LayoutView>
@ -18,7 +19,8 @@ else {
bool isDisplayable;
protected override void OnInitialized() {
protected override void OnInitialized()
{
var page = MyNavigationManager.Uri.Remove(0, MyNavigationManager.BaseUri.Length);
isDisplayable = WebDeploymentModel.Get().Contains(page);
}

9
Contexts/DatabaseContext.cs

@ -1,18 +1,19 @@
#if NO_SQL
#else
using Microsoft.EntityFrameworkCore;
using Model.Doc;
using Model.Git;
using Model.Notes;
using Model.Website;
using Model.Git;
using Model.Work.Tasks;
namespace Contexts;
public class DatabaseContext : DbContext {
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options) {
public class DatabaseContext : DbContext
{
public DatabaseContext(DbContextOptions<DatabaseContext> options) : base(options)
{
Database.EnsureCreated();
}

BIN
IGP/Database.db

Binary file not shown.

16
IGP/Index.razor

@ -1,19 +1,5 @@
@page "/"
@using Microsoft.Extensions.Localization
@layout PageLayout
@inject IStringLocalizer<Localizations> locale
<DevOnlyComponent>
<DocumentationIndexPage></DocumentationIndexPage>
@locale["Greeting"].Value
@locale["Greeting"]
@locale["Greeting"].Name
@locale["Greeting"].ResourceNotFound
</DevOnlyComponent>
<HomePage></HomePage>
<HomePage/>

6
IGP/Localizations.Designer.cs generated

@ -104,5 +104,11 @@ namespace IGP {
return ResourceManager.GetString("Tooltip Hotkey Info", resourceCulture);
}
}
internal static string Tooltip_Options_Info {
get {
return ResourceManager.GetString("Tooltip Options Info", resourceCulture);
}
}
}
}

6
IGP/Localizations.resx

@ -1,7 +1,8 @@
<?xml version="1.0" encoding="utf-8"?>
<root>
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="root"
<xsd:schema xmlns:xsd="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata"
id="root"
xmlns="">
<xsd:element name="root" msdata:IsDataSet="true">
@ -66,4 +67,7 @@ You can also use the default Immortal hotkeys, but the above hotkey UI must have
Additionally, more entities will appear as you build the required technology. You can click or press ` to remove the last made entity. &lt;i&gt;But you cannot remove the starting entities at interval 0.&lt;/i&gt;</value>
</data>
<data name="Tooltip Options Info" xml:space="preserve">
<value>Misc calculator controls.</value>
</data>
</root>

103
IGP/Pages/BuildCalculator/BuildCalculatorPage.razor

@ -10,7 +10,6 @@
@inject ITimingService timingService
@page "/build-calculator"
@using Microsoft.Extensions.Localization
@implements IDisposable
@ -20,89 +19,93 @@
<AlertComponent Type="@SeverityType.Warning">
<Title>Work In Progress and Not Fully Tested</Title>
<Message>
Currently not considering training queue times. Lacking error toasts for invalid actions. Performance needs to be optimized. Calculations haven't been thoroughly compared against real gameplay. Added a 2 second delay to actions to account for casual micro (will probably tweak later).
Currently not considering training queue times.
</Message>
</AlertComponent>
<ContentDividerComponent></ContentDividerComponent>
<div class="calculatorGrid">
<div style="grid-area: timing;" class="gridItem">
<div class="gridItem" style="grid-area: timing;">
<ButtonComponent OnClick="OnResetClicked">Clear Build Order</ButtonComponent>
<PanelComponent>
<InfoTooltipComponent InfoText="@locale["Tooltip Timing Info"]">
<TimingComponent></TimingComponent>
</InfoTooltipComponent>
</PanelComponent>
</div>
@if (true)
{
<div style="grid-area: chart;" class="gridItem">
<div class="gridItem" style="grid-area: chart;">
<PanelComponent>
<InfoTooltipComponent InfoText="@locale["Tooltip Chart Info"]">
<ChartComponent></ChartComponent>
</InfoTooltipComponent>
</PanelComponent>
</div>
}
<div style="grid-area: filter;" class="gridItem">
<div class="gridItem" style="grid-area: filter;">
<PanelComponent>
<InfoTooltipComponent InfoText="@locale["Tooltip Filter Info"]">
<FilterComponent></FilterComponent>
</InfoTooltipComponent>
</PanelComponent>
<PanelComponent>
<InfoTooltipComponent InfoText="@locale["Tooltip Options Info"]">
<OptionsComponent></OptionsComponent>
</InfoTooltipComponent>
</PanelComponent>
</div>
@if (true)
{
<div style="grid-area: view;" class="gridItem">
<div class="gridItem" style="grid-area: view;">
<PanelComponent>
<InfoTooltipComponent InfoText="@locale["Tooltip Entity Info"]">
<EntityClickViewComponent/>
</InfoTooltipComponent>
</PanelComponent>
</div>
}
@if (true)
{
<div style="grid-area: bank;" class="gridItem">
<div class="gridItem" style="grid-area: bank;">
<PanelComponent>
<InfoTooltipComponent InfoText="@locale["Tooltip Bank Info"]">
<BankComponent></BankComponent>
</InfoTooltipComponent>
</PanelComponent>
</div>
}
@if (true)
{
<div style="grid-area: army;" class="gridItem">
<div class="gridItem" style="grid-area: army;">
<PanelComponent>
<InfoTooltipComponent InfoText="@locale["Tooltip Army Info"]">
<ArmyComponent></ArmyComponent>
</InfoTooltipComponent>
</PanelComponent>
</div>
}
<div class="gridItem gridKeys">
<PanelComponent>
<InfoTooltipComponent InfoText="@locale["Tooltip Hotkey Info"]">
<HotkeyViewerComponent Size="80"></HotkeyViewerComponent>
</InfoTooltipComponent>
</PanelComponent>
</div>
@if (true)
{
<div style="grid-area: highlights;" class="gridItem">
<div class="gridItem" style="grid-area: highlights;">
<PanelComponent>
<InfoTooltipComponent InfoText="@locale["Tooltip Highlights Info"]">
<HighlightsComponent></HighlightsComponent>
</InfoTooltipComponent>
</PanelComponent>
</div>
}
@if (true)
{
<div style="grid-area: buildorder;" class="gridItem">
<div class="gridItem" style="grid-area: buildorder;">
<PanelComponent>
<InfoTooltipComponent InfoText="@locale["Tooltip BuildOrder Info"]">
<BuildOrderComponent></BuildOrderComponent>
</InfoTooltipComponent>
</PanelComponent>
</div>
}
</div>
<ContentDividerComponent></ContentDividerComponent>
@ -135,7 +138,7 @@
<InfoBodyComponent>
<InfoQuestionComponent>
What is CONTROl key for?
What is CONTROL key for?
</InfoQuestionComponent>
<InfoAnswerComponent>
Economy and tech related upgrades for townhalls.
@ -165,6 +168,12 @@
<style>
.gridItem {
display: flex;
flex-direction: column;
gap: 8px;
}
.calculatorGrid {
display: grid;
gap: 8px;
@ -178,12 +187,6 @@
'chart chart chart chart';
}
.gridItem {
border: 2px solid var(--paper-border);
padding: 20px;
background-color: var(--paper);
}
.gridKeys {
grid-area: keys;
}
@ -209,17 +212,12 @@
padding-left: 2px;
padding-right: 2px;
}
.gridItem {
padding: 0px;
border: 0px;
width: 100%;
}
}
</style>
@code {
protected override void OnInitialized()
{
economyService.Calculate(buildOrderService, timingService, 0);
@ -232,6 +230,16 @@
keyService.Unsubscribe(HandleClick);
}
private void OnResetClicked()
{
toastService.AddToast(new ToastModel(){
SeverityType = SeverityType.Success,
Message = "Build order has been cleared.",
Title = "Reset"});
buildOrderService.Reset();
}
private void HandleClick()
{
@ -254,7 +262,7 @@
var faction = filterService.GetFactionType();
var immortal = filterService.GetImmortalType();
EntityModel? entity = EntityModel.GetFrom(hotkey!, hotkeyGroup, isHoldSpace, faction, immortal);
var entity = EntityModel.GetFrom(hotkey!, hotkeyGroup, isHoldSpace, faction, immortal);
if (entity == null)
{
@ -266,4 +274,5 @@
economyService.Calculate(buildOrderService, timingService, buildOrderService.GetLastRequestInterval());
}
}
}

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

@ -1,6 +1,6 @@
@inject IJSRuntime jsRuntime;
@inject IJSRuntime jsRuntime
@inject IBuildOrderService BuildOrder
@inject IBuildOrderService buildOrder
@implements IDisposable
@ -35,12 +35,12 @@
protected override void OnInitialized()
{
BuildOrder.Subscribe(OnBuildOrderChanged);
buildOrder.Subscribe(OnBuildOrderChanged);
}
void IDisposable.Dispose()
{
BuildOrder.Unsubscribe(OnBuildOrderChanged);
buildOrder.Unsubscribe(OnBuildOrderChanged);
}
protected override bool ShouldRender()
@ -60,7 +60,7 @@
void OnBuildOrderChanged()
{
int armyCountWas = 0;
var armyCountWas = 0;
foreach (var army in armyCount)
{
armyCountWas += army.Value;
@ -70,7 +70,7 @@
lastInterval = 0;
var entitiesOverTime = BuildOrder.GetOrders();
var entitiesOverTime = buildOrder.GetOrders();
foreach (var entitiesAtTime in entitiesOverTime)
{
@ -92,7 +92,7 @@
}
//TODO Better
int armyCountIs = 0;
var armyCountIs = 0;
foreach (var army in armyCount)
{
armyCountIs += army.Value;
@ -103,7 +103,6 @@
{
StateHasChanged();
}
}
}

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

@ -1,6 +1,5 @@
@inject IJSRuntime jsRuntime;
@inject IBuildOrderService buildOrderService
@implements IDisposable
@ -39,4 +38,5 @@
jsRuntime.InvokeVoidAsync("console.timeEnd", "BuildOrderComponent");
#endif
}
}

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

@ -6,7 +6,6 @@
@if (lastRequestedRefreshIndex != requestedRefreshIndex)
{
<LoadingComponent/>
}
else
{
@ -60,8 +59,6 @@ else
<Display>@highestArmyPoint</Display>
</FormDisplayComponent>
</FormLayoutComponent>
}
@code {
@ -95,6 +92,7 @@ else
int lastRequestedRefreshIndex = 0;
void OnAge(object? sender, ElapsedEventArgs elapsedEventArgs)
{
if (requestedRefreshIndex > 0)
@ -113,7 +111,6 @@ else
}
void IDisposable.Dispose()
{
buildOrderService.Unsubscribe(OnBuilderOrderChanged);
@ -121,6 +118,7 @@ else
int requestedRefreshIndex = 0;
void OnBuilderOrderChanged()
{
requestedRefreshIndex++;

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

@ -1,6 +1,4 @@
@inject IJSRuntime jsRuntime;
@inject IKeyService keyService
@inject IImmortalSelectionService filterService
@inject IBuildOrderService buildOrderService
@ -68,4 +66,5 @@
StateHasChanged();
}
}
}

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

@ -1,4 +1,5 @@
@inject IJSRuntime jsRuntime;
@inject IImmortalSelectionService filterService
<FormLayoutComponent>
<FormSelectComponent OnChange="@OnFactionChanged">
@ -12,12 +13,12 @@
<FormSelectComponent OnChange="@OnImmortalChanged">
<FormLabelComponent>Immortal</FormLabelComponent>
<ChildContent>
@if (FilterService.GetFactionType() == FactionType.QRath)
@if (filterService.GetFactionType() == FactionType.QRath)
{
<option value="@DataType.IMMORTAL_Orzum" selected>Orzum</option>
<option value="@DataType.IMMORTAL_Ajari">Ajari</option>
}
@if (FilterService.GetFactionType() == FactionType.Aru)
@if (filterService.GetFactionType() == FactionType.Aru)
{
<option value="@DataType.IMMORTAL_Mala" selected>Mala</option>
<option value="@DataType.IMMORTAL_Xol">Xol</option>
@ -28,17 +29,14 @@
@code {
[Inject]
public IImmortalSelectionService FilterService { get; set; } = default!;
void OnFactionChanged(ChangeEventArgs e)
{
FilterService.SelectFactionType(e.Value!.ToString()!);
filterService.SelectFactionType(e.Value!.ToString()!);
}
void OnImmortalChanged(ChangeEventArgs e)
{
FilterService.SelectImmortalType(e.Value!.ToString()!);
filterService.SelectImmortalType(e.Value!.ToString()!);
}
protected override bool ShouldRender()
@ -56,4 +54,5 @@
jsRuntime.InvokeVoidAsync("console.timeEnd", "FilterComponent");
#endif
}
}

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

@ -1,5 +1,7 @@
@inject IJSRuntime jsRuntime;
@inject IEconomyService economyService
@inject IBuildOrderService buildOrderService
@inject ITimingService timingService
@implements IDisposable
@ -7,7 +9,7 @@
<div>
<div>Requested</div>
@foreach (var ordersAtTime in BuildOrderService.StartedOrders.Reverse())
@foreach (var ordersAtTime in buildOrderService.StartedOrders.Reverse())
{
foreach (var order in ordersAtTime.Value)
{
@ -26,7 +28,7 @@
<div>
<div>Finished</div>
@foreach (var ordersAtTime in BuildOrderService.CompletedOrders.Reverse())
@foreach (var ordersAtTime in buildOrderService.CompletedOrders.Reverse())
{
foreach (var order in ordersAtTime.Value)
{
@ -57,25 +59,16 @@
@code {
[Inject]
IEconomyService EconomyService { get; set; } = default!;
[Inject]
IBuildOrderService BuildOrderService { get; set; } = default!;
[Inject]
ITimingService TimingService { get; set; } = default!;
protected override void OnInitialized()
{
EconomyService.Subscribe(StateHasChanged);
BuildOrderService.Subscribe(StateHasChanged);
economyService.Subscribe(StateHasChanged);
buildOrderService.Subscribe(StateHasChanged);
}
void IDisposable.Dispose()
{
EconomyService.Unsubscribe(StateHasChanged);
BuildOrderService.Unsubscribe(StateHasChanged);
economyService.Unsubscribe(StateHasChanged);
buildOrderService.Unsubscribe(StateHasChanged);
}
protected override bool ShouldRender()

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

@ -1,6 +1,4 @@
@inject IJSRuntime jsRuntime;
@using System.Diagnostics
@implements IDisposable
@inject IKeyService keyService
@inject IBuildOrderService buildOrderService
@ -57,7 +55,6 @@
@hotkey.KeyText
@foreach (var entity in data.Values)
{
if (InvalidKey(entity, hotkey) || InvalidKeyGroup(entity, hotkey) || InvalidHoldSpace(entity))
{
continue;
@ -138,6 +135,7 @@
}
int completedTimeCount = 0;
void OnBuilderOrderChanged()
{
if (buildOrderService.UniqueCompletedTimes.Count != completedTimeCount)
@ -252,9 +250,8 @@
void OnKeyPressed()
{
string controlGroupWas = controlGroup;
string keyWas = key;
var controlGroupWas = controlGroup;
var keyWas = key;
if (keyService.GetAllPressedKeys().Contains("Z"))
@ -299,10 +296,8 @@
if (controlGroupWas != controlGroup || keyWas != key)
{
StateHasChanged();
}
}
@ -327,7 +322,7 @@
var faction = filterService.GetFactionType();
var immortal = filterService.GetImmortalType();
EntityModel? entity = EntityModel.GetFrom(hotkey!, hotkeyGroup, isHoldSpace, faction, immortal);
var entity = EntityModel.GetFrom(hotkey!, hotkeyGroup, isHoldSpace, faction, immortal);
if (entity == null)
{
@ -339,4 +334,5 @@
economyService.Calculate(buildOrderService, timingService, buildOrderService.GetLastRequestInterval());
}
}
}

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

@ -13,6 +13,7 @@
</div>
@code {
[Parameter]
public RenderFragment ChildContent { get; set; } = default!;
@ -41,4 +42,5 @@
jsRuntime.InvokeVoidAsync("console.timeEnd", "InputPanelComponent");
#endif
}
}

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

@ -0,0 +1,41 @@
@inject IJSRuntime jsRuntime;
@inject IBuildOrderService buildOrderService
@inject IEconomyService economyService
@inject IToastService toastService
@inject ITimingService timingService
<FormLayoutComponent>
<FormNumberComponent Max="600"
Min="0"
Value="@buildOrderService.BuildingInputDelay"
OnChange="@OnBuildingInputDelayChanged">
<FormLabelComponent>Building Input Delay</FormLabelComponent>
<FormInfoComponent>Add a input delay to constructing buildings for simulating worker movement and player micro.</FormInfoComponent>
</FormNumberComponent>
</FormLayoutComponent>
@code {
void OnBuildingInputDelayChanged(ChangeEventArgs changeEventArgs)
{
buildOrderService.BuildingInputDelay = int.Parse(changeEventArgs.Value!.ToString()!);
}
protected override bool ShouldRender()
{
#if DEBUG
jsRuntime.InvokeVoidAsync("console.time", "TimingComponent");
#endif
return true;
}
protected override void OnAfterRender(bool firstRender)
{
#if DEBUG
jsRuntime.InvokeVoidAsync("console.timeEnd", "TimingComponent");
#endif
}
}

25
IGP/Pages/BuildCalculator/Parts/TimelineComponent.razor

@ -1,9 +1,10 @@
@inject IJSRuntime jsRuntime;
@inject IJSRuntime jsRuntime
@inject IEconomyService economyService
@inject IBuildOrderService buildOrderService
@implements IDisposable
<Virtualize Items="@EconomyService.GetOverTime()" Context="economyAtSecond" ItemSize="400" OverscanCount="4">
<Virtualize Items="@economyService.GetOverTime()" Context="economyAtSecond" ItemSize="400" OverscanCount="4">
<div style="display: grid; gap: 8px; grid-template-columns: 1fr 1fr;">
<div>
<div>
@ -28,7 +29,7 @@
</div>
<div>
@if (BuildOrderService.StartedOrders.TryGetValue(economyAtSecond.Interval, out var ordersAtTime))
@if (buildOrderService.StartedOrders.TryGetValue(economyAtSecond.Interval, out var ordersAtTime))
{
@foreach (var order in ordersAtTime)
{
@ -39,7 +40,7 @@
}
@if (BuildOrderService.CompletedOrders.TryGetValue(economyAtSecond.Interval, out var ordersCompletedAtTime))
@if (buildOrderService.CompletedOrders.TryGetValue(economyAtSecond.Interval, out var ordersCompletedAtTime))
{
@foreach (var order in ordersCompletedAtTime)
{
@ -55,22 +56,16 @@
@code {
[Inject]
IEconomyService EconomyService { get; set; } = default!;
[Inject]
IBuildOrderService BuildOrderService { get; set; } = default!;
protected override void OnInitialized()
{
EconomyService.Subscribe(StateHasChanged);
BuildOrderService.Subscribe(StateHasChanged);
economyService.Subscribe(StateHasChanged);
buildOrderService.Subscribe(StateHasChanged);
}
void IDisposable.Dispose()
{
EconomyService.Unsubscribe(StateHasChanged);
BuildOrderService.Unsubscribe(StateHasChanged);
economyService.Unsubscribe(StateHasChanged);
buildOrderService.Unsubscribe(StateHasChanged);
}
protected override bool ShouldRender()

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

@ -5,11 +5,8 @@
@inject IToastService toastService
@inject ITimingService timingService
<FormLayoutComponent>
<FormNumberComponent ReadOnly="true"
Max="600"
<FormNumberComponent Max="2048"
Min="0"
Value="@timingService.GetTiming()"
OnChange="@OnTimingChanged">
@ -27,10 +24,17 @@
</FormLayoutComponent>
@code {
void OnTimingChanged(ChangeEventArgs changeEventArgs)
{
timingService.SetTiming(int.Parse(changeEventArgs.Value!.ToString()!));
economyService.Calculate(buildOrderService, timingService, buildOrderService.GetLastRequestInterval());
toastService.AddToast(new ToastModel()
{
Title = "Timing Interval",
Message = "Timing interval has changed.",
SeverityType = SeverityType.Success
});
}
void OnNameChanged(ChangeEventArgs changeEventArgs)
@ -65,5 +69,4 @@
#endif
}
}

1
IGP/Pages/Database/Entity/Parts/EntityPassivesComponent.razor

@ -122,7 +122,6 @@
}
</div>
}
}
</EntityDisplayComponent>
}

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

@ -1,7 +1,3 @@
@using System.Net.Http.Json
@using Markdig.Extensions.Yaml
@using Markdig.Syntax
@using YamlDotNet.Serialization
@inject HttpClient httpClient
@if (content == null)
@ -19,10 +15,10 @@ else
</div>
</div>
<div class="docContent">@((MarkupString)Markdown.ToHtml(content, Pipeline))</div>
<div class="docFooter"><EditLinkComponent Href="@GitUrl"></EditLinkComponent></div>
<div class="docFooter">
<EditLinkComponent Href="@GitUrl"></EditLinkComponent>
</div>
</div>
}
<style>
@ -56,7 +52,9 @@ else
</style>
@code {
[Parameter] public DocContentModel DocContentModel { get; set; } = default!;
[Parameter]
public DocContentModel DocContentModel { get; set; } = default!;
DocFrontMatterModel docFrontMatter = null!;
private string? content = null;
@ -65,13 +63,22 @@ else
private string GitUrl => $"{Project.GitResourcesUrl}/{Filepath}";
private MarkdownPipeline Pipeline => MarkdownFiles.Pipeline;
private async Task<DocFrontMatterModel> LoadContent() {
private async Task<DocFrontMatterModel> LoadContent()
{
content = await MarkdownFiles.LoadMarkdown(httpClient, Filepath);
return docFrontMatter =
await MarkdownFiles.LoadFrontMatter<DocFrontMatterModel>(httpClient, Filepath);
}
protected override async Task OnParametersSetAsync() => await LoadContent();
protected override async Task OnInitializedAsync() => await LoadContent();
protected override async Task OnParametersSetAsync()
{
await LoadContent();
}
protected override async Task OnInitializedAsync()
{
await LoadContent();
}
}

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

@ -6,15 +6,15 @@
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>
<NavSectionComponent Section=@(new WebSectionModel { Name = "Example Section" })
<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" } })>
</NavSectionComponent>
</DesktopNavSectionComponent>
</Example>
<Usage>
<CodeComponent>
&lt;NavSectionComponent Section=@@(new WebSectionModel{Name = &quot;Example Section&quot;})
&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;/NavSectionComponent&gt;
&lt;/DesktopNavSectionComponent&gt;
</CodeComponent>
</Usage>
<Code>

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

@ -15,7 +15,9 @@ else
</div>
</div>
<div class="noteContent">@((MarkupString)Markdown.ToHtml(content, Pipeline))</div>
<div class="noteFooter"><EditLinkComponent Href="@GitUrl"></EditLinkComponent></div>
<div class="noteFooter">
<EditLinkComponent Href="@GitUrl"></EditLinkComponent>
</div>
</div>
}
@ -43,7 +45,9 @@ else
</style>
@code {
[Parameter] public NoteContentModel NoteContentModel { get; set; } = default!;
[Parameter]
public NoteContentModel NoteContentModel { get; set; } = default!;
NoteFrontMatterModel noteFrontMatter = null!;
private string? content = null;
@ -52,14 +56,22 @@ else
private string GitUrl => $"{Project.GitResourcesUrl}/{Filepath}";
private MarkdownPipeline Pipeline => MarkdownFiles.Pipeline;
private async Task<NoteFrontMatterModel> LoadContent() {
private async Task<NoteFrontMatterModel> LoadContent()
{
content = await MarkdownFiles.LoadMarkdown(httpClient, Filepath);
return noteFrontMatter =
await MarkdownFiles.LoadFrontMatter<NoteFrontMatterModel>(httpClient, Filepath);
}
protected override async Task OnParametersSetAsync() => await LoadContent();
protected override async Task OnInitializedAsync() => await LoadContent();
protected override async Task OnParametersSetAsync()
{
await LoadContent();
}
protected override async Task OnInitializedAsync()
{
await LoadContent();
}
}

3
IGP/Portals/ToastPortal.razor

@ -7,7 +7,6 @@
<div class="toastsContainer">
@foreach (var toast in Toasts)
{
<ToastComponent Toast="toast"/>
}
</div>
@ -37,7 +36,6 @@
ageTimer = new Timer(10);
ageTimer.Elapsed += OnAge!;
ageTimer.Enabled = true;
}
public void Dispose()
@ -57,4 +55,5 @@
{
StateHasChanged();
}
}

3
IGP/Program.cs

@ -6,7 +6,6 @@ using Services.Development;
using Services.Immortal;
using Services.Website;
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("en-US");
@ -44,7 +43,6 @@ builder.Services.AddSingleton(new HttpClient
});
#if NO_SQL
#else
@ -52,7 +50,6 @@ builder.Services.AddSingleton(new HttpClient
#endif
await builder.Build().RunAsync();

6
IGP/Utils/Interval.cs

@ -1,7 +1,9 @@
namespace IGP.Utils;
public static class Interval {
public static string ToTime(int interval) {
public static class Interval
{
public static string ToTime(int interval)
{
return TimeSpan.FromSeconds(interval).ToString(@"mm\:ss");
}
}

7
IGP/Utils/Markdown.cs

@ -1,12 +1,12 @@
using Markdig;
using Markdig.Extensions.Yaml;
using Markdig.Syntax;
using Model.Doc;
using YamlDotNet.Serialization;
namespace IGP.Utils;
public static class MarkdownFiles {
public static class MarkdownFiles
{
private static readonly IDeserializer YamlDeserializer =
new DeserializerBuilder()
.IgnoreUnmatchedProperties()
@ -23,7 +23,8 @@ public static class MarkdownFiles {
return await httpClient.GetStringAsync(filepath);
}
public static async Task<T> LoadFrontMatter<T>(HttpClient httpClient, string filepath) {
public static async Task<T> LoadFrontMatter<T>(HttpClient httpClient, string filepath)
{
var markdown = await LoadMarkdown(httpClient, filepath);
var document = Markdown.Parse(markdown, Pipeline);

7
IGP/Utils/Project.cs

@ -1,6 +1,7 @@
namespace IGP.Utils;
public static class Project {
public static string GitResourcesUrl => "https://github.com/JonathanMcCaffrey/IGP-Fan-Reference/blob/develop/IGP/wwwroot/";
public static class Project
{
public static string GitResourcesUrl =>
"https://github.com/JonathanMcCaffrey/IGP-Fan-Reference/blob/develop/IGP/wwwroot/";
}

14
IGP/_Imports.razor

@ -6,11 +6,10 @@
@using Components.Layout
@using Components.Navigation
@using Components.Shared
@using IGP.Utils
@using System.Globalization
@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
@ -23,27 +22,28 @@
@using IGP.Pages.Home.Parts
@using IGP.Pages.MakingOf.Parts
@using IGP.Pages.MemoryTester.Parts
@using IGP.Pages.RoadMap.Parts
@using IGP.Pages.Notes
@using IGP.Pages.Notes.Parts
@using System.Timers
@using IGP.Pages.RoadMap.Parts
@using IGP.Portals
@using IGP.Utils
@using Markdig
@using Model.Feedback
@using Microsoft.AspNetCore.Components.Forms
@using Microsoft.AspNetCore.Components.Routing
@using Microsoft.AspNetCore.Components.Web
@using Microsoft.AspNetCore.Components.Web.Virtualization
@using Microsoft.AspNetCore.Components.WebAssembly.Http
@using Microsoft.EntityFrameworkCore
@using Microsoft.Extensions.Localization
@using Microsoft.JSInterop
@using Model.Chart
@using Model.Git
@using Model.Doc
@using Model.Economy
@using Model.Entity
@using Model.Entity.Data
@using Model.Entity.Parts
@using Model.Feedback
@using Model.Git
@using Model.Hotkeys
@using Model.MemoryTester
@using Model.Notes
@ -55,4 +55,6 @@
@using Model.Work.Tasks.Enums
@using Services
@using Services.Immortal
@using System.Globalization
@using System.Reflection
@using System.Timers

6
IGP/wwwroot/content/docs/cheat-sheet.md

@ -1,8 +1,6 @@
---
title: Cheat Sheet
summary: Handy links or quick information on this project.
created_date: 2022-04-11
updated_date: 2022-04-11
title: Cheat Sheet summary: Handy links or quick information on this project. created_date: 2022-04-11 updated_date:
2022-04-11
---
# Overview

15
IGP/wwwroot/content/docs/project-data.md

@ -1,8 +1,5 @@
---
title: Project Data
summary: Using data in this project.
created_date: 2022-04-11
updated_date: 2022-04-11
title: Project Data summary: Using data in this project. created_date: 2022-04-11 updated_date: 2022-04-11
---
# Overview
@ -11,7 +8,8 @@ This document will contain general information on data in this project.
## General Note
This project is a work in progress. As such, most of the data in the website doesn't follow the document. Ideally, this will be fixed over time.
This project is a work in progress. As such, most of the data in the website doesn't follow the document. Ideally, this
will be fixed over time.
## SQL
@ -19,7 +17,9 @@ Relational data is stored in a local SQL database.
This data is converted into JSON so it can be handled by Blazor WASM.
<i>Currently, Blazor WASM has a bug that prevents SQL from being used in production. Although, given SQL doesn't seem to provide much, aside from increased build times and load times, it might just be best to use the design principals of SQL, and it's interface, without using the actual technology in production.</i>
<i>Currently, Blazor WASM has a bug that prevents SQL from being used in production. Although, given SQL doesn't seem to
provide much, aside from increased build times and load times, it might just be best to use the design principals of
SQL, and it's interface, without using the actual technology in production.</i>
The data is then loaded in by a Service, via it's load method.
@ -90,7 +90,8 @@ else
Localized strings are handled in the Localizations.resx file.
Most text isn't using localized strings yet. And English is the only plan of support in the short term, and probably in the long term as well.
Most text isn't using localized strings yet. And English is the only plan of support in the short term, and probably in
the long term as well.
## Markdown Files

29
IGP/wwwroot/content/docs/setup.md

@ -1,18 +1,15 @@
---
title: Setup
summary: Get set up on developing this web project.
created_date: 3/30/2022
updated_date: 4/7/2022
title: Setup summary: Get set up on developing this web project. created_date: 3/30/2022 updated_date: 4/7/2022
---
# Overview
This document will contain general setup notes for the project.
## Prerequisite
To understand content in this document, it is recommended to have some software development experience. Particularly using GitHub and Visual Studio.
To understand content in this document, it is recommended to have some software development experience. Particularly
using GitHub and Visual Studio.
- [GitHub Documentation](https://docs.github.com/en/get-started)
@ -24,10 +21,13 @@ To make updates to this website, it is recommended to understand HTML/CSS and C#
- [Mozilla's HTML Documentation](https://developer.mozilla.org/en-US/docs/Learn/HTML/Introduction_to_HTML/Getting_started)
- [W3SCHOOLS' HTML Documentation](https://www.w3schools.com/html/)
Further, you should understand the product and clients this website is for. So it is recommended to play "Immortal: Gates of Pyre".
Further, you should understand the product and clients this website is for. So it is recommended to play "Immortal:
Gates of Pyre".
- [IGP Website](https://gatesofpyre.com/)
- **Please Note:** This product currently has restricted access with it is in a pre-alpha state. If you are not aware or interested in IGP, I recommend to wait for product release. Otherwise, check out their discord for steps of getting access.
- **Please Note:** This product currently has restricted access with it is in a pre-alpha state. If you are not
aware or interested in IGP, I recommend to wait for product release. Otherwise, check out their discord for steps
of getting access.
## Installation
@ -37,7 +37,8 @@ Download and install Visual Studio preview.
[https://visualstudio.microsoft.com/vs/preview/](https://visualstudio.microsoft.com/vs/preview/)
When installing, ensure you have selected "Workloads | **ASP.NET and web development**" and "Individual components | **.NET WebAssembly build tools**".
When installing, ensure you have selected "Workloads | **ASP.NET and web development**" and "Individual components | **
.NET WebAssembly build tools**".
## Download Project
@ -77,11 +78,13 @@ C:.
Code committed to the `main` branch will automatically be deployed to [production](https://www.igpfanreference.com/).
Code committed to the `develop` branch will automatically be deployed to [development](https://calm-mud-04916b210.1.azurestaticapps.net/).
Code committed to the `develop` branch will automatically be deployed
to [development](https://calm-mud-04916b210.1.azurestaticapps.net/).
_This is handle via the files in `.github/workflow`. Look into these [GitHub Actions Documents](https://docs.github.com/en/actions) if curious about how this CI system works._
_This is handle via the files in `.github/workflow`. Look into
these [GitHub Actions Documents](https://docs.github.com/en/actions) if curious about how this CI system works._
## Troubleshooting
Nothing that some good internet searches cannot resolved. But you can also contact the project maintainer on [Discord](https://discord.gg/uMq8bMGeeN).
Nothing that some good internet searches cannot resolved. But you can also contact the project maintainer
on [Discord](https://discord.gg/uMq8bMGeeN).

36
IGP/wwwroot/content/notes/coop/holdout.md

@ -1,26 +1,27 @@
---
title: Holdout
summary: First coop test map in pre-alpha.
created_date: 2/18/2022
updated_date: 2/18/2022
title: Holdout summary: First coop test map in pre-alpha. created_date: 2/18/2022 updated_date: 2/18/2022
---
Information contained in this note is based on this <a href="https://www.youtube.com/watch?v=XkAgOCIz3DE">YouTube, Reference Video</a>.
Information contained in this note is based on this <a href="https://www.youtube.com/watch?v=XkAgOCIz3DE">YouTube,
Reference Video</a>.
![Open Bases](./image/notes/coop-holdout/OpenBases.png)
**Open Bases**
On this map, you start with around 500 alloy and 100 ether. You are probably going to want to expand to the bases in the marked order, given the density of defending enemies shown on the minimap.
You should know that these are all standard bases that will mine out in 10 minutes. Giving a total of 18,000 alloy and 7,200 ether. Plus an additional 6,000 alloy from the starting Bastion. In the late game, you will have zero income, aside from pyre.
On this map, you start with around 500 alloy and 100 ether. You are probably going to want to expand to the bases in the
marked order, given the density of defending enemies shown on the minimap.
You should know that these are all standard bases that will mine out in 10 minutes. Giving a total of 18,000 alloy and
7,200 ether. Plus an additional 6,000 alloy from the starting Bastion. In the late game, you will have zero income,
aside from pyre.
![Enemy Spawns](./image/notes/coop-holdout/EnemySpawns.png)
**Enemy Spawn Areas**
The first enemy wave will spawn at 1 minute, and every 2 minutes after will spawn a new wave. These waves are small, and won't be a threat until the 15-minute mark.
The first enemy wave will spawn at 1 minute, and every 2 minutes after will spawn a new wave. These waves are small, and
won't be a threat until the 15-minute mark.
![Defend Points](./image/notes/coop-holdout/DefendPoints.png)
@ -28,19 +29,26 @@ The first enemy wave will spawn at 1 minute, and every 2 minutes after will spaw
You have till then to take all 5 of your bases, and set a defensive line at the outer Pyre towers.
The spawn size post the 15-minute mark does become rather large. You may be tempted to fall back and abandon forward bases, but the waves will stack if not dealt with. Eventually, more units than the game can handle, so ensure outer pyre towers are held. Try to take them back if you lose them.
The spawn size post the 15-minute mark does become rather large. You may be tempted to fall back and abandon forward
bases, but the waves will stack if not dealt with. Eventually, more units than the game can handle, so ensure outer pyre
towers are held. Try to take them back if you lose them.
![Pyre](./image/notes/coop-holdout/Pyre.png)
**Pyre Camps**
When you have the time you are also going to need to take the 4 pyre camps spread around the map. It will probably be ideal to split your army in half, to protect your two outer towers, and just have a small force of Ichors or Dervishes to clear the camps quickly.
When you have the time you are also going to need to take the 4 pyre camps spread around the map. It will probably be
ideal to split your army in half, to protect your two outer towers, and just have a small force of Ichors or Dervishes
to clear the camps quickly.
![Multipliers](./image/notes/coop-holdout/Multipliers.png)
**Multipliers**
If you have additional free time, you can take out the Altar of the Worthys on the edges of the map to double your current more multiplier: 2, 4, 8, to the max of 16. Amber Wombs will also spawn, with a pack of enemies to defend them. Killing an Amber Womb will increase your score, but also spawn random friendly and enemy units. With this spawning, it's possible to go past the supply cap.
If you have additional free time, you can take out the Altar of the Worthys on the edges of the map to double your
current more multiplier: 2, 4, 8, to the max of 16. Amber Wombs will also spawn, with a pack of enemies to defend them.
Killing an Amber Womb will increase your score, but also spawn random friendly and enemy units. With this spawning, it's
possible to go past the supply cap.
But really, these optional objectives can be completely ignored, so you can just focus on surviving for as long as possible.
But really, these optional objectives can be completely ignored, so you can just focus on surviving for as long as
possible.

35
IGP/wwwroot/content/notes/settings/hotkeys.md

@ -1,8 +1,6 @@
---
title: Custom HotKey Setup
summary: Customize your hotkeys in the pre-alpha.
created_date: 4/13/2022
updated_date: 4/13/2022
title: Custom HotKey Setup summary: Customize your hotkeys in the pre-alpha. created_date: 4/13/2022 updated_date:
4/13/2022
---
In the pre-alpha, IGP comes with some Unreal default hotkey setup.
@ -87,6 +85,7 @@ ActionMappings=(ActionName="ZoomIn",bShift=False,bCtrl=False,bAlt=False,bCmd=Fal
ActionMappings = (ActionName="ZoomOut",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=MouseScrollDown)
ActionMappings = (ActionName="UnitTypeSelectionModifier",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=LeftControl)
```
***Copy the above content and save the file as `Input.ini`.***
## Understand the Input.ini
@ -94,13 +93,20 @@ ActionMappings=(ActionName="UnitTypeSelectionModifier",bShift=False,bCtrl=False,
You can notice a single line of this file can be broken down like this.
`ActionMappings=(ActionName="AttackMove",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=A)`
- `ActionMappings=(***)`: Indicates content is an action mapping. i.e. a hotkey
- `ActionName="AttackMove"`: Indicates the name of the selected action. Here, we are using the `AttackMove` move action, that forces your selected army to attack.
- `Key=A`: Indicates key being mapped to the action. Set to `Key=Tab` to require the Tab key to be pressed instead, to perform the `AttackMove` action.
- `bShift=False`: Indicates that the Shift key is not held. Set to `bShift=True` to require a shift key held to perform the mapped action.
- `bCtrl=False`: Indicates that the Ctrl key is not held. Set to `bCtrl=True` to require a ctrl key held to perform the mapped action.
- `bAlt=False`: Indicates that the Alt key is not held. Set to `bAlt=True` to require a alt key held to perform the mapped action.
- `bCmd=False`: Indicates that the Cmd key is not held. Set to `bCmd=True` to require a cmd key held to perform the mapped action. (Macs not supported by IGP)
- `ActionName="AttackMove"`: Indicates the name of the selected action. Here, we are using the `AttackMove` move action,
that forces your selected army to attack.
- `Key=A`: Indicates key being mapped to the action. Set to `Key=Tab` to require the Tab key to be pressed instead, to
perform the `AttackMove` action.
- `bShift=False`: Indicates that the Shift key is not held. Set to `bShift=True` to require a shift key held to
perform the mapped action.
- `bCtrl=False`: Indicates that the Ctrl key is not held. Set to `bCtrl=True` to require a ctrl key held to perform
the mapped action.
- `bAlt=False`: Indicates that the Alt key is not held. Set to `bAlt=True` to require a alt key held to perform the
mapped action.
- `bCmd=False`: Indicates that the Cmd key is not held. Set to `bCmd=True` to require a cmd key held to perform the
mapped action. (Macs not supported by IGP)
## Modify the Input.ini file
@ -108,9 +114,11 @@ You are now going to want to modify the file with your own hotkey setup.
To do this, replace any of the Key=`VALUE` mapped to the desired actions with any value from the list below.
*For example, you can swap the Z Key value in SelectUnitProductionBuildings to C. Which would look like this: `ActionMappings=(ActionName="SelectUnitProductionBuildings",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=C)`*
*For example, you can swap the Z Key value in SelectUnitProductionBuildings to C. Which would look like
this: `ActionMappings=(ActionName="SelectUnitProductionBuildings",bShift=False,bCtrl=False,bAlt=False,bCmd=False,Key=C)`*
**Key Values**
- `AnyKey`
- `MouseX`
- `MouseY`
@ -242,8 +250,9 @@ To do this, replace any of the Key=`VALUE` mapped to the desired actions with an
# Transfer the Input.ini file
Drag and drop this new `Input.ini` file into `WindowsClient` folder in IGP. This folder can be found under `C:\Users\YOUR_USER\AppData\Local\Programs\CURRENT_IMMORTAL_CLIENT\Immortal\Saved\Config\WindowsClient\`.
Remember to use proper values for `YOUR_USER` and `CURRENT_IMMORTAL_CLIENT`.
Drag and drop this new `Input.ini` file into `WindowsClient` folder in IGP. This folder can be found
under `C:\Users\YOUR_USER\AppData\Local\Programs\CURRENT_IMMORTAL_CLIENT\Immortal\Saved\Config\WindowsClient\`. Remember
to use proper values for `YOUR_USER` and `CURRENT_IMMORTAL_CLIENT`.
There will be a blank `Input.ini` file in this folder. You can safely override it with your modified file.

36
IGP/wwwroot/content/notes/the-basics/armor-types.md

@ -1,8 +1,5 @@
---
title: Armor Types
summary: Heavy, Medium, and Light. What does it mean?
created_date: 4/13/2000
updated_date: 4/13/2000
title: Armor Types summary: Heavy, Medium, and Light. What does it mean? created_date: 4/13/2000 updated_date: 4/13/2000
---
## Armor Types
@ -13,12 +10,14 @@ All units in "IMMORTAL: Gates of Pyre" have one of the three armor types.
- Medium
- Light
These types mean nothing inherently. A Light armor unit will not take more damage than a Heavy armor unit, from the same damage source.
These types mean nothing inherently. A Light armor unit will not take more damage than a Heavy armor unit, from the same
damage source.
Where this change is some units deal particular damage to a particular armor type.
For example, a Dervish deals 16 damage generally, 24 to medium, and 32 damage to heavy armor types.
As an opposite example, the Zephyr will deal 20 damage generally, 24 to medium and 28 to heavy armor types. And as a different example, Sipari deals 20 damage generally, 18 damage to heavy armor.
For example, a Dervish deals 16 damage generally, 24 to medium, and 32 damage to heavy armor types. As an opposite
example, the Zephyr will deal 20 damage generally, 24 to medium and 28 to heavy armor types. And as a different example,
Sipari deals 20 damage generally, 18 damage to heavy armor.
Wait? There was no pattern to what general and specific damage is.
@ -32,29 +31,36 @@ They have two armies, one is a pack of Masked Hunters, and one is a pack of Xaca
Masked Hunters have the light armor type and Xacals have the heavy armor type.
Well, you know Dervish deals the most damage to Light, and Zephyr deals the most damage to heavy, so you should generally have your Dervish attack the Masked Hunters and the Zephyr's attack the Xacals.
Well, you know Dervish deals the most damage to Light, and Zephyr deals the most damage to heavy, so you should
generally have your Dervish attack the Masked Hunters and the Zephyr's attack the Xacals.
But wait, your opponent moved their two armies together!
Defensively, Masked Hunters deal less damage to the heavy armor type, and Xacals do less damage to Light.
So while you want your Dervish to attack Masked Hunters, and the Zephyrs to attack the Xacals, you also want your Dervish to defend against the Xacals and Zephyrs to defend against the Masked Hunters.
So while you want your Dervish to attack Masked Hunters, and the Zephyrs to attack the Xacals, you also want your
Dervish to defend against the Xacals and Zephyrs to defend against the Masked Hunters.
In mixed battles, you can spend some micro on your units to defend against enemy attackers they are strong against while having them attack potentially different enemies.
So what should you do? Build some Sipari, a Light armor unit, to help tank some Xacal shots, to give your Dervish and Zephyrs room to maneuver against their key targets.
In mixed battles, you can spend some micro on your units to defend against enemy attackers they are strong against while
having them attack potentially different enemies.
So what should you do? Build some Sipari, a Light armor unit, to help tank some Xacal shots, to give your Dervish and
Zephyrs room to maneuver against their key targets.
## What else is there?
Some units have additional tags, such as Structure and Etheric. Units can deal particular, or BONUS damage against enemies with those tags.
Some units have additional tags, such as Structure and Etheric. Units can deal particular, or BONUS damage against
enemies with those tags.
**What are structures?** Structures are any building. Pretty much anything not a part of your moving army.
As an example of bonus damage, the Absolver deals +8 damage to structures, giving it 29 DPS to heavy structures. Conversely, it has 23 DPS against heavy structures when deployed, meaning Absolvers are better at sniping most structures when mobilized.
As an example of bonus damage, the Absolver deals +8 damage to structures, giving it 29 DPS to heavy structures.
Conversely, it has 23 DPS against heavy structures when deployed, meaning Absolvers are better at sniping most
structures when mobilized.
Heads up though, that not all structures are heavy, examples include Radiant Wards generated by the Dervish.
**What are etherics?** Etherics are most caster units in the game. These casters can generally win long drawn-out fights, so are good to snipe with units that deal extra damage against them.
**What are etherics?** Etherics are most caster units in the game. These casters can generally win long drawn-out
fights, so are good to snipe with units that deal extra damage against them.
An etheric example would be the Bloodbounds Culling Strike ability, which deals bonus damage against etheric.

6
IGP/wwwroot/content/notes/the-basics/economy-overview.md

@ -1,6 +1,4 @@
---
title: Economy Overview
summary: Alloy, Ether and Supply. Don't forget to expand!
created_date: 1/01/2000
updated_date: 1/01/2000
title: Economy Overview summary: Alloy, Ether and Supply. Don't forget to expand!
created_date: 1/01/2000 updated_date: 1/01/2000
---

6
IGP/wwwroot/content/notes/the-basics/families-factions-and-immortal-vanguards.md

@ -1,6 +1,4 @@
---
title: Families, Factions, and Immortal Vanguards
summary: How IMMORTAL: Gates of Pyre handle your army selection choices
created_date: 1/01/2000
updated_date: 1/01/2000
title: Families, Factions, and Immortal Vanguards summary: How IMMORTAL: Gates of Pyre handle your army selection
choices created_date: 1/01/2000 updated_date: 1/01/2000
---

6
IGP/wwwroot/content/notes/the-basics/immortal-spells-and-pyre.md

@ -1,8 +1,6 @@
---
title: Immortals Spells and Pyre
summary: Customize your hotkeys in the pre-alpha.
created_date: 1/01/2000
updated_date: 1/01/2000
title: Immortals Spells and Pyre summary: Customize your hotkeys in the pre-alpha. created_date: 1/01/2000 updated_date:
1/01/2000
---
## What are Immortals

5
IGP/wwwroot/content/notes/the-basics/timing-and-scouting.md

@ -1,8 +1,5 @@
---
title: Timing and Scouting
summary: Knowing is half the battle.
created_date: 1/01/2000
updated_date: 1/01/2000
title: Timing and Scouting summary: Knowing is half the battle. created_date: 1/01/2000 updated_date: 1/01/2000
---
## Timing

2
IGP/wwwroot/generated/AgileTaskModels.json

File diff suppressed because one or more lines are too long

3
IGP_Convert/Program.cs

@ -10,7 +10,8 @@ var options = new DbContextOptionsBuilder<DatabaseContext>();
options.UseSqlite($"Filename={projectPath}/{webProjectName}/Database.db");
// Load our database
using (var db = new DatabaseContext(options.Options)) {
using (var db = new DatabaseContext(options.Options))
{
// And save data in format Blazor Wasm can use
File.WriteAllTextAsync($"{webPath}/GitPatchModels.json", JsonSerializer.Serialize(db.GitPatchModels));
File.WriteAllTextAsync($"{webPath}/GitChangeModels.json", JsonSerializer.Serialize(db.GitChangeModels));

73
Model/BuildOrders/BuildOrderModel.cs

@ -1,7 +1,6 @@
using System.Collections.Generic;
using System.Linq;
using Model.Entity;
using Model.Entity.Data;
namespace Model.BuildOrders;
@ -9,75 +8,15 @@ public class BuildOrderModel
{
public string Name { get; set; } = "";
public string Color { get; set; } = "red";
public int CurrentSupplyUsed { get; set; } = 0;
public Dictionary<int, List<EntityModel>> StartedOrders { get; set; } = new()
{
{
0,
new List<EntityModel>
{
EntityModel.Get(DataType.STARTING_Bastion),
EntityModel.Get(DataType.STARTING_TownHall_Aru)
}
}
};
public Dictionary<int, List<EntityModel>> CompletedOrders { get; set; } = new()
{
{
0,
new List<EntityModel>
{
EntityModel.Get(DataType.STARTING_Bastion),
EntityModel.Get(DataType.STARTING_TownHall_Aru)
}
}
};
public Dictionary<string, int> UniqueCompletedTimes { get; set; } = new()
{
{
DataType.STARTING_Bastion, 0
},
{
DataType.STARTING_TownHall_Aru, 0
}
};
public Dictionary<string, int> UniqueCompletedCount { get; set; } = new()
{
{
DataType.STARTING_Bastion, 1
},
{
DataType.STARTING_TownHall_Aru, 1
}
};
public Dictionary<int, int> SupplyCountTimes { get; set; } = new()
{
{
0, 0
}
};
public string Notes { get; set; } = @"";
public List<string> BuildTypes { get; set; } = new();
public List<EntityModel> GetCompletedBefore(int interval)
{
return (from ordersAtTime in StartedOrders
from orders in ordersAtTime.Value
where ordersAtTime.Key >= interval
where ordersAtTime.Key + (orders.Production() == null ? 0 : orders.Production().BuildTime) <= interval
select orders).ToList();
}
public int CurrentSupplyUsed { get; set; } = 0;
public Dictionary<int, List<EntityModel>> StartedOrders { get; set; } = new();
public Dictionary<int, List<EntityModel>> CompletedOrders { get; set; } = new();
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 List<EntityModel> GetHarvestersCompletedBefore(int interval)
{

5
Model/Doc/DocContentModel.cs

@ -12,7 +12,10 @@ public class DocContentModel
public int? ParentId { get; set; } = null;
public int? DocSectionModelId { get; set; } = null;
public string Href { get; set; }
[NotMapped] public virtual ICollection<DocContentModel> DocumentationModels { get; set; } = new List<DocContentModel>();
[NotMapped]
public virtual ICollection<DocContentModel> DocumentationModels { get; set; } = new List<DocContentModel>();
[NotMapped] public virtual DocContentModel Parent { get; set; }
[NotMapped] public virtual int PageOrder { get; set; }
public DateTime CreatedDate { get; set; }

5
Model/Economy/EconomyOverTimeModel.cs

@ -100,7 +100,6 @@ public class EconomyOverTimeModel
// Remove Funds from Build Order
if (buildOrder.StartedOrders.TryGetValue(interval, out var ordersAtTime))
{
foreach (var order in ordersAtTime)
{
var foundEntity = EntityModel.GetDictionary()[order.DataType];
@ -116,11 +115,8 @@ public class EconomyOverTimeModel
}
}
}
// Handle new entities
if (buildOrder.StartedOrders.TryGetValue(interval, out var ordersCompletedAtTime))
{
foreach (var newEntity in ordersCompletedAtTime)
{
var harvest = newEntity;
@ -132,4 +128,3 @@ public class EconomyOverTimeModel
}
}
}
}

196
Model/Entity/Data/DATA.cs

@ -55,7 +55,8 @@ public class DATA
DataType.TEAPOT_FlyingTeapot, new EntityModel(DataType.TEAPOT_FlyingTeapot, EntityType.Teapot)
.AddPart(new EntityInfoModel
{
Name = "Flying Teapot", Description = "Basic observer. Can fly and see hidden units within 1000 range.",
Name = "Flying Teapot",
Description = "Basic observer. Can fly and see hidden units within 1000 range.",
Notes = @"Doesn't take up a scout slot."
})
.AddPart(new EntityRequirementModel { Id = DataType.TEAPOT_Teapot })
@ -322,16 +323,19 @@ public class DATA
.AddPart(new EntityInfoModel
{
Name = "Mother's Hunger",
Description = "Mala's Blood Wells grant you pyre for nearby non-quitl deaths, based on the supply.",
Description =
"Mala's Blood Wells grant you pyre for nearby non-quitl deaths, based on the supply.",
Notes = "+1 per supply"
})
},
{
DataType.IPASSIVE_StalkersSense,
new EntityModel(DataType.IPASSIVE_StalkersSense, EntityType.Passive)
.AddPart(new EntityInfoModel
{ Name = "Stalker's Sense", Description = "Xol's units sense nearby enemies in the fog of war.", Notes = "Not implemented."})
{
Name = "Stalker's Sense", Description = "Xol's units sense nearby enemies in the fog of war.",
Notes = "Not implemented."
})
},
// Keys
@ -369,7 +373,7 @@ public class DATA
.AddPart(new EntityVitalityModel { Health = 500, Armor = ArmorType.Heavy })
.AddPart(new EntityWeaponModel
{ Damage = 30, AttacksPerSecond = 1.401f, Targets = TargetType.All, Range = 700 })
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_BastionPassives})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_BastionPassives })
},
{
DataType.STARTING_Tower,
@ -389,7 +393,7 @@ public class DATA
MediumDamage = 25, HeavyDamage = 30
})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Respite })
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_HallowedGround})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedGround })
},
// Starting Structures
// Aru
@ -406,7 +410,7 @@ public class DATA
HarvestedPerInterval = 1, RequiresWorker = true, Resource = ResourceType.Alloy, Slots = 6,
TotalAmount = 6000
})
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_Rootway})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Rootway })
},
// Starting Structures
// Q'Rath
@ -423,7 +427,7 @@ public class DATA
HarvestedPerInterval = 6, RequiresWorker = false, Resource = ResourceType.Alloy, Slots = 1,
TotalAmount = 6000
})
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_HallowedGround})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedGround })
},
// Vanguard
@ -589,8 +593,6 @@ public class DATA
{ Damage = 16, Range = 700, AttacksPerSecond = 0.606f, Targets = TargetType.Ground })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_ProjectileGestation })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_FallenHarvest })
},
{
DataType.VANGUARD_DreadSister_Mala,
@ -647,8 +649,8 @@ public class DATA
.AddPart(new EntityMovementModel { Speed = 350, Movement = MovementType.Ground })
.AddPart(new EntityWeaponModel
{ Damage = 11, Range = 400, AttacksPerSecond = 1.02f, Targets = TargetType.All })
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_Stalk})
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_Ambush})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Stalk })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Ambush })
},
{
DataType.VANGUARD_WhiteWoodReaper_Xol,
@ -726,10 +728,10 @@ public class DATA
Damage = 20, HeavyDamage = 18, Range = 180, AttacksPerSecond = 0.699f,
SecondsBetweenAttacks = 1.43f, Targets = TargetType.Ground
})
.AddPart(new EntityIdUpgradeModel() { Id = DataType.UPGRADE_GreavesOfAhqar})
.AddPart(new EntityIdUpgradeModel() { Id = DataType.UPGRADE_FortifiedIcons})
.AddPart(new EntityIdPassiveModel() { Id = DataType.PASSIVE_GreavesOfAhqar})
.AddPart(new EntityIdPassiveModel() { Id = DataType.PASSIVE_FortifiedIcons})
.AddPart(new EntityIdUpgradeModel { Id = DataType.UPGRADE_GreavesOfAhqar })
.AddPart(new EntityIdUpgradeModel { Id = DataType.UPGRADE_FortifiedIcons })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_GreavesOfAhqar })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_FortifiedIcons })
},
{
DataType.UNIT_Magi,
@ -833,8 +835,7 @@ public class DATA
.AddPart(new EntityIdUpgradeModel { Id = DataType.UPGRADE_SiroccoScript })
.AddPart(new EntityIdUpgradeModel { Id = DataType.UPGRADE_RadiantWard })
.AddPart(new EntityIdAbilityModel { Id = DataType.ABILITY_RadiantWard })
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_SiroccoScript})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_SiroccoScript })
},
{
DataType.UNIT_Absolver,
@ -937,7 +938,6 @@ public class DATA
Damage = 60, MediumDamage = 80, HeavyDamage = 100, Range = 1300, AttacksPerSecond = 0.143f,
Targets = TargetType.Ground
})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedRuin })
},
{
@ -1026,9 +1026,9 @@ public class DATA
.AddPart(new EntityMovementModel { Speed = 420, Movement = MovementType.Air })
.AddPart(new EntityWeaponModel
{ Damage = 32, Range = 600, AttacksPerSecond = 0.556f, Targets = TargetType.Ground })
.AddPart(new EntityIdUpgradeModel(){Id = DataType.UPGRADE_WingsOfTheKenLatir})
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_WingsOfTheKenLatir})
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_ExecutionRites})
.AddPart(new EntityIdUpgradeModel { Id = DataType.UPGRADE_WingsOfTheKenLatir })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_WingsOfTheKenLatir })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_ExecutionRites })
},
{
DataType.UNIT_SharU,
@ -1167,7 +1167,6 @@ public class DATA
Targets = TargetType.Ground
})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_QuenchingScythes })
.AddPart(new EntityIdPassiveModel { Id = DataType.ABILITY_CullingStrike })
},
{
@ -1267,7 +1266,7 @@ public class DATA
})
.AddPart(new EntityIdUpgradeModel { Id = DataType.UPGRADE_DenInstinct })
.AddPart(new EntityIdUpgradeModel { Id = DataType.UPGRADE_PursuitLigaments })
.AddPart(new EntityIdPassiveModel() { Id = DataType.PASSIVE_ExternalDigestion })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_ExternalDigestion })
},
{
DataType.UNIT_Resinant,
@ -1332,8 +1331,7 @@ public class DATA
TargetType.Air
})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_AaroxBurn })
.AddPart(new EntityIdAbilityModel() { Id = DataType.ABILITY_DiveBomb })
.AddPart(new EntityIdAbilityModel { Id = DataType.ABILITY_DiveBomb })
},
{
DataType.UNIT_Thrum,
@ -1378,7 +1376,7 @@ public class DATA
.AddPart(new EntityWeaponModel
{ Damage = 9, Range = 500, AttacksPerSecond = 0.714f, Targets = TargetType.Ground })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_GuidingAmber })
.AddPart(new EntityIdPassiveModel() { Id = DataType.PASSIVE_WraithBowRange })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_WraithBowRange })
},
{
DataType.UNIT_Behemoth,
@ -1409,8 +1407,6 @@ public class DATA
.AddPart(new EntityIdUpgradeModel { Id = DataType.UPGRADE_BehemothCapacity })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_BehemothCapacity })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_QuitlStorage2 })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_FireQuitl })
},
{
@ -1420,7 +1416,7 @@ public class DATA
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
.AddPart(new EntityVitalityModel { Health = 65, Armor = ArmorType.Light })
.AddPart(new EntityMovementModel { Speed = 168, Movement = MovementType.Ground })
.AddPart(new EntityIdPassiveModel() {Id = DataType.PASSIVE_Temporary})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Temporary })
},
// Upgrades
// Q'Rath
@ -1442,8 +1438,6 @@ public class DATA
})
.AddPart(new EntityVanguardReplacedModel
{ ImmortalId = DataType.IMMORTAL_Orzum, ReplacedById = DataType.UPGRADE_FaithCastBlades })
},
{
DataType.UPGRADE_RadiantWard,
@ -1481,8 +1475,6 @@ public class DATA
})
.AddPart(new EntityVanguardReplacedModel
{ ImmortalId = DataType.IMMORTAL_Orzum, ReplacedById = DataType.UPGRADE_IconOfKhastEem })
},
{
@ -1496,10 +1488,10 @@ public class DATA
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "TAB" })
.AddPart(new EntityFactionModel { Faction = FactionType.QRath })
.AddPart(new EntityProductionModel { Alloy = 50, Ether = 100, BuildTime = 60 })
.AddPart(new EntityRequirementModel { Id = DataType.BUILDING_Reliquary, Requirement = RequirementType.Research_Building})
.AddPart(new EntityRequirementModel
{ Id = DataType.BUILDING_Reliquary, Requirement = RequirementType.Research_Building })
.AddPart(new EntityVanguardAddedModel
{ ReplaceId = DataType.UPGRADE_GreavesOfAhqar, ImmortalId = DataType.IMMORTAL_Orzum })
},
{
DataType.UPGRADE_RelicOfTheWrathfulGaze,
@ -1526,7 +1518,8 @@ public class DATA
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "TAB" })
.AddPart(new EntityFactionModel { Faction = FactionType.QRath })
.AddPart(new EntityProductionModel { Alloy = 50, Ether = 75, BuildTime = 55 })
.AddPart(new EntityRequirementModel { Id = DataType.BUILDING_Reliquary, Requirement = RequirementType.Production_Building })
.AddPart(new EntityRequirementModel
{ Id = DataType.BUILDING_Reliquary, Requirement = RequirementType.Production_Building })
},
{
DataType.UPGRADE_ZephyrRange,
@ -1539,7 +1532,8 @@ public class DATA
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "TAB" })
.AddPart(new EntityFactionModel { Faction = FactionType.QRath })
.AddPart(new EntityProductionModel { Alloy = 150, Ether = 100, BuildTime = 43 })
.AddPart(new EntityRequirementModel { Id = DataType.UPGRADE_ZephyrRange, Requirement = RequirementType.Production_Building })
.AddPart(new EntityRequirementModel
{ Id = DataType.UPGRADE_ZephyrRange, Requirement = RequirementType.Production_Building })
.AddPart(new EntityIdUpgradeModel { Id = DataType.UPGRADE_WindStep })
.AddPart(new EntityIdUpgradeModel { Id = DataType.UPGRADE_ZephyrRange })
},
@ -1578,8 +1572,6 @@ public class DATA
})
.AddPart(new EntityVanguardAddedModel
{ ReplaceId = DataType.UPGRADE_FortifiedIcons, ImmortalId = DataType.IMMORTAL_Orzum })
},
{
DataType.UPGRADE_BladesOfTheGodhead,
@ -1622,12 +1614,12 @@ public class DATA
{
Name = "Offering",
Descriptive = DescriptiveType.Upgrade,
Description = "Unlocks Offering" })
Description = "Unlocks Offering"
})
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "TAB" })
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
.AddPart(new EntityVanguardReplacedModel
{ ImmortalId = DataType.IMMORTAL_Xol, ReplacedById = DataType.UPGRADE_Stalk })
.AddPart(new EntityRequirementModel
{
Id = DataType.BUILDING_AltarOfTheWorthy,
@ -1649,7 +1641,6 @@ public class DATA
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
.AddPart(new EntityVanguardReplacedModel
{ ImmortalId = DataType.IMMORTAL_Xol, ReplacedById = DataType.UPGRADE_Ambush })
.AddPart(new EntityRequirementModel
{
Id = DataType.BUILDING_AmberWomb,
@ -1770,7 +1761,6 @@ public class DATA
.AddPart(new EntityProductionModel { Alloy = 100, Ether = 80, BuildTime = 60 })
.AddPart(new EntityVanguardAddedModel
{ ReplaceId = DataType.UPGRADE_Offering, ImmortalId = DataType.IMMORTAL_Xol })
},
{
@ -1889,7 +1879,8 @@ public class DATA
Name = "Greaves Of Ahqar", Descriptive = DescriptiveType.Ability,
Description = @"+75 Sipari Speed"
})
.AddPart(new EntityRequirementModel(){ Id = DataType.UPGRADE_GreavesOfAhqar, Requirement = RequirementType.Research_Upgrade})
.AddPart(new EntityRequirementModel
{ Id = DataType.UPGRADE_GreavesOfAhqar, Requirement = RequirementType.Research_Upgrade })
.AddPart(new EntityFactionModel { Faction = FactionType.QRath })
},
@ -1901,7 +1892,10 @@ public class DATA
Name = "Relic Of The Wrathful Gaze", Descriptive = DescriptiveType.Ability,
Description = @"Increases Castigator range against air."
})
.AddPart(new EntityRequirementModel(){ Id = DataType.UPGRADE_RelicOfTheWrathfulGaze, Requirement = RequirementType.Research_Upgrade})
.AddPart(new EntityRequirementModel
{
Id = DataType.UPGRADE_RelicOfTheWrathfulGaze, Requirement = RequirementType.Research_Upgrade
})
.AddPart(new EntityFactionModel { Faction = FactionType.QRath })
},
{
@ -1912,7 +1906,8 @@ public class DATA
Name = "Wings of the Ken'Latir", Descriptive = DescriptiveType.Passive,
Description = @"Increases the Warden's speed and shields significantly."
})
.AddPart(new EntityRequirementModel(){ Id = DataType.UPGRADE_WingsOfTheKenLatir, Requirement = RequirementType.Research_Upgrade})
.AddPart(new EntityRequirementModel
{ Id = DataType.UPGRADE_WingsOfTheKenLatir, Requirement = RequirementType.Research_Upgrade })
.AddPart(new EntityFactionModel { Faction = FactionType.QRath })
},
@ -1935,7 +1930,8 @@ public class DATA
Name = "Icon Of Khast'Eem", Descriptive = DescriptiveType.Passive,
Description = @"Grants the Zentari shields and flat armor reduction."
})
.AddPart(new EntityRequirementModel(){ Id = DataType.UPGRADE_IconOfKhastEem, Requirement = RequirementType.Research_Upgrade})
.AddPart(new EntityRequirementModel
{ Id = DataType.UPGRADE_IconOfKhastEem, Requirement = RequirementType.Research_Upgrade })
.AddPart(new EntityFactionModel { Faction = FactionType.QRath })
},
@ -1947,9 +1943,9 @@ public class DATA
Name = "Faith Cast Blades", Descriptive = DescriptiveType.Passive,
Description = @"Increases the range of the Zentari's ranged weapon."
})
.AddPart(new EntityRequirementModel(){ Id = DataType.UPGRADE_FaithCastBlades, Requirement = RequirementType.Research_Upgrade})
.AddPart(new EntityRequirementModel
{ Id = DataType.UPGRADE_FaithCastBlades, Requirement = RequirementType.Research_Upgrade })
.AddPart(new EntityFactionModel { Faction = FactionType.QRath })
},
{
@ -1971,7 +1967,7 @@ public class DATA
Name = "Sirocco Script Rites", Descriptive = DescriptiveType.Passive,
Description = @"Increases the derish's movement speed"
})
.AddPart(new EntityRequirementModel(){ Id = DataType.UPGRADE_SiroccoScript})
.AddPart(new EntityRequirementModel { Id = DataType.UPGRADE_SiroccoScript })
.AddPart(new EntityFactionModel { Faction = FactionType.QRath })
},
@ -2003,7 +1999,8 @@ public class DATA
.AddPart(new EntityInfoModel
{
Name = "Regent's Wrath", Descriptive = DescriptiveType.Passive,
Description = @"Scepters gain energy when stabilized. They lose energy when moving. Energy is spent to have splash on attack."
Description =
@"Scepters gain energy when stabilized. They lose energy when moving. Energy is spent to have splash on attack."
})
.AddPart(new EntityFactionModel { Faction = FactionType.QRath })
},
@ -2061,7 +2058,8 @@ public class DATA
Name = "Wraith Bow Range", Descriptive = DescriptiveType.Ability,
Description = @"Increases Wraith Bow range against air."
})
.AddPart(new EntityRequirementModel(){ Id = DataType.UPGRADE_WraithBowRange, Requirement = RequirementType.Research_Upgrade})
.AddPart(new EntityRequirementModel
{ Id = DataType.UPGRADE_WraithBowRange, Requirement = RequirementType.Research_Upgrade })
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
},
@ -2071,7 +2069,7 @@ public class DATA
.AddPart(new EntityInfoModel
{
Name = "Rootway", Descriptive = DescriptiveType.Passive,
Description = @"Building generates Rootway.",
Description = @"Building generates Rootway."
})
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
},
@ -2082,7 +2080,7 @@ public class DATA
.AddPart(new EntityInfoModel
{
Name = "Quitl Storage", Descriptive = DescriptiveType.Passive,
Description = @"Unit stores quitl for attacks.",
Description = @"Unit stores quitl for attacks."
})
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
},
@ -2093,10 +2091,11 @@ public class DATA
.AddPart(new EntityInfoModel
{
Name = "Quitl Storage", Descriptive = DescriptiveType.Passive,
Description = @"Unit stores more quitl for attacks.",
Description = @"Unit stores more quitl for attacks."
})
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
.AddPart(new EntityRequirementModel { Id = DataType.UPGRADE_BehemothCapacity, Requirement = RequirementType.Research_Upgrade} )
.AddPart(new EntityRequirementModel
{ Id = DataType.UPGRADE_BehemothCapacity, Requirement = RequirementType.Research_Upgrade })
},
{
@ -2116,20 +2115,20 @@ public class DATA
.AddPart(new EntityInfoModel
{
Name = "Temporary", Descriptive = DescriptiveType.Passive,
Description = @"This unit has a limited duration before it dies.",
Description = @"This unit has a limited duration before it dies."
})
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
},
{
DataType.PASSIVE_Stalk,
new EntityModel(DataType.PASSIVE_Stalk, EntityType.Passive)
.AddPart(new EntityInfoModel
{
Name = "Stalk", Descriptive = DescriptiveType.Passive,
Description = @"After remaining stationary for several seconds, gain Hidden 3 and a movement speed boost.",
Description =
@"After remaining stationary for several seconds, gain Hidden 3 and a movement speed boost.",
Notes = "Lose hidden on attacking"
})
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
@ -2142,10 +2141,10 @@ public class DATA
.AddPart(new EntityInfoModel
{
Name = "Ambush", Descriptive = DescriptiveType.Passive,
Description = @"This unit deals double damage when attacking from hidden.",
Description = @"This unit deals double damage when attacking from hidden."
})
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
.AddPart(new EntityRequirementModel(){Id=DataType.UPGRADE_Ambush})
.AddPart(new EntityRequirementModel { Id = DataType.UPGRADE_Ambush })
},
{
@ -2154,7 +2153,7 @@ public class DATA
.AddPart(new EntityInfoModel
{
Name = "Hidden X", Descriptive = DescriptiveType.Passive,
Description = @"This unit cannot be seen unless enemies units are within X.",
Description = @"This unit cannot be seen unless enemies units are within X."
})
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
},
@ -2194,8 +2193,6 @@ public class DATA
},
{
DataType.PASSIVE_FortifiedIcons,
new EntityModel(DataType.PASSIVE_FortifiedIcons, EntityType.Passive)
@ -2205,7 +2202,8 @@ public class DATA
Description = @"Increases Sipari shields and increases the bonus while in Hallowed Ground",
Notes = "+20 Shields, and +20 Shields on Hallowed Ground."
})
.AddPart(new EntityRequirementModel(){ Id = DataType.UPGRADE_FortifiedIcons, Requirement = RequirementType.Research_Upgrade})
.AddPart(new EntityRequirementModel
{ Id = DataType.UPGRADE_FortifiedIcons, Requirement = RequirementType.Research_Upgrade })
.AddPart(new EntityFactionModel { Faction = FactionType.QRath })
},
{
@ -2265,7 +2263,8 @@ public class DATA
Description =
@"Xacal builds up charges for double damage overtime. These charges can be spent on attacking."
})
.AddPart(new EntityRequirementModel(){ Id = DataType.UPGRADE_XacalDamage, Requirement = RequirementType.Research_Upgrade})
.AddPart(new EntityRequirementModel
{ Id = DataType.UPGRADE_XacalDamage, Requirement = RequirementType.Research_Upgrade })
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
},
{
@ -2362,12 +2361,14 @@ public class DATA
.AddPart(new EntityInfoModel
{
Name = "Radiant Ward", Descriptive = DescriptiveType.Ability,
Description = @"Spawns a mine that reveals enemy units, slows them, and makes them take increased damage for a duration.",
Description =
@"Spawns a mine that reveals enemy units, slows them, and makes them take increased damage for a duration."
})
.AddPart(new EntityFactionModel { Faction = FactionType.QRath })
.AddPart(new EntityProductionModel { DefensiveLayer = 30, Cooldown = 40 })
.AddPart(new EntityRequirementModel { Id = DataType.UPGRADE_RadiantWard })
.AddPart(new EntityVitalityModel {Health = 30, DefenseLayer = 30, Lasts = 30, Armor = ArmorType.Light, IsStructure = true})
.AddPart(new EntityVitalityModel
{ Health = 30, DefenseLayer = 30, Lasts = 30, Armor = ArmorType.Light, IsStructure = true })
.AddPart(new EntityRequirementModel
{
Id = DataType.BUILDING_HouseOfFadingSaints,
@ -2513,8 +2514,7 @@ public class DATA
})
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "D" })
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
.AddPart(new EntityRequirementModel(){ Id= DataType.UPGRADE_Offering})
.AddPart(new EntityRequirementModel { Id = DataType.UPGRADE_Offering })
},
{
DataType.ABILITY_DiveBomb,
@ -2668,12 +2668,13 @@ public class DATA
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
.AddPart(new EntityProductionModel { Alloy = 100, BuildTime = 25 })
.AddPart(new EntitySupplyModel { Takes = 6 })
.AddPart(new EntityRequirementModel { Id = DataType.UNIT_RedSeer, Requirement = RequirementType.Morph })
.AddPart(new EntityRequirementModel
{ Id = DataType.UNIT_RedSeer, Requirement = RequirementType.Morph })
.AddPart(new EntityVitalityModel { Health = 200, DefenseLayer = 60, Armor = ArmorType.Heavy })
.AddPart(new EntityMovementModel { Speed = 210, Movement = MovementType.Ground })
.AddPart(new EntityWeaponModel
{ Damage = 125, Range = 1500, AttacksPerSecond = 0.175f, Targets = TargetType.Ground })
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_HiddenX})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HiddenX })
},
// Pyre Spells
// Q'Rath
@ -2696,7 +2697,7 @@ public class DATA
MediumDamage = 25, HeavyDamage = 30
})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Respite })
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_HallowedGround})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedGround })
},
{
DataType.ISPELL_PillarOfHeaven,
@ -2711,7 +2712,8 @@ public class DATA
.AddPart(new EntityFactionModel { Faction = FactionType.QRath })
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Orzum })
.AddPart(new EntityProductionModel { Pyre = 100, Cooldown = 15 })
.AddPart(new EntityVitalityModel { Health = 300, DefenseLayer = 200, Armor = ArmorType.Heavy, IsStructure = true})
.AddPart(new EntityVitalityModel
{ Health = 300, DefenseLayer = 200, Armor = ArmorType.Heavy, IsStructure = true })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Zeal })
},
{
@ -2787,7 +2789,7 @@ public class DATA
.AddPart(new EntityWeaponModel
{ Damage = 15, Range = 800, AttacksPerSecond = 1.887f, Targets = TargetType.All })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Respite })
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_Rootway})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Rootway })
},
{
DataType.ISPELL_ConstructBloodWell,
@ -2801,10 +2803,11 @@ public class DATA
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "1" })
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
.AddPart(new EntityProductionModel { Pyre = 50, Cooldown = 21 })
.AddPart(new EntityVitalityModel {Health = 400, Energy = 100, DefenseLayer = 50, Armor = ArmorType.Heavy, IsStructure = true})
.AddPart(new EntityVitalityModel
{ Health = 400, Energy = 100, DefenseLayer = 50, Armor = ArmorType.Heavy, IsStructure = true })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_RestoreLifeblood })
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Transfusion })
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_Rootway})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Rootway })
},
{
DataType.ISPELL_RedTithe,
@ -2835,8 +2838,11 @@ public class DATA
{
DataType.ISPELL_MarkPrey,
new EntityModel(DataType.ISPELL_MarkPrey, EntityType.Pyre_Spell)
.AddPart(new EntityInfoModel { Name = "Mark Prey",
Description = @"Enemy units in the target area are <b>Revealed</b> through fog of war. Units still in the area after a short delay are marked for 10 seconds to take bonus damage and provide Pyre when killed.",
.AddPart(new EntityInfoModel
{
Name = "Mark Prey",
Description =
@"Enemy units in the target area are <b>Revealed</b> through fog of war. Units still in the area after a short delay are marked for 10 seconds to take bonus damage and provide Pyre when killed.",
Notes = "+3 pyre for kills"
})
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "1" })
@ -2847,7 +2853,11 @@ public class DATA
{
DataType.ISPELL_TheGreatHunt,
new EntityModel(DataType.ISPELL_TheGreatHunt, EntityType.Pyre_Spell)
.AddPart(new EntityInfoModel { Name = "The Great Hunt", Description = "Enemy unit and structures have their vision reduce to 3 for a short time."})
.AddPart(new EntityInfoModel
{
Name = "The Great Hunt",
Description = "Enemy unit and structures have their vision reduce to 3 for a short time."
})
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "1" })
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Xol })
@ -2875,7 +2885,7 @@ public class DATA
HarvestedPerInterval = 1, RequiresWorker = true, Resource = ResourceType.Alloy, Slots = 2,
TotalAmount = 3600
})
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_HallowedGround})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedGround })
},
{
DataType.BUPGRADE_MiningLevel2_QRath,
@ -2954,7 +2964,7 @@ public class DATA
.AddPart(new EntityProductionModel { Alloy = 250, BuildTime = 38, RequiresWorker = true })
.AddPart(new EntityVitalityModel
{ Health = 500, DefenseLayer = 500, Armor = ArmorType.Heavy, IsStructure = true })
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_HallowedGround})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedGround })
},
{
DataType.DEFENSE_FireSinger,
@ -3032,7 +3042,7 @@ public class DATA
})
.AddPart(new EntityVitalityModel
{ Health = 450, DefenseLayer = 450, Armor = ArmorType.Heavy, IsStructure = true })
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_HallowedGround})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedGround })
},
{
DataType.BUILDING_HouseOfFadingSaints,
@ -3074,7 +3084,7 @@ public class DATA
})
.AddPart(new EntityVitalityModel
{ Health = 600, DefenseLayer = 600, Armor = ArmorType.Heavy, IsStructure = true })
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_HallowedGround})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedGround })
},
{
DataType.BUILDING_EyeOfAros,
@ -3139,7 +3149,7 @@ public class DATA
HarvestedPerInterval = 1, RequiresWorker = true, Resource = ResourceType.Alloy, Slots = 2,
TotalAmount = 3600
})
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_Rootway})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Rootway })
},
{
DataType.BUPGRADE_GodHeart,
@ -3161,7 +3171,7 @@ public class DATA
})
.AddPart(new EntityProductionModel
{ Alloy = 100, Ether = 75, BuildTime = 36, RequiresWorker = false })
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_Rootway})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Rootway })
},
{
DataType.BUPGRADE_MiningLevel2_Aru,
@ -3240,7 +3250,7 @@ public class DATA
.AddPart(new EntityProductionModel { Alloy = 250, BuildTime = 36, RequiresWorker = true })
.AddPart(new EntityVitalityModel
{ Health = 900, DefenseLayer = 100, Armor = ArmorType.Heavy, IsStructure = true })
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_Rootway})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Rootway })
},
{
DataType.BUILDING_Neurocyte,
@ -3282,9 +3292,11 @@ public class DATA
.AddPart(new EntityInfoModel { Name = "Omnivore", Descriptive = DescriptiveType.Upgrade })
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "SHIFT" })
.AddPart(new EntityFactionModel { Faction = FactionType.Aru })
.AddPart(new EntityRequirementModel {
.AddPart(new EntityRequirementModel
{
Id = DataType.DEFENSE_Aerovore,
Requirement = RequirementType.Morph })
Requirement = RequirementType.Morph
})
.AddPart(new EntityProductionModel { Alloy = 50, BuildTime = 18, RequiresWorker = false })
.AddPart(new EntityVitalityModel
{ Health = 400, DefenseLayer = 50, Armor = ArmorType.Heavy, IsStructure = true })
@ -3314,7 +3326,7 @@ public class DATA
})
.AddPart(new EntityVitalityModel
{ Health = 1000, DefenseLayer = 250, Armor = ArmorType.Heavy, IsStructure = true })
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_Rootway})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Rootway })
},
{
DataType.BUILDING_BoneCanopy,
@ -3336,7 +3348,7 @@ public class DATA
})
.AddPart(new EntityVitalityModel
{ Health = 1000, DefenseLayer = 300, Armor = ArmorType.Heavy, IsStructure = true })
.AddPart(new EntityIdPassiveModel(){Id = DataType.PASSIVE_Rootway})
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Rootway })
},
{
DataType.BUILDING_RedVale,

8
Model/Entity/EntityModel.cs

@ -20,7 +20,7 @@ public class EntityModel
private static List<EntityModel> _entityModelsOnlyHotkey = null!;
private static Dictionary<string, List<EntityModel>>? _entityModelsByHotkey= null!;
private static Dictionary<string, List<EntityModel>>? _entityModelsByHotkey;
public EntityModel(string data, string entity, bool isSpeculative = false)
@ -224,19 +224,19 @@ public class EntityModel
public EntityHotkeyModel Hotkey()
{
return ((EntityHotkeyModel)EntityParts.Find(x => x.GetType() == typeof(EntityHotkeyModel))!);
return (EntityHotkeyModel)EntityParts.Find(x => x.GetType() == typeof(EntityHotkeyModel))!;
}
public EntityFactionModel Faction()
{
return ((EntityFactionModel)EntityParts.Find(x => x.GetType() == typeof(EntityFactionModel))!);
return (EntityFactionModel)EntityParts.Find(x => x.GetType() == typeof(EntityFactionModel))!;
}
public EntityHarvestModel Harvest()
{
return ((EntityHarvestModel)EntityParts.Find(x => x.GetType() == typeof(EntityHarvestModel))!);
return (EntityHarvestModel)EntityParts.Find(x => x.GetType() == typeof(EntityHarvestModel))!;
}

4
Model/Git/GitChangeModel.cs

@ -1,6 +1,4 @@
using System;
namespace Model.Git;
namespace Model.Git;
public class GitChangeModel
{

7
Model/Notes/NoteContentModel.cs

@ -1,7 +1,6 @@
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations.Schema;
using Model.Doc;
namespace Model.Notes;
@ -12,6 +11,7 @@ public class NoteContentModel
public int? ParentId { get; set; } = null;
public int? NoteSectionModelId { get; set; } = null;
public string Href { get; set; }
//public DateTime LastUpdated { get; set; }
@ -25,7 +25,9 @@ public class NoteContentModel
public string IsHidden { get; set; } = "False";
public string IsPreAlpha { get; set; } = "True";
[NotMapped] public virtual ICollection<NoteContentModel> NoteContentModels { get; set; } = new List<NoteContentModel>();
[NotMapped]
public virtual ICollection<NoteContentModel> NoteContentModels { get; set; } = new List<NoteContentModel>();
[NotMapped] public virtual NoteContentModel Parent { get; set; }
[NotMapped] public virtual int PageOrder { get; set; }
@ -43,5 +45,4 @@ public class NoteContentModel
{
return $"notes/{GetLink()}";
}
}

2
Model/Website/WebSectionModel.cs

@ -12,5 +12,5 @@ public class WebSectionModel
public int Order { get; set; } = 0;
public string IsPrivate { get; set; } = "True";
[NotMapped] public List<WebPageModel> WebPageModels { get; set; } = new List<WebPageModel>();
[NotMapped] public List<WebPageModel> WebPageModels { get; set; } = new();
}

11
Services/Development/DocumentationService.cs

@ -22,7 +22,6 @@ public class DocumentationService : IDocumentationService
public List<DocConnectionModel> DocConnectionModels { get; set; } = new();
public void Subscribe(Action? action)
{
OnChange += action;
@ -88,18 +87,10 @@ public class DocumentationService : IDocumentationService
}
foreach (var content in DocContentModels)
{
if (content.DocSectionModelId != null)
{
foreach (var section in DocSectionModels)
{
if (section.Id == content.DocSectionModelId)
{
section.DocumentationModels.Add(content);
}
}
}
}
ByPageOrder();
}
@ -109,7 +100,7 @@ public class DocumentationService : IDocumentationService
{
DocContentModelsByPageOrder = new List<DocContentModel>();
int order = 1;
var order = 1;
foreach (var documentation in DocContentModels)
{
if (documentation.Parent != null) continue;

37
Services/Development/GitService.cs

@ -10,7 +10,8 @@ using Microsoft.EntityFrameworkCore;
namespace Services.Development;
public class GitService : IGitService {
public class GitService : IGitService
{
private readonly HttpClient httpClient;
private bool isLoaded;
@ -18,7 +19,8 @@ public class GitService : IGitService {
private event Action OnChange = default!;
public GitService(HttpClient httpClient) {
public GitService(HttpClient httpClient)
{
this.httpClient = httpClient;
}
@ -32,30 +34,32 @@ public class GitService : IGitService {
#endif
public void Subscribe(Action action) {
public void Subscribe(Action action)
{
OnChange += action;
}
public void Unsubscribe(Action action) {
public void Unsubscribe(Action action)
{
OnChange -= action;
}
public bool IsLoaded() {
public bool IsLoaded()
{
return isLoaded;
}
#if NO_SQL
public async Task Load() {
if (isLoaded) {
return;
}
public async Task Load()
{
if (isLoaded) return;
GitChangeModels = (await httpClient.GetFromJsonAsync<GitChangeModel[]>("generated/GitChangeModels.json") ?? Array.Empty<GitChangeModel>()).ToList();
GitPatchModels = (await httpClient.GetFromJsonAsync<GitPatchModel[]>("generated/GitPatchModels.json") ?? Array.Empty<GitPatchModel>()).ToList();
GitChangeModels = (await httpClient.GetFromJsonAsync<GitChangeModel[]>("generated/GitChangeModels.json") ??
Array.Empty<GitChangeModel>()).ToList();
GitPatchModels = (await httpClient.GetFromJsonAsync<GitPatchModel[]>("generated/GitPatchModels.json") ??
Array.Empty<GitPatchModel>()).ToList();
isLoaded = true;
@ -64,7 +68,6 @@ public class GitService : IGitService {
}
#else
public async Task Load(DatabaseContext database) {
Database = database;
@ -85,11 +88,13 @@ public class GitService : IGitService {
#endif
public void Update() {
public void Update()
{
NotifyDataChanged();
}
private void NotifyDataChanged() {
private void NotifyDataChanged()
{
OnChange?.Invoke();
}
}

77
Services/Development/NoteService.cs

@ -10,49 +10,52 @@ using Microsoft.EntityFrameworkCore;
namespace Services.Development;
public class NoteService : INoteService {
public class NoteService : INoteService
{
private readonly HttpClient httpClient;
private bool isLoaded;
private event Action OnChange = default!;
private void NotifyDataChanged() {
OnChange?.Invoke();
}
public NoteService(HttpClient httpClient) {
public NoteService(HttpClient httpClient)
{
this.httpClient = httpClient;
}
public List<NoteContentModel> NoteContentModelsByPageOrder { get; set; } = new();
public List<NoteContentModel> NoteContentModels { get; set; } = default!;
public List<NoteConnectionModel> NoteConnectionModels { get; set; } = null!;
public List<NoteSectionModel> NoteSectionModels { get; set; } = null!;
public List<NoteContentModel> NoteContentModelsByPageOrder { get; set; } = new();
public void Subscribe(Action action) {
public void Subscribe(Action action)
{
OnChange += action;
}
public void Unsubscribe(Action action) {
public void Unsubscribe(Action action)
{
OnChange -= action;
}
public bool IsLoaded() {
public bool IsLoaded()
{
return isLoaded;
}
public async Task Load() {
if (isLoaded) {
return;
}
NoteContentModels = (await httpClient.GetFromJsonAsync<NoteContentModel[]>("generated/NoteContentModels.json") ?? Array.Empty<NoteContentModel>()).ToList();
NoteConnectionModels = (await httpClient.GetFromJsonAsync<NoteConnectionModel[]>("generated/NoteConnectionModels.json") ?? Array.Empty<NoteConnectionModel>()).ToList();
NoteSectionModels = (await httpClient.GetFromJsonAsync<NoteSectionModel[]>("generated/NoteSectionModels.json") ?? Array.Empty<NoteSectionModel>()).ToList();
public async Task Load()
{
if (isLoaded) return;
NoteContentModels =
(await httpClient.GetFromJsonAsync<NoteContentModel[]>("generated/NoteContentModels.json") ??
Array.Empty<NoteContentModel>()).ToList();
NoteConnectionModels =
(await httpClient.GetFromJsonAsync<NoteConnectionModel[]>("generated/NoteConnectionModels.json") ??
Array.Empty<NoteConnectionModel>()).ToList();
NoteSectionModels =
(await httpClient.GetFromJsonAsync<NoteSectionModel[]>("generated/NoteSectionModels.json") ??
Array.Empty<NoteSectionModel>()).ToList();
isLoaded = true;
@ -61,6 +64,19 @@ public class NoteService : INoteService {
NotifyDataChanged();
}
public void Update()
{
NotifyDataChanged();
}
private event Action OnChange = default!;
private void NotifyDataChanged()
{
OnChange?.Invoke();
}
private NoteContentModel? ContentById(int id)
{
foreach (var data in NoteContentModels!)
@ -79,18 +95,10 @@ public class NoteService : INoteService {
}
foreach (var content in NoteContentModels)
{
if (content.NoteSectionModelId != null)
{
foreach (var section in NoteSectionModels)
{
if (section.Id == content.NoteSectionModelId)
{
section.NoteContentModels.Add(content);
}
}
}
}
ByPageOrder();
}
@ -99,7 +107,7 @@ public class NoteService : INoteService {
{
NoteContentModelsByPageOrder = new List<NoteContentModel>();
int order = 1;
var order = 1;
foreach (var note in NoteContentModels)
{
if (note.Parent != null) continue;
@ -121,10 +129,7 @@ public class NoteService : INoteService {
GetAllChildren(note);
}
NoteContentModelsByPageOrder = NoteContentModelsByPageOrder.OrderBy(noteContent => noteContent.PageOrder).ToList();
}
public void Update() {
NotifyDataChanged();
NoteContentModelsByPageOrder =
NoteContentModelsByPageOrder.OrderBy(noteContent => noteContent.PageOrder).ToList();
}
}

60
Services/IServices.cs

@ -1,20 +1,18 @@

using Model.BuildOrders;
using Model.Doc;
using Model.BuildOrders;
using Model.Economy;
using Model.Entity;
using Model.Feedback;
using Model.Git;
using Model.MemoryTester;
using Model.Notes;
using Model.Website;
using Model.Website.Enums;
using Model.Git;
using Model.Feedback;
using Model.Work.Tasks;
using Services.Immortal;
namespace Services;
public interface IToastService
{
public void Subscribe(Action action);
@ -27,7 +25,6 @@ public interface IToastService
void ClearAllToasts();
}
public interface IEntityDialogService
{
public void Subscribe(Action action);
@ -44,7 +41,8 @@ public interface IEntityDialogService
public bool HasHistory();
}
public interface IWebsiteService {
public interface IWebsiteService
{
public List<WebPageModel> WebPageModels { get; set; }
public List<WebSectionModel> WebSectionModels { get; set; }
@ -58,8 +56,8 @@ public interface IWebsiteService {
public bool IsLoaded();
}
public interface IAgileService {
public interface IAgileService
{
#if NO_SQL
public List<AgileSprintModel>? AgileSprintModels { get; set; }
public List<AgileTaskModel>? AgileTaskModels { get; set; }
@ -76,7 +74,8 @@ public interface IAgileService {
public bool IsLoaded();
}
public interface INoteService {
public interface INoteService
{
public List<NoteContentModel> NoteContentModels { get; set; }
public List<NoteConnectionModel> NoteConnectionModels { get; set; }
public List<NoteSectionModel> NoteSectionModels { get; set; }
@ -101,9 +100,8 @@ public interface IDocumentationService
public bool IsLoaded();
}
public interface IGitService {
public interface IGitService
{
#if NO_SQL
public List<GitChangeModel> GitChangeModels { get; set; }
public List<GitPatchModel> GitPatchModels { get; set; }
@ -124,7 +122,8 @@ public interface IGitService {
public bool IsLoaded();
}
public interface INavigationService {
public interface INavigationService
{
public void Subscribe(Action action);
public void Unsubscribe(Action action);
@ -143,7 +142,8 @@ public interface INavigationService {
public Type GetRenderType();
}
public interface IBuildComparisonService {
public interface IBuildComparisonService
{
public void SetBuilds(BuildComparisonModel buildComparisonModel);
public BuildComparisonModel Get();
public string BuildOrderAsYaml();
@ -153,14 +153,16 @@ public interface IBuildComparisonService {
public void Unsubscribe(Action action);
}
public interface ITimingService {
public interface ITimingService
{
public int GetTiming();
public void SetTiming(int timing);
public void Subscribe(Action? action);
public void Unsubscribe(Action? action);
}
public interface IEconomyService {
public interface IEconomyService
{
public List<EconomyModel> GetOverTime();
public EconomyModel GetEconomy(int atInterval);
public void Calculate(IBuildOrderService buildOrder, ITimingService timing, int fromInterval);
@ -168,7 +170,8 @@ public interface IEconomyService {
public void Unsubscribe(Action action);
}
public interface IEntityFilterService {
public interface IEntityFilterService
{
public delegate void EntityFilterAction(EntityFilterEvent entityFilterEvent);
public string GetFactionType();
@ -191,13 +194,11 @@ public interface IEntityFilterService {
public void Unsubscribe(EntityFilterAction action);
}
public interface IEntityService {
public interface IEntityService
{
public List<EntityModel> GetEntities();
}
public interface IEntityDisplayService
{
public List<string> DefaultChoices();
@ -208,7 +209,8 @@ public interface IEntityDisplayService
public void Unsubscribe(Action action);
}
public interface IImmortalSelectionService {
public interface IImmortalSelectionService
{
public string GetFactionType();
public string GetImmortalType();
public bool SelectFactionType(string factionType);
@ -217,7 +219,8 @@ public interface IImmortalSelectionService {
public void Unsubscribe(Action action);
}
public interface IKeyService {
public interface IKeyService
{
public List<string> GetAllPressedKeys();
public string? GetHotkey();
public string GetHotkeyGroup();
@ -228,7 +231,8 @@ public interface IKeyService {
public void Unsubscribe(Action? action);
}
public interface IMemoryTesterService {
public interface IMemoryTesterService
{
public delegate void MemoryAction(MemoryTesterEvent memoryEvent);
public List<MemoryEntityModel> GetEntities();
@ -244,8 +248,9 @@ public interface IMemoryTesterService {
public void Unsubscribe(MemoryAction memoryAction);
}
public interface IBuildOrderService {
public interface IBuildOrderService
{
public int BuildingInputDelay { get; set; }
public Dictionary<int, List<EntityModel>> StartedOrders { get; }
public Dictionary<int, List<EntityModel>> CompletedOrders { get; }
public Dictionary<string, int> UniqueCompletedTimes { get; }
@ -272,6 +277,7 @@ public interface IBuildOrderService {
public List<EntityModel> GetHarvestersCompletedBefore(int interval);
public void RemoveLast();
public void Reset();
public int GetLastRequestInterval();
public string BuildOrderAsYaml();

53
Services/Immortal/BuildComparisionService.cs

@ -3,48 +3,51 @@ using System.Text.Json;
using System.Text.Json.Serialization;
using Model.BuildOrders;
using Model.Entity;
using Model.Entity.Data;
using YamlDotNet.Serialization;
namespace Services.Immortal;
public class BuildComparisionService : IBuildComparisonService {
private event Action OnChange = default!;
public class BuildComparisionService : IBuildComparisonService
{
private BuildComparisonModel buildComparison = new();
public void Subscribe(Action action) {
public void Subscribe(Action action)
{
OnChange += action;
}
public void Unsubscribe(Action action) {
public void Unsubscribe(Action action)
{
OnChange -= action;
}
private void NotifyDataChanged() {
OnChange?.Invoke();
}
public void SetBuilds(BuildComparisonModel buildComparisonModel) {
public void SetBuilds(BuildComparisonModel buildComparisonModel)
{
buildComparison = buildComparisonModel;
NotifyDataChanged();
}
public BuildComparisonModel Get() {
public BuildComparisonModel Get()
{
return buildComparison;
}
public string AsJson() {
var options = new JsonSerializerOptions {
public string AsJson()
{
var options = new JsonSerializerOptions
{
WriteIndented = true
};
options.Converters.Add(new JsonStringEnumConverter());
return JsonSerializer.Serialize(buildComparison, options);
}
public bool LoadJson(string data) {
try {
var options = new JsonSerializerOptions {
public bool LoadJson(string data)
{
try
{
var options = new JsonSerializerOptions
{
WriteIndented = true
};
options.Converters.Add(new JsonStringEnumConverter());
@ -56,12 +59,14 @@ public class BuildComparisionService : IBuildComparisonService {
NotifyDataChanged();
return true;
}
catch {
catch
{
return false;
}
}
public string BuildOrderAsYaml() {
public string BuildOrderAsYaml()
{
var stringBuilder = new StringBuilder();
var serializer = new Serializer();
stringBuilder.AppendLine(serializer.Serialize(buildComparison));
@ -69,8 +74,16 @@ public class BuildComparisionService : IBuildComparisonService {
return buildOrderText;
}
private event Action OnChange = default!;
private void NotifyDataChanged()
{
OnChange?.Invoke();
}
public void HydratedLoadedJson() {
public void HydratedLoadedJson()
{
foreach (var build in buildComparison.Builds)
foreach (var orders in build.StartedOrders.Values)
foreach (var order in orders)

81
Services/Immortal/BuildOrderService.cs

@ -12,11 +12,14 @@ namespace Services.Immortal;
public class BuildOrderService : IBuildOrderService
{
private readonly BuildOrderModel buildOrder = new();
private readonly int HumanMicro = 2;
private int lastInterval;
private BuildOrderModel buildOrder = new();
public int BuildingInputDelay { get; set; } = 2;
private int lastInterval = 0;
private event Action OnChange = null!;
public BuildOrderService()
{
Reset();
}
public Dictionary<int, List<EntityModel>> StartedOrders => buildOrder.StartedOrders;
public Dictionary<int, List<EntityModel>> CompletedOrders => buildOrder.CompletedOrders;
@ -92,7 +95,6 @@ public class BuildOrderService : IBuildOrderService
var metTime = 0;
foreach (var requiredEntity in requirements)
{
if (buildOrder.UniqueCompletedTimes.TryGetValue(requiredEntity.Id, out var completedTime))
{
if (completedTime > metTime) metTime = completedTime;
@ -101,7 +103,6 @@ public class BuildOrderService : IBuildOrderService
{
return null;
}
}
return metTime;
}
@ -135,7 +136,6 @@ public class BuildOrderService : IBuildOrderService
public void RemoveLast()
{
if (buildOrder.StartedOrders.Keys.Count > 1)
{
var lastStarted = buildOrder.StartedOrders.Keys.Last();
@ -166,9 +166,8 @@ public class BuildOrderService : IBuildOrderService
buildOrder.UniqueCompletedCount[entityRemoved!.DataType]--;
if (buildOrder.UniqueCompletedCount[entityRemoved!.DataType] == 0) {
if (buildOrder.UniqueCompletedCount[entityRemoved!.DataType] == 0)
UniqueCompletedTimes.Remove(entityRemoved.DataType);
}
if (entityRemoved.Info().Descriptive == DescriptiveType.Worker)
{
@ -252,6 +251,65 @@ public class BuildOrderService : IBuildOrderService
return buildOrder.Color;
}
public void Reset()
{
lastInterval = 0;
buildOrder = new BuildOrderModel
{
StartedOrders = new Dictionary<int, List<EntityModel>>
{
{
0,
new List<EntityModel>
{
EntityModel.Get(DataType.STARTING_Bastion),
EntityModel.Get(DataType.STARTING_TownHall_Aru)
}
}
},
CompletedOrders = new Dictionary<int, List<EntityModel>>
{
{
0,
new List<EntityModel>
{
EntityModel.Get(DataType.STARTING_Bastion),
EntityModel.Get(DataType.STARTING_TownHall_Aru)
}
}
},
UniqueCompletedTimes = new Dictionary<string, int>
{
{
DataType.STARTING_Bastion, 0
},
{
DataType.STARTING_TownHall_Aru, 0
}
},
UniqueCompletedCount = new Dictionary<string, int>
{
{
DataType.STARTING_Bastion, 1
},
{
DataType.STARTING_TownHall_Aru, 1
}
},
SupplyCountTimes = new Dictionary<int, int>
{
{
0, 0
}
}
};
NotifyDataChanged();
}
private event Action OnChange = null!;
private bool HandleEconomy(EntityModel entity, IEconomyService withEconomy, IToastService withToasts,
ref int atInterval)
{
@ -267,10 +325,7 @@ public class BuildOrderService : IBuildOrderService
{
atInterval = interval;
if (entity.EntityType != EntityType.Army)
{
atInterval += HumanMicro;
}
if (entity.EntityType != EntityType.Army) atInterval += BuildingInputDelay;
return true;

47
Services/Immortal/EconomyService.cs

@ -1,43 +1,47 @@
using Model.Economy;
using Model.Entity;
using Model.Feedback;
using Model.Types;
using Services.Website;
namespace Services.Immortal;
public class EconomyService : IEconomyService {
public class EconomyService : IEconomyService
{
private List<EconomyModel> _economyOverTime = null!;
private event Action OnChange = null!;
public List<EconomyModel> GetOverTime() {
public List<EconomyModel> GetOverTime()
{
return _economyOverTime;
}
public void Subscribe(Action action) {
public void Subscribe(Action action)
{
OnChange += action;
}
public void Unsubscribe(Action action) {
public void Unsubscribe(Action action)
{
OnChange -= action;
}
public void Calculate(IBuildOrderService buildOrder, ITimingService timing, int fromInterval) {
public void Calculate(IBuildOrderService buildOrder, ITimingService timing, int fromInterval)
{
//TODO Break all this up
if (_economyOverTime == null) {
if (_economyOverTime == null)
{
_economyOverTime = new List<EconomyModel>();
for (var interval = 0; interval < timing.GetTiming(); interval++)
_economyOverTime.Add(new EconomyModel { Interval = interval });
}
if (_economyOverTime.Count > timing.GetTiming())
_economyOverTime.RemoveRange(timing.GetTiming(), _economyOverTime.Count - timing.GetTiming());
while (_economyOverTime.Count < timing.GetTiming()) _economyOverTime.Add(new EconomyModel { Interval = _economyOverTime.Count - 1 });
while (_economyOverTime.Count < timing.GetTiming())
_economyOverTime.Add(new EconomyModel { Interval = _economyOverTime.Count - 1 });
for (var interval = fromInterval; interval < timing.GetTiming(); interval++)
{
@ -119,8 +123,6 @@ public class EconomyService : IEconomyService {
// Remove Funds from Build Order
if (buildOrder.StartedOrders.TryGetValue(interval, out var ordersAtTime))
{
foreach (var order in ordersAtTime)
{
var foundEntity = EntityModel.GetDictionary()[order.DataType];
@ -138,11 +140,9 @@ public class EconomyService : IEconomyService {
if (production.ConsumesWorker) economyAtSecond.WorkerCount -= 1;
}
}
}
// Handle new entities
if (buildOrder.CompletedOrders.TryGetValue(interval, out var completedAtInterval))
{
foreach (var newEntity in completedAtInterval)
{
var harvest = newEntity;
@ -152,17 +152,26 @@ public class EconomyService : IEconomyService {
if (production != null && production.RequiresWorker) economyAtSecond.BusyWorkerCount -= 1;
}
}
}
NotifyDataChanged();
}
public EconomyModel GetEconomy(int atInterval) {
public EconomyModel GetEconomy(int atInterval)
{
if (atInterval >= _economyOverTime.Count)
{
return _economyOverTime.Last();
}
return _economyOverTime[atInterval];
}
private void NotifyDataChanged() {
private event Action OnChange = null!;
private void NotifyDataChanged()
{
OnChange?.Invoke();
}
}

26
Services/Immortal/EntityDisplayService.cs

@ -1,28 +1,24 @@
using Model.Types;
namespace Services.Immortal;
namespace Services.Immortal;
public class EntityDisplayService : IEntityDisplayService {
public class EntityDisplayService : IEntityDisplayService
{
private string displayType = "Detailed";
private event Action OnChange = null!;
public List<string> DefaultChoices()
{
return new List<string>() { "Detailed", "Plain" };
return new List<string> { "Detailed", "Plain" };
}
public void Subscribe(Action action) {
public void Subscribe(Action action)
{
OnChange += action;
}
public void Unsubscribe(Action action) {
public void Unsubscribe(Action action)
{
OnChange -= action;
}
private void NotifyDataChanged() {
OnChange?.Invoke();
}
public string GetDisplayType()
{
return displayType;
@ -34,4 +30,10 @@ public class EntityDisplayService : IEntityDisplayService {
NotifyDataChanged();
}
private event Action OnChange = null!;
private void NotifyDataChanged()
{
OnChange?.Invoke();
}
}

87
Services/Immortal/EntityFilterService.cs

@ -4,14 +4,16 @@ using static Services.IEntityFilterService;
namespace Services.Immortal;
public enum EntityFilterEvent {
public enum EntityFilterEvent
{
OnRefreshFaction,
OnRefreshImmortal,
OnRefreshEntity,
OnRefreshSearch
}
public class EntityFilterService : IEntityFilterService {
public class EntityFilterService : IEntityFilterService
{
private readonly List<string> _entityChoices = new();
private readonly List<string> _factionChoices = new() { FactionType.Any, FactionType.QRath, FactionType.Aru };
@ -21,36 +23,42 @@ public class EntityFilterService : IEntityFilterService {
private string _selectedFaction = FactionType.Any;
private string _selectedImmortal = ImmortalType.Any;
private event EntityFilterAction OnChange = null!;
public EntityFilterService() {
public EntityFilterService()
{
RefreshImmortalChoices();
RefreshEntityChoices();
}
public void Subscribe(EntityFilterAction action) {
public void Subscribe(EntityFilterAction action)
{
OnChange += action;
}
public void Unsubscribe(EntityFilterAction action) {
public void Unsubscribe(EntityFilterAction action)
{
OnChange -= action;
}
public string GetEntityType() {
public string GetEntityType()
{
return _entityType;
}
public string GetFactionType() {
public string GetFactionType()
{
return _selectedFaction;
}
public string GetImmortalType() {
public string GetImmortalType()
{
return _selectedImmortal;
}
public bool SelectFactionType(string factionType) {
if (_selectedFaction == factionType) {
public bool SelectFactionType(string factionType)
{
if (_selectedFaction == factionType)
{
_selectedFaction = FactionType.None;
_selectedImmortal = ImmortalType.None;
@ -72,8 +80,10 @@ public class EntityFilterService : IEntityFilterService {
return true;
}
public bool SelectImmortalType(string immortalType) {
if (_selectedImmortal == immortalType) {
public bool SelectImmortalType(string immortalType)
{
if (_selectedImmortal == immortalType)
{
_selectedImmortal = ImmortalType.None;
NotifyDataChanged(EntityFilterEvent.OnRefreshImmortal);
return true;
@ -84,14 +94,16 @@ public class EntityFilterService : IEntityFilterService {
return true;
}
public bool SelectEntityType(string entityType) {
public bool SelectEntityType(string entityType)
{
if (_entityType == entityType) return false;
_entityType = entityType;
NotifyDataChanged(EntityFilterEvent.OnRefreshEntity);
return true;
}
public bool EnterSearchText(string searchText) {
public bool EnterSearchText(string searchText)
{
if (_searchText.Equals(searchText))
return false;
_searchText = searchText;
@ -99,23 +111,30 @@ public class EntityFilterService : IEntityFilterService {
return true;
}
public List<string> GetFactionChoices() {
public List<string> GetFactionChoices()
{
return _factionChoices;
}
public List<string> GetImmortalChoices() {
public List<string> GetImmortalChoices()
{
return _immortalChoices;
}
public List<string> GetEntityChoices() {
public List<string> GetEntityChoices()
{
return _entityChoices;
}
public string GetSearchText() {
public string GetSearchText()
{
return _searchText;
}
private void RefreshImmortalChoices() {
private event EntityFilterAction OnChange = null!;
private void RefreshImmortalChoices()
{
_immortalChoices.Clear();
//TODO Consider getting these values from the database
@ -129,21 +148,26 @@ public class EntityFilterService : IEntityFilterService {
_immortalChoices.Add(ImmortalType.Xol);
}*/
if (_selectedFaction == FactionType.QRath || _selectedFaction == FactionType.Any) {
if (_selectedFaction == FactionType.QRath || _selectedFaction == FactionType.Any)
{
_immortalChoices.Add(DataType.IMMORTAL_Orzum);
_immortalChoices.Add(DataType.IMMORTAL_Ajari);
}
if (_selectedFaction == FactionType.Aru || _selectedFaction == FactionType.Any) {
if (_selectedFaction == FactionType.Aru || _selectedFaction == FactionType.Any)
{
_immortalChoices.Add(DataType.IMMORTAL_Mala);
_immortalChoices.Add(DataType.IMMORTAL_Xol);
}
}
private void RefreshEntityChoices() {
private void RefreshEntityChoices()
{
_entityChoices.Clear();
if (_selectedFaction == FactionType.QRath || _selectedFaction == FactionType.Aru || _selectedFaction == FactionType.Any) {
if (_selectedFaction == FactionType.QRath || _selectedFaction == FactionType.Aru ||
_selectedFaction == FactionType.Any)
{
_entityChoices.Add(EntityType.Army);
_entityChoices.Add(EntityType.Immortal);
_entityChoices.Add(EntityType.Passive);
@ -154,21 +178,22 @@ public class EntityFilterService : IEntityFilterService {
_entityChoices.Add(EntityType.Worker);
}
if (_selectedFaction == FactionType.Any) {
_entityChoices.Add(EntityType.Any);
}
if (_selectedFaction == FactionType.Any) _entityChoices.Add(EntityType.Any);
}
private void NotifyDataChanged(EntityFilterEvent entityFilterEvent) {
private void NotifyDataChanged(EntityFilterEvent entityFilterEvent)
{
OnChange?.Invoke(entityFilterEvent);
}
public void Subscribe(Action action) {
public void Subscribe(Action action)
{
throw new NotImplementedException();
}
public void Unsubscribe(Action action) {
public void Unsubscribe(Action action)
{
throw new NotImplementedException();
}
}

6
Services/Immortal/EntityService.cs

@ -2,8 +2,10 @@
namespace Services.Immortal;
public class EntityService : IEntityService {
public List<EntityModel> GetEntities() {
public class EntityService : IEntityService
{
public List<EntityModel> GetEntities()
{
throw new NotImplementedException();
}
}

25
Services/Immortal/ImmortalSelectionService.cs

@ -3,27 +3,33 @@ using Model.Types;
namespace Services.Immortal;
public class ImmortalSelectionService : IImmortalSelectionService {
public class ImmortalSelectionService : IImmortalSelectionService
{
private string _selectedFaction = FactionType.QRath;
private string _selectedImmortal = DataType.IMMORTAL_Orzum;
public void Subscribe(Action action) {
public void Subscribe(Action action)
{
OnChange += action;
}
public void Unsubscribe(Action action) {
public void Unsubscribe(Action action)
{
OnChange -= action;
}
public string GetFactionType() {
public string GetFactionType()
{
return _selectedFaction;
}
public string GetImmortalType() {
public string GetImmortalType()
{
return _selectedImmortal;
}
public bool SelectFactionType(string factionType) {
public bool SelectFactionType(string factionType)
{
if (_selectedFaction == factionType) return false;
_selectedFaction = factionType;
@ -35,7 +41,8 @@ public class ImmortalSelectionService : IImmortalSelectionService {
return true;
}
public bool SelectImmortalType(string immortalType) {
public bool SelectImmortalType(string immortalType)
{
if (_selectedImmortal == immortalType) return false;
_selectedImmortal = immortalType;
NotifyDataChanged();
@ -44,8 +51,8 @@ public class ImmortalSelectionService : IImmortalSelectionService {
private event Action OnChange = null!;
private void NotifyDataChanged() {
private void NotifyDataChanged()
{
OnChange?.Invoke();
}
}

53
Services/Immortal/KeyService.cs

@ -2,31 +2,35 @@
namespace Services.Immortal;
public class KeyService : IKeyService {
public class KeyService : IKeyService
{
private static readonly List<string> PressedKeys = new();
private string? _hotkey;
private string _hotkeyGroup = "C";
private bool _isHoldingSpace;
private event Action? OnChange;
public void Subscribe(Action? action) {
public void Subscribe(Action? action)
{
OnChange += action;
}
public void Unsubscribe(Action? action) {
public void Unsubscribe(Action? action)
{
OnChange -= action;
}
public bool AddPressedKey(string key) {
public bool AddPressedKey(string key)
{
_hotkey = null;
if (PressedKeys.Count > 0) return false;
var pressedKey = key.ToUpper();
if (pressedKey.Equals(" ") || pressedKey.Equals("SPACE")) {
if (!_isHoldingSpace) {
if (pressedKey.Equals(" ") || pressedKey.Equals("SPACE"))
{
if (!_isHoldingSpace)
{
_isHoldingSpace = true;
NotifyDataChanged();
}
@ -34,7 +38,8 @@ public class KeyService : IKeyService {
return false;
}
if (!PressedKeys.Contains(pressedKey)) {
if (!PressedKeys.Contains(pressedKey))
{
if (HotkeyModel.KeyGroups.Contains(pressedKey)) _hotkeyGroup = pressedKey;
if (HotkeyModel.HotKeys.Contains(pressedKey)) _hotkey = pressedKey;
@ -47,16 +52,20 @@ public class KeyService : IKeyService {
return false;
}
public List<string> GetAllPressedKeys() {
public List<string> GetAllPressedKeys()
{
return PressedKeys;
}
public bool RemovePressedKey(string key) {
public bool RemovePressedKey(string key)
{
_hotkey = null;
var pressedKey = key.ToUpper();
if (pressedKey.Equals(" ") || pressedKey.Equals("SPACE")) {
if (_isHoldingSpace || true) {
if (pressedKey.Equals(" ") || pressedKey.Equals("SPACE"))
{
if (_isHoldingSpace || true)
{
_isHoldingSpace = false;
NotifyDataChanged();
}
@ -64,7 +73,8 @@ public class KeyService : IKeyService {
return false;
}
if (PressedKeys.Contains(pressedKey)) {
if (PressedKeys.Contains(pressedKey))
{
PressedKeys.Remove(pressedKey);
NotifyDataChanged();
return true;
@ -73,20 +83,25 @@ public class KeyService : IKeyService {
return false;
}
public bool IsHoldingSpace() {
public bool IsHoldingSpace()
{
return _isHoldingSpace;
}
public string? GetHotkey() {
public string? GetHotkey()
{
return _hotkey;
}
public string GetHotkeyGroup() {
public string GetHotkeyGroup()
{
return _hotkeyGroup;
}
private void NotifyDataChanged() {
private event Action? OnChange;
private void NotifyDataChanged()
{
OnChange?.Invoke();
}
}

52
Services/Immortal/MemoryTesterService.cs

@ -5,12 +5,14 @@ using static Services.IMemoryTesterService;
namespace Services.Immortal;
public enum MemoryTesterEvent {
public enum MemoryTesterEvent
{
OnVerify,
OnRefresh
}
public class MemoryTesterService : IMemoryTesterService {
public class MemoryTesterService : IMemoryTesterService
{
private readonly List<MemoryEntityModel> memoryEntities = MemoryEntityModel.TestData;
private readonly List<MemoryQuestionModel> memoryQuestions = MemoryQuestionModel.TestData;
@ -18,18 +20,18 @@ public class MemoryTesterService : IMemoryTesterService {
private readonly Random random = new();
private event MemoryAction OnChange = null!;
public void Subscribe(MemoryAction action) {
public void Subscribe(MemoryAction action)
{
OnChange += action;
}
public void Unsubscribe(MemoryAction action) {
public void Unsubscribe(MemoryAction action)
{
OnChange -= action;
}
public void GenerateQuiz() {
public void GenerateQuiz()
{
memoryEntities.Clear();
memoryQuestions.Clear();
@ -42,17 +44,21 @@ public class MemoryTesterService : IMemoryTesterService {
var entityIndex = 0;
var questionIndex = 0;
foreach (var unit in units) {
memoryEntities.Add(new MemoryEntityModel {
foreach (var unit in units)
{
memoryEntities.Add(new MemoryEntityModel
{
Id = ++entityIndex,
Name = unit.Info().Name
});
var weaponIndex = 0;
foreach (var weapon in unit.Weapons()) {
foreach (var weapon in unit.Weapons())
{
weaponIndex++;
memoryQuestions.Add(new MemoryQuestionModel {
memoryQuestions.Add(new MemoryQuestionModel
{
Id = ++questionIndex,
MemoryEntityModelId = entityIndex,
Name = $"Range (Weapon {weaponIndex})",
@ -61,7 +67,8 @@ public class MemoryTesterService : IMemoryTesterService {
});
}
memoryQuestions.Add(new MemoryQuestionModel {
memoryQuestions.Add(new MemoryQuestionModel
{
Id = ++questionIndex,
MemoryEntityModelId = entityIndex,
Name = "Speed",
@ -73,27 +80,34 @@ public class MemoryTesterService : IMemoryTesterService {
NotifyDataChanged(MemoryTesterEvent.OnRefresh);
}
public List<MemoryEntityModel> GetEntities() {
public List<MemoryEntityModel> GetEntities()
{
return memoryEntities;
}
public List<MemoryQuestionModel> GetQuestions() {
public List<MemoryQuestionModel> GetQuestions()
{
return memoryQuestions;
}
public void Update(MemoryQuestionModel memoryQuestion) {
public void Update(MemoryQuestionModel memoryQuestion)
{
memoryQuestions[memoryQuestion.Id - 1].Guess = memoryQuestion.Guess;
}
public void Verify() {
public void Verify()
{
NotifyDataChanged(MemoryTesterEvent.OnVerify);
}
private event MemoryAction OnChange = null!;
//public delegate void MemoryAction(MemoryTesterActions memoryAction);
private void NotifyDataChanged(MemoryTesterEvent memoryAction) {
private void NotifyDataChanged(MemoryTesterEvent memoryAction)
{
OnChange?.Invoke(memoryAction);
}
}

26
Services/Immortal/TimingService.cs

@ -1,31 +1,37 @@
namespace Services.Immortal;
public class TimingService : ITimingService {
public class TimingService : ITimingService
{
private int _timing = 1500;
private event Action? OnChange;
public void Subscribe(Action? action) {
public void Subscribe(Action? action)
{
OnChange += action;
}
public void Unsubscribe(Action? action) {
public void Unsubscribe(Action? action)
{
OnChange -= action;
}
public int GetTiming() {
public int GetTiming()
{
return _timing;
}
public void SetTiming(int timing) {
if (_timing != timing) {
public void SetTiming(int timing)
{
if (_timing != timing)
{
_timing = timing;
NotifyDataChanged();
}
}
private void NotifyDataChanged() {
private event Action? OnChange;
private void NotifyDataChanged()
{
OnChange?.Invoke();
}
}

28
Services/Website/EntityDialogService.cs

@ -1,26 +1,19 @@
using System.ComponentModel.DataAnnotations;
using DataType = Model.Entity.Data.DataType;
namespace Services.Website;
namespace Services.Website;
//TODO Move to a database folder, with EntityService, EntityFilterService
public class EntityDialogService : IEntityDialogService
{
private string? entityId = null;
private List<string> history = new List<string>();
private string? entityId;
private event Action OnChange = null!;
private readonly List<string> history = new();
private void NotifyDataChanged() {
OnChange?.Invoke();
}
public void Subscribe(Action action) {
public void Subscribe(Action action)
{
OnChange += action;
}
public void Unsubscribe(Action action) {
public void Unsubscribe(Action action)
{
OnChange += action;
}
@ -74,6 +67,11 @@ public class EntityDialogService : IEntityDialogService
{
return entityId;
}
}
private event Action OnChange = null!;
private void NotifyDataChanged()
{
OnChange?.Invoke();
}
}

56
Services/Website/NavigationService.cs

@ -2,27 +2,26 @@
namespace Services.Website;
public class NavigationService : INavigationService {
private string navigationStateType = NavigationStateType.Default;
public class NavigationService : INavigationService
{
private int navigationStateId = -1;
private string navigationStateType = NavigationStateType.Default;
private NavSelectionType navSelectionType = NavSelectionType.None;
private Type renderType = null!;
private int webPageType;
private int webSectionType;
private event Action OnChange = null!;
public void Subscribe(Action action) {
public void Subscribe(Action action)
{
OnChange += action;
}
public void Unsubscribe(Action action) {
public void Unsubscribe(Action action)
{
OnChange += action;
}
@ -37,7 +36,8 @@ public class NavigationService : INavigationService {
return navigationStateId;
}
public void ChangeNavigationState(string newState) {
public void ChangeNavigationState(string newState)
{
if (newState.Equals(navigationStateType))
return;
@ -46,12 +46,15 @@ public class NavigationService : INavigationService {
NotifyDataChanged();
}
public string GetNavigationState() {
public string GetNavigationState()
{
return navigationStateType;
}
public void SelectPage(int pageType, Type page) {
if (renderType != page) {
public void SelectPage(int pageType, Type page)
{
if (renderType != page)
{
renderType = page;
webPageType = pageType;
navSelectionType = NavSelectionType.Page;
@ -60,7 +63,8 @@ public class NavigationService : INavigationService {
}
public void SelectSection(int section) {
public void SelectSection(int section)
{
if (section == webSectionType) return;
webSectionType = section;
navSelectionType = NavSelectionType.Section;
@ -68,15 +72,18 @@ public class NavigationService : INavigationService {
NotifyDataChanged();
}
public void Back() {
if (navSelectionType == NavSelectionType.Page) {
public void Back()
{
if (navSelectionType == NavSelectionType.Page)
{
navSelectionType = NavSelectionType.Section;
webPageType = 0;
NotifyDataChanged();
return;
}
if (navSelectionType == NavSelectionType.Section) {
if (navSelectionType == NavSelectionType.Section)
{
navSelectionType = NavSelectionType.None;
webSectionType = 0;
webPageType = 0;
@ -84,23 +91,30 @@ public class NavigationService : INavigationService {
}
}
public NavSelectionType GetNavSelectionType() {
public NavSelectionType GetNavSelectionType()
{
return navSelectionType;
}
public int GetWebPageId() {
public int GetWebPageId()
{
return webPageType;
}
public int GetWebSectionId() {
public int GetWebSectionId()
{
return webSectionType;
}
public Type GetRenderType() {
public Type GetRenderType()
{
return renderType;
}
private void NotifyDataChanged() {
private event Action OnChange = null!;
private void NotifyDataChanged()
{
OnChange?.Invoke();
}
}

34
Services/Website/ToastService.cs

@ -6,28 +6,13 @@ public class ToastService : IToastService
{
private readonly List<ToastModel> toasts = new();
private event Action OnChange = null!;
#if DEBUG
public ToastService()
public void Subscribe(Action action)
{
toasts.Add(new ToastModel(){Message = "Example message", SeverityType = SeverityType.Error, Title = "Example Error"});
toasts.Add(new ToastModel(){Message = "Example message", SeverityType = SeverityType.Information, Title = "Example Information"});
toasts.Add(new ToastModel(){Message = "Example message", SeverityType = SeverityType.Success, Title = "Example Success"});
toasts.Add(new ToastModel(){Message = "Example message", SeverityType = SeverityType.Warning, Title = "Example Warning"});
}
#endif
private void NotifyDataChanged() {
OnChange();
}
public void Subscribe(Action action) {
OnChange += action;
}
public void Unsubscribe(Action action) {
public void Unsubscribe(Action action)
{
OnChange += action;
}
@ -55,10 +40,7 @@ public class ToastService : IToastService
public void AgeToasts()
{
foreach (var toast in toasts)
{
toast.Age++;
}
foreach (var toast in toasts) toast.Age++;
NotifyDataChanged();
}
@ -69,6 +51,10 @@ public class ToastService : IToastService
NotifyDataChanged();
}
}
private event Action OnChange = null!;
private void NotifyDataChanged()
{
OnChange();
}
}

53
Services/Website/WebsiteService.cs

@ -1,45 +1,47 @@
using System.Net.Http.Json;
using Model.Website;
namespace Services.Development;
public class WebsiteService : IWebsiteService {
public class WebsiteService : IWebsiteService
{
private readonly HttpClient httpClient;
private bool isLoaded;
private event Action OnChange = default!;
public WebsiteService(HttpClient httpClient) {
public WebsiteService(HttpClient httpClient)
{
this.httpClient = httpClient;
}
public List<WebSectionModel> WebSectionModels { get; set; } = default!;
public List<WebPageModel> WebPageModels { get; set; } = default!;
public void Subscribe(Action action) {
public void Subscribe(Action action)
{
OnChange += action;
}
public void Unsubscribe(Action action) {
public void Unsubscribe(Action action)
{
OnChange -= action;
}
public bool IsLoaded() {
public bool IsLoaded()
{
return isLoaded;
}
public async Task Load() {
if (isLoaded) {return;}
public async Task Load()
{
if (isLoaded) return;
WebPageModels = ((await httpClient.GetFromJsonAsync<WebPageModel[]>("generated/WebPageModels.json"))!).ToList();
WebSectionModels =((await httpClient.GetFromJsonAsync<WebSectionModel[]>("generated/WebSectionModels.json"))!).ToList();
WebPageModels = (await httpClient.GetFromJsonAsync<WebPageModel[]>("generated/WebPageModels.json"))!.ToList();
WebSectionModels = (await httpClient.GetFromJsonAsync<WebSectionModel[]>("generated/WebSectionModels.json"))!
.ToList();
isLoaded = true;
@ -48,27 +50,28 @@ public class WebsiteService : IWebsiteService {
NotifyDataChanged();
}
void SortSql()
{
foreach (var page in WebPageModels)
public void Update()
{
NotifyDataChanged();
}
private event Action OnChange = default!;
private void SortSql()
{
foreach (var page in WebPageModels)
if (page.WebSectionModelId != null)
{
WebSectionModel webSection =
var webSection =
WebSectionModels.Find(webSection => webSection.Id == page.WebSectionModelId);
webSection.WebPageModels.Add(page);
}
}
}
public void Update() {
NotifyDataChanged();
}
private void NotifyDataChanged() {
private void NotifyDataChanged()
{
OnChange?.Invoke();
}
}
Loading…
Cancel
Save