Converting Tests back to C# but still with Playwright

This commit is contained in:
2026-06-03 14:45:18 -04:00
parent 85834466f1
commit 46150d3a69
209 changed files with 1503 additions and 683 deletions
+261
View File
@@ -0,0 +1,261 @@
@layout PageLayout
@inherits BasePage
@page "/database"
@using Model
@implements IDisposable
@inject IEntityDisplayService EntityDisplayService
<LayoutLargeContentComponent>
<WebsiteTitleComponent>Database</WebsiteTitleComponent>
<PaperComponent>
<FormDisplayComponent Label="Patch">
<Display>
Game Patch: @Variables.GamePatch
</Display>
</FormDisplayComponent>
</PaperComponent>
<div style="margin-left: 8px">
<ButtonGroupComponent OnClick="choice => { EntityDisplayService.SetDisplayType(choice); }"
Choice="@EntityDisplayService.GetDisplayType()"
Choices="@EntityDisplayService.DefaultChoices()"></ButtonGroupComponent>
</div>
<PaperComponent>
<EntityFilterComponent></EntityFilterComponent>
@if (searches != null)
{
<div class="databaseItems">
@foreach (var entity in searches)
{
<CascadingValue Value="entity">
<CascadingValue Value="@EntityDisplayService.GetDisplayType()">
<EntityViewComponent></EntityViewComponent>
</CascadingValue>
</CascadingValue>
}
</div>
}
</PaperComponent>
<ContentDividerComponent></ContentDividerComponent>
<PaperComponent>
<InfoBodyComponent>
<InfoQuestionComponent>
What is this tool?
</InfoQuestionComponent>
<InfoAnswerComponent>
This is a reference database. Mostly so unit stats can be reviewed outside of the game, IMMORTAL: Gates
of Pyre.
</InfoAnswerComponent>
</InfoBodyComponent>
<InfoBodyComponent>
<InfoQuestionComponent>
Is this database complete?
</InfoQuestionComponent>
<InfoAnswerComponent>
No. A lot of content is missing, that needs to be manually transfered from screenshots of IMMORTAL:
Gates of Pyre. This will happen slowly over-time.
</InfoAnswerComponent>
</InfoBodyComponent>
<InfoBodyComponent>
<InfoQuestionComponent>
Is this database updated to the latest version?
</InfoQuestionComponent>
<InfoAnswerComponent>
Maybe. Check this <b>@Variables.GamePatch</b> version number, and compare it to the
number on discord, in the <b>#game-updates</b> channel. That should give a general sense of how out of
date the data is.
</InfoAnswerComponent>
</InfoBodyComponent>
<InfoBodyComponent>
<InfoQuestionComponent>
This database has some errors in it.
</InfoQuestionComponent>
<InfoAnswerComponent>
Yup. The content is being transferred by hand, so some mistakes are expected.
</InfoAnswerComponent>
</InfoBodyComponent>
</PaperComponent>
</LayoutLargeContentComponent>
<style>
.databaseItems {
height: 900px;
overflow-x: hidden;
overflow-y: auto;
background-color: var(--background);
gap: 4px;
border-top: 4px solid var(--accent);
border-bottom: 4px solid var(--accent);
padding: 4px;
}
.databaseInfoContainer {
display: flex;
gap: 24px;
}
</style>
@code {
[Inject] public IEntityFilterService EntityFilterService { get; set; } = default!;
readonly List<EntityModel> defaults = (from entity in EntityModel.GetList()
where !entity.IsSpeculative
select entity).ToList();
List<EntityModel> factions = default!;
List<EntityModel> immortals = default!;
List<EntityModel> entities = default!;
List<EntityModel> searches = default!;
string selectedFactionType = DataType.Any;
string selectedImmortalType = DataType.Any;
string selectedEntityType = EntityType.Army;
string searchText = "";
protected override void OnInitialized()
{
base.OnInitialized();
RefreshFactionSearch();
EntityFilterService.Subscribe(OnChange);
EntityDisplayService.Subscribe(StateHasChanged);
}
void IDisposable.Dispose()
{
EntityFilterService.Unsubscribe(OnChange);
EntityDisplayService.Unsubscribe(StateHasChanged);
}
void OnChange(EntityFilterEvent filterEntityEvent)
{
if (filterEntityEvent == EntityFilterEvent.OnRefreshFaction)
{
RefreshFactionSearch();
}
if (filterEntityEvent == EntityFilterEvent.OnRefreshImmortal)
{
RefreshImmortalSearch();
}
if (filterEntityEvent == EntityFilterEvent.OnRefreshEntity)
{
RefreshEntitySearch();
}
if (filterEntityEvent == EntityFilterEvent.OnRefreshSearch)
{
RefreshTextSearch();
}
}
void RefreshFactionSearch()
{
selectedFactionType = EntityFilterService.GetFactionType();
if (selectedFactionType == DataType.Any)
{
factions = defaults.ToList();
}
else
{
factions = (from entity in defaults
where entity.Faction() != null && entity.Faction().Faction == selectedFactionType
select entity).ToList();
}
RefreshImmortalSearch();
}
void OnImmortalChanged(ChangeEventArgs e)
{
selectedImmortalType = e.Value!.ToString()!;
RefreshImmortalSearch();
}
void RefreshImmortalSearch()
{
selectedImmortalType = EntityFilterService.GetImmortalType();
if (selectedImmortalType == DataType.Any)
{
immortals = factions.ToList();
}
else
{
immortals = (from entity in factions
where entity.VanguardAdded() == null || entity.VanguardAdded().ImmortalId == selectedImmortalType
select entity).ToList();
}
RefreshEntitySearch();
}
void OnEntityChanged(ChangeEventArgs e)
{
selectedEntityType = e.Value!.ToString()!;
RefreshEntitySearch();
}
void RefreshEntitySearch()
{
selectedEntityType = EntityFilterService.GetEntityType();
if (selectedEntityType == EntityType.Any)
{
entities = immortals.ToList();
}
else
{
entities = (from entity in immortals
where entity.EntityType == selectedEntityType
select entity).ToList();
}
RefreshTextSearch();
}
void OnSearchTextChanged(ChangeEventArgs e)
{
searchText = e.Value!.ToString()!;
RefreshTextSearch();
}
void RefreshTextSearch()
{
searchText = EntityFilterService.GetSearchText();
if (searchText.Trim() == "")
{
searches = entities.ToList();
}
else
{
searches = (from entity in entities
where entity.Info().Name.ToLower().Contains(searchText.ToLower())
select entity).ToList();
}
StateHasChanged();
}
}
+100
View File
@@ -0,0 +1,100 @@
@layout PageLayout
@inherits BasePage
@page "/database/{text}"
@inject IEntityDisplayService EntityDisplayService
@using Model
@implements IDisposable
<LayoutLargeContentComponent>
<PaperComponent>
<FormDisplayComponent Label="Patch">
<Display>
Game Patch: @Variables.GamePatch
</Display>
</FormDisplayComponent>
</PaperComponent>
<div style="margin-left: 8px">
<ButtonGroupComponent OnClick="choice => { EntityDisplayService.SetDisplayType(choice); }"
Choice="@EntityDisplayService.GetDisplayType()"
Choices="@EntityDisplayService.DefaultChoices()"></ButtonGroupComponent>
</div>
@if (Text!.Trim().ToLower().Equals("walter"))
{
<PaperComponent>
<CodeComponent>
Unhandled Exception: EXCEPTION_MEMORY_SIZE_VIOLATION
UNIT_WALTER too powerful to be displayed.
This SHOULD NEVER HAPPEN!
</CodeComponent>
</PaperComponent>
}
else if (_entity == null)
{
<PaperComponent>
<div>Invalid entity name entered: <span id="invalidSearch">@Text</span></div>
<div>No such entity. Did you mean <b>"<span id="validSearch">Throne</span>"</b>?</div>
</PaperComponent>
}
else
{
<PaperComponent>
<CascadingValue Value="_entity">
<CascadingValue Value="@EntityDisplayService.GetDisplayType()">
<EntityViewComponent></EntityViewComponent>
</CascadingValue>
</CascadingValue>
</PaperComponent>
}
</LayoutLargeContentComponent>
@code {
[Parameter] public string? Text { get; set; }
private EntityModel? _entity;
protected override void OnInitialized()
{
EntityDisplayService.Subscribe(StateHasChanged);
}
protected override void OnParametersSet()
{
base.OnParametersSet();
base.OnInitialized();
FocusEntity();
}
private void FocusEntity()
{
foreach (var e in EntityData.Get().Values)
{
if (e.Info().Name.ToLower().Equals(Text!.ToLower()))
{
_entity = e;
return;
}
}
}
void IDisposable.Dispose()
{
EntityDisplayService.Unsubscribe(StateHasChanged);
}
}
@@ -0,0 +1,56 @@
@if (Entity != null)
{
var isVanguard = Entity.VanguardAdded() != null ? " vanguard" : "";
<div id="@Entity.EntityType.ToLower()-@Entity.Info().Name.ToLower()" class="entitiesContainer @isVanguard">
<EntityHeaderComponent></EntityHeaderComponent>
<div class="entityPartsContainer">
<EntityVanguardAddedComponent></EntityVanguardAddedComponent>
<EntityInfoComponent></EntityInfoComponent>
<EntityVanguardsComponent></EntityVanguardsComponent>
<EntityProductionComponent></EntityProductionComponent>
<EntityStatsComponent></EntityStatsComponent>
<EntityMechanicsComponent></EntityMechanicsComponent>
<EntityPassivesComponent></EntityPassivesComponent>
<EntityPyreSpellsComponent></EntityPyreSpellsComponent>
<EntityUpgradesComponent></EntityUpgradesComponent>
<EntityWeaponsComponent></EntityWeaponsComponent>
<EntityAbilitiesComponent></EntityAbilitiesComponent>
</div>
</div>
}
<style>
.entitiesContainer {
margin-bottom: 12px;
width: 100%;
overflow-y: auto;
overflow-x: hidden;
padding: 30px;
display: flex;
flex-wrap: wrap;
flex-direction: column;
}
.entityPartsContainer {
display: flex;
flex-direction: column;
flex-wrap: wrap;
width: 100%;
}
@@media only screen and (max-width: 1025px) {
.entitiesContainer {
padding: 0px;
}
}
</style>
@code {
[CascadingParameter] public EntityModel? Entity { get; set; }
[CascadingParameter] public string? StyleType { get; set; }
}
@@ -0,0 +1,134 @@
@if (Entity!.IdAbilities().Count > 0)
{
@if (StyleType.Equals("Plain"))
{
@foreach (var idAbility in Entity.IdAbilities())
{
var spell = EntityModel.Get(idAbility.Id);
var info = spell.Info();
var production = spell.Production();
<div>
<div>
<b>Ability Name:</b> @spell.Info().Name
</div>
<div>
<b>- Description:</b> @((MarkupString)info.Description)
</div>
@if (!info.Notes.Trim().Equals(""))
{
<div>
<b>- Notes:</b> @((MarkupString)info.Notes)
</div>
}
<div>
@if (production != null)
{
if (production.Energy != 0)
{
<div>
<b>- Energy: </b> @production.Energy
</div>
}
@if (!production.DefensiveLayer.Equals(0))
{
<div>
<b>- Shields:</b> @production.DefensiveLayer
</div>
}
if (production.BuildTime != 0)
{
<div>
<b>- BuildTime: </b> @production.BuildTime
</div>
}
if (production.Cooldown != 0)
{
<div>
<b>- Cooldown: </b> @production.Cooldown
</div>
}
}
</div>
</div>
}
}
else
{
<EntityDisplayComponent Title="Abilities">
@foreach (var idAbility in Entity.IdAbilities())
{
var spell = EntityModel.Get(idAbility.Id);
var info = spell.Info();
var production = spell.Production();
<div>
<div>
<b>Name:</b>
<EntityLabelComponent EntityId="@spell.DataType"/>
</div>
<div>
<b>Description:</b> @((MarkupString)info.Description)
</div>
@if (!info.Notes.Trim().Equals(""))
{
<div>
<b>Notes:</b> @((MarkupString)info.Notes)
</div>
}
<div>
@if (production != null)
{
if (production.Energy != 0)
{
<div>
<b> Energy: </b> @production.Energy
</div>
}
@if (!production.DefensiveLayer.Equals(0))
{
<div>
<b>Shields:</b> @production.DefensiveLayer
</div>
}
if (production.BuildTime != 0)
{
<div>
<b> BuildTime: </b> @production.BuildTime
</div>
}
if (production.Cooldown != 0)
{
<div>
<b> Cooldown: </b> @production.Cooldown
</div>
}
}
</div>
</div>
}
</EntityDisplayComponent>
}
}
@code {
[CascadingParameter] public EntityModel? Entity { get; set; }
[CascadingParameter] public string StyleType { get; set; } = "Detailed";
}
@@ -0,0 +1,72 @@
@if (StyleType.Equals("Plain"))
{
<div>
<b id="entityName">@Entity?.Info().Name</b>
@if (Entity?.Info().Descriptive != DescriptiveType.None)
{
<span>, @Entity?.Info().Descriptive.Replace("_", " ")</span>
}
</div>
}
else
{
<div class="entityHeader">
<div id="entityName" class="entityHeaderText">
@Entity?.Info().Name
</div>
<div style="font-size:1.4rem;">
<b>@Entity?.EntityType.Replace("_", " ")</b>
@if (Entity?.Info().Descriptive != DescriptiveType.None)
{
<span>
<b>:</b> @Entity!.Info().Descriptive.Replace("_", " ")
</span>
}
</div>
<div>
@if (Entity.Info().FlavorText != "")
{
<div>
<i> @((MarkupString)Entity.Info().FlavorText)</i>
</div>
}
</div>
</div>
<style>
.entityHeader {
display: flex;
flex-direction: row;
gap: 4px;
justify-content: space-between;
margin-bottom: 22px;
width: 100%;
margin-left: -6px;
}
.entityHeaderText {
font-size: 2rem;
font-weight: 900;
}
@@media only screen and (max-width: 1025px) {
.entityHeader {
flex-direction: column;
justify-content: normal;
margin-left: 4px;
}
}
</style>
}
@code {
[CascadingParameter] public EntityModel? Entity { get; set; }
[CascadingParameter] public string StyleType { get; set; } = "Detailed";
}
@@ -0,0 +1,132 @@
@if (StyleType.Equals("Plain"))
{
@if (Entity!.Info().Description != "")
{
<div>
<b>Description:</b> @((MarkupString)Entity.Info().Description)
</div>
}
@if (Entity.Info().Notes != "")
{
<div>
<b>Notes:</b> @((MarkupString)Entity.Info().Notes)
</div>
}
@if (Entity.Info().FlavorText != "")
{
<div>
<i>@((MarkupString)Entity.Info().FlavorText)</i>
</div>
}
<div class="infoDisplayContainer">
<div>
@if (Entity.Faction() != null)
{
<div>
<b>Faction:</b> @EntityData.Get()[Entity.Faction().Faction].Info().Name
</div>
}
@if (Entity.Tier() != null)
{
<div>
<b>Tier:</b> @Entity.Tier().Tier
</div>
}
</div>
@if (Entity.Hotkey() != null)
{
<div>
<div>
<b>Hotkey Group:</b> @Entity.Hotkey().HotkeyGroup
</div>
<div>
<b>Hotkey:</b> @Entity.Hotkey().Hotkey
</div>
<div>
<b>Hold Space:</b> @(Entity.Hotkey().HoldSpace ? "Yes" : "No")
</div>
</div>
}
</div>
}
else
{
<EntityDisplayComponent Title="Info">
@if (Entity!.Info().Description != "")
{
<div>
<b>Description:</b> @((MarkupString)Entity.Info().Description)
</div>
}
@if (Entity.Info().Notes != "")
{
<div>
<b>Notes:</b> @((MarkupString)Entity.Info().Notes)
</div>
}
<div class="infoDisplayContainer">
<div>
@if (Entity.Faction() != null)
{
<div>
<b>Faction:</b> @EntityData.Get()[Entity.Faction().Faction].Info().Name
</div>
}
@if (Entity.Tier() != null)
{
<div>
<b>Tier:</b> @Entity.Tier().Tier
</div>
}
</div>
@if (Entity.Hotkey() != null)
{
<div>
<div>
<b>Hotkey Group:</b> @Entity.Hotkey().HotkeyGroup
</div>
<div>
<b>Hotkey:</b> @Entity.Hotkey().Hotkey
</div>
<div>
<b>Hold Space:</b> @(Entity.Hotkey().HoldSpace ? "Yes" : "No")
</div>
</div>
}
</div>
</EntityDisplayComponent>
<style>
.infoDisplayContainer {
display: flex;
gap: 32px;
}
@@media only screen and (max-width: 1025px) {
.infoDisplayContainer {
flex-direction: column;
gap: 4px;
}
}
</style>
}
@code {
[CascadingParameter] public EntityModel? Entity { get; set; }
[CascadingParameter] public string StyleType { get; set; } = "Detailed";
}
@@ -0,0 +1,30 @@
@if (Entity!.Mechanics().Count > 0)
{
<EntityDisplayComponent Title="Mechanics">
<div>
@foreach (var data in Entity.Mechanics())
{
<div>
<div>
<span>
<b>Name:</b> @data.Name
</span>
</div>
<div>
<b>Description:</b> @data.Description
</div>
<br/>
</div>
}
</div>
</EntityDisplayComponent>
}
@code {
[CascadingParameter] public EntityModel? Entity { get; set; }
[CascadingParameter] public string StyleType { get; set; } = "Detailed";
}
@@ -0,0 +1,139 @@
@if (Entity!.IdPassives().Count > 0)
{
@if (StyleType.Equals("Plain"))
{
@foreach (var idPassive in Entity.IdPassives())
{
var passive = EntityModel.Get(idPassive.Id);
var info = passive.Info();
var production = passive.Production();
var requirements = passive.Requirements();
<div>
<div>
<b>Passive Name:</b> @info.Name
</div>
<div>
<b>- Description:</b> @((MarkupString)info.Description)
</div>
@if (!info.Notes.Trim().Equals(""))
{
<div>
<b>- Notes:</b> @((MarkupString)info.Notes)
</div>
}
</div>
@if (production != null)
{
<div>
@if (production.Pyre != 0)
{
<div>
<b>- Pyre:</b> @production.Pyre
</div>
}
@if (production.Cooldown != 0)
{
<div>
<b>- Cooldown:</b> @production.Cooldown.ToString()s
</div>
}
</div>
}
@if (requirements.Count > 0)
{
@foreach (var requirement in requirements)
{
var requirementModel = EntityData.Get()[requirement.Id];
<div>
<span>
<b>- @requirement.Requirement.Replace("_", " "):</b> @requirementModel.Info().Name
</span>
</div>
}
}
}
}
else
{
<EntityDisplayComponent Title="Passives">
@foreach (var idPassive in Entity.IdPassives())
{
var passive = EntityModel.Get(idPassive.Id);
var info = passive.Info();
var production = passive.Production();
var requirements = passive.Requirements();
<div>
<div>
<b>Name:</b>
<EntityLabelComponent EntityId="@passive.DataType"/>
</div>
<div>
<b>Description:</b> @((MarkupString)info.Description)
</div>
@if (!info.Notes.Trim().Equals(""))
{
<div>
<b>Notes:</b> @((MarkupString)info.Notes)
</div>
}
@if (requirements.Count > 0)
{
@foreach (var requirement in requirements)
{
var requirementModel = EntityData.Get()[requirement.Id];
<div>
<span>
<b>@requirement.Requirement.Replace("_", " "):</b> @requirementModel.Info().Name
</span>
</div>
}
}
</div>
@if (production != null)
{
<div>
@if (production.Pyre != 0)
{
<div>
<b>Pyre:</b> @production.Pyre
</div>
}
@if (production.Cooldown != 0)
{
<div>
<b>Cooldown:</b> @production.Cooldown.ToString()s
</div>
}
</div>
}
}
</EntityDisplayComponent>
}
}
@code {
[CascadingParameter] public EntityModel? Entity { get; set; }
[CascadingParameter] public string StyleType { get; set; } = "Detailed";
}
@@ -0,0 +1,225 @@
@if (Production != null || Supply != null || Requirements.Count > 0)
{
@if (StyleType.Equals("Plain"))
{
@if (Requirements.Count() > 0)
{
<div>
@foreach (var requirement in Requirements)
{
var requirementModel = EntityData.Get()[requirement.Id];
<div>
<span>
<b>@requirement.Requirement.Replace("_", " "):</b> @requirementModel.Info().Name
</span>
</div>
}
</div>
}
@if (Production != null && (!Production.Alloy.Equals(0)
|| !Production.Ether.Equals(0)
|| !Production.BuildTime.Equals(0)
|| !Production.Cooldown.Equals(0)))
{
<div>
@if (!Production.Alloy.Equals(0))
{
<div>
<b>Alloy:</b> @Production.Alloy
</div>
}
@if (!Production.Ether.Equals(0))
{
<div>
<b>Ether:</b> @Production.Ether
</div>
}
@if (!Production.Pyre.Equals(0))
{
<div>
<b>Pyre:</b> @Production.Pyre
</div>
}
@if (!Production.Pyre.Equals(0))
{
<div>
<b>Shields:</b> @Production.DefensiveLayer
</div>
}
@if (!Production.BuildTime.Equals(0))
{
<div>
<b>Build Time:</b> @Production.BuildTime.ToString()s
</div>
}
@if (!Production.Energy.Equals(0))
{
<div>
<b>Energy:</b> @Production.Energy
</div>
}
@if (!Production.Cooldown.Equals(0))
{
<div>
<b>Cooldown:</b> @Production.Cooldown.ToString()s
</div>
}
</div>
}
@if (Supply != null)
{
<div>
@if (!Supply.Grants.Equals(0))
{
<div>
<b>Grants:</b> @Supply.Grants
</div>
}
@if (!Supply.Takes.Equals(0))
{
<div>
<b>Takes:</b> @Supply.Takes Supply
</div>
}
</div>
}
}
else
{
<EntityDisplayComponent Title="Production">
<div class="ProductionContainer">
@if (Requirements.Count() > 0)
{
<div>
@foreach (var requirement in Requirements)
{
<div>
<span>
<b>@requirement.Requirement.Replace("_", " "):</b> <EntityLabelComponent
EntityId="@requirement.Id"/>
</span>
</div>
}
</div>
}
@if (Production != null && (!Production.Alloy.Equals(0)
|| !Production.Ether.Equals(0)
|| !Production.BuildTime.Equals(0)
|| !Production.Cooldown.Equals(0)))
{
<div>
@if (!Production.Alloy.Equals(0))
{
<div>
<b>Alloy:</b> @Production.Alloy
</div>
}
@if (!Production.Ether.Equals(0))
{
<div>
<b>Ether:</b> @Production.Ether
</div>
}
@if (!Production.Pyre.Equals(0))
{
<div>
<b>Pyre:</b> @Production.Pyre
</div>
}
@if (!Production.DefensiveLayer.Equals(0))
{
<div>
<b>Shields:</b> @Production.DefensiveLayer
</div>
}
@if (!Production.BuildTime.Equals(0))
{
<div>
<b>Build Time:</b> @Production.BuildTime.ToString()s
</div>
}
@if (!Production.Energy.Equals(0))
{
<div>
<b>Energy:</b> @Production.Energy
</div>
}
@if (!Production.Cooldown.Equals(0))
{
<div>
<b>Cooldown:</b> @Production.Cooldown.ToString()s
</div>
}
</div>
}
@if (Supply != null)
{
<div>
@if (!Supply.Grants.Equals(0))
{
<div>
<b>Grants:</b> @Supply.Grants
</div>
}
@if (!Supply.Takes.Equals(0))
{
<div>
<b>Takes:</b> @Supply.Takes Supply
</div>
}
</div>
}
</div>
</EntityDisplayComponent>
<style>
.ProductionContainer {
display: flex;
gap: 32px;
}
@@media only screen and (max-width: 1025px) {
.ProductionContainer {
flex-direction: column;
gap: 4px;
}
}
</style>
}
}
@code {
[CascadingParameter] public EntityModel? Entity { get; set; }
[CascadingParameter] public string StyleType { get; set; } = "Detailed";
private EntityProductionModel Production => Entity!.Production();
private List<EntityRequirementModel> Requirements => Entity!.Requirements();
private EntitySupplyModel Supply => Entity!.Supply();
protected override void OnParametersSet()
{
StateHasChanged();
}
}
@@ -0,0 +1,98 @@
@if (Entity!.IdPyreSpells().Count > 0)
{
@if (StyleType.Equals("Plain"))
{
@foreach (var pyreSpell in Entity.IdPyreSpells())
{
var spell = EntityModel.Get(pyreSpell.Id);
var info = spell.Info();
var production = spell.Production();
<div>
<div>
<b>Spell Name:</b> @spell.Info().Name
</div>
<div>
<b>- Description:</b> @((MarkupString)info.Description)
</div>
<div>
@if (production != null)
{
if (production.Pyre != 0)
{
<b>- Pyre: </b>
@production.Pyre
}
if (production.BuildTime != 0)
{
<b>- BuildTime: </b>
@production.BuildTime
}
if (production.Cooldown != 0)
{
<b>- Cooldown: </b>
@production.Cooldown
}
}
</div>
</div>
}
}
else
{
<EntityDisplayComponent Title="Pyre Spells">
@foreach (var pyreSpell in Entity.IdPyreSpells())
{
var spell = EntityModel.Get(pyreSpell.Id);
var info = spell.Info();
var production = spell.Production();
<div>
<div>
<b>Name:</b>
<EntityLabelComponent EntityId="@spell.DataType"/>
</div>
<div>
<b>Description:</b> @((MarkupString)info.Description)
</div>
<div>
@if (production != null)
{
if (production.Pyre != 0)
{
<b> Pyre: </b>
@production.Pyre
}
if (production.BuildTime != 0)
{
<b> BuildTime: </b>
@production.BuildTime
}
if (production.Cooldown != 0)
{
<b> Cooldown: </b>
@production.Cooldown
}
}
</div>
</div>
}
</EntityDisplayComponent>
}
}
@code {
[CascadingParameter] public EntityModel? Entity { get; set; }
[CascadingParameter] public string StyleType { get; set; } = "Detailed";
}
@@ -0,0 +1,180 @@
@if (Vitality != null || Movement != null)
{
@if (StyleType.Equals("Plain"))
{
@if (Vitality != null)
{
<div>
@if (!Vitality.DefenseLayer.Equals(0))
{
<div>
<b>Shield:</b> @Vitality.DefenseLayer
</div>
}
@if (!Vitality.Health.Equals(0))
{
<div>
<b>Health:</b> <span id="entityHealth">@Vitality.Health</span>
</div>
}
@if (!Vitality.Energy.Equals(0))
{
<div>
<b>Energy:</b> @Vitality.Energy
</div>
}
@if (!Vitality.Lasts.Equals(0))
{
<div>
<b>Lasts:</b> @Vitality.Lasts.ToString()s
</div>
}
@if (Vitality.Armor != "")
{
<div>
<b>Armor:</b> @Vitality.Armor
</div>
}
@if (Vitality.IsEtheric)
{
<div>
<b> + Etheric</b>
</div>
}
@if (Vitality.IsStructure)
{
<div>
<b> + Structure</b>
</div>
}
</div>
}
@if (Movement != null)
{
<div>
@if (!Movement.Speed.Equals(0))
{
<div>
<b>Speed:</b> @Movement.Speed
</div>
}
else
{
<div>
<b>Speed:</b> Immobile
</div>
}
<div>
<b>Move Type:</b> @Movement.Movement
</div>
</div>
}
}
else
{
<EntityDisplayComponent Title="Stats">
<div class="statContainer">
@if (Vitality != null)
{
<div>
@if (!Vitality.DefenseLayer.Equals(0))
{
<div>
<b>Shield:</b> @Vitality.DefenseLayer
</div>
}
@if (!Vitality.Health.Equals(0))
{
<div>
<b>Health:</b> <span id="entityHealth">@Vitality.Health</span>
</div>
}
@if (!Vitality.Energy.Equals(0))
{
<div>
<b>Energy:</b> @Vitality.Energy
</div>
}
@if (!Vitality.Lasts.Equals(0))
{
<div>
<b>Lasts:</b> @Vitality.Lasts.ToString()s
</div>
}
@if (Vitality.Armor != "")
{
<div>
<b>Armor:</b> @Vitality.Armor
</div>
}
@if (Vitality.IsEtheric)
{
<div>
<b> + Etheric</b>
</div>
}
@if (Vitality.IsStructure)
{
<div>
<b> + Structure</b>
</div>
}
</div>
}
@if (Movement != null)
{
<div>
@if (!Movement.Speed.Equals(0))
{
<div>
<b>Speed:</b> @Movement.Speed
</div>
}
else
{
<div>
<b>Speed:</b> Immobile
</div>
}
<div>
<b>Move Type:</b> @Movement.Movement
</div>
</div>
}
</div>
</EntityDisplayComponent>
<style>
.statContainer {
display: flex;
gap: 32px;
}
@@media only screen and (max-width: 1025px) {
.statContainer {
flex-direction: column;
gap: 4px;
}
}
</style>
}
}
@code {
[CascadingParameter] public EntityModel? Entity { get; set; }
[CascadingParameter] public string StyleType { get; set; } = "Detailed";
private EntityVitalityModel Vitality => Entity!.Vitality();
private EntityMovementModel Movement => Entity!.Movement();
}
@@ -0,0 +1,63 @@
@if (Entity!.IdUpgrades().Count > 0)
{
@if (StyleType.Equals("Plain"))
{
@foreach (var upgradeId in Entity.IdUpgrades())
{
var entity = EntityModel.Get(upgradeId.Id);
<div>
<div>
<b>Upgrade Name:</b> @entity.Info().Name
</div>
<div>
<b>- Description:</b> @entity.Info().Description
</div>
</div>
}
}
else
{
<EntityDisplayComponent Title="Upgrades">
<div class="upgradesContainer">
@foreach (var upgradeId in Entity.IdUpgrades())
{
var entity = EntityModel.Get(upgradeId.Id);
<div>
<div>
<b>Name:</b>
<EntityLabelComponent EntityId="@entity.DataType"/>
</div>
<div>
<b>Description:</b> @entity.Info().Description
</div>
</div>
}
</div>
</EntityDisplayComponent>
<style>
.upgradesContainer {
display: flex;
gap: 32px;
}
@@media only screen and (max-width: 1025px) {
.upgradesContainer {
flex-direction: column;
gap: 4px;
}
}
</style>
}
}
@code {
[CascadingParameter] public EntityModel? Entity { get; set; }
[CascadingParameter] public string StyleType { get; set; } = "Detailed";
}
@@ -0,0 +1,49 @@
@if (Vanguard != null)
{
var immortalId = Vanguard.ImmortalId;
var immortal = EntityData.Get()[immortalId];
var replaced = EntityData.Get()[Vanguard.ReplaceId];
@if (StyleType.Equals("Plain"))
{
<div>
<b>Immortal:</b> @immortal.Info().Name
</div>
@if (!Vanguard.ReplaceId.Equals(""))
{
<div>
<b>Replaces:</b> @replaced.Info().Name
</div>
}
}
else
{
<EntityDisplayComponent Title="Vanguard">
<div>
<div>
<b>Immortal:</b>
<EntityLabelComponent EntityId="@immortal.DataType"/>
</div>
@if (!Vanguard.ReplaceId.Equals(""))
{
<div>
<b>Replaces:</b>
<EntityLabelComponent EntityId="@Vanguard.ReplaceId"></EntityLabelComponent>
</div>
}
</div>
</EntityDisplayComponent>
}
}
@code {
[CascadingParameter] public EntityModel? Entity { get; set; }
private EntityVanguardAddedModel? Vanguard => Entity?.VanguardAdded();
[CascadingParameter] public string StyleType { get; set; } = "Detailed";
}
@@ -0,0 +1,71 @@
@if (Entity!.IdVanguards().Count > 0)
{
@if (StyleType.Equals("Plain"))
{
@foreach (var data in Entity.IdVanguards())
{
var entity = EntityModel.Get(data.Id);
var requirements = entity.Requirements();
var vanguardAdded = entity.VanguardAdded();
var replaced = EntityData.Get()[vanguardAdded.ReplaceId];
var immortal = EntityData.Get()[vanguardAdded.ImmortalId];
var productionBuilding = (from building in requirements
where building.Requirement == RequirementType.Production_Building
select building).First().Id;
<div>
<div>
<b>Name:</b> @entity.Info().Name
</div>
<div>
<b>- Replaces:</b> @replaced.Info().Name
</div>
<div>
<b>- Built From:</b> @immortal.Info().Name
</div>
</div>
}
}
else
{
<EntityDisplayComponent Title="Vanguards">
@foreach (var data in Entity.IdVanguards())
{
var entity = EntityModel.Get(data.Id);
var requirements = entity.Requirements();
var vanguard = entity.VanguardAdded();
var productionBuilding = (from building in requirements
where building.Requirement == RequirementType.Production_Building
select building).First().Id;
<div>
<div>
<b>Name:</b>
<EntityLabelComponent EntityId="@entity.DataType"/>
</div>
<div>
<b>Replaces:</b>
<EntityLabelComponent EntityId="@vanguard.ReplaceId"/>
</div>
<div>
<b>Built From:</b>
<EntityLabelComponent EntityId="@productionBuilding"/>
</div>
</div>
}
</EntityDisplayComponent>
}
}
@code {
[CascadingParameter] public EntityModel? Entity { get; set; }
[CascadingParameter] public string StyleType { get; set; } = "Detailed";
}
@@ -0,0 +1,263 @@
@inject IStorageService StorageService
@using Services.Website
@implements IDisposable
@if (Entity!.Weapons().Count > 0)
{
@if (StyleType.Equals("Plain"))
{
var index = 0;
foreach (var data in Entity.Weapons())
{
index++;
<div>
<div>
<div class="damageContainer">
<div>
<b>Weapon @index</b>
</div>
<div>
<b>- Damage:</b> @data.Damage
</div>
@if (data.LightDamage != 0)
{
<div class="alternateDamage">
<i>- vs Light: @data.LightDamage</i>&nbsp;
</div>
}
@if (data.MediumDamage != 0)
{
<div class="alternateDamage">
<i>- vs Medium: @data.MediumDamage</i>&nbsp;
</div>
}
@if (data.HeavyDamage != 0)
{
<div class="alternateDamage">
<i>- vs Heavy: @data.HeavyDamage</i>&nbsp;
</div>
}
@if (data.EthericDamageBonus != 0)
{
<div class="alternateDamage">
<i>- vs Etheric +@data.EthericDamageBonus</i>&nbsp;
</div>
}
@if (data.StructureDamageBonus != 0)
{
<div class="alternateDamage">
<i>- vs Structure: +@data.StructureDamageBonus</i>&nbsp;
</div>
}
</div>
</div>
<div>
<b>- Range:</b> @data.Range
</div>
@if (data.SecondsBetweenAttacks > 0)
{
<div>
<b>- AttacksPerSecond:</b> @(1 / data.SecondsBetweenAttacks)
</div>
<div>
- (or <b>SecondsBetweenAttacks:</b> @data.SecondsBetweenAttacks)
</div>
}
else if (data.AttacksPerSecond > 0)
{
<div>
<b>- AttacksPerSecond:</b> @data.AttacksPerSecond
</div>
<div>
- (or <b>SecondsBetweenAttacks:</b> @(1 / data.AttacksPerSecond))
</div>
}
<div>
<b>- Targets:</b> @data.Targets
</div>
@if (data.AttacksPerSecond != 0)
{
<span>
<b>- DPS:</b> @(Math.Round(data.Damage * data.AttacksPerSecond))&nbsp;
</span>
@if (data.LightDamage != 0)
{
<span>
<i>- Light DPS: @(Math.Round(data.LightDamage * data.AttacksPerSecond))</i>&nbsp;
</span>
}
@if (data.MediumDamage != 0)
{
<span>
<i>- Medium DPS: @(Math.Round(data.MediumDamage * data.AttacksPerSecond))</i>&nbsp;
</span>
}
@if (data.HeavyDamage != 0)
{
<span>
<i>- Heavy DPS: @(Math.Round(data.HeavyDamage * data.AttacksPerSecond))</i>&nbsp;
</span>
}
}
</div>
}
}
else
{
<EntityDisplayComponent Title="Weapons">
<div class="weaponsContainer">
@foreach (var data in Entity.Weapons())
{
<div>
<div>
<div class="damageContainer">
<div>
<b>Damage:</b> @data.Damage
</div>
@if (data.LightDamage != 0)
{
<div class="alternateDamage">
<i>vs Light: @data.LightDamage</i>&nbsp;
</div>
}
@if (data.MediumDamage != 0)
{
<div class="alternateDamage">
<i>vs Medium: @data.MediumDamage</i>&nbsp;
</div>
}
@if (data.HeavyDamage != 0)
{
<div class="alternateDamage">
<i>vs Heavy: @data.HeavyDamage</i>&nbsp;
</div>
}
@if (data.EthericDamageBonus != 0)
{
<div class="alternateDamage">
<i>vs Etheric +@data.EthericDamageBonus</i>&nbsp;
</div>
}
@if (data.StructureDamageBonus != 0)
{
<div class="alternateDamage">
<i>vs Structure: +@data.StructureDamageBonus</i>&nbsp;
</div>
}
</div>
</div>
<div>
<b>Range:</b> @data.Range
</div>
@if (data.SecondsBetweenAttacks > 0)
{
<div>
<b>AttacksPerSecond:</b> @(1 / data.SecondsBetweenAttacks)
</div>
<div>
(or <b>SecondsBetweenAttacks:</b> @data.SecondsBetweenAttacks)
</div>
}
else if (data.AttacksPerSecond > 0)
{
<div>
<b>AttacksPerSecond:</b> @data.AttacksPerSecond
</div>
<div>
(or <b>SecondsBetweenAttacks:</b> @(1 / data.AttacksPerSecond))
</div>
}
<div>
<b>Targets:</b> @data.Targets
</div>
@if (data.AttacksPerSecond != 0)
{
<div>
<b>DPS:</b> @(Math.Round(data.Damage * data.AttacksPerSecond))&nbsp;
</div>
@if (data.LightDamage != 0)
{
<div>
<i>Light DPS: @(Math.Round(data.LightDamage * data.AttacksPerSecond))</i>&nbsp;
</div>
}
@if (data.MediumDamage != 0)
{
<div>
<i>Medium DPS: @(Math.Round(data.MediumDamage * data.AttacksPerSecond))</i>&nbsp;
</div>
}
@if (data.HeavyDamage != 0)
{
<div>
<i>Heavy DPS: @(Math.Round(data.HeavyDamage * data.AttacksPerSecond))</i>&nbsp;
</div>
}
}
</div>
}
</div>
</EntityDisplayComponent>
<style>
.weaponsContainer {
display: flex;
gap: 32px;
}
@@media only screen and (max-width: 1025px) {
.weaponsContainer {
flex-direction: column;
gap: 4px;
}
}
.alternateDamage {
margin-left: 18px;
}
.damageContainer {
margin-bottom: 6px;
}
</style>
}
}
@code {
[CascadingParameter] public EntityModel? Entity { get; set; }
[CascadingParameter] public string StyleType { get; set; } = "Detailed";
private bool _isDynamicFormatting;
protected override void OnInitialized()
{
base.OnInitialized();
StorageService.Subscribe(RefreshDefaults);
}
void RefreshDefaults()
{
_isDynamicFormatting = StorageService.GetValue<bool>(StorageKeys.IsDynamicFormatting);
}
void IDisposable.Dispose()
{
StorageService.Unsubscribe(RefreshDefaults);
}
}
@@ -0,0 +1,264 @@
<div class="desktopFilters">
<div class="desktopFiltersContainer">
<div class="filtersContainer">
<div class="filterContainer">
@foreach (var choice in EntityFilterService.GetFactionChoices())
{
var styleClass = "";
if (choice.Equals(EntityFilterService.GetFactionType()))
{
styleClass = "selected";
}
<button @onclick="@(e => OnChangeFaction(choice))"
class="choiceButton @styleClass">
@(choice == DataType.Any
? DataType.Any
: EntityData.Get()[choice].Info().Name)
</button>
}
</div>
@if (EntityFilterService.GetFactionType() != "Any" && EntityFilterService.GetFactionType() != "None")
{
<div class="filterContainer">
@foreach (var choice in EntityFilterService.GetImmortalChoices())
{
var name = EntityData.Get()[choice].Info().Name;
var styleClass = "";
if (choice.Equals(EntityFilterService.GetImmortalType()))
{
styleClass = "selected";
}
<button class="choiceButton @styleClass"
@onclick="@(e => OnChangeImmortal(choice))">@name</button>
}
</div>
}
</div>
<div class="filterContainer">
@foreach (var choice in EntityFilterService.GetEntityChoices())
{
var styleClass = "";
if (choice.Equals(EntityFilterService.GetEntityType()))
{
styleClass = "selected";
}
<button class="choiceButton @styleClass"
@onclick="@(e => OnChangeEntity(choice))">@choice.Replace("_", " ")</button>
}
</div>
<FormTextComponent Id="filterName" Label="Filter Name" Placeholder="Throne..."
OnChange="@(e => EntityFilterService.EnterSearchText(e.Value!.ToString()!))"/>
</div>
</div>
<div class="mobileFilters">
<FormLayoutComponent>
<FormSelectComponent OnChange="@OnFactionChanged">
<FormLabelComponent>Faction</FormLabelComponent>
<ChildContent>
<option value="@DataType.Any" selected>Any</option>
<option value="@DataType.FACTION_Aru">Aru</option>
<option value="@DataType.FACTION_QRath">Q'Rath</option>
</ChildContent>
</FormSelectComponent>
<FormSelectComponent OnChange="@OnImmortalChanged">
<FormLabelComponent>Immortal</FormLabelComponent>
<ChildContent>
<option value="@DataType.Any" selected>Any</option>
<option value="@DataType.IMMORTAL_Atzlan">Atzlan</option>
<option value="@DataType.IMMORTAL_Mala">Mala</option>
<option value="@DataType.IMMORTAL_Xol">Xol</option>
<option value="@DataType.IMMORTAL_Orzum">Orzum</option>
<option value="@DataType.IMMORTAL_Ajari">Ajari</option>
</ChildContent>
</FormSelectComponent>
<FormSelectComponent OnChange="@OnEntityChanged">
<FormLabelComponent>Entity</FormLabelComponent>
<ChildContent>
<option value="@EntityType.Any">Any</option>
<option value="@EntityType.Ability">Ability</option>
<option value="@EntityType.Army" selected>Army</option>
<option value="@EntityType.Building">Building</option>
<option value="@EntityType.Building_Upgrade">Building Upgrade</option>
<option value="@EntityType.Command">Command</option>
<option value="@EntityType.Faction">Faction</option>
<option value="@EntityType.Immortal">Immortal</option>
<option value="@EntityType.Pyre_Spell">Spell</option>
<option value="@EntityType.Passive">Passive</option>
<option value="@EntityType.Tech">Tech</option>
<option value="@EntityType.Worker">Worker</option>
</ChildContent>
</FormSelectComponent>
<FormTextComponent Id="filterName" Label="Filter Name" Placeholder="Throne..." OnChange="OnSearchTextChanged"/>
</FormLayoutComponent>
</div>
<style>
.desktopFilters {
display: flex;
gap: 12px;
flex-direction: column;
justify-content: flex-start;
justify-items: flex-start;
top: 50px;
padding: 12px;
width: 100%;
left: 0px;
}
.desktopFiltersContainer {
width: 75%;
min-width: 1000px;
margin: auto;
display: flex;
gap: 16px;
flex-direction: column;
justify-content: flex-start;
justify-items: flex-start;
}
.filtersContainer {
display: flex;
gap: 16px;
}
.filterContainer {
display: flex;
background-color: var(--background);
gap: 2px;
margin-right: auto;
border-radius: 8px;
}
.choiceButton {
background-color: var(--primary);
color: white;
padding: 12px;
border: 1px solid var(--primary);
}
.choiceButton:hover {
background-color: var(--primary-hover);
border-color: var(--primary-border-hover);
}
.selected {
background-color: var(--secondary);
color: white;
font-style: normal;
font-weight: bold;
}
.selected:hover {
background-color: var(--secondary-hover);
border-color: var(--secondary-border-hover);
}
.filterContainer .choiceButton:first-child {
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
}
.filterContainer .choiceButton:last-child {
border-top-right-radius: 8px;
border-bottom-right-radius: 8px;
}
@@media only screen and (max-width: 1025px) {
.desktopNavContainer {
display: none;
}
}
@@media only screen and (max-width: 480px) {
.filtersContainer {
flex-direction: column;
}
.filterContainer {
flex-direction: column;
}
}
.mobileFilters {
display: none;
}
@@media only screen and (max-width: 1024px) {
.mobileFilters {
display: block;
}
.desktopFilters {
display: none;
}
.desktopSpacer {
display: none;
}
}
</style>
@code {
[Inject] public IEntityFilterService EntityFilterService { get; set; } = default!;
protected override void OnInitialized()
{
base.OnInitialized();
}
void OnChangeFaction(string clickedFaction)
{
EntityFilterService.SelectFactionType(clickedFaction);
StateHasChanged();
}
void OnChangeImmortal(string clickedImmortal)
{
EntityFilterService.SelectImmortalType(clickedImmortal);
}
void OnChangeEntity(string clickedEntity)
{
EntityFilterService.SelectEntityType(clickedEntity);
}
void OnFactionChanged(ChangeEventArgs e)
{
EntityFilterService.SelectFactionType(e.Value!.ToString()!);
}
void OnImmortalChanged(ChangeEventArgs e)
{
EntityFilterService.SelectImmortalType(e.Value!.ToString()!);
}
void OnEntityChanged(ChangeEventArgs e)
{
EntityFilterService.SelectEntityType(e.Value!.ToString()!);
}
void OnSearchTextChanged(ChangeEventArgs e)
{
EntityFilterService.EnterSearchText(e.Value!.ToString()!);
}
}
@@ -0,0 +1,106 @@
.desktopFilters {
display: flex;
gap: 12px;
flex-direction: column;
justify-content: flex-start;
justify-items: flex-start;
top: 50px;
padding: 12px;
width: 100%;
left: 0px;
}
.desktopFiltersContainer {
width: 75%;
min-width: 1000px;
margin: auto;
display: flex;
gap: 16px;
flex-direction: column;
justify-content: flex-start;
justify-items: flex-start;
}
.filtersContainer {
display: flex;
gap: 16px;
}
.filterContainer {
display: flex;
background-color: var(--background);
gap: 2px;
margin-right: auto;
border-radius: 8px;
}
.choiceButton {
background-color: var(--primary);
color: white;
padding: 12px;
border: 1px solid var(--primary);
}
.choiceButton:hover {
background-color: var(--primary-hover);
border-color: var(--primary-border-hover);
}
.selected {
background-color: var(--secondary);
color: white;
font-style: normal;
font-weight: bold;
}
.selected:hover {
background-color: var(--secondary-hover);
border-color: var(--secondary-border-hover);
}
.filterContainer .choiceButton:first-child {
border-top-left-radius: 8px;
border-bottom-left-radius: 8px;
}
.filterContainer .choiceButton:last-child {
border-top-right-radius: 8px;
border-bottom-right-radius: 8px;
}
@media only screen and (max-width: 1025px) {
.desktopNavContainer {
display: none;
}
}
@media only screen and (max-width: 480px) {
.filtersContainer {
flex-direction: column;
}
.filterContainer {
flex-direction: column;
}
}
.mobileFilters {
display: none;
}
@media only screen and (max-width: 1024px) {
.mobileFilters {
display: block;
}
.desktopFilters {
display: none;
}
.desktopSpacer {
display: none;
}
}