Compare commits
16 Commits
1bc78306b8
..
main
| Author | SHA1 | Date | |
|---|---|---|---|
| 46150d3a69 | |||
| 85834466f1 | |||
| 7da6f554a8 | |||
| dc0395c7d3 | |||
| 026aebd5ad | |||
| 3974fcfb91 | |||
| 410e7e23b7 | |||
| 6655cdeee7 | |||
| 1f7a0819fc | |||
| 73f29cea08 | |||
| b63d8330f7 | |||
| ad7aabd9e0 | |||
| 7680fae30b | |||
| 9e9b25ae6f | |||
| cf3f6e647b | |||
| c0ea4a094e |
@@ -25,7 +25,7 @@ jobs:
|
||||
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_CALM_MUD_04916B210 }}
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
action: "upload"
|
||||
app_location: "IGP"
|
||||
app_location: "Web"
|
||||
api_location: ""
|
||||
output_location: "./wwwroot"
|
||||
|
||||
|
||||
@@ -25,7 +25,7 @@ jobs:
|
||||
azure_static_web_apps_api_token: ${{ secrets.AZURE_STATIC_WEB_APPS_API_TOKEN_NICE_COAST_0F8B08010 }}
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
action: "upload"
|
||||
app_location: "IGP"
|
||||
app_location: "Web"
|
||||
api_location: ""
|
||||
output_location: "./wwwroot"
|
||||
|
||||
|
||||
@@ -137,6 +137,7 @@ DocProject/Help/html
|
||||
|
||||
# Click-Once directory
|
||||
publish/
|
||||
publish_release/
|
||||
|
||||
# Publish Web Output
|
||||
*.[Pp]ublish.xml
|
||||
@@ -264,3 +265,6 @@ __pycache__/
|
||||
|
||||
**/.vs/
|
||||
.DS_Store
|
||||
|
||||
|
||||
publish_release/
|
||||
@@ -0,0 +1 @@
|
||||
3.0
|
||||
Vendored
+3
-3
@@ -7,7 +7,7 @@
|
||||
"type": "process",
|
||||
"args": [
|
||||
"build",
|
||||
"${workspaceFolder}/IGP/IGP.csproj",
|
||||
"${workspaceFolder}/Web/Web.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
@@ -19,7 +19,7 @@
|
||||
"type": "process",
|
||||
"args": [
|
||||
"publish",
|
||||
"${workspaceFolder}/IGP/IGP.csproj",
|
||||
"${workspaceFolder}/Web/Web.csproj",
|
||||
"/property:GenerateFullPaths=true",
|
||||
"/consoleloggerparameters:NoSummary"
|
||||
],
|
||||
@@ -33,7 +33,7 @@
|
||||
"watch",
|
||||
"run",
|
||||
"--project",
|
||||
"${workspaceFolder}/IGP/IGP.csproj"
|
||||
"${workspaceFolder}/Web/Web.csproj"
|
||||
],
|
||||
"problemMatcher": "$msCompile"
|
||||
}
|
||||
|
||||
@@ -0,0 +1,16 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<OutputType>Exe</OutputType>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>IGP.Calculator.Cli</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\Services\Services.csproj" />
|
||||
<ProjectReference Include="..\Model\Model.csproj" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
+155
@@ -0,0 +1,155 @@
|
||||
using IGP.Calculator.Cli.Services;
|
||||
using Model.Entity;
|
||||
using Model.Entity.Data;
|
||||
using Services.Immortal;
|
||||
using Services.Website;
|
||||
|
||||
var faction = DataType.FACTION_QRath;
|
||||
var immortal = DataType.IMMORTAL_Orzum;
|
||||
var attackTime = 1500;
|
||||
var entityNames = new List<string>();
|
||||
|
||||
for (var i = 0; i < args.Length; i++)
|
||||
switch (args[i].ToLower())
|
||||
{
|
||||
case "--faction" when i + 1 < args.Length:
|
||||
var f = args[++i].ToLower();
|
||||
faction = f switch
|
||||
{
|
||||
"qrath" => DataType.FACTION_QRath,
|
||||
"aru" => DataType.FACTION_Aru,
|
||||
_ => throw new Exception($"Unknown faction '{args[i]}'. Use QRath or Aru.")
|
||||
};
|
||||
immortal = f switch
|
||||
{
|
||||
"qrath" => DataType.IMMORTAL_Orzum,
|
||||
"aru" => DataType.IMMORTAL_Mala,
|
||||
_ => immortal
|
||||
};
|
||||
break;
|
||||
case "--immortal" when i + 1 < args.Length:
|
||||
var im = args[++i];
|
||||
immortal = im switch
|
||||
{
|
||||
nameof(DataType.IMMORTAL_Orzum) => DataType.IMMORTAL_Orzum,
|
||||
nameof(DataType.IMMORTAL_Ajari) => DataType.IMMORTAL_Ajari,
|
||||
nameof(DataType.IMMORTAL_Atzlan) => DataType.IMMORTAL_Atzlan,
|
||||
nameof(DataType.IMMORTAL_Mala) => DataType.IMMORTAL_Mala,
|
||||
nameof(DataType.IMMORTAL_Xol) => DataType.IMMORTAL_Xol,
|
||||
_ => throw new Exception($"Unknown immortal '{im}'.")
|
||||
};
|
||||
break;
|
||||
case "--attack-time" or "-a" when i + 1 < args.Length:
|
||||
attackTime = int.Parse(args[++i]);
|
||||
break;
|
||||
default:
|
||||
entityNames.Add(args[i]);
|
||||
break;
|
||||
}
|
||||
|
||||
var toastService = new ToastService();
|
||||
var storageService = new NullStorageService();
|
||||
var timingService = new TimingService(storageService);
|
||||
timingService.SetAttackTime(attackTime);
|
||||
var buildOrderService = new BuildOrderService(toastService, timingService);
|
||||
var economyService = new EconomyService();
|
||||
|
||||
buildOrderService.Reset(faction);
|
||||
economyService.Calculate(buildOrderService, timingService, 0);
|
||||
|
||||
Console.WriteLine($"Faction: {(faction == DataType.FACTION_QRath ? "Q'Rath" : "Aru")}");
|
||||
Console.WriteLine($"Immortal: {immortal.Replace("IMMORTAL_", "")}");
|
||||
Console.WriteLine($"Attack Time: {attackTime}s");
|
||||
Console.WriteLine(new string('-', 50));
|
||||
|
||||
foreach (var name in entityNames)
|
||||
{
|
||||
if (name.StartsWith("wait ", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var seconds = int.Parse(name[5..]);
|
||||
buildOrderService.AddWait(seconds);
|
||||
economyService.Calculate(buildOrderService, timingService, buildOrderService.GetLastRequestInterval());
|
||||
Console.WriteLine($" Wait {seconds}s -> now at interval {buildOrderService.GetLastRequestInterval()}");
|
||||
continue;
|
||||
}
|
||||
|
||||
if (name.StartsWith("waitto ", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
var interval = int.Parse(name[7..]);
|
||||
buildOrderService.AddWaitTo(interval);
|
||||
economyService.Calculate(buildOrderService, timingService, buildOrderService.GetLastRequestInterval());
|
||||
Console.WriteLine($" Wait to {interval}s -> now at interval {buildOrderService.GetLastRequestInterval()}");
|
||||
continue;
|
||||
}
|
||||
|
||||
var entity = FindEntity(name, faction, immortal);
|
||||
if (entity == null)
|
||||
{
|
||||
Console.WriteLine($" ERROR: '{name}' not found for this faction/immortal.");
|
||||
continue;
|
||||
}
|
||||
|
||||
var beforeInterval = buildOrderService.GetLastRequestInterval();
|
||||
var added = buildOrderService.Add(entity, economyService);
|
||||
if (added)
|
||||
{
|
||||
economyService.Calculate(buildOrderService, timingService, buildOrderService.GetLastRequestInterval());
|
||||
var startedAt = buildOrderService.GetLastRequestInterval();
|
||||
var production = entity.Production();
|
||||
var completedAt = production != null ? startedAt + production.BuildTime : startedAt;
|
||||
var cost = production != null
|
||||
? $" [{production.Alloy}a/{production.Ether}e/{production.Pyre}p, {production.BuildTime}s]"
|
||||
: "";
|
||||
Console.WriteLine($" {entity.GetName(),-25} start={startedAt,4}s done={completedAt,4}s{cost}");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine($" ERROR: Could not add '{name}'.");
|
||||
var toasts = toastService.GetToasts();
|
||||
if (toasts.Count > 0)
|
||||
{
|
||||
var lastToast = toasts[0];
|
||||
Console.WriteLine($" Reason: {lastToast.Title} - {lastToast.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Console.WriteLine(new string('-', 50));
|
||||
|
||||
var lastInterval = buildOrderService.GetLastRequestInterval();
|
||||
var finalEconomy = economyService.GetEconomy(timingService.GetAttackTime());
|
||||
var lastEconomy = economyService.GetEconomy(lastInterval);
|
||||
|
||||
Console.WriteLine($"Army Attacking At: {timingService.GetAttackTime()}s");
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine($"At attack time ({timingService.GetAttackTime()}s):");
|
||||
Console.WriteLine($" Alloy: {finalEconomy.Alloy,10:F1}");
|
||||
Console.WriteLine($" Ether: {finalEconomy.Ether,10:F1}");
|
||||
Console.WriteLine($" Pyre: {finalEconomy.Pyre,10:F1}");
|
||||
Console.WriteLine("");
|
||||
Console.WriteLine($"At last build action ({lastInterval}s):");
|
||||
Console.WriteLine($" Alloy: {lastEconomy.Alloy,10:F1}");
|
||||
Console.WriteLine($" Ether: {lastEconomy.Ether,10:F1}");
|
||||
Console.WriteLine($" Pyre: {lastEconomy.Pyre,10:F1}");
|
||||
|
||||
static EntityModel? FindEntity(string name, string faction, string immortal)
|
||||
{
|
||||
var candidates = EntityModel.GetList()
|
||||
.Where(e => e.Info()?.Name?.Equals(name, StringComparison.OrdinalIgnoreCase) == true)
|
||||
.Where(e => e.Faction()?.Faction == faction)
|
||||
.ToList();
|
||||
|
||||
if (candidates.Count == 0)
|
||||
candidates = EntityModel.GetList()
|
||||
.Where(e => e.Info()?.Name?.Equals(name, StringComparison.OrdinalIgnoreCase) == true)
|
||||
.Where(e => e.Faction() == null || e.Faction()!.Faction == DataType.FACTION_Neutral)
|
||||
.ToList();
|
||||
|
||||
if (candidates.Count == 0) return null;
|
||||
if (candidates.Count == 1) return candidates[0];
|
||||
|
||||
var vanguardMatch = candidates.FirstOrDefault(e => e.VanguardAdded()?.ImmortalId == immortal);
|
||||
if (vanguardMatch != null) return vanguardMatch;
|
||||
|
||||
return candidates.FirstOrDefault(e => e.VanguardAdded() == null);
|
||||
}
|
||||
@@ -0,0 +1,33 @@
|
||||
using Services;
|
||||
|
||||
namespace IGP.Calculator.Cli.Services;
|
||||
|
||||
public class NullStorageService : IStorageService
|
||||
{
|
||||
private readonly Dictionary<string, object?> _store = new();
|
||||
|
||||
public void Subscribe(Action action)
|
||||
{
|
||||
}
|
||||
|
||||
public void Unsubscribe(Action action)
|
||||
{
|
||||
}
|
||||
|
||||
public T GetValue<T>(string forKey)
|
||||
{
|
||||
if (_store.TryGetValue(forKey, out var value) && value is T typed)
|
||||
return typed;
|
||||
return default!;
|
||||
}
|
||||
|
||||
public void SetValue<T>(string key, T value)
|
||||
{
|
||||
_store[key] = value;
|
||||
}
|
||||
|
||||
public Task Load()
|
||||
{
|
||||
return Task.CompletedTask;
|
||||
}
|
||||
}
|
||||
@@ -1,9 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk.Razor">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<Nullable>enable</Nullable>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<RootNamespace>Components</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
}
|
||||
<div>
|
||||
<input readonly="@MemoryQuestion.IsRevealed"
|
||||
class="formTextInput @(MemoryQuestion.IsRevealed ? "revealed" : IsSubmitted == false ? "guess" : int.Parse(guess ?? string.Empty) == MemoryQuestion.Answer ? "correct" : "wrong")"
|
||||
class="formTextInput @(MemoryQuestion.IsRevealed ? "revealed" : !IsSubmitted ? "guess" : int.Parse(guess ?? string.Empty) == MemoryQuestion.Answer ? "correct" : "wrong")"
|
||||
placeholder="guess..."
|
||||
type="number"
|
||||
value="@guess"
|
||||
|
||||
@@ -16,7 +16,7 @@ else
|
||||
|
||||
[Parameter] public string EntityId { get; set; } = default!;
|
||||
|
||||
private EntityModel Entity => DATA.Get()[EntityId];
|
||||
private EntityModel Entity => EntityData.Get()[EntityId];
|
||||
|
||||
void EntityLabelClicked()
|
||||
{
|
||||
|
||||
@@ -1,33 +1,6 @@
|
||||
@inject IVariableService VariableService
|
||||
|
||||
<div class="footerContainer" xmlns="http://www.w3.org/1999/html">
|
||||
<div class="footerSocials">
|
||||
|
||||
<a class="footerIcon" href="https://github.com/JonathanMcCaffrey/IGP-Fan-Reference/discussions" target="_blank">
|
||||
<i class="fa-brands fa-github"></i>
|
||||
</a>
|
||||
|
||||
<a class="footerIcon" href="mailto:igpfanreference@jonathanmccaffrey.com" target="_blank">
|
||||
<i class="fa-solid fa-envelope"></i>
|
||||
</a>
|
||||
|
||||
<a class="footerIcon" href="https://discord.gg/uMq8bMGeeN" target="_blank">
|
||||
<i class="fa-brands fa-discord"></i>
|
||||
</a>
|
||||
<a class="footerIcon" href="https://www.youtube.com/channel/UCQx88d5C12yp4l7uszNYrdQ" target="_blank">
|
||||
<i class="fa-brands fa-youtube"></i>
|
||||
</a>
|
||||
<a class="footerIcon" href="https://www.twitch.tv/jonathanmccaffrey" target="_blank">
|
||||
<i class="fa-brands fa-twitch"></i>
|
||||
</a>
|
||||
</div>
|
||||
|
||||
<div class="footerContainer" xmlns="http://www.w3.org/1999/html">
|
||||
<div class="footerDivider"></div>
|
||||
|
||||
<div class="footerLastUpdated">Website updated <a
|
||||
href="https://github.com/JonathanMcCaffrey/IGP-Fan-Reference/commits/main"><b>@VariableService.Variables["LastUpdated"]</b></a>
|
||||
</div>
|
||||
|
||||
<div class="footerDisclaimer">
|
||||
This website is fan-made and not affiliated with <b>SunSpear Games</b> in any way.
|
||||
</div>
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
FROM mcr.microsoft.com/dotnet/sdk:10.0 AS build
|
||||
WORKDIR /src
|
||||
COPY . .
|
||||
RUN dotnet publish Web/Web.csproj -c Release -o /app/publish
|
||||
|
||||
FROM nginx:alpine AS final
|
||||
WORKDIR /usr/share/nginx/html
|
||||
COPY --from=build /app/publish/wwwroot ./
|
||||
COPY nginx.conf /etc/nginx/nginx.conf
|
||||
@@ -0,0 +1,107 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.32112.339
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Model", "Model\Model.csproj", "{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Services", "Services\Services.csproj", "{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Web", "Web\Web.csproj", "{4A54E237-4FF6-4459-91D9-9249AE3E72E0}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "CLI", "CLI\CLI.csproj", "{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Components", "Components\Components.csproj", "{C0ACFFEB-8098-4119-9C28-3A4D671C2514}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tests", "Tests\Tests.csproj", "{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Debug|x64 = Debug|x64
|
||||
Debug|x86 = Debug|x86
|
||||
Release|Any CPU = Release|Any CPU
|
||||
Release|x64 = Release|x64
|
||||
Release|x86 = Release|x86
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F4D758C2-349F-49F2-86E2-7DAB2B53BB61}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4F56F39D-D34C-4987-8C3F-3A8E03A2D7E6}.Release|x86.Build.0 = Release|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Release|x64.Build.0 = Release|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{4A54E237-4FF6-4459-91D9-9249AE3E72E0}.Release|x86.Build.0 = Release|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Release|x64.Build.0 = Release|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{F8A23FFE-7FCC-4C28-B5CF-DBE435284AFA}.Release|x86.Build.0 = Release|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Release|x64.Build.0 = Release|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{C0ACFFEB-8098-4119-9C28-3A4D671C2514}.Release|x86.Build.0 = Release|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Debug|x64.ActiveCfg = Debug|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Debug|x64.Build.0 = Debug|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Debug|x86.ActiveCfg = Debug|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Debug|x86.Build.0 = Debug|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|x64.ActiveCfg = Release|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|x64.Build.0 = Release|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|x86.ActiveCfg = Release|Any CPU
|
||||
{1460CB4C-7E8E-41F0-8DF2-174C69A3E366}.Release|x86.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {19100811-B909-4D20-9AE0-2EB579A55B3A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
-49
@@ -1,49 +0,0 @@
|
||||
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 17
|
||||
VisualStudioVersion = 17.0.32112.339
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "IGP", "IGP.csproj", "{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Model", "..\Model\Model.csproj", "{77395F7A-BE93-470C-9F10-F48FFA445B63}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Components", "..\Components\Components.csproj", "{0419E7CD-0971-4A56-A61F-C090DF60FAF6}"
|
||||
EndProject
|
||||
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Services", "..\Services\Services.csproj", "{621178C8-4E8B-478E-80E5-7478F0E7B67E}"
|
||||
EndProject
|
||||
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TestAutomation", "..\TestAutomation\TestAutomation.csproj", "{8B49D038-D013-460D-9C4F-817CAFFEB06F}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Any CPU = Debug|Any CPU
|
||||
Release|Any CPU = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{172D35E4-8E7B-40D1-96D6-BE2A2043CFCA}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{77395F7A-BE93-470C-9F10-F48FFA445B63}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{0419E7CD-0971-4A56-A61F-C090DF60FAF6}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{621178C8-4E8B-478E-80E5-7478F0E7B67E}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
{8B49D038-D013-460D-9C4F-817CAFFEB06F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
|
||||
{8B49D038-D013-460D-9C4F-817CAFFEB06F}.Debug|Any CPU.Build.0 = Debug|Any CPU
|
||||
{8B49D038-D013-460D-9C4F-817CAFFEB06F}.Release|Any CPU.ActiveCfg = Release|Any CPU
|
||||
{8B49D038-D013-460D-9C4F-817CAFFEB06F}.Release|Any CPU.Build.0 = Release|Any CPU
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {19100811-B909-4D20-9AE0-2EB579A55B3A}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
||||
@@ -1,162 +0,0 @@
|
||||
[
|
||||
{
|
||||
"Id": 1,
|
||||
"WebSectionModelId": 2,
|
||||
"Name": "Database",
|
||||
"Description": "Database of game information",
|
||||
"Href": "database",
|
||||
"IsPrivate": "False"
|
||||
},
|
||||
{
|
||||
"Id": 2,
|
||||
"WebSectionModelId": 1,
|
||||
"Name": "Build Calculator",
|
||||
"Description": "Build order calculator for determining army timings",
|
||||
"Href": "build-calculator",
|
||||
"IsPrivate": "False"
|
||||
},
|
||||
{
|
||||
"Id": 3,
|
||||
"WebSectionModelId": 1,
|
||||
"Name": "Harass Calculator",
|
||||
"Description": "Alloy harassment calculator",
|
||||
"Href": "harass-calculator",
|
||||
"IsPrivate": "False"
|
||||
},
|
||||
{
|
||||
"Id": 4,
|
||||
"WebSectionModelId": 1,
|
||||
"Name": "Memory Tester",
|
||||
"Description": "Testing memory",
|
||||
"Href": "memory-tester",
|
||||
"IsPrivate": "False"
|
||||
},
|
||||
{
|
||||
"Id": 5,
|
||||
"WebSectionModelId": 1,
|
||||
"Name": "Comparion Charts",
|
||||
"Description": "Ecnomy charts to compare build orders",
|
||||
"Href": "comparison-charts",
|
||||
"IsPrivate": "True"
|
||||
},
|
||||
{
|
||||
"Id": 6,
|
||||
"WebSectionModelId": 2,
|
||||
"Name": "Notes",
|
||||
"Description": "General player notes",
|
||||
"Href": "notes",
|
||||
"IsPrivate": "False"
|
||||
},
|
||||
{
|
||||
"Id": 7,
|
||||
"WebSectionModelId": 2,
|
||||
"Name": "Key Mapping",
|
||||
"Description": "General key mapping info",
|
||||
"Href": "keymapping",
|
||||
"IsPrivate": "True"
|
||||
},
|
||||
{
|
||||
"Id": 8,
|
||||
"WebSectionModelId": 4,
|
||||
"Name": "Milestones",
|
||||
"Description": "Link to Milestones on GitHub",
|
||||
"Href": "https://github.com/JonathanMcCaffrey/IGP-Fan-Reference/milestones",
|
||||
"IsPrivate": "False"
|
||||
},
|
||||
{
|
||||
"Id": 9,
|
||||
"WebSectionModelId": 4,
|
||||
"Name": "Commits",
|
||||
"Description": "Link to Commits on GitHub",
|
||||
"Href": "https://github.com/JonathanMcCaffrey/IGP-Fan-Reference/commits/main",
|
||||
"IsPrivate": "False"
|
||||
},
|
||||
{
|
||||
"Id": 10,
|
||||
"WebSectionModelId": 4,
|
||||
"Name": "Tasks",
|
||||
"Description": "Link to Tasks on GitHub",
|
||||
"Href": "https://github.com/JonathanMcCaffrey/IGP-Fan-Reference/projects/3",
|
||||
"IsPrivate": "False"
|
||||
},
|
||||
{
|
||||
"Id": 11,
|
||||
"WebSectionModelId": 4,
|
||||
"Name": "Code",
|
||||
"Description": "Link to Code on GitHub",
|
||||
"Href": "https://github.com/JonathanMcCaffrey/IGP-Fan-Reference",
|
||||
"IsPrivate": "False"
|
||||
},
|
||||
{
|
||||
"Id": 12,
|
||||
"WebSectionModelId": 2,
|
||||
"Name": "Documentation",
|
||||
"Description": "Explaining how to use this website",
|
||||
"Href": "documentation",
|
||||
"IsPrivate": "True"
|
||||
},
|
||||
{
|
||||
"Id": 13,
|
||||
"WebSectionModelId": 3,
|
||||
"Name": "About",
|
||||
"Description": "Answering general questions on the website",
|
||||
"Href": "about",
|
||||
"IsPrivate": "False"
|
||||
},
|
||||
{
|
||||
"Id": 14,
|
||||
"WebSectionModelId": 3,
|
||||
"Name": "Contact",
|
||||
"Description": "My contact info",
|
||||
"Href": "contact",
|
||||
"IsPrivate": "False"
|
||||
},
|
||||
{
|
||||
"Id": 15,
|
||||
"WebSectionModelId": 3,
|
||||
"Name": "Streams",
|
||||
"Description": "Stream info",
|
||||
"Href": "streams",
|
||||
"IsPrivate": "False"
|
||||
},
|
||||
{
|
||||
"Id": 16,
|
||||
"WebSectionModelId": 4,
|
||||
"Name": "Wiki",
|
||||
"Description": "Link to Wiki on GitHub",
|
||||
"Href": "https://github.com/JonathanMcCaffrey/IGP-Fan-Reference/wiki",
|
||||
"IsPrivate": "False"
|
||||
},
|
||||
{
|
||||
"Id": 17,
|
||||
"WebSectionModelId": 5,
|
||||
"Name": "Permissions",
|
||||
"Description": "Permission Settings",
|
||||
"Href": "permissions",
|
||||
"IsPrivate": "False"
|
||||
},
|
||||
{
|
||||
"Id": 18,
|
||||
"WebSectionModelId": 5,
|
||||
"Name": "Data Collection",
|
||||
"Description": "Data Collection Settings",
|
||||
"Href": "data-collection",
|
||||
"IsPrivate": "True"
|
||||
},
|
||||
{
|
||||
"Id": 19,
|
||||
"WebSectionModelId": 5,
|
||||
"Name": "Storage",
|
||||
"Description": "Storage Settings",
|
||||
"Href": "storage",
|
||||
"IsPrivate": "False"
|
||||
},
|
||||
{
|
||||
"Id": 20,
|
||||
"WebSectionModelId": 1,
|
||||
"Name": "Economy Comparison",
|
||||
"Description": "Compare economies",
|
||||
"Href": "economy-comparison",
|
||||
"IsPrivate": "False"
|
||||
}
|
||||
]
|
||||
@@ -1,52 +0,0 @@
|
||||
[
|
||||
{
|
||||
"Id": 1,
|
||||
"Name": "Tools",
|
||||
"Description": "Tools Stuff",
|
||||
"Order": 1,
|
||||
"IsPrivate": "False",
|
||||
"Icon": "fa-screwdriver-wrench",
|
||||
"OnlyIcon": false,
|
||||
"WebPageModels": []
|
||||
},
|
||||
{
|
||||
"Id": 2,
|
||||
"Name": "Resources",
|
||||
"Description": "Resources Stuff",
|
||||
"Order": 2,
|
||||
"IsPrivate": "False",
|
||||
"Icon": "fa-toolbox",
|
||||
"OnlyIcon": false,
|
||||
"WebPageModels": []
|
||||
},
|
||||
{
|
||||
"Id": 3,
|
||||
"Name": "General",
|
||||
"Description": "About Stuff",
|
||||
"Order": 3,
|
||||
"IsPrivate": "False",
|
||||
"Icon": "fa-circle-info",
|
||||
"OnlyIcon": false,
|
||||
"WebPageModels": []
|
||||
},
|
||||
{
|
||||
"Id": 4,
|
||||
"Name": "Development",
|
||||
"Description": "Development Stuff",
|
||||
"Order": 4,
|
||||
"IsPrivate": "False",
|
||||
"Icon": "fa-code",
|
||||
"OnlyIcon": false,
|
||||
"WebPageModels": []
|
||||
},
|
||||
{
|
||||
"Id": 5,
|
||||
"Name": "Settings",
|
||||
"Description": "Settings Stuff",
|
||||
"Order": 5,
|
||||
"IsPrivate": "False",
|
||||
"Icon": "fa-gear",
|
||||
"OnlyIcon": false,
|
||||
"WebPageModels": []
|
||||
}
|
||||
]
|
||||
@@ -48,7 +48,7 @@ public class BuildOrderModel
|
||||
new List<EntityModel>
|
||||
{
|
||||
EntityModel.Get(DataType.STARTING_Bastion),
|
||||
EntityModel.Get(DataType.STARTING_TownHall_Aru)
|
||||
EntityModel.Get(factionStartingTownHall)
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -59,7 +59,7 @@ public class BuildOrderModel
|
||||
new List<EntityModel>
|
||||
{
|
||||
EntityModel.Get(DataType.STARTING_Bastion),
|
||||
EntityModel.Get(DataType.STARTING_TownHall_Aru)
|
||||
EntityModel.Get(factionStartingTownHall)
|
||||
}
|
||||
}
|
||||
};
|
||||
@@ -69,7 +69,7 @@ public class BuildOrderModel
|
||||
DataType.STARTING_Bastion, 0
|
||||
},
|
||||
{
|
||||
DataType.STARTING_TownHall_Aru, 0
|
||||
factionStartingTownHall, 0
|
||||
}
|
||||
};
|
||||
UniqueCompletedCount = new Dictionary<string, int>
|
||||
@@ -78,7 +78,7 @@ public class BuildOrderModel
|
||||
DataType.STARTING_Bastion, 1
|
||||
},
|
||||
{
|
||||
DataType.STARTING_TownHall_Aru, 1
|
||||
factionStartingTownHall, 1
|
||||
}
|
||||
};
|
||||
SupplyCountTimes = new Dictionary<int, int>
|
||||
|
||||
@@ -11,12 +11,12 @@ public class BuildToCompareModel
|
||||
private int numberOfTownHallExpansions;
|
||||
public string Faction { get; set; }
|
||||
|
||||
public EntityModel GetTownHallEntity => DATA.Get()[
|
||||
public EntityModel GetTownHallEntity => EntityData.Get()[
|
||||
Faction.Equals(DataType.FACTION_QRath)
|
||||
? DataType.BUILDING_Acropolis
|
||||
: DataType.BUILDING_GroveHeart];
|
||||
|
||||
public EntityModel GetTownHallMining2Entity => DATA.Get()[
|
||||
public EntityModel GetTownHallMining2Entity => EntityData.Get()[
|
||||
Faction.Equals(DataType.FACTION_QRath)
|
||||
? DataType.BUPGRADE_MiningLevel2_QRath
|
||||
: DataType.BUPGRADE_MiningLevel2_Aru];
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,438 @@
|
||||
using System.Collections.Generic;
|
||||
using Model.Entity.Parts;
|
||||
using Model.Entity.Types;
|
||||
using Model.Types;
|
||||
|
||||
namespace Model.Entity.Data;
|
||||
|
||||
public partial class EntityData
|
||||
{
|
||||
public static Dictionary<string, EntityModel> GetAbilityData()
|
||||
{
|
||||
return new Dictionary<string, EntityModel>
|
||||
{
|
||||
// Abilities
|
||||
// Q'Rath
|
||||
{
|
||||
DataType.ABILITY_RadiantWard,
|
||||
new EntityModel(DataType.ABILITY_RadiantWard, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Radiant Ward", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"""
|
||||
Target a location. A Hidden mine is laid there.
|
||||
|
||||
Detonates when enemy ground units get close. Enemies hit
|
||||
are revealed, slowed, and take additional damage.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "D", HoldSpace = true })
|
||||
.AddPart(new EntityProductionModel { DefensiveLayer = 45, Cooldown = 40 })
|
||||
.AddPart(new EntityRequirementModel { Id = DataType.UPGRADE_RadiantWard })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 30, DefenseLayer = 30, Lasts = 60, Armor = ArmorType.Light })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_HouseOfFadingSaints,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_Maledictions,
|
||||
new EntityModel(DataType.PASSIVE_Maledictions, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Maledictions", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"Stun ground units? With Maledictions passive.",
|
||||
Notes = "Not implemented"
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_Windstep,
|
||||
new EntityModel(DataType.ABILITY_Windstep, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Windstep", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"""
|
||||
Target a location. Teleport to location: costs Shields.
|
||||
|
||||
Decrease cooldown and avoid Shield cost when teleporting to <b style="color:white">Hallowed Ground</b>.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel { Cooldown = 20 })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_Intervention,
|
||||
new EntityModel(DataType.ABILITY_Intervention, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Intervention", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"Target a location. A Saoshin leaps to the location and heals friendly units."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel { Cooldown = 30 })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_OrdainedPassage,
|
||||
new EntityModel(DataType.ABILITY_OrdainedPassage, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Ordained Passage", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"""
|
||||
Target an area to send a damage reduction to circle to.
|
||||
|
||||
The circle starts at the Ark Mother, travels to the target location, and stays there for a duration.
|
||||
Friendly units get damage reduction while in the circle, including when it is movie.
|
||||
Costs all the Ark Mother's Shields.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "D", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel { Cooldown = 3, Energy = 55 })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_DeployAbsolver,
|
||||
new EntityModel(DataType.ABILITY_DeployAbsolver, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Deploy Absolver", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
@"Deploying the Absolver drastically <b style=""color: orange"">increases its attack speed</b>."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_DeployMagi,
|
||||
new EntityModel(DataType.ABILITY_DeployMagi, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Deploy Magi", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"""Deploys the Magi to project <b style="color:white">Hallowed Ground</b> around it."""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.ABILITY_DeploySentinel,
|
||||
new EntityModel(DataType.ABILITY_DeploySentinel, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Deploy Sentinel", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"""
|
||||
Deploy to absorb enemy projectiles. Costs Shields.
|
||||
|
||||
Deployed units cannot move. Use Mobilize to move again.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "D", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
DataType.ABILITY_Smite,
|
||||
new EntityModel(DataType.ABILITY_Smite, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Smite", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"""
|
||||
Target an enemy unit. Deal damage after a delay.
|
||||
|
||||
After being targeted, the enemy can move out of range to
|
||||
cancel damage. Can store up to 2 charges.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel { Cooldown = 2, Energy = 30 })
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
DataType.ABILITY_Awestrike,
|
||||
new EntityModel(DataType.ABILITY_Awestrike, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Awestrike", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"""
|
||||
Target and area. Damages enemy ground units.
|
||||
|
||||
Units in the center of the area take more damage.
|
||||
Units take damage over time after the initial burst of damage and continue to take damage
|
||||
over time after leaving the area.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel { Cooldown = 8, Energy = 60 })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.ABILITY_TitheBlades,
|
||||
new EntityModel(DataType.ABILITY_TitheBlades, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Tithe Blades", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"""
|
||||
Activate to steal Shields from your nearby ground units, up to a cap.
|
||||
|
||||
Also increases attack speed based on amount of Shields stolen.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "D", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel { Cooldown = 70 })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.ABILITY_MobilizeQrath,
|
||||
new EntityModel(DataType.ABILITY_MobilizeQrath, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Mobilize Q'Rath", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"Mobilize all deployed Q'Rath units."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "TAB", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
// Abilities
|
||||
// Aru
|
||||
{
|
||||
DataType.ABILITY_MobilizeAru,
|
||||
new EntityModel(DataType.ABILITY_MobilizeAru, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Mobilize Aru", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
@"Mobilize all deployed Aru units."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_Offering,
|
||||
new EntityModel(DataType.ABILITY_Offering, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Offering", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"Sacrifices 10 life to give Masked Hunters +3 damage for 3 shots. And increased speed and attack speed."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel { Id = DataType.UPGRADE_Offering })
|
||||
.AddPart(new EntityProductionModel { Cooldown = 5 })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_DiveBomb,
|
||||
new EntityModel(DataType.ABILITY_DiveBomb, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Dive Bomb", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"The aarox dives down into the ground, dealing damage in a smaller area. Non-hovering units in the area take additional damage over time."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_DrainingEmbrace,
|
||||
new EntityModel(DataType.ABILITY_DrainingEmbrace, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Draining Embrace", Descriptive = DescriptiveType.Ability,
|
||||
Description = "Units in the target area are rooted and lose energy."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityProductionModel { Energy = 30, Cooldown = 5 })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_BloodPlague,
|
||||
new EntityModel(DataType.ABILITY_BloodPlague, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Blood Plague", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"Damage is based on maximum HP. Damage cannot kill a unit."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityProductionModel { Energy = 90, Cooldown = 8 })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_DeployResinant,
|
||||
new EntityModel(DataType.ABILITY_DeployResinant, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Deploy Resinant", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"Get even more attack range if deployed on Rootway. Deploying a unit makes it unable to move. Use Mobilize to move again."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_DeployBloodAnchor,
|
||||
new EntityModel(DataType.ABILITY_DeployBloodAnchor, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Deploy Blood Anchor", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"""
|
||||
Activate to deploy and begin spawning Cystic Crawlers.<br/>
|
||||
<br/>
|
||||
Generates Rootway. Cystic Crawlers are temporary units that cannot survive off Rootway and self-destruct to deal
|
||||
area damage. Deployed units cannot move. Use Mobilize to move again.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_BloodyRebound,
|
||||
new EntityModel(DataType.ABILITY_BloodyRebound, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Bloody Rebond", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"""
|
||||
Active to prevent death for a duration.
|
||||
Instead of dying, the Bloodbound will teleport to the place it was when the ability was activated.
|
||||
It also takes less damage while Blood Rebound is active.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Mala })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel { Energy = 50, Cooldown = 40 })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.ABILITY_RootVice,
|
||||
new EntityModel(DataType.ABILITY_RootVice, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Root Vice", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"Roots all units for several seconds, then leaves them slowed for several seconds after."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Mala })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel { Energy = 50, Cooldown = 10 })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_BirthingStorm,
|
||||
new EntityModel(DataType.ABILITY_BirthingStorm, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Birthing Storm", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"""
|
||||
Target an area to deal damage and Seed enemies.
|
||||
""",
|
||||
Notes =
|
||||
"""
|
||||
Deals 10 damage + 5% of max life of the target immediately upon affecting the enemy unit. <br/>
|
||||
It deals 15 damage + 15% after 8 seconds. If the unit dies during those 8 seconds (including the final burst),
|
||||
spawns 1 quitl every 2 supply of the dead unit, rounded up. Stacking only refreshes duration of debuff.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "D" })
|
||||
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Mala })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel { Energy = 95, Cooldown = 8 })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_DeployDreadSister,
|
||||
new EntityModel(DataType.ABILITY_DeployDreadSister, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Deploy Dread Sister", Descriptive = DescriptiveType.Dislodger,
|
||||
Description =
|
||||
"""
|
||||
Deploy to get more damage and more range.<br/>
|
||||
<br/>
|
||||
Can only attack ground. Each shot cost Energy. Use Mobilize to move again.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HoldSpace = true, HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityVanguardAddedModel
|
||||
{ ImmortalId = DataType.IMMORTAL_Mala, ReplaceId = DataType.UNIT_Godphage })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_MorphToGodphage,
|
||||
new EntityModel(DataType.ABILITY_MorphToGodphage, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Morph To Godphage", Descriptive = DescriptiveType.Dislodger,
|
||||
Description = "Active to morph a Red Seer into a Godphage"
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HoldSpace = true, HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel { Alloy = 150, Energy = 100, BuildTime = 25 })
|
||||
.AddPart(new EntitySupplyModel { Takes = 8 })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{ Id = DataType.UNIT_RedSeer, Requirement = RequirementType.Morph })
|
||||
.AddPart(new EntityVitalityModel { Health = 225, DefenseLayer = 375, Armor = ArmorType.Heavy })
|
||||
.AddPart(new EntityMovementModel { Speed = 350, Movement = MovementType.Ground })
|
||||
.AddPart(new EntityWeaponModel
|
||||
{ Damage = 155, Range = 1100, SecondsBetweenAttacks = 7f, Targets = TargetType.Ground })
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HiddenX })
|
||||
.AddPart(new EntityIdUpgradeModel { Id = DataType.UPGRADE_GodphageDamage })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.ABILITY_DeepTunnel,
|
||||
new EntityModel(DataType.ABILITY_DeepTunnel, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Deep Tunnel"
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "TAB", HoldSpace = true, HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.ABILITY_ObstructingSwarm,
|
||||
new EntityModel(DataType.ABILITY_ObstructingSwarm, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Obstructing Swarm"
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HoldSpace = true, HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
{
|
||||
DataType.ABILITY_Hematoma,
|
||||
new EntityModel(DataType.ABILITY_Hematoma, EntityType.Ability)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Hematoma"
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HoldSpace = true, HotkeyGroup = "D" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,754 @@
|
||||
using System.Collections.Generic;
|
||||
using Model.Entity.Parts;
|
||||
using Model.Entity.Types;
|
||||
using Model.Types;
|
||||
|
||||
namespace Model.Entity.Data;
|
||||
|
||||
public partial class EntityData
|
||||
{
|
||||
public static Dictionary<string, EntityModel> GetBuildingData()
|
||||
{
|
||||
return new Dictionary<string, EntityModel>
|
||||
{
|
||||
// Building
|
||||
// Q'Rath
|
||||
{
|
||||
DataType.BUILDING_Acropolis,
|
||||
new EntityModel(DataType.BUILDING_Acropolis, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Acropolis",
|
||||
Descriptive = DescriptiveType.Stronghold,
|
||||
Description =
|
||||
"""
|
||||
Collect Alloy and Ether.<br/>
|
||||
If all your Strongholds are destroyed, you lose the game.
|
||||
""",
|
||||
FlavorText =
|
||||
"""
|
||||
"Bereft of Ancient Kin, from My burning heart new mothers shall be made and woven onto
|
||||
the grateful earth. And within their walls, the righteous shall gather in great number."<br/>
|
||||
—Holy Aros, "Utterances 14.7"
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 450, BuildTime = 90, RequiresWorker = true, ConsumesWorker = true })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 2300, DefenseLayer = 1200, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
HarvestedPerInterval = 1, RequiresWorker = true, Resource = ResourceType.Alloy, Slots = 2,
|
||||
TotalAmount = 3600
|
||||
})
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedGround })
|
||||
},
|
||||
{
|
||||
DataType.BUPGRADE_MiningLevel2_QRath,
|
||||
new EntityModel(DataType.BUPGRADE_MiningLevel2_QRath, EntityType.Building_Upgrade)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Mining Level 2", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Upgrades the nearest resource cluster to allow more workers to mine from it."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "CONTROL" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_Acropolis,
|
||||
Requirement = RequirementType.Morph
|
||||
})
|
||||
.AddPart(new EntityProductionModel { Alloy = 400, BuildTime = 20, RequiresWorker = false })
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
HarvestedPerInterval = 1, RequiresWorker = true, Resource = ResourceType.Alloy, Slots = 4,
|
||||
TotalAmount = 3600
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.CONVERSION_EtherSruge_Aru,
|
||||
new EntityModel(DataType.CONVERSION_EtherSruge_Aru, EntityType.Building_Upgrade)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Ether Surge", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Gain 100 Ether over 50 seconds"
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "CONTROL" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.STARTING_TownHall_Aru,
|
||||
Requirement = RequirementType.Morph
|
||||
})
|
||||
.AddPart(new EntityProductionModel { Alloy = 50, Cooldown = 50, RequiresWorker = false })
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
HarvestedPerInterval = 2, RequiresWorker = false, Resource = ResourceType.Ether, Slots = 1,
|
||||
TotalAmount = 100
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.CONVERSION_EtherSruge_QRath,
|
||||
new EntityModel(DataType.CONVERSION_EtherSruge_QRath, EntityType.Building_Upgrade)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Ether Surge", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Gain 100 Ether over 50 seconds"
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "CONTROL" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.STARTING_TownHall_QRath,
|
||||
Requirement = RequirementType.Morph
|
||||
})
|
||||
.AddPart(new EntityProductionModel { Alloy = 50, Cooldown = 50, RequiresWorker = false })
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
HarvestedPerInterval = 2, RequiresWorker = false, Resource = ResourceType.Ether, Slots = 1,
|
||||
TotalAmount = 100
|
||||
})
|
||||
},
|
||||
|
||||
{
|
||||
DataType.BUILDING_ApostleOfBinding,
|
||||
new EntityModel(DataType.BUILDING_ApostleOfBinding, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Apostle of Binding",
|
||||
Descriptive = DescriptiveType.Economy,
|
||||
Description =
|
||||
"""
|
||||
Automatically harvest Ether.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "TAB", HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel { Alloy = 175, BuildTime = 30, RequiresWorker = true })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 300, DefenseLayer = 200, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
HarvestedPerInterval = 1.5625f,
|
||||
RequiresWorker = false,
|
||||
Resource = ResourceType.Ether,
|
||||
Slots = 1,
|
||||
TotalAmount = 1200
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_LegionHall,
|
||||
new EntityModel(DataType.BUILDING_LegionHall, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Legion Hall",
|
||||
Descriptive = DescriptiveType.Training,
|
||||
Description =
|
||||
"""
|
||||
Trains Sipari, Zephyrs, Magi, and their Vanguard Replacements. Increases Population Capacity.
|
||||
""",
|
||||
FlavorText =
|
||||
"""
|
||||
"May the angels' army grow in size. May its many arms grow in strength. And may its soul grow in faith."<br/>
|
||||
—Steel Banner-Lord Sian
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntitySupplyModel { Grants = 16 })
|
||||
.AddPart(new EntityProductionModel { Alloy = 250, BuildTime = 38, RequiresWorker = true })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 600, DefenseLayer = 600, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedGround })
|
||||
},
|
||||
{
|
||||
DataType.DEFENSE_FireSinger,
|
||||
new EntityModel(DataType.DEFENSE_FireSinger, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Fire Singer",
|
||||
Descriptive = DescriptiveType.Defense,
|
||||
Description =
|
||||
"""
|
||||
Attacks ground/air targets. Does damage over time.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel { Alloy = 75, BuildTime = 30, RequiresWorker = true })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_KeeperOfTheHardenedFlames,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityWeaponModel
|
||||
{
|
||||
Damage = 34, Range = 700, SecondsBetweenAttacks = 1.8f, Targets
|
||||
= TargetType.All
|
||||
})
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedWeapons })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 350, DefenseLayer = 200, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_PsalmOfFire })
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_KeeperOfTheHardenedFlames,
|
||||
new EntityModel(DataType.BUILDING_KeeperOfTheHardenedFlames, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Keeper Of the Hardened Flames",
|
||||
Descriptive = DescriptiveType.Technology,
|
||||
Description =
|
||||
"""
|
||||
Unlocks Attack and Defense Upgrades for all units.<br/>
|
||||
Unlocks Fire Singer building and research.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "TAB", HoldSpace = true, HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel { Alloy = 100, BuildTime = 30, RequiresWorker = true })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 625, DefenseLayer = 625, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_Reliquary,
|
||||
new EntityModel(DataType.BUILDING_Reliquary, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Reliquary", Descriptive = DescriptiveType.Technology,
|
||||
Description =
|
||||
"""
|
||||
Unlocks training of Magi and their Vanguard replacements.<br/>
|
||||
Unlocks research for Sipari and their Vanguard replacements.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HoldSpace = true, HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 25, BuildTime = 30, RequiresWorker = true })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_LegionHall,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 700, DefenseLayer = 700, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.BUILDING_MonasteryOfIzur,
|
||||
new EntityModel(DataType.BUILDING_MonasteryOfIzur, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Monastery of Izur",
|
||||
Descriptive = DescriptiveType.Technology,
|
||||
Description =
|
||||
"""
|
||||
Unlocks training of Zephyrs and their Vanguard replacements.<br/>
|
||||
Unlocks research for Zephyrs and their Vanguard replacements.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HoldSpace = true, HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 50, BuildTime = 30, RequiresWorker = true })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_LegionHall,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 700, DefenseLayer = 700, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
DataType.BUILDING_SoulFoundry,
|
||||
new EntityModel(DataType.BUILDING_SoulFoundry, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Soul Foundry",
|
||||
Descriptive = DescriptiveType.Training,
|
||||
Description =
|
||||
"""
|
||||
Trains Hallowers, Castigators, Absolvers, and their Vanguard replacements. Increases Population Capacity.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 250, Ether = 100, BuildTime = 42, RequiresWorker = true })
|
||||
.AddPart(new EntitySupplyModel { Grants = 16 })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_LegionHall,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 900, DefenseLayer = 900, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedGround })
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_HouseOfFadingSaints,
|
||||
new EntityModel(DataType.BUILDING_HouseOfFadingSaints, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "House of the Fading Saints",
|
||||
Descriptive = DescriptiveType.Technology,
|
||||
Description =
|
||||
"""
|
||||
Unlocks training of Hallowers and their Vanguard replacements.
|
||||
""",
|
||||
FlavorText =
|
||||
"""
|
||||
"And soul pases unto the horizon, joining to the Holiest of Holies and the Host of Heaven."<br/>
|
||||
— Ceremony of the Passing
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HoldSpace = true, HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 100, BuildTime = 45, RequiresWorker = true })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_SoulFoundry,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 500, DefenseLayer = 500, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_Angelarium,
|
||||
new EntityModel(DataType.BUILDING_Angelarium, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Angelarium",
|
||||
Descriptive = DescriptiveType.Training,
|
||||
Description =
|
||||
"""
|
||||
Trains Sentinels, Wardens, Thrones, Shar'U and their Vanguard replacements. Increases Population Capacity.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 250, Ether = 150, BuildTime = 42, RequiresWorker = true })
|
||||
.AddPart(new EntitySupplyModel { Grants = 16 })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_SoulFoundry,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 950, DefenseLayer = 950, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedGround })
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_EyeOfAros,
|
||||
new EntityModel(DataType.BUILDING_EyeOfAros, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Eye of Aros",
|
||||
Descriptive = DescriptiveType.Technology,
|
||||
Description =
|
||||
"""
|
||||
Unlocks training of Shar'U.<br/>
|
||||
Unlocks research for Sipari, Shar'U, and their Vanguard replacements.
|
||||
""",
|
||||
FlavorText =
|
||||
"""
|
||||
"Where My gaze shall fall, blessed son and sun of Mine, you shall go forth
|
||||
and claim in Our Holy Cause. For you are My Lance and My Rook, and you shall never fail me."<br/>
|
||||
—Holy Aros to Orzum
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HoldSpace = true, HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 200, Ether = 250, BuildTime = 60, RequiresWorker = true })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_Angelarium,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 550, DefenseLayer = 550, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_BearerOfTheCrown,
|
||||
new EntityModel(DataType.BUILDING_BearerOfTheCrown, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Bearer of the Crown",
|
||||
Descriptive = DescriptiveType.Technology,
|
||||
Description =
|
||||
"""
|
||||
Unlocks training of Thrones.<br/>
|
||||
Unlocks research for: Thrones, Wardens, and their vanguard replacements.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HoldSpace = true, HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 250, Ether = 250, BuildTime = 70, RequiresWorker = true })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_Angelarium,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 625, DefenseLayer = 625, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
},
|
||||
// Building
|
||||
// Aru
|
||||
{
|
||||
DataType.BUILDING_GroveHeart,
|
||||
new EntityModel(DataType.BUILDING_GroveHeart, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Grove Heart", Descriptive = DescriptiveType.Stronghold,
|
||||
Description =
|
||||
"""
|
||||
Collect Alloy and Ether.<br/>
|
||||
If all your Strongholds are destroyed, you lose the game.
|
||||
"""
|
||||
}) //TODO: Add Alloy, Ether and Pyre, Supply to the database
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel { Alloy = 500, BuildTime = 90, RequiresWorker = true })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{
|
||||
Health = 2000, DefenseLayer = 400, Defense = DefenseType.Overgrowth, Armor = ArmorType.Heavy,
|
||||
IsStructure = true
|
||||
})
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
HarvestedPerInterval = 1, RequiresWorker = true, Resource = ResourceType.Alloy, Slots = 2,
|
||||
TotalAmount = 3600
|
||||
})
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Rootway })
|
||||
},
|
||||
{
|
||||
DataType.BUPGRADE_GodHeart,
|
||||
new EntityModel(DataType.BUPGRADE_GodHeart, EntityType.Building_Upgrade)
|
||||
.AddPart(new EntityInfoModel { Name = "God Heart", Descriptive = DescriptiveType.Upgrade })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "A", HotkeyGroup = "CONTROL" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 3100, DefenseLayer = 900, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.STARTING_TownHall_Aru,
|
||||
Requirement = RequirementType.Morph
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_AltarOfTheWorthy,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 200, Ether = 100, BuildTime = 60, RequiresWorker = false })
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Rootway })
|
||||
},
|
||||
{
|
||||
DataType.BUPGRADE_MiningLevel2_Aru,
|
||||
new EntityModel(DataType.BUPGRADE_MiningLevel2_Aru, EntityType.Building_Upgrade)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Mining Level 2", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Upgrades the nearest resource cluster to allow more workers to mine from it."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "CONTROL" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_GroveHeart,
|
||||
Requirement = RequirementType.Morph
|
||||
})
|
||||
.AddPart(new EntityProductionModel { Alloy = 400, BuildTime = 20, RequiresWorker = false })
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
HarvestedPerInterval = 1, RequiresWorker = true, Resource = ResourceType.Alloy, Slots = 2,
|
||||
TotalAmount = 3600
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_EtherMaw,
|
||||
new EntityModel(DataType.BUILDING_EtherMaw, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Ether Maw",
|
||||
Descriptive = DescriptiveType.Economy,
|
||||
Description = "Automatically harvest Ether.",
|
||||
FlavorText =
|
||||
"""
|
||||
"The Matriarchs tell of where the ether goes. ARound Lacuathon's trunk swirls a great aurora of
|
||||
this world's lifeblood, flowing upwards into the half-eaten sky."<br/>
|
||||
—Hunter Ipotzil
|
||||
"""
|
||||
}) //TODO Add Ether Node to database
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "TAB", HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel { Alloy = 225, BuildTime = 30, RequiresWorker = true })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 400, DefenseLayer = 225, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
HarvestedPerInterval = 1.5625f, RequiresWorker = false, Resource = ResourceType.Ether,
|
||||
Slots = 1, TotalAmount = 1200
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_AltarOfTheWorthy,
|
||||
new EntityModel(DataType.BUILDING_AltarOfTheWorthy, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Altar of the Worthy",
|
||||
Descriptive = DescriptiveType.Training,
|
||||
Description =
|
||||
"""
|
||||
Trains Masked Hunters, Xacal, Underspins, Ichors, and their Vanguard replacements. Increases Population Capacity.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntitySupplyModel { Grants = 16 })
|
||||
.AddPart(new EntityProductionModel { Alloy = 300, BuildTime = 38, RequiresWorker = true })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 1000, DefenseLayer = 500, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Rootway })
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_Neurocyte,
|
||||
new EntityModel(DataType.BUILDING_Neurocyte, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Neurocyte",
|
||||
Descriptive = DescriptiveType.Technology,
|
||||
Description =
|
||||
"""
|
||||
Unlocks training of Xacal, Underspines, Red Seers, Brood Anchors, Aarox, Behemoths, and their
|
||||
Vanguard replacements.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HoldSpace = true, HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 75, BuildTime = 30, RequiresWorker = true })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_AltarOfTheWorthy,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 800, DefenseLayer = 200, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_RootCradle,
|
||||
new EntityModel(DataType.BUILDING_RootCradle, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Root Cradle",
|
||||
Descriptive = DescriptiveType.Technology,
|
||||
Description = "Unlocks Attack and Defense Upgrades for all units.",
|
||||
FlavorText =
|
||||
"""
|
||||
Grinding and deep, Laculathon's Rootsong speaks of a time fast apporaching where the Worthy
|
||||
shall rise and pierce the heart of the Wretched Sun.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Tab", HoldSpace = true, HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 150, Ether = 0, BuildTime = 30, RequiresWorker = true })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_AltarOfTheWorthy,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 900, DefenseLayer = 300, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
},
|
||||
{
|
||||
DataType.DEFENSE_Aerovore,
|
||||
new EntityModel(DataType.DEFENSE_Aerovore, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Aerovore",
|
||||
Descriptive = DescriptiveType.Defense,
|
||||
Description = "Attacks air targets."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel { Alloy = 75, BuildTime = 18, RequiresWorker = true })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 300, DefenseLayer = 150, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityWeaponModel
|
||||
{ Damage = 65, Range = 700, SecondsBetweenAttacks = 1.35f, Targets = TargetType.Air })
|
||||
},
|
||||
{
|
||||
DataType.BUPGRADE_Omnivore,
|
||||
new EntityModel(DataType.BUPGRADE_Omnivore, EntityType.Building_Upgrade)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Omnivore",
|
||||
Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Attacks ground/air targets."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "SHIFT" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.DEFENSE_Aerovore,
|
||||
Requirement = RequirementType.Morph
|
||||
})
|
||||
.AddPart(new EntityProductionModel { Alloy = 75, BuildTime = 18, RequiresWorker = false })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 400, DefenseLayer = 150, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityWeaponModel
|
||||
{ Damage = 65, Range = 700, SecondsBetweenAttacks = 1.35f, Targets = TargetType.Air })
|
||||
.AddPart(new EntityWeaponModel
|
||||
{
|
||||
Damage = 52, MediumDamage = 61, HeavyDamage = 70, Range = 700, SecondsBetweenAttacks = 1.35f,
|
||||
Targets = TargetType.Ground
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_AmberWomb,
|
||||
new EntityModel(DataType.BUILDING_AmberWomb, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Amber Womb",
|
||||
Descriptive = DescriptiveType.Training,
|
||||
Description =
|
||||
"""
|
||||
Trains Wraith Bows, Brood Anchors, and their Vanguard replacements. Increases Population Capacity.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 300, Ether = 100, BuildTime = 42, RequiresWorker = true })
|
||||
.AddPart(new EntitySupplyModel { Grants = 16 })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_AltarOfTheWorthy,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUPGRADE_GodHeart,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 1000, DefenseLayer = 600, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Rootway })
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_BoneCanopy,
|
||||
new EntityModel(DataType.BUILDING_BoneCanopy, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Bone Canopy",
|
||||
Descriptive = DescriptiveType.Training,
|
||||
Description =
|
||||
"""
|
||||
Trains Aarox, Thrums, Behemoths, and their Vanguard replacements. Increases Population Capacity.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 300, Ether = 150, BuildTime = 42, RequiresWorker = true })
|
||||
.AddPart(new EntitySupplyModel { Grants = 16 })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUPGRADE_GodHeart,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 1100, DefenseLayer = 650, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Rootway })
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_MurderHollow,
|
||||
new EntityModel(DataType.BUILDING_MurderHollow, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Murder Hollow", Descriptive = DescriptiveType.Technology,
|
||||
Description =
|
||||
"""
|
||||
Unlocks Ichor training and research.
|
||||
""",
|
||||
FlavorText =
|
||||
"""
|
||||
"Foolish Otapeke thought himself the master of Her children. Now he eases the acid
|
||||
in their bellies. Stay for from the Hollow when they howl like that."<br/>
|
||||
—Huntmaster Etatzli to his initiates
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HoldSpace = true, HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 75, Ether = 50, BuildTime = 45, RequiresWorker = true })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_AltarOfTheWorthy,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 1000, DefenseLayer = 400, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_RedVale,
|
||||
new EntityModel(DataType.BUILDING_RedVale, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Red Vale",
|
||||
Descriptive = DescriptiveType.Technology,
|
||||
Description =
|
||||
"""
|
||||
Unlock training of Red Seer and their Vanguard replacements.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HoldSpace = true, HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 150, Ether = 175, BuildTime = 60, RequiresWorker = true })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_AltarOfTheWorthy,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 1000, DefenseLayer = 500, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
},
|
||||
{
|
||||
DataType.BUILDING_DeepNest,
|
||||
new EntityModel(DataType.BUILDING_DeepNest, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Deep Nest",
|
||||
Descriptive = DescriptiveType.Technology,
|
||||
Description =
|
||||
"""
|
||||
Unlock training of Behemoth and their Vanguard replacements.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HoldSpace = true, HotkeyGroup = "C" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 225, Ether = 175, BuildTime = 70, RequiresWorker = true })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_BoneCanopy,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 1000, DefenseLayer = 500, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,524 @@
|
||||
using System.Collections.Generic;
|
||||
using Model.Entity.Parts;
|
||||
using Model.Entity.Types;
|
||||
using Model.Types;
|
||||
|
||||
namespace Model.Entity.Data;
|
||||
|
||||
public partial class EntityData
|
||||
{
|
||||
public static Dictionary<string, EntityModel> GetImmortalData()
|
||||
{
|
||||
return new Dictionary<string, EntityModel>
|
||||
{
|
||||
// Immortals
|
||||
// Aru
|
||||
{
|
||||
DataType.IMMORTAL_Atzlan,
|
||||
new EntityModel(DataType.IMMORTAL_Atzlan, EntityType.Immortal)
|
||||
.AddPart(new EntityInfoModel { Name = "Atzlan" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
Resource = ResourceType.Pyre, HarvestedPerInterval = 1, HarvestDelay = 3,
|
||||
RequiresWorker = false, Slots = 1, TotalAmount = -1
|
||||
})
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.IPASSIVE_GreenThumb })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_SummonGroveGuardian })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_ProphetOfTheRoots })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_WallOfRoots })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_SummonDeepWyrm })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_SummonRootBud })
|
||||
.AddPart(new EntityIdVanguardModel { Id = DataType.VANGUARD_RootShepard_Atzlan })
|
||||
.AddPart(new EntityIdVanguardModel { Id = DataType.VANGUARD_Resinant_Atzlan })
|
||||
},
|
||||
{
|
||||
DataType.IMMORTAL_Mala,
|
||||
new EntityModel(DataType.IMMORTAL_Mala, EntityType.Immortal)
|
||||
.AddPart(new EntityInfoModel { Name = "Mala" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
Resource = ResourceType.Pyre, HarvestedPerInterval = 1, HarvestDelay = 3,
|
||||
RequiresWorker = false, Slots = 1, TotalAmount = -1
|
||||
})
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.IPASSIVE_MothersHunger })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_SummonGroveGuardian })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_RedHarvest })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_ProphetsFavor })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_RainOfBlood })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_ConstructBloodWell })
|
||||
.AddPart(new EntityIdVanguardModel { Id = DataType.VANGUARD_Incubator_Mala })
|
||||
.AddPart(new EntityIdVanguardModel { Id = DataType.VANGUARD_DreadSister_Mala })
|
||||
},
|
||||
{
|
||||
DataType.IMMORTAL_Xol,
|
||||
new EntityModel(DataType.IMMORTAL_Xol, EntityType.Immortal)
|
||||
.AddPart(new EntityInfoModel { Name = "Xol" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
Resource = ResourceType.Pyre, HarvestedPerInterval = 1, HarvestDelay = 3,
|
||||
RequiresWorker = false, Slots = 1, TotalAmount = -1
|
||||
})
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.IPASSIVE_StalkersSense })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_SummonGroveGuardian })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_ProphetOfTheHunt })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_HuntingGrounds })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_TheGreatHunt })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_ConstructBloodWell })
|
||||
.AddPart(new EntityIdVanguardModel { Id = DataType.VANGUARD_BoneStalker_Xol })
|
||||
.AddPart(new EntityIdVanguardModel { Id = DataType.VANGUARD_WhiteWoodReaper_Xol })
|
||||
},
|
||||
// Immortals
|
||||
// Q'Rath
|
||||
{
|
||||
DataType.IMMORTAL_Ajari,
|
||||
new EntityModel(DataType.IMMORTAL_Ajari, EntityType.Immortal)
|
||||
.AddPart(new EntityInfoModel { Name = "Ajari" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
Resource = ResourceType.Pyre, HarvestedPerInterval = 1, HarvestDelay = 3,
|
||||
RequiresWorker = false, Slots = 1, TotalAmount = -1
|
||||
})
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.IPASSIVE_MendingGrace })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_SummonCitadel })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_HeavensAegis })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_DeliverFromEvil })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_Salvation })
|
||||
.AddPart(new EntityIdVanguardModel { Id = DataType.VANGUARD_Saoshin_Ajari })
|
||||
.AddPart(new EntityIdVanguardModel { Id = DataType.VANGUARD_ArkMother_Ajari })
|
||||
},
|
||||
{
|
||||
DataType.IMMORTAL_Orzum,
|
||||
new EntityModel(DataType.IMMORTAL_Orzum, EntityType.Immortal)
|
||||
.AddPart(new EntityInfoModel { Name = "Orzum" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
Resource = ResourceType.Pyre, HarvestedPerInterval = 1, HarvestDelay = 3,
|
||||
RequiresWorker = false, Slots = 1, TotalAmount = -1
|
||||
})
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.IPASSIVE_OrdainedConquest })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_RookOfIra })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_SummonCitadel })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_EmpireUnbroken })
|
||||
.AddPart(new EntityIdPyreSpellModel { Id = DataType.ISPELL_PillarOfHeaven })
|
||||
.AddPart(new EntityIdVanguardModel { Id = DataType.VANGUARD_Zentari_Orzum })
|
||||
.AddPart(new EntityIdVanguardModel { Id = DataType.VANGUARD_Sceptre_Orzum })
|
||||
},
|
||||
|
||||
// Immortal Passives
|
||||
{
|
||||
DataType.IPASSIVE_MendingGrace,
|
||||
new EntityModel(DataType.IPASSIVE_MendingGrace, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Mending Grace",
|
||||
Description =
|
||||
"Friendly units heal on Hallowed Ground. Units get an initial burst of healing after a delay, then heal over time."
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.IPASSIVE_OrdainedConquest,
|
||||
new EntityModel(DataType.IPASSIVE_OrdainedConquest, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Ordained Conquest",
|
||||
Description =
|
||||
"""
|
||||
Citadels generate Pyre passively. Execute towers below 20% HP.
|
||||
"""
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.IPASSIVE_MothersHunger,
|
||||
new EntityModel(DataType.IPASSIVE_MothersHunger, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Mother's Hunger",
|
||||
Description =
|
||||
"""
|
||||
Units that die near Blood Wells, Grove Guardians, Incubators, Dread Sisters, Mala's Specter,
|
||||
and units that dies during Rain of Blood give Sacral Blood.
|
||||
"""
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.IPASSIVE_StalkersSense,
|
||||
new EntityModel(DataType.IPASSIVE_StalkersSense, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Stalker's Sense",
|
||||
Description = "Increases the vision range of your units by 1."
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.IPASSIVE_GreenThumb,
|
||||
new EntityModel(DataType.IPASSIVE_GreenThumb, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Green Thumb",
|
||||
Description = "Friendly units near Atzlan heal over time."
|
||||
})
|
||||
},
|
||||
|
||||
|
||||
// Pyre Spells
|
||||
// Q'Rath
|
||||
{
|
||||
DataType.ISPELL_SummonCitadel,
|
||||
new EntityModel(DataType.ISPELL_SummonCitadel, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Summon Citadel",
|
||||
Description =
|
||||
"""
|
||||
Target a Phyric Foundation. Summon a Citadel.<br/>
|
||||
Can attack ground and air. Heals friendly units. Does not require a worker.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel { Pyre = 50, BuildTime = 70 })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 1000, DefenseLayer = 500, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityWeaponModel
|
||||
{
|
||||
Damage = 20, Range = 800, AttacksPerSecond = 1.124f, Targets = TargetType.All,
|
||||
MediumDamage = 25, HeavyDamage = 30
|
||||
})
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Respite })
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedGround })
|
||||
},
|
||||
// Orzum
|
||||
{
|
||||
DataType.ISPELL_RookOfIra,
|
||||
new EntityModel(DataType.ISPELL_RookOfIra, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Rook of Ira",
|
||||
Description =
|
||||
"Target a location to summon a Rook of Ira."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Orzum })
|
||||
.AddPart(new EntityProductionModel { Pyre = 125, Cooldown = 90, BuildTime = 3 })
|
||||
},
|
||||
{
|
||||
DataType.ISPELL_EmpireUnbroken,
|
||||
new EntityModel(DataType.ISPELL_EmpireUnbroken, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Empire Unbroken",
|
||||
Description =
|
||||
"""
|
||||
Targe an area. Buildings get damage reduction then heal.<br/>
|
||||
Temporary. Healing happens at the end of the spell.<br/>
|
||||
Temporarily turns Citadels into Rooks of Ira.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Orzum })
|
||||
.AddPart(new EntityProductionModel { Pyre = 50, Cooldown = 120 })
|
||||
},
|
||||
{
|
||||
DataType.ISPELL_PillarOfHeaven,
|
||||
new EntityModel(DataType.ISPELL_PillarOfHeaven, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Pillar of the Heavens",
|
||||
Description =
|
||||
"""
|
||||
Target an area to deal massive area damage.<br/>
|
||||
Creates temporary <b>Hallowed Ground</b> that improves friendly unit attack speed.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Orzum })
|
||||
.AddPart(new EntityProductionModel { Pyre = 150, Cooldown = 120 })
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Zeal })
|
||||
},
|
||||
// Ajari
|
||||
{
|
||||
DataType.ISPELL_HeavensAegis,
|
||||
new EntityModel(DataType.ISPELL_HeavensAegis, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Heaven's Aegis",
|
||||
Description =
|
||||
"""
|
||||
Target a friendly unit. It gets movement speed and Shields.<br/>
|
||||
Periodically saves charges, up to a cap.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Ajari })
|
||||
.AddPart(new EntityProductionModel { Pyre = 25, Cooldown = 0.5f })
|
||||
},
|
||||
{
|
||||
DataType.ISPELL_DeliverFromEvil,
|
||||
new EntityModel(DataType.ISPELL_DeliverFromEvil, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Deliver from Evil",
|
||||
Description =
|
||||
"""
|
||||
Target an area. Friendly units teleport to your nearest Acropolis.<br/>
|
||||
Units get Shields for a few seconds before teleporting.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Ajari })
|
||||
.AddPart(new EntityProductionModel { Pyre = 50, Cooldown = 60 })
|
||||
},
|
||||
{
|
||||
DataType.ISPELL_Salvation,
|
||||
new EntityModel(DataType.ISPELL_Salvation, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Salvation",
|
||||
Description =
|
||||
"""
|
||||
Target a location. Summon Ajari's Specter to prevent death.<br/>
|
||||
Saved units are brought back to Ajari's location at the end of the duration. Destroying Ajari's
|
||||
Urn ends the effect and prevents Saved units from coming back.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Ajari })
|
||||
.AddPart(new EntityProductionModel { Pyre = 175, Cooldown = 45 })
|
||||
},
|
||||
// Immortal Spells
|
||||
// Aru
|
||||
{
|
||||
DataType.ISPELL_SummonGroveGuardian,
|
||||
new EntityModel(DataType.ISPELL_SummonGroveGuardian, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Summon Grove Guardian",
|
||||
Description = "Creates a powerful defensive structure on a Tower Foundation."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel { Pyre = 50, BuildTime = 70 })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 1850, DefenseLayer = 450, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityWeaponModel
|
||||
{ Damage = 19, Range = 800, AttacksPerSecond = 1.887f, Targets = TargetType.All })
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Respite })
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Rootway })
|
||||
},
|
||||
{
|
||||
DataType.ISPELL_ConstructBloodWell,
|
||||
new EntityModel(DataType.ISPELL_ConstructBloodWell, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Construct Blood Well",
|
||||
Description =
|
||||
"Creates a rootway generating structure that heals nearby allied units, and transfers it's blood to nearby allied units."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel { Pyre = 25, Cooldown = 21, BuildTime = 3 })
|
||||
.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 })
|
||||
},
|
||||
// Atzlan
|
||||
{
|
||||
DataType.ISPELL_ProphetOfTheRoots,
|
||||
new EntityModel(DataType.ISPELL_ProphetOfTheRoots, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Prophet Of The Roots",
|
||||
Description =
|
||||
"Moves Atzlan to target Root Bud. Blesses the Root Bud to give units nearby more overgrowth Atzlan heals nearby units."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel { Pyre = 25, Cooldown = 3 })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 25, DefenseLayer = 50, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
},
|
||||
{
|
||||
DataType.ISPELL_WallOfRoots,
|
||||
new EntityModel(DataType.ISPELL_WallOfRoots, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Wall of Roots",
|
||||
Description =
|
||||
"""
|
||||
Spawn a Wall of Roots that blocks ground pathing.<br/>
|
||||
Click and drag to "draw" the wall. Wall takes damage over time when it's off rootway.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel { Pyre = 50, Cooldown = 10 })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 25, DefenseLayer = 50, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.ISPELL_SummonDeepWyrm,
|
||||
new EntityModel(DataType.ISPELL_SummonDeepWyrm, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Summon Deep Wyrm",
|
||||
Description =
|
||||
"""
|
||||
The Deep Wyrm roams in the area and attacks any ground enemy unit entering the rootway.<br/>
|
||||
Spawns rootway on summon. The Deep Wyrm is invulnerable and uncontrollable.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel { Pyre = 150, Cooldown = 55 })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 25, DefenseLayer = 50, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.ISPELL_SummonRootBud,
|
||||
new EntityModel(DataType.ISPELL_SummonRootBud, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Summon Root Bud",
|
||||
Description =
|
||||
"""
|
||||
Generates Rootway<br/>
|
||||
Must be placed on Rootway and in range of another Root Bud or Stronghold
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel { Cooldown = 25 })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 25, DefenseLayer = 50, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
},
|
||||
|
||||
|
||||
// Mala
|
||||
{
|
||||
DataType.ISPELL_RedHarvest,
|
||||
new EntityModel(DataType.ISPELL_RedHarvest, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Red Harvest",
|
||||
Description =
|
||||
"""
|
||||
Target a location to summon Mala's Specter.<br/>
|
||||
Nearby units make Quitl and give Sacral Blood on-death.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Mala })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel { Pyre = 75, Cooldown = 90 })
|
||||
},
|
||||
{
|
||||
DataType.ISPELL_ProphetsFavor,
|
||||
new EntityModel(DataType.ISPELL_ProphetsFavor, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Prophet's Favor",
|
||||
Description =
|
||||
"""
|
||||
Target an area. Permanently empower units.<br/>
|
||||
Empowered units get more HP and damage. Does not stack.<br/>
|
||||
Costs Sacral Blood. Get Sacral Blood from Mala's other Powers and Vanguard.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Mala })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel { Cooldown = 5 })
|
||||
},
|
||||
{
|
||||
DataType.ISPELL_RainOfBlood,
|
||||
new EntityModel(DataType.ISPELL_RainOfBlood, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Rain of Blood",
|
||||
Description =
|
||||
"Rains blood from the sky for 30 seconds. Massively increases global life regeneration for allied troops. Allies anywhere also have significantly increased blood regeneration."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Mala })
|
||||
.AddPart(new EntityProductionModel { Pyre = 150, Cooldown = 30 })
|
||||
},
|
||||
// Xol
|
||||
{
|
||||
DataType.ISPELL_ProphetOfTheHunt,
|
||||
new EntityModel(DataType.ISPELL_ProphetOfTheHunt, ImmortalSpellType.Combat)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Prophet Of The Hunt",
|
||||
Description =
|
||||
"""
|
||||
Target a location to summon Xol.
|
||||
|
||||
Get XP by assiting in kills. Level up to become more powerful.
|
||||
""",
|
||||
Notes =
|
||||
" - Summons Xol (stealthed) after a 1 second delay.<br/> - Xol has 225 Life, 75 Shield, 410 Move speed, 70 Radius.<br/> - Xol has a weapon: 28 Damage, 1s Cooldown, 7 Range, Ground and Air.<br/> - Every 4 attacks, the next attack will deal 22 bonus damage.<br/>"
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Xol })
|
||||
.AddPart(new EntityProductionModel { Cooldown = 50, Pyre = 50 })
|
||||
},
|
||||
{
|
||||
DataType.ISPELL_HuntingGrounds,
|
||||
new EntityModel(DataType.ISPELL_HuntingGrounds, ImmortalSpellType.Combat)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Hunting Grounds",
|
||||
Description =
|
||||
"""
|
||||
Target an area. Friendly units there become Hidden.
|
||||
|
||||
After becoming Hidden, friendly units get more movement speed, attack speed, and damage on their first attack.
|
||||
Hunting Grounds disappears 5 seconds after a unit inside attacks.
|
||||
""",
|
||||
Notes =
|
||||
" - After a 10 second delay, creates a large ambush area which lasts until after an ambush is sprung.<br/> - Units in the Hunting Ground become stealth.<br/> - Stealth units will deal double damage on the first attack and spring the ambush.<br/> - The Hunting Ground will disappear 5 seconds after an ambush is sprung.<br/> - Units can only get the bonus once."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Xol })
|
||||
.AddPart(new EntityProductionModel { Cooldown = 10, Pyre = 25 })
|
||||
},
|
||||
{
|
||||
DataType.ISPELL_TheGreatHunt,
|
||||
new EntityModel(DataType.ISPELL_TheGreatHunt, EntityType.Pyre_Spell)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "The Great Hunt",
|
||||
Description =
|
||||
"""
|
||||
Activate to reduce enemy vision range and give friendly units movement speed.
|
||||
Affects all units. Friendly units get attack speed on-kill
|
||||
""",
|
||||
Notes =
|
||||
" - Reduces enemy vision to 6 range (used to be 3).<br/> - After a 3 second delay, the hunt begins. The hunt lasts 20 seconds.<br/> - Summons Xol (moveable) to lead the hunt.<br/> - Units gain a 70% decaying move speed bonus for 6 seconds.<br/> - While on the hunt killing an enemy will cause a frenzy giving the killer double attack speed for 7 seconds <br/> - Adds duration timer to Xol"
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "V" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityVanguardAddedModel { ImmortalId = DataType.IMMORTAL_Xol })
|
||||
.AddPart(new EntityProductionModel { Pyre = 175, Cooldown = 50 })
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,387 @@
|
||||
using System.Collections.Generic;
|
||||
using Model.Entity.Parts;
|
||||
using Model.Entity.Types;
|
||||
using Model.Types;
|
||||
|
||||
namespace Model.Entity.Data;
|
||||
|
||||
public partial class EntityData
|
||||
{
|
||||
public static Dictionary<string, EntityModel> GetMiscData()
|
||||
{
|
||||
return new Dictionary<string, EntityModel>
|
||||
{
|
||||
// Neutrals
|
||||
|
||||
// Pyre Events
|
||||
{
|
||||
DataType.PYREEVENT_CampTaken, new EntityModel(DataType.PYREEVENT_CampTaken, EntityType.Pyre_Event)
|
||||
.AddPart(new EntityInfoModel { Name = "Pyre Camp", Description = "Provides 25 when taken." })
|
||||
.AddPart(new EntityPyreRewardModel { BaseReward = 25 })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "2" })
|
||||
},
|
||||
{
|
||||
DataType.PYREEVENT_MinerTaken, new EntityModel(DataType.PYREEVENT_MinerTaken, EntityType.Pyre_Event)
|
||||
.AddPart(new EntityInfoModel { Name = "Pyre Camp", Description = "Provides 90 when taken." })
|
||||
.AddPart(new EntityPyreRewardModel
|
||||
{ BaseReward = 0, OverTimeRewardDuration = 90, OverTimeReward = 1 })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "2" })
|
||||
},
|
||||
{
|
||||
DataType.PYREEVENT_TowerKilled, new EntityModel(DataType.PYREEVENT_TowerKilled, EntityType.Pyre_Event)
|
||||
.AddPart(new EntityInfoModel { Name = "Tower Taken", Description = "Provides 10 when destroyed." })
|
||||
.AddPart(new EntityPyreRewardModel { BaseReward = 10 })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "2" })
|
||||
},
|
||||
|
||||
// TEAPOTS
|
||||
{
|
||||
DataType.TEAPOT_Teapot, new EntityModel(DataType.TEAPOT_Teapot, EntityType.Teapot)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Teapot", Description = "Basic scout. Every faction has this",
|
||||
Notes = @"Very powerful! So Fast"
|
||||
})
|
||||
.AddPart(new EntityVitalityModel { Health = 120, Armor = ArmorType.Light })
|
||||
.AddPart(new EntityMovementModel { Speed = 400 })
|
||||
},
|
||||
{
|
||||
DataType.TEAPOT_FlyingTeapot, new EntityModel(DataType.TEAPOT_FlyingTeapot, EntityType.Teapot)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Detector",
|
||||
Description = "Has 1100 vision, and can see hidden units within 1000 range.",
|
||||
Notes = @"Doesn't take up a scout slot."
|
||||
})
|
||||
.AddPart(new EntityRequirementModel { Id = DataType.TEAPOT_Teapot })
|
||||
.AddPart(new EntitySupplyModel { Takes = 1 })
|
||||
.AddPart(new EntityProductionModel { Alloy = 100, Ether = 50 })
|
||||
.AddPart(new EntityVitalityModel { Health = 120, DefenseLayer = 80, Armor = ArmorType.Light })
|
||||
.AddPart(new EntityMovementModel { Speed = 280, Movement = MovementType.Air })
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Detection })
|
||||
},
|
||||
|
||||
// Families
|
||||
{
|
||||
DataType.FAMILY_Rae,
|
||||
new EntityModel(DataType.FAMILY_Rae, EntityType.Family, true)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Rae"
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.FAMILY_Sylv,
|
||||
new EntityModel(DataType.FAMILY_Sylv, EntityType.Family, true)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Sylv"
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.FAMILY_Angelic,
|
||||
new EntityModel(DataType.FAMILY_Angelic, EntityType.Family, true)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Angelic"
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.FAMILY_Human,
|
||||
new EntityModel(DataType.FAMILY_Human, EntityType.Family, true)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Human"
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.FAMILY_Coalition,
|
||||
new EntityModel(DataType.FAMILY_Coalition, EntityType.Family, true)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Coalition?"
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.FAMILY_Demonic,
|
||||
new EntityModel(DataType.FAMILY_Demonic, EntityType.Family, true)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Demonic?"
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.FAMILY_NazRa,
|
||||
new EntityModel(DataType.FAMILY_NazRa, EntityType.Family, true)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Naz'Ra"
|
||||
})
|
||||
},
|
||||
// Factions
|
||||
// Sylv
|
||||
{
|
||||
DataType.FACTION_Aru,
|
||||
new EntityModel(DataType.FACTION_Aru, EntityType.Faction)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Aru"
|
||||
})
|
||||
.AddPart(new EntityPassiveModel
|
||||
{
|
||||
Name = "Overgrowth",
|
||||
Description =
|
||||
"Your units have an extra layer of health a regens rapidly when a unit hasn't been damaged recently. This regen is doubled on rootway."
|
||||
})
|
||||
.AddPart(new EntityPassiveModel
|
||||
{
|
||||
Name = "Blood",
|
||||
Description =
|
||||
"Your casters passively get blood for spells. This blood regen rate is increased on rootway. Your casters can also spend their own life as blood. (Spending health as blood is currenly not in game.)"
|
||||
})
|
||||
.AddPart(new EntityPassiveModel
|
||||
{
|
||||
Name = "Blood Wells",
|
||||
Description =
|
||||
"You can summon blood wells for pyre, that allow you to heal your units health and mana."
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.FACTION_Iratek,
|
||||
new EntityModel(DataType.FACTION_Iratek, EntityType.Faction, true)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Iratek"
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.FACTION_Yul,
|
||||
new EntityModel(DataType.FACTION_Yul, EntityType.Faction, true)
|
||||
.AddPart(new EntityInfoModel { Name = "Yul" })
|
||||
},
|
||||
// Factions
|
||||
// Angelic
|
||||
{
|
||||
DataType.FACTION_QRath,
|
||||
new EntityModel(DataType.FACTION_QRath, EntityType.Faction)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Q'Rath",
|
||||
Notes =
|
||||
"Angelic faction that has adopted many humans into their ranks. They seek to bring more into their collective."
|
||||
})
|
||||
.AddPart(new EntityPassiveModel
|
||||
{
|
||||
Name = "Wards",
|
||||
Description =
|
||||
"Your units have an extra layer of health that is always (but slowly) regenerates. The regeneration is double on Hallowed Ground."
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.FACTION_YRiah,
|
||||
new EntityModel(DataType.FACTION_QRath, EntityType.Faction, true)
|
||||
.AddPart(new EntityInfoModel { Name = "R'Raih" })
|
||||
},
|
||||
{
|
||||
DataType.FACTION_ArkShai,
|
||||
new EntityModel(DataType.FACTION_QRath, EntityType.Faction, true)
|
||||
.AddPart(new EntityInfoModel
|
||||
{ Name = "Ark'Shai" })
|
||||
},
|
||||
// Factions
|
||||
// Human
|
||||
{
|
||||
DataType.FACTION_Jora,
|
||||
new EntityModel(DataType.FACTION_Jora, EntityType.Faction, true)
|
||||
.AddPart(new EntityInfoModel { Name = "Jora" })
|
||||
},
|
||||
{
|
||||
DataType.FACTION_Telmetra,
|
||||
new EntityModel(DataType.FACTION_Telmetra, EntityType.Faction, true)
|
||||
.AddPart(new EntityInfoModel { Name = "Talmetra" })
|
||||
},
|
||||
{
|
||||
DataType.FACTION_Kjor,
|
||||
new EntityModel(DataType.FACTION_Kjor, EntityType.Faction, true)
|
||||
.AddPart(new EntityInfoModel
|
||||
{ Name = "Kjor" }
|
||||
)
|
||||
},
|
||||
// Factions
|
||||
// Rae
|
||||
{
|
||||
DataType.FACTION_Herlesh,
|
||||
new EntityModel(DataType.FACTION_Herlesh, EntityType.Faction, true)
|
||||
.AddPart(new EntityInfoModel { Name = "Herlesh" })
|
||||
},
|
||||
// Factions
|
||||
// Coalition
|
||||
{
|
||||
DataType.FACTION_Khardu,
|
||||
new EntityModel(DataType.FACTION_Khardu, EntityType.Faction, true)
|
||||
.AddPart(new EntityInfoModel { Name = "Khardu" })
|
||||
},
|
||||
// Factions
|
||||
// Neutral
|
||||
{
|
||||
DataType.FACTION_Neutral,
|
||||
new EntityModel(DataType.FACTION_Neutral, EntityType.Faction)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Neutral"
|
||||
})
|
||||
},
|
||||
|
||||
// Keys
|
||||
{
|
||||
DataType.COMMAND_Attack,
|
||||
new EntityModel(DataType.COMMAND_Attack, EntityType.Command)
|
||||
.AddPart(new EntityInfoModel
|
||||
{ Name = "Attack", Description = "Makes selected units attack targeted area." })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "A", HotkeyGroup = "D" })
|
||||
},
|
||||
{
|
||||
DataType.COMMAND_StandGround,
|
||||
new EntityModel(DataType.COMMAND_StandGround, EntityType.Command)
|
||||
.AddPart(new EntityInfoModel
|
||||
{ Name = "Stand Ground", Description = "Makes selected units stop moving." })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "S", HotkeyGroup = "D" })
|
||||
},
|
||||
// Starting Structures
|
||||
{
|
||||
DataType.STARTING_Bastion,
|
||||
new EntityModel(DataType.STARTING_Bastion, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Bastion",
|
||||
Description = "Provides a fully upgraded base worth of alloy.",
|
||||
Notes = "Revives in 40 seconds when destroyed."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Neutral })
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
HarvestedPerInterval = 6, RequiresWorker = false, Resource = ResourceType.Alloy, Slots = 1,
|
||||
TotalAmount = 6000
|
||||
})
|
||||
.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 })
|
||||
},
|
||||
{
|
||||
DataType.STARTING_Tower,
|
||||
new EntityModel(DataType.STARTING_Tower, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Starting Tower",
|
||||
Notes = "Currently not in game. Can be upgraded to the factions pyre tower."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Neutral })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Neutral })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 1000, DefenseLayer = 500, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityWeaponModel
|
||||
{
|
||||
Damage = 20, Range = 800, AttacksPerSecond = 1.124f, Targets = TargetType.All,
|
||||
MediumDamage = 25, HeavyDamage = 30
|
||||
})
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Respite })
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedGround })
|
||||
},
|
||||
// Starting Structures
|
||||
// Aru
|
||||
{
|
||||
DataType.STARTING_TownHall_Aru,
|
||||
new EntityModel(DataType.STARTING_TownHall_Aru, EntityType.Building, true)
|
||||
.AddPart(new EntityInfoModel
|
||||
{ Name = "Grove Heart (Starting)", Descriptive = DescriptiveType.TownHall_Starting })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 2000, DefenseLayer = 400, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
HarvestedPerInterval = 1, RequiresWorker = true, Resource = ResourceType.Alloy, Slots = 6,
|
||||
TotalAmount = 6000
|
||||
})
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_Rootway })
|
||||
},
|
||||
// Starting Structures
|
||||
// Q'Rath
|
||||
{
|
||||
DataType.STARTING_TownHall_QRath,
|
||||
new EntityModel(DataType.STARTING_TownHall_QRath, EntityType.Building, true)
|
||||
.AddPart(new EntityInfoModel
|
||||
{ Name = "Acropolis (Starting)", Descriptive = DescriptiveType.TownHall_Starting })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityVitalityModel
|
||||
{ Health = 1600, DefenseLayer = 800, Armor = ArmorType.Heavy, IsStructure = true })
|
||||
.AddPart(new EntityHarvestModel
|
||||
{
|
||||
HarvestedPerInterval = 6, RequiresWorker = false, Resource = ResourceType.Alloy, Slots = 1,
|
||||
TotalAmount = 6000
|
||||
})
|
||||
.AddPart(new EntityIdPassiveModel { Id = DataType.PASSIVE_HallowedGround })
|
||||
},
|
||||
|
||||
// Passives
|
||||
// Neutral
|
||||
|
||||
{
|
||||
DataType.PASSIVE_Detection,
|
||||
new EntityModel(DataType.PASSIVE_Detection, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Detection", Descriptive = DescriptiveType.Passive,
|
||||
Description =
|
||||
@"Unit can see all hidden units in its detection radius."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.Any })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_BastionPassives,
|
||||
new EntityModel(DataType.PASSIVE_BastionPassives, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "(Scouts and Pyre)", Descriptive = DescriptiveType.Passive,
|
||||
Description =
|
||||
@"Bastion generates one scout in 2 minutes, up to a max of 2 scouts. And generates pyre over time."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.Any })
|
||||
},
|
||||
{
|
||||
DataType.PASSIVE_Respite,
|
||||
new EntityModel(DataType.PASSIVE_Respite, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Respite", Descriptive = DescriptiveType.Passive,
|
||||
Description =
|
||||
@"Nearby units will slowly heal after not attacking or being attacked for 10 seconds."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.Any })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_HarvestAlloy,
|
||||
new EntityModel(DataType.PASSIVE_HarvestAlloy, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Harvest Alloy", Descriptive = DescriptiveType.Passive,
|
||||
Description = "This unit can harvest alloy."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.Any })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.NEUTRAL_PyreMiner,
|
||||
new EntityModel(DataType.NEUTRAL_PyreMiner, EntityType.Building)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Pyre Miner",
|
||||
Description = "Passively harvest pyre.",
|
||||
Notes = ""
|
||||
})
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,548 @@
|
||||
using System.Collections.Generic;
|
||||
using Model.Entity.Parts;
|
||||
using Model.Types;
|
||||
|
||||
namespace Model.Entity.Data;
|
||||
|
||||
public partial class EntityData
|
||||
{
|
||||
public static Dictionary<string, EntityModel> GetPassiveData()
|
||||
{
|
||||
return new Dictionary<string, EntityModel>
|
||||
{
|
||||
// Passives
|
||||
// Q'Rath Passives
|
||||
|
||||
{
|
||||
DataType.PASSIVE_HallowedWarrior,
|
||||
new EntityModel(DataType.PASSIVE_HallowedWarrior, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Hallowed Warrior", Descriptive = DescriptiveType.Ability,
|
||||
Description = @"Gains bonus shields when in Hallowed Ground",
|
||||
Notes = "+20 Shields on Hallowed Ground."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_GreavesOfAhqar,
|
||||
new EntityModel(DataType.PASSIVE_GreavesOfAhqar, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Greaves Of Ahqar", Descriptive = DescriptiveType.Ability,
|
||||
Description = @"+75 Sipari Speed"
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{ Id = DataType.UPGRADE_GreavesOfAhqar, Requirement = RequirementType.Research_Upgrade })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_RelicOfTheWrathfulGaze,
|
||||
new EntityModel(DataType.PASSIVE_RelicOfTheWrathfulGaze, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
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 EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
{
|
||||
DataType.PASSIVE_WingsOfTheKenLatir,
|
||||
new EntityModel(DataType.PASSIVE_WingsOfTheKenLatir, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
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 EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_ExecutionRites,
|
||||
new EntityModel(DataType.PASSIVE_ExecutionRites, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Execution Rites", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"Warden's attacks charge up to a hit that deals greatly increased damage."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_IconOfKhastEem,
|
||||
new EntityModel(DataType.PASSIVE_IconOfKhastEem, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
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 EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_FaithCastBlades,
|
||||
new EntityModel(DataType.PASSIVE_FaithCastBlades, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
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 EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_ThroneMovingShot,
|
||||
new EntityModel(DataType.PASSIVE_ThroneMovingShot, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Throne Moving Shot", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"Thrones can attack while moving."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_SiroccoScript,
|
||||
new EntityModel(DataType.PASSIVE_SiroccoScript, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Sirocco Script Rites", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"Increases the derish's movement speed"
|
||||
})
|
||||
.AddPart(new EntityRequirementModel { Id = DataType.UPGRADE_SiroccoScript })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_HallowingRites,
|
||||
new EntityModel(DataType.PASSIVE_HallowingRites, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Hallowing Rites", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"Ark Mother's creates Hallowed Ground on stabilize."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_HallowedRuin,
|
||||
new EntityModel(DataType.PASSIVE_HallowedRuin, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Hallowed Ruin", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"Hallowers have splash on attacks that leave an area of Hallowed Ground."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_RegentsWrath,
|
||||
new EntityModel(DataType.PASSIVE_RegentsWrath, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Regent's Wrath", Descriptive = DescriptiveType.Passive,
|
||||
Description =
|
||||
@"Sceptres gain energy when stabilized. They lose energy when moving. Energy is spent to have splash on attack."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_PsalmOfFire,
|
||||
new EntityModel(DataType.PASSIVE_PsalmOfFire, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Psalm Of Fire", Descriptive = DescriptiveType.Applies_Debuff,
|
||||
Description = @"Fire Singers deal damage over time against attacked targets."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_HallowedWeapons,
|
||||
new EntityModel(DataType.PASSIVE_HallowedWeapons, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Hallowed Weapons", Descriptive = DescriptiveType.Applies_Debuff,
|
||||
Description = @"Gains 14 damage while in Hallowed Ground"
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
DataType.PASSIVE_Zeal,
|
||||
new EntityModel(DataType.PASSIVE_Zeal, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Zeal", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"Increased attack speed to allied near Pillar of the Heavens"
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_HallowedGround,
|
||||
new EntityModel(DataType.PASSIVE_HallowedGround, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Hallowed Ground", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"This building generates Hallowed Ground."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
DataType.PASSIVE_WraithBowRange,
|
||||
new EntityModel(DataType.PASSIVE_WraithBowRange, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Wraith Bow Range", Descriptive = DescriptiveType.Ability,
|
||||
Description = @"Increases Wraith Bow range against air."
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{ Id = DataType.UPGRADE_EoxBowstring, Requirement = RequirementType.Research_Upgrade })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_Rootway,
|
||||
new EntityModel(DataType.PASSIVE_Rootway, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Rootway", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"Building generates Rootway."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_BehemothCapacity,
|
||||
new EntityModel(DataType.PASSIVE_BehemothCapacity, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Quitl Storage", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"Unit stores quitl for attacks."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_QuitlStorage2,
|
||||
new EntityModel(DataType.PASSIVE_QuitlStorage2, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Quitl Storage", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"Unit stores more quitl for attacks."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{ Id = DataType.UPGRADE_VitellineCysts, Requirement = RequirementType.Research_Upgrade })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_ExternalDigestion,
|
||||
new EntityModel(DataType.PASSIVE_ExternalDigestion, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "External Digestion", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"Ichor attacks splash in a cone."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_PursuitLigaments,
|
||||
new EntityModel(DataType.PASSIVE_PursuitLigaments, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Pursuit Ligaments", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"Increases Ichor speed to 530 (+106)."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{ Id = DataType.UPGRADE_PursuitLigaments, Requirement = RequirementType.Research_Upgrade })
|
||||
},
|
||||
{
|
||||
DataType.PASSIVE_Temporary,
|
||||
new EntityModel(DataType.PASSIVE_Temporary, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Temporary", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"This unit has a limited duration before it dies."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_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.",
|
||||
Notes = "Lose hidden on attacking"
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_Ambush,
|
||||
new EntityModel(DataType.PASSIVE_Stalk, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Ambush", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"This unit deals double damage when attacking from hidden."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel { Id = DataType.UPGRADE_Ambush })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_HiddenX,
|
||||
new EntityModel(DataType.PASSIVE_HiddenX, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Hidden X", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"This unit cannot be seen unless enemies units are within X."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_FallenHarvest,
|
||||
new EntityModel(DataType.PASSIVE_FallenHarvest, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Fallen Harvest", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"Incubator gets energy when nearby non-quitl units die."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_RestoreLifeblood,
|
||||
new EntityModel(DataType.PASSIVE_RestoreLifeblood, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Restore Lifeblood", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"Quickly heals a nearby unit"
|
||||
})
|
||||
.AddPart(new EntityProductionModel { Cooldown = 0.25f })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
DataType.PASSIVE_Transfusion,
|
||||
new EntityModel(DataType.PASSIVE_Transfusion, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Transfusion", Descriptive = DescriptiveType.Passive,
|
||||
Description = @"Spends mana to refill the mana of nearby units"
|
||||
})
|
||||
.AddPart(new EntityProductionModel { Energy = 4, Cooldown = 1 })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
{
|
||||
DataType.PASSIVE_FortifiedIcons,
|
||||
new EntityModel(DataType.PASSIVE_FortifiedIcons, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Fortified Icons", Descriptive = DescriptiveType.Ability,
|
||||
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 EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
{
|
||||
DataType.PASSIVE_MendingDecree,
|
||||
new EntityModel(DataType.PASSIVE_MendingDecree, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Mending Decree", Descriptive = DescriptiveType.Ability,
|
||||
Description = @"Heals a nearby allied unit."
|
||||
})
|
||||
.AddPart(new EntityProductionModel { Energy = 10, Cooldown = 3 })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
{
|
||||
DataType.PASSIVE_GodstoneBulwark,
|
||||
new EntityModel(DataType.PASSIVE_GodstoneBulwark, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Godstone Bulkwark", Descriptive = DescriptiveType.Ability,
|
||||
Description = @"Grants +1 damage reduction."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_Invervention,
|
||||
new EntityModel(DataType.PASSIVE_Invervention, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Intervention", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
"The Saoshin releases healing energy. Allied units nearby heal over several seconds. This automatically activates when the Saoshin drops below 70 HP."
|
||||
})
|
||||
.AddPart(new EntityProductionModel { Pyre = 70, Cooldown = 5 })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
},
|
||||
// Passives
|
||||
// Aru Passives
|
||||
{
|
||||
DataType.PASSIVE_BloodFrenzy,
|
||||
new EntityModel(DataType.PASSIVE_BloodFrenzy, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Blood Frenzy", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
@"Thrums gain more attack speed for a short duration when a nearby allied Thrum kills an enemy unit."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_XacalDamage,
|
||||
new EntityModel(DataType.PASSIVE_XacalDamage, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Xacal Damage", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
@"Xacal builds up charges for double damage overtime. These charges can be spent on attacking."
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{ Id = DataType.UPGRADE_EthericFibers, Requirement = RequirementType.Research_Upgrade })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_XacalDefense,
|
||||
new EntityModel(DataType.PASSIVE_XacalDefense, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Xacal Defense", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
@"Xacal take 1 less damage from all attacks."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_CastingFromBlood,
|
||||
new EntityModel(DataType.PASSIVE_CastingFromBlood, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Cast From Blood", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
@"This unit can spend life to cast abilities when it doesn't have enough energy.",
|
||||
Notes = "They must have at least one remaining hitpoint after to perform Cast From Blood."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_OssifyingSwarm,
|
||||
new EntityModel(DataType.PASSIVE_OssifyingSwarm, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Ossifying Swarm", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
@"Reduces the movement speed and attack speed of enemies near your attack target.",
|
||||
Notes = "10% movement and attack speed. Stacks up to 5 times."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_AaroxBurn,
|
||||
new EntityModel(DataType.PASSIVE_AaroxBurn, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Aarox Burn", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
@"The aarox dies when attacking. Any units in its area of effect suffer damage over time.",
|
||||
Notes = "Deals 75 damage over 3 seconds."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_QuenchingScythes,
|
||||
new EntityModel(DataType.PASSIVE_QuenchingScythes, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Quenching Scythes", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
@"Recovers 5 life when dealing damage, or 10 mana if life is full."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_EngorgedArteries,
|
||||
new EntityModel(DataType.PASSIVE_EngorgedArteries, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Engorged Arteries", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
@"Grants +2 range when deployed on rootway."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_ProjectileGestation,
|
||||
new EntityModel(DataType.PASSIVE_ProjectileGestation, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Projectile Gestation", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
@"Fires a quitl at a target enemy unit."
|
||||
})
|
||||
.AddPart(new EntityProductionModel { Energy = 35, Cooldown = 2.5f })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_GuidingAmber,
|
||||
new EntityModel(DataType.PASSIVE_GuidingAmber, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Guiding Amber", Descriptive = DescriptiveType.Ability,
|
||||
Description =
|
||||
@"Units hit by this attack takes +1 damage for a few seconds, to a maximum of +4."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.PASSIVE_FireQuitl,
|
||||
new EntityModel(DataType.PASSIVE_FireQuitl, EntityType.Passive)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Spawn Quitl", Descriptive = DescriptiveType.Ability,
|
||||
Description = @"Unit spawns Quitl on attack.",
|
||||
Notes = "Quitl deals 99 damage over it's life span of 8 seconds."
|
||||
})
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,654 @@
|
||||
using System.Collections.Generic;
|
||||
using Model.Entity.Parts;
|
||||
using Model.Types;
|
||||
|
||||
namespace Model.Entity.Data;
|
||||
|
||||
public partial class EntityData
|
||||
{
|
||||
public static Dictionary<string, EntityModel> GetResearchData()
|
||||
{
|
||||
return new Dictionary<string, EntityModel>
|
||||
{
|
||||
// Upgrades
|
||||
// Q'Rath
|
||||
{
|
||||
DataType.UPGRADE_GreavesOfAhqar,
|
||||
new EntityModel(DataType.UPGRADE_GreavesOfAhqar, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Greaves Of Ahqar", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Increases the Sipari speed by 75."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "X" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 100, BuildTime = 100, ProducedBy = DataType.BUILDING_Reliquary })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_Reliquary,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityVanguardReplacedModel
|
||||
{ ImmortalId = DataType.IMMORTAL_Orzum, ReplacedById = DataType.UPGRADE_FaithCastBlades })
|
||||
},
|
||||
{
|
||||
DataType.UPGRADE_RadiantWard,
|
||||
new EntityModel(DataType.UPGRADE_RadiantWard, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Research Radiant Ward", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Unlocks the dervish's Radiant Ward ability"
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HoldSpace = true, HotkeyGroup = "X" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 80, Ether = 80, BuildTime = 34, ProducedBy = DataType.BUILDING_HouseOfFadingSaints })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_HouseOfFadingSaints,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_FortifiedIcons,
|
||||
new EntityModel(DataType.UPGRADE_FortifiedIcons, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Fortified Icons", Descriptive = DescriptiveType.Upgrade,
|
||||
Description =
|
||||
"""
|
||||
Sipari get more Shields.
|
||||
|
||||
Bonus Shields from <b style="color:white">Hallowed Ground</b>
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 125, Ether = 125, BuildTime = 60, ProducedBy = DataType.BUILDING_EyeOfAros })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_EyeOfAros,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_Reliquary,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityVanguardReplacedModel
|
||||
{ ImmortalId = DataType.IMMORTAL_Orzum, ReplacedById = DataType.UPGRADE_IconOfKhastEem })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_FaithCastBlades,
|
||||
new EntityModel(DataType.UPGRADE_FaithCastBlades, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Faith-Cast Blades", Descriptive = DescriptiveType.Upgrade,
|
||||
Description =
|
||||
"""
|
||||
Zentari get more range attack when in Hallowed Ground.
|
||||
"""
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "X" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 50, Ether = 125, BuildTime = 60, ProducedBy = DataType.BUILDING_Reliquary })
|
||||
.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,
|
||||
new EntityModel(DataType.UPGRADE_RelicOfTheWrathfulGaze, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Relic Of The Wrathful Gaze", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Increases the Castigator's anti-air weapon range."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 75, Ether = 75, BuildTime = 29, ProducedBy = DataType.BUILDING_HouseOfFadingSaints })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_HouseOfFadingSaints,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_PsalmOfFire,
|
||||
new EntityModel(DataType.UPGRADE_PsalmOfFire, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Psalm of Fire", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Fire Singers get area damage."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "S", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{
|
||||
Alloy = 125, Ether = 200, BuildTime = 64,
|
||||
ProducedBy = DataType.BUILDING_KeeperOfTheHardenedFlames
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_KeeperOfTheHardenedFlames,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_WindStep,
|
||||
new EntityModel(DataType.UPGRADE_WindStep, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{ Name = "Windstep", Descriptive = DescriptiveType.Upgrade, Description = "Unlocks windstep." })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "X" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 125, Ether = 100, BuildTime = 75, ProducedBy = DataType.BUILDING_MonasteryOfIzur })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{ Id = DataType.BUILDING_Reliquary, Requirement = RequirementType.Production_Building })
|
||||
},
|
||||
{
|
||||
DataType.UPGRADE_ZephyrRange,
|
||||
new EntityModel(DataType.UPGRADE_ZephyrRange, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Zephyr Range", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Increases Zephyr's range by 100."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "X" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 50, BuildTime = 50, ProducedBy = DataType.BUILDING_Reliquary })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{ Id = DataType.BUILDING_Reliquary, Requirement = RequirementType.Research_Building })
|
||||
.AddPart(new EntityIdUpgradeModel { Id = DataType.UPGRADE_WindStep })
|
||||
.AddPart(new EntityIdUpgradeModel { Id = DataType.UPGRADE_ZephyrRange })
|
||||
},
|
||||
{
|
||||
DataType.UPGRADE_SiroccoScript,
|
||||
new EntityModel(DataType.UPGRADE_SiroccoScript, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Sirocco Script", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Grant's the Dervish Sirocco Script"
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{
|
||||
Alloy = 100, Ether = 125, BuildTime = 60, ProducedBy = DataType.BUILDING_HouseOfFadingSaints
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_HouseOfFadingSaints,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_IconOfTheEnduringVigil,
|
||||
new EntityModel(DataType.UPGRADE_IconOfTheEnduringVigil, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Icon of the Enduring Vigil", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "The Dervish's Radiant Wards become permanent."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{
|
||||
Alloy = 100, Ether = 100, BuildTime = 34, ProducedBy = DataType.BUILDING_HouseOfFadingSaints
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_HouseOfFadingSaints,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_AbsolverHealthUpgrade,
|
||||
new EntityModel(DataType.UPGRADE_AbsolverHealthUpgrade, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Absolver Health Upgrade", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Absolvers get more HP and damage reductions"
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "X" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{
|
||||
Alloy = 150, Ether = 150, BuildTime = 90, ProducedBy = DataType.BUILDING_HouseOfFadingSaints
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_HouseOfFadingSaints,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_Awestrike,
|
||||
new EntityModel(DataType.UPGRADE_Awestrike, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Awestrike", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Unlocks a damage spell for Shar'u"
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "X" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{
|
||||
Alloy = 125, Ether = 150, BuildTime = 45, ProducedBy = DataType.BUILDING_HouseOfFadingSaints
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_EyeOfAros,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_IconOfKhastEem,
|
||||
new EntityModel(DataType.UPGRADE_IconOfKhastEem, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Icon of Khast'Eem", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Grants the Zentari shields and flat armor reduction."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HoldSpace = true, HotkeyGroup = "X" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 100, BuildTime = 43, ProducedBy = DataType.BUILDING_EyeOfAros })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_EyeOfAros,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityVanguardAddedModel
|
||||
{ ReplaceId = DataType.UPGRADE_FortifiedIcons, ImmortalId = DataType.IMMORTAL_Orzum })
|
||||
},
|
||||
{
|
||||
DataType.UPGRADE_WingsOfTheKenLatir,
|
||||
new EntityModel(DataType.UPGRADE_WingsOfTheKenLatir, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Wings of the Ken'Latir", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Wardens use their empowered attacks more often."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HoldSpace = true, HotkeyGroup = "X" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 150, Ether = 100, BuildTime = 60, ProducedBy = DataType.BUILDING_BearerOfTheCrown })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_EyeOfAros, Requirement = RequirementType.Production_Building
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.UPGRADE_TitheBlades,
|
||||
new EntityModel(DataType.UPGRADE_TitheBlades, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Tithe Blades", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Unlocks a utility ability for Thrones."
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "A", HoldSpace = true, HotkeyGroup = "X" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_QRath })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 200, Ether = 200, BuildTime = 120, ProducedBy = DataType.BUILDING_BearerOfTheCrown })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_BearerOfTheCrown, Requirement = RequirementType.Production_Building
|
||||
})
|
||||
},
|
||||
// Upgrades
|
||||
// Aru
|
||||
{
|
||||
DataType.UPGRADE_Offering,
|
||||
new EntityModel(DataType.UPGRADE_Offering, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Offering",
|
||||
Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Unlocks Offering"
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "X" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_AltarOfTheWorthy,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_Neurocyte,
|
||||
Requirement = RequirementType.Production_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 75, BuildTime = 60, ProducedBy = DataType.BUILDING_Neurocyte })
|
||||
},
|
||||
{
|
||||
DataType.UPGRADE_PursuitLigaments,
|
||||
new EntityModel(DataType.UPGRADE_PursuitLigaments, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Pursuit Ligaments",
|
||||
Description =
|
||||
"""
|
||||
Ichors get more movement speed.
|
||||
""",
|
||||
Descriptive = DescriptiveType.Upgrade
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "X" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_Neurocyte,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 100, BuildTime = 80, ProducedBy = DataType.BUILDING_Neurocyte })
|
||||
},
|
||||
{
|
||||
DataType.UPGRADE_ResinantSpeed,
|
||||
new EntityModel(DataType.UPGRADE_ResinantSpeed, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel { Name = "Resinant Speed", Descriptive = DescriptiveType.Upgrade })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "X" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_Neurocyte,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_AmberWomb,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 100, BuildTime = 60, ProducedBy = DataType.BUILDING_Neurocyte })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_RootShepherdHidden,
|
||||
new EntityModel(DataType.UPGRADE_RootShepherdHidden, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{ Name = "Root Shepherd Hidden", Descriptive = DescriptiveType.Upgrade })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_Neurocyte,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_AmberWomb,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 100, BuildTime = 60, ProducedBy = DataType.BUILDING_Neurocyte })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_RootShepherdSpeed,
|
||||
new EntityModel(DataType.UPGRADE_RootShepherdSpeed, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{ Name = "Root Shepherd Speed", Descriptive = DescriptiveType.Upgrade })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_Neurocyte,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_AmberWomb,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 100, BuildTime = 80, ProducedBy = DataType.BUILDING_Neurocyte })
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
DataType.UPGRADE_EthericFibers,
|
||||
new EntityModel(DataType.UPGRADE_EthericFibers, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel { Name = "Etheric Fibers", Descriptive = DescriptiveType.Upgrade })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "Q", HotkeyGroup = "X" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_Neurocyte,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 75, Ether = 100, BuildTime = 75, ProducedBy = DataType.BUILDING_Neurocyte })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_ObstructingSwarm,
|
||||
new EntityModel(DataType.UPGRADE_ObstructingSwarm, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel { Name = "Obstructing Swarm", Descriptive = DescriptiveType.Upgrade })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_Neurocyte,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_AmberWomb,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 150, Ether = 100, BuildTime = 100, ProducedBy = DataType.BUILDING_Neurocyte })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_Hematoma,
|
||||
new EntityModel(DataType.UPGRADE_Hematoma, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel { Name = "Hematoma", Descriptive = DescriptiveType.Upgrade })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_Neurocyte,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_AmberWomb,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 125, Ether = 125, BuildTime = 45, ProducedBy = DataType.BUILDING_Neurocyte })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_VitellineCysts,
|
||||
new EntityModel(DataType.UPGRADE_VitellineCysts, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel { Name = "Vitelline Cysts", Descriptive = DescriptiveType.Upgrade })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "A", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_DeepNest,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 200, Ether = 200, BuildTime = 46, ProducedBy = DataType.BUILDING_DeepNest })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_HyperAdrenoceptors,
|
||||
new EntityModel(DataType.UPGRADE_HyperAdrenoceptors, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{ Name = "Hyper Adrenoceptors", Descriptive = DescriptiveType.Upgrade })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_DeepNest,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 100, BuildTime = 60, ProducedBy = DataType.BUILDING_DeepNest })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_EoxBowstring,
|
||||
new EntityModel(DataType.UPGRADE_EoxBowstring, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Eox Bowstring",
|
||||
Description = "Increase's the range of the Wraith Bow anti-air attack.",
|
||||
Descriptive = DescriptiveType.Upgrade
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_Neurocyte,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 50, Ether = 100, BuildTime = 29, ProducedBy = DataType.BUILDING_Neurocyte })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_SporeBurst,
|
||||
new EntityModel(DataType.UPGRADE_SporeBurst, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Spore Burst",
|
||||
Description = "Aerovores and Omnivores get area damage vs air units.",
|
||||
Descriptive = DescriptiveType.Upgrade
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "S", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_RootCradle,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 150, BuildTime = 64, ProducedBy = DataType.BUILDING_RootCradle })
|
||||
},
|
||||
{
|
||||
DataType.UPGRADE_Ambush,
|
||||
new EntityModel(DataType.UPGRADE_Ambush, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Research Ambush",
|
||||
Description =
|
||||
"""
|
||||
Bone Stalkers get double damage for a few seconds after attack while Hidden.
|
||||
""",
|
||||
Descriptive = DescriptiveType.Upgrade
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "W", HotkeyGroup = "X", HoldSpace = false })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 75, BuildTime = 60, ProducedBy = DataType.BUILDING_RedVale })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_RedVale,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
},
|
||||
{
|
||||
DataType.UPGRADE_BloodPlague,
|
||||
new EntityModel(DataType.UPGRADE_BloodPlague, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{
|
||||
Name = "Blood Plague", Descriptive = DescriptiveType.Upgrade,
|
||||
Description = "Unlocks Blood Plague"
|
||||
})
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "R", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_RedVale,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 150, BuildTime = 100, ProducedBy = DataType.BUILDING_RedVale })
|
||||
.AddPart(new EntityVanguardReplacedModel
|
||||
{ ImmortalId = DataType.IMMORTAL_Xol, ReplacedById = DataType.ABILITY_BirthingStorm })
|
||||
},
|
||||
{
|
||||
DataType.UPGRADE_GodphageDamage,
|
||||
new EntityModel(DataType.UPGRADE_GodphageDamage, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel { Name = "Godphage Damage", Descriptive = DescriptiveType.Upgrade })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "E", HotkeyGroup = "X" })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_DeepNest,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 250, Ether = 250, BuildTime = 100, ProducedBy = DataType.BUILDING_DeepNest })
|
||||
},
|
||||
{
|
||||
DataType.UPGRADE_BirthingStorm,
|
||||
new EntityModel(DataType.UPGRADE_BirthingStorm, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel { Name = "Birthing Storm", Descriptive = DescriptiveType.Upgrade })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "F", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_RedVale,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 100, Ether = 150, BuildTime = 100, ProducedBy = DataType.BUILDING_RedVale })
|
||||
.AddPart(new EntityVanguardAddedModel
|
||||
{ ImmortalId = DataType.IMMORTAL_Mala, ReplaceId = DataType.ABILITY_BloodPlague })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_GENERIC_Attack1,
|
||||
new EntityModel(DataType.UPGRADE_GENERIC_Attack1, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel { Name = "Aru Attack Level 1", Descriptive = DescriptiveType.Upgrade })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "A", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_RedVale,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 125, Ether = 125, BuildTime = 114, ProducedBy = DataType.BUILDING_RootCradle })
|
||||
},
|
||||
|
||||
{
|
||||
DataType.UPGRADE_GENERIC_Defense1,
|
||||
new EntityModel(DataType.UPGRADE_GENERIC_Defense1, EntityType.Tech)
|
||||
.AddPart(new EntityInfoModel
|
||||
{ Name = "Aru Defense Level 1", Descriptive = DescriptiveType.Upgrade })
|
||||
.AddPart(new EntityHotkeyModel { Hotkey = "S", HotkeyGroup = "X", HoldSpace = true })
|
||||
.AddPart(new EntityFactionModel { Faction = DataType.FACTION_Aru })
|
||||
.AddPart(new EntityRequirementModel
|
||||
{
|
||||
Id = DataType.BUILDING_RedVale,
|
||||
Requirement = RequirementType.Research_Building
|
||||
})
|
||||
.AddPart(new EntityProductionModel
|
||||
{ Alloy = 125, Ether = 125, BuildTime = 114, ProducedBy = DataType.BUILDING_RootCradle })
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Model.Entity.Data;
|
||||
|
||||
public partial class EntityData
|
||||
{
|
||||
public static string AsJson()
|
||||
{
|
||||
var json = JsonConvert.SerializeObject(Get(), Formatting.Indented);
|
||||
return json;
|
||||
}
|
||||
|
||||
public static Dictionary<string, EntityModel> Get()
|
||||
{
|
||||
return GetResearchData()
|
||||
.Concat(GetArmyData())
|
||||
.Concat(GetAbilityData())
|
||||
.Concat(GetMiscData())
|
||||
.Concat(GetImmortalData())
|
||||
.Concat(GetBuildingData())
|
||||
.Concat(GetPassiveData())
|
||||
.ToDictionary();
|
||||
}
|
||||
}
|
||||
@@ -49,7 +49,7 @@ public class EntityModel
|
||||
{
|
||||
var entityInfo =
|
||||
EntityParts.FirstOrDefault(a => a.GetType() == typeof(EntityFactionModel)) as EntityFactionModel;
|
||||
return entityInfo == null ? string.Empty : DATA.Get()[entityInfo.Faction].GetName();
|
||||
return entityInfo == null ? string.Empty : EntityData.Get()[entityInfo.Faction].GetName();
|
||||
}
|
||||
|
||||
public string GetImmortal()
|
||||
@@ -57,7 +57,7 @@ public class EntityModel
|
||||
var entityInfo =
|
||||
EntityParts.FirstOrDefault(a =>
|
||||
a.GetType() == typeof(EntityVanguardAddedModel)) as EntityVanguardAddedModel;
|
||||
return entityInfo == null ? string.Empty : DATA.Get()[entityInfo.ImmortalId].GetName();
|
||||
return entityInfo == null ? string.Empty : EntityData.Get()[entityInfo.ImmortalId].GetName();
|
||||
}
|
||||
|
||||
public string AsYaml()
|
||||
@@ -93,7 +93,7 @@ public class EntityModel
|
||||
|
||||
public static Dictionary<string, EntityModel> GetDictionary()
|
||||
{
|
||||
if (_database == null) _database = DATA.Get();
|
||||
if (_database == null) _database = EntityData.Get();
|
||||
|
||||
return _database;
|
||||
}
|
||||
@@ -101,7 +101,7 @@ public class EntityModel
|
||||
|
||||
public static EntityModel Get(string entity)
|
||||
{
|
||||
if (_database == null) _database = DATA.Get();
|
||||
if (_database == null) _database = EntityData.Get();
|
||||
|
||||
return _database[entity];
|
||||
}
|
||||
@@ -109,7 +109,7 @@ public class EntityModel
|
||||
|
||||
public static List<EntityModel> GetList()
|
||||
{
|
||||
if (_entityModels == null) _entityModels = DATA.Get().Values.ToList();
|
||||
if (_entityModels == null) _entityModels = EntityData.Get().Values.ToList();
|
||||
|
||||
return _entityModels;
|
||||
}
|
||||
@@ -121,7 +121,7 @@ public class EntityModel
|
||||
{
|
||||
_entityModelsOnlyHotkey = new List<EntityModel>();
|
||||
|
||||
foreach (var entity in DATA.Get().Values)
|
||||
foreach (var entity in EntityData.Get().Values)
|
||||
if (entity.Hotkey() != null)
|
||||
_entityModelsOnlyHotkey.Add(entity);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
namespace Model.Entity.Parts;
|
||||
using System.Text.Json.Serialization;
|
||||
|
||||
namespace Model.Entity.Parts;
|
||||
|
||||
public class IEntityPartInterface
|
||||
{
|
||||
public EntityModel Parent { get; set; }
|
||||
[JsonIgnore] public EntityModel Parent { get; set; }
|
||||
}
|
||||
+2
-1
@@ -1,7 +1,8 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<RootNamespace>Model</RootNamespace>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Newtonsoft.Json" Version="13.0.1"/>
|
||||
|
||||
@@ -0,0 +1,53 @@
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Model.Website.Data;
|
||||
|
||||
public class WebsiteData
|
||||
{
|
||||
public static List<WebPageModel> GetPages()
|
||||
{
|
||||
return
|
||||
[
|
||||
new WebPageModel
|
||||
{
|
||||
Id = 2,
|
||||
WebSectionModelId = 2,
|
||||
Name = "Build Calculator",
|
||||
Description = "Build order calculator for determining army timings",
|
||||
Href = "build-calculator",
|
||||
IsPrivate = "False",
|
||||
Icon = "fa-solid fa-helmet-battle"
|
||||
},
|
||||
new WebPageModel
|
||||
{
|
||||
Id = 1,
|
||||
WebSectionModelId = 2,
|
||||
Name = "Database",
|
||||
Description = "Database of game information",
|
||||
Href = "database",
|
||||
IsPrivate = "False",
|
||||
Icon = "fa-solid fa-clipboard-list"
|
||||
},
|
||||
new WebPageModel
|
||||
{
|
||||
Id = 3,
|
||||
WebSectionModelId = 2,
|
||||
Name = "Harass Calculator",
|
||||
Description = "Database of game information",
|
||||
Href = "harass-calculator",
|
||||
IsPrivate = "False",
|
||||
Icon = "fa-solid fa-bow-arrow"
|
||||
},
|
||||
new WebPageModel
|
||||
{
|
||||
Id = 4,
|
||||
WebSectionModelId = 2,
|
||||
Name = "Data Tables",
|
||||
Description = "Data tables",
|
||||
Href = "data-tables",
|
||||
IsPrivate = "False",
|
||||
Icon = "fa-solid fa-table-list"
|
||||
}
|
||||
];
|
||||
}
|
||||
}
|
||||
@@ -2,10 +2,11 @@
|
||||
|
||||
public class WebPageModel
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int? WebSectionModelId { get; set; }
|
||||
public int Id { get; set; } // Not used
|
||||
public int? WebSectionModelId { get; set; } // Not used
|
||||
public string Name { get; set; } = "Add name";
|
||||
public string Description { get; set; } = "Add description";
|
||||
public string Href { get; set; } = null;
|
||||
public string IsPrivate { get; set; } = "True";
|
||||
public string IsPrivate { get; set; } = "True"; // Not used. Make boolean
|
||||
public string Icon { get; set; }
|
||||
}
|
||||
@@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
test-results/
|
||||
playwright-report/
|
||||
@@ -0,0 +1,99 @@
|
||||
const ScreenType = Object.freeze({ Desktop: 'desktop', Tablet: 'tablet', Mobile: 'mobile' });
|
||||
|
||||
class Website {
|
||||
constructor(page, options = {}) {
|
||||
this.page = page;
|
||||
this.screenType = ScreenType.Desktop;
|
||||
this.runAgainstProduction = options.production || process.env.RUN_AGAINST_PRODUCTION === 'true';
|
||||
|
||||
if (this.runAgainstProduction) {
|
||||
this.baseUrl = 'https://igpfanreference.ca';
|
||||
} else {
|
||||
const hook = process.env.TEST_HOOK || '';
|
||||
this.deploymentType = hook.includes('localhost') ? 'Local' : 'Dev';
|
||||
this.baseUrl = 'https://localhost:7234';
|
||||
}
|
||||
|
||||
const BuildCalculatorPage = require('../pages/buildCalculatorPage');
|
||||
const HarassCalculatorPage = require('../pages/harassCalculator.page');
|
||||
const DatabasePage = require('../pages/database.page');
|
||||
const DatabaseSinglePage = require('../pages/databaseSingle.page');
|
||||
const NavigationBar = require('../shared/navigationBar');
|
||||
const WebsiteSearchDialog = require('../shared/websiteSearchDialog');
|
||||
|
||||
this.buildCalculatorPage = new BuildCalculatorPage(this);
|
||||
this.harassCalculatorPage = new HarassCalculatorPage(this);
|
||||
this.databasePage = new DatabasePage(this);
|
||||
this.databaseSinglePage = new DatabaseSinglePage(this);
|
||||
this.navigationBar = new NavigationBar(this);
|
||||
this.websiteSearchDialog = new WebsiteSearchDialog(this);
|
||||
}
|
||||
|
||||
locator(selector) {
|
||||
return this.page.locator(selector);
|
||||
}
|
||||
|
||||
find(byId) {
|
||||
return this.page.locator(`#${byId}`);
|
||||
}
|
||||
|
||||
findWithParent(byId, withParentId) {
|
||||
return this.page.locator(`#${withParentId} #${byId}`);
|
||||
}
|
||||
|
||||
findScreenSpecific(byId) {
|
||||
return this.page.locator(`#${this.screenType}-${byId}`);
|
||||
}
|
||||
|
||||
findAll(byId) {
|
||||
return this.page.locator(`#${byId}`);
|
||||
}
|
||||
|
||||
findAllWithTag(tag) {
|
||||
return this.page.locator(tag);
|
||||
}
|
||||
|
||||
findAllWithTagFromElement(element, tag) {
|
||||
return element.locator(tag);
|
||||
}
|
||||
|
||||
findButtonWithLabel(label) {
|
||||
return this.page.locator(`button[label="${label}"]`);
|
||||
}
|
||||
|
||||
findChildren(ofId, tagname) {
|
||||
return this.page.locator(`#${ofId} ${tagname}`);
|
||||
}
|
||||
|
||||
async findText(byId) {
|
||||
return (await this.page.locator(`#${byId}`).textContent()) || '';
|
||||
}
|
||||
|
||||
async findInt(byId) {
|
||||
const text = await this.findText(byId);
|
||||
return parseInt(text, 10);
|
||||
}
|
||||
|
||||
async clickSearchBackground() {
|
||||
await this.page.locator('#searchBackground').click();
|
||||
}
|
||||
|
||||
async clickElement(element) {
|
||||
await element.click();
|
||||
}
|
||||
|
||||
async enterInput(element, value) {
|
||||
await element.fill(String(value));
|
||||
await element.press('Enter');
|
||||
}
|
||||
|
||||
async goto(path) {
|
||||
if (path) {
|
||||
await this.page.goto(`${this.baseUrl}/${path}`);
|
||||
} else {
|
||||
await this.page.goto(this.baseUrl);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = { Website, ScreenType };
|
||||
Generated
+76
@@ -0,0 +1,76 @@
|
||||
{
|
||||
"name": "playwright",
|
||||
"version": "1.0.0",
|
||||
"lockfileVersion": 3,
|
||||
"requires": true,
|
||||
"packages": {
|
||||
"": {
|
||||
"name": "playwright",
|
||||
"version": "1.0.0",
|
||||
"license": "ISC",
|
||||
"dependencies": {
|
||||
"@playwright/test": "^1.60.0",
|
||||
"playwright": "^1.60.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@playwright/test": {
|
||||
"version": "1.60.0",
|
||||
"resolved": "https://registry.npmjs.org/@playwright/test/-/test-1.60.0.tgz",
|
||||
"integrity": "sha512-O71yZIbAh/PxDMNGns37GHBIfrVkEVyn+AXyIa5dOTfb4/xNvRWV+Vv/NMbNCtODB/pO7vLlF2OTmMVLhmr7Ag==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright": "1.60.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
},
|
||||
"node_modules/fsevents": {
|
||||
"version": "2.3.2",
|
||||
"resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz",
|
||||
"integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==",
|
||||
"hasInstallScript": true,
|
||||
"license": "MIT",
|
||||
"optional": true,
|
||||
"os": [
|
||||
"darwin"
|
||||
],
|
||||
"engines": {
|
||||
"node": "^8.16.0 || ^10.6.0 || >=11.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright": {
|
||||
"version": "1.60.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright/-/playwright-1.60.0.tgz",
|
||||
"integrity": "sha512-hheHdokM8cdqCb0lcE3s+zT4t4W+vvjpGxsZlDnikarzx8tSzMebh3UiFtgqwFwnTnjYQcsyMF8ei2mCO/tpeA==",
|
||||
"license": "Apache-2.0",
|
||||
"dependencies": {
|
||||
"playwright-core": "1.60.0"
|
||||
},
|
||||
"bin": {
|
||||
"playwright": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
},
|
||||
"optionalDependencies": {
|
||||
"fsevents": "2.3.2"
|
||||
}
|
||||
},
|
||||
"node_modules/playwright-core": {
|
||||
"version": "1.60.0",
|
||||
"resolved": "https://registry.npmjs.org/playwright-core/-/playwright-core-1.60.0.tgz",
|
||||
"integrity": "sha512-9bW6zvX/m0lEbgTKJ6YppOKx8H3VOPBMOCFh2irXFOT4BbHgrx5hPjwJYLT40Lu+4qtD36qKc/Hn56StUW57IA==",
|
||||
"license": "Apache-2.0",
|
||||
"bin": {
|
||||
"playwright-core": "cli.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=18"
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
{
|
||||
"name": "playwright",
|
||||
"version": "1.0.0",
|
||||
"description": "",
|
||||
"main": "index.js",
|
||||
"scripts": {
|
||||
"test": "npx playwright test",
|
||||
"test:headed": "npx playwright test --headed",
|
||||
"report": "npx playwright show-report"
|
||||
},
|
||||
"keywords": [],
|
||||
"author": "",
|
||||
"license": "ISC",
|
||||
"type": "commonjs",
|
||||
"dependencies": {
|
||||
"@playwright/test": "^1.60.0",
|
||||
"playwright": "^1.60.0"
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
class BasePage {
|
||||
constructor(website) {
|
||||
this.website = website;
|
||||
}
|
||||
|
||||
get url() {
|
||||
throw new Error('Subclasses must implement url');
|
||||
}
|
||||
|
||||
async getLinks() {
|
||||
const content = this.website.find('content');
|
||||
const links = content.locator('a');
|
||||
return await links.evaluateAll(els => els.map(el => el.getAttribute('href')).filter(Boolean));
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BasePage;
|
||||
@@ -0,0 +1,52 @@
|
||||
class ArmyComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
armyView() {
|
||||
return this.page.locator('.armyView');
|
||||
}
|
||||
|
||||
displayValue(label) {
|
||||
return this.page.locator('.displayContainer').filter({ hasText: label }).locator('.displayContent');
|
||||
}
|
||||
|
||||
armyCards() {
|
||||
return this.armyView().locator('.armyCard');
|
||||
}
|
||||
|
||||
async getArmyCompletedAt() {
|
||||
return await this.displayValue('Army Completed At').textContent();
|
||||
}
|
||||
|
||||
async getArmyAttackingAt() {
|
||||
return await this.displayValue('Army Attacking At').textContent();
|
||||
}
|
||||
|
||||
async getArmyUnitNames() {
|
||||
const cards = await this.armyCards().all();
|
||||
const names = [];
|
||||
for (const card of cards) {
|
||||
const text = await card.innerText();
|
||||
const match = text.match(/\d+x\s*(.+)/);
|
||||
names.push(match ? match[1].trim() : text.trim());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
async getArmyUnitCounts() {
|
||||
const cards = await this.armyCards().all();
|
||||
const counts = [];
|
||||
for (const card of cards) {
|
||||
const countEl = card.locator('.armyCount');
|
||||
const nameEl = card.locator('div').last();
|
||||
const count = await countEl.textContent();
|
||||
const name = await nameEl.textContent();
|
||||
const num = count ? parseInt(count.replace('x', ''), 10) : 0;
|
||||
counts.push({ name: (name || '').trim(), count: num });
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ArmyComponent;
|
||||
@@ -0,0 +1,47 @@
|
||||
class BankComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
bankContainer() {
|
||||
return this.page.locator('.bankContainer');
|
||||
}
|
||||
|
||||
displayValue(label) {
|
||||
return this.bankContainer().locator('.displayContainer').filter({ hasText: label }).locator('.displayContent');
|
||||
}
|
||||
|
||||
async getTime() {
|
||||
return await this.displayValue('Time').textContent();
|
||||
}
|
||||
|
||||
async getAlloy() {
|
||||
return await this.displayValue('Alloy').textContent();
|
||||
}
|
||||
|
||||
async getEther() {
|
||||
return await this.displayValue('Ether').textContent();
|
||||
}
|
||||
|
||||
async getPyre() {
|
||||
return await this.displayValue('Pyre').textContent();
|
||||
}
|
||||
|
||||
async getSupply() {
|
||||
return await this.displayValue('Supply').textContent();
|
||||
}
|
||||
|
||||
async getWorkerCount() {
|
||||
return await this.bankContainer().locator('.workerText').locator('.displayContent').nth(0).textContent();
|
||||
}
|
||||
|
||||
async getBusyWorkerCount() {
|
||||
return await this.bankContainer().locator('.workerText').locator('.displayContent').nth(1).textContent();
|
||||
}
|
||||
|
||||
async getCreatingWorkerCount() {
|
||||
return await this.bankContainer().locator('.workerText').locator('.displayContent').nth(2).textContent();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BankComponent;
|
||||
@@ -0,0 +1,35 @@
|
||||
class BuildChartComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
chartsContainer() {
|
||||
return this.page.locator('.chartsContainer');
|
||||
}
|
||||
|
||||
displayValue(label) {
|
||||
return this.page.locator('.displayContainer').filter({ hasText: label }).locator('.displayContent');
|
||||
}
|
||||
|
||||
async getHighestAlloy() {
|
||||
return await this.displayValue('Highest Alloy').textContent();
|
||||
}
|
||||
|
||||
async getHighestEther() {
|
||||
return await this.displayValue('Highest Ether').textContent();
|
||||
}
|
||||
|
||||
async getHighestPyre() {
|
||||
return await this.displayValue('Highest Pyre').textContent();
|
||||
}
|
||||
|
||||
async getHighestArmy() {
|
||||
return await this.displayValue('Highest Army').textContent();
|
||||
}
|
||||
|
||||
async getChartCount() {
|
||||
return await this.chartsContainer().locator('> div').count();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BuildChartComponent;
|
||||
@@ -0,0 +1,15 @@
|
||||
class BuildOrderComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
jsonTextarea() {
|
||||
return this.page.locator('textarea');
|
||||
}
|
||||
|
||||
async getJsonData() {
|
||||
return await this.jsonTextarea().inputValue();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BuildOrderComponent;
|
||||
@@ -0,0 +1,33 @@
|
||||
class EntityClickViewComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
entityClickView() {
|
||||
return this.page.locator('.entityClickView');
|
||||
}
|
||||
|
||||
async getEntityName() {
|
||||
const el = this.entityClickView().locator('#entityName');
|
||||
if ((await el.count()) === 0) return null;
|
||||
return (await el.textContent()) || '';
|
||||
}
|
||||
|
||||
async getEntityHealth() {
|
||||
const healthText = this.entityClickView().locator('div').filter({ hasText: /Health/i }).first();
|
||||
if ((await healthText.count()) === 0) return null;
|
||||
const text = (await healthText.textContent()) || '';
|
||||
const match = text.match(/(\d+)/);
|
||||
return match ? match[1] : null;
|
||||
}
|
||||
|
||||
async clickDetailedView() {
|
||||
await this.entityClickView().locator('button').filter({ hasText: 'Detailed' }).click();
|
||||
}
|
||||
|
||||
async clickPlainView() {
|
||||
await this.entityClickView().locator('button').filter({ hasText: 'Plain' }).click();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = EntityClickViewComponent;
|
||||
@@ -0,0 +1,35 @@
|
||||
class FilterComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
factionSelect() {
|
||||
return this.page.locator('select').filter({ has: this.page.locator('option:has-text("Aru"), option:has-text("Q\'Rath")') });
|
||||
}
|
||||
|
||||
immortalSelect() {
|
||||
return this.page.locator('select').filter({ has: this.page.locator('option:has-text("Orzum"), option:has-text("Ajari"), option:has-text("Atzlan"), option:has-text("Mala"), option:has-text("Xol")') });
|
||||
}
|
||||
|
||||
async selectFaction(faction) {
|
||||
await this.factionSelect().selectOption(faction);
|
||||
}
|
||||
|
||||
async selectImmortal(immortal) {
|
||||
await this.immortalSelect().selectOption(immortal);
|
||||
}
|
||||
|
||||
async getSelectedFaction() {
|
||||
return await this.factionSelect().inputValue();
|
||||
}
|
||||
|
||||
async getSelectedImmortal() {
|
||||
return await this.immortalSelect().inputValue();
|
||||
}
|
||||
|
||||
async getAvailableImmortals() {
|
||||
return await this.immortalSelect().locator('option').allTextContents();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = FilterComponent;
|
||||
@@ -0,0 +1,39 @@
|
||||
class HighlightsComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
highlightsContainer() {
|
||||
return this.page.locator('.highlightsContainer');
|
||||
}
|
||||
|
||||
requestedColumn() {
|
||||
return this.highlightsContainer().locator('div').filter({ hasText: 'Requested' }).locator('+ div');
|
||||
}
|
||||
|
||||
finishedColumn() {
|
||||
return this.highlightsContainer().locator('div').filter({ hasText: 'Finished' }).locator('+ div');
|
||||
}
|
||||
|
||||
async getRequestedItems() {
|
||||
const items = await this.highlightsContainer().locator('div').filter({ hasText: /^\d+\s*\|/ }).all();
|
||||
const result = [];
|
||||
for (const item of items) {
|
||||
const text = (await item.textContent()) || '';
|
||||
result.push(text.trim());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
async getFinishedItems() {
|
||||
const items = await this.highlightsContainer().locator('div').filter({ hasText: /^\d+\s*\|/ }).all();
|
||||
const result = [];
|
||||
for (const item of items) {
|
||||
const text = (await item.textContent()) || '';
|
||||
result.push(text.trim());
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HighlightsComponent;
|
||||
@@ -0,0 +1,50 @@
|
||||
class HotkeyViewerComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
keyContainer() {
|
||||
return this.page.locator('.keyContainer');
|
||||
}
|
||||
|
||||
async _findKeyButton(keyLabel) {
|
||||
const upper = keyLabel.toUpperCase();
|
||||
const buttons = this.keyContainer().locator('> div > div');
|
||||
const count = await buttons.count();
|
||||
for (let i = 0; i < count; i++) {
|
||||
const btn = buttons.nth(i);
|
||||
const text = (await btn.textContent()) || '';
|
||||
if (text.trim().toUpperCase().startsWith(upper)) return btn;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
async clickKey(keyText) {
|
||||
const btn = await this._findKeyButton(keyText);
|
||||
if (!btn) throw new Error(`Key "${keyText}" not found`);
|
||||
await btn.click({ force: true });
|
||||
}
|
||||
|
||||
async getFirstEntityName(keyText) {
|
||||
const btn = await this._findKeyButton(keyText);
|
||||
if (!btn) return null;
|
||||
const entities = btn.locator('> div');
|
||||
if ((await entities.count()) === 0) return null;
|
||||
return (await entities.first().textContent()) || '';
|
||||
}
|
||||
|
||||
async getEntityNamesOnKey(keyText) {
|
||||
const btn = await this._findKeyButton(keyText);
|
||||
if (!btn) return [];
|
||||
const entities = btn.locator('> div');
|
||||
const count = await entities.count();
|
||||
const names = [];
|
||||
for (let i = 0; i < count; i++) {
|
||||
const text = (await entities.nth(i).textContent()) || '';
|
||||
names.push(text.trim());
|
||||
}
|
||||
return names.filter(Boolean);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HotkeyViewerComponent;
|
||||
@@ -0,0 +1,68 @@
|
||||
class OptionsComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
buildingInputDelayInput() {
|
||||
return this.formNumberInput('Building Input Delay');
|
||||
}
|
||||
|
||||
waitTimeInput() {
|
||||
return this.formNumberInput('Wait Time');
|
||||
}
|
||||
|
||||
waitToInput() {
|
||||
return this.formNumberInput('Wait To');
|
||||
}
|
||||
|
||||
addWaitButton() {
|
||||
return this.buttonWithLabel('Add Wait').first();
|
||||
}
|
||||
|
||||
addWaitToButton() {
|
||||
return this.buttonWithLabel('Add Wait').last();
|
||||
}
|
||||
|
||||
formNumberInput(label) {
|
||||
return this.page.locator(`.formNumberContainer`).filter({ hasText: label }).locator('input[type="number"]');
|
||||
}
|
||||
|
||||
buttonWithLabel(label) {
|
||||
return this.page.locator('button').filter({ hasText: label });
|
||||
}
|
||||
|
||||
async setBuildingInputDelay(value) {
|
||||
await this.buildingInputDelayInput().fill(String(value));
|
||||
await this.buildingInputDelayInput().press('Enter');
|
||||
}
|
||||
|
||||
async setWaitTime(value) {
|
||||
await this.waitTimeInput().fill(String(value));
|
||||
}
|
||||
|
||||
async setWaitTo(value) {
|
||||
await this.waitToInput().fill(String(value));
|
||||
}
|
||||
|
||||
async clickAddWait() {
|
||||
await this.addWaitButton().click();
|
||||
}
|
||||
|
||||
async clickAddWaitTo() {
|
||||
await this.addWaitToButton().click();
|
||||
}
|
||||
|
||||
async getBuildingInputDelay() {
|
||||
return await this.buildingInputDelayInput().inputValue();
|
||||
}
|
||||
|
||||
async getWaitTime() {
|
||||
return await this.waitTimeInput().inputValue();
|
||||
}
|
||||
|
||||
async getWaitTo() {
|
||||
return await this.waitToInput().inputValue();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = OptionsComponent;
|
||||
@@ -0,0 +1,16 @@
|
||||
class TimelineComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
container() {
|
||||
return this.page.locator('.calculatorGrid > div').filter({ hasText: 'Timeline highlights' });
|
||||
}
|
||||
|
||||
async containsEntity(name) {
|
||||
const text = (await this.container().textContent()) || '';
|
||||
return text.includes(name);
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TimelineComponent;
|
||||
@@ -0,0 +1,37 @@
|
||||
class TimingComponent {
|
||||
constructor(website) {
|
||||
this.website = website;
|
||||
}
|
||||
|
||||
attackTimeInput() {
|
||||
return this.formNumberInput('Attack Time');
|
||||
}
|
||||
|
||||
travelTimeInput() {
|
||||
return this.formNumberInput('Travel Time');
|
||||
}
|
||||
|
||||
formNumberInput(label) {
|
||||
return this.website.locator(`.formNumberContainer`).filter({ hasText: label }).locator('input[type="number"]');
|
||||
}
|
||||
|
||||
async setAttackTime(value) {
|
||||
await this.attackTimeInput().fill(String(value));
|
||||
await this.attackTimeInput().press('Enter');
|
||||
}
|
||||
|
||||
async setTravelTime(value) {
|
||||
await this.travelTimeInput().fill(String(value));
|
||||
await this.travelTimeInput().press('Enter');
|
||||
}
|
||||
|
||||
async getAttackTime() {
|
||||
return await this.attackTimeInput().inputValue();
|
||||
}
|
||||
|
||||
async getTravelTime() {
|
||||
return await this.travelTimeInput().inputValue();
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TimingComponent;
|
||||
@@ -0,0 +1,56 @@
|
||||
const TimingComponent = require('./buildCalculator/timingComponent');
|
||||
const FilterComponent = require('./buildCalculator/filterComponent');
|
||||
const OptionsComponent = require('./buildCalculator/optionsComponent');
|
||||
const BankComponent = require('./buildCalculator/bankComponent');
|
||||
const ArmyComponent = require('./buildCalculator/armyComponent');
|
||||
const HighlightsComponent = require('./buildCalculator/highlightsComponent');
|
||||
const BuildOrderComponent = require('./buildCalculator/buildOrderComponent');
|
||||
const TimelineComponent = require('./buildCalculator/timelineComponent');
|
||||
const HotkeyViewerComponent = require('./buildCalculator/hotkeyViewerComponent');
|
||||
const EntityClickViewComponent = require('./buildCalculator/entityClickViewComponent');
|
||||
const BuildChartComponent = require('./buildCalculator/buildChartComponent');
|
||||
const ToastComponent = require('../shared/toastComponent');
|
||||
|
||||
const BasePage = require('./base.page');
|
||||
|
||||
|
||||
class BuildCalculatorPage extends BasePage {
|
||||
constructor(website) {
|
||||
super(website);
|
||||
this.timing = new TimingComponent(website);
|
||||
this.filter = new FilterComponent(website);
|
||||
this.options = new OptionsComponent(website);
|
||||
this.bank = new BankComponent(website);
|
||||
this.army = new ArmyComponent(website);
|
||||
this.highlights = new HighlightsComponent(website);
|
||||
this.buildOrder = new BuildOrderComponent(website);
|
||||
this.timeline = new TimelineComponent(website);
|
||||
this.hotkeys = new HotkeyViewerComponent(website);
|
||||
this.entityView = new EntityClickViewComponent(website);
|
||||
this.chart = new BuildChartComponent(website);
|
||||
this.toast = new ToastComponent(website);
|
||||
}
|
||||
|
||||
get url() {
|
||||
return 'build-calculator';
|
||||
}
|
||||
|
||||
calculatorGrid() {
|
||||
return this.website.locator('.calculatorGrid');
|
||||
}
|
||||
|
||||
clearBuildOrderButton() {
|
||||
return this.website.locator('button').filter({ hasText: 'Clear Build Order' });
|
||||
}
|
||||
|
||||
async clickClearBuildOrder() {
|
||||
await this.clearBuildOrderButton().click();
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.website.goto(this.url);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = BuildCalculatorPage;
|
||||
@@ -0,0 +1,27 @@
|
||||
const BasePage = require('./base.page');
|
||||
|
||||
class DatabasePage extends BasePage {
|
||||
get url() { return 'database'; }
|
||||
|
||||
async filterName(name) {
|
||||
await this.website.enterInput(this.website.findAll('filterName').first(), name);
|
||||
return this;
|
||||
}
|
||||
|
||||
async getEntityName(entityType, entityName) {
|
||||
return await this.website
|
||||
.findWithParent('entityName', `${entityType.toLowerCase()}-${entityName.toLowerCase()}`)
|
||||
.innerText();
|
||||
}
|
||||
|
||||
async getEntityNameByIndex(index) {
|
||||
return await this.website.findAll('entityName').nth(index).innerText();
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.website.goto(this.url);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DatabasePage;
|
||||
@@ -0,0 +1,28 @@
|
||||
const BasePage = require('./base.page');
|
||||
|
||||
class DatabaseSinglePage extends BasePage {
|
||||
get url() { return 'database'; }
|
||||
|
||||
async getEntityName() {
|
||||
return await this.website.find('entityName').innerText();
|
||||
}
|
||||
|
||||
async getEntityHealth() {
|
||||
return await this.website.find('entityHealth').innerText();
|
||||
}
|
||||
|
||||
async getInvalidSearch() {
|
||||
return await this.website.find('invalidSearch').innerText();
|
||||
}
|
||||
|
||||
async getValidSearch() {
|
||||
return await this.website.find('validSearch').innerText();
|
||||
}
|
||||
|
||||
async goto(searchText) {
|
||||
await this.website.goto(`${this.url}/${searchText}`);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = DatabaseSinglePage;
|
||||
@@ -0,0 +1,68 @@
|
||||
const BasePage = require('./base.page');
|
||||
|
||||
class HarassCalculatorPage extends BasePage {
|
||||
get url() { return 'harass-calculator'; }
|
||||
|
||||
async setWorkersLostToHarass(number) {
|
||||
await this.website.enterInput(this.website.find('numberOfWorkersLostToHarass'), number);
|
||||
return this;
|
||||
}
|
||||
|
||||
async setNumberOfTownHallsExisting(number) {
|
||||
await this.website.enterInput(this.website.find('numberOfTownHallsExisting'), number);
|
||||
return this;
|
||||
}
|
||||
|
||||
async setTownHallTravelTime(forTownHall, number) {
|
||||
const inputs = this.website.findChildren('numberOfTownHallTravelTimes', 'input');
|
||||
await this.website.enterInput(inputs.nth(forTownHall), number);
|
||||
return this;
|
||||
}
|
||||
|
||||
async getTotalAlloyHarassment() {
|
||||
return await this.website.findInt('totalAlloyHarassment');
|
||||
}
|
||||
|
||||
async getWorkerReplacementCost() {
|
||||
return await this.website.findInt('workerReplacementCost');
|
||||
}
|
||||
|
||||
async getDelayedMiningCost() {
|
||||
return await this.website.findInt('delayedMiningCost');
|
||||
}
|
||||
|
||||
async getAverageTravelTime() {
|
||||
return await this.website.findInt('getAverageTravelTime');
|
||||
}
|
||||
|
||||
async getExampleTotalAlloyLoss() {
|
||||
return await this.website.findInt('exampleTotalAlloyLoss');
|
||||
}
|
||||
|
||||
async getExampleWorkerCost() {
|
||||
return await this.website.findInt('exampleWorkerCost');
|
||||
}
|
||||
|
||||
async getExampleMiningTimeCost() {
|
||||
return await this.website.findInt('exampleMiningTimeCost');
|
||||
}
|
||||
|
||||
async getExampleTotalAlloyLossAccurate() {
|
||||
return await this.website.findInt('exampleTotalAlloyLossAccurate');
|
||||
}
|
||||
|
||||
async getExampleTotalAlloyLossDifference() {
|
||||
return await this.website.findInt('exampleTotalAlloyLossDifference');
|
||||
}
|
||||
|
||||
async getExampleTotalAlloyLossAccurateDifference() {
|
||||
return await this.website.findInt('exampleTotalAlloyLossAccurateDifference');
|
||||
}
|
||||
|
||||
async goto() {
|
||||
await this.website.goto(this.url);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = HarassCalculatorPage;
|
||||
@@ -0,0 +1,15 @@
|
||||
const { defineConfig } = require('@playwright/test');
|
||||
|
||||
module.exports = defineConfig({
|
||||
testDir: './tests',
|
||||
fullyParallel: true,
|
||||
retries: 1,
|
||||
timeout: 30000,
|
||||
use: {
|
||||
trace: 'on-first-retry',
|
||||
screenshot: 'only-on-failure',
|
||||
},
|
||||
projects: [
|
||||
{ name: 'chromium', use: { browserName: 'chromium' } },
|
||||
],
|
||||
});
|
||||
@@ -0,0 +1,19 @@
|
||||
class NavigationBar {
|
||||
constructor(website) {
|
||||
this.website = website;
|
||||
}
|
||||
|
||||
get searchButton() { return this.website.findScreenSpecific('searchButton'); }
|
||||
|
||||
async clickHomeLink() {
|
||||
await this.website.clickElement(this.website.locator('a:has-text("IGP Fan Reference")'));
|
||||
return this;
|
||||
}
|
||||
|
||||
async clickSearchButton() {
|
||||
await this.website.clickElement(this.searchButton);
|
||||
return this.website.websiteSearchDialog;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = NavigationBar;
|
||||
@@ -0,0 +1,40 @@
|
||||
class ToastComponent {
|
||||
constructor(page) {
|
||||
this.page = page;
|
||||
}
|
||||
|
||||
container() {
|
||||
return this.page.locator('.toastsContainer');
|
||||
}
|
||||
|
||||
toasts() {
|
||||
return this.page.locator('.toastsContainer .toastContainer');
|
||||
}
|
||||
|
||||
async getToastTitles() {
|
||||
const titles = await this.page.locator('.toastsContainer .toastTitle').allTextContents();
|
||||
return titles.map(t => t.trim()).filter(Boolean);
|
||||
}
|
||||
|
||||
_page() {
|
||||
return this.page.page || this.page;
|
||||
}
|
||||
|
||||
async hasToastContaining(text) {
|
||||
try {
|
||||
await this._page().waitForFunction(
|
||||
(expected) => {
|
||||
const titles = document.querySelectorAll('.toastsContainer .toastTitle');
|
||||
return Array.from(titles).some(t => t.textContent.trim().includes(expected));
|
||||
},
|
||||
text,
|
||||
{ timeout: 3000 }
|
||||
);
|
||||
return true;
|
||||
} catch {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = ToastComponent;
|
||||
@@ -0,0 +1,25 @@
|
||||
class WebsiteSearchDialog {
|
||||
constructor(website) {
|
||||
this.website = website;
|
||||
}
|
||||
|
||||
get searchBackground() { return this.website.find('searchBackground'); }
|
||||
get searchInput() { return this.website.find('searchInput'); }
|
||||
|
||||
async closeDialog() {
|
||||
await this.website.clickSearchBackground();
|
||||
return this.website.navigationBar;
|
||||
}
|
||||
|
||||
async search(text) {
|
||||
await this.website.enterInput(this.searchInput, text);
|
||||
return this;
|
||||
}
|
||||
|
||||
async selectSearchEntity(label) {
|
||||
await this.website.clickElement(this.website.findButtonWithLabel(label));
|
||||
return this.website.databaseSinglePage;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = WebsiteSearchDialog;
|
||||
@@ -0,0 +1,104 @@
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const BuildCalculatorPage = require('../pages/buildCalculatorPage');
|
||||
const { Website } = require('../helpers/website');
|
||||
|
||||
|
||||
|
||||
test.describe('Build Calculator', () => {
|
||||
|
||||
let website;
|
||||
|
||||
test.beforeEach(({ page }) => {
|
||||
website = new Website(page);
|
||||
});
|
||||
|
||||
test('Add entities via keyboard Q, W, E with Q\'Rath/Orzum', async ({ page }) => {
|
||||
const calc = website.buildCalculatorPage;
|
||||
await calc.goto();
|
||||
|
||||
await calc.filter.selectFaction("Q'Rath");
|
||||
await calc.filter.selectImmortal('Orzum');
|
||||
|
||||
await calc.hotkeys.clickKey('TAB');
|
||||
|
||||
const keyNames = { Q: 'q', W: 'w', E: 'e', TAB: 'Tab' };
|
||||
|
||||
for (const key of ['Q', 'W', 'E', 'TAB']) {
|
||||
const entityNames = await calc.hotkeys.getEntityNamesOnKey(key);
|
||||
if (entityNames.length === 0) continue;
|
||||
|
||||
await page.keyboard.press(keyNames[key]);
|
||||
|
||||
const viewName = await calc.entityView.getEntityName();
|
||||
expect(viewName).toBeTruthy();
|
||||
expect(entityNames).toContain(viewName);
|
||||
}
|
||||
});
|
||||
|
||||
test('Add entities via hotkeys TAB, Q, W, E with Q\'Rath/Orzum', async ({ page }) => {
|
||||
const calc = website.buildCalculatorPage;
|
||||
await calc.goto();
|
||||
|
||||
await calc.filter.selectFaction("Q'Rath");
|
||||
await calc.filter.selectImmortal('Orzum');
|
||||
|
||||
for (const key of ['TAB', 'Q', 'W', 'E']) {
|
||||
const entityNames = await calc.hotkeys.getEntityNamesOnKey(key);
|
||||
if (entityNames.length === 0) continue;
|
||||
|
||||
await calc.hotkeys.clickKey(key);
|
||||
|
||||
const viewName = await calc.entityView.getEntityName();
|
||||
expect(viewName).toBeTruthy();
|
||||
expect(entityNames).toContain(viewName);
|
||||
}
|
||||
});
|
||||
|
||||
test('Add Acropolis via Q, verify entity view and timeline, then clear', async ({ page }) => {
|
||||
const calc = website.buildCalculatorPage;
|
||||
await calc.goto();
|
||||
|
||||
await calc.filter.selectFaction("Q'Rath");
|
||||
await calc.filter.selectImmortal('Orzum');
|
||||
|
||||
expect(await calc.timeline.containsEntity('Acropolis')).toBe(false);
|
||||
|
||||
await calc.hotkeys.clickKey('Q');
|
||||
|
||||
expect(await calc.entityView.getEntityName()).toBe('Acropolis');
|
||||
expect(await calc.timeline.containsEntity('Acropolis')).toBe(true);
|
||||
|
||||
await calc.clickClearBuildOrder();
|
||||
await page.waitForTimeout(1000);
|
||||
|
||||
expect(await calc.timeline.containsEntity('Acropolis')).toBe(false);
|
||||
expect(await calc.entityView.getEntityName()).toBeNull();
|
||||
});
|
||||
|
||||
test('Missing Requirements toast when building Soul Foundry without Legion Hall', async ({ page }) => {
|
||||
const calc = website.buildCalculatorPage;
|
||||
await calc.goto();
|
||||
|
||||
await calc.filter.selectFaction("Q'Rath");
|
||||
await calc.filter.selectImmortal('Orzum');
|
||||
|
||||
await calc.hotkeys.clickKey('E');
|
||||
const hasToast = await calc.toast.hasToastContaining('Missing Requirements');
|
||||
expect(hasToast).toBe(true);
|
||||
});
|
||||
|
||||
test('Not Enough Ether toast when building Soul Foundry after Legion Hall', async ({ page }) => {
|
||||
const calc = website.buildCalculatorPage;
|
||||
|
||||
await calc.goto();
|
||||
|
||||
await calc.filter.selectFaction("Q'Rath");
|
||||
await calc.filter.selectImmortal('Orzum');
|
||||
|
||||
await calc.hotkeys.clickKey('W');
|
||||
|
||||
await calc.hotkeys.clickKey('E');
|
||||
const hasToast = await calc.toast.hasToastContaining('Not Enough Ether');
|
||||
expect(hasToast).toBe(true);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,32 @@
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const { Website } = require('../helpers/website');
|
||||
|
||||
test.describe('Harass Calculator', () => {
|
||||
let website;
|
||||
|
||||
test.beforeEach(({ page }) => {
|
||||
website = new Website(page);
|
||||
});
|
||||
|
||||
test('CalculatorInput', async () => {
|
||||
const page = website.harassCalculatorPage;
|
||||
await page.goto();
|
||||
await page.setWorkersLostToHarass(3);
|
||||
await page.setNumberOfTownHallsExisting(2);
|
||||
await page.setTownHallTravelTime(0, 30);
|
||||
const result = await page.getTotalAlloyHarassment();
|
||||
expect(result).toBe(240);
|
||||
});
|
||||
|
||||
test('CalculatedExampleInformation', async () => {
|
||||
const page = website.harassCalculatorPage;
|
||||
await page.goto();
|
||||
|
||||
expect(await page.getExampleTotalAlloyLoss()).toBe(720);
|
||||
expect(await page.getExampleWorkerCost()).toBe(300);
|
||||
expect(await page.getExampleMiningTimeCost()).toBe(420);
|
||||
expect(await page.getExampleTotalAlloyLossAccurate()).toBe(450);
|
||||
expect(await page.getExampleTotalAlloyLossDifference()).toBe(300);
|
||||
expect(await page.getExampleTotalAlloyLossAccurateDifference()).toBe(270);
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,28 @@
|
||||
const { test } = require('@playwright/test');
|
||||
const { Website } = require('../helpers/website');
|
||||
const TestReport = require('../utils/testReport');
|
||||
|
||||
test.describe('Link Verification', () => {
|
||||
let website;
|
||||
let testReport;
|
||||
|
||||
test.beforeEach(() => {
|
||||
testReport = new TestReport();
|
||||
});
|
||||
|
||||
test('VerifyPageLinks', async ({ page }) => {
|
||||
website = new Website(page);
|
||||
testReport.createTest(test.info().title);
|
||||
|
||||
await website.harassCalculatorPage.goto();
|
||||
await testReport.verifyLinks(website.harassCalculatorPage);
|
||||
|
||||
await website.databasePage.goto();
|
||||
await testReport.verifyLinks(website.databasePage);
|
||||
|
||||
await website.databaseSinglePage.goto('throne');
|
||||
await testReport.verifyLinks(website.databaseSinglePage);
|
||||
|
||||
testReport.throwErrors();
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,54 @@
|
||||
const { test, expect } = require('@playwright/test');
|
||||
const { Website } = require('../helpers/website');
|
||||
|
||||
test.describe('Search Features', () => {
|
||||
let website;
|
||||
|
||||
test.beforeEach(({ page }) => {
|
||||
website = new Website(page);
|
||||
});
|
||||
|
||||
test('DesktopOpenCloseSearchDialog', async () => {
|
||||
await website.goto();
|
||||
await website.navigationBar.clickSearchButton();
|
||||
await website.websiteSearchDialog.closeDialog();
|
||||
await website.navigationBar.clickHomeLink();
|
||||
});
|
||||
|
||||
test('DesktopSearchForThrone', async () => {
|
||||
await website.goto();
|
||||
await website.navigationBar.clickSearchButton();
|
||||
await website.websiteSearchDialog.search('Throne');
|
||||
const page = await website.websiteSearchDialog.selectSearchEntity('Throne');
|
||||
|
||||
const name = await page.getEntityName();
|
||||
const health = await page.getEntityHealth();
|
||||
|
||||
expect(name).toBe('Throne');
|
||||
expect(health.trim()).not.toBe('');
|
||||
});
|
||||
|
||||
test('DesktopFilterForThrone', async () => {
|
||||
const page = website.databasePage;
|
||||
await page.goto();
|
||||
await page.filterName('Throne');
|
||||
const name = await page.getEntityNameByIndex(0);
|
||||
expect(name).toBe('Throne');
|
||||
});
|
||||
|
||||
test('SeeThroneByDefault', async () => {
|
||||
const page = website.databasePage;
|
||||
await page.goto();
|
||||
const name = await page.getEntityName('army', 'throne');
|
||||
expect(name).toBe('Throne');
|
||||
});
|
||||
|
||||
test('DirectLinkNotThroneFailure', async () => {
|
||||
const page = website.databaseSinglePage;
|
||||
await page.goto('not throne');
|
||||
const invalidSearch = await page.getInvalidSearch();
|
||||
const validSearch = await page.getValidSearch();
|
||||
expect(invalidSearch).toBe('not throne');
|
||||
expect(validSearch).toBe('Throne');
|
||||
});
|
||||
});
|
||||
@@ -0,0 +1,77 @@
|
||||
class TestReport {
|
||||
constructor() {
|
||||
this.tests = [];
|
||||
}
|
||||
|
||||
createTest(name) {
|
||||
const test = { name, result: true, messages: [] };
|
||||
this.tests.push(test);
|
||||
return test;
|
||||
}
|
||||
|
||||
throwErrors() {
|
||||
const latest = this.tests[this.tests.length - 1];
|
||||
if (!latest.result) {
|
||||
const msgs = latest.messages.map(m => m.description).join('\n');
|
||||
throw new Error(`${latest.name} test failed with ${latest.messages.length} messages.\n\n${msgs}`);
|
||||
}
|
||||
}
|
||||
|
||||
checkPassed(passed, message) {
|
||||
if (!passed) {
|
||||
const latest = this.tests[this.tests.length - 1];
|
||||
latest.result = false;
|
||||
latest.messages.push(message);
|
||||
}
|
||||
}
|
||||
|
||||
async verifyLinks(page) {
|
||||
const links = await page.getLinks();
|
||||
for (const link of links) {
|
||||
if (link.startsWith('mailto')) continue;
|
||||
try {
|
||||
const response = await fetch(link);
|
||||
if (!response.ok) {
|
||||
this.checkPassed(false, {
|
||||
color: 'red',
|
||||
title: 'Bad Link',
|
||||
description: `${link} failed on page ${page.url} with status code ${response.status}`
|
||||
});
|
||||
}
|
||||
} catch (e) {
|
||||
this.checkPassed(false, {
|
||||
color: 'red',
|
||||
title: 'Bad Link',
|
||||
description: `${link} failed on page ${page.url} with error ${e.message}`
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
didTestsPass() {
|
||||
return this.tests.every(t => t.result);
|
||||
}
|
||||
|
||||
getMessages() {
|
||||
if (this.didTestsPass()) {
|
||||
return [{
|
||||
title: 'Passed',
|
||||
color: 0x00FF00,
|
||||
description: `All ${this.tests.length} tests passed.`
|
||||
}];
|
||||
}
|
||||
const messages = [];
|
||||
for (const test of this.tests) {
|
||||
for (const msg of test.messages) {
|
||||
messages.push({
|
||||
title: msg.title,
|
||||
color: parseInt(msg.color, 16),
|
||||
description: msg.description
|
||||
});
|
||||
}
|
||||
}
|
||||
return messages;
|
||||
}
|
||||
}
|
||||
|
||||
module.exports = TestReport;
|
||||
@@ -1 +1,26 @@
|
||||
|
||||
|
||||
# IGP Fan Reference
|
||||
|
||||
A fan-made reference site for *IMMORTAL: Gates of Pyre*.
|
||||
|
||||
## Documentation
|
||||
|
||||
Comprehensive documentation for developers is available in the [`/docs`](./docs) folder:
|
||||
|
||||
- [**Overview**](./docs/overview.md): High-level project description and tech stack.
|
||||
- [**Architecture**](./docs/architecture.md): Solution structure and design patterns.
|
||||
- [**Services**](./docs/services.md): Detailed explanation of core application services.
|
||||
- [**Components**](./docs/components.md): UI component library and layout structure.
|
||||
- [**Development Guide**](./docs/development.md): Build, test, and deployment instructions.
|
||||
- [**Recommendations**](./docs/recommendations.md): Suggestions for code and design improvements.
|
||||
|
||||
## Quick Start
|
||||
|
||||
To run the project locally:
|
||||
|
||||
1. Navigate to the `IGP` directory.
|
||||
2. Run `dotnet watch run`.
|
||||
3. Open `https://localhost:5001`.
|
||||
|
||||
For more details, see the [Development Guide](./docs/development.md).
|
||||
|
||||
+1
-22
@@ -80,13 +80,6 @@ public interface IMyDialogService
|
||||
public void Hide();
|
||||
}
|
||||
|
||||
public interface IVariableService
|
||||
{
|
||||
public Dictionary<string, string> Variables { get; set; }
|
||||
public Task Load();
|
||||
public bool IsLoaded();
|
||||
}
|
||||
|
||||
public interface IEconomyComparisonService
|
||||
{
|
||||
public List<BuildToCompareModel> BuildsToCompare { get; set; }
|
||||
@@ -121,21 +114,6 @@ public interface IEntityDialogService
|
||||
public bool HasHistory();
|
||||
}
|
||||
|
||||
public interface IWebsiteService
|
||||
{
|
||||
public List<WebPageModel> WebPageModels { get; set; }
|
||||
public List<WebSectionModel> WebSectionModels { get; set; }
|
||||
|
||||
public void Subscribe(Action action);
|
||||
public void Unsubscribe(Action action);
|
||||
public void Update();
|
||||
|
||||
|
||||
public Task Load();
|
||||
|
||||
public bool IsLoaded();
|
||||
}
|
||||
|
||||
public interface INoteService
|
||||
{
|
||||
public List<NoteContentModel> NoteContentModels { get; set; }
|
||||
@@ -312,6 +290,7 @@ public interface IBuildOrderService
|
||||
|
||||
public void RemoveLast();
|
||||
public void Reset();
|
||||
public void Reset(string faction);
|
||||
|
||||
public int GetLastRequestInterval();
|
||||
public string BuildOrderAsYaml();
|
||||
|
||||
@@ -290,6 +290,7 @@ public class BuildOrderService : IBuildOrderService
|
||||
{
|
||||
return (from ordersAtTime in _buildOrder.StartedOrders
|
||||
from orders in ordersAtTime.Value
|
||||
where orders.Harvest() != null
|
||||
where ordersAtTime.Key + (orders.Production() == null
|
||||
? 0
|
||||
: orders.Production().BuildTime) <= interval
|
||||
@@ -298,7 +299,6 @@ public class BuildOrderService : IBuildOrderService
|
||||
ordersAtTime.Key + (orders.Production() == null
|
||||
? 0
|
||||
: orders.Production().BuildTime))
|
||||
where orders.Harvest() != null
|
||||
select orders).ToList();
|
||||
}
|
||||
|
||||
@@ -341,6 +341,13 @@ public class BuildOrderService : IBuildOrderService
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
public void Reset(string faction)
|
||||
{
|
||||
_lastInterval = 0;
|
||||
_buildOrder.Initialize(faction);
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
public int? WillMeetTrainingQueue(EntityModel entity)
|
||||
{
|
||||
var supply = entity.Supply();
|
||||
|
||||
@@ -192,7 +192,7 @@ public class EconomyComparisionService : IEconomyComparisonService
|
||||
if (usedWorkers < harvester.Slots) workersNeeded += 1;
|
||||
}
|
||||
|
||||
if (harvester.RequiresWorker == false)
|
||||
if (!harvester.RequiresWorker)
|
||||
{
|
||||
if (harvester.Resource == ResourceType.Ether)
|
||||
economyAtSecond.Ether += harvester.HarvestedPerInterval * harvester.Slots;
|
||||
|
||||
@@ -80,7 +80,9 @@ public class EconomyService : IEconomyService
|
||||
economyAtSecond.Interval = interval;
|
||||
economyAtSecond.HarvestPoints =
|
||||
(from harvester
|
||||
in buildOrder.GetUndepletedHarvestPointsCompletedBefore(interval + 1) // One second into the future for completed harvest points
|
||||
in buildOrder
|
||||
.GetUndepletedHarvestPointsCompletedBefore(interval +
|
||||
1) // One second into the future for completed harvest points
|
||||
select harvester).ToList();
|
||||
}
|
||||
|
||||
@@ -166,10 +168,8 @@ public class EconomyService : IEconomyService
|
||||
|
||||
foreach (var harvesterPoint in
|
||||
economyAtSecond.HarvestPoints.Select(entity => entity.Harvest()))
|
||||
{
|
||||
//if (harvesterPoint.IsDepleted(economyAtSecond.Interval))
|
||||
// continue;
|
||||
|
||||
switch (harvesterPoint.RequiresWorker)
|
||||
{
|
||||
case true:
|
||||
@@ -207,7 +207,6 @@ public class EconomyService : IEconomyService
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return workersNeeded;
|
||||
}
|
||||
|
||||
@@ -1,9 +1,10 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<TargetFramework>net10.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
<RootNamespace>Services</RootNamespace>
|
||||
</PropertyGroup>
|
||||
|
||||
<PropertyGroup Condition=" '$(Configuration)' == 'Debug' ">
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
using Model.Entity.Data;
|
||||
using Model.Website;
|
||||
using Model.Website.Data;
|
||||
|
||||
namespace Services.Website;
|
||||
|
||||
@@ -7,14 +8,10 @@ public class SearchService : ISearchService
|
||||
{
|
||||
private readonly INoteService noteService;
|
||||
|
||||
|
||||
private readonly IWebsiteService websiteService;
|
||||
|
||||
private bool isLoaded;
|
||||
|
||||
public SearchService(IWebsiteService websiteService, INoteService noteService)
|
||||
public SearchService(INoteService noteService)
|
||||
{
|
||||
this.websiteService = websiteService;
|
||||
this.noteService = noteService;
|
||||
}
|
||||
|
||||
@@ -40,7 +37,6 @@ public class SearchService : ISearchService
|
||||
|
||||
public async Task Load()
|
||||
{
|
||||
await websiteService.Load();
|
||||
await noteService.Load();
|
||||
|
||||
|
||||
@@ -48,7 +44,7 @@ public class SearchService : ISearchService
|
||||
Searches.Add("Notes", new List<SearchPointModel>());
|
||||
Searches.Add("Entities", new List<SearchPointModel>());
|
||||
|
||||
foreach (var webPage in websiteService.WebPageModels)
|
||||
foreach (var webPage in WebsiteData.GetPages())
|
||||
{
|
||||
SearchPoints.Add(new SearchPointModel
|
||||
{
|
||||
@@ -75,7 +71,7 @@ public class SearchService : ISearchService
|
||||
}
|
||||
|
||||
|
||||
foreach (var entity in DATA.Get().Values)
|
||||
foreach (var entity in EntityData.Get().Values)
|
||||
{
|
||||
var summary =
|
||||
entity.Info().Description.Length > 35
|
||||
|
||||
@@ -1,36 +0,0 @@
|
||||
using System.Net.Http.Json;
|
||||
using Model;
|
||||
|
||||
namespace Services.Website;
|
||||
|
||||
public class VariableService : IVariableService
|
||||
{
|
||||
private readonly HttpClient httpClient;
|
||||
|
||||
private bool isLoaded;
|
||||
|
||||
|
||||
public VariableService(HttpClient httpClient)
|
||||
{
|
||||
this.httpClient = httpClient;
|
||||
}
|
||||
|
||||
public Dictionary<string, string> Variables { get; set; } = new();
|
||||
|
||||
public bool IsLoaded()
|
||||
{
|
||||
return isLoaded;
|
||||
}
|
||||
|
||||
public async Task Load()
|
||||
{
|
||||
if (isLoaded) return;
|
||||
|
||||
var variables = (await httpClient.GetFromJsonAsync<Variable[]>("generated/Variables.json"))!
|
||||
.ToList();
|
||||
|
||||
foreach (var variable in variables) Variables.Add(variable.Key, variable.Value);
|
||||
|
||||
isLoaded = true;
|
||||
}
|
||||
}
|
||||
@@ -1,77 +0,0 @@
|
||||
using System.Net.Http.Json;
|
||||
using Model.Website;
|
||||
|
||||
namespace Services.Development;
|
||||
|
||||
public class WebsiteService : IWebsiteService
|
||||
{
|
||||
private readonly HttpClient httpClient;
|
||||
|
||||
private bool isLoaded;
|
||||
|
||||
|
||||
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)
|
||||
{
|
||||
OnChange += action;
|
||||
}
|
||||
|
||||
public void Unsubscribe(Action action)
|
||||
{
|
||||
OnChange -= action;
|
||||
}
|
||||
|
||||
public bool IsLoaded()
|
||||
{
|
||||
return isLoaded;
|
||||
}
|
||||
|
||||
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();
|
||||
|
||||
isLoaded = true;
|
||||
|
||||
SortSql();
|
||||
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
public void Update()
|
||||
{
|
||||
NotifyDataChanged();
|
||||
}
|
||||
|
||||
|
||||
private event Action OnChange = default!;
|
||||
|
||||
private void SortSql()
|
||||
{
|
||||
foreach (var page in WebPageModels)
|
||||
if (page.WebSectionModelId != null)
|
||||
{
|
||||
var webSection =
|
||||
WebSectionModels.Find(webSection => webSection.Id == page.WebSectionModelId);
|
||||
|
||||
webSection.WebPageModels.Add(page);
|
||||
}
|
||||
}
|
||||
|
||||
private void NotifyDataChanged()
|
||||
{
|
||||
OnChange?.Invoke();
|
||||
}
|
||||
}
|
||||
@@ -1,43 +0,0 @@
|
||||
using TestAutomation.Utils;
|
||||
|
||||
namespace TestAutomation;
|
||||
|
||||
public enum DeploymentType
|
||||
{
|
||||
Dev,
|
||||
Local
|
||||
}
|
||||
|
||||
public class BaseTest
|
||||
{
|
||||
protected static readonly TestReport TestReport = new();
|
||||
|
||||
|
||||
protected static Website WebsiteInstance = default!;
|
||||
protected readonly HttpClient HttpClient = new();
|
||||
|
||||
protected static Website Website
|
||||
{
|
||||
get
|
||||
{
|
||||
if (WebsiteInstance == null)
|
||||
{
|
||||
var options = new FirefoxOptions();
|
||||
|
||||
options.AcceptInsecureCertificates = true;
|
||||
|
||||
if (Website.DeploymentType.Equals(DeploymentType.Dev)) options.AddArgument("--headless");
|
||||
options.AddArgument("--ignore-certificate-errors");
|
||||
options.AddArgument("--start-maximized");
|
||||
options.AddArgument("--test-type");
|
||||
options.AddArgument("--allow-running-insecure-content");
|
||||
|
||||
IWebDriver webDriver = new FirefoxDriver(Environment.CurrentDirectory, options);
|
||||
|
||||
WebsiteInstance = new Website(webDriver, TestReport);
|
||||
}
|
||||
|
||||
return WebsiteInstance;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace TestAutomation.Enums;
|
||||
|
||||
public enum ScreenType
|
||||
{
|
||||
Desktop,
|
||||
Tablet,
|
||||
Mobile
|
||||
}
|
||||
@@ -1,29 +0,0 @@
|
||||
using TestAutomation.Shared;
|
||||
using TestAutomation.Utils;
|
||||
|
||||
namespace TestAutomation.Pages;
|
||||
|
||||
public abstract class BasePage : BaseElement
|
||||
{
|
||||
protected BasePage(Website website) : base(website)
|
||||
{
|
||||
}
|
||||
|
||||
private IEnumerable<string> Links =>
|
||||
Website.FindAllWithTag(Website.Find("content"), "a")
|
||||
.Select(x => x.GetAttribute("href"));
|
||||
|
||||
public abstract string Url { get; set; }
|
||||
|
||||
public IEnumerable<string> GetLinks()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Links;
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"Couldn't get links on page {Url}");
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,53 +0,0 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using TestAutomation.Utils;
|
||||
|
||||
namespace TestAutomation.Pages;
|
||||
|
||||
public class DatabasePage : BasePage
|
||||
{
|
||||
public DatabasePage(Website website) : base(website)
|
||||
{
|
||||
}
|
||||
|
||||
private IWebElement FilterNameInput => Website.Find("filterName");
|
||||
|
||||
public override string Url { get; set; } = "database";
|
||||
|
||||
|
||||
private ReadOnlyCollection<IWebElement> EntityNames()
|
||||
{
|
||||
return Website.FindAll("entityName");
|
||||
}
|
||||
|
||||
|
||||
private IWebElement EntityName(string entityType, string entityName)
|
||||
{
|
||||
return Website.Find("entityName",
|
||||
$"{entityType.ToLower()}-{entityName.ToLower()}");
|
||||
}
|
||||
|
||||
public DatabasePage FilterName(string name)
|
||||
{
|
||||
Website.EnterInput(FilterNameInput, name);
|
||||
|
||||
return this;
|
||||
}
|
||||
|
||||
public DatabasePage GetEntityName(string entityType, string entityName, out string result)
|
||||
{
|
||||
result = EntityName(entityType, entityName).Text;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DatabasePage GetEntityName(int index, out string result)
|
||||
{
|
||||
result = EntityNames()[index].Text;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DatabasePage Goto()
|
||||
{
|
||||
Website.Goto(Url);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,49 +0,0 @@
|
||||
using TestAutomation.Utils;
|
||||
|
||||
namespace TestAutomation.Pages;
|
||||
|
||||
public class DatabaseSinglePage : BasePage
|
||||
{
|
||||
public DatabaseSinglePage(Website website) : base(website)
|
||||
{
|
||||
}
|
||||
|
||||
private IWebElement EntityName => Website.Find("entityName");
|
||||
private IWebElement EntityHealth => Website.Find("entityHealth");
|
||||
|
||||
private IWebElement InvalidSearch => Website.Find("invalidSearch");
|
||||
private IWebElement ValidSearch => Website.Find("validSearch");
|
||||
|
||||
public override string Url { get; set; } = "database";
|
||||
|
||||
|
||||
public DatabaseSinglePage GetEntityName(out string result)
|
||||
{
|
||||
result = EntityName.Text;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DatabaseSinglePage GetEntityHealth(out string result)
|
||||
{
|
||||
result = EntityHealth.Text;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DatabaseSinglePage GetInvalidSearch(out string result)
|
||||
{
|
||||
result = InvalidSearch.Text;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DatabaseSinglePage GetValidSearch(out string result)
|
||||
{
|
||||
result = ValidSearch.Text;
|
||||
return this;
|
||||
}
|
||||
|
||||
public DatabaseSinglePage Goto(string searchText)
|
||||
{
|
||||
Website.Goto($"{Url}/{searchText}");
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
using TestAutomation.Utils;
|
||||
|
||||
namespace TestAutomation.Pages;
|
||||
|
||||
public class HarassCalculatorPage : BasePage
|
||||
{
|
||||
public HarassCalculatorPage(Website website) : base(website)
|
||||
{
|
||||
}
|
||||
|
||||
private IWebElement NumberOfWorkersLostToHarass => Website.Find("numberOfWorkersLostToHarass");
|
||||
private IWebElement NumberOfTownHallsExisting => Website.Find("numberOfTownHallsExisting");
|
||||
private IList<IWebElement> OnTownHallTravelTimes => Website.FindChildren("numberOfTownHallTravelTimes", "input");
|
||||
private int TotalAlloyHarassment => Website.FindInt("totalAlloyHarassment");
|
||||
private int WorkerReplacementCost => Website.FindInt("workerReplacementCost");
|
||||
private int DelayedMiningCost => Website.FindInt("delayedMiningCost");
|
||||
private int AverageTravelTime => Website.FindInt("getAverageTravelTime");
|
||||
|
||||
private int ExampleTotalAlloyLoss => Website.FindInt("exampleTotalAlloyLoss");
|
||||
private int ExampleWorkerCost => Website.FindInt("exampleWorkerCost");
|
||||
private int ExampleMiningTimeCost => Website.FindInt("exampleMiningTimeCost");
|
||||
private int ExampleTotalAlloyLossDifference => Website.FindInt("exampleTotalAlloyLossDifference");
|
||||
private int ExampleTotalAlloyLossAccurate => Website.FindInt("exampleTotalAlloyLossAccurate");
|
||||
private int ExampleTotalAlloyLossAccurateDifference => Website.FindInt("exampleTotalAlloyLossAccurateDifference");
|
||||
|
||||
public override string Url { get; set; } = "harass-calculator";
|
||||
|
||||
public HarassCalculatorPage SetWorkersLostToHarass(int number)
|
||||
{
|
||||
Website.EnterInput(NumberOfWorkersLostToHarass, number);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HarassCalculatorPage SetNumberOfTownHallsExisting(int number)
|
||||
{
|
||||
Website.EnterInput(NumberOfTownHallsExisting, number);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HarassCalculatorPage SetTownHallTravelTime(int forTownHall, int number)
|
||||
{
|
||||
Website.EnterInput(OnTownHallTravelTimes[forTownHall], number);
|
||||
return this;
|
||||
}
|
||||
|
||||
public HarassCalculatorPage GetTotalAlloyHarassment(out int result)
|
||||
{
|
||||
result = TotalAlloyHarassment;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
public HarassCalculatorPage GetExampleTotalAlloyLoss(out int result)
|
||||
{
|
||||
result = ExampleTotalAlloyLoss;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HarassCalculatorPage GetExampleWorkerCost(out int result)
|
||||
{
|
||||
result = ExampleWorkerCost;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HarassCalculatorPage GetExampleMiningTimeCost(out int result)
|
||||
{
|
||||
result = ExampleMiningTimeCost;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HarassCalculatorPage GetExampleTotalAlloyLossAccurate(out int result)
|
||||
{
|
||||
result = ExampleTotalAlloyLossAccurate;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HarassCalculatorPage GetExampleTotalAlloyLossDifference(out int result)
|
||||
{
|
||||
result = ExampleTotalAlloyLossDifference;
|
||||
return this;
|
||||
}
|
||||
|
||||
public HarassCalculatorPage GetExampleTotalAlloyLossAccurateDifference(out int result)
|
||||
{
|
||||
result = ExampleTotalAlloyLossAccurateDifference;
|
||||
return this;
|
||||
}
|
||||
|
||||
protected HarassCalculatorPage NavigateTo()
|
||||
{
|
||||
return this;
|
||||
}
|
||||
|
||||
public HarassCalculatorPage Goto()
|
||||
{
|
||||
Website.Goto(Url);
|
||||
return this;
|
||||
}
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
using TestAutomation.Utils;
|
||||
|
||||
namespace TestAutomation.Shared;
|
||||
|
||||
public abstract class BaseElement
|
||||
{
|
||||
protected readonly Website Website;
|
||||
|
||||
protected BaseElement(Website website)
|
||||
{
|
||||
Website = website;
|
||||
}
|
||||
}
|
||||
@@ -1,25 +0,0 @@
|
||||
using TestAutomation.Utils;
|
||||
|
||||
namespace TestAutomation.Shared;
|
||||
|
||||
public class NavigationBar : BaseElement
|
||||
{
|
||||
public NavigationBar(Website website) : base(website)
|
||||
{
|
||||
}
|
||||
|
||||
private IWebElement HomeLink => Website.FindScreenSpecific("homeLink");
|
||||
private IWebElement SearchButton => Website.FindScreenSpecific("searchButton");
|
||||
|
||||
public NavigationBar ClickHomeLink()
|
||||
{
|
||||
Website.Click(HomeLink);
|
||||
return this;
|
||||
}
|
||||
|
||||
public WebsiteSearchDialog ClickSearchButton()
|
||||
{
|
||||
Website.Click(SearchButton);
|
||||
return Website.WebsiteSearchDialog;
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
using TestAutomation.Utils;
|
||||
|
||||
namespace TestAutomation.Shared;
|
||||
|
||||
public class WebsiteSearchDialog : BaseElement
|
||||
{
|
||||
public WebsiteSearchDialog(Website website) : base(website)
|
||||
{
|
||||
}
|
||||
|
||||
public IWebElement SearchBackground => Website.Find("searchBackground");
|
||||
|
||||
public IWebElement SearchInput => Website.Find("searchInput");
|
||||
|
||||
public NavigationBar CloseDialog()
|
||||
{
|
||||
Website.ClickTopLeft();
|
||||
return Website.NavigationBar;
|
||||
}
|
||||
|
||||
public WebsiteSearchDialog Search(string throne)
|
||||
{
|
||||
Website.EnterInput(SearchInput, throne);
|
||||
return this;
|
||||
}
|
||||
|
||||
public DatabaseSinglePage SelectSearchEntity(string throne)
|
||||
{
|
||||
Website.Click(Website.FindButtonWithLabel(throne));
|
||||
return Website.DatabaseSinglePage;
|
||||
}
|
||||
}
|
||||
@@ -1,28 +0,0 @@
|
||||
<Project Sdk="Microsoft.NET.Sdk">
|
||||
|
||||
<PropertyGroup>
|
||||
<TargetFramework>net8.0</TargetFramework>
|
||||
<ImplicitUsings>enable</ImplicitUsings>
|
||||
<Nullable>enable</Nullable>
|
||||
|
||||
<IsPackable>false</IsPackable>
|
||||
</PropertyGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="Discord.Net.Webhook" Version="3.6.0"/>
|
||||
<PackageReference Include="Microsoft.Extensions.Configuration" Version="8.0.14"/>
|
||||
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.0.0"/>
|
||||
<PackageReference Include="NUnit" Version="3.13.2"/>
|
||||
<PackageReference Include="NUnit3TestAdapter" Version="4.2.0"/>
|
||||
<PackageReference Include="NUnit.Analyzers" Version="3.2.0"/>
|
||||
<PackageReference Include="coverlet.collector" Version="3.1.0"/>
|
||||
<PackageReference Include="Selenium.WebDriver" Version="4.1.0"/>
|
||||
<PackageReference Include="Selenium.WebDriver.ChromeDriver" Version="101.0.4951.4100"/>
|
||||
<PackageReference Include="Selenium.WebDriver.GeckoDriver" Version="0.31.0"/>
|
||||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<Folder Include="Pages\"/>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
@@ -1,95 +0,0 @@
|
||||
using TestAutomation.Utils;
|
||||
|
||||
namespace TestAutomation;
|
||||
|
||||
[TestFixture]
|
||||
public class TestHarassCalculator : BaseTest
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
TestReport.CreateTest();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
TestReport.ThrowErrors();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CalculatorInput()
|
||||
{
|
||||
var expectedTotalAlloyHarassment = 240;
|
||||
|
||||
Website.HarassCalculatorPage
|
||||
.Goto()
|
||||
.SetWorkersLostToHarass(3)
|
||||
.SetNumberOfTownHallsExisting(2)
|
||||
.SetTownHallTravelTime(0, 30)
|
||||
.GetTotalAlloyHarassment(out var foundTotalAlloyHarassment);
|
||||
|
||||
TestReport.CheckPassed(expectedTotalAlloyHarassment.Equals(foundTotalAlloyHarassment),
|
||||
TestMessage.CreateFailedMessage($"expectTotalAlloyHarassment of {expectedTotalAlloyHarassment} " +
|
||||
"does not equal " +
|
||||
$"foundTotalAlloyHarassment of {foundTotalAlloyHarassment} "));
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void CalculatedExampleInformation()
|
||||
{
|
||||
var expectedExampleTotalAlloyLoss = 720;
|
||||
var expectedExampleWorkerCost = 300;
|
||||
var expectedExampleMiningTimeCost = 420;
|
||||
var expectedExampleTotalAlloyLossDifference = 300;
|
||||
var expectedExampleTotalAlloyLossAccurate = 450;
|
||||
var expectedExampleTotalAlloyLossAccurateDifference = 270;
|
||||
|
||||
Website.HarassCalculatorPage
|
||||
.Goto()
|
||||
.GetExampleTotalAlloyLoss(out var foundTotalAlloyLoss)
|
||||
.GetExampleWorkerCost(out var foundExampleWorkerCost)
|
||||
.GetExampleMiningTimeCost(out var foundExampleMiningTimeCost)
|
||||
.GetExampleTotalAlloyLossAccurate(out var foundExampleTotalAlloyLossAccurate)
|
||||
.GetExampleTotalAlloyLossDifference(out var foundGetExampleTotalAlloyLossDifference)
|
||||
.GetExampleTotalAlloyLossAccurateDifference(out var foundExampleTotalAlloyLossAccurateDifference);
|
||||
|
||||
TestReport.CheckPassed(expectedExampleTotalAlloyLoss.Equals(foundTotalAlloyLoss),
|
||||
TestMessage.CreateFailedMessage($"expectedExampleTotalAlloyLoss of {expectedExampleTotalAlloyLoss} " +
|
||||
"does not equal " +
|
||||
$"foundTotalAlloyLoss of {foundTotalAlloyLoss} "));
|
||||
|
||||
TestReport.CheckPassed(expectedExampleWorkerCost.Equals(foundExampleWorkerCost),
|
||||
TestMessage.CreateFailedMessage($"expectedExampleWorkerCost of {expectedExampleWorkerCost} " +
|
||||
"does not equal " +
|
||||
$"foundExampleWorkerCost of {foundExampleWorkerCost} "));
|
||||
|
||||
|
||||
TestReport.CheckPassed(expectedExampleMiningTimeCost.Equals(foundExampleMiningTimeCost),
|
||||
TestMessage.CreateFailedMessage($"expectedExampleMiningTimeCost of {expectedExampleMiningTimeCost} " +
|
||||
"does not equal " +
|
||||
$"foundExampleMiningTimeCost of {foundExampleMiningTimeCost} "));
|
||||
|
||||
|
||||
TestReport.CheckPassed(expectedExampleTotalAlloyLossAccurate.Equals(foundExampleTotalAlloyLossAccurate),
|
||||
TestMessage.CreateFailedMessage(
|
||||
$"expectedExampleTotalAlloyLossAccurate of {expectedExampleTotalAlloyLossAccurate} " +
|
||||
"does not equal " +
|
||||
$"foundExampleTotalAlloyLossAccurate of {foundExampleTotalAlloyLossAccurate} "));
|
||||
|
||||
|
||||
TestReport.CheckPassed(expectedExampleTotalAlloyLossDifference.Equals(foundGetExampleTotalAlloyLossDifference),
|
||||
TestMessage.CreateFailedMessage(
|
||||
$"expectedExampleTotalAlloyLossDifference of {expectedExampleTotalAlloyLossDifference} " +
|
||||
"does not equal " +
|
||||
$"foundGetExampleTotalAlloyLossDifference of {foundGetExampleTotalAlloyLossDifference} "));
|
||||
|
||||
|
||||
TestReport.CheckPassed(
|
||||
expectedExampleTotalAlloyLossAccurateDifference.Equals(foundExampleTotalAlloyLossAccurateDifference),
|
||||
TestMessage.CreateFailedMessage(
|
||||
$"expectedExampleTotalAlloyLossAccurateDifference of {expectedExampleTotalAlloyLossAccurateDifference} " +
|
||||
"does not equal " +
|
||||
$"foundExampleTotalAlloyLossAccurateDifference of {foundExampleTotalAlloyLossAccurateDifference} "));
|
||||
}
|
||||
}
|
||||
@@ -1,30 +0,0 @@
|
||||
namespace TestAutomation;
|
||||
|
||||
[TestFixture]
|
||||
public class TestLinks : BaseTest
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
TestReport.CreateTest();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
TestReport.ThrowErrors();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void VerifyPageLinks()
|
||||
{
|
||||
Website.HarassCalculatorPage.Goto();
|
||||
TestReport.VerifyLinks(Website.HarassCalculatorPage).Wait();
|
||||
|
||||
Website.DatabasePage.Goto();
|
||||
TestReport.VerifyLinks(Website.DatabasePage).Wait();
|
||||
|
||||
Website.DatabaseSinglePage.Goto("throne");
|
||||
TestReport.VerifyLinks(Website.DatabaseSinglePage).Wait();
|
||||
}
|
||||
}
|
||||
@@ -1,84 +0,0 @@
|
||||
using TestAutomation.Utils;
|
||||
|
||||
namespace TestAutomation;
|
||||
|
||||
[TestFixture]
|
||||
public class TestSearchFeatures : BaseTest
|
||||
{
|
||||
[SetUp]
|
||||
public void SetUp()
|
||||
{
|
||||
TestReport.CreateTest();
|
||||
}
|
||||
|
||||
[TearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
TestReport.ThrowErrors();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DesktopOpenCloseSearchDialog()
|
||||
{
|
||||
Website
|
||||
.Goto()
|
||||
.NavigationBar
|
||||
.ClickSearchButton()
|
||||
.CloseDialog()
|
||||
.ClickHomeLink();
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DesktopSearchForThrone()
|
||||
{
|
||||
Website
|
||||
.Goto()
|
||||
.NavigationBar.ClickSearchButton()
|
||||
.Search("Throne")
|
||||
.SelectSearchEntity("Throne")
|
||||
.GetEntityName(out var name)
|
||||
.GetEntityHealth(out var health);
|
||||
|
||||
TestReport.CheckPassed(name.Equals("Throne"),
|
||||
new TestMessage { Description = "Couldn't find Throne via search." });
|
||||
TestReport.CheckPassed(!health.Trim().Equals(""),
|
||||
new TestMessage { Description = "Throne has no visible health!" });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DesktopFilterForThrone()
|
||||
{
|
||||
Website.DatabasePage
|
||||
.Goto()
|
||||
.FilterName("Throne")
|
||||
.GetEntityName(0, out var name);
|
||||
|
||||
TestReport.CheckPassed(name.Equals("Throne"),
|
||||
new TestMessage { Description = "Couldn't find Throne via filter." });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void SeeThroneByDefault()
|
||||
{
|
||||
Website.DatabasePage
|
||||
.Goto()
|
||||
.GetEntityName("army", "throne", out var name);
|
||||
|
||||
TestReport.CheckPassed(name.Equals("Throne"),
|
||||
new TestMessage { Description = "Couldn't find Throne on the page by default." });
|
||||
}
|
||||
|
||||
[Test]
|
||||
public void DirectLinkNotThroneFailure()
|
||||
{
|
||||
Website.DatabaseSinglePage
|
||||
.Goto("not throne")
|
||||
.GetInvalidSearch(out var invalidSearch)
|
||||
.GetValidSearch(out var validSearch);
|
||||
|
||||
TestReport.CheckPassed(invalidSearch.Equals("not throne"),
|
||||
new TestMessage { Description = "Couldn't find invalid search text on the page." });
|
||||
TestReport.CheckPassed(validSearch.Equals("Throne"),
|
||||
new TestMessage { Description = "Couldn't find valid search text on the page." });
|
||||
}
|
||||
}
|
||||
@@ -1,8 +0,0 @@
|
||||
namespace TestAutomation.Utils;
|
||||
|
||||
public class Test
|
||||
{
|
||||
public string Name { get; set; } = "Name...";
|
||||
public bool Result { get; set; } = true;
|
||||
public IList<TestMessage> Messages { get; set; } = new List<TestMessage>();
|
||||
}
|
||||
@@ -1,13 +0,0 @@
|
||||
namespace TestAutomation.Utils;
|
||||
|
||||
public class TestMessage
|
||||
{
|
||||
public string Title { get; set; } = "Name...";
|
||||
public string Description { get; set; } = "";
|
||||
public string Color { get; set; } = "FFFFFF";
|
||||
|
||||
public static TestMessage CreateFailedMessage(string description)
|
||||
{
|
||||
return new TestMessage { Title = "Check Failed", Description = description, Color = "FF0000" };
|
||||
}
|
||||
}
|
||||
@@ -1,111 +0,0 @@
|
||||
using System.Globalization;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace TestAutomation.Utils;
|
||||
|
||||
public class TestReport
|
||||
{
|
||||
private List<Test> Tests { get; } = new();
|
||||
|
||||
[MethodImpl(MethodImplOptions.NoInlining)]
|
||||
public Test CreateTest()
|
||||
{
|
||||
Tests.Add(new Test
|
||||
{
|
||||
Name = TestContext.CurrentContext.Test.Name
|
||||
});
|
||||
return Tests.Last();
|
||||
}
|
||||
|
||||
public void ThrowErrors()
|
||||
{
|
||||
if (!Tests.Last().Result)
|
||||
{
|
||||
var messages = string.Join("\n", Tests.Last().Messages.Select(x => x.Description).ToList());
|
||||
|
||||
throw new Exception(
|
||||
$"{Tests.Last().Name} test failed with {Tests.Last().Messages.Count} messages.\n\n{messages}");
|
||||
}
|
||||
}
|
||||
|
||||
public async Task VerifyLinks(BasePage page)
|
||||
{
|
||||
foreach (var link in page.GetLinks())
|
||||
try
|
||||
{
|
||||
if (link.StartsWith("mailto")) continue;
|
||||
|
||||
using var client = new HttpClient();
|
||||
var response = await client.GetAsync(link);
|
||||
|
||||
if (!response.IsSuccessStatusCode)
|
||||
{
|
||||
CheckPassed(false,
|
||||
new TestMessage
|
||||
{
|
||||
Color = "red", Title = "Bad Link",
|
||||
Description = $"{link} failed on page {page.Url} with status code {response.StatusCode}"
|
||||
});
|
||||
Console.WriteLine(response.StatusCode.ToString());
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
CheckPassed(false,
|
||||
new TestMessage
|
||||
{
|
||||
Color = "red", Title = "Bad Link",
|
||||
Description = $"{link} failed on page {page.Url} with stacktrace {e.StackTrace}"
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public void CheckPassed(bool passed, TestMessage message)
|
||||
{
|
||||
if (passed) return;
|
||||
Tests.Last().Result = false;
|
||||
Tests.Last().Messages.Add(message);
|
||||
}
|
||||
|
||||
public bool DidTestsPass()
|
||||
{
|
||||
foreach (var test in Tests)
|
||||
{
|
||||
if (test.Result) continue;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public List<object> GetMessages()
|
||||
{
|
||||
if (DidTestsPass())
|
||||
return new List<object>
|
||||
{
|
||||
new
|
||||
{
|
||||
title = "Passed",
|
||||
color = int.Parse("00FF00", NumberStyles.HexNumber),
|
||||
description = $"All {Tests.Count} tests passed."
|
||||
}
|
||||
};
|
||||
|
||||
var messageList = new List<object>();
|
||||
foreach (var test in Tests)
|
||||
foreach (var message in test.Messages)
|
||||
messageList.Add(
|
||||
new
|
||||
{
|
||||
title = message.Title,
|
||||
color = int.Parse(message.Color, NumberStyles.HexNumber),
|
||||
description = message.Description
|
||||
});
|
||||
|
||||
|
||||
return messageList;
|
||||
}
|
||||
}
|
||||
@@ -1,7 +0,0 @@
|
||||
global using NUnit.Framework;
|
||||
global using OpenQA.Selenium;
|
||||
global using OpenQA.Selenium.Firefox;
|
||||
global using OpenQA.Selenium.Chrome;
|
||||
global using TestAutomation.Pages;
|
||||
global using OpenQA.Selenium.Support.UI;
|
||||
global using OpenQA.Selenium.Support;
|
||||
@@ -1,216 +0,0 @@
|
||||
using System.Collections.ObjectModel;
|
||||
using OpenQA.Selenium.Interactions;
|
||||
using TestAutomation.Enums;
|
||||
using TestAutomation.Shared;
|
||||
|
||||
namespace TestAutomation.Utils;
|
||||
|
||||
public class Website
|
||||
{
|
||||
public static readonly DeploymentType DeploymentType =
|
||||
Environment.GetEnvironmentVariable("TEST_HOOK")!.Contains("localhost")
|
||||
? DeploymentType.Local
|
||||
: DeploymentType.Dev;
|
||||
|
||||
public static readonly string Url =
|
||||
DeploymentType.Equals(DeploymentType.Dev)
|
||||
? "https://calm-mud-04916b210.1.azurestaticapps.net"
|
||||
: "https://localhost:7234";
|
||||
|
||||
public readonly ScreenType ScreenType = ScreenType.Desktop;
|
||||
|
||||
public Website(IWebDriver webDriver, TestReport testReport)
|
||||
{
|
||||
WebDriver = webDriver;
|
||||
TestReport = testReport;
|
||||
|
||||
// Pages
|
||||
HarassCalculatorPage = new HarassCalculatorPage(this);
|
||||
DatabasePage = new DatabasePage(this);
|
||||
DatabaseSinglePage = new DatabaseSinglePage(this);
|
||||
|
||||
// Navigation
|
||||
NavigationBar = new NavigationBar(this);
|
||||
|
||||
// Dialogs
|
||||
WebsiteSearchDialog = new WebsiteSearchDialog(this);
|
||||
}
|
||||
|
||||
public TestReport TestReport { get; set; }
|
||||
|
||||
public IWebDriver WebDriver { get; }
|
||||
|
||||
public HarassCalculatorPage HarassCalculatorPage { get; }
|
||||
public DatabaseSinglePage DatabaseSinglePage { get; }
|
||||
public DatabasePage DatabasePage { get; }
|
||||
public NavigationBar NavigationBar { get; }
|
||||
public WebsiteSearchDialog WebsiteSearchDialog { get; }
|
||||
|
||||
public IWebElement FindScreenSpecific(string byId)
|
||||
{
|
||||
var screenSpecificId = $"{ScreenType.ToString().ToLower()}-{byId}";
|
||||
|
||||
try
|
||||
{
|
||||
return WebDriver.FindElement(By.Id(screenSpecificId));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"Couldn't find {screenSpecificId}. Element does not exist on current page. " +
|
||||
"\n\nPerhaps an Id is missing.");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
public IWebElement Find(string byId, string withParentId)
|
||||
{
|
||||
IWebElement parent;
|
||||
|
||||
try
|
||||
{
|
||||
parent = WebDriver.FindElement(By.Id(withParentId));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"Couldn't find parent {withParentId}. Element does not exist on current page. " +
|
||||
"\n\nPerhaps an Id is missing.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
return parent.FindElement(By.Id(byId));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"Couldn't find {byId}. Element does not exist on current page. " +
|
||||
"\n\nPerhaps an Id is missing.");
|
||||
}
|
||||
}
|
||||
|
||||
public IWebElement Find(string byId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return WebDriver.FindElement(By.Id(byId));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"Couldn't find {byId}. Element does not exist on current page. " +
|
||||
"\n\nPerhaps an Id is missing.");
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlyCollection<IWebElement> FindAll(string byId)
|
||||
{
|
||||
try
|
||||
{
|
||||
return WebDriver.FindElements(By.Id(byId));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"Couldn't find {byId}. Element does not exist on current page. " +
|
||||
"\n\nPerhaps an Id is missing.");
|
||||
}
|
||||
}
|
||||
|
||||
public ReadOnlyCollection<IWebElement> FindAllWithTag(string tag)
|
||||
{
|
||||
return WebDriver.FindElements(By.TagName(tag));
|
||||
}
|
||||
|
||||
public ReadOnlyCollection<IWebElement> FindAllWithTag(IWebElement parent, string tag)
|
||||
{
|
||||
return parent.FindElements(By.TagName(tag));
|
||||
}
|
||||
|
||||
|
||||
public IWebElement FindButtonWithLabel(string label)
|
||||
{
|
||||
try
|
||||
{
|
||||
return WebDriver.FindElement(By.XPath($"//button[@label='{label}']"));
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
throw new Exception($"Couldn't find with label: {label}. Element does not exist on current page. ");
|
||||
}
|
||||
}
|
||||
|
||||
//@FindBy(xpath = "//div[@label='First Name']")
|
||||
|
||||
public IList<IWebElement> FindChildren(string ofId, string tagname)
|
||||
{
|
||||
return WebDriver.FindElements(By.CssSelector($"#{ofId} {tagname}"));
|
||||
}
|
||||
|
||||
public string FindText(string byId)
|
||||
{
|
||||
return WebDriver.FindElement(By.Id(byId)).Text;
|
||||
}
|
||||
|
||||
|
||||
public int FindInt(string byId)
|
||||
{
|
||||
return int.Parse(WebDriver.FindElement(By.Id(byId)).Text);
|
||||
}
|
||||
|
||||
|
||||
public void ClickTopLeft()
|
||||
{
|
||||
new Actions(WebDriver)
|
||||
.MoveByOffset(32, 32)
|
||||
.Click()
|
||||
.Perform();
|
||||
}
|
||||
|
||||
public IWebElement Click(IWebElement element)
|
||||
{
|
||||
try
|
||||
{
|
||||
element.Click();
|
||||
}
|
||||
catch
|
||||
{
|
||||
throw new Exception($"Couldn't click on {element.GetDomProperty("id")}. ");
|
||||
}
|
||||
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
public IWebElement EnterInput<T>(IWebElement element, T input)
|
||||
{
|
||||
element.Clear();
|
||||
element.SendKeys(input!.ToString());
|
||||
element.SendKeys(Keys.Enter);
|
||||
return element;
|
||||
}
|
||||
|
||||
|
||||
public IWebElement EnterInput<T>(string byId, T input)
|
||||
{
|
||||
var element = Find(byId);
|
||||
element.Clear();
|
||||
element.SendKeys(input!.ToString());
|
||||
element.SendKeys(Keys.Enter);
|
||||
return element;
|
||||
}
|
||||
|
||||
public string GetLabel(string byId)
|
||||
{
|
||||
return Find(byId).Text;
|
||||
}
|
||||
|
||||
public Website Goto()
|
||||
{
|
||||
WebDriver.Navigate().GoToUrl($"{Url}");
|
||||
return this;
|
||||
}
|
||||
|
||||
public void Goto(string path)
|
||||
{
|
||||
var url = $"{Url}/{path}";
|
||||
|
||||
WebDriver.Navigate().GoToUrl($"{url}");
|
||||
}
|
||||
}
|
||||
@@ -1,33 +0,0 @@
|
||||
using System.Text;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace TestAutomation;
|
||||
|
||||
[SetUpFixture]
|
||||
public class Tests : BaseTest
|
||||
{
|
||||
[OneTimeSetUp]
|
||||
public void Setup()
|
||||
{
|
||||
Website.Goto();
|
||||
Website.WebDriver.Manage().Timeouts().ImplicitWait = TimeSpan.FromSeconds(15);
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void TearDown()
|
||||
{
|
||||
Website.WebDriver.Quit();
|
||||
|
||||
var message = new
|
||||
{
|
||||
content = "Test Report " + DateTime.Now.ToString("dd/MM/yyyy"),
|
||||
embeds = TestReport.GetMessages()
|
||||
};
|
||||
|
||||
var content = new StringContent(JsonConvert.SerializeObject(message), Encoding.UTF8, "application/json");
|
||||
|
||||
if (Environment.GetEnvironmentVariable("TEST_HOOK") == null) return;
|
||||
|
||||
HttpClient.PostAsync(Environment.GetEnvironmentVariable("TEST_HOOK"), content).Wait();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using NUnit.Framework;
|
||||
using Tests.Helpers;
|
||||
|
||||
namespace Tests;
|
||||
|
||||
[SetUpFixture]
|
||||
public class GlobalSetup
|
||||
{
|
||||
[OneTimeSetUp]
|
||||
public async Task GlobalStart()
|
||||
{
|
||||
if (Environment.GetEnvironmentVariable("RUN_AGAINST_PRODUCTION") != "true")
|
||||
{
|
||||
await LocalServer.StartAsync();
|
||||
}
|
||||
}
|
||||
|
||||
[OneTimeTearDown]
|
||||
public void GlobalStop()
|
||||
{
|
||||
LocalServer.Stop();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,106 @@
|
||||
using System.Diagnostics;
|
||||
using System.Text.RegularExpressions;
|
||||
|
||||
namespace Tests.Helpers;
|
||||
|
||||
public static class LocalServer
|
||||
{
|
||||
private static Process? _process;
|
||||
public static string? BaseUrl { get; private set; }
|
||||
|
||||
public static async Task StartAsync()
|
||||
{
|
||||
if (_process != null) return;
|
||||
|
||||
var root = FindProjectRoot();
|
||||
var webProject = Path.Combine(root, "Web");
|
||||
|
||||
Console.WriteLine($"[DEBUG_LOG] Starting local server for project: {webProject}");
|
||||
|
||||
_process = new Process
|
||||
{
|
||||
StartInfo = new ProcessStartInfo
|
||||
{
|
||||
FileName = "dotnet",
|
||||
Arguments = $"run --project \"{webProject}\" --urls http://127.0.0.1:0",
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
UseShellExecute = false,
|
||||
CreateNoWindow = true,
|
||||
WorkingDirectory = root
|
||||
}
|
||||
};
|
||||
|
||||
var tcs = new TaskCompletionSource<string>();
|
||||
|
||||
_process.OutputDataReceived += (s, e) =>
|
||||
{
|
||||
if (string.IsNullOrEmpty(e.Data)) return;
|
||||
Console.WriteLine($"[SERVER] {e.Data}");
|
||||
var match = Regex.Match(e.Data, @"Now listening on:\s+(http://127.0.0.1:\d+)");
|
||||
if (match.Success)
|
||||
{
|
||||
tcs.TrySetResult(match.Groups[1].Value);
|
||||
}
|
||||
};
|
||||
|
||||
_process.ErrorDataReceived += (s, e) =>
|
||||
{
|
||||
if (!string.IsNullOrEmpty(e.Data))
|
||||
Console.Error.WriteLine($"[SERVER ERROR] {e.Data}");
|
||||
};
|
||||
|
||||
_process.Start();
|
||||
_process.BeginOutputReadLine();
|
||||
_process.BeginErrorReadLine();
|
||||
|
||||
// Wait for the URL to be parsed from output
|
||||
BaseUrl = await Task.WhenAny(tcs.Task, Task.Delay(30000)) == tcs.Task
|
||||
? tcs.Task.Result
|
||||
: null;
|
||||
|
||||
if (BaseUrl == null)
|
||||
{
|
||||
Stop();
|
||||
throw new Exception("Timeout waiting for local server to start and provide a URL.");
|
||||
}
|
||||
|
||||
Console.WriteLine($"[DEBUG_LOG] Local server started at: {BaseUrl}");
|
||||
}
|
||||
|
||||
public static void Stop()
|
||||
{
|
||||
if (_process != null && !_process.HasExited)
|
||||
{
|
||||
Console.WriteLine("[DEBUG_LOG] Stopping local server...");
|
||||
_process.Kill(true);
|
||||
_process.Dispose();
|
||||
_process = null;
|
||||
}
|
||||
}
|
||||
|
||||
private static string FindProjectRoot()
|
||||
{
|
||||
var current = AppDomain.CurrentDomain.BaseDirectory;
|
||||
while (!string.IsNullOrEmpty(current) && !File.Exists(Path.Combine(current, "IGP.sln")))
|
||||
{
|
||||
var parent = Path.GetDirectoryName(current);
|
||||
if (parent == current) break;
|
||||
current = parent;
|
||||
}
|
||||
|
||||
if (string.IsNullOrEmpty(current) || !File.Exists(Path.Combine(current, "IGP.sln")))
|
||||
{
|
||||
// Fallback to searching up from current directory if BaseDirectory fails
|
||||
current = Directory.GetCurrentDirectory();
|
||||
while (!string.IsNullOrEmpty(current) && !File.Exists(Path.Combine(current, "IGP.sln")))
|
||||
{
|
||||
var parent = Path.GetDirectoryName(current);
|
||||
if (parent == current) break;
|
||||
current = parent;
|
||||
}
|
||||
}
|
||||
|
||||
return current ?? throw new Exception("Could not find project root containing IGP.sln");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
using Microsoft.Playwright;
|
||||
using Tests.Pages;
|
||||
using Tests.Shared;
|
||||
|
||||
namespace Tests.Helpers;
|
||||
|
||||
public class Website
|
||||
{
|
||||
public IPage Page { get; }
|
||||
public bool RunAgainstProduction { get; }
|
||||
public string BaseUrl { get; }
|
||||
|
||||
public Website(IPage page)
|
||||
{
|
||||
Page = page;
|
||||
RunAgainstProduction = Environment.GetEnvironmentVariable("RUN_AGAINST_PRODUCTION") == "true";
|
||||
|
||||
BaseUrl = RunAgainstProduction ? "https://igpfanreference.ca" : (LocalServer.BaseUrl ?? "http://localhost:5234");
|
||||
|
||||
NavigationBar = new NavigationBar(this);
|
||||
SearchDialog = new SearchDialog(this);
|
||||
BuildCalculatorPage = new BuildCalculatorPage(this);
|
||||
HarassCalculatorPage = new HarassCalculatorPage(this);
|
||||
DatabasePage = new DatabasePage(this);
|
||||
DatabaseSinglePage = new DatabaseSinglePage(this);
|
||||
}
|
||||
|
||||
public ILocator Locator(string selector) => Page.Locator(selector);
|
||||
public ILocator FindById(string id) => Page.Locator($"#{id}");
|
||||
public NavigationBar NavigationBar { get; }
|
||||
public SearchDialog SearchDialog { get; }
|
||||
public BuildCalculatorPage BuildCalculatorPage { get; }
|
||||
public HarassCalculatorPage HarassCalculatorPage { get; }
|
||||
public DatabasePage DatabasePage { get; }
|
||||
public DatabaseSinglePage DatabaseSinglePage { get; }
|
||||
|
||||
public async Task GotoAsync(string? path = null)
|
||||
{
|
||||
var url = path is null ? BaseUrl : $"{BaseUrl}/{path}";
|
||||
await Page.GotoAsync(url);
|
||||
}
|
||||
|
||||
public async Task ClickElementAsync(ILocator locator) => await locator.ClickAsync();
|
||||
|
||||
public async Task EnterInputAsync(ILocator locator, string value)
|
||||
{
|
||||
await locator.FillAsync(value);
|
||||
await locator.PressAsync("Enter");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
using Tests.Helpers;
|
||||
|
||||
namespace Tests.Pages;
|
||||
|
||||
public abstract class BasePage
|
||||
{
|
||||
protected Website Website { get; }
|
||||
|
||||
protected BasePage(Website website)
|
||||
{
|
||||
Website = website;
|
||||
}
|
||||
|
||||
public abstract string Url { get; }
|
||||
|
||||
public virtual async Task GotoAsync()
|
||||
{
|
||||
await Website.GotoAsync(Url);
|
||||
}
|
||||
|
||||
public async Task<List<string>> GetLinksAsync()
|
||||
{
|
||||
var content = Website.FindById("content");
|
||||
var links = content.Locator("a");
|
||||
var hrefs = await links.EvaluateAllAsync<string[]>("els => els.map(el => el.getAttribute('href')).filter(Boolean)");
|
||||
return hrefs.ToList();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,48 @@
|
||||
namespace Tests.Pages.BuildCalculator;
|
||||
|
||||
public class ArmyComponent
|
||||
{
|
||||
private readonly Website _website;
|
||||
public ArmyComponent(Website website) => _website = website;
|
||||
|
||||
public ILocator ArmyView => _website.Locator(".armyView");
|
||||
|
||||
public ILocator DisplayValue(string label) =>
|
||||
_website.Locator(".displayContainer").Filter(new() { HasText = label }).Locator(".displayContent");
|
||||
|
||||
public ILocator ArmyCards => ArmyView.Locator(".armyCard");
|
||||
|
||||
public async Task<string> GetArmyCompletedAtAsync() =>
|
||||
(await DisplayValue("Army Completed At").TextContentAsync())?.Trim() ?? "";
|
||||
|
||||
public async Task<string> GetArmyAttackingAtAsync() =>
|
||||
(await DisplayValue("Army Attacking At").TextContentAsync())?.Trim() ?? "";
|
||||
|
||||
public async Task<IReadOnlyList<string>> GetArmyUnitNamesAsync()
|
||||
{
|
||||
var cards = await ArmyCards.AllAsync();
|
||||
var names = new List<string>();
|
||||
foreach (var card in cards)
|
||||
{
|
||||
var text = (await card.InnerTextAsync()).Trim();
|
||||
var match = System.Text.RegularExpressions.Regex.Match(text, @"\d+x\s*(.+)");
|
||||
names.Add(match.Success ? match.Groups[1].Value.Trim() : text);
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
public async Task<IReadOnlyList<(string Name, int Count)>> GetArmyUnitCountsAsync()
|
||||
{
|
||||
var cards = await ArmyCards.AllAsync();
|
||||
var counts = new List<(string, int)>();
|
||||
foreach (var card in cards)
|
||||
{
|
||||
var countEl = card.Locator(".armyCount");
|
||||
var nameEl = card.Locator("div").Last;
|
||||
var count = (await countEl.TextContentAsync())?.Replace("x", "").Trim() ?? "0";
|
||||
var name = (await nameEl.TextContentAsync())?.Trim() ?? "";
|
||||
counts.Add((name, int.Parse(count)));
|
||||
}
|
||||
return counts;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace Tests.Pages.BuildCalculator;
|
||||
|
||||
public class BankComponent
|
||||
{
|
||||
private readonly Website _website;
|
||||
public BankComponent(Website website) => _website = website;
|
||||
|
||||
public ILocator BankContainer => _website.Locator(".bankContainer");
|
||||
|
||||
public ILocator DisplayValue(string label) =>
|
||||
BankContainer.Locator(".displayContainer").Filter(new() { HasText = label }).Locator(".displayContent");
|
||||
|
||||
public async Task<string> GetTimeAsync() => (await DisplayValue("Time").TextContentAsync())?.Trim() ?? "";
|
||||
public async Task<string> GetAlloyAsync() => (await DisplayValue("Alloy").TextContentAsync())?.Trim() ?? "";
|
||||
public async Task<string> GetEtherAsync() => (await DisplayValue("Ether").TextContentAsync())?.Trim() ?? "";
|
||||
public async Task<string> GetPyreAsync() => (await DisplayValue("Pyre").TextContentAsync())?.Trim() ?? "";
|
||||
public async Task<string> GetSupplyAsync() => (await DisplayValue("Supply").TextContentAsync())?.Trim() ?? "";
|
||||
|
||||
public async Task<string> GetWorkerCountAsync() =>
|
||||
(await BankContainer.Locator(".workerText").Locator(".displayContent").Nth(0).TextContentAsync())?.Trim() ?? "";
|
||||
|
||||
public async Task<string> GetBusyWorkerCountAsync() =>
|
||||
(await BankContainer.Locator(".workerText").Locator(".displayContent").Nth(1).TextContentAsync())?.Trim() ?? "";
|
||||
|
||||
public async Task<string> GetCreatingWorkerCountAsync() =>
|
||||
(await BankContainer.Locator(".workerText").Locator(".displayContent").Nth(2).TextContentAsync())?.Trim() ?? "";
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user