mirror of
https://github.com/gwdevhub/Daybreak.git
synced 2026-09-19 14:55:24 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
986d09867c | ||
|
|
97cb2151f9 | ||
|
|
338ac100a1 | ||
|
|
01badcee6a |
@@ -11,14 +11,12 @@ public sealed class FocusViewOptions
|
||||
[OptionName(Name = "Enabled", Description = "If true, the focus view is enabled, showing live information from the game")]
|
||||
public bool Enabled { get; set; }
|
||||
|
||||
[JsonProperty(nameof(MemoryReaderFrequency))]
|
||||
[OptionRange<double>(MinValue = 0, MaxValue = 1000)]
|
||||
[OptionName(Name = "Memory Reader Frequency", Description = "Measured in ms. Sets how often should the launcher poll information from the game. Actual frequency is capped by the memory reading speed")]
|
||||
public double MemoryReaderFrequency { get; set; } = 0;
|
||||
|
||||
[OptionName(Name = "Inventory Component Enabled", Description = "If true, the focus view will show a component with the inventory contents")]
|
||||
public bool InventoryComponentVisible { get; set; }
|
||||
|
||||
[OptionName(Name = "Minimap Component Enabled", Description = "If true, the focus view will show a minimap component")]
|
||||
public bool MinimapComponentVisible { get; set; }
|
||||
|
||||
[JsonProperty(nameof(ExperienceDisplay))]
|
||||
[OptionName(Name = "Experience Display Mode", Description = "Sets how should the experience display show the information")]
|
||||
public ExperienceDisplay ExperienceDisplay { get; set; }
|
||||
|
||||
@@ -0,0 +1,13 @@
|
||||
using Daybreak.Attributes;
|
||||
using Newtonsoft.Json;
|
||||
|
||||
namespace Daybreak.Configuration.Options;
|
||||
|
||||
[OptionsName(Name = "Memory Reader")]
|
||||
public sealed class MemoryReaderOptions
|
||||
{
|
||||
[JsonProperty(nameof(MemoryReaderFrequency))]
|
||||
[OptionRange<double>(MinValue = 0, MaxValue = 1000)]
|
||||
[OptionName(Name = "Memory Reader Frequency", Description = "Measured in ms. Sets how often should the launcher polls information from the game. Actual frequency is capped by the memory reading speed")]
|
||||
public double MemoryReaderFrequency { get; set; } = 0;
|
||||
}
|
||||
@@ -177,6 +177,7 @@ public static class ProjectConfiguration
|
||||
services.AddSingleton<INotificationStorage, NotificationStorage>();
|
||||
services.AddSingleton<ITradeAlertingService, TradeAlertingService>();
|
||||
services.AddSingleton<IModsManager, ModsManager>();
|
||||
services.AddSingleton<IGuildwarsMemoryCache, GuildwarsMemoryCache>();
|
||||
services.AddScoped<ICredentialManager, CredentialManager>();
|
||||
services.AddScoped<IApplicationLauncher, ApplicationLauncher>();
|
||||
services.AddScoped<IScreenshotProvider, ScreenshotProvider>();
|
||||
@@ -328,6 +329,7 @@ public static class ProjectConfiguration
|
||||
optionsProducer.RegisterOptions<BrowserOptions>();
|
||||
optionsProducer.RegisterOptions<BuildSynchronizationOptions>();
|
||||
optionsProducer.RegisterOptions<FocusViewOptions>();
|
||||
optionsProducer.RegisterOptions<MemoryReaderOptions>();
|
||||
optionsProducer.RegisterOptions<LauncherOptions>();
|
||||
optionsProducer.RegisterOptions<ToolboxOptions>();
|
||||
optionsProducer.RegisterOptions<UModOptions>();
|
||||
|
||||
@@ -281,18 +281,19 @@ public partial class PlayerResourcesComponent : UserControl
|
||||
private void UpdateGameData()
|
||||
{
|
||||
if (this.DataContext is not GameData gameData ||
|
||||
gameData.MainPlayer is not MainPlayerInformation)
|
||||
gameData.MainPlayer is null ||
|
||||
gameData.Session is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.CurrentExperienceInLevel = this.experienceCalculator.GetExperienceForCurrentLevel(gameData.MainPlayer!.Value.Experience);
|
||||
this.NextLevelExperienceThreshold = this.experienceCalculator.GetNextExperienceThreshold(gameData.MainPlayer!.Value.Experience);
|
||||
this.TotalFoes = (int)(gameData.Session!.Value.FoesKilled + gameData.Session.Value.FoesToKill);
|
||||
this.Vanquishing = gameData.Session.Value.FoesToKill + gameData.Session.Value.FoesKilled > 0U;
|
||||
this.TitleActive = gameData.MainPlayer.Value.TitleInformation is not null && gameData.MainPlayer.Value.TitleInformation.Value.IsValid;
|
||||
this.CurrentExperienceInLevel = this.experienceCalculator.GetExperienceForCurrentLevel(gameData.MainPlayer.Experience);
|
||||
this.NextLevelExperienceThreshold = this.experienceCalculator.GetNextExperienceThreshold(gameData.MainPlayer.Experience);
|
||||
this.TotalFoes = (int)(gameData.Session.FoesKilled + gameData.Session.FoesToKill);
|
||||
this.Vanquishing = gameData.Session.FoesToKill + gameData.Session.FoesKilled > 0U;
|
||||
this.TitleActive = gameData.MainPlayer.TitleInformation is not null && gameData.MainPlayer.TitleInformation.IsValid;
|
||||
|
||||
if (gameData.MainPlayer.Value.TitleInformation is TitleInformation titleInformation && titleInformation.IsValid)
|
||||
if (gameData.MainPlayer.TitleInformation is TitleInformation titleInformation && titleInformation.IsValid)
|
||||
{
|
||||
if (titleInformation.MaxTierNumber == titleInformation.TierNumber)
|
||||
{
|
||||
@@ -324,7 +325,8 @@ public partial class PlayerResourcesComponent : UserControl
|
||||
|
||||
private void UpdateExperienceText()
|
||||
{
|
||||
if (this.DataContext is not GameData gameData)
|
||||
if (this.DataContext is not GameData gameData ||
|
||||
gameData.MainPlayer is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -332,22 +334,22 @@ public partial class PlayerResourcesComponent : UserControl
|
||||
switch (this.liveOptions.Value.ExperienceDisplay)
|
||||
{
|
||||
case Configuration.FocusView.ExperienceDisplay.CurrentLevelCurrentAndCurrentLevelMax:
|
||||
var currentExperienceInLevel = this.experienceCalculator.GetExperienceForCurrentLevel(gameData.MainPlayer!.Value.Experience);
|
||||
var nextLevelExperienceThreshold = this.experienceCalculator.GetNextExperienceThreshold(gameData.MainPlayer!.Value.Experience);
|
||||
var currentExperienceInLevel = this.experienceCalculator.GetExperienceForCurrentLevel(gameData.MainPlayer.Experience);
|
||||
var nextLevelExperienceThreshold = this.experienceCalculator.GetNextExperienceThreshold(gameData.MainPlayer.Experience);
|
||||
this.ExperienceBarText = $"{(int)currentExperienceInLevel} / {(int)nextLevelExperienceThreshold} XP";
|
||||
break;
|
||||
case Configuration.FocusView.ExperienceDisplay.TotalCurretAndTotalMax:
|
||||
var currentTotalExperience = gameData.MainPlayer!.Value.Experience;
|
||||
var currentTotalExperience = gameData.MainPlayer.Experience;
|
||||
var requiredTotalExperience = this.experienceCalculator.GetTotalExperienceForNextLevel(currentTotalExperience);
|
||||
this.ExperienceBarText = $"{(int)currentTotalExperience} / {(int)requiredTotalExperience} XP";
|
||||
break;
|
||||
case Configuration.FocusView.ExperienceDisplay.RemainingUntilNextLevel:
|
||||
var remainingExperience = this.experienceCalculator.GetRemainingExperienceForNextLevel(gameData.MainPlayer!.Value.Experience);
|
||||
var remainingExperience = this.experienceCalculator.GetRemainingExperienceForNextLevel(gameData.MainPlayer.Experience);
|
||||
this.ExperienceBarText = $"Remaining {(int)remainingExperience} XP";
|
||||
break;
|
||||
case Configuration.FocusView.ExperienceDisplay.Percentage:
|
||||
var currentExperienceInLevel2 = this.experienceCalculator.GetExperienceForCurrentLevel(gameData.MainPlayer!.Value.Experience);
|
||||
var nextLevelExperienceThreshold2 = this.experienceCalculator.GetNextExperienceThreshold(gameData.MainPlayer!.Value.Experience);
|
||||
var currentExperienceInLevel2 = this.experienceCalculator.GetExperienceForCurrentLevel(gameData.MainPlayer.Experience);
|
||||
var nextLevelExperienceThreshold2 = this.experienceCalculator.GetNextExperienceThreshold(gameData.MainPlayer.Experience);
|
||||
this.ExperienceBarText = $"{(int)((double)currentExperienceInLevel2 / (double)nextLevelExperienceThreshold2 * 100)}% XP";
|
||||
break;
|
||||
}
|
||||
@@ -439,7 +441,8 @@ public partial class PlayerResourcesComponent : UserControl
|
||||
|
||||
private void UpdateVanquishingText()
|
||||
{
|
||||
if (this.DataContext is not GameData gameData)
|
||||
if (this.DataContext is not GameData gameData ||
|
||||
gameData.Session is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -447,20 +450,21 @@ public partial class PlayerResourcesComponent : UserControl
|
||||
switch (this.liveOptions.Value.VanquishingDisplay)
|
||||
{
|
||||
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
|
||||
this.VanquishingText = $"{gameData.Session!.Value.FoesKilled} / {(int)this.TotalFoes} Foes Killed";
|
||||
this.VanquishingText = $"{gameData.Session.FoesKilled} / {(int)this.TotalFoes} Foes Killed";
|
||||
break;
|
||||
case Configuration.FocusView.PointsDisplay.Remaining:
|
||||
this.VanquishingText = $"Remaining {gameData.Session!.Value.FoesToKill} Foes";
|
||||
this.VanquishingText = $"Remaining {gameData.Session.FoesToKill} Foes";
|
||||
break;
|
||||
case Configuration.FocusView.PointsDisplay.Percentage:
|
||||
this.VanquishingText = $"{(int)((double)gameData.Session!.Value.FoesKilled / (double)this.TotalFoes * 100)}% Foes Killed";
|
||||
this.VanquishingText = $"{(int)((double)gameData.Session.FoesKilled / (double)this.TotalFoes * 100)}% Foes Killed";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateHealthText()
|
||||
{
|
||||
if (this.DataContext is not GameData gameData)
|
||||
if (this.DataContext is not GameData gameData ||
|
||||
gameData.MainPlayer is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -468,20 +472,21 @@ public partial class PlayerResourcesComponent : UserControl
|
||||
switch (this.liveOptions.Value.HealthDisplay)
|
||||
{
|
||||
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
|
||||
this.HealthBarText = $"{(int)gameData.MainPlayer!.Value.CurrentHealth} / {(int)gameData.MainPlayer.Value.MaxHealth} Health";
|
||||
this.HealthBarText = $"{(int)gameData.MainPlayer.CurrentHealth} / {(int)gameData.MainPlayer.MaxHealth} Health";
|
||||
break;
|
||||
case Configuration.FocusView.PointsDisplay.Remaining:
|
||||
this.HealthBarText = $"Remaining {(int)gameData.MainPlayer!.Value.CurrentHealth} Health";
|
||||
this.HealthBarText = $"Remaining {(int)gameData.MainPlayer.CurrentHealth} Health";
|
||||
break;
|
||||
case Configuration.FocusView.PointsDisplay.Percentage:
|
||||
this.HealthBarText = $"{(int)(gameData.MainPlayer!.Value.CurrentHealth / gameData.MainPlayer.Value.MaxHealth * 100)}% Health";
|
||||
this.HealthBarText = $"{(int)(gameData.MainPlayer.CurrentHealth / gameData.MainPlayer.MaxHealth * 100)}% Health";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void UpdateEnergyText()
|
||||
{
|
||||
if (this.DataContext is not GameData gameData)
|
||||
if (this.DataContext is not GameData gameData ||
|
||||
gameData.MainPlayer is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -489,13 +494,13 @@ public partial class PlayerResourcesComponent : UserControl
|
||||
switch (this.liveOptions.Value.EnergyDisplay)
|
||||
{
|
||||
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
|
||||
this.EnergyBarText = $"{(int)gameData.MainPlayer!.Value.CurrentEnergy} / {(int)gameData.MainPlayer.Value.MaxEnergy} Energy";
|
||||
this.EnergyBarText = $"{(int)gameData.MainPlayer.CurrentEnergy} / {(int)gameData.MainPlayer.MaxEnergy} Energy";
|
||||
break;
|
||||
case Configuration.FocusView.PointsDisplay.Remaining:
|
||||
this.EnergyBarText = $"Remaining {(int)gameData.MainPlayer!.Value.CurrentEnergy} Energy";
|
||||
this.EnergyBarText = $"Remaining {(int)gameData.MainPlayer.CurrentEnergy} Energy";
|
||||
break;
|
||||
case Configuration.FocusView.PointsDisplay.Percentage:
|
||||
this.EnergyBarText = $"{(int)(gameData.MainPlayer!.Value.CurrentEnergy / gameData.MainPlayer.Value.MaxEnergy * 100)}% Energy";
|
||||
this.EnergyBarText = $"{(int)(gameData.MainPlayer.CurrentEnergy / gameData.MainPlayer.MaxEnergy * 100)}% Energy";
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -43,7 +43,7 @@ public partial class LivingEntityContextMenu : UserControl
|
||||
|
||||
private void NpcDefinitionTextBlock_MouseLeftButtonDown(object _, MouseButtonEventArgs e)
|
||||
{
|
||||
this.LivingEntityContextMenuClicked?.Invoke(this, this.DataContext as LivingEntity? ?? default);
|
||||
this.LivingEntityContextMenuClicked?.Invoke(this, this.DataContext as LivingEntity ?? default);
|
||||
}
|
||||
|
||||
private void PrimaryProfessionTextBlock_MouseLeftButtonDown(object _, MouseButtonEventArgs e)
|
||||
|
||||
@@ -19,6 +19,6 @@ public partial class MapIconContextMenu : UserControl
|
||||
|
||||
private void TextBlock_MouseLeftButtonDown(object _, MouseButtonEventArgs e)
|
||||
{
|
||||
this.MapIconContextMenuClicked?.Invoke(this, this.DataContext as MapIcon? ?? default);
|
||||
this.MapIconContextMenuClicked?.Invoke(this, this.DataContext as MapIcon ?? default);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,7 +86,7 @@ public partial class GuildwarsMinimap : UserControl
|
||||
public event EventHandler<LivingEntity>? LivingEntityClicked;
|
||||
public event EventHandler<PlayerInformation>? PlayerInformationClicked;
|
||||
public event EventHandler<MapIcon>? MapIconClicked;
|
||||
public event EventHandler<Profession?> ProfessionClicked;
|
||||
public event EventHandler<Profession>? ProfessionClicked;
|
||||
|
||||
public GuildwarsMinimap()
|
||||
:this(
|
||||
@@ -119,6 +119,11 @@ public partial class GuildwarsMinimap : UserControl
|
||||
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
|
||||
{
|
||||
base.OnPropertyChanged(e);
|
||||
if (!this.IsEnabled)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (e.Property == ActualHeightProperty ||
|
||||
e.Property == ActualWidthProperty)
|
||||
{
|
||||
@@ -146,7 +151,8 @@ public partial class GuildwarsMinimap : UserControl
|
||||
|
||||
private void UpdateGameData()
|
||||
{
|
||||
if(this.GameData.Valid is false)
|
||||
if(this.GameData is null ||
|
||||
this.GameData.Valid is false)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -162,7 +168,7 @@ public partial class GuildwarsMinimap : UserControl
|
||||
}
|
||||
|
||||
this.TargetEntityId = this.GameData.Session?.CurrentTargetId ?? 0;
|
||||
this.TargetEntityModelId = (int?)this.GameData.LivingEntities?.FirstOrDefault(e => e.Id == this.TargetEntityId).ModelType ?? 0;
|
||||
this.TargetEntityModelId = (int?)this.GameData.LivingEntities?.FirstOrDefault(e => e.Id == this.TargetEntityId)?.ModelType ?? 0;
|
||||
var screenVirtualWidth = this.ActualWidth / this.Zoom;
|
||||
var screenVirtualHeight = this.ActualHeight / this.Zoom;
|
||||
var position = debounceResponse.MainPlayer.Position!.Value;
|
||||
@@ -202,7 +208,8 @@ public partial class GuildwarsMinimap : UserControl
|
||||
|
||||
private void DrawMap()
|
||||
{
|
||||
if (this.PathingData.Trapezoids is null)
|
||||
if (this.PathingData is null ||
|
||||
this.PathingData.Trapezoids is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -582,23 +589,26 @@ public partial class GuildwarsMinimap : UserControl
|
||||
|
||||
private void GuildwarsMinimap_MouseMove(object sender, MouseEventArgs e)
|
||||
{
|
||||
if (this.GameData.Valid is false)
|
||||
if (this.GameData.Valid is false ||
|
||||
this.GameData.MainPlayer is null ||
|
||||
this.GameData.Party is null ||
|
||||
this.GameData.WorldPlayers is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.DragMinimap();
|
||||
if (this.CheckMouseOverEntity(this.GameData.Party!.OfType<IEntity>()) is not null)
|
||||
if (this.CheckMouseOverEntity(this.GameData.Party.OfType<IEntity>()) is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.CheckMouseOverEntity(this.GameData.WorldPlayers!.OfType<IEntity>()) is not null)
|
||||
if (this.CheckMouseOverEntity(this.GameData.WorldPlayers.OfType<IEntity>()) is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.CheckMouseOverEntity(Enumerable.Repeat(this.GameData.MainPlayer.As<IEntity>(), 1)) is not null)
|
||||
if (this.CheckMouseOverEntity(Enumerable.Repeat(this.GameData.MainPlayer.Cast<IEntity>(), 1)) is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
@@ -613,14 +623,14 @@ public partial class GuildwarsMinimap : UserControl
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.CheckMouseOverEntity(this.GameData.MainPlayer!.Value.QuestLog!
|
||||
if (this.CheckMouseOverEntity(this.GameData.MainPlayer.QuestLog!
|
||||
.Where(entity => this.drawingService.IsEntityOnScreen(entity.Position, out _, out _))
|
||||
.OfType<IPositionalEntity>()) is not null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (this.CheckMouseOverEntity(this.GameData.MainPlayer!.Value.QuestLog!
|
||||
if (this.CheckMouseOverEntity(this.GameData.MainPlayer.QuestLog!
|
||||
.Where(entity => !this.drawingService.IsEntityOnScreen(entity.Position, out _, out _))
|
||||
.Select(oldQuestMetadata =>
|
||||
{
|
||||
@@ -665,32 +675,32 @@ public partial class GuildwarsMinimap : UserControl
|
||||
|
||||
private void QuestContextMenu_QuestContextMenuClicked(object _, QuestMetadata? quest)
|
||||
{
|
||||
if (quest is not QuestMetadata)
|
||||
if (quest is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.QuestMetadataClicked?.Invoke(this, quest.Value);
|
||||
this.QuestMetadataClicked?.Invoke(this, quest);
|
||||
}
|
||||
|
||||
private void PlayerContextMenu_PlayerContextMenuClicked(object _, PlayerInformation? playerInformation)
|
||||
{
|
||||
if (playerInformation is not PlayerInformation)
|
||||
if (playerInformation is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.PlayerInformationClicked?.Invoke(this, playerInformation.Value);
|
||||
this.PlayerInformationClicked?.Invoke(this, playerInformation);
|
||||
}
|
||||
|
||||
private void LivingEntityContextMenu_LivingEntityContextMenuClicked(object _, LivingEntity? livingEntity)
|
||||
{
|
||||
if (livingEntity is not LivingEntity)
|
||||
if (livingEntity is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.LivingEntityClicked?.Invoke(this, livingEntity.Value);
|
||||
this.LivingEntityClicked?.Invoke(this, livingEntity);
|
||||
}
|
||||
|
||||
private void LivingEntityContextMenu_LivingEntityProfessionContextMenuClicked(object _, Profession? e)
|
||||
@@ -705,12 +715,12 @@ public partial class GuildwarsMinimap : UserControl
|
||||
|
||||
private void MapIconContextMenu_MapIconContextMenuClicked(object _, MapIcon? mapIcon)
|
||||
{
|
||||
if (mapIcon is not MapIcon)
|
||||
if (mapIcon is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.MapIconClicked?.Invoke(this, mapIcon.Value);
|
||||
this.MapIconClicked?.Invoke(this, mapIcon);
|
||||
}
|
||||
|
||||
private void MaximizeButton_Clicked(object sender, EventArgs e)
|
||||
|
||||
@@ -18,6 +18,6 @@ public partial class PlayerContextMenu : UserControl
|
||||
|
||||
private void TextBlock_MouseLeftButtonDown(object _, MouseButtonEventArgs e)
|
||||
{
|
||||
this.PlayerContextMenuClicked?.Invoke(this, this.DataContext as PlayerInformation? ?? default);
|
||||
this.PlayerContextMenuClicked?.Invoke(this, this.DataContext as PlayerInformation ?? default);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,6 +19,6 @@ public partial class QuestContextMenu : UserControl
|
||||
|
||||
private void TextBlock_MouseLeftButtonDown(object _, MouseButtonEventArgs e)
|
||||
{
|
||||
this.QuestContextMenuClicked?.Invoke(this, this.DataContext as QuestMetadata? ?? default);
|
||||
this.QuestContextMenuClicked?.Invoke(this, this.DataContext as QuestMetadata ?? default);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,43 @@
|
||||
using System;
|
||||
using System.Extensions;
|
||||
using System.Globalization;
|
||||
using System.Windows;
|
||||
using System.Windows.Data;
|
||||
|
||||
namespace Daybreak.Converters;
|
||||
|
||||
public sealed class BooleanToGridLengthConverter : IValueConverter
|
||||
{
|
||||
private static GridLength Collapsed = new(0);
|
||||
|
||||
public GridLength VisibleValue { get; set; }
|
||||
|
||||
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
return this.GetVisibility(value);
|
||||
}
|
||||
|
||||
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
|
||||
{
|
||||
throw new NotImplementedException();
|
||||
}
|
||||
|
||||
private object GetVisibility(object value)
|
||||
{
|
||||
if (value is not bool)
|
||||
{
|
||||
return this.VisibleValue;
|
||||
}
|
||||
|
||||
var objValue = value.Cast<bool>();
|
||||
if (objValue)
|
||||
{
|
||||
return this.VisibleValue;
|
||||
}
|
||||
else
|
||||
{
|
||||
return Collapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,7 +13,7 @@
|
||||
<LangVersion>preview</LangVersion>
|
||||
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
|
||||
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
|
||||
<Version>0.9.8.74</Version>
|
||||
<Version>0.9.8.78</Version>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<UserSecretsId>cfb2a489-db80-448d-a969-80270f314c46</UserSecretsId>
|
||||
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
|
||||
|
||||
@@ -123,17 +123,14 @@ public partial class MainWindow : MetroWindow
|
||||
},
|
||||
onNone: async () =>
|
||||
{
|
||||
var maybeImageStream = await this.bloogumClient.GetRandomScreenShot().ConfigureAwait(true);
|
||||
maybeImageStream.DoAny(
|
||||
onSome: (stream) =>
|
||||
{
|
||||
var bitmapImage = new BitmapImage();
|
||||
bitmapImage.BeginInit();
|
||||
bitmapImage.StreamSource = stream;
|
||||
bitmapImage.EndInit();
|
||||
this.SetImage(bitmapImage);
|
||||
this.CreditText = "http://bloogum.net/guildwars";
|
||||
});
|
||||
var maybeImageSource = await this.bloogumClient.GetImage(true).ConfigureAwait(true);
|
||||
if (maybeImageSource is null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
this.SetImage(maybeImageSource);
|
||||
this.CreditText = "http://bloogum.net/guildwars";
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct Bag
|
||||
public sealed class Bag
|
||||
{
|
||||
public List<IBagContent> Items { get; init; }
|
||||
public int Capacity { get; init; }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct BagItem : IBagContent
|
||||
public sealed class BagItem : IBagContent
|
||||
{
|
||||
public ItemBase Item { get; init; }
|
||||
public uint Slot { get; init; }
|
||||
|
||||
@@ -0,0 +1,136 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public sealed class Campaign
|
||||
{
|
||||
public static Campaign Core { get; } = new()
|
||||
{
|
||||
Id = 0,
|
||||
Name = "Core",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Guild_Wars_Core",
|
||||
Continents = new List<Continent>
|
||||
{
|
||||
Continent.TheBattleIsles,
|
||||
Continent.TheMists
|
||||
}
|
||||
};
|
||||
|
||||
public static Campaign Prophecies { get; } = new()
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Prophecies",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Guild_Wars_Prophecies",
|
||||
Continents = new List<Continent>
|
||||
{
|
||||
Continent.Tyria
|
||||
}
|
||||
};
|
||||
|
||||
public static Campaign Factions { get; } = new()
|
||||
{
|
||||
Id = 2,
|
||||
Name = "Factions",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Guild_Wars_Factions",
|
||||
Continents = new List<Continent>
|
||||
{
|
||||
Continent.Cantha
|
||||
}
|
||||
};
|
||||
|
||||
public static Campaign Nightfall { get; } = new()
|
||||
{
|
||||
Id = 3,
|
||||
Name = "Nightfall",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Guild_Wars_Nightfall",
|
||||
Continents = new List<Continent>
|
||||
{
|
||||
Continent.Elona,
|
||||
Continent.RealmOfTorment
|
||||
}
|
||||
};
|
||||
|
||||
public static Campaign EyeOfTheNorth { get; } = new()
|
||||
{
|
||||
Id = 4,
|
||||
Name = "Eye of the North",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Guild_Wars_Eye_of_the_North",
|
||||
Continents = new List<Continent>
|
||||
{
|
||||
Continent.Tyria
|
||||
}
|
||||
};
|
||||
|
||||
public static Campaign BonusMissionPack { get; } = new()
|
||||
{
|
||||
Id = 5,
|
||||
Name = "Bonus Mission Pack",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Guild_Wars_Bonus_Mission_Pack",
|
||||
Continents = new List<Continent>
|
||||
{
|
||||
Continent.Tyria,
|
||||
Continent.Elona,
|
||||
Continent.Cantha
|
||||
}
|
||||
};
|
||||
|
||||
public static IReadOnlyList<Campaign> Campaigns { get; } = new List<Campaign>
|
||||
{
|
||||
Core,
|
||||
Prophecies,
|
||||
Factions,
|
||||
Nightfall,
|
||||
EyeOfTheNorth,
|
||||
BonusMissionPack
|
||||
};
|
||||
|
||||
public static bool TryParse(int id, out Campaign campaign)
|
||||
{
|
||||
campaign = Campaigns.Where(campaign => campaign.Id == id).FirstOrDefault()!;
|
||||
if (campaign is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
public static bool TryParse(string name, out Campaign campaign)
|
||||
{
|
||||
campaign = Campaigns.Where(region => region.Name == name).FirstOrDefault()!;
|
||||
if (campaign is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
public static Campaign Parse(int id)
|
||||
{
|
||||
if (TryParse(id, out var campaign) is false)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not find a campaign with id {id}");
|
||||
}
|
||||
|
||||
return campaign;
|
||||
}
|
||||
public static Campaign Parse(string name)
|
||||
{
|
||||
if (TryParse(name, out var region) is false)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not find a campaign with name {name}");
|
||||
}
|
||||
|
||||
return region;
|
||||
}
|
||||
|
||||
private Campaign()
|
||||
{
|
||||
}
|
||||
|
||||
public int Id { get; init; }
|
||||
public string? Name { get; init; }
|
||||
public string? WikiUrl { get; init; }
|
||||
public IReadOnlyList<Continent>? Continents { get; init; }
|
||||
}
|
||||
@@ -0,0 +1,152 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public sealed class Continent
|
||||
{
|
||||
public static Continent Tyria { get; } = new Continent
|
||||
{
|
||||
Id = 0,
|
||||
Name = "Tyria",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Tyria",
|
||||
Regions = new List<Region>
|
||||
{
|
||||
Region.Ascalon,
|
||||
Region.PresearingAscalon,
|
||||
Region.CrystalDesert,
|
||||
Region.Kryta,
|
||||
Region.MaguumaJungle,
|
||||
Region.RingOfFireIslands,
|
||||
Region.ShiverpeakMountains,
|
||||
Region.CharrHomelands,
|
||||
Region.DepthsOfTyria,
|
||||
Region.FarShiverpeaks,
|
||||
Region.TarnishedCoast,
|
||||
Region.TheFlightNorth,
|
||||
Region.TheRiseOfTheWhiteMantle
|
||||
}
|
||||
};
|
||||
|
||||
public static Continent TheMists { get; } = new Continent
|
||||
{
|
||||
Id = 1,
|
||||
Name = "The Mists",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/The_Mists",
|
||||
Regions = new List<Region>
|
||||
{
|
||||
Region.HeroesAscent
|
||||
}
|
||||
};
|
||||
|
||||
public static Continent Cantha { get; } = new Continent
|
||||
{
|
||||
Id = 2,
|
||||
Name = "Cantha",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Cantha",
|
||||
Regions = new List<Region>
|
||||
{
|
||||
Region.ShingJeaIsland,
|
||||
Region.KainengCity,
|
||||
Region.EchovaldForest,
|
||||
Region.TheJadeSea,
|
||||
Region.TheTenguAccords
|
||||
}
|
||||
};
|
||||
|
||||
public static Continent TheBattleIsles { get; } = new Continent
|
||||
{
|
||||
Id = 3,
|
||||
Name = "The Battle Isles",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/The_Battle_Isles",
|
||||
Regions = new List<Region>
|
||||
{
|
||||
Region.TheBattleIsles,
|
||||
}
|
||||
};
|
||||
|
||||
public static Continent Elona { get; } = new Continent
|
||||
{
|
||||
Id = 4,
|
||||
Name = "Elona",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Elona",
|
||||
Regions = new List<Region>
|
||||
{
|
||||
Region.Istan,
|
||||
Region.Kourna,
|
||||
Region.Vabbi,
|
||||
Region.TheDesolation,
|
||||
Region.TheBattleOfJahai
|
||||
}
|
||||
};
|
||||
|
||||
public static Continent RealmOfTorment { get; } = new Continent
|
||||
{
|
||||
Id = 5,
|
||||
Name = "Realm of Torment",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Realm_of_Torment",
|
||||
Regions = new List<Region>
|
||||
{
|
||||
Region.RealmOfTorment
|
||||
}
|
||||
};
|
||||
|
||||
public static IReadOnlyList<Continent> Continents { get; } = new List<Continent>
|
||||
{
|
||||
Tyria,
|
||||
TheMists,
|
||||
Cantha,
|
||||
TheBattleIsles,
|
||||
Elona,
|
||||
RealmOfTorment
|
||||
};
|
||||
|
||||
public static bool TryParse(int id, out Continent continent)
|
||||
{
|
||||
continent = Continents.Where(continent => continent.Id == id).FirstOrDefault()!;
|
||||
if (continent is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
public static bool TryParse(string name, out Continent continent)
|
||||
{
|
||||
continent = Continents.Where(continent => continent.Name == name).FirstOrDefault()!;
|
||||
if (continent is null)
|
||||
{
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
public static Continent Parse(int id)
|
||||
{
|
||||
if (TryParse(id, out var continent) is false)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not find a continent with id {id}");
|
||||
}
|
||||
|
||||
return continent;
|
||||
}
|
||||
public static Continent Parse(string name)
|
||||
{
|
||||
if (TryParse(name, out var continent) is false)
|
||||
{
|
||||
throw new InvalidOperationException($"Could not find a continent with name {name}");
|
||||
}
|
||||
|
||||
return continent;
|
||||
}
|
||||
|
||||
private Continent()
|
||||
{
|
||||
}
|
||||
|
||||
public int Id { get; init; }
|
||||
public string? Name { get; init; }
|
||||
public string? WikiUrl { get; init; }
|
||||
public IReadOnlyList<Region>? Regions { get; init; }
|
||||
}
|
||||
@@ -1,9 +1,8 @@
|
||||
using Daybreak.Models.Guildwars;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Daybreak.Models;
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct GameData
|
||||
public sealed class GameData
|
||||
{
|
||||
public bool Valid { get; init; }
|
||||
public MainPlayerInformation? MainPlayer { get; init; }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct InventoryData
|
||||
public sealed class InventoryData
|
||||
{
|
||||
public Bag? Backpack { get; init; }
|
||||
public Bag? BeltPouch { get; init; }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct LivingEntity : IEntity
|
||||
public sealed class LivingEntity : IEntity
|
||||
{
|
||||
public int Id { get; init; }
|
||||
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct LoginData
|
||||
public sealed class LoginData
|
||||
{
|
||||
public string Email { get; init; }
|
||||
public string PlayerName { get; init; }
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct MainPlayerInformation : IEntity
|
||||
public sealed class MainPlayerInformation : IEntity
|
||||
{
|
||||
public string? Name { get; init; }
|
||||
public uint Timer { get; init; }
|
||||
|
||||
+258
-258
File diff suppressed because it is too large
Load Diff
@@ -1,6 +1,6 @@
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct MapIcon : IPositionalEntity
|
||||
public sealed class MapIcon : IPositionalEntity
|
||||
{
|
||||
public Position? Position { get; init; }
|
||||
public GuildwarsIcon? Icon { get; init; }
|
||||
|
||||
@@ -77,9 +77,8 @@ public sealed class Npc
|
||||
public static readonly Npc Krytan = new() { Ids = new int[] { 1492, 1960, 1964, 1966, 1967, 1991, 2008, 2009, 2020, 2032, 2049, 2120, 2858, 2867, 2869 }, Name = "Krytan", WikiUrl = "https://wiki.guildwars.com/wiki/Krytan" };
|
||||
public static readonly Npc Gwen = new() { Ids = new int[] { 1493, 1494, 5966, 5970 }, Name = "Gwen", WikiUrl = "https://wiki.guildwars.com/wiki/Gwen" };
|
||||
public static readonly Npc FarrahCappo = new() { Ids = new int[] { 1497, 2863 }, Name = "Farrah Cappo", WikiUrl = "https://wiki.guildwars.com/wiki/Farrah_Cappo" };
|
||||
public static readonly Npc Cynn = new() { Ids = new int[] { 1501, 1924, 2128, 2853, 3489, 4565, 4575, 5988 }, Name = "Cynn", WikiUrl = "https://wiki.guildwars.com/wiki/Cynn" };
|
||||
public static readonly Npc Devona = new() { Ids = new int[] { 1502, 1932, 2132, 2854, 3492, 3509, 4569, 4579, 5993 }, Name = "Devona", WikiUrl = "https://wiki.guildwars.com/wiki/Devona" };
|
||||
public static readonly Npc BrotherMhenlo = new() { Ids = new int[] { 1503 }, Name = "Brother Mhenlo", WikiUrl = "https://wiki.guildwars.com/wiki/Brother_Mhenlo" };
|
||||
public static readonly Npc Cynn = new() { Ids = new int[] { 1501, 1924, 2128, 2853, 3489, 4565, 4575, 5988, 4585 }, Name = "Cynn", WikiUrl = "https://wiki.guildwars.com/wiki/Cynn" };
|
||||
public static readonly Npc Devona = new() { Ids = new int[] { 1502, 1932, 2132, 2854, 3492, 3509, 4569, 4579, 5993, 4589 }, Name = "Devona", WikiUrl = "https://wiki.guildwars.com/wiki/Devona" };
|
||||
public static readonly Npc AscalonDuke = new() { Ids = new int[] { 1504, 1511 }, Name = "Ascalon Duke", WikiUrl = "https://wiki.guildwars.com/wiki/Guild_Wars_Wiki:Projects/NPC_models/Humans/Ascalon" };
|
||||
public static readonly Npc PrinceRurik = new() { Ids = new int[] { 1505 }, Name = "Prince Rurik", WikiUrl = "https://wiki.guildwars.com/wiki/Prince_Rurik" };
|
||||
public static readonly Npc LadyAlthea = new() { Ids = new int[] { 1507 }, Name = "Lady Althea", WikiUrl = "https://wiki.guildwars.com/wiki/Lady_Althea" };
|
||||
@@ -111,7 +110,7 @@ public sealed class Npc
|
||||
public static readonly Npc DwarvenScout = new() { Ids = new int[] { 1567 }, Name = "Dwarven Scout", WikiUrl = "https://wiki.guildwars.com/wiki/Dwarven_Scout" };
|
||||
public static readonly Npc CharrChaot = new() { Ids = new int[] { 1635 }, Name = "Charr Chaot", WikiUrl = "https://wiki.guildwars.com/wiki/Charr_Chaot" };
|
||||
public static readonly Npc CharrAshenClaw = new() { Ids = new int[] { 1638 }, Name = "Charr Ashen Claw", WikiUrl = "https://wiki.guildwars.com/wiki/Charr_Ashen_Claw" };
|
||||
public static readonly Npc CharrShaman = new() { Ids = new int[] { 1646, 5711 }, Name = "Charr Shaman", WikiUrl = "https://wiki.guildwars.com/wiki/Charr_Shaman" };
|
||||
public static readonly Npc CharrShaman = new() { Ids = new int[] { 1646, 5711, 4972 }, Name = "Charr Shaman", WikiUrl = "https://wiki.guildwars.com/wiki/Charr_Shaman" };
|
||||
public static readonly Npc CharrAxeFiend = new() { Ids = new int[] { 1651, 7802 }, Name = "Charr Axe Fiend", WikiUrl = "https://wiki.guildwars.com/wiki/Charr_Axe_Fiend" };
|
||||
public static readonly Npc CharrBladeStorm = new() { Ids = new int[] { 1653 }, Name = "Charr Blade Storm", WikiUrl = "https://wiki.guildwars.com/wiki/Charr_Blade_Storm" };
|
||||
public static readonly Npc CharrHunter = new() { Ids = new int[] { 1656 }, Name = "Charr Hunter", WikiUrl = "https://wiki.guildwars.com/wiki/Charr_Hunter" };
|
||||
@@ -175,7 +174,7 @@ public sealed class Npc
|
||||
public static readonly Npc EnemyPriest = new() { Ids = new int[] { 1871 }, Name = "Enemy Priest", WikiUrl = "https://wiki.guildwars.com/wiki/Enemy_Priest" };
|
||||
public static readonly Npc ForgottenChampion = new() { Ids = new int[] { 1872 }, Name = "Forgotten Champion", WikiUrl = "https://wiki.guildwars.com/wiki/Forgotten_Champion" };
|
||||
public static readonly Npc ForgottenAvenger = new() { Ids = new int[] { 1873 }, Name = "Forgotten Avenger", WikiUrl = "https://wiki.guildwars.com/wiki/Forgotten_Avenger" };
|
||||
public static readonly Npc Forgotten = new() { Ids = new int[] { 1874 }, Name = "Forgotten", WikiUrl = "https://wiki.guildwars.com/wiki/Forgotten" };
|
||||
public static readonly Npc Forgotten = new() { Ids = new int[] { 1874, 5002 }, Name = "Forgotten", WikiUrl = "https://wiki.guildwars.com/wiki/Forgotten" };
|
||||
public static readonly Npc Dunham = new() { Ids = new int[] { 1890, 1897, 1904, 1911, 1919, 1920, 2847 }, Name = "Dunham", WikiUrl = "https://wiki.guildwars.com/wiki/Dunham" };
|
||||
public static readonly Npc Claude = new() { Ids = new int[] { 1891, 1898, 1905, 1912, 1921, 2848 }, Name = "Claude", WikiUrl = "https://wiki.guildwars.com/wiki/Claude" };
|
||||
public static readonly Npc Orion = new() { Ids = new int[] { 1892, 1899, 1906, 1913, 1923, 2849 }, Name = "Orion", WikiUrl = "https://wiki.guildwars.com/wiki/Orion" };
|
||||
@@ -184,10 +183,10 @@ public sealed class Npc
|
||||
public static readonly Npc Stefan = new() { Ids = new int[] { 1895, 1902, 1909, 1917, 1931, 2845 }, Name = "Stefan", WikiUrl = "https://wiki.guildwars.com/wiki/Stefan" };
|
||||
public static readonly Npc Reyna = new() { Ids = new int[] { 1896, 1903, 1910, 1918, 1933, 2846 }, Name = "Reyna", WikiUrl = "https://wiki.guildwars.com/wiki/Reyna" };
|
||||
public static readonly Npc Lina = new() { Ids = new int[] { 1915, 1927, 1928, 2851, 5991 }, Name = "Lina", WikiUrl = "https://wiki.guildwars.com/wiki/Lina" };
|
||||
public static readonly Npc Eve = new() { Ids = new int[] { 1922, 2873, 3488, 4564, 4574, 5987 }, Name = "Eve", WikiUrl = "https://wiki.guildwars.com/wiki/Eve" };
|
||||
public static readonly Npc Mhenlo = new() { Ids = new int[] { 1926, 2136, 2855, 3121, 4577, 5990 }, Name = "Mhenlo", WikiUrl = "https://wiki.guildwars.com/wiki/Mhenlo" };
|
||||
public static readonly Npc Aidan = new() { Ids = new int[] { 1934, 2124, 2852, 3493, 4570, 4580, 5994 }, Name = "Aidan", WikiUrl = "https://wiki.guildwars.com/wiki/Aidan" };
|
||||
public static readonly Npc Olias = new() { Ids = new int[] { 1935 }, Name = "Olias", WikiUrl = "https://wiki.guildwars.com/wiki/Olias" };
|
||||
public static readonly Npc Eve = new() { Ids = new int[] { 1922, 2873, 3488, 4564, 4574, 5987, 4584 }, Name = "Eve", WikiUrl = "https://wiki.guildwars.com/wiki/Eve" };
|
||||
public static readonly Npc Mhenlo = new() { Ids = new int[] { 1926, 2136, 2855, 3121, 4577, 5990, 4587, 1503 }, Name = "Mhenlo", WikiUrl = "https://wiki.guildwars.com/wiki/Mhenlo" };
|
||||
public static readonly Npc Aidan = new() { Ids = new int[] { 1934, 2124, 2852, 3493, 4570, 4580, 5994, 4590 }, Name = "Aidan", WikiUrl = "https://wiki.guildwars.com/wiki/Aidan" };
|
||||
public static readonly Npc Olias = new() { Ids = new int[] { 1935, 1938 }, Name = "Olias", WikiUrl = "https://wiki.guildwars.com/wiki/Olias" };
|
||||
public static readonly Npc LyssasMuse = new() { Ids = new int[] { 1944 }, Name = "Lyssa's Muse", WikiUrl = "https://wiki.guildwars.com/wiki/Lyssa%27s_Muse" };
|
||||
public static readonly Npc VoiceOfGrenth = new() { Ids = new int[] { 1945 }, Name = "Voice Of Grenth", WikiUrl = "https://wiki.guildwars.com/wiki/Voice_of_Grenth" };
|
||||
public static readonly Npc AvatarOfDwayna = new() { Ids = new int[] { 1946 }, Name = "Avatar Of Dwayna", WikiUrl = "https://wiki.guildwars.com/wiki/Avatar_of_Dwayna" };
|
||||
@@ -200,7 +199,7 @@ public sealed class Npc
|
||||
public static readonly Npc AscalonSettler = new() { Ids = new int[] { 1986, 1987 }, Name = "Ascalon Settler", WikiUrl = "https://wiki.guildwars.com/wiki/Ascalon_Settler" };
|
||||
public static readonly Npc SettlementGuard = new() { Ids = new int[] { 1989 }, Name = "Settlement Guard", WikiUrl = "https://wiki.guildwars.com/wiki/Settlement_Guard" };
|
||||
public static readonly Npc CaptainGreywind = new() { Ids = new int[] { 1990 }, Name = "Captain Greywind", WikiUrl = "https://wiki.guildwars.com/wiki/Captain_Greywind" };
|
||||
public static readonly Npc AscalonianGhost = new() { Ids = new int[] { 1998, 2141, 2353, 2354, 2355, 2534 }, Name = "Ascalonian Ghost", WikiUrl = "https://wiki.guildwars.com/wiki/Guild_Wars_Wiki:Projects/NPC_models/Ghosts" };
|
||||
public static readonly Npc AscalonianGhost = new() { Ids = new int[] { 1998, 2141, 2353, 2354, 2355, 2534, 5617 }, Name = "Ascalonian Ghost", WikiUrl = "https://wiki.guildwars.com/wiki/Guild_Wars_Wiki:Projects/NPC_models/Ghosts" };
|
||||
public static readonly Npc KrytanSmith = new() { Ids = new int[] { 2000 }, Name = "Krytan Smith", WikiUrl = "https://wiki.guildwars.com/wiki/Guild_Wars_Wiki:Projects/NPC_models/Humans/Kryta" };
|
||||
public static readonly Npc WhiteMantleZealot = new() { Ids = new int[] { 2006, 2206, 2207 }, Name = "White Mantle Zealot", WikiUrl = "https://wiki.guildwars.com/wiki/White_Mantle_Zealot" };
|
||||
public static readonly Npc CaptainGrumby = new() { Ids = new int[] { 2016 }, Name = "Captain Grumby", WikiUrl = "https://wiki.guildwars.com/wiki/Captain_Grumby" };
|
||||
@@ -318,7 +317,7 @@ public sealed class Npc
|
||||
public static readonly Npc BurningTitan = new() { Ids = new int[] { 2670 }, Name = "Burning Titan", WikiUrl = "https://wiki.guildwars.com/wiki/Burning_Titan" };
|
||||
public static readonly Npc HandOfTheTitans = new() { Ids = new int[] { 2671 }, Name = "Hand Of The Titans", WikiUrl = "https://wiki.guildwars.com/wiki/Hand_of_the_Titans" };
|
||||
public static readonly Npc FistOfTheTitans = new() { Ids = new int[] { 2672 }, Name = "Fist Of The Titans", WikiUrl = "https://wiki.guildwars.com/wiki/Fist_of_the_Titans" };
|
||||
public static readonly Npc UndeadLich = new() { Ids = new int[] { 2695 }, Name = "Undead Lich", WikiUrl = "https://wiki.guildwars.com/wiki/Undead_Lich" };
|
||||
public static readonly Npc UndeadLich = new() { Ids = new int[] { 2695, 4953 }, Name = "Undead Lich", WikiUrl = "https://wiki.guildwars.com/wiki/Undead_Lich" };
|
||||
public static readonly Npc UndeadPrinceRurik = new() { Ids = new int[] { 2698 }, Name = "Undead Prince Rurik", WikiUrl = "https://wiki.guildwars.com/wiki/Undead_Prince_Rurik" };
|
||||
public static readonly Npc ZaimGrimeclaw = new() { Ids = new int[] { 2700 }, Name = "Zaim Grimeclaw", WikiUrl = "https://wiki.guildwars.com/wiki/Zaim_Grimeclaw" };
|
||||
public static readonly Npc Ruinwing = new() { Ids = new int[] { 2729 }, Name = "Ruinwing", WikiUrl = "https://wiki.guildwars.com/wiki/Ruinwing" };
|
||||
@@ -626,7 +625,7 @@ public sealed class Npc
|
||||
public static readonly Npc AssassinsConstruct = new() { Ids = new int[] { 4081, 4090 }, Name = "Assassin's Construct", WikiUrl = "https://wiki.guildwars.com/wiki/Assassin%27s_Construct" };
|
||||
public static readonly Npc ElementalsConstruct = new() { Ids = new int[] { 4093 }, Name = "Elemental's Construct", WikiUrl = "https://wiki.guildwars.com/wiki/Elemental%27s_Construct" };
|
||||
public static readonly Npc RangersConstruct = new() { Ids = new int[] { 4096 }, Name = "Ranger's Construct", WikiUrl = "https://wiki.guildwars.com/wiki/Ranger%27s_Construct" };
|
||||
public static readonly Npc ShiroTagachi = new() { Ids = new int[] { 4120 }, Name = "Shiro Tagachi", WikiUrl = "https://wiki.guildwars.com/wiki/Shiro_Tagachi" };
|
||||
public static readonly Npc ShiroTagachi = new() { Ids = new int[] { 4120, 4952 }, Name = "Shiro Tagachi", WikiUrl = "https://wiki.guildwars.com/wiki/Shiro_Tagachi" };
|
||||
public static readonly Npc ShirokenAssassin = new() { Ids = new int[] { 4125 }, Name = "Shiro'ken Assassin", WikiUrl = "https://wiki.guildwars.com/wiki/Shiro%27ken_Assassin" };
|
||||
public static readonly Npc ShirokenNecromancer = new() { Ids = new int[] { 4127 }, Name = "Shiro'ken Necromancer", WikiUrl = "https://wiki.guildwars.com/wiki/Shiro%27ken_Necromancer" };
|
||||
public static readonly Npc ShirokenWarrior = new() { Ids = new int[] { 4130 }, Name = "Shiro'ken Warrior", WikiUrl = "https://wiki.guildwars.com/wiki/Shiro%27ken_Warrior" };
|
||||
@@ -692,18 +691,18 @@ public sealed class Npc
|
||||
public static readonly Npc Dehjah = new() { Ids = new int[] { 4406 }, Name = "Dehjah", WikiUrl = "https://wiki.guildwars.com/wiki/Dehjah" };
|
||||
public static readonly Npc BoklonBlackwater = new() { Ids = new int[] { 4414 }, Name = "Boklon Blackwater", WikiUrl = "https://wiki.guildwars.com/wiki/Boklon_Blackwater" };
|
||||
public static readonly Npc GlugKlugg = new() { Ids = new int[] { 4420 }, Name = "Glug Klugg", WikiUrl = "https://wiki.guildwars.com/wiki/Glug_Klugg" };
|
||||
public static readonly Npc ZhedShadowhoof = new() { Ids = new int[] { 4437, 4940 }, Name = "Zhed Shadowhoof", WikiUrl = "https://wiki.guildwars.com/wiki/Zhed_Shadowhoof" };
|
||||
public static readonly Npc ZhedShadowhoof = new() { Ids = new int[] { 4437, 4940, 4440 }, Name = "Zhed Shadowhoof", WikiUrl = "https://wiki.guildwars.com/wiki/Zhed_Shadowhoof" };
|
||||
public static readonly Npc Tahlkora = new() { Ids = new int[] { 4456, 4459 }, Name = "Tahlkora", WikiUrl = "https://wiki.guildwars.com/wiki/Tahlkora" };
|
||||
public static readonly Npc MasterOfWhispers = new() { Ids = new int[] { 4464 }, Name = "Master Of Whispers", WikiUrl = "https://wiki.guildwars.com/wiki/Master_of_Whispers" };
|
||||
public static readonly Npc AcolyteJin = new() { Ids = new int[] { 4469 }, Name = "Acolyte Jin", WikiUrl = "https://wiki.guildwars.com/wiki/Acolyte_Jin" };
|
||||
public static readonly Npc AcolyteSousuke = new() { Ids = new int[] { 4487 }, Name = "Acolyte Sousuke", WikiUrl = "https://wiki.guildwars.com/wiki/Acolyte_Sousuke" };
|
||||
public static readonly Npc AcolyteSousuke = new() { Ids = new int[] { 4487, 4490 }, Name = "Acolyte Sousuke", WikiUrl = "https://wiki.guildwars.com/wiki/Acolyte_Sousuke" };
|
||||
public static readonly Npc Melonni = new() { Ids = new int[] { 4493, 4496 }, Name = "Melonni ", WikiUrl = "https://wiki.guildwars.com/wiki/Melonni" };
|
||||
public static readonly Npc Khim = new() { Ids = new int[] { 4531, 4549, 4578 }, Name = "Khim", WikiUrl = "https://wiki.guildwars.com/wiki/Khim" };
|
||||
public static readonly Npc Herta = new() { Ids = new int[] { 4532, 4537, 4544, 4551, 4566, 4576, 5989 }, Name = "Herta", WikiUrl = "https://wiki.guildwars.com/wiki/Herta" };
|
||||
public static readonly Npc Gehraz = new() { Ids = new int[] { 4533, 4540, 4547, 4554, 4571, 4581 }, Name = "Gehraz", WikiUrl = "https://wiki.guildwars.com/wiki/Gehraz" };
|
||||
public static readonly Npc Sogolon = new() { Ids = new int[] { 4534, 4541, 4548, 4555, 4572, 4582 }, Name = "Sogolon", WikiUrl = "https://wiki.guildwars.com/wiki/Sogolon" };
|
||||
public static readonly Npc Herta = new() { Ids = new int[] { 4532, 4537, 4544, 4551, 4566, 4576, 5989, 4586 }, Name = "Herta", WikiUrl = "https://wiki.guildwars.com/wiki/Herta" };
|
||||
public static readonly Npc Gehraz = new() { Ids = new int[] { 4533, 4540, 4547, 4554, 4571, 4581, 4591 }, Name = "Gehraz", WikiUrl = "https://wiki.guildwars.com/wiki/Gehraz" };
|
||||
public static readonly Npc Sogolon = new() { Ids = new int[] { 4534, 4541, 4548, 4555, 4572, 4582, 4592 }, Name = "Sogolon", WikiUrl = "https://wiki.guildwars.com/wiki/Sogolon" };
|
||||
public static readonly Npc Kihm = new() { Ids = new int[] { 4535, 4542, 4568 }, Name = "Kihm", WikiUrl = "https://wiki.guildwars.com/wiki/Kihm" };
|
||||
public static readonly Npc Odurra = new() { Ids = new int[] { 4536, 4543, 4550, 4563, 4573 }, Name = "Odurra", WikiUrl = "https://wiki.guildwars.com/wiki/Odurra" };
|
||||
public static readonly Npc Odurra = new() { Ids = new int[] { 4536, 4543, 4550, 4563, 4573, 4583 }, Name = "Odurra", WikiUrl = "https://wiki.guildwars.com/wiki/Odurra" };
|
||||
public static readonly Npc Timera = new() { Ids = new int[] { 4538, 4545, 4552 }, Name = "Timera", WikiUrl = "https://wiki.guildwars.com/wiki/Timera" };
|
||||
public static readonly Npc Abasi = new() { Ids = new int[] { 4539, 4553, 4546 }, Name = "Abasi", WikiUrl = "https://wiki.guildwars.com/wiki/Abasi" };
|
||||
public static readonly Npc Sunspear = new() { Ids = new int[] { 4698, 4726, 4727, 4774, 4778, 4779, 4781, 4782, 4784, 4786, 4811, 4739, 4738 }, Name = "Sunspear", WikiUrl = "https://wiki.guildwars.com/wiki/Sunspear" };
|
||||
@@ -1112,8 +1111,8 @@ public sealed class Npc
|
||||
public static readonly Npc VabbianBlacksmith = new() { Ids = new int[] { 5667 }, Name = "Vabbian Blacksmith", WikiUrl = "https://wiki.guildwars.com/wiki/Guild_Wars_Wiki:Projects/NPC_models/Humans/Vabbians" };
|
||||
public static readonly Npc YammironEtherLord = new () { Ids = new int[] { 4636 }, Name = "Yammiron, Ether Lord", WikiUrl = "https://wiki.guildwars.com/wiki/Yammiron,_Ether_Lord" };
|
||||
public static readonly Npc RoaringEther = new() { Ids = new int[] { 4675, 4676 }, Name = "Roaring Ether", WikiUrl = "https://wiki.guildwars.com/wiki/Roaring_Ether" };
|
||||
public static readonly Npc SapphireDjinn = new() { Ids = new int[] { 4673 }, Name = "Sapphire Djinn", WikiUrl = "https://wiki.guildwars.com/wiki/Sapphire_Djinn" };
|
||||
public static readonly Npc RubyDjinn = new() { Ids = new int[] { 4670 }, Name = "Ruby Djinn", WikiUrl = "https://wiki.guildwars.com/wiki/Ruby_Djinn" };
|
||||
public static readonly Npc SapphireDjinn = new() { Ids = new int[] { 4673, 4312 }, Name = "Sapphire Djinn", WikiUrl = "https://wiki.guildwars.com/wiki/Sapphire_Djinn" };
|
||||
public static readonly Npc RubyDjinn = new() { Ids = new int[] { 4670, 4311 }, Name = "Ruby Djinn", WikiUrl = "https://wiki.guildwars.com/wiki/Ruby_Djinn" };
|
||||
public static readonly Npc CobaltMokele = new() { Ids = new int[] { 4664 }, Name = "Cobalt Mokele", WikiUrl = "https://wiki.guildwars.com/wiki/Cobalt_Mokele" };
|
||||
public static readonly Npc JisholDarksong = new() { Ids = new int[] { 4626 }, Name = "Jishol Darksong", WikiUrl = "https://wiki.guildwars.com/wiki/Jishol_Darksong" };
|
||||
public static readonly Npc CobaltShrieker = new() { Ids = new int[] { 4665 }, Name = "Cobalt Shrieker", WikiUrl = "https://wiki.guildwars.com/wiki/Cobalt_Shrieker" };
|
||||
@@ -1153,7 +1152,7 @@ public sealed class Npc
|
||||
public static readonly Npc KorrLivingFlame = new () { Ids = new int[] { 4877 }, Name = "Korr Living Flame", WikiUrl = "https://wiki.guildwars.com/wiki/Korr,_Living_Flame" };
|
||||
public static readonly Npc ImmolatedDjinn = new() { Ids = new int[] { 4906 }, Name = "Immolated Djinn", WikiUrl = "https://wiki.guildwars.com/wiki/Immolated_Djinn" };
|
||||
public static readonly Npc AscensionPilgrim = new() { Ids = new int[] { 5007 }, Name = "Ascension Pilgrim", WikiUrl = "https://wiki.guildwars.com/wiki/Ascension_Pilgrim" };
|
||||
public static readonly Npc DynasticSpirit = new() { Ids = new int[] { 5006, 5611, 5005 }, Name = "Dynastic Spirit", WikiUrl = "https://wiki.guildwars.com/wiki/Dynastic_Spirit" };
|
||||
public static readonly Npc DynasticSpirit = new() { Ids = new int[] { 5006, 5611, 5005, 5613 }, Name = "Dynastic Spirit", WikiUrl = "https://wiki.guildwars.com/wiki/Dynastic_Spirit" };
|
||||
public static readonly Npc DiamondDjinn = new() { Ids = new int[] { 4671 }, Name = "Diamond Djinn", WikiUrl = "https://wiki.guildwars.com/wiki/Diamond_Djinn" };
|
||||
public static readonly Npc VahlenTheSilent = new() { Ids = new int[] { 5566 }, Name = "Vahlen the Silent", WikiUrl = "https://wiki.guildwars.com/wiki/Vahlen_the_Silent" };
|
||||
public static readonly Npc AwakenedAcolyte = new() { Ids = new int[] { 5577 }, Name = "Awakened Acolyte", WikiUrl = "https://wiki.guildwars.com/wiki/Awakened_Acolyte" };
|
||||
@@ -1181,19 +1180,78 @@ public sealed class Npc
|
||||
public static readonly Npc RavenousMandragor = new() { Ids = new int[] { 4305 }, Name = "Ravenous Mandragor", WikiUrl = "https://wiki.guildwars.com/wiki/Ravenous_Mandragor" };
|
||||
public static readonly Npc MandragorTerror = new() { Ids = new int[] { 4307 }, Name = "Mandragor Terror", WikiUrl = "https://wiki.guildwars.com/wiki/Mandragor_Terror" };
|
||||
public static readonly Npc MandragorSandDevil = new() { Ids = new int[] { 4306 }, Name = "Mandragor Sand Devil", WikiUrl = "https://wiki.guildwars.com/wiki/Mandragor_Sand_Devil" };
|
||||
public static readonly Npc GhostlyScout = new() { Ids = new int[] { 5547 }, Name = "Ghostly Scout", WikiUrl = "https://wiki.guildwars.com/wiki/Ghostly_Scout" };
|
||||
public static readonly Npc GhostlyScout = new() { Ids = new int[] { 5547, 5548 }, Name = "Ghostly Scout", WikiUrl = "https://wiki.guildwars.com/wiki/Ghostly_Scout" };
|
||||
public static readonly Npc BladedDuneTermite = new() { Ids = new int[] { 4319 }, Name = "Bladed Dune Termite", WikiUrl = "https://wiki.guildwars.com/wiki/Bladed_Dune_Termite" };
|
||||
public static readonly Npc DuneBeetleLance = new() { Ids = new int[] { 4320 }, Name = "Dune Beetle Lance", WikiUrl = "https://wiki.guildwars.com/wiki/Dune_Beetle_Lance" };
|
||||
public static readonly Npc DuneSpider = new() { Ids = new int[] { 4303 }, Name = "Dune Spider", WikiUrl = "https://wiki.guildwars.com/wiki/Dune_Spider" };
|
||||
public static readonly Npc AwakenedHead = new() { Ids = new int[] { 5580 }, Name = "Awakened Head", WikiUrl = "https://wiki.guildwars.com/wiki/Awakened_Head" };
|
||||
public static readonly Npc GeneralHuduh = new() { Ids = new int[] { 5606 }, Name = "General Huduh", WikiUrl = "https://wiki.guildwars.com/wiki/General_Huduh" };
|
||||
public static readonly Npc UndeadGeneral = new() { Ids = new int[] { 5606 }, Name = "Undead General", WikiUrl = "https://wiki.guildwars.com/wiki/Guild_Wars_Wiki:Projects/NPC_models/Skeletons" };
|
||||
public static readonly Npc Chirah = new() { Ids = new int[] { 5272 }, Name = "Chirah", WikiUrl = "https://wiki.guildwars.com/wiki/Chirah" };
|
||||
public static readonly Npc AwakenedMonk = new() { Ids = new int[] { 5626 }, Name = "Awakened Monk", WikiUrl = "https://wiki.guildwars.com/wiki/Awakened" };
|
||||
public static readonly Npc AwakenedMesmer = new() { Ids = new int[] { 5625 }, Name = "Awakened Mesmer", WikiUrl = "https://wiki.guildwars.com/wiki/Awakened" };
|
||||
public static readonly Npc AwakenedDervish = new() { Ids = new int[] { 5630 }, Name = "Awakened Dervish", WikiUrl = "https://wiki.guildwars.com/wiki/Awakened" };
|
||||
public static readonly Npc Awata = new() { Ids = new int[] { 5624 }, Name = "Awata", WikiUrl = "https://wiki.guildwars.com/wiki/Awata" };
|
||||
public static readonly Npc Thrall = new() { Ids = new int[] { 5588, 5589, 5585 }, Name = "Thrall", WikiUrl = "https://wiki.guildwars.com/wiki/Thrall" };
|
||||
public static readonly Npc Thrall = new() { Ids = new int[] { 5588, 5589, 5585, 5590 }, Name = "Thrall", WikiUrl = "https://wiki.guildwars.com/wiki/Thrall" };
|
||||
public static readonly Npc GhostlyPriest = new() { Ids = new int[] { 5615 }, Name = "Ghostly Priest", WikiUrl = "https://wiki.guildwars.com/wiki/Ghostly_Priest" };
|
||||
public static readonly Npc PrimevalKingJahnus = new() { Ids = new int[] { 5596 }, Name = "Primeval King Jahnus", WikiUrl = "https://wiki.guildwars.com/wiki/Primeval_King_Jahnus" };
|
||||
public static readonly Npc NomadGiant = new() { Ids = new int[] { 4314 }, Name = "Nomad Giant", WikiUrl = "https://wiki.guildwars.com/wiki/Nomad_Giant" };
|
||||
public static readonly Npc KoahmTheWeary = new() { Ids = new int[] { 5559 }, Name = "Koahm the Weary", WikiUrl = "https://wiki.guildwars.com/wiki/Koahm_the_Weary" };
|
||||
public static readonly Npc SadisticGiant = new() { Ids = new int[] { 4313 }, Name = "Sadistic Giant", WikiUrl = "https://wiki.guildwars.com/wiki/Sadistic_Giant" };
|
||||
public static readonly Npc UhiwiTheSmoky = new() { Ids = new int[] { 4300 }, Name = "Uhiwi the Smoky", WikiUrl = "https://wiki.guildwars.com/wiki/Uhiwi_the_Smoky" };
|
||||
public static readonly Npc ElderSiegeWurm = new() { Ids = new int[] { 5610 }, Name = "Elder Siege Wurm", WikiUrl = "https://wiki.guildwars.com/wiki/Elder_Siege_Wurm" };
|
||||
public static readonly Npc AmireshThePious = new() { Ids = new int[] { 5563 }, Name = "Amiresh the Pious", WikiUrl = "https://wiki.guildwars.com/wiki/Amiresh_the_Pious" };
|
||||
public static readonly Npc HordeofDarkness = new() { Ids = new int[] { 5486 }, Name = "Horde of Darkness", WikiUrl = "https://wiki.guildwars.com/wiki/Horde_of_Darkness" };
|
||||
public static readonly Npc DesertWurm = new() { Ids = new int[] { 4323 }, Name = "Desert Wurm", WikiUrl = "https://wiki.guildwars.com/wiki/Desert_Wurm" };
|
||||
public static readonly Npc BladeOfCorruption = new() { Ids = new int[] { 5447 }, Name = "Blade of Corruption", WikiUrl = "https://wiki.guildwars.com/wiki/Blade_of_Corruption" };
|
||||
public static readonly Npc CaptainMehhan = new() { Ids = new int[] { 5366 }, Name = "Captain Mehhan", WikiUrl = "https://wiki.guildwars.com/wiki/Captain_Mehhan" };
|
||||
public static readonly Npc AwakenedParagon = new() { Ids = new int[] { 5631 }, Name = "Awakened Paragon", WikiUrl = "https://wiki.guildwars.com/wiki/Guild_Wars_Wiki:Projects/NPC_models/Zombies#Mummies_(Awakened)" };
|
||||
public static readonly Npc TormentedSoul = new() { Ids = new int[] { 4983, 4984, 4988, 4989 }, Name = "Tormented Soul", WikiUrl = "https://wiki.guildwars.com/wiki/Tormented_Soul" };
|
||||
public static readonly Npc SecretKeeper = new() { Ids = new int[] { 4985 }, Name = "Secret Keeper", WikiUrl = "https://wiki.guildwars.com/wiki/Secret_Keeper" };
|
||||
public static readonly Npc WordOfMadness = new() { Ids = new int[] { 5446 }, Name = "Word of Madness", WikiUrl = "https://wiki.guildwars.com/wiki/Word_of_Madness" };
|
||||
public static readonly Npc RainOfTerror = new() { Ids = new int[] { 5445 }, Name = "Rain of Terror", WikiUrl = "https://wiki.guildwars.com/wiki/Rain_of_Terror" };
|
||||
public static readonly Npc ShadowOfFear = new() { Ids = new int[] { 5444 }, Name = "Shadow of Fear", WikiUrl = "https://wiki.guildwars.com/wiki/Shadow_of_Fear" };
|
||||
public static readonly Npc ScytheOfChaos = new() { Ids = new int[] { 5449, 4959 }, Name = "Scythe of Chaos", WikiUrl = "https://wiki.guildwars.com/wiki/Scythe_of_Chaos" };
|
||||
public static readonly Npc SpearOfTorment = new() { Ids = new int[] { 5450 }, Name = "Spear of Torment", WikiUrl = "https://wiki.guildwars.com/wiki/Spear_of_Torment" };
|
||||
public static readonly Npc HeraldOfNightmares = new() { Ids = new int[] { 5443 }, Name = "Herald of Nightmares", WikiUrl = "https://wiki.guildwars.com/wiki/Herald_of_Nightmares" };
|
||||
public static readonly Npc ArmOfInsanity = new() { Ids = new int[] { 5448 }, Name = "Arm of Insanity", WikiUrl = "https://wiki.guildwars.com/wiki/Arm_of_Insanity" };
|
||||
public static readonly Npc OnslaughtOfTerror = new() { Ids = new int[] { 5430 }, Name = "Onslaught of Terror", WikiUrl = "https://wiki.guildwars.com/wiki/Onslaught_of_Terror" };
|
||||
public static readonly Npc ZombieMonk = new() { Ids = new int[] { 5003 }, Name = "Zombie Monk", WikiUrl = "https://wiki.guildwars.com/wiki/Guild_Wars_Wiki:Projects/NPC_models/Zombies#Zombies" };
|
||||
public static readonly Npc ChimorTheLightblooded = new() { Ids = new int[] { 5520 }, Name = "Chimor the Lightblooded", WikiUrl = "https://wiki.guildwars.com/wiki/Chimor_the_Lightblooded" };
|
||||
public static readonly Npc ScoutAhktum = new() { Ids = new int[] { 5017 }, Name = "Scout Ahktum", WikiUrl = "https://wiki.guildwars.com/wiki/Scout_Ahktum" };
|
||||
public static readonly Npc Thenemi = new() { Ids = new int[] { 5018 }, Name = "Thenemi", WikiUrl = "https://wiki.guildwars.com/wiki/Thenemi" };
|
||||
public static readonly Npc GarfazSteelfur = new() { Ids = new int[] { 5014 }, Name = "Garfaz Steelfur", WikiUrl = "https://wiki.guildwars.com/wiki/Garfaz_Steelfur" };
|
||||
public static readonly Npc CaptainYithlis = new() { Ids = new int[] { 5013 }, Name = "Captain Yithlis", WikiUrl = "https://wiki.guildwars.com/wiki/Captain_Yithlis" };
|
||||
public static readonly Npc Igraine = new() { Ids = new int[] { 5015 }, Name = "Igraine", WikiUrl = "https://wiki.guildwars.com/wiki/Igraine" };
|
||||
public static readonly Npc TheLost = new() { Ids = new int[] { 5016 }, Name = "The Lost", WikiUrl = "https://wiki.guildwars.com/wiki/The_Lost" };
|
||||
public static readonly Npc AbaddonsAdjutant = new() { Ids = new int[] { 5483 }, Name = "Abaddon's Adjutant", WikiUrl = "https://wiki.guildwars.com/wiki/Abaddon%27s_Adjutant" };
|
||||
public static readonly Npc EmissaryOfDhuum = new() { Ids = new int[] { 5412, 5425 }, Name = "Emissary of Dhuum", WikiUrl = "https://wiki.guildwars.com/wiki/Emissary_of_Dhuum" };
|
||||
public static readonly Npc TerrorwebDryder = new() { Ids = new int[] { 2321 }, Name = "Terrorweb Dryder", WikiUrl = "https://wiki.guildwars.com/wiki/Terrorweb_Dryder" };
|
||||
public static readonly Npc Rukkassa = new() { Ids = new int[] { 4973 }, Name = "Rukkassa", WikiUrl = "https://wiki.guildwars.com/wiki/Rukkassa" };
|
||||
public static readonly Npc TorturewebDryder = new() { Ids = new int[] { 5427 }, Name = "Tortureweb Dryder", WikiUrl = "https://wiki.guildwars.com/wiki/Tortureweb_Dryder" };
|
||||
public static readonly Npc LostSoul = new() { Ids = new int[] { 2358 }, Name = "Lost Soul", WikiUrl = "https://wiki.guildwars.com/wiki/Lost_Soul" };
|
||||
public static readonly Npc Apostate = new() { Ids = new int[] { 5491 }, Name = "Apostate", WikiUrl = "https://wiki.guildwars.com/wiki/Apostate" };
|
||||
public static readonly Npc StormOfAnguish = new() { Ids = new int[] { 5436 }, Name = "Storm of Anguish", WikiUrl = "https://wiki.guildwars.com/wiki/Storm_of_Anguish" };
|
||||
public static readonly Npc ShriekerOfDread = new() { Ids = new int[] { 5439 }, Name = "Shrieker of Dread", WikiUrl = "https://wiki.guildwars.com/wiki/Shrieker_of_Dread" };
|
||||
public static readonly Npc ShirokenElementalist = new() { Ids = new int[] { 4128 }, Name = "Shiro'ken Elementalist", WikiUrl = "https://wiki.guildwars.com/wiki/Shiro%27ken_Elementalist" };
|
||||
public static readonly Npc PainTitan = new() { Ids = new int[] { 4969 }, Name = "Pain Titan", WikiUrl = "https://wiki.guildwars.com/wiki/Pain_Titan" };
|
||||
public static readonly Npc ShirokenMonk = new() { Ids = new int[] { 4129 }, Name = "Shiro'ken Monk", WikiUrl = "https://wiki.guildwars.com/wiki/Shiro%27ken_Monk" };
|
||||
public static readonly Npc ShirokenMesmer = new() { Ids = new int[] { 4126 }, Name = "Shiro'ken Mesmer", WikiUrl = "https://wiki.guildwars.com/wiki/Shiro%27ken_Mesmer" };
|
||||
public static readonly Npc TitanAbomination = new() { Ids = new int[] { 4968 }, Name = "Titan Abomination", WikiUrl = "https://wiki.guildwars.com/wiki/Titan_Abomination" };
|
||||
public static readonly Npc BoundTiendi = new() { Ids = new int[] { 4087 }, Name = "Bound Tiendi", WikiUrl = "https://wiki.guildwars.com/wiki/Bound_Tiendi" };
|
||||
public static readonly Npc BoundKaichen = new() { Ids = new int[] { 4085 }, Name = "Bound Kaichen", WikiUrl = "https://wiki.guildwars.com/wiki/Bound_Kaichen" };
|
||||
public static readonly Npc WrathfulStorm = new() { Ids = new int[] { 4961 }, Name = "Wrathful Storm", WikiUrl = "https://wiki.guildwars.com/wiki/Wrathful_Storm" };
|
||||
public static readonly Npc GraspOfInsanity = new() { Ids = new int[] { 4960 }, Name = "Grasp of Insanity", WikiUrl = "https://wiki.guildwars.com/wiki/Grasp_of_Insanity" };
|
||||
public static readonly Npc TormentClaw = new() { Ids = new int[] { 5461 }, Name = "Torment Claw", WikiUrl = "https://wiki.guildwars.com/wiki/Torment_Claw" };
|
||||
public static readonly Npc BoundHaoLi = new() { Ids = new int[] { 4082 }, Name = "Bound Hao Li", WikiUrl = "https://wiki.guildwars.com/wiki/Bound_Hao_Li" };
|
||||
public static readonly Npc IgnisCruor = new() { Ids = new int[] { 4978 }, Name = "Ignis Cruor", WikiUrl = "https://wiki.guildwars.com/wiki/Ignis_Cruor" };
|
||||
public static readonly Npc ShadowBeast = new() { Ids = new int[] { 4967 }, Name = "Shadow Beast", WikiUrl = "https://wiki.guildwars.com/wiki/Shadow_Beast" };
|
||||
public static readonly Npc CreoVulnero = new() { Ids = new int[] { 4957 }, Name = "Creo Vulnero", WikiUrl = "https://wiki.guildwars.com/wiki/Creo_Vulnero" };
|
||||
public static readonly Npc PortalWraith = new() { Ids = new int[] { 2764 }, Name = "Portal Wraith", WikiUrl = "https://wiki.guildwars.com/wiki/Portal_Wraith" };
|
||||
public static readonly Npc ArmageddonLord = new() { Ids = new int[] { 2674 }, Name = "Armageddon Lord", WikiUrl = "https://wiki.guildwars.com/wiki/Armageddon_Lord" };
|
||||
public static readonly Npc JoyousSoul = new() { Ids = new int[] { 5011, 5009, 5010, 5012, 5008 }, Name = "Joyous Soul", WikiUrl = "https://wiki.guildwars.com/wiki/Joyous_Soul" };
|
||||
public static readonly Npc MagridTheSly = new() { Ids = new int[] { 4451 }, Name = "Magrid the Sly", WikiUrl = "https://wiki.guildwars.com/wiki/Magrid_the_Sly" };
|
||||
public static readonly Npc Zenmai = new() { Ids = new int[] { 3552 }, Name = "Zenmai", WikiUrl = "https://wiki.guildwars.com/wiki/Zenmai" };
|
||||
public static readonly Npc Abaddon = new() { Ids = new int[] { 5142 }, Name = "Abaddon", WikiUrl = "https://wiki.guildwars.com/wiki/Abaddon" };
|
||||
|
||||
public static IEnumerable<Npc> Npcs { get; } = new List<Npc>()
|
||||
{
|
||||
@@ -1269,7 +1327,6 @@ public sealed class Npc
|
||||
FarrahCappo,
|
||||
Cynn,
|
||||
Devona,
|
||||
BrotherMhenlo,
|
||||
AscalonDuke,
|
||||
PrinceRurik,
|
||||
LadyAlthea,
|
||||
@@ -2376,14 +2433,73 @@ public sealed class Npc
|
||||
DuneBeetleLance,
|
||||
DuneSpider,
|
||||
AwakenedHead,
|
||||
GeneralHuduh,
|
||||
UndeadGeneral,
|
||||
Chirah,
|
||||
AwakenedMonk,
|
||||
AwakenedMesmer,
|
||||
AwakenedDervish,
|
||||
Awata,
|
||||
Thrall,
|
||||
GhostlyPriest
|
||||
GhostlyPriest,
|
||||
PrimevalKingJahnus,
|
||||
NomadGiant,
|
||||
KoahmTheWeary,
|
||||
SadisticGiant,
|
||||
UhiwiTheSmoky,
|
||||
ElderSiegeWurm,
|
||||
AmireshThePious,
|
||||
HordeofDarkness,
|
||||
DesertWurm,
|
||||
BladeOfCorruption,
|
||||
CaptainMehhan,
|
||||
AwakenedParagon,
|
||||
TormentedSoul,
|
||||
SecretKeeper,
|
||||
WordOfMadness,
|
||||
RainOfTerror,
|
||||
ShadowOfFear,
|
||||
ScytheOfChaos,
|
||||
SpearOfTorment,
|
||||
HeraldOfNightmares,
|
||||
ArmOfInsanity,
|
||||
OnslaughtOfTerror,
|
||||
ZombieMonk,
|
||||
ChimorTheLightblooded,
|
||||
ScoutAhktum,
|
||||
Thenemi,
|
||||
GarfazSteelfur,
|
||||
CaptainYithlis,
|
||||
Igraine,
|
||||
TheLost,
|
||||
AbaddonsAdjutant,
|
||||
EmissaryOfDhuum,
|
||||
TerrorwebDryder,
|
||||
Rukkassa,
|
||||
TorturewebDryder,
|
||||
LostSoul,
|
||||
Apostate,
|
||||
StormOfAnguish,
|
||||
ShriekerOfDread,
|
||||
ShirokenElementalist,
|
||||
PainTitan,
|
||||
ShirokenMonk,
|
||||
ShirokenMesmer,
|
||||
TitanAbomination,
|
||||
BoundTiendi,
|
||||
BoundKaichen,
|
||||
WrathfulStorm,
|
||||
GraspOfInsanity,
|
||||
TormentClaw,
|
||||
BoundHaoLi,
|
||||
IgnisCruor,
|
||||
ShadowBeast,
|
||||
CreoVulnero,
|
||||
PortalWraith,
|
||||
ArmageddonLord,
|
||||
JoyousSoul,
|
||||
MagridTheSly,
|
||||
Zenmai,
|
||||
Abaddon
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct PathingData
|
||||
public sealed class PathingData
|
||||
{
|
||||
public List<Trapezoid> Trapezoids { get; init; }
|
||||
public List<List<int>> ComputedPathingMaps { get; init; }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct PathingMetadata
|
||||
public sealed class PathingMetadata
|
||||
{
|
||||
public int TrapezoidCount { get; init; }
|
||||
}
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct PlayerInformation : IEntity
|
||||
public sealed class PlayerInformation : IEntity
|
||||
{
|
||||
public int Id { get; init; }
|
||||
public uint Timer { get; init; }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct QuestMetadata : IPositionalEntity
|
||||
public sealed class QuestMetadata : IPositionalEntity
|
||||
{
|
||||
public Quest? Quest { get; init; }
|
||||
public Position? Position { get; init; }
|
||||
|
||||
@@ -6,44 +6,824 @@ namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public sealed class Region : IWikiEntity
|
||||
{
|
||||
public static Region Kryta { get; } = new Region { Id = 0, Name = "Kryta", WikiUrl = "https://wiki.guildwars.com/wiki/Kryta" };
|
||||
public static Region MaguumaJungle { get; } = new Region { Id = 1, Name = "Maguuma Jungle", WikiUrl = "https://wiki.guildwars.com/wiki/Maguuma_Jungle" };
|
||||
public static Region Ascalon { get; } = new Region { Id = 2, Name = "Ascalon", WikiUrl = "https://wiki.guildwars.com/wiki/Ascalon" };
|
||||
public static Region NorthernShiverpeaks { get; } = new Region { Id = 3, Name = "Northern Shiverpeaks", WikiUrl = "https://wiki.guildwars.com/wiki/Northern_Shiverpeaks" };
|
||||
public static Region HeroesAscent { get; } = new Region { Id = 4, Name = "Heroes' Ascent", WikiUrl = "https://wiki.guildwars.com/wiki/Heroes%27_Ascent" };
|
||||
public static Region CrystalDesert { get; } = new Region { Id = 5, Name = "Crystal Desert", WikiUrl = "https://wiki.guildwars.com/wiki/Crystal_Desert" };
|
||||
public static Region FissureOfWoe { get; } = new Region { Id = 6, Name = "Fissure Of Woe", WikiUrl = "https://wiki.guildwars.com/wiki/The_Fissure_of_Woe" };
|
||||
public static Region PresearingAscalon { get; } = new Region { Id = 7, Name = "Pre Searing Ascalon", WikiUrl = "https://wiki.guildwars.com/wiki/Ascalon_(pre-Searing)" };
|
||||
public static Region KainengCity { get; } = new Region { Id = 8, Name = "Kaineng City", WikiUrl = "https://wiki.guildwars.com/wiki/Kaineng_City" };
|
||||
public static Region EchovaldForest { get; } = new Region { Id = 9, Name = "Echovald Forest", WikiUrl = "https://wiki.guildwars.com/wiki/Echovald_Forest" };
|
||||
public static Region TheJadeSea { get; } = new Region { Id = 10, Name = "The Jade Sea", WikiUrl = "https://wiki.guildwars.com/wiki/The_Jade_Sea" };
|
||||
public static Region ShingJeaIsland { get; } = new Region { Id = 11, Name = "Shing Jea Island", WikiUrl = "https://wiki.guildwars.com/wiki/Shing_Jea_Island" };
|
||||
public static Region Kourna { get; } = new Region { Id = 12, Name = "Kourna", WikiUrl = "https://wiki.guildwars.com/wiki/Kourna" };
|
||||
public static Region Vabbi { get; } = new Region { Id = 13, Name = "Vabbi", WikiUrl = "https://wiki.guildwars.com/wiki/Vabbi" };
|
||||
public static Region TheDesolation { get; } = new Region { Id = 14, Name = "TheDesolation", WikiUrl = "https://wiki.guildwars.com/wiki/The_Desolation" };
|
||||
public static Region Istan { get; } = new Region { Id = 15, Name = "Istan", WikiUrl = "https://wiki.guildwars.com/wiki/Istan" };
|
||||
public static Region DomainOfAnguish { get; } = new Region { Id = 16, Name = "Domain Of Anguish", WikiUrl = "https://wiki.guildwars.com/wiki/Domain_of_Anguish" };
|
||||
public static Region TarnishedCoast { get; } = new Region { Id = 17, Name = "Tarnished Coast", WikiUrl = "https://wiki.guildwars.com/wiki/Tarnished_Coast" };
|
||||
public static Region DepthsOfTyria { get; } = new Region { Id = 18, Name = "Depths Of Tyria", WikiUrl = "https://wiki.guildwars.com/wiki/Depths_of_Tyria" };
|
||||
public static Region FarShiverpeaks { get; } = new Region { Id = 19, Name = "Far Shiverpeaks", WikiUrl = "https://wiki.guildwars.com/wiki/Far_Shiverpeaks" };
|
||||
public static Region CharrHomelands { get; } = new Region { Id = 20, Name = "Charr Homelands", WikiUrl = "https://wiki.guildwars.com/wiki/Charr_Homelands" };
|
||||
public static Region TheBattleIsles { get; } = new Region { Id = 21, Name = "The Battle Isles", WikiUrl = "https://wiki.guildwars.com/wiki/The_Battle_Isles" };
|
||||
public static Region TheBattleOfJahai { get; } = new Region { Id = 22, Name = "The Battle Of Jahai", WikiUrl = "https://wiki.guildwars.com/wiki/The_Battle_of_Jahai" };
|
||||
public static Region TheFlightNorth { get; } = new Region { Id = 23, Name = "The Flight North", WikiUrl = "https://wiki.guildwars.com/wiki/The_Flight_North" };
|
||||
public static Region TheTenguAccords { get; } = new Region { Id = 24, Name = "The Tengu Accords", WikiUrl = "https://wiki.guildwars.com/wiki/The_Tengu_Accords" };
|
||||
public static Region TheRiseOfTheWhiteMantle { get; } = new Region { Id = 25, Name = "The Rise Of The White Mantle", WikiUrl = "https://wiki.guildwars.com/wiki/The_Rise_of_the_White_Mantle" };
|
||||
public static Region Kryta { get; } = new Region
|
||||
{
|
||||
Id = 0,
|
||||
Name = "Kryta",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Kryta",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.LionsArchCanthanNewYearOutpost,
|
||||
Map.LionsArchHalloweenOutpost,
|
||||
Map.LionsArchOutpost,
|
||||
Map.LionsArchSunspearsinKryta,
|
||||
Map.LionsArchWintersdayOutpost,
|
||||
Map.BeetletunOutpost,
|
||||
Map.BergenHotSpringsOutpost,
|
||||
Map.FishermensHavenOutpost,
|
||||
Map.TempleOfTheAges,
|
||||
Map.TempleOfTheAgesROX,
|
||||
Map.DAlessioSeaboard,
|
||||
Map.DivinityCoast,
|
||||
Map.GatesOfKryta,
|
||||
Map.RiversideProvince,
|
||||
Map.WarinKrytaRiversideProvince,
|
||||
Map.SanctumCay,
|
||||
Map.CursedLands,
|
||||
Map.KessexPeak,
|
||||
Map.LionsGate,
|
||||
Map.MajestysRest,
|
||||
Map.NeboTerrace,
|
||||
Map.NorthKrytaProvince,
|
||||
Map.ScoundrelsRise,
|
||||
Map.StingrayStrand,
|
||||
Map.TalmarkWilderness,
|
||||
Map.TearsOfTheFallen,
|
||||
Map.TheBlackCurtain,
|
||||
Map.TwinSerpentLakes,
|
||||
Map.WatchtowerCoast
|
||||
}
|
||||
};
|
||||
public static Region MaguumaJungle { get; } = new Region
|
||||
{
|
||||
Id = 1,
|
||||
Name = "Maguuma Jungle",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Maguuma_Jungle",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.HengeOfDenraviOutpost,
|
||||
Map.DruidsOverlookOutpost,
|
||||
Map.MaguumaStadeOutpost,
|
||||
Map.QuarrelFallsOutpost,
|
||||
Map.VentarisRefugeOutpost,
|
||||
Map.AuroraGlade,
|
||||
Map.BloodstoneFen,
|
||||
Map.BloodstoneFenQuest,
|
||||
Map.TheWilds,
|
||||
Map.DryTop,
|
||||
Map.EttinsBack,
|
||||
Map.MajestysRest,
|
||||
Map.MamnoonLagoon,
|
||||
Map.ReedBog,
|
||||
Map.SageLands,
|
||||
Map.Silverwood,
|
||||
Map.TangleRoot,
|
||||
Map.TheFalls
|
||||
}
|
||||
};
|
||||
public static Region Ascalon { get; } = new Region
|
||||
{
|
||||
Id = 2,
|
||||
Name = "Ascalon",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Ascalon",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.AscalonCityOutpost,
|
||||
Map.AscalonCityWintersdayOutpost,
|
||||
Map.FrontierGateOutpost,
|
||||
Map.EasternFrontier,
|
||||
Map.GrendichCourthouseOutpost,
|
||||
Map.SpecialOpsGrendichCourthouse,
|
||||
Map.PikenSquareOutpost,
|
||||
Map.SardelacSanitariumOutpost,
|
||||
Map.SerenityTempleOutpost,
|
||||
Map.FortRanik,
|
||||
Map.RuinsOfSurmia,
|
||||
Map.NolaniAcademy,
|
||||
Map.TheGreatNorthernWall,
|
||||
Map.AscalonArena,
|
||||
Map.AscalonArenaMission,
|
||||
Map.AscalonFoothills,
|
||||
Map.DiessaLowlands,
|
||||
Map.DragonsGullet,
|
||||
Map.EasternFrontier,
|
||||
Map.FlameTempleCorridor,
|
||||
Map.OldAscalon,
|
||||
Map.PockmarkFlats,
|
||||
Map.RegentValley,
|
||||
Map.TheBreach
|
||||
}
|
||||
};
|
||||
public static Region ShiverpeakMountains { get; } = new Region
|
||||
{
|
||||
Id = 3,
|
||||
Name = "Shiverpeak Mountains",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Shiverpeak_Mountains",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.BeaconsPerchOutpost,
|
||||
Map.IceToothCaveOutpost,
|
||||
Map.YaksBendOutpost,
|
||||
Map.BorlisPass,
|
||||
Map.TheFrostGate,
|
||||
Map.ShiverpeakArena,
|
||||
Map.ShiverpeakArenaMission,
|
||||
Map.ShiverpeakArenaMission2,
|
||||
Map.ShiverpeakArenaMission3,
|
||||
Map.AnvilRock,
|
||||
Map.DeldrimorBowl,
|
||||
Map.GriffonsMouth,
|
||||
Map.IronHorseMine,
|
||||
Map.TravelersVale,
|
||||
Map.DroknarsForgeOutpost,
|
||||
Map.DroknarsForgeWintersdayOutpost,
|
||||
Map.DroknarsForgeHalloweenOutpost,
|
||||
Map.DroknarsForgeCinematic,
|
||||
Map.CampRankorOutpost,
|
||||
Map.CopperhammerMinesOutpost,
|
||||
Map.DeldrimorWarCampOutpost,
|
||||
Map.MarhansGrottoOutpost,
|
||||
Map.PortSledgeOutpost,
|
||||
Map.TheGraniteCitadelOutpost,
|
||||
Map.IceCavesofSorrow,
|
||||
Map.IronMinesofMoladune,
|
||||
Map.ThunderheadKeep,
|
||||
Map.DreadnoughtsDrift,
|
||||
Map.FrozenForest,
|
||||
Map.GrenthsFootprint,
|
||||
Map.IceFloe,
|
||||
Map.Icedome,
|
||||
Map.LornarsPass,
|
||||
Map.MineralSprings,
|
||||
Map.SnakeDance,
|
||||
Map.SorrowsFurnace,
|
||||
Map.SpearheadPeak,
|
||||
Map.TalusChute,
|
||||
Map.TascasDemise,
|
||||
Map.WitmansFolly
|
||||
}
|
||||
};
|
||||
public static Region HeroesAscent { get; } = new Region
|
||||
{
|
||||
Id = 4,
|
||||
Name = "Heroes' Ascent",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Heroes%27_Ascent",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.BurialMoundsMission,
|
||||
Map.FetidRiverMission,
|
||||
Map.TheUnderworldArenaMission,
|
||||
Map.UnholyTemplesMission,
|
||||
Map.ForgottenShrinesMission,
|
||||
Map.GoldenGatesMission,
|
||||
Map.TheCourtyardArenaMission,
|
||||
Map.TheVaultMission,
|
||||
Map.TheHallOfHeroesArenaMission,
|
||||
Map.BrokenTowerMission,
|
||||
Map.SacredTemplesMission,
|
||||
Map.ScarredEarth,
|
||||
Map.ScarredEarth2
|
||||
}
|
||||
};
|
||||
public static Region CrystalDesert { get; } = new Region
|
||||
{
|
||||
Id = 5,
|
||||
Name = "Crystal Desert",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Crystal_Desert",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.TheAmnoonOasisOutpost,
|
||||
Map.DestinysGorgeOutpost,
|
||||
Map.HeroesAudienceOutpost,
|
||||
Map.SeekersPassageOutpost,
|
||||
Map.TombOfThePrimevalKings,
|
||||
Map.TombOfThePrimevalKingsHalloweenOutpost,
|
||||
Map.AuguryRockMission,
|
||||
Map.AuguryRockOutpost,
|
||||
Map.DunesOfDespair,
|
||||
Map.ElonaReach,
|
||||
Map.ThirstyRiver,
|
||||
Map.TheDragonsLair,
|
||||
Map.DivinersAscent,
|
||||
Map.ProphetsPath,
|
||||
Map.SaltFlats,
|
||||
Map.SkywardReach,
|
||||
Map.TheAridSea,
|
||||
Map.TheScar,
|
||||
Map.VultureDrifts
|
||||
}
|
||||
};
|
||||
public static Region RingOfFireIslands { get; } = new Region
|
||||
{
|
||||
Id = 6,
|
||||
Name = "Ring of Fire Islands",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Ring_of_Fire_Islands",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.EmberLightCampOutpost,
|
||||
Map.AbaddonsMouth,
|
||||
Map.HellsPrecipice,
|
||||
Map.RingOfFire,
|
||||
Map.PerditionRock,
|
||||
Map.TheFissureofWoe
|
||||
}
|
||||
};
|
||||
public static Region PresearingAscalon { get; } = new Region
|
||||
{
|
||||
Id = 7,
|
||||
Name = "Pre Searing Ascalon",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Ascalon_(pre-Searing)",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.AscalonCityPresearing,
|
||||
Map.AshfordAbbeyOutpost,
|
||||
Map.AshfordCatacombs1070AE,
|
||||
Map.FoiblesFairOutpost,
|
||||
Map.FortRanikPreSearingOutpost,
|
||||
Map.TheBarradinEstateOutpost,
|
||||
Map.AscalonAcademyPvPBattleMission,
|
||||
Map.GreenHillsCounty,
|
||||
Map.LakesideCounty,
|
||||
Map.LakesideCounty1070AE,
|
||||
Map.RegentValleyPreSearing,
|
||||
Map.TheCatacombs,
|
||||
Map.TheNorthlands,
|
||||
Map.WizardsFolly
|
||||
}
|
||||
};
|
||||
public static Region KainengCity { get; } = new Region
|
||||
{
|
||||
Id = 8,
|
||||
Name = "Kaineng City",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Kaineng_City",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.KainengCenterOutpost,
|
||||
Map.KainengCenterCanthanNewYearOutpost,
|
||||
Map.KainengCenterSunspearsInCantha,
|
||||
Map.KainengCenterWindsOfChangeAChanceEncounter,
|
||||
Map.KainengCenterWindsOfChangeRaidonKainengCenter,
|
||||
Map.MaatuKeepOutpost,
|
||||
Map.SenjisCornerOutpost,
|
||||
Map.TheMarketplaceOutpost,
|
||||
Map.TheMarketplaceAreaTrackingtheCorruption,
|
||||
Map.ZinKuCorridorOutpost,
|
||||
Map.DragonsThroat,
|
||||
Map.DragonsThroatAreaWhatWaitsInShadow,
|
||||
Map.ImperialSanctumOutpostMission,
|
||||
Map.NahpuiQuarterOutpostMission,
|
||||
Map.NahpuiQuarterExplorable,
|
||||
Map.RaisuPalace,
|
||||
Map.RaisuPalaceOutpostMission,
|
||||
Map.SunjiangDistrictExplorable,
|
||||
Map.SunjiangDistrictOutpostMission,
|
||||
Map.TahnnakaiTempleOutpostMission,
|
||||
Map.TahnnakaiTempleExplorable,
|
||||
Map.TahnnakaiTempleWindsOfChangeTheRescueAttempt,
|
||||
Map.VizunahSquareMission,
|
||||
Map.VizunahSquareForeignQuarterOutpost,
|
||||
Map.VizunahSquareLocalQuarterOutpost,
|
||||
Map.BejunkanPier,
|
||||
Map.BukdekByway,
|
||||
Map.BukdekBywayWindsOfChangeCanthaCourierCrisis,
|
||||
Map.DivinePath,
|
||||
Map.KainengDocks,
|
||||
Map.PongmeiValley,
|
||||
Map.RaisuPavilion,
|
||||
Map.ShadowsPassage,
|
||||
Map.ShenzunTunnels,
|
||||
Map.TheUndercity,
|
||||
Map.WajjunBazaar,
|
||||
Map.WajjunBazaarPOX,
|
||||
Map.WajjunBazaarWindsOfChangeMinistryOfOppression,
|
||||
Map.WajjunBazaarWindsOfChangeViolenceInTheStreets,
|
||||
Map.XaquangSkyway
|
||||
}
|
||||
};
|
||||
public static Region EchovaldForest { get; } = new Region
|
||||
{
|
||||
Id = 9,
|
||||
Name = "Echovald Forest",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Echovald_Forest",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.HouseZuHeltzerOutpost,
|
||||
Map.AspenwoodGateKurzickOutpost,
|
||||
Map.BrauerAcademyOutpost,
|
||||
Map.DurheimArchivesOutpost,
|
||||
Map.JadeFlatsKurzickOutpost,
|
||||
Map.LutgardisConservatoryOutpost,
|
||||
Map.SaintAnjekasShrineOutpost,
|
||||
Map.TanglewoodCopseOutpost,
|
||||
Map.VasburgArmoryOutpost,
|
||||
Map.AltrummRuins,
|
||||
Map.AltrummRuinsFindingJinnai,
|
||||
Map.AmatzBasin,
|
||||
Map.ArborstoneOutpostMission,
|
||||
Map.ArborstoneExplorable,
|
||||
Map.FortAspenwoodKurzickOutpost,
|
||||
Map.FortAspenwoodMission,
|
||||
Map.TheEternalGrove,
|
||||
Map.TheEternalGroveOutpostMission,
|
||||
Map.TheJadeQuarryKurzickOutpost,
|
||||
Map.UnwakingWatersKurzickOutpost,
|
||||
Map.UrgozsWarren,
|
||||
Map.DrazachThicket,
|
||||
Map.Ferndale,
|
||||
Map.MelandrusHope,
|
||||
Map.MorostavTrail,
|
||||
Map.MourningVeilFalls
|
||||
}
|
||||
};
|
||||
public static Region TheJadeSea { get; } = new Region
|
||||
{
|
||||
Id = 10,
|
||||
Name = "The Jade Sea",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/The_Jade_Sea",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.CavalonOutpost,
|
||||
Map.AspenwoodGateLuxonOutpost,
|
||||
Map.BaiPaasuReachOutpost,
|
||||
Map.BreakerHollowOutpost,
|
||||
Map.EredonTerraceOutpost,
|
||||
Map.HarvestTempleOutpost,
|
||||
Map.JadeFlatsLuxonOutpost,
|
||||
Map.LeviathanPitsOutpost,
|
||||
Map.SeafarersRestOutpost,
|
||||
Map.BoreasSeabedExplorable,
|
||||
Map.BoreasSeabedOutpostMission,
|
||||
Map.FortAspenwoodLuxonOutpost,
|
||||
Map.GyalaHatchery,
|
||||
Map.GyalaHatcheryOutpostMission,
|
||||
Map.TheAuriosMines,
|
||||
Map.TheDeep,
|
||||
Map.TheJadeQuarryLuxonOutpost,
|
||||
Map.TheJadeQuarryMission,
|
||||
Map.UnwakingWaters,
|
||||
Map.UnwakingWatersLuxonOutpost,
|
||||
Map.UnwakingWatersMission,
|
||||
Map.ZosShivrosChannel,
|
||||
Map.Archipelagos,
|
||||
Map.MaishangHills,
|
||||
Map.MountQinkai,
|
||||
Map.RheasCrater,
|
||||
Map.SilentSurf
|
||||
}
|
||||
};
|
||||
public static Region ShingJeaIsland { get; } = new Region
|
||||
{
|
||||
Id = 11,
|
||||
Name = "Shing Jea Island",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Shing_Jea_Island",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.ShingJeaArena,
|
||||
Map.ShingJeaArenaMission,
|
||||
Map.ShingJeaMonasteryCanthanNewYearOutpost,
|
||||
Map.ShingJeaMonasteryDragonFestivalOutpost,
|
||||
Map.ShingJeaMonasteryMission,
|
||||
Map.ShingJeaMonasteryOutpost,
|
||||
Map.ShingJeaMonasteryRaidOnShingJeaMonastery,
|
||||
Map.RanMusuGardensOutpost,
|
||||
Map.SeitungHarborAreaDeadlyCargo,
|
||||
Map.SeitungHarborMission,
|
||||
Map.SeitungHarborMission2,
|
||||
Map.SeitungHarborOutpost,
|
||||
Map.TsumeiVillageMission,
|
||||
Map.TsumeiVillageMission2,
|
||||
Map.TsumeiVillageOutpost,
|
||||
Map.TsumeiVillageWindsOfChangeATreatysATreaty,
|
||||
Map.MinisterChosEstateExplorable,
|
||||
Map.MinisterChosEstateMission2,
|
||||
Map.MinisterChosEstateOutpostMission,
|
||||
Map.ZenDaijunExplorable,
|
||||
Map.ZenDaijunOutpostMission,
|
||||
Map.HaijuLagoon,
|
||||
Map.HaijuLagoonMission,
|
||||
Map.JayaBluffs,
|
||||
Map.JayaBluffsMission,
|
||||
Map.KinyaProvince,
|
||||
Map.LinnokCourtyard,
|
||||
Map.MonasteryOverlook1,
|
||||
Map.MonasteryOverlook2,
|
||||
Map.PanjiangPeninsula,
|
||||
Map.SaoshangTrail,
|
||||
Map.SunquaVale
|
||||
}
|
||||
};
|
||||
public static Region Kourna { get; } = new Region
|
||||
{
|
||||
Id = 12,
|
||||
Name = "Kourna",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Kourna",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.SunspearSanctuaryOutpost,
|
||||
Map.CampHojanuOutpost,
|
||||
Map.WehhanTerracesOutpost,
|
||||
Map.YohlonHavenOutpost,
|
||||
Map.DajkahInlet,
|
||||
Map.KodonurCrossroads,
|
||||
Map.ModdokCrevice,
|
||||
Map.NunduBay,
|
||||
Map.PogahnPassage,
|
||||
Map.RilohnRefuge,
|
||||
Map.VentaCemetery,
|
||||
Map.ArkjokWard,
|
||||
Map.BahdokCaverns,
|
||||
Map.BarbarousShore,
|
||||
Map.CommandPost,
|
||||
Map.DejarinEstate,
|
||||
Map.GandaraTheMoonFortress,
|
||||
Map.JahaiBluffs,
|
||||
Map.MargaCoast,
|
||||
Map.SunwardMarches,
|
||||
Map.TheFloodplainOfMahnkelon,
|
||||
Map.TuraisProcession,
|
||||
Map.NightfallenCoast
|
||||
}
|
||||
};
|
||||
public static Region Vabbi { get; } = new Region
|
||||
{
|
||||
Id = 13,
|
||||
Name = "Vabbi",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Vabbi",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.TheKodashBazaarOutpost,
|
||||
Map.BasaltGrottoOutpost,
|
||||
Map.ChantryOfSecretsOutpost,
|
||||
Map.HonurHillOutpost,
|
||||
Map.MihanuTownshipOutpost,
|
||||
Map.YahnurMarketOutpost,
|
||||
Map.DashaVestibule,
|
||||
Map.DzagonurBastion,
|
||||
Map.GrandCourtOfSebelkeh,
|
||||
Map.JennursHorde,
|
||||
Map.TiharkOrchard,
|
||||
Map.BokkaAmphitheatre,
|
||||
Map.BokkaAmphitheatreNOX,
|
||||
Map.ForumHighlands,
|
||||
Map.GardenOfSeborhin,
|
||||
Map.HoldingsOfChokhin,
|
||||
Map.ResplendentMakuun,
|
||||
Map.ResplendentMakuun2,
|
||||
Map.TheHiddenCityOfAhdashim,
|
||||
Map.TheMirrorOfLyss,
|
||||
Map.VehjinMines,
|
||||
Map.VehtendiValley,
|
||||
Map.WildernessOfBahdza,
|
||||
Map.YatendiCanyons
|
||||
}
|
||||
};
|
||||
public static Region TheDesolation { get; } = new Region
|
||||
{
|
||||
Id = 14,
|
||||
Name = "TheDesolation",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/The_Desolation",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.BonePalaceOutpost,
|
||||
Map.LairOfTheForgottenOutpost,
|
||||
Map.TheMouthOfTormentOutpost,
|
||||
Map.GateOfDesolation,
|
||||
Map.RuinsOfMorah,
|
||||
Map.RemainsOfSahlahja,
|
||||
Map.CrystalOverlook,
|
||||
Map.JokosDomain,
|
||||
Map.PoisonedOutcrops,
|
||||
Map.TheAlkaliPan,
|
||||
Map.TheRupturedHeart,
|
||||
Map.TheShatteredRavines,
|
||||
Map.TheSulfurousWastes
|
||||
}
|
||||
};
|
||||
public static Region Istan { get; } = new Region
|
||||
{
|
||||
Id = 15,
|
||||
Name = "Istan",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Istan",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.KamadanJewelOfIstanCanthanNewYearOutpost,
|
||||
Map.KamadanJewelOfIstanExplorable,
|
||||
Map.KamadanJewelOfIstanHalloweenOutpost,
|
||||
Map.KamadanJewelOfIstanOutpost,
|
||||
Map.KamadanJewelOfIstanWintersdayOutpost,
|
||||
Map.KamadanMission,
|
||||
Map.BeknurHarbor,
|
||||
Map.BeknurHarborOutpost,
|
||||
Map.ChampionsDawnOutpost,
|
||||
Map.KodlonuHamletOutpost,
|
||||
Map.SunspearArena,
|
||||
Map.SunspearArenaMission,
|
||||
Map.SunspearGreatHallOutpost,
|
||||
Map.TheAstralariumOutpost,
|
||||
Map.BlacktideDen,
|
||||
Map.ChahbekVillage,
|
||||
Map.Consulate,
|
||||
Map.ConsulateDocks,
|
||||
Map.JokanurDiggings,
|
||||
Map.ChurrhirFields,
|
||||
Map.CliffsOfDohjok,
|
||||
Map.FahranurMission,
|
||||
Map.FahranurTheFirstCity,
|
||||
Map.IslandOfShehkah,
|
||||
Map.IssnurIsles,
|
||||
Map.LahtendaBog,
|
||||
Map.MehtaniKeys,
|
||||
Map.PlainsOfJarin,
|
||||
Map.SunDocks,
|
||||
Map.ZehlonReach
|
||||
}
|
||||
};
|
||||
public static Region RealmOfTorment { get; } = new Region
|
||||
{
|
||||
Id = 16,
|
||||
Name = "Realm of Torment",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Realm_of_Torment",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.DomainOfAnguish,
|
||||
Map.GateOfTormentOutpost,
|
||||
Map.GateOfFearOutpost,
|
||||
Map.GateOfSecretsOutpost,
|
||||
Map.GateOftheNightfallenLandsOutpost,
|
||||
Map.AbaddonsGate,
|
||||
Map.GateOfMadness,
|
||||
Map.GateOfPain,
|
||||
Map.TheShadowNexus,
|
||||
Map.TheEbonyCitadelOfMallyxMission,
|
||||
Map.DepthsOfMadness,
|
||||
Map.DomainOfFear,
|
||||
Map.DomainOfPain,
|
||||
Map.DomainOfSecrets,
|
||||
Map.HeartOfAbaddon,
|
||||
Map.NightfallenGarden,
|
||||
Map.NightfallenJahai,
|
||||
Map.ThroneOfSecrets
|
||||
}
|
||||
};
|
||||
public static Region TarnishedCoast { get; } = new Region
|
||||
{
|
||||
Id = 17,
|
||||
Name = "Tarnished Coast",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Tarnished_Coast",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.RataSumOutpost,
|
||||
Map.GaddsEncampmentOutpost,
|
||||
Map.TarnishedHavenOutpost,
|
||||
Map.UmbralGrottoOutpost,
|
||||
Map.VloxsFalls,
|
||||
Map.FindingTheBloodstoneMission,
|
||||
Map.FindingTheBloodstoneLevel1,
|
||||
Map.FindingTheBloodstoneLevel2,
|
||||
Map.FindingTheBloodstoneLevel3,
|
||||
Map.TheElusiveGolemancerMission,
|
||||
Map.TheElusiveGolemancerLevel1,
|
||||
Map.TheElusiveGolemancerLevel2,
|
||||
Map.TheElusiveGolemancerLevel3,
|
||||
Map.GeniusOperatedLivingEnchantedManifestation,
|
||||
Map.GeniusOperatedLivingEnchantedManifestationMission,
|
||||
Map.ArborBay,
|
||||
Map.AlcaziaTangle,
|
||||
Map.MagusStones,
|
||||
Map.PolymockColiseum,
|
||||
Map.RivenEarth,
|
||||
Map.SparkflySwamp,
|
||||
Map.VerdantCascades
|
||||
}
|
||||
};
|
||||
public static Region DepthsOfTyria { get; } = new Region
|
||||
{
|
||||
Id = 18,
|
||||
Name = "Depths Of Tyria",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Depths_of_Tyria",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.CentralTransferChamberOutpost,
|
||||
Map.DestructionsDepthsMission,
|
||||
Map.DestructionsDepthsLevel1,
|
||||
Map.DestructionsDepthsLevel2,
|
||||
Map.DestructionsDepthsLevel3,
|
||||
Map.ATimeForHeroes,
|
||||
Map.ATimeForHeroesMission,
|
||||
Map.BattledepthsLevel1,
|
||||
Map.BattledepthsLevel2,
|
||||
Map.BattledepthsLevel3,
|
||||
Map.BeneathLionsArch,
|
||||
Map.CavernsBelowKamadan,
|
||||
Map.TunnelsBelowCantha,
|
||||
Map.WarinKrytaTheMausoleum,
|
||||
Map.ArachnisHauntLevel1,
|
||||
Map.ArachnisHauntLevel2,
|
||||
Map.BloodstoneCavesLevel1,
|
||||
Map.BloodstoneCavesLevel2,
|
||||
Map.BloodstoneCavesLevel3,
|
||||
Map.BogrootGrowthsLevel1,
|
||||
Map.BogrootGrowthsLevel2,
|
||||
Map.CatacombsofKathandraxLevel1,
|
||||
Map.CatacombsofKathandraxLevel2,
|
||||
Map.CatacombsofKathandraxLevel3,
|
||||
Map.CathedralofFlamesLevel1,
|
||||
Map.CathedralofFlamesLevel2,
|
||||
Map.CathedralofFlamesLevel3,
|
||||
Map.DarkrimeDelvesLevel1,
|
||||
Map.DarkrimeDelvesLevel2,
|
||||
Map.DarkrimeDelvesLevel3,
|
||||
Map.FronisIrontoesLairMission,
|
||||
Map.HeartOftheShiverpeaksLevel1,
|
||||
Map.HeartOftheShiverpeaksLevel2,
|
||||
Map.HeartOftheShiverpeaksLevel3,
|
||||
Map.OolasLabLevel1,
|
||||
Map.OolasLabLevel2,
|
||||
Map.OolasLabLevel3,
|
||||
Map.OozePit,
|
||||
Map.OozePitMission,
|
||||
Map.RavensPointLevel1,
|
||||
Map.RavensPointLevel2,
|
||||
Map.RavensPointLevel3,
|
||||
Map.RragarsMenagerieLevel1,
|
||||
Map.RragarsMenagerieLevel2,
|
||||
Map.RragarsMenagerieLevel3,
|
||||
Map.SecretLairOftheSnowmen,
|
||||
Map.SecretLairOftheSnowmen2,
|
||||
Map.SecretLairOftheSnowmen3,
|
||||
Map.SepulchreOfDragrimmarLevel1,
|
||||
Map.SepulchreOfDragrimmarLevel2,
|
||||
Map.ShardsOfOrrLevel1,
|
||||
Map.ShardsOfOrrLevel2,
|
||||
Map.ShardsOfOrrLevel3,
|
||||
Map.SlaversExileLevel1,
|
||||
Map.SlaversExileLevel2,
|
||||
Map.SlaversExileLevel3,
|
||||
Map.SlaversExileLevel4,
|
||||
Map.SlaversExileLevel5,
|
||||
Map.VloxenExcavationsLevel1,
|
||||
Map.VloxenExcavationsLevel2,
|
||||
Map.VloxenExcavationsLevel3
|
||||
}
|
||||
};
|
||||
public static Region FarShiverpeaks { get; } = new Region
|
||||
{
|
||||
Id = 19,
|
||||
Name = "Far Shiverpeaks",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Far_Shiverpeaks",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.GunnarsHoldOutpost,
|
||||
Map.BorealStationOutpost,
|
||||
Map.EyeOfTheNorthOutpost,
|
||||
Map.EyeOfTheNorthOutpostWintersdayOutpost,
|
||||
Map.LongeyesLedgeOutpost,
|
||||
Map.OlafsteadOutpost,
|
||||
Map.OlafsteadCinematic,
|
||||
Map.SifhallaOutpost,
|
||||
Map.CurseOfTheNornbear,
|
||||
Map.CurseOfTheNornbearMission,
|
||||
Map.CinematicCaveNornCursed,
|
||||
Map.AGateTooFarMission,
|
||||
Map.AGateTooFarLevel1,
|
||||
Map.AGateTooFarLevel2,
|
||||
Map.AGateTooFarLevel3,
|
||||
Map.BloodWashesBlood,
|
||||
Map.BloodWashesBloodMission,
|
||||
Map.TheNornFightingTournament,
|
||||
Map.BjoraMarches,
|
||||
Map.DrakkarLake,
|
||||
Map.Epilogue,
|
||||
Map.HallOfMonuments,
|
||||
Map.IceCliffChasms,
|
||||
Map.JagaMoraine,
|
||||
Map.NorrhartDomains,
|
||||
Map.PolymockGlacier,
|
||||
Map.VarajarFells
|
||||
}
|
||||
};
|
||||
public static Region CharrHomelands { get; } = new Region
|
||||
{
|
||||
Id = 20,
|
||||
Name = "Charr Homelands",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/Charr_Homelands",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.DoomloreShrineOutpost,
|
||||
Map.AgainstTheCharr,
|
||||
Map.AgainstTheCharrMission,
|
||||
Map.WarbandOfBrothersMission,
|
||||
Map.WarbandOfBrothersLevel1,
|
||||
Map.WarbandOfBrothersLevel2,
|
||||
Map.WarbandOfBrothersLevel3,
|
||||
Map.AssaultOnTheStronghold,
|
||||
Map.AssaultOnTheStrongholdMission,
|
||||
Map.DaladaUplands,
|
||||
Map.GrothmarWardowns,
|
||||
Map.PolymockCrossing,
|
||||
Map.SacnothValley
|
||||
}
|
||||
};
|
||||
public static Region TheBattleIsles { get; } = new Region
|
||||
{
|
||||
Id = 21,
|
||||
Name = "The Battle Isles",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/The_Battle_Isles",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.GreatTempleOfBalthazarOutpost,
|
||||
Map.IsleOfTheDeadGuildHall,
|
||||
Map.IsleOfTheDeadGuildHallMission,
|
||||
Map.IsleOfTheDeadGuildHallOutpost,
|
||||
Map.BurningIsle,
|
||||
Map.BurningIsleMission,
|
||||
Map.BurningIsleOutpost,
|
||||
Map.DruidsIsle,
|
||||
Map.DruidsIsleMission,
|
||||
Map.DruidsIsleOutpost,
|
||||
Map.FrozenIsle,
|
||||
Map.FrozenIsleMission,
|
||||
Map.FrozenIsleOutpost,
|
||||
Map.HuntersIsle,
|
||||
Map.HuntersIsleMission,
|
||||
Map.HuntersIsleOutpost,
|
||||
Map.NomadsIsle,
|
||||
Map.NomadsIsleMission,
|
||||
Map.NomadsIsleOutpost,
|
||||
Map.WarriorsIsle,
|
||||
Map.WarriorsIsleMission,
|
||||
Map.WarriorsIsleOutpost,
|
||||
Map.WizardsIsle,
|
||||
Map.WizardsIsleMission,
|
||||
Map.WizardsIsleOutpost,
|
||||
Map.ImperialIsle,
|
||||
Map.ImperialIsleMission,
|
||||
Map.ImperialIsleOutpost,
|
||||
Map.IsleOfJade,
|
||||
Map.IsleOfJadeMission,
|
||||
Map.IsleOfJadeOutpost,
|
||||
Map.IsleOfMeditation,
|
||||
Map.IsleOfMeditationMission,
|
||||
Map.IsleOfMeditationOutpost,
|
||||
Map.IsleOfWeepingStone,
|
||||
Map.IsleOfWeepingStoneMission,
|
||||
Map.IsleOfWeepingStoneOutpost,
|
||||
Map.CorruptedIsle,
|
||||
Map.CorruptedIsleMission,
|
||||
Map.CorruptedIsleOutpost,
|
||||
Map.IsleOfSolitude,
|
||||
Map.IsleOfSolitudeMission,
|
||||
Map.IsleOfSolitudeOutpost,
|
||||
Map.IsleOfWurms,
|
||||
Map.IsleOfWurmsMission,
|
||||
Map.IsleOfWurmsOutpost,
|
||||
Map.UnchartedIsle,
|
||||
Map.UnchartedIsleMission,
|
||||
Map.UnchartedIsleOutpost
|
||||
}
|
||||
};
|
||||
public static Region TheBattleOfJahai { get; } = new Region
|
||||
{
|
||||
Id = 22,
|
||||
Name = "The Battle Of Jahai",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/The_Battle_of_Jahai",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.TheBattleOfJahai
|
||||
}
|
||||
};
|
||||
public static Region TheFlightNorth { get; } = new Region
|
||||
{
|
||||
Id = 23,
|
||||
Name = "The Flight North",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/The_Flight_North",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.TheFlightNorth
|
||||
}
|
||||
};
|
||||
public static Region TheTenguAccords { get; } = new Region
|
||||
{
|
||||
Id = 24,
|
||||
Name = "The Tengu Accords",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/The_Tengu_Accords",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.TheTenguAccords
|
||||
}
|
||||
};
|
||||
public static Region TheRiseOfTheWhiteMantle { get; } = new Region
|
||||
{
|
||||
Id = 25,
|
||||
Name = "The Rise Of The White Mantle",
|
||||
WikiUrl = "https://wiki.guildwars.com/wiki/The_Rise_of_the_White_Mantle",
|
||||
Maps = new List<Map>
|
||||
{
|
||||
Map.TheRiseOfTheWhiteMantle
|
||||
}
|
||||
};
|
||||
public static Region Swat { get; } = new Region { Id = 26 };
|
||||
public static Region DevRegion { get; } = new Region { Id = 27 };
|
||||
|
||||
public static List<Region> Regions { get; } = new()
|
||||
public static IReadOnlyList<Region> Regions { get; } = new List<Region>()
|
||||
{
|
||||
Kryta,
|
||||
MaguumaJungle,
|
||||
Ascalon,
|
||||
NorthernShiverpeaks,
|
||||
ShiverpeakMountains,
|
||||
HeroesAscent,
|
||||
CrystalDesert,
|
||||
FissureOfWoe,
|
||||
RingOfFireIslands,
|
||||
PresearingAscalon,
|
||||
KainengCity,
|
||||
EchovaldForest,
|
||||
@@ -52,7 +832,7 @@ public sealed class Region : IWikiEntity
|
||||
Vabbi,
|
||||
TheDesolation,
|
||||
Istan,
|
||||
DomainOfAnguish,
|
||||
RealmOfTorment,
|
||||
TarnishedCoast,
|
||||
DepthsOfTyria,
|
||||
FarShiverpeaks,
|
||||
@@ -112,4 +892,5 @@ public sealed class Region : IWikiEntity
|
||||
public int Id { get; init; }
|
||||
public string? Name { get; init; }
|
||||
public string? WikiUrl { get; init; }
|
||||
public IReadOnlyList<Map>? Maps { get; init; }
|
||||
}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct SessionInformation
|
||||
public sealed class SessionInformation
|
||||
{
|
||||
public uint FoesKilled { get; init; }
|
||||
public uint FoesToKill { get; init; }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct SkillMetadata
|
||||
public sealed class SkillMetadata
|
||||
{
|
||||
public Skill? Skill { get; init; }
|
||||
public uint Adrenaline1 { get; init; }
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct TitleInformation
|
||||
public sealed class TitleInformation
|
||||
{
|
||||
public bool IsValid => this.CurrentPoints != 0 && this.PointsForCurrentRank != 0 && this.PointsForNextRank != 0 && this.TierNumber != 0 && this.MaxTierNumber != 0;
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct UnknownBagItem : IBagContent
|
||||
public sealed class UnknownBagItem : IBagContent
|
||||
{
|
||||
public uint ItemId { get; init; }
|
||||
public uint Slot { get; init; }
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public sealed class WorldData
|
||||
{
|
||||
public Campaign? Campaign { get; init; }
|
||||
public Continent? Continent { get; init; }
|
||||
public Region? Region { get; init; }
|
||||
public Map? Map { get; init; }
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public readonly struct WorldPlayerInformation : IEntity
|
||||
public sealed class WorldPlayerInformation : IEntity
|
||||
{
|
||||
public string? Name { get; init; }
|
||||
public uint Timer { get; init; }
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Daybreak.Models.Interop;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public readonly struct AreaInfoContext
|
||||
{
|
||||
[FieldOffset(0x0000)]
|
||||
public readonly uint CampaignId;
|
||||
[FieldOffset(0x0004)]
|
||||
public readonly uint ContinentId;
|
||||
[FieldOffset(0x0008)]
|
||||
public readonly uint RegionId;
|
||||
[FieldOffset(0x000C)]
|
||||
public readonly RegionType RegionType;
|
||||
[FieldOffset(0x0010)]
|
||||
public readonly uint Flags;
|
||||
[FieldOffset(0x0018)]
|
||||
public readonly uint MinPartySize;
|
||||
[FieldOffset(0x001C)]
|
||||
public readonly uint MaxPartySize;
|
||||
[FieldOffset(0x0020)]
|
||||
public readonly uint MinPlayerSize;
|
||||
[FieldOffset(0x0024)]
|
||||
public readonly uint MaxPlayerSize;
|
||||
[FieldOffset(0x0030)]
|
||||
public readonly uint MinLevel;
|
||||
[FieldOffset(0x0034)]
|
||||
public readonly uint MaxLevel;
|
||||
[FieldOffset(0x0040)]
|
||||
public readonly uint IconPositionX;
|
||||
[FieldOffset(0x0044)]
|
||||
public readonly uint IconPositionY;
|
||||
[FieldOffset(0x0048)]
|
||||
public readonly uint IconStartX;
|
||||
[FieldOffset(0x004C)]
|
||||
public readonly uint IconStartY;
|
||||
[FieldOffset(0x0050)]
|
||||
public readonly uint IconEndX;
|
||||
[FieldOffset(0x0054)]
|
||||
public readonly uint IconEndY;
|
||||
|
||||
public bool HasEnterButton => (this.Flags & 0x100) != 0 || (this.Flags & 0x40000) != 0;
|
||||
public bool IsOnWorldMap => (this.Flags & 0x20) == 0;
|
||||
public bool IsPvp => (this.Flags & 0x1) != 0;
|
||||
public bool IsGuildHall => (this.Flags & 0x800000) != 0;
|
||||
}
|
||||
@@ -0,0 +1,9 @@
|
||||
namespace Daybreak.Models.Interop;
|
||||
public readonly struct InstanceInfoContext
|
||||
{
|
||||
public readonly GuildwarsPointer<uint> TerrainInfo;
|
||||
public readonly InstanceType InstanceType;
|
||||
public readonly GuildwarsPointer<AreaInfoContext> AreaInfo;
|
||||
public readonly uint TerrainCount;
|
||||
public readonly GuildwarsPointer<uint> TerrainInfo2;
|
||||
}
|
||||
@@ -0,0 +1,8 @@
|
||||
namespace Daybreak.Models.Interop;
|
||||
|
||||
public enum InstanceType
|
||||
{
|
||||
Outpost,
|
||||
Explorable,
|
||||
Loading
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
namespace Daybreak.Models.Interop;
|
||||
|
||||
public enum RegionType : uint
|
||||
{
|
||||
AllianceBattle,
|
||||
Arena,
|
||||
ExplorableZone,
|
||||
GuildBattleArea,
|
||||
GuildHall,
|
||||
MissionOutpost,
|
||||
CooperativeMission,
|
||||
CompetitiveMission,
|
||||
EliteMission,
|
||||
Challenge,
|
||||
Outpost,
|
||||
ZaishenBattle,
|
||||
HeroesAscent,
|
||||
City,
|
||||
MissionArea,
|
||||
HeroBattleOutpost,
|
||||
HeroBattleArea,
|
||||
EotnMission,
|
||||
Dungeon,
|
||||
Marketplace,
|
||||
Unknown,
|
||||
DevRegion
|
||||
}
|
||||
@@ -1,39 +1,113 @@
|
||||
using Daybreak.Services.Bloogum.Models;
|
||||
using Daybreak.Services.Images;
|
||||
using Daybreak.Services.Scanner;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Core.Extensions;
|
||||
using System.Extensions;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using System.Net.Http;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Daybreak.Services.Bloogum;
|
||||
|
||||
public sealed class BloogumClient : IBloogumClient
|
||||
{
|
||||
private const string CacheFolder = "Bloogum";
|
||||
private const string BaseAddress = "http://bloogum.net/guildwars";
|
||||
private readonly IImageCache imageCache;
|
||||
private readonly IGuildwarsMemoryCache guildwarsMemoryCache;
|
||||
private readonly IHttpClient<BloogumClient> httpClient;
|
||||
private readonly ILogger logger;
|
||||
private readonly Random random = new();
|
||||
|
||||
public BloogumClient(
|
||||
IImageCache imageCache,
|
||||
IGuildwarsMemoryCache guildwarsMemoryCache,
|
||||
ILogger<BloogumClient> logger,
|
||||
IHttpClient<BloogumClient> httpClient)
|
||||
{
|
||||
this.logger = logger.ThrowIfNull(nameof(logger));
|
||||
this.httpClient = httpClient.ThrowIfNull(nameof(httpClient));
|
||||
this.imageCache = imageCache.ThrowIfNull();
|
||||
this.guildwarsMemoryCache = guildwarsMemoryCache.ThrowIfNull();
|
||||
this.logger = logger.ThrowIfNull();
|
||||
this.httpClient = httpClient.ThrowIfNull();
|
||||
}
|
||||
|
||||
public async Task<Optional<Stream>> GetRandomScreenShot()
|
||||
public async Task<ImageSource?> GetImage(bool localized)
|
||||
{
|
||||
var location = Location.Locations[this.random.Next(0, Location.Locations.Count)];
|
||||
var category = location.Categories[this.random.Next(0, location.Categories.Count)];
|
||||
var picture = this.random.Next(0, category.ImageCount) + 1;
|
||||
var uri = await this.GetImageUri(localized);
|
||||
var localUri = Path.GetFullPath(Path.Combine(CacheFolder, uri));
|
||||
if (!File.Exists(localUri))
|
||||
{
|
||||
var imageStream = await this.GetRemoteImage(uri);
|
||||
if (imageStream is null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var uri = $"{location.LocationName}/{category.CategoryName}/{picture:00}.jpg";
|
||||
return await this.GetImage(uri).ConfigureAwait(false);
|
||||
await CacheImage(localUri, imageStream);
|
||||
imageStream.Dispose();
|
||||
}
|
||||
|
||||
return await this.imageCache.GetImage(localUri);
|
||||
}
|
||||
|
||||
private async Task<Optional<Stream>> GetImage(string url)
|
||||
private async Task<string> GetImageUri(bool localized)
|
||||
{
|
||||
if (!localized)
|
||||
{
|
||||
return GetRandomScreenShot();
|
||||
}
|
||||
|
||||
var worldInfo = await this.guildwarsMemoryCache.ReadWorldData(CancellationToken.None);
|
||||
if (worldInfo is null)
|
||||
{
|
||||
return GetRandomScreenShot();
|
||||
}
|
||||
|
||||
var validLocations = Location.Locations.Where(l => l.Region == worldInfo.Region).ToList();
|
||||
var validCategories = validLocations.SelectMany(l => l.Categories).Where(c => c.Map == worldInfo.Map).ToList();
|
||||
if (validCategories.None())
|
||||
{
|
||||
if (validLocations.None())
|
||||
{
|
||||
return GetRandomScreenShot();
|
||||
}
|
||||
|
||||
var location = validLocations[Random.Shared.Next(0, validLocations.Count)];
|
||||
return GetRandomScreenShot(location);
|
||||
}
|
||||
|
||||
var selectedCategory = validCategories[Random.Shared.Next(0, validCategories.Count)];
|
||||
if (selectedCategory.ImageCount == 0)
|
||||
{
|
||||
if (validLocations.None())
|
||||
{
|
||||
return GetRandomScreenShot();
|
||||
}
|
||||
|
||||
var location = validLocations[Random.Shared.Next(0, validLocations.Count)];
|
||||
return GetRandomScreenShot(location);
|
||||
}
|
||||
|
||||
var selectedLocation = Location.Locations.FirstOrDefault(l => l.Categories.Contains(selectedCategory));
|
||||
if (selectedLocation is null)
|
||||
{
|
||||
if (validLocations.None())
|
||||
{
|
||||
return GetRandomScreenShot();
|
||||
}
|
||||
|
||||
var location = validLocations[Random.Shared.Next(0, validLocations.Count)];
|
||||
return GetRandomScreenShot(location);
|
||||
}
|
||||
|
||||
return GetScreenshotName(selectedLocation, selectedCategory, Random.Shared.Next(0, selectedCategory.ImageCount));
|
||||
}
|
||||
|
||||
private async Task<Stream?> GetRemoteImage(string url)
|
||||
{
|
||||
this.logger.LogInformation($"Retrieving image from {BaseAddress}/{url}");
|
||||
try
|
||||
@@ -48,13 +122,40 @@ public sealed class BloogumClient : IBloogumClient
|
||||
else
|
||||
{
|
||||
this.logger.LogError($"Failed to retrive image. Status code {response.StatusCode}. Reason {response.ReasonPhrase}");
|
||||
return Optional.None<Stream>();
|
||||
return default;
|
||||
}
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
this.logger.LogError(e.ToString());
|
||||
return Optional.None<Stream>();
|
||||
return default;
|
||||
}
|
||||
}
|
||||
|
||||
private static string GetRandomScreenShot()
|
||||
{
|
||||
var location = Location.Locations[Random.Shared.Next(0, Location.Locations.Count)];
|
||||
return GetRandomScreenShot(location);
|
||||
}
|
||||
|
||||
private static string GetRandomScreenShot(Location location)
|
||||
{
|
||||
var category = location.Categories[Random.Shared.Next(0, location.Categories.Count)];
|
||||
var picture = Random.Shared.Next(0, category.ImageCount) + 1;
|
||||
|
||||
return GetScreenshotName(location, category, picture);
|
||||
}
|
||||
|
||||
private static string GetScreenshotName(Location location, Category category, int picture)
|
||||
{
|
||||
return $"{location.LocationName}/{category.CategoryName}/{picture:00}.jpg";
|
||||
}
|
||||
|
||||
private static async Task CacheImage(string uri, Stream imageStream)
|
||||
{
|
||||
var directoryName = Path.GetDirectoryName(uri);
|
||||
Directory.CreateDirectory(directoryName);
|
||||
using var fs = File.Create(uri);
|
||||
await imageStream.CopyToAsync(fs);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
using System.Extensions;
|
||||
using System.IO;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Daybreak.Services.Bloogum;
|
||||
|
||||
public interface IBloogumClient
|
||||
{
|
||||
Task<Optional<Stream>> GetRandomScreenShot();
|
||||
Task<ImageSource?> GetImage(bool localized);
|
||||
}
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
namespace Daybreak.Services.Bloogum.Models;
|
||||
using Daybreak.Models.Guildwars;
|
||||
|
||||
namespace Daybreak.Services.Bloogum.Models;
|
||||
|
||||
public sealed class Category
|
||||
{
|
||||
public Map Map { get; }
|
||||
public string CategoryName { get; }
|
||||
public int ImageCount { get; }
|
||||
|
||||
public Category(string categoryName, int imageCount)
|
||||
public Category(Map map, string categoryName, int imageCount)
|
||||
{
|
||||
this.Map = map;
|
||||
this.CategoryName = categoryName;
|
||||
this.ImageCount = imageCount;
|
||||
}
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using System.Threading.Tasks;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Media;
|
||||
|
||||
namespace Daybreak.Services.Images;
|
||||
|
||||
@@ -73,6 +73,13 @@ public sealed class StupidPathfinder : IPathfinder
|
||||
return new PathfindingFailure.PathfindingDisabled();
|
||||
}
|
||||
|
||||
if (map is null ||
|
||||
map.Trapezoids is null)
|
||||
{
|
||||
scopedLogger.LogError("Null pathfinding map");
|
||||
return new PathfindingFailure.UnexpectedFailure();
|
||||
}
|
||||
|
||||
if (GetContainingTrapezoid(map, startPoint) is not Trapezoid startTrapezoid)
|
||||
{
|
||||
scopedLogger.LogInformation("Start point not in map. Getting closest start point in map");
|
||||
@@ -163,7 +170,8 @@ public sealed class StupidPathfinder : IPathfinder
|
||||
|
||||
private static Trapezoid? GetContainingTrapezoid(PathingData map, Point point)
|
||||
{
|
||||
if (map.Trapezoids is null)
|
||||
if (map is null ||
|
||||
map.Trapezoids is null)
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
@@ -0,0 +1,78 @@
|
||||
using Daybreak.Configuration.Options;
|
||||
using Daybreak.Models.Guildwars;
|
||||
using Daybreak.Services.Scanner.Models;
|
||||
using System;
|
||||
using System.Configuration;
|
||||
using System.Core.Extensions;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Daybreak.Services.Scanner;
|
||||
|
||||
public sealed class GuildwarsMemoryCache : IGuildwarsMemoryCache
|
||||
{
|
||||
private readonly IGuildwarsMemoryReader guildwarsMemoryReader;
|
||||
private readonly ILiveOptions<MemoryReaderOptions> liveOptions;
|
||||
private readonly CachedData<GameData?> gameDataCache = new();
|
||||
private readonly CachedData<InventoryData?> inventoryDataCache = new();
|
||||
private readonly CachedData<LoginData?> loginDataCache = new();
|
||||
private readonly CachedData<PathingData?> pathingDataCache = new();
|
||||
private readonly CachedData<PathingMetadata?> pathingMetadataCache = new();
|
||||
private readonly CachedData<WorldData?> worldDataCache = new();
|
||||
|
||||
public GuildwarsMemoryCache(
|
||||
IGuildwarsMemoryReader guildwarsMemoryReader,
|
||||
ILiveOptions<MemoryReaderOptions> liveOptions)
|
||||
{
|
||||
this.guildwarsMemoryReader = guildwarsMemoryReader.ThrowIfNull();
|
||||
this.liveOptions = liveOptions.ThrowIfNull();
|
||||
}
|
||||
|
||||
public Task<GameData?> ReadGameData(CancellationToken cancellationToken)
|
||||
{
|
||||
return this.ReadDataInternal(this.gameDataCache, this.guildwarsMemoryReader.ReadGameData, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<InventoryData?> ReadInventoryData(CancellationToken cancellationToken)
|
||||
{
|
||||
return this.ReadDataInternal(this.inventoryDataCache, this.guildwarsMemoryReader.ReadInventoryData, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<LoginData?> ReadLoginData(CancellationToken cancellationToken)
|
||||
{
|
||||
return this.ReadDataInternal(this.loginDataCache, this.guildwarsMemoryReader.ReadLoginData, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<PathingData?> ReadPathingData(CancellationToken cancellationToken)
|
||||
{
|
||||
return this.ReadDataInternal(this.pathingDataCache, this.guildwarsMemoryReader.ReadPathingData, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<PathingMetadata?> ReadPathingMetaData(CancellationToken cancellationToken)
|
||||
{
|
||||
return this.ReadDataInternal(this.pathingMetadataCache, this.guildwarsMemoryReader.ReadPathingMetaData, cancellationToken);
|
||||
}
|
||||
|
||||
public Task<WorldData?> ReadWorldData(CancellationToken cancellationToken)
|
||||
{
|
||||
return this.ReadDataInternal(this.worldDataCache, this.guildwarsMemoryReader.ReadWorldData, cancellationToken);
|
||||
}
|
||||
|
||||
private async Task<T?> ReadDataInternal<T>(CachedData<T?> cachedData, Func<CancellationToken, Task<T?>> task, CancellationToken cancellationToken)
|
||||
{
|
||||
if (DateTime.Now - cachedData.SetTime <= TimeSpan.FromMilliseconds(this.liveOptions.Value.MemoryReaderFrequency))
|
||||
{
|
||||
return cachedData.Data;
|
||||
}
|
||||
|
||||
await this.guildwarsMemoryReader.EnsureInitialized(cancellationToken);
|
||||
var data = await task(cancellationToken);
|
||||
if (data is null)
|
||||
{
|
||||
return data;
|
||||
}
|
||||
|
||||
cachedData.SetData(data);
|
||||
return cachedData.Data;
|
||||
}
|
||||
}
|
||||
@@ -40,6 +40,7 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
|
||||
private uint entityArrayPointer;
|
||||
private uint titleDataPointer;
|
||||
private uint targetIdPointer;
|
||||
private uint instanceInfoPointer;
|
||||
|
||||
public GuildwarsMemoryReader(
|
||||
IApplicationLauncher applicationLauncher,
|
||||
@@ -135,6 +136,16 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
|
||||
return Task.Run(() => this.SafeReadGameMemory(this.ReadInventoryDataInternal), cancellationToken);
|
||||
}
|
||||
|
||||
public Task<WorldData?> ReadWorldData(CancellationToken cancellationToken)
|
||||
{
|
||||
if (this.memoryScanner.Scanning is false)
|
||||
{
|
||||
return Task.FromResult<WorldData?>(default);
|
||||
}
|
||||
|
||||
return Task.Run(() => this.SafeReadGameMemory(this.ReadWorldDataInternal), cancellationToken);
|
||||
}
|
||||
|
||||
private async Task InitializeSafe(Process process, ScopedLogger<GuildwarsMemoryReader> scopedLogger)
|
||||
{
|
||||
if (this.memoryScanner.Process is null ||
|
||||
@@ -411,6 +422,35 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
|
||||
return inventoryData;
|
||||
}
|
||||
|
||||
private WorldData? ReadWorldDataInternal()
|
||||
{
|
||||
var globalContext = this.memoryScanner.ReadPtrChain<GlobalContext>(this.memoryScanner.ModuleStartAddress, finalPointerOffset: 0x0, 0x00629204, 0x18);
|
||||
|
||||
if (!globalContext.GameContext.IsValid() ||
|
||||
!globalContext.UserContext.IsValid() ||
|
||||
!globalContext.InstanceContext.IsValid())
|
||||
{
|
||||
return default;
|
||||
}
|
||||
|
||||
var userContext = this.memoryScanner.Read(globalContext.UserContext, UserContext.BaseOffset);
|
||||
var instanceInfo = this.memoryScanner.ReadPtrChain<InstanceInfoContext>(this.GetInstanceInfoPointer(), 0x0, 0x0);
|
||||
var areaInfo = this.memoryScanner.Read(instanceInfo.AreaInfo);
|
||||
|
||||
_ = Map.TryParse((int)userContext.MapId, out var map);
|
||||
_ = Region.TryParse((int)areaInfo.RegionId, out var region);
|
||||
_ = Continent.TryParse((int)areaInfo.ContinentId, out var continent);
|
||||
_ = Campaign.TryParse((int)areaInfo.CampaignId, out var campaign);
|
||||
|
||||
return new WorldData
|
||||
{
|
||||
Campaign = campaign,
|
||||
Continent = continent,
|
||||
Region = region,
|
||||
Map = map
|
||||
};
|
||||
}
|
||||
|
||||
private uint GetPlayerIdPointer()
|
||||
{
|
||||
if (this.playerIdPointer == 0)
|
||||
@@ -451,6 +491,16 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader
|
||||
return this.targetIdPointer;
|
||||
}
|
||||
|
||||
private uint GetInstanceInfoPointer()
|
||||
{
|
||||
if (this.instanceInfoPointer == 0)
|
||||
{
|
||||
this.instanceInfoPointer = this.memoryScanner.ScanForPtr(new byte[] { 0x6A, 0x2C, 0x50, 0xE8, 0x00, 0x00, 0x00, 0x00, 0x83, 0xC4, 0x08, 0xC7 }, "xxxx????xxxx") + 0xD;
|
||||
}
|
||||
|
||||
return this.instanceInfoPointer;
|
||||
}
|
||||
|
||||
private Bag? GetBag(GuildwarsPointer<BagInfo> bagInfoPtr, uint expectedBagType, bool returnEmptyBag)
|
||||
{
|
||||
var bagInfo = this.memoryScanner.Read(bagInfoPtr);
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using Daybreak.Models.Guildwars;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
namespace Daybreak.Services.Scanner;
|
||||
|
||||
public interface IGuildwarsMemoryCache
|
||||
{
|
||||
Task<LoginData?> ReadLoginData(CancellationToken cancellationToken);
|
||||
Task<GameData?> ReadGameData(CancellationToken cancellationToken);
|
||||
Task<PathingData?> ReadPathingData(CancellationToken cancellationToken);
|
||||
Task<PathingMetadata?> ReadPathingMetaData(CancellationToken cancellationToken);
|
||||
Task<InventoryData?> ReadInventoryData(CancellationToken cancellationToken);
|
||||
Task<WorldData?> ReadWorldData(CancellationToken cancellationToken);
|
||||
}
|
||||
@@ -1,5 +1,4 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Models.Guildwars;
|
||||
using Daybreak.Models.Guildwars;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
|
||||
@@ -13,5 +12,6 @@ public interface IGuildwarsMemoryReader
|
||||
Task<PathingData?> ReadPathingData(CancellationToken cancellationToken);
|
||||
Task<PathingMetadata?> ReadPathingMetaData(CancellationToken cancellationToken);
|
||||
Task<InventoryData?> ReadInventoryData(CancellationToken cancellationToken);
|
||||
Task<WorldData?> ReadWorldData(CancellationToken cancellationToken);
|
||||
void Stop();
|
||||
}
|
||||
|
||||
@@ -0,0 +1,15 @@
|
||||
using System;
|
||||
|
||||
namespace Daybreak.Services.Scanner.Models;
|
||||
|
||||
public sealed class CachedData<T>
|
||||
{
|
||||
public T? Data { get; private set; }
|
||||
public DateTime SetTime { get; private set; } = DateTime.MinValue;
|
||||
|
||||
public void SetData(T data)
|
||||
{
|
||||
this.Data = data;
|
||||
this.SetTime = DateTime.Now;
|
||||
}
|
||||
}
|
||||
@@ -17,8 +17,10 @@
|
||||
x:Name="_this"
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
|
||||
<converters:BooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter" TriggerValue="True" ></converters:BooleanToVisibilityConverter>
|
||||
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" />
|
||||
<converters:BooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter" TriggerValue="True" />
|
||||
<converters:BooleanToGridLengthConverter x:Key="InventoryComponentVisibilityConverter" VisibleValue="1*" />
|
||||
<converters:BooleanToGridLengthConverter x:Key="MinimapComponentVisibilityConverter" VisibleValue="1*" />
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -69,8 +71,8 @@
|
||||
<Grid Grid.Column="1"
|
||||
Margin="5, 0, 5, 0">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"/>
|
||||
<RowDefinition Height="1*"/>
|
||||
<RowDefinition Height="{Binding ElementName=_this, Path=InventoryVisible, Mode=OneWay, Converter={StaticResource InventoryComponentVisibilityConverter}}" />
|
||||
<RowDefinition Height="{Binding ElementName=_this, Path=MinimapVisible, Mode=OneWay, Converter={StaticResource MinimapComponentVisibilityConverter}}" />
|
||||
<RowDefinition Height="1.5*"/>
|
||||
</Grid.RowDefinitions>
|
||||
<Grid x:Name="InventoryHolder"
|
||||
@@ -83,7 +85,8 @@
|
||||
MaximizeClicked="InventoryComponent_MaximizeClicked"
|
||||
Visibility="{Binding ElementName=_this, Path=MainPlayerDataValid, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
|
||||
ItemWikiClicked="InventoryComponent_ItemWikiClicked"
|
||||
PriceHistoryClicked="InventoryComponent_PriceHistoryClicked"/>
|
||||
PriceHistoryClicked="InventoryComponent_PriceHistoryClicked"
|
||||
IsEnabled="{Binding ElementName=_this, Path=InventoryVisible, Mode=OneWay}"/>
|
||||
</Grid>
|
||||
</Grid>
|
||||
<Grid x:Name="MinimapHolder"
|
||||
@@ -106,7 +109,8 @@
|
||||
ClipToBounds="True"
|
||||
Background="{DynamicResource Daybreak.Brushes.Background}"
|
||||
BorderBrush="{DynamicResource MahApps.Brushes.ThemeForeground}"
|
||||
BorderThickness="1" />
|
||||
BorderThickness="1"
|
||||
IsEnabled="{Binding ElementName=_this, Path=MinimapVisible, Mode=OneWay}"/>
|
||||
<controls:CircularLoadingWidget Height="100"
|
||||
Width="100"
|
||||
Visibility="{Binding ElementName=_this,Path=LoadingPathingData,Mode=OneWay,Converter={StaticResource BooleanToVisibilityConverter}}" />
|
||||
|
||||
@@ -27,7 +27,7 @@ public partial class FocusView : UserControl
|
||||
{
|
||||
private readonly IBuildTemplateManager buildTemplateManager;
|
||||
private readonly IApplicationLauncher applicationLauncher;
|
||||
private readonly IGuildwarsMemoryReader guildwarsMemoryReader;
|
||||
private readonly IGuildwarsMemoryCache guildwarsMemoryCache;
|
||||
private readonly IExperienceCalculator experienceCalculator;
|
||||
private readonly IViewManager viewManager;
|
||||
private readonly ILiveUpdateableOptions<FocusViewOptions> liveUpdateableOptions;
|
||||
@@ -57,16 +57,18 @@ public partial class FocusView : UserControl
|
||||
[GenerateDependencyProperty]
|
||||
private bool inventoryVisible;
|
||||
|
||||
[GenerateDependencyProperty]
|
||||
private bool minimapVisible;
|
||||
|
||||
private bool browserMaximized = false;
|
||||
private bool minimapMaximized = false;
|
||||
private bool inventoryMaximized = false;
|
||||
private CancellationTokenSource? cancellationTokenSource;
|
||||
private CancellationTokenSource? loadingPathingDataCancellationTokenSource;
|
||||
|
||||
public FocusView(
|
||||
IBuildTemplateManager buildTemplateManager,
|
||||
IApplicationLauncher applicationLauncher,
|
||||
IGuildwarsMemoryReader guildwarsMemoryReader,
|
||||
IGuildwarsMemoryCache guildwarsMemoryCache,
|
||||
IExperienceCalculator experienceCalculator,
|
||||
IViewManager viewManager,
|
||||
ILiveUpdateableOptions<FocusViewOptions> liveUpdateableOptions,
|
||||
@@ -74,7 +76,7 @@ public partial class FocusView : UserControl
|
||||
{
|
||||
this.buildTemplateManager = buildTemplateManager.ThrowIfNull();
|
||||
this.applicationLauncher = applicationLauncher.ThrowIfNull();
|
||||
this.guildwarsMemoryReader = guildwarsMemoryReader.ThrowIfNull();
|
||||
this.guildwarsMemoryCache = guildwarsMemoryCache.ThrowIfNull();
|
||||
this.experienceCalculator = experienceCalculator.ThrowIfNull();
|
||||
this.viewManager = viewManager.ThrowIfNull();
|
||||
this.liveUpdateableOptions = liveUpdateableOptions.ThrowIfNull();
|
||||
@@ -97,12 +99,7 @@ public partial class FocusView : UserControl
|
||||
base.OnPropertyChanged(e);
|
||||
}
|
||||
|
||||
private TimeSpan GetMemoryReaderLatency()
|
||||
{
|
||||
return TimeSpan.FromMilliseconds(this.liveUpdateableOptions.Value.MemoryReaderFrequency);
|
||||
}
|
||||
|
||||
private async Task UpdatePathingData(CancellationToken cancellationToken)
|
||||
private async Task UpdatePathingData()
|
||||
{
|
||||
if (this.applicationLauncher.IsGuildwarsRunning is false)
|
||||
{
|
||||
@@ -112,24 +109,23 @@ public partial class FocusView : UserControl
|
||||
return;
|
||||
}
|
||||
|
||||
this.Dispatcher.Invoke(() =>
|
||||
var pathingMeta = await this.guildwarsMemoryCache.ReadPathingMetaData(this.cancellationTokenSource?.Token ?? CancellationToken.None);
|
||||
if (pathingMeta?.TrapezoidCount == this.PathingData?.Trapezoids?.Count ||
|
||||
this.cancellationTokenSource?.IsCancellationRequested is not false)
|
||||
{
|
||||
this.LoadingPathingData = true;
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
await this.guildwarsMemoryReader.EnsureInitialized(cancellationToken);
|
||||
|
||||
var maybePathingData = await this.guildwarsMemoryReader.ReadPathingData(cancellationToken);
|
||||
await this.Dispatcher.InvokeAsync(() => this.LoadingPathingData = true);
|
||||
var maybePathingData = await this.guildwarsMemoryCache.ReadPathingData(this.cancellationTokenSource?.Token ?? CancellationToken.None);
|
||||
if (maybePathingData is not PathingData pathingData ||
|
||||
pathingData.Trapezoids is null ||
|
||||
pathingData.Trapezoids.Count == 0)
|
||||
{
|
||||
await Task.Delay(1000, cancellationToken);
|
||||
await this.UpdatePathingData(cancellationToken);
|
||||
return;
|
||||
}
|
||||
|
||||
this.Dispatcher.Invoke(() =>
|
||||
await this.Dispatcher.InvokeAsync(() =>
|
||||
{
|
||||
this.PathingData = pathingData;
|
||||
this.LoadingPathingData = false;
|
||||
@@ -146,9 +142,7 @@ public partial class FocusView : UserControl
|
||||
return;
|
||||
}
|
||||
|
||||
await this.guildwarsMemoryReader.EnsureInitialized(this.cancellationTokenSource?.Token ?? CancellationToken.None).ConfigureAwait(true);
|
||||
|
||||
var maybeGameData = await this.guildwarsMemoryReader.ReadGameData(this.cancellationTokenSource?.Token ?? CancellationToken.None).ConfigureAwait(true);
|
||||
var maybeGameData = await this.guildwarsMemoryCache.ReadGameData(this.cancellationTokenSource?.Token ?? CancellationToken.None).ConfigureAwait(true);
|
||||
if (maybeGameData is not GameData gameData)
|
||||
{
|
||||
this.MainPlayerDataValid = false;
|
||||
@@ -168,19 +162,6 @@ public partial class FocusView : UserControl
|
||||
|
||||
this.GameData = gameData;
|
||||
this.MainPlayerDataValid = true;
|
||||
var pathingMeta = await this.guildwarsMemoryReader.ReadPathingMetaData(this.cancellationTokenSource?.Token ?? CancellationToken.None);
|
||||
if (pathingMeta?.TrapezoidCount != this.PathingData.Trapezoids?.Count &&
|
||||
this.loadingPathingDataCancellationTokenSource is null)
|
||||
{
|
||||
this.loadingPathingDataCancellationTokenSource = new CancellationTokenSource();
|
||||
_ = Task.Run(() => this.UpdatePathingData(this.loadingPathingDataCancellationTokenSource.Token), this.loadingPathingDataCancellationTokenSource.Token)
|
||||
.ContinueWith(_ =>
|
||||
{
|
||||
this.loadingPathingDataCancellationTokenSource?.Dispose();
|
||||
this.loadingPathingDataCancellationTokenSource = null;
|
||||
});
|
||||
}
|
||||
|
||||
this.Browser.Visibility = this.MainPlayerDataValid is true ?
|
||||
this.minimapMaximized ?
|
||||
Visibility.Hidden :
|
||||
@@ -188,9 +169,31 @@ public partial class FocusView : UserControl
|
||||
Visibility.Collapsed;
|
||||
}
|
||||
|
||||
private async void PeriodicallyReadPathingData(CancellationToken cancellationToken)
|
||||
{
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
await Task.WhenAll(
|
||||
this.UpdatePathingData(),
|
||||
Task.Delay(1000, cancellationToken)).ConfigureAwait(true);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
this.logger.LogError(ex, "Encountered non-terminating exception. Silently continuing");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private async void PeriodicallyReadGameData(CancellationToken cancellationToken)
|
||||
{
|
||||
var memoryReaderLatency = this.GetMemoryReaderLatency();
|
||||
while (!cancellationToken.IsCancellationRequested)
|
||||
{
|
||||
try
|
||||
@@ -202,7 +205,7 @@ public partial class FocusView : UserControl
|
||||
|
||||
await Task.WhenAll(
|
||||
this.UpdateGameData(),
|
||||
Task.Delay(memoryReaderLatency, cancellationToken)).ConfigureAwait(true);
|
||||
Task.Delay(16, cancellationToken)).ConfigureAwait(true);
|
||||
|
||||
}
|
||||
catch (Exception ex)
|
||||
@@ -223,18 +226,18 @@ public partial class FocusView : UserControl
|
||||
return;
|
||||
}
|
||||
|
||||
var readInventoryTask = this.guildwarsMemoryReader.ReadInventoryData(cancellationToken);
|
||||
var readInventoryTask = this.guildwarsMemoryCache.ReadInventoryData(cancellationToken);
|
||||
await Task.WhenAll(
|
||||
readInventoryTask,
|
||||
Task.Delay(1000, cancellationToken)).ConfigureAwait(true);
|
||||
|
||||
var maybeInventoryData = await readInventoryTask;
|
||||
if (!maybeInventoryData.HasValue)
|
||||
if (maybeInventoryData is null)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
this.InventoryData = maybeInventoryData.Value;
|
||||
this.InventoryData = maybeInventoryData;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
@@ -247,6 +250,7 @@ public partial class FocusView : UserControl
|
||||
{
|
||||
this.BrowserAddress = this.liveUpdateableOptions.Value.BrowserUrl;
|
||||
this.InventoryVisible = this.liveUpdateableOptions.Value.InventoryComponentVisible;
|
||||
this.MinimapVisible = this.liveUpdateableOptions.Value.MinimapComponentVisible;
|
||||
this.cancellationTokenSource?.Dispose();
|
||||
this.cancellationTokenSource = new CancellationTokenSource();
|
||||
var cancellationToken = this.cancellationTokenSource.Token;
|
||||
@@ -255,15 +259,17 @@ public partial class FocusView : UserControl
|
||||
{
|
||||
this.PeriodicallyReadInventoryData(cancellationToken);
|
||||
}
|
||||
|
||||
if (this.MinimapVisible)
|
||||
{
|
||||
this.PeriodicallyReadPathingData(cancellationToken);
|
||||
}
|
||||
}
|
||||
|
||||
private void FocusView_Unloaded(object _, RoutedEventArgs e)
|
||||
{
|
||||
this.cancellationTokenSource?.Cancel();
|
||||
this.cancellationTokenSource = null;
|
||||
this.loadingPathingDataCancellationTokenSource?.Cancel();
|
||||
this.loadingPathingDataCancellationTokenSource = null;
|
||||
this.guildwarsMemoryReader?.Stop();
|
||||
}
|
||||
|
||||
private void Browser_MaximizeClicked(object _, EventArgs e)
|
||||
|
||||
Reference in New Issue
Block a user