Compare commits

...
5 Commits
Author SHA1 Message Date
amacocianandGitHub b61c99dd1c Fix build template failing to load secondary attributes (#157)
Improve build template handling logic
Add attribute points calculator
2023-03-03 20:51:03 +00:00
amacocianandGitHub 1ccb0d5334 Add map entry to FocusView (#155)
Fix Icon Downloader
Improve settings navigation
Change multiple views to match the design of the rest of the application
2023-03-02 20:11:00 +01:00
amacocianandGitHub 7fa01353dd UX Bugfixes and improvements (#151)
Fix memory reading issues when restarting the game
Fixed current energy and health retrieval and calculation
Added health and energy bars in focus view
Fixed focus view size calculation
Improved buildtemplate performance
Improved buildtemplate navigation
Added loading widgets to menus that are loading asynchronously
2023-03-02 16:49:00 +00:00
amacocianandGitHub 4f849d880b Ux improvements (#148)
Rearrange quest log layout
Fix some quest names
Fix some buttons ux
Mitigate issues when reading guildwars memory
2023-03-02 01:58:07 +00:00
amacocianandGitHub 4b60c96aad Closes #146 #145 (#147) 2023-03-02 00:39:52 +00:00
78 changed files with 1582 additions and 698 deletions
@@ -28,14 +28,14 @@ public class BuildTemplateManagerTests
build.Primary.Should().Be(Profession.Assassin);
build.Secondary.Should().Be(Profession.None);
build.Attributes.Count.Should().Be(4);
build.Attributes[0].Attribute.Should().Be(Attribute.DaggerMastery);
build.Attributes[1].Attribute.Should().Be(Attribute.DaggerMastery);
build.Attributes[1].Points.Should().Be(11);
build.Attributes[2].Attribute.Should().Be(Attribute.DeadlyArts);
build.Attributes[2].Points.Should().Be(1);
build.Attributes[3].Attribute.Should().Be(Attribute.ShadowArts);
build.Attributes[3].Points.Should().Be(5);
build.Attributes[0].Attribute.Should().Be(Attribute.CriticalStrikes);
build.Attributes[0].Points.Should().Be(11);
build.Attributes[1].Attribute.Should().Be(Attribute.DeadlyArts);
build.Attributes[1].Points.Should().Be(1);
build.Attributes[2].Attribute.Should().Be(Attribute.ShadowArts);
build.Attributes[2].Points.Should().Be(5);
build.Attributes[3].Attribute.Should().Be(Attribute.CriticalStrikes);
build.Attributes[3].Points.Should().Be(11);
build.Skills.Count.Should().Be(8);
build.Skills[0].Should().Be(Skill.UnsuspectingStrike);
build.Skills[1].Should().Be(Skill.WildStrike);
@@ -1,8 +1,9 @@
namespace Daybreak.Configuration.FocusView;
public enum EnergyDisplay
public enum ExperienceDisplay
{
TotalCurretAndTotalMax,
CurrentLevelCurrentAndCurrentLevelMax,
RemainingUntilNextLevel
RemainingUntilNextLevel,
Percentage
}
@@ -4,8 +4,8 @@ namespace Daybreak.Configuration.FocusView;
public sealed class FocusViewOptions
{
[JsonProperty("EnergyDisplay")]
public EnergyDisplay EnergyDisplay { get; set; }
[JsonProperty("ExperienceDisplay")]
public ExperienceDisplay ExperienceDisplay { get; set; }
[JsonProperty("KurzickPointsDisplay")]
public PointsDisplay KurzickPointsDisplay { get; set; }
[JsonProperty("LuxonPointsDisplay")]
@@ -14,6 +14,12 @@ public sealed class FocusViewOptions
public PointsDisplay BalthazarPointsDisplay { get; set; }
[JsonProperty("ImperialPointsDisplay")]
public PointsDisplay ImperialPointsDisplay { get; set; }
[JsonProperty("VanquishingDisplay")]
public PointsDisplay VanquishingDisplay { get; set; }
[JsonProperty("HealthDisplay")]
public PointsDisplay HealthDisplay { get; set; }
[JsonProperty("EnergyDisplay")]
public PointsDisplay EnergyDisplay { get; set; }
[JsonProperty("BrowserUrl")]
public string? BrowserUrl { get; set; } = string.Empty;
}
@@ -3,5 +3,6 @@
public enum PointsDisplay
{
CurrentAndMax,
Remaining
Remaining,
Percentage
}
@@ -115,6 +115,7 @@ public static class ProjectConfiguration
services.AddScoped<IGuildwarsMemoryReader, GuildwarsMemoryReader>();
services.AddScoped<IMemoryScanner, MemoryScanner>();
services.AddScoped<IExperienceCalculator, ExperienceCalculator>();
services.AddScoped<IAttributePointCalculator, AttributePointCalculator>();
}
public static void RegisterViews(IViewProducer viewProducer)
+1
View File
@@ -6,6 +6,7 @@
xmlns:local="clr-namespace:Daybreak.Controls"
mc:Ignorable="d"
x:Name="_this"
Cursor="Hand"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<Grid>
@@ -6,6 +6,7 @@
xmlns:local="clr-namespace:Daybreak.Controls"
mc:Ignorable="d"
x:Name="_this"
Cursor="Hand"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<Grid>
+1
View File
@@ -6,6 +6,7 @@
xmlns:local="clr-namespace:Daybreak.Controls"
mc:Ignorable="d"
x:Name="_this"
Cursor="Hand"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<Grid>
@@ -6,6 +6,7 @@
xmlns:local="clr-namespace:Daybreak.Controls"
mc:Ignorable="d"
x:Name="_this"
Cursor="Hand"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<Grid>
@@ -6,6 +6,7 @@
xmlns:local="clr-namespace:Daybreak.Controls"
mc:Ignorable="d"
x:Name="_this"
Cursor="Hand"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Ellipse x:Name="BackgroundEllipse" Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" Visibility="Visible" Opacity="0.1" />
@@ -6,6 +6,7 @@
xmlns:local="clr-namespace:Daybreak.Controls"
mc:Ignorable="d"
x:Name="_this"
Cursor="Hand"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<Grid>
+2 -1
View File
@@ -4,8 +4,9 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Controls"
mc:Ignorable="d"
mc:Ignorable="d"
x:Name="_this"
Cursor="Hand"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<Grid>
@@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Controls"
x:Name="_this"
Cursor="Hand"
mc:Ignorable="d"
d:DesignHeight="50" d:DesignWidth="50">
<Grid>
@@ -8,6 +8,7 @@
xmlns:behaviors="clr-namespace:Daybreak.Behaviors"
xmlns:converters="clr-namespace:Daybreak.Converters"
x:Name="_this"
Cursor="Hand"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
@@ -6,6 +6,7 @@
xmlns:local="clr-namespace:Daybreak.Controls"
mc:Ignorable="d"
x:Name="_this"
Cursor="Hand"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<Grid>
+4 -4
View File
@@ -6,6 +6,7 @@
xmlns:local="clr-namespace:Daybreak.Controls"
mc:Ignorable="d"
x:Name="_this"
Cursor="Hand"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
@@ -20,13 +21,12 @@
FontStretch="{Binding ElementName=_this, Path=FontStretch, Mode=OneWay}"
FontWeight="{Binding ElementName=_this, Path=FontWeight, Mode=OneWay}"
FontStyle="{Binding ElementName=_this, Path=FontStyle, Mode=OneWay}"
VerticalAlignment="Center"
HorizontalAlignment="Center"></TextBlock>
VerticalAlignment="{Binding ElementName=_this, Path=TextVerticalAlignment, Mode=OneWay}"
HorizontalAlignment="{Binding ElementName=_this, Path=TextHorizontalAlignment, Mode=OneWay}"></TextBlock>
<Rectangle Fill="{Binding ElementName=_this, Path=Highlight, Mode=OneWay}"
Opacity="{Binding ElementName=_this, Path=HighlightOpacity, Mode=OneWay}"
Visibility="{Binding ElementName=_this, Path=HighlightVisible, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"></Rectangle>
<Rectangle MouseEnter="Rectangle_MouseEnter" MouseLeave="Rectangle_MouseLeave"
MouseLeftButtonDown="Rectangle_MouseLeftButtonDown" Fill="Transparent"
Cursor="Hand"></Rectangle>
MouseLeftButtonDown="Rectangle_MouseLeftButtonDown" Fill="Transparent"></Rectangle>
</Grid>
</UserControl>
@@ -26,6 +26,10 @@ public partial class OpaqueButton : UserControl
public double highlightOpacity;
[GenerateDependencyProperty]
public bool highlightVisible;
[GenerateDependencyProperty(InitialValue = VerticalAlignment.Center)]
public VerticalAlignment textVerticalAlignment;
[GenerateDependencyProperty(InitialValue = HorizontalAlignment.Center)]
public HorizontalAlignment textHorizontalAlignment;
public OpaqueButton()
{
@@ -5,6 +5,7 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Controls"
x:Name="_this"
Cursor="Hand"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
@@ -6,6 +6,7 @@
xmlns:local="clr-namespace:Daybreak.Controls"
mc:Ignorable="d"
x:Name="_this"
Cursor="Hand"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<Grid>
@@ -8,6 +8,7 @@
xmlns:behaviors="clr-namespace:Daybreak.Behaviors"
xmlns:converters="clr-namespace:Daybreak.Converters"
x:Name="_this"
Cursor="Hand"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
@@ -19,7 +19,7 @@
<ContextMenu.Template>
<ControlTemplate>
<StackPanel Margin="10">
<local:OpaqueButton Text="Load build template" Foreground="White" Background="#F0202020" BackgroundOpacity="0.4"
<local:OpaqueButton Text="Load build template" Foreground="White" Background="#F0212121" BackgroundOpacity="0.4"
Clicked="LoadBuildTemplateButton_Click" FontSize="16" Height="40" Width="200"></local:OpaqueButton>
</StackPanel>
</ControlTemplate>
@@ -33,7 +33,7 @@
</Grid.RowDefinitions>
<wv2:WebView2 x:Name="WebBrowser" Source="{Binding ElementName=_this, Path=Address, Mode=TwoWay}"
IsEnabled="{Binding ElementName=_this, Path=BrowserSupported, Mode=OneWay}"></wv2:WebView2>
<Grid Grid.Row="1" Background="#80808080" IsEnabled="{Binding ElementName=_this, Path=BrowserSupported, Mode=OneWay}"
<Grid Grid.Row="1" Background="#F0212121" IsEnabled="{Binding ElementName=_this, Path=BrowserSupported, Mode=OneWay}"
Visibility="{Binding ElementName=_this, Path=ControlsEnabled, Mode=OneWay, Converter={StaticResource ReverseBooleanToVisibilityConverter}}">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
@@ -1,4 +1,4 @@
<UserControl x:Class="Daybreak.Controls.ResourceBar"
<UserControl x:Class="Daybreak.Controls.HorizontalResourceBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
@@ -13,16 +13,16 @@
<Rectangle
Fill="{Binding ElementName=_this, Path=BarColor, Mode=OneWay}"
VerticalAlignment="Stretch"
HorizontalAlignment="Left"
HorizontalAlignment="{Binding ElementName=_this, Path=FillAlignment, Mode=OneWay}"
Width="{Binding ElementName=_this, Path=MaxResourceValue, Mode=TwoWay}"></Rectangle>
<Rectangle
Fill="#A0202020"
Fill="#F0202020"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"></Rectangle>
<Rectangle
Fill="{Binding ElementName=_this, Path=BarColor, Mode=OneWay}"
VerticalAlignment="Stretch"
HorizontalAlignment="Left"
HorizontalAlignment="{Binding ElementName=_this, Path=FillAlignment, Mode=OneWay}"
Width="{Binding ElementName=_this, Path=CurrentResourceValue, Mode=TwoWay}"></Rectangle>
</Grid>
</Viewbox>
@@ -0,0 +1,34 @@
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
using System.Windows.Media;
namespace Daybreak.Controls;
/// <summary>
/// Interaction logic for ResourceBar.xaml
/// </summary>
public partial class HorizontalResourceBar : UserControl
{
[GenerateDependencyProperty]
private double currentResourceValue;
[GenerateDependencyProperty]
private double maxResourceValue;
[GenerateDependencyProperty]
private Brush barColor;
[GenerateDependencyProperty]
private string text;
[GenerateDependencyProperty(InitialValue = HorizontalAlignment.Left)]
private HorizontalAlignment fillAlignment;
public HorizontalResourceBar()
{
this.InitializeComponent();
this.MaxResourceValue = 1;
this.CurrentResourceValue = 0;
this.barColor = Brushes.Transparent;
this.text = string.Empty;
}
}
+8
View File
@@ -12,6 +12,7 @@
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="GameCompanionButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:GoldenArrowGlyph Foreground="White"
@@ -22,6 +23,7 @@
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="ManageBuildsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:FireballGlyph Foreground="White"
@@ -32,6 +34,7 @@
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="AccountSettingsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:AvatarGlyph Foreground="White"
@@ -42,6 +45,7 @@
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="GuildwarsSettingsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:FileGlyph Foreground="White"
@@ -52,6 +56,7 @@
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="LauncherSettingsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:CogGlyph Foreground="White"
@@ -63,6 +68,7 @@
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="ExperimentalSettingsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:ExperimentGlyph Foreground="White"
@@ -73,6 +79,7 @@
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="VersionManagementButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:StaticGlyph Foreground="White"
@@ -83,6 +90,7 @@
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="LogsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:LogsGlyph Foreground="White"
@@ -1,5 +1,7 @@
using Daybreak.Models.Builds;
using Daybreak.Services.BuildTemplates;
using System;
using System.Core.Extensions;
using System.Extensions;
using System.Windows;
using System.Windows.Controls;
@@ -16,7 +18,9 @@ public partial class AttributeTemplate : UserControl
{
public event EventHandler<AttributeEntry>? HelpClicked;
public event EventHandler<AttributeEntry>? AttributeChanged;
private IAttributePointCalculator? attributePointCalculator;
[GenerateDependencyProperty(InitialValue = false)]
private bool canAdd;
[GenerateDependencyProperty(InitialValue = false)]
@@ -27,6 +31,23 @@ public partial class AttributeTemplate : UserControl
this.InitializeComponent();
this.DataContextChanged += this.AttributeTemplate_DataContextChanged;
}
public void InitializeAttributeTemplate(
int attributePoints,
IAttributePointCalculator attributePointCalculator)
{
this.attributePointCalculator = attributePointCalculator.ThrowIfNull();
var remainingPoints = attributePoints;
var requiredPointsForNextLevel = this.attributePointCalculator?.GetPointsRequiredToIncreaseRank(this.DataContext.As<AttributeEntry>().Points) ?? 0;
if (remainingPoints < requiredPointsForNextLevel)
{
this.CanAdd = false;
}
else if (this.DataContext.As<AttributeEntry>().Points < 12)
{
this.CanAdd = true;
}
}
private void AttributeTemplate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
+67 -48
View File
@@ -11,6 +11,9 @@
x:Name="_this"
Unloaded="BuildTemplate_Unloaded"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
</UserControl.Resources>
<Grid Background="Transparent" MouseLeftButtonDown="Grid_MouseLeftButtonDown">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
@@ -30,20 +33,15 @@
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Primary Profession: " FontSize="16" Foreground="White" Grid.Column="1"></TextBlock>
<ListView FontSize="16" Foreground="White" Grid.Column="2" BorderThickness="0"
Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=Professions, Mode=OneWay}"
SelectedItem="{Binding ElementName=_this, Path=PrimaryProfession, Mode=TwoWay}" Height="30"
SelectionMode="Single" ScrollViewer.VerticalScrollBarVisibility="Disabled"
PreviewMouseWheel="ListView_NavigateWithMouseWheel">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name, Mode=OneWay}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
<interactivity:Interaction.Behaviors>
<behaviors:ScrollIntoView></behaviors:ScrollIntoView>
</interactivity:Interaction.Behaviors>
</ListView>
<local:OpaqueButton Grid.Column="2"
Text="{Binding ElementName=_this, Path=BuildEntry.Primary.Name, Mode=OneWay}"
Foreground="White"
Highlight="White"
HighlightOpacity="0.4"
FontSize="16"
TextHorizontalAlignment="Left"
Margin="0, 0, 10, 0"
Clicked="PrimaryProfessionButton_Clicked"></local:OpaqueButton>
<local:HelpButton Grid.Column="0" Foreground="White" Width="20" Height="20" Margin="3"
Clicked="HelpButtonPrimary_Clicked" Cursor="Hand"></local:HelpButton>
</Grid>
@@ -54,29 +52,26 @@
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Secondary Profession: " FontSize="16" Foreground="White" Grid.Column="1"></TextBlock>
<ListView FontSize="16" Foreground="White" Grid.Column="2" BorderThickness="0"
Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=Professions, Mode=OneWay}"
SelectedItem="{Binding ElementName=_this, Path=SecondaryProfession, Mode=TwoWay}" Height="30"
SelectionMode="Single" ScrollViewer.VerticalScrollBarVisibility="Disabled"
PreviewMouseWheel="ListView_NavigateWithMouseWheel">
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock Text="{Binding Name, Mode=OneWay}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
<interactivity:Interaction.Behaviors>
<behaviors:ScrollIntoView></behaviors:ScrollIntoView>
</interactivity:Interaction.Behaviors>
</ListView>
<local:OpaqueButton Grid.Column="2"
Text="{Binding ElementName=_this, Path=BuildEntry.Secondary.Name, Mode=OneWay}"
Foreground="White"
Highlight="White"
HighlightOpacity="0.4"
FontSize="16"
TextHorizontalAlignment="Left"
Margin="0, 0, 10, 0"
Clicked="SecondaryProfessionButton_Clicked"></local:OpaqueButton>
<local:HelpButton Grid.Column="0" Foreground="White" Width="20" Height="20" Margin="3"
Clicked="HelpButtonSecondary_Clicked" Cursor="Hand"></local:HelpButton>
</Grid>
<Grid Grid.Row="3">
<ListBox Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=Attributes, Mode=OneWay}"
<ListBox Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=BuildEntry.Attributes, Mode=OneWay}"
HorizontalContentAlignment="Stretch" BorderThickness="0">
<ListBox.ItemTemplate>
<DataTemplate>
<local:AttributeTemplate HelpClicked="AttributeTemplate_HelpClicked" AttributeChanged="AttributeTemplate_AttributeChanged"></local:AttributeTemplate>
<local:AttributeTemplate HelpClicked="AttributeTemplate_HelpClicked"
AttributeChanged="AttributeTemplate_AttributeChanged"
Loaded="AttributeTemplate_Loaded"></local:AttributeTemplate>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
@@ -98,60 +93,60 @@
</Grid.RowDefinitions>
<local:SkillTemplate Grid.Column="0" x:Name="SkillTemplate0"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill0, Mode=TwoWay}"
FontSize="22" DataContext="{Binding ElementName=_this, Path=BuildEntry.FirstSkill, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
<local:SkillTemplate Grid.Column="1" x:Name="SkillTemplate1"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill1, Mode=TwoWay}"
FontSize="22" DataContext="{Binding ElementName=_this, Path=BuildEntry.SecondSkill, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
<local:SkillTemplate Grid.Column="2" x:Name="SkillTemplate2"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill2, Mode=TwoWay}"
FontSize="22" DataContext="{Binding ElementName=_this, Path=BuildEntry.ThirdSkill, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
<local:SkillTemplate Grid.Column="3" x:Name="SkillTemplate3"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill3, Mode=TwoWay}"
FontSize="22" DataContext="{Binding ElementName=_this, Path=BuildEntry.FourthSkill, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
<local:SkillTemplate Grid.Column="4" x:Name="SkillTemplate4"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill4, Mode=TwoWay}"
FontSize="22" DataContext="{Binding ElementName=_this, Path=BuildEntry.FifthSkill, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
<local:SkillTemplate Grid.Column="5" x:Name="SkillTemplate5"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill5, Mode=TwoWay}"
FontSize="22" DataContext="{Binding ElementName=_this, Path=BuildEntry.SixthSkill, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
<local:SkillTemplate Grid.Column="6" x:Name="SkillTemplate6"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill6, Mode=TwoWay}"
FontSize="22" DataContext="{Binding ElementName=_this, Path=BuildEntry.SeventhSkill, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
<local:SkillTemplate Grid.Column="7" x:Name="SkillTemplate7"
Foreground="White" Cursor="Hand"
FontSize="22" DataContext="{Binding ElementName=_this, Path=Skill7, Mode=TwoWay}"
FontSize="22" DataContext="{Binding ElementName=_this, Path=BuildEntry.EigthSkill, Mode=TwoWay}"
VerticalAlignment="Stretch" Clicked="SkillTemplate_Clicked"
RemoveClicked="SkillTemplate_RemoveClicked"></local:SkillTemplate>
<TextBlock Grid.Column="0" Grid.Row="1" TextWrapping="Wrap" FontSize="16"
Foreground="White" Text="{Binding ElementName=_this, Path=Skill0.Name, Mode=OneWay}"></TextBlock>
Foreground="White" Text="{Binding ElementName=_this, Path=BuildEntry.FirstSkill.Name, Mode=OneWay}"></TextBlock>
<TextBlock Grid.Column="1" Grid.Row="1" TextWrapping="Wrap" FontSize="16"
Foreground="White" Text="{Binding ElementName=_this, Path=Skill1.Name, Mode=OneWay}"></TextBlock>
Foreground="White" Text="{Binding ElementName=_this, Path=BuildEntry.SecondSkill.Name, Mode=OneWay}"></TextBlock>
<TextBlock Grid.Column="2" Grid.Row="1" TextWrapping="Wrap" FontSize="16"
Foreground="White" Text="{Binding ElementName=_this, Path=Skill2.Name, Mode=OneWay}"></TextBlock>
Foreground="White" Text="{Binding ElementName=_this, Path=BuildEntry.ThirdSkill.Name, Mode=OneWay}"></TextBlock>
<TextBlock Grid.Column="3" Grid.Row="1" TextWrapping="Wrap" FontSize="16"
Foreground="White" Text="{Binding ElementName=_this, Path=Skill3.Name, Mode=OneWay}"></TextBlock>
Foreground="White" Text="{Binding ElementName=_this, Path=BuildEntry.FourthSkill.Name, Mode=OneWay}"></TextBlock>
<TextBlock Grid.Column="4" Grid.Row="1" TextWrapping="Wrap" FontSize="16"
Foreground="White" Text="{Binding ElementName=_this, Path=Skill4.Name, Mode=OneWay}"></TextBlock>
Foreground="White" Text="{Binding ElementName=_this, Path=BuildEntry.FifthSkill.Name, Mode=OneWay}"></TextBlock>
<TextBlock Grid.Column="5" Grid.Row="1" TextWrapping="Wrap" FontSize="16"
Foreground="White" Text="{Binding ElementName=_this, Path=Skill5.Name, Mode=OneWay}"></TextBlock>
Foreground="White" Text="{Binding ElementName=_this, Path=BuildEntry.SixthSkill.Name, Mode=OneWay}"></TextBlock>
<TextBlock Grid.Column="6" Grid.Row="1" TextWrapping="Wrap" FontSize="16"
Foreground="White" Text="{Binding ElementName=_this, Path=Skill6.Name, Mode=OneWay}"></TextBlock>
Foreground="White" Text="{Binding ElementName=_this, Path=BuildEntry.SeventhSkill.Name, Mode=OneWay}"></TextBlock>
<TextBlock Grid.Column="7" Grid.Row="1" TextWrapping="Wrap" FontSize="16"
Foreground="White" Text="{Binding ElementName=_this, Path=Skill7.Name, Mode=OneWay}"></TextBlock>
Foreground="White" Text="{Binding ElementName=_this, Path=BuildEntry.EigthSkill.Name, Mode=OneWay}"></TextBlock>
</Grid>
<Grid Grid.Column="1" Grid.RowSpan="6">
<local:ChromiumBrowserWrapper x:Name="SkillBrowser" ControlsEnabled="False" Width="0" AddressBarReadonly="True" CanNavigate="True"></local:ChromiumBrowserWrapper>
@@ -170,10 +165,11 @@
<ListView x:Name="SkillsListView"
Grid.Row="1"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Hidden"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Background="Transparent"
ScrollViewer.CanContentScroll="False"
ItemsSource="{Binding ElementName=_this, Path=AvailableSkills, Mode=OneWay}"
MouseDoubleClick="ListView_MouseDoubleClick">
MouseDoubleClick="SkillListView_MouseDoubleClick">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel>
@@ -188,5 +184,28 @@
</ListView>
</Grid>
</Grid>
<Grid Grid.Column="1" Grid.RowSpan="6">
<ListView x:Name="ProfessionListView"
Width="0"
HorizontalAlignment="Right"
ScrollViewer.HorizontalScrollBarVisibility="Disabled"
ScrollViewer.VerticalScrollBarVisibility="Auto"
Background="Transparent"
ScrollViewer.CanContentScroll="False"
ItemsSource="{Binding ElementName=_this, Path=Professions, Mode=OneWay}"
MouseDoubleClick="ProfessionListView_MouseDoubleClick">
<ListView.ItemsPanel>
<ItemsPanelTemplate>
<VirtualizingStackPanel>
</VirtualizingStackPanel>
</ItemsPanelTemplate>
</ListView.ItemsPanel>
<ListView.ItemTemplate>
<DataTemplate>
<TextBlock FontSize="16" Foreground="White" TextWrapping="Wrap" Text="{Binding Name}"></TextBlock>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
</Grid>
</UserControl>
+220 -255
View File
@@ -8,6 +8,7 @@ using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.ComponentModel;
using System.Configuration;
using System.Core.Extensions;
using System.Extensions;
@@ -30,11 +31,13 @@ public partial class BuildTemplate : UserControl
private const string InfoNamePlaceholder = "[NAME]";
private const string BaseAddress = $"https://wiki.guildwars.com/wiki/{InfoNamePlaceholder}";
private bool suppressBuildChanged = false;
private bool loadedProperties = false;
private bool showingSkillList = false;
private bool replacingSecondaryProfession;
private bool replacingPrimaryProfession;
private IIconBrowser? iconBrowser;
private BuildEntry? loadedBuild;
private IAttributePointCalculator? attributePointCalculator;
private SkillTemplate? selectingSkillTemplate;
private List<Skill>? skillListCache;
private CancellationTokenSource? cancellationTokenSource = new();
public event EventHandler? BuildChanged;
@@ -42,25 +45,10 @@ public partial class BuildTemplate : UserControl
[GenerateDependencyProperty]
private string skillSearchText = string.Empty;
[GenerateDependencyProperty]
private Profession primaryProfession = default!;
private BuildEntry buildEntry;
[GenerateDependencyProperty]
private Profession secondaryProfession = default!;
[GenerateDependencyProperty]
private Skill skill0 = default!;
[GenerateDependencyProperty]
private Skill skill1 = default!;
[GenerateDependencyProperty]
private Skill skill2 = default!;
[GenerateDependencyProperty]
private Skill skill3 = default!;
[GenerateDependencyProperty]
private Skill skill4 = default!;
[GenerateDependencyProperty]
private Skill skill5 = default!;
[GenerateDependencyProperty]
private Skill skill6 = default!;
[GenerateDependencyProperty]
private Skill skill7 = default!;
private int attributePoints;
public ObservableCollection<Skill> AvailableSkills { get; } = new ObservableCollection<Skill>();
public ObservableCollection<AttributeEntry> Attributes { get; } = new ObservableCollection<AttributeEntry>();
public ObservableCollection<Profession> Professions { get; } = new ObservableCollection<Profession>(Profession.Professions);
@@ -68,17 +56,19 @@ public partial class BuildTemplate : UserControl
public BuildTemplate()
{
this.InitializeComponent();
this.InitializeProperties();
this.buildEntry = new BuildEntry();
this.DataContextChanged += this.BuildTemplate_DataContextChanged;
}
public async void InitializeTemplate(
IAttributePointCalculator attributePointCalculator,
IIconCache iconRetriever,
IIconBrowser iconBrowser,
ILiveOptions<ApplicationConfiguration> liveOptions,
IBuildTemplateManager buildTemplateManager,
ILogger<ChromiumBrowserWrapper> logger)
{
this.attributePointCalculator = attributePointCalculator.ThrowIfNull();
this.iconBrowser = iconBrowser.ThrowIfNull();
await this.SkillBrowser.InitializeDefaultBrowser(liveOptions, buildTemplateManager, logger);
this.SkillTemplate0.InitializeSkillTemplate(iconRetriever);
@@ -94,200 +84,35 @@ public partial class BuildTemplate : UserControl
this.HideInfoBrowser();
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (this.loadedProperties is false)
{
return;
}
if (e.Property == PrimaryProfessionProperty || e.Property == SecondaryProfessionProperty)
{
if (e.Property == PrimaryProfessionProperty)
{
this.loadedBuild!.Build!.Primary = this.PrimaryProfession;
}
else
{
this.loadedBuild!.Build!.Secondary = this.SecondaryProfession;
}
this.LoadSkills();
this.LoadAttributes();
if (this.suppressBuildChanged is false)
{
this.BuildChanged?.Invoke(this, new EventArgs());
}
}
if (e.Property == Skill0Property ||
e.Property == Skill1Property ||
e.Property == Skill2Property ||
e.Property == Skill3Property ||
e.Property == Skill4Property ||
e.Property == Skill5Property ||
e.Property == Skill6Property ||
e.Property == Skill7Property)
{
if (this.suppressBuildChanged is false)
{
this.loadedBuild!.Build!.Skills[0] = this.Skill0;
this.loadedBuild!.Build!.Skills[1] = this.Skill1;
this.loadedBuild!.Build!.Skills[2] = this.Skill2;
this.loadedBuild!.Build!.Skills[3] = this.Skill3;
this.loadedBuild!.Build!.Skills[4] = this.Skill4;
this.loadedBuild!.Build!.Skills[5] = this.Skill5;
this.loadedBuild!.Build!.Skills[6] = this.Skill6;
this.loadedBuild!.Build!.Skills[7] = this.Skill7;
this.BuildChanged?.Invoke(this, new EventArgs());
}
}
}
private void BuildTemplate_Unloaded(object sender, RoutedEventArgs e)
{
this.cancellationTokenSource?.Cancel();
}
private void InitializeProperties()
{
this.PrimaryProfession = Profession.None;
this.SecondaryProfession = Profession.None;
this.Skill0 = Skill.NoSkill;
this.Skill1 = Skill.NoSkill;
this.Skill2 = Skill.NoSkill;
this.Skill3 = Skill.NoSkill;
this.Skill4 = Skill.NoSkill;
this.Skill5 = Skill.NoSkill;
this.Skill6 = Skill.NoSkill;
this.Skill7 = Skill.NoSkill;
this.loadedProperties = true;
}
private void BuildTemplate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if(e.NewValue is BuildEntry)
if(e.NewValue is BuildEntry buildEntry)
{
this.LoadBuild();
this.LoadSkills();
this.LoadAttributes();
}
}
private void Grid_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
this.HideSkillListView();
this.HideInfoBrowser();
}
private void LoadAttributes()
{
var possibleAttributes = new List<AttributeEntry>();
if (this.PrimaryProfession != Profession.None)
{
possibleAttributes.Add(new AttributeEntry { Attribute = this.PrimaryProfession.PrimaryAttribute });
possibleAttributes.AddRange(this.PrimaryProfession.Attributes!.Select(a => new AttributeEntry { Attribute = a }));
}
if (this.SecondaryProfession != Profession.None && this.SecondaryProfession != this.PrimaryProfession)
{
possibleAttributes.AddRange(this.SecondaryProfession.Attributes!.Select(a => new AttributeEntry { Attribute = a }));
}
this.Attributes.ClearAnd().AddRange(possibleAttributes.Select(entry =>
{
var maybePresentAttribute = this.loadedBuild!.Build!.Attributes.Where(buildEntry => entry.Attribute == buildEntry.Attribute).FirstOrDefault();
if (maybePresentAttribute is null)
if (this.BuildEntry is not null)
{
return entry;
this.BuildEntry.PropertyChanged -= this.BuildEntry_Changed;
}
this.BuildEntry = buildEntry;
this.BuildEntry.PropertyChanged += this.BuildEntry_Changed;
this.AttributePoints = this.attributePointCalculator!.GetRemainingFreePoints(this.BuildEntry.Build!);
}
}
entry.Points = maybePresentAttribute.Points;
return entry;
}));
this.loadedBuild!.Build!.Attributes = this.Attributes.ToList();
private void BuildEntry_Changed(object? sender, PropertyChangedEventArgs propertyChangedEventArgs)
{
this.LoadSkills();
}
private async void LoadSkills()
{
if (this.Skill0.Profession != this.PrimaryProfession &&
this.Skill0.Profession != this.SecondaryProfession &&
this.Skill0.Profession != Profession.None)
{
this.Skill0 = Skill.NoSkill;
}
if (this.Skill1.Profession != this.PrimaryProfession &&
this.Skill1.Profession != this.SecondaryProfession &&
this.Skill1.Profession != Profession.None)
{
this.Skill1 = Skill.NoSkill;
}
if (this.Skill2.Profession != this.PrimaryProfession &&
this.Skill2.Profession != this.SecondaryProfession &&
this.Skill2.Profession != Profession.None)
{
this.Skill2 = Skill.NoSkill;
}
if (this.Skill3.Profession != this.PrimaryProfession &&
this.Skill3.Profession != this.SecondaryProfession &&
this.Skill3.Profession != Profession.None)
{
this.Skill3 = Skill.NoSkill;
}
if (this.Skill4.Profession != this.PrimaryProfession &&
this.Skill4.Profession != this.SecondaryProfession &&
this.Skill4.Profession != Profession.None)
{
this.Skill4 = Skill.NoSkill;
}
if (this.Skill5.Profession != this.PrimaryProfession &&
this.Skill5.Profession != this.SecondaryProfession &&
this.Skill5.Profession != Profession.None)
{
this.Skill5 = Skill.NoSkill;
}
if (this.Skill6.Profession != this.PrimaryProfession &&
this.Skill6.Profession != this.SecondaryProfession &&
this.Skill6.Profession != Profession.None)
{
this.Skill6 = Skill.NoSkill;
}
if (this.Skill7.Profession != this.PrimaryProfession &&
this.Skill7.Profession != this.SecondaryProfession &&
this.Skill7.Profession != Profession.None)
{
this.Skill7 = Skill.NoSkill;
}
var filteredSkills = await this.FilterSkills(this.SkillSearchText).ToListAsync().ConfigureAwait(true);
this.AvailableSkills.ClearAnd().AddRange(filteredSkills);
}
private void LoadBuild()
{
this.suppressBuildChanged = true;
var build = this.DataContext.As<BuildEntry>();
this.loadedBuild = build;
this.PrimaryProfession = build.Build!.Primary;
this.SecondaryProfession = build.Build.Secondary;
this.Skill0 = build.Build.Skills[0];
this.Skill1 = build.Build.Skills[1];
this.Skill2 = build.Build.Skills[2];
this.Skill3 = build.Build.Skills[3];
this.Skill4 = build.Build.Skills[4];
this.Skill5 = build.Build.Skills[5];
this.Skill6 = build.Build.Skills[6];
this.Skill7 = build.Build.Skills[7];
this.suppressBuildChanged = false;
this.PrepareSkillListCache(filteredSkills);
}
private void BrowseToInfo(string infoName)
@@ -301,8 +126,9 @@ public partial class BuildTemplate : UserControl
{
if (this.SkillBrowser.BrowserSupported is true)
{
this.HideSkillListView();
this.HideProfessionListView();
this.SkillBrowser.Width = 400;
this.SkillListContainer.Width = 0;
}
}
@@ -316,23 +142,103 @@ public partial class BuildTemplate : UserControl
private void ShowSkillListView()
{
this.SkillBrowser.Width = 0;
this.HideInfoBrowser();
this.HideProfessionListView();
this.SkillListContainer.Width = 400;
}
private void HideSkillListView()
{
this.SkillListContainer.Width = 0;
}
private void HelpButtonPrimary_Clicked(object sender, System.EventArgs e)
{
if (this.PrimaryProfession == Profession.None)
this.SkillListContainer.Visibility = Visibility.Visible;
this.showingSkillList = true;
if (this.skillListCache?.Except(this.AvailableSkills).None() is true &&
this.skillListCache.Count == this.AvailableSkills.Count)
{
return;
}
this.BrowseToInfo(this.PrimaryProfession.Name!);
this.AvailableSkills.ClearAnd().AddRange(this.skillListCache);
}
private void HideSkillListView()
{
this.SkillListContainer.Visibility = Visibility.Hidden;
this.SkillListContainer.Width = 0;
this.showingSkillList = false;
}
private void ShowProfessionListView()
{
this.HideInfoBrowser();
this.HideSkillListView();
this.ProfessionListView.Width = 400;
this.ProfessionListView.Visibility = Visibility.Visible;
}
private void HideProfessionListView()
{
this.ProfessionListView.Visibility = Visibility.Hidden;
this.ProfessionListView.Width = 0;
}
private void PrepareSkillListCache(List<Skill> skills)
{
/*
* To improve application performance, only load the skill list when it is showing.
* Otherwise, defer the loading to when the skill list will show.
*/
this.skillListCache = skills;
if (this.showingSkillList)
{
this.ShowSkillListView();
}
}
private async IAsyncEnumerable<Skill> FilterSkills(string searchTerm)
{
// Replace symbols to ease search
searchTerm = searchTerm?.Replace("\"", "").Replace("!", "")!;
foreach (var skill in Skill.Skills)
{
if (skill == Skill.NoSkill)
{
continue;
}
if (skill.Profession != this.BuildEntry!.Primary &&
skill.Profession != this.BuildEntry!.Secondary &&
skill.Profession != Profession.None)
{
continue;
}
if (searchTerm.IsNullOrWhiteSpace())
{
yield return skill;
continue;
}
var matchesName = await Task.Run(() => StringUtils.MatchesSearchString(skill.Name!.Replace("\"", "").Replace("!", ""), searchTerm!));
if (matchesName)
{
yield return skill;
continue;
}
}
}
private void Grid_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
this.HideSkillListView();
this.HideInfoBrowser();
}
private void HelpButtonPrimary_Clicked(object sender, System.EventArgs e)
{
if (this.BuildEntry!.Primary == Profession.None)
{
return;
}
this.BrowseToInfo(this.BuildEntry.Primary.Name!);
if (e is RoutedEventArgs routedEventArgs)
{
routedEventArgs.Handled = true;
@@ -341,12 +247,12 @@ public partial class BuildTemplate : UserControl
private void HelpButtonSecondary_Clicked(object sender, System.EventArgs e)
{
if (this.SecondaryProfession == Profession.None)
if (this.BuildEntry!.Secondary == Profession.None)
{
return;
}
this.BrowseToInfo(this.SecondaryProfession.Name!);
this.BrowseToInfo(this.BuildEntry.Secondary.Name!);
if (e is RoutedEventArgs routedEventArgs)
{
routedEventArgs.Handled = true;
@@ -362,6 +268,7 @@ public partial class BuildTemplate : UserControl
{
e.ThrowIfNull();
this.BuildChanged?.Invoke(this, new EventArgs());
this.AttributePoints = this.attributePointCalculator!.GetRemainingFreePoints(this.BuildEntry.Build!);
}
private void SkillTemplate_Clicked(object sender, RoutedEventArgs e)
@@ -389,10 +296,41 @@ public partial class BuildTemplate : UserControl
private void SkillTemplate_RemoveClicked(object sender, System.EventArgs e)
{
sender.As<SkillTemplate>().DataContext = Skill.NoSkill;
if (sender == this.SkillTemplate0)
{
this.BuildEntry.FirstSkill = Skill.NoSkill;
}
else if (sender == this.SkillTemplate1)
{
this.BuildEntry.SecondSkill = Skill.NoSkill;
}
else if (sender == this.SkillTemplate2)
{
this.BuildEntry.ThirdSkill = Skill.NoSkill;
}
else if (sender == this.SkillTemplate3)
{
this.BuildEntry.FourthSkill = Skill.NoSkill;
}
else if (sender == this.SkillTemplate4)
{
this.BuildEntry.FifthSkill = Skill.NoSkill;
}
else if (sender == this.SkillTemplate5)
{
this.BuildEntry.SixthSkill = Skill.NoSkill;
}
else if (sender == this.SkillTemplate6)
{
this.BuildEntry.SeventhSkill = Skill.NoSkill;
}
else if (sender == this.SkillTemplate7)
{
this.BuildEntry.EigthSkill = Skill.NoSkill;
}
}
private void ListView_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
private void SkillListView_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (this.selectingSkillTemplate is null)
{
@@ -400,16 +338,56 @@ public partial class BuildTemplate : UserControl
return;
}
this.selectingSkillTemplate.DataContext = sender.As<ListView>().SelectedItem;
var selectedSkilll = sender.As<ListView>().SelectedItem.As<Skill>();
if (this.selectingSkillTemplate == this.SkillTemplate0)
{
this.BuildEntry.FirstSkill = selectedSkilll;
}
else if (this.selectingSkillTemplate == this.SkillTemplate1)
{
this.BuildEntry.SecondSkill = selectedSkilll;
}
else if (this.selectingSkillTemplate == this.SkillTemplate2)
{
this.BuildEntry.ThirdSkill = selectedSkilll;
}
else if (this.selectingSkillTemplate == this.SkillTemplate3)
{
this.BuildEntry.FourthSkill = selectedSkilll;
}
else if (this.selectingSkillTemplate == this.SkillTemplate4)
{
this.BuildEntry.FifthSkill = selectedSkilll;
}
else if (this.selectingSkillTemplate == this.SkillTemplate5)
{
this.BuildEntry.SixthSkill = selectedSkilll;
}
else if (this.selectingSkillTemplate == this.SkillTemplate6)
{
this.BuildEntry.SeventhSkill = selectedSkilll;
}
else if (this.selectingSkillTemplate == this.SkillTemplate7)
{
this.BuildEntry.EigthSkill = selectedSkilll;
}
this.HideSkillListView();
this.loadedBuild!.Build!.Skills[0] = this.Skill0;
this.loadedBuild!.Build!.Skills[1] = this.Skill1;
this.loadedBuild!.Build!.Skills[2] = this.Skill2;
this.loadedBuild!.Build!.Skills[3] = this.Skill3;
this.loadedBuild!.Build!.Skills[4] = this.Skill4;
this.loadedBuild!.Build!.Skills[5] = this.Skill5;
this.loadedBuild!.Build!.Skills[6] = this.Skill6;
this.loadedBuild!.Build!.Skills[7] = this.Skill7;
}
private void ProfessionListView_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
var selected = sender.As<ListView>().SelectedItem.As<Profession>();
if (this.replacingPrimaryProfession)
{
this.BuildEntry!.Primary = selected;
}
else if (this.replacingSecondaryProfession)
{
this.BuildEntry!.Secondary = selected;
}
this.HideProfessionListView();
}
private void ListView_NavigateWithMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
@@ -440,37 +418,24 @@ public partial class BuildTemplate : UserControl
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta);
}
private async IAsyncEnumerable<Skill> FilterSkills(string searchTerm)
private void SecondaryProfessionButton_Clicked(object sender, EventArgs e)
{
// Replace symbols to ease search
searchTerm = searchTerm?.Replace("\"", "").Replace("!", "")!;
foreach (var skill in Skill.Skills)
{
if (skill == Skill.NoSkill)
{
continue;
}
this.replacingPrimaryProfession = false;
this.replacingSecondaryProfession = true;
this.Professions.ClearAnd().AddRange(Profession.Professions.Where(p => p != this.BuildEntry?.Secondary));
this.ShowProfessionListView();
}
if (skill.Profession != this.PrimaryProfession &&
skill.Profession != this.SecondaryProfession &&
skill.Profession != Profession.None)
{
continue;
}
private void PrimaryProfessionButton_Clicked(object sender, EventArgs e)
{
this.replacingPrimaryProfession = true;
this.replacingSecondaryProfession = false;
this.Professions.ClearAnd().AddRange(Profession.Professions.Where(p => p != this.BuildEntry?.Primary));
this.ShowProfessionListView();
}
if (searchTerm.IsNullOrWhiteSpace())
{
yield return skill;
continue;
}
var matchesName = await Task.Run(() => StringUtils.MatchesSearchString(skill.Name!.Replace("\"", "").Replace("!", ""), searchTerm!));
if (matchesName)
{
yield return skill;
continue;
}
}
private void AttributeTemplate_Loaded(object sender, RoutedEventArgs e)
{
sender.As<AttributeTemplate>().InitializeAttributeTemplate(this.AttributePoints, this.attributePointCalculator!);
}
}
@@ -7,6 +7,7 @@ using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Extensions;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Shapes;
namespace Daybreak.Controls.Templates;
@@ -60,36 +61,22 @@ public partial class QuestLogTemplate : UserControl
foreach(var grouping in this.questLogCache)
{
var location = grouping.Key;
var locationTextBlock = new TextBlock
var locationTextBlock = new OpaqueButton
{
Text = grouping.Key?.Name ?? UncategorizedQuestsString,
FontSize = 18,
Margin = new Thickness(5),
Cursor = grouping.Key is null ? Cursors.Arrow : Cursors.Hand,
HorizontalAlignment = HorizontalAlignment.Left
HorizontalAlignment = HorizontalAlignment.Stretch,
TextHorizontalAlignment = HorizontalAlignment.Left,
Highlight = Brushes.White,
HighlightOpacity = grouping.Key is null ? 0 : 0.6
};
locationTextBlock.MouseLeftButtonDown += (_, _) => this.OnMapClicked(location);
this.ItemStackPanel.Children.Add(locationTextBlock);
foreach(var questMetadata in grouping)
{
var quest = questMetadata.Quest;
var questTextBlock = new TextBlock
{
Text = quest?.Name,
FontSize = 20,
Cursor = Cursors.Hand,
HorizontalAlignment = HorizontalAlignment.Left
};
questTextBlock.MouseLeftButtonDown += (_, _) => this.OnQuestClicked(quest);
this.ItemStackPanel.Children.Add(questTextBlock);
}
var rectangle = new Rectangle
{
Height = 1,
Margin = new Thickness(0, 0, 0, 10)
};
var rectangleForegroundBinding = new Binding("Foreground")
@@ -99,6 +86,30 @@ public partial class QuestLogTemplate : UserControl
rectangle.SetBinding(Rectangle.FillProperty, rectangleForegroundBinding);
this.ItemStackPanel.Children.Add(rectangle);
foreach (var questMetadata in grouping)
{
var quest = questMetadata.Quest;
var questTextBlock = new OpaqueButton
{
Text = quest?.Name,
FontSize = 20,
Cursor = Cursors.Hand,
Highlight = Brushes.White,
HighlightOpacity = 0.6,
HorizontalAlignment = HorizontalAlignment.Stretch,
TextHorizontalAlignment = HorizontalAlignment.Left
};
questTextBlock.MouseLeftButtonDown += (_, _) => this.OnQuestClicked(quest);
this.ItemStackPanel.Children.Add(questTextBlock);
}
this.ItemStackPanel.Children.Add(new Rectangle
{
Fill = Brushes.Transparent,
Height = 10,
HorizontalAlignment = HorizontalAlignment.Stretch
});
}
}
@@ -0,0 +1,37 @@
<UserControl x:Class="Daybreak.Controls.VerticalResourceBar"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Controls"
mc:Ignorable="d"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<Grid>
<Viewbox VerticalAlignment="Stretch" HorizontalAlignment="Stretch" Stretch="Fill">
<Grid Width="1">
<Rectangle
Fill="{Binding ElementName=_this, Path=BarColor, Mode=OneWay}"
VerticalAlignment="{Binding ElementName=_this, Path=FillAlignment, Mode=OneWay}"
HorizontalAlignment="Stretch"
Height="{Binding ElementName=_this, Path=MaxResourceValue, Mode=TwoWay}"></Rectangle>
<Rectangle
Fill="#F0212121"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"></Rectangle>
<Rectangle
Fill="{Binding ElementName=_this, Path=BarColor, Mode=OneWay}"
VerticalAlignment="{Binding ElementName=_this, Path=FillAlignment, Mode=OneWay}"
HorizontalAlignment="Stretch"
Height="{Binding ElementName=_this, Path=CurrentResourceValue, Mode=TwoWay}"></Rectangle>
</Grid>
</Viewbox>
<TextBlock
FontSize="{Binding ElementName=_this, Path=FontSize, Mode=OneWay}"
FontFamily="{Binding ElementName=_this, Path=FontFamily, Mode=OneWay}"
Text="{Binding ElementName=_this, Path=Text, Mode=OneWay}"
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
VerticalAlignment="Center"
HorizontalAlignment="Center"></TextBlock>
</Grid>
</UserControl>
@@ -8,7 +8,7 @@ namespace Daybreak.Controls;
/// <summary>
/// Interaction logic for ResourceBar.xaml
/// </summary>
public partial class ResourceBar : UserControl
public partial class VerticalResourceBar : UserControl
{
[GenerateDependencyProperty]
private double currentResourceValue;
@@ -18,8 +18,10 @@ public partial class ResourceBar : UserControl
private Brush barColor;
[GenerateDependencyProperty]
private string text;
[GenerateDependencyProperty(InitialValue = VerticalAlignment.Bottom)]
private VerticalAlignment fillAlignment;
public ResourceBar()
public VerticalResourceBar()
{
this.InitializeComponent();
this.MaxResourceValue = 1;
+4 -1
View File
@@ -13,7 +13,7 @@
<LangVersion>preview</LangVersion>
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
<Version>0.9.8.2</Version>
<Version>0.9.8.7</Version>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UserSecretsId>cfb2a489-db80-448d-a969-80270f314c46</UserSecretsId>
</PropertyGroup>
@@ -72,6 +72,9 @@
<Compile Update="Controls\Buttons\CancelButton.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Controls\VerticalResourceBar.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\IconDownloadView.xaml.cs">
<SubType>Code</SubType>
</Compile>
+8 -5
View File
@@ -23,7 +23,7 @@
<Setter Property="Template">
<Setter.Value>
<ControlTemplate TargetType="{x:Type ToggleButton}">
<Grid>
<Grid Cursor="Hand">
<Rectangle x:Name="OverlayRect" Fill="{TemplateBinding Background}" Opacity="0"></Rectangle>
<controls:ThreeDotsGlyph Margin="13"></controls:ThreeDotsGlyph>
</Grid>
@@ -33,7 +33,7 @@
<Condition Property="IsMouseOver" Value="True"></Condition>
</MultiTrigger.Conditions>
<MultiTrigger.Setters>
<Setter Property="Opacity" TargetName="OverlayRect" Value="0.5"></Setter>
<Setter Property="Opacity" TargetName="OverlayRect" Value="1"></Setter>
</MultiTrigger.Setters>
</MultiTrigger>
<MultiTrigger>
@@ -118,7 +118,7 @@
Grid.RowSpan="2"
VerticalAlignment="Stretch"
HorizontalAlignment="Left"
Background="#A0202020">
Background="#F0212121">
<controls:MenuList Margin="0, 30, 0, 0"/>
</Grid>
<ToggleButton x:Name="OpeningSettingsButton"
@@ -126,6 +126,7 @@
Width="50"
Height="30"
HorizontalAlignment="Left"
Background="#F0212121"
Margin="0, 0, 0, 0"
Grid.ColumnSpan="2"
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
@@ -138,6 +139,7 @@
Margin="0, 0, 0, 0"
Grid.ColumnSpan="2"
Foreground="White"
Background="#F09E9E9E"
Visibility="{Binding ElementName=_this, Path=IsShowingDropdown, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
Click="SettingsButton_Clicked"></ToggleButton>
<wcl:Border OnResize="Border_OnResize" Grid.RowSpan="2" Grid.ColumnSpan="2" Active="True"></wcl:Border>
@@ -146,8 +148,9 @@
HorizontalAlignment="Center"
Visibility="Visible"
ControlsEnabled="False"
CanNavigate="False"
CanNavigate="True"
CanDownloadBuild="False"
Grid.Row="1"></controls:ChromiumBrowserWrapper>
Grid.Row="1"
Grid.Column="1"></controls:ChromiumBrowserWrapper>
</Grid>
</Window>
+170
View File
@@ -1,5 +1,9 @@
using Daybreak.Models.Guildwars;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Extensions;
using System.Linq;
namespace Daybreak.Models.Builds;
@@ -28,4 +32,170 @@ public sealed class BuildEntry : INotifyPropertyChanged
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Build)));
}
}
public Profession Primary
{
get => this.Build!.Primary;
set
{
this.Build!.Primary = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Primary)));
this.UpdateAttributes();
}
}
public Profession Secondary
{
get => this.Build!.Secondary;
set
{
this.Build!.Secondary = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Secondary)));
this.UpdateAttributes();
}
}
public List<AttributeEntry> Attributes
{
get => this.Build!.Attributes;
set
{
this.Build!.Attributes = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Attributes)));
this.UpdateSkills();
}
}
public Skill FirstSkill
{
get => this.Build!.Skills[0];
set
{
this.Build!.Skills[0] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.FirstSkill)));
}
}
public Skill SecondSkill
{
get => this.Build!.Skills[1];
set
{
this.Build!.Skills[1] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.SecondSkill)));
}
}
public Skill ThirdSkill
{
get => this.Build!.Skills[2];
set
{
this.Build!.Skills[2] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.ThirdSkill)));
}
}
public Skill FourthSkill
{
get => this.Build!.Skills[3];
set
{
this.Build!.Skills[3] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.FourthSkill)));
}
}
public Skill FifthSkill
{
get => this.Build!.Skills[4];
set
{
this.Build!.Skills[4] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.FifthSkill)));
}
}
public Skill SixthSkill
{
get => this.Build!.Skills[5];
set
{
this.Build!.Skills[5] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.SixthSkill)));
}
}
public Skill SeventhSkill
{
get => this.Build!.Skills[6];
set
{
this.Build!.Skills[6] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.SeventhSkill)));
}
}
public Skill EigthSkill
{
get => this.Build!.Skills[7];
set
{
this.Build!.Skills[7] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.EigthSkill)));
}
}
private void UpdateAttributes()
{
var attributesToAdd = new List<Attribute>();
if (this.Primary.PrimaryAttribute is not null)
{
attributesToAdd.Add(this.Primary.PrimaryAttribute);
}
attributesToAdd.AddRange(this.Primary.Attributes);
attributesToAdd.AddRange(this.Secondary.Attributes);
this.Attributes = attributesToAdd.Distinct().Select(attribute =>
{
if (this.Attributes.FirstOrDefault(attributeEntry => attributeEntry.Attribute == attribute) is AttributeEntry attributeEntry)
{
return attributeEntry;
}
return new AttributeEntry { Attribute = attribute };
}).ToList();
}
private void UpdateSkills()
{
if (this.FirstSkill.Profession != this.Primary && this.FirstSkill.Profession != this.Secondary)
{
this.FirstSkill = Skill.NoSkill;
}
if (this.SecondSkill.Profession != this.Primary && this.SecondSkill.Profession != this.Secondary)
{
this.SecondSkill = Skill.NoSkill;
}
if (this.ThirdSkill.Profession != this.Primary && this.ThirdSkill.Profession != this.Secondary)
{
this.ThirdSkill = Skill.NoSkill;
}
if (this.FourthSkill.Profession != this.Primary && this.FourthSkill.Profession != this.Secondary)
{
this.FourthSkill = Skill.NoSkill;
}
if (this.FifthSkill.Profession != this.Primary && this.FifthSkill.Profession != this.Secondary)
{
this.FifthSkill = Skill.NoSkill;
}
if (this.SixthSkill.Profession != this.Primary && this.SixthSkill.Profession != this.Secondary)
{
this.SixthSkill = Skill.NoSkill;
}
if (this.SeventhSkill.Profession != this.Primary && this.SeventhSkill.Profession != this.Secondary)
{
this.SeventhSkill = Skill.NoSkill;
}
if (this.EigthSkill.Profession != this.Primary && this.EigthSkill.Profession != this.Secondary)
{
this.EigthSkill = Skill.NoSkill;
}
}
}
+43 -42
View File
@@ -6,48 +6,48 @@ namespace Daybreak.Models.Guildwars;
public sealed class Attribute
{
public static Attribute FastCasting { get; } = new() { Name = "Fast Casting", Id = 0 };
public static Attribute IllusionMagic { get; } = new() { Name = "Illusion Magic", Id = 1 };
public static Attribute DominationMagic { get; } = new() { Name = "Domination Magic", Id = 2 };
public static Attribute InspirationMagic { get; } = new() { Name = "Inspiration Magic", Id = 3 };
public static Attribute BloodMagic { get; } = new() { Name = "Blood Magic", Id = 4 };
public static Attribute DeathMagic { get; } = new() { Name = "Death Magic", Id = 5 };
public static Attribute SoulReaping { get; } = new() { Name = "Soul Reaping", Id = 6 };
public static Attribute Curses { get; } = new() { Name = "Curses", Id = 7 };
public static Attribute AirMagic { get; } = new() { Name = "Air Magic", Id = 8 };
public static Attribute EarthMagic { get; } = new() { Name = "Earth Magic", Id = 9 };
public static Attribute FireMagic { get; } = new() { Name = "Fire Magic", Id = 10 };
public static Attribute WaterMagic { get; } = new() { Name = "Water Magic", Id = 11 };
public static Attribute EnergyStorage { get; } = new() { Name = "Energy Storage", Id = 12 };
public static Attribute HealingPrayers { get; } = new() { Name = "Healing Prayers", Id = 13 };
public static Attribute SmitingPrayers { get; } = new() { Name = "Smiting Prayers", Id = 14 };
public static Attribute ProtectionPrayers { get; } = new() { Name = "Protection Prayers", Id = 15 };
public static Attribute DivineFavor { get; } = new() { Name = "Divine Favor", Id = 16 };
public static Attribute Strength { get; } = new() { Name = "Strength", Id = 17 };
public static Attribute AxeMastery { get; } = new() { Name = "Axe Mastery", Id = 18 };
public static Attribute HammerMastery { get; } = new() { Name = "Hammer Mastery", Id = 19 };
public static Attribute Swordsmanship { get; } = new() { Name = "Swordsmanship", Id = 20};
public static Attribute Tactics { get; } = new() { Name = "Tactics", Id = 21 };
public static Attribute BeastMastery { get; } = new() { Name = "Beast Mastery", Id = 22 };
public static Attribute Expertise { get; } = new() { Name = "Expertise", Id = 23 };
public static Attribute WildernessSurvival { get; } = new() { Name = "Wilderness Survival", Id = 24 };
public static Attribute Marksmanship { get; } = new() { Name = "Marksmanship", Id = 25 };
public static Attribute DaggerMastery { get; } = new() { Name = "Dagger Mastery", Id = 29 };
public static Attribute DeadlyArts { get; } = new() { Name = "Deadly Arts", Id = 30 };
public static Attribute ShadowArts { get; } = new() { Name = "Shadow Arts", Id = 31 };
public static Attribute Communing { get; } = new() { Name = "Communing", Id = 32 };
public static Attribute RestorationMagic { get; } = new() { Name = "Restoration Magic", Id = 33 };
public static Attribute ChannelingMagic { get; } = new() { Name = "Channeling Magic", Id = 34 };
public static Attribute CriticalStrikes { get; } = new() { Name = "Critical Strikes", Id = 35 };
public static Attribute SpawningPower { get; } = new() { Name = "Spawning Power", Id = 36 };
public static Attribute SpearMastery { get; } = new() { Name = "Spear Mastery", Id = 37 };
public static Attribute Command { get; } = new() { Name = "Command", Id = 38 };
public static Attribute Motivation { get; } = new() { Name = "Motivation", Id = 39 };
public static Attribute Leadership { get; } = new() { Name = "Leadership", Id = 40 };
public static Attribute ScytheMastery { get; } = new() { Name = "Scythe Mastery", Id = 41 };
public static Attribute WindPrayers { get; } = new() { Name = "Wind Prayers", Id = 42 };
public static Attribute EarthPrayers { get; } = new() { Name = "Earth Prayers", Id = 43 };
public static Attribute Mysticism { get; } = new() { Name = "Mysticism", Id = 44 };
public static Attribute FastCasting { get; } = new() { Name = "Fast Casting", Id = 0, Profession = Profession.Mesmer };
public static Attribute IllusionMagic { get; } = new() { Name = "Illusion Magic", Id = 1, Profession = Profession.Mesmer };
public static Attribute DominationMagic { get; } = new() { Name = "Domination Magic", Id = 2, Profession = Profession.Mesmer };
public static Attribute InspirationMagic { get; } = new() { Name = "Inspiration Magic", Id = 3, Profession = Profession.Mesmer };
public static Attribute BloodMagic { get; } = new() { Name = "Blood Magic", Id = 4, Profession = Profession.Necromancer };
public static Attribute DeathMagic { get; } = new() { Name = "Death Magic", Id = 5, Profession = Profession.Necromancer };
public static Attribute SoulReaping { get; } = new() { Name = "Soul Reaping", Id = 6, Profession = Profession.Necromancer };
public static Attribute Curses { get; } = new() { Name = "Curses", Id = 7, Profession = Profession.Necromancer };
public static Attribute AirMagic { get; } = new() { Name = "Air Magic", Id = 8, Profession = Profession.Elementalist };
public static Attribute EarthMagic { get; } = new() { Name = "Earth Magic", Id = 9, Profession = Profession.Elementalist };
public static Attribute FireMagic { get; } = new() { Name = "Fire Magic", Id = 10, Profession = Profession.Elementalist };
public static Attribute WaterMagic { get; } = new() { Name = "Water Magic", Id = 11, Profession = Profession.Elementalist };
public static Attribute EnergyStorage { get; } = new() { Name = "Energy Storage", Id = 12, Profession = Profession.Elementalist };
public static Attribute HealingPrayers { get; } = new() { Name = "Healing Prayers", Id = 13, Profession = Profession.Monk };
public static Attribute SmitingPrayers { get; } = new() { Name = "Smiting Prayers", Id = 14, Profession = Profession.Monk };
public static Attribute ProtectionPrayers { get; } = new() { Name = "Protection Prayers", Id = 15, Profession = Profession.Monk };
public static Attribute DivineFavor { get; } = new() { Name = "Divine Favor", Id = 16, Profession = Profession.Monk };
public static Attribute Strength { get; } = new() { Name = "Strength", Id = 17, Profession = Profession.Warrior };
public static Attribute AxeMastery { get; } = new() { Name = "Axe Mastery", Id = 18, Profession = Profession.Warrior };
public static Attribute HammerMastery { get; } = new() { Name = "Hammer Mastery", Id = 19, Profession = Profession.Warrior };
public static Attribute Swordsmanship { get; } = new() { Name = "Swordsmanship", Id = 20, Profession = Profession.Warrior };
public static Attribute Tactics { get; } = new() { Name = "Tactics", Id = 21, Profession = Profession.Warrior };
public static Attribute BeastMastery { get; } = new() { Name = "Beast Mastery", Id = 22, Profession = Profession.Ranger };
public static Attribute Expertise { get; } = new() { Name = "Expertise", Id = 23, Profession = Profession.Ranger };
public static Attribute WildernessSurvival { get; } = new() { Name = "Wilderness Survival", Id = 24, Profession = Profession.Ranger };
public static Attribute Marksmanship { get; } = new() { Name = "Marksmanship", Id = 25, Profession = Profession.Ranger };
public static Attribute DaggerMastery { get; } = new() { Name = "Dagger Mastery", Id = 29, Profession = Profession.Assassin };
public static Attribute DeadlyArts { get; } = new() { Name = "Deadly Arts", Id = 30, Profession = Profession.Assassin };
public static Attribute ShadowArts { get; } = new() { Name = "Shadow Arts", Id = 31, Profession = Profession.Assassin };
public static Attribute Communing { get; } = new() { Name = "Communing", Id = 32, Profession = Profession.Ritualist };
public static Attribute RestorationMagic { get; } = new() { Name = "Restoration Magic", Id = 33, Profession = Profession.Ritualist };
public static Attribute ChannelingMagic { get; } = new() { Name = "Channeling Magic", Id = 34, Profession = Profession.Ritualist };
public static Attribute CriticalStrikes { get; } = new() { Name = "Critical Strikes", Id = 35, Profession = Profession.Assassin };
public static Attribute SpawningPower { get; } = new() { Name = "Spawning Power", Id = 36, Profession = Profession.Ritualist };
public static Attribute SpearMastery { get; } = new() { Name = "Spear Mastery", Id = 37, Profession = Profession.Paragon };
public static Attribute Command { get; } = new() { Name = "Command", Id = 38, Profession = Profession.Paragon };
public static Attribute Motivation { get; } = new() { Name = "Motivation", Id = 39, Profession = Profession.Paragon };
public static Attribute Leadership { get; } = new() { Name = "Leadership", Id = 40, Profession = Profession.Paragon };
public static Attribute ScytheMastery { get; } = new() { Name = "Scythe Mastery", Id = 41, Profession = Profession.Dervish };
public static Attribute WindPrayers { get; } = new() { Name = "Wind Prayers", Id = 42, Profession = Profession.Dervish };
public static Attribute EarthPrayers { get; } = new() { Name = "Earth Prayers", Id = 43, Profession = Profession.Dervish };
public static Attribute Mysticism { get; } = new() { Name = "Mysticism", Id = 44, Profession = Profession.Dervish };
public static IEnumerable<Attribute> Attributes { get; } = new List<Attribute>
{
FastCasting,
@@ -136,6 +136,7 @@ public sealed class Attribute
public int Id { get; private set; }
public string? Name { get; private set; }
public Profession? Profession { get; private set; }
private Attribute()
{
}
+1
View File
@@ -9,4 +9,5 @@ public sealed class GameData
public List<PlayerInformation>? Party { get; init; }
public UserInformation? User { get; init; }
public SessionInformation? Session { get; init; }
public List<WorldPlayerInformation>? WorldPlayers { get; init; }
}
@@ -2,9 +2,8 @@
namespace Daybreak.Models.Guildwars;
public sealed class MainPlayerInformation : PlayerInformation
public sealed class MainPlayerInformation : WorldPlayerInformation
{
public string? Name { get; init; }
public bool HardModeUnlocked { get; init; }
public uint Experience { get; init; }
public uint Level { get; init; }
+63 -63
View File
@@ -627,68 +627,68 @@ public sealed class Map
public static readonly Map KilroysPunchoutTournament = new() { Id = 726, Name = "Plains of Jarin", WikiUrl = "https://wiki.guildwars.com/wiki/Plains_of_Jarin" };
public static readonly Map SpecialOpsFlameTempleCorridor = new() { Id = 727, Name = "Plains of Jarin", WikiUrl = "https://wiki.guildwars.com/wiki/Plains_of_Jarin" };
public static readonly Map SpecialOpsDragonsGullet = new() { Id = 728, Name = "(crash)", WikiUrl = "" };
public static readonly Map SpecialOpsGrendichCourthouse = new() { Id = 729, Name = " Special Ops Grendich Courthouse", WikiUrl = "https://wiki.guildwars.com/wiki/Grendich_Courthouse" };
public static readonly Map TheTenguAccords = new() { Id = 730, Name = " The Tengu Accords", WikiUrl = "https://wiki.guildwars.com/wiki/_The_Tengu_Accords" };
public static readonly Map TheBattleofJahai = new() { Id = 731, Name = " The Battle of Jahai", WikiUrl = "https://wiki.guildwars.com/wiki/The_Battle_of_Jahai" };
public static readonly Map TheFlightNorth = new() { Id = 732, Name = " The Flight North", WikiUrl = "https://wiki.guildwars.com/wiki/_The_Flight_North" };
public static readonly Map TheRiseoftheWhiteMantle = new() { Id = 733, Name = " The Rise of the White Mantle", WikiUrl = "https://wiki.guildwars.com/wiki/The_Rise_of_the_White_Mantle" };
public static readonly Map FindingtheBloodstoneMission = new() { Id = 734, Name = " Finding the Bloodstone Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Finding_the_Bloodstone" };
public static readonly Map GeniusOperatedLivingEnchantedManifestationMission = new() { Id = 760, Name = " Genius Operated Living Enchanted Manifestation Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Genius_Operated_Living_Enchanted_Manifestation" };
public static readonly Map AgainsttheCharrMission = new() { Id = 761, Name = " Against the Charr Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Against_the_Charr" };
public static readonly Map WarbandofbrothersMission = new() { Id = 762, Name = " Warband of brothers Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Warband_of_Brothers" };
public static readonly Map AssaultontheStrongholdMission = new() { Id = 763, Name = " Assault on the Stronghold Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Assault_on_the_Stronghold" };
public static readonly Map DestructionsDepthsMission = new() { Id = 764, Name = " Destructions Depths Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Destruction's_Depths" };
public static readonly Map ATimeforHeroesMission = new() { Id = 765, Name = " A Time for Heroes Mission", WikiUrl = "https://wiki.guildwars.com/wiki/A_Time_for_Heroes" };
public static readonly Map CurseoftheNornbearMission = new() { Id = 766, Name = " Curse of the Nornbear Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Curse_of_the_Nornbear" };
public static readonly Map BloodWashesBloodMission = new() { Id = 767, Name = " Blood Washes Blood Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Blood_Washes_Blood" };
public static readonly Map AGateTooFarMission = new() { Id = 768, Name = " A Gate Too Far Mission", WikiUrl = "https://wiki.guildwars.com/wiki/A_Gate_Too_Far" };
public static readonly Map TheElusiveGolemancerMission = new() { Id = 769, Name = " The Elusive Golemancer Mission", WikiUrl = "https://wiki.guildwars.com/wiki/The_Elusive_Golemancer" };
public static readonly Map SecretLairoftheSnowmen2 = new() { Id = 770, Name = " Secret Lair of the Snowmen2", WikiUrl = "https://wiki.guildwars.com/wiki/Secret_Lair_of_the_Snowmen" };
public static readonly Map SecretLairoftheSnowmen3 = new() { Id = 782, Name = " Secret Lair of the Snowmen3", WikiUrl = "https://wiki.guildwars.com/wiki/Secret_Lair_of_the_Snowmen" };
public static readonly Map DroknarsForgecinematic = new() { Id = 783, Name = " Droknars Forge (cinematic)", WikiUrl = "https://wiki.guildwars.com/wiki/Droknar's_Forge" };
public static readonly Map IsleoftheNamelessPvP = new() { Id = 784, Name = " Isle of the Nameless PvP", WikiUrl = "https://wiki.guildwars.com/wiki/Isle_of_the_Nameless_(PvP)" };
public static readonly Map TempleoftheAgesROX = new() { Id = 785, Name = " Temple of the Ages ROX", WikiUrl = "https://wiki.guildwars.com/wiki/Deactivating_R.O.X." };
public static readonly Map WajjunBazaarPOX = new() { Id = 789, Name = " Wajjun Bazaar POX", WikiUrl = "https://wiki.guildwars.com/wiki/Deactivating_P.O.X." };
public static readonly Map BokkaAmphitheatreNOX = new() { Id = 790, Name = " Bokka Amphitheatre NOX", WikiUrl = "https://wiki.guildwars.com/wiki/Deactivating_N.O.X." };
public static readonly Map SecretUndergroundLair = new() { Id = 791, Name = " Secret Underground Lair", WikiUrl = "https://wiki.guildwars.com/wiki/_Secret_Underground_Lair" };
public static readonly Map GolemTutorialSimulation = new() { Id = 792, Name = " Golem Tutorial Simulation", WikiUrl = "https://wiki.guildwars.com/wiki/_Golem_Tutorial_Simulation" };
public static readonly Map SnowballDominance = new() { Id = 793, Name = " Snowball Dominance", WikiUrl = "https://wiki.guildwars.com/wiki/_Snowball_Dominance" };
public static readonly Map ZaishenMenagerieGrounds = new() { Id = 794, Name = " Zaishen Menagerie Grounds", WikiUrl = "https://wiki.guildwars.com/wiki/_Zaishen_Menagerie_Grounds" };
public static readonly Map ZaishenMenagerieOutpost = new() { Id = 795, Name = " Zaishen Menagerie (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Zaishen_Menagerie_(outpost)" };
public static readonly Map CodexArenaOutpost = new() { Id = 796, Name = " Codex Arena (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Codex_Arena_(outpost)" };
public static readonly Map TheUnderworldSomethingWickedThisWayComes = new() { Id = 797, Name = " The Underworld Something Wicked This Way Comes", WikiUrl = "https://wiki.guildwars.com/wiki/Something_Wicked_This_Way_Comes" };
public static readonly Map TheUnderworldDontFeartheReapers = new() { Id = 807, Name = " The Underworld Don't Fear the Reapers", WikiUrl = "https://wiki.guildwars.com/wiki/Don't_Fear_the_Reapers" };
public static readonly Map LionsArchHalloweenOutpost = new() { Id = 808, Name = " Lions Arch Halloween (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Lion's_Arch_(Halloween)" };
public static readonly Map LionsArchWintersdayOutpost = new() { Id = 809, Name = " Lions Arch Wintersday (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Lion's_Arch_(Wintersday)" };
public static readonly Map LionsArchCanthanNewYearOutpost = new() { Id = 810, Name = " Lions Arch Canthan New Year (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Lion's_Arch" };
public static readonly Map AscalonCityWintersdayOutpost = new() { Id = 811, Name = " Ascalon City Wintersday (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Ascalon_City_(Wintersday)" };
public static readonly Map DroknarsForgeHalloweenOutpost = new() { Id = 812, Name = " Droknars Forge Halloween (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Droknar's_Forge_(Halloween)" };
public static readonly Map DroknarsForgeWintersdayOutpost = new() { Id = 813, Name = " Droknars Forge Wintersday (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Droknar's_Forge_(Halloween)" };
public static readonly Map TombofthePrimevalKingsHalloweenOutpost = new() { Id = 814, Name = " Tomb of the Primeval Kings Halloween (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Tomb_of_the_Primeval_Kings_(Halloween)" };
public static readonly Map ShingJeaMonasteryDragonFestivalOutpost = new() { Id = 815, Name = " Shing Jea Monastery Dragon Festival (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Shing_Jea_Monastery" };
public static readonly Map ShingJeaMonasteryCanthanNewYearOutpost = new() { Id = 816, Name = " Shing Jea Monastery Canthan New Year (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Shing_Jea_Monastery" };
public static readonly Map KainengCenterCanthanNewYearOutpost = new() { Id = 817, Name = " Kaineng Center Canthan New Year (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Kaineng_Center" };
public static readonly Map KamadanJewelofIstanHalloweenOutpost = new() { Id = 818, Name = " Kamadan Jewel of Istan Halloween (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Kamadan,_Jewel_of_Istan_(Halloween)" };
public static readonly Map KamadanJewelofIstanWintersdayOutpost = new() { Id = 819, Name = " Kamadan Jewel of Istan Wintersday (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Kamadan,_Jewel_of_Istan_(Wintersday)" };
public static readonly Map KamadanJewelofIstanCanthanNewYearOutpost = new() { Id = 820, Name = " Kamadan Jewel of Istan Canthan New Year (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Kamadan,_Jewel_of_Istan" };
public static readonly Map EyeoftheNorthOutpostWintersdayOutpost = new() { Id = 821, Name = " Eyeofthe North Outpost Wintersday (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Eye_of_the_North_(outpost)_(Wintersday)" };
public static readonly Map WarinKrytaTalmarkWilderness = new() { Id = 822, Name = " War in Kryta Talmark Wilderness", WikiUrl = "https://wiki.guildwars.com/wiki/Talmark_Wilderness_(War_in_Kryta)" };
public static readonly Map WarinKrytaTrialofZinn = new() { Id = 838, Name = " War in Kryta Trialof Zinn", WikiUrl = "https://wiki.guildwars.com/wiki/Trial_of_Zinn" };
public static readonly Map WarinKrytaDivinityCoast = new() { Id = 839, Name = " War in Kryta Divinity Coast", WikiUrl = "https://wiki.guildwars.com/wiki/Divinity_Coast_(explorable_area)" };
public static readonly Map WarinKrytaLionsArchKeep = new() { Id = 840, Name = " War in Kryta Lions Arch Keep", WikiUrl = "https://wiki.guildwars.com/wiki/Lion's_Arch_(War_in_Kryta)" };
public static readonly Map WarinKrytaDAlessioSeaboard = new() { Id = 841, Name = " War in Kryta D'Alessio Seaboard", WikiUrl = "https://wiki.guildwars.com/wiki/D'Alessio_Seaboard_(explorable_area)" };
public static readonly Map WarinKrytaTheBattleforLionsArch = new() { Id = 842, Name = " War in Kryta The Battle for Lions Arch", WikiUrl = "https://wiki.guildwars.com/wiki/The_Battle_for_Lion's_Arch" };
public static readonly Map WarinKrytaRiversideProvince = new() { Id = 843, Name = " War in Kryta Riverside Province", WikiUrl = "https://wiki.guildwars.com/wiki/Riverside_Province_(explorable_area)" };
public static readonly Map WarinKrytaLionsArch = new() { Id = 844, Name = " War in Kryta Lions Arch", WikiUrl = "https://wiki.guildwars.com/wiki/Lion's_Arch_(War_in_Kryta)" };
public static readonly Map WarinKrytaTheMausoleum = new() { Id = 845, Name = " War in Kryta The Mausoleum", WikiUrl = "https://wiki.guildwars.com/wiki/The_Mausoleum" };
public static readonly Map WarinKrytaRise = new() { Id = 846, Name = " War in Kryta Rise", WikiUrl = "https://wiki.guildwars.com/wiki/Rise" };
public static readonly Map WarinKrytaShadowsintheJungle = new() { Id = 847, Name = " War in Kryta Shadows in the Jungle", WikiUrl = "https://wiki.guildwars.com/wiki/Shadows_in_the_Jungle" };
public static readonly Map WarinKrytaAVengeanceofBlades = new() { Id = 848, Name = " War in Kryta A Vengeance of Blades", WikiUrl = "https://wiki.guildwars.com/wiki/A_Vengeance_of_Blades" };
public static readonly Map WarinKrytaAuspiciousBeginnings = new() { Id = 849, Name = " War in Kryta Auspicious Beginnings", WikiUrl = "https://wiki.guildwars.com/wiki/Auspicious_Beginnings" };
public static readonly Map Olafsteadcinematic = new() { Id = 850, Name = " Olafstead (cinematic)", WikiUrl = "https://wiki.guildwars.com/wiki/Olafstead" };
public static readonly Map TheGreatSnowballFightoftheGodsOperationCrushSpirits = new() { Id = 855, Name = " The Great Snowball Fight of the Gods Operation Crush Spirits", WikiUrl = "https://wiki.guildwars.com/wiki/The_Great_Snowball_Fight_of_the_Gods_(outpost)" };
public static readonly Map TheGreatSnowballFightoftheGodsFightinginaWinterWonderland = new() { Id = 856, Name = " The Great Snowball Fight of the Gods Fighting in a Winter Wonderland", WikiUrl = "https://wiki.guildwars.com/wiki/The_Great_Snowball_Fight_of_the_Gods" };
public static readonly Map EmbarkBeach = new() { Id = 857, Name = " Embark Beach", WikiUrl = "https://wiki.guildwars.com/wiki/_Embark_Beach" };
public static readonly Map SpecialOpsGrendichCourthouse = new() { Id = 729, Name = "Special Ops Grendich Courthouse", WikiUrl = "https://wiki.guildwars.com/wiki/Grendich_Courthouse" };
public static readonly Map TheTenguAccords = new() { Id = 730, Name = "The Tengu Accords", WikiUrl = "https://wiki.guildwars.com/wiki/_The_Tengu_Accords" };
public static readonly Map TheBattleofJahai = new() { Id = 731, Name = "The Battle of Jahai", WikiUrl = "https://wiki.guildwars.com/wiki/The_Battle_of_Jahai" };
public static readonly Map TheFlightNorth = new() { Id = 732, Name = "The Flight North", WikiUrl = "https://wiki.guildwars.com/wiki/_The_Flight_North" };
public static readonly Map TheRiseoftheWhiteMantle = new() { Id = 733, Name = "The Rise of the White Mantle", WikiUrl = "https://wiki.guildwars.com/wiki/The_Rise_of_the_White_Mantle" };
public static readonly Map FindingtheBloodstoneMission = new() { Id = 734, Name = "Finding the Bloodstone Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Finding_the_Bloodstone" };
public static readonly Map GeniusOperatedLivingEnchantedManifestationMission = new() { Id = 760, Name = "Genius Operated Living Enchanted Manifestation Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Genius_Operated_Living_Enchanted_Manifestation" };
public static readonly Map AgainsttheCharrMission = new() { Id = 761, Name = "Against the Charr Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Against_the_Charr" };
public static readonly Map WarbandofbrothersMission = new() { Id = 762, Name = "Warband of brothers Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Warband_of_Brothers" };
public static readonly Map AssaultontheStrongholdMission = new() { Id = 763, Name = "Assault on the Stronghold Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Assault_on_the_Stronghold" };
public static readonly Map DestructionsDepthsMission = new() { Id = 764, Name = "Destructions Depths Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Destruction's_Depths" };
public static readonly Map ATimeforHeroesMission = new() { Id = 765, Name = "A Time for Heroes Mission", WikiUrl = "https://wiki.guildwars.com/wiki/A_Time_for_Heroes" };
public static readonly Map CurseoftheNornbearMission = new() { Id = 766, Name = "Curse of the Nornbear Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Curse_of_the_Nornbear" };
public static readonly Map BloodWashesBloodMission = new() { Id = 767, Name = "Blood Washes Blood Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Blood_Washes_Blood" };
public static readonly Map AGateTooFarMission = new() { Id = 768, Name = "A Gate Too Far Mission", WikiUrl = "https://wiki.guildwars.com/wiki/A_Gate_Too_Far" };
public static readonly Map TheElusiveGolemancerMission = new() { Id = 769, Name = "The Elusive Golemancer Mission", WikiUrl = "https://wiki.guildwars.com/wiki/The_Elusive_Golemancer" };
public static readonly Map SecretLairoftheSnowmen2 = new() { Id = 770, Name = "Secret Lair of the Snowmen2", WikiUrl = "https://wiki.guildwars.com/wiki/Secret_Lair_of_the_Snowmen" };
public static readonly Map SecretLairoftheSnowmen3 = new() { Id = 782, Name = "Secret Lair of the Snowmen3", WikiUrl = "https://wiki.guildwars.com/wiki/Secret_Lair_of_the_Snowmen" };
public static readonly Map DroknarsForgecinematic = new() { Id = 783, Name = "Droknars Forge (cinematic)", WikiUrl = "https://wiki.guildwars.com/wiki/Droknar's_Forge" };
public static readonly Map IsleoftheNamelessPvP = new() { Id = 784, Name = "Isle of the Nameless PvP", WikiUrl = "https://wiki.guildwars.com/wiki/Isle_of_the_Nameless_(PvP)" };
public static readonly Map TempleoftheAgesROX = new() { Id = 785, Name = "Temple of the Ages ROX", WikiUrl = "https://wiki.guildwars.com/wiki/Deactivating_R.O.X." };
public static readonly Map WajjunBazaarPOX = new() { Id = 789, Name = "Wajjun Bazaar POX", WikiUrl = "https://wiki.guildwars.com/wiki/Deactivating_P.O.X." };
public static readonly Map BokkaAmphitheatreNOX = new() { Id = 790, Name = "Bokka Amphitheatre NOX", WikiUrl = "https://wiki.guildwars.com/wiki/Deactivating_N.O.X." };
public static readonly Map SecretUndergroundLair = new() { Id = 791, Name = "Secret Underground Lair", WikiUrl = "https://wiki.guildwars.com/wiki/_Secret_Underground_Lair" };
public static readonly Map GolemTutorialSimulation = new() { Id = 792, Name = "Golem Tutorial Simulation", WikiUrl = "https://wiki.guildwars.com/wiki/_Golem_Tutorial_Simulation" };
public static readonly Map SnowballDominance = new() { Id = 793, Name = "Snowball Dominance", WikiUrl = "https://wiki.guildwars.com/wiki/_Snowball_Dominance" };
public static readonly Map ZaishenMenagerieGrounds = new() { Id = 794, Name = "Zaishen Menagerie Grounds", WikiUrl = "https://wiki.guildwars.com/wiki/_Zaishen_Menagerie_Grounds" };
public static readonly Map ZaishenMenagerieOutpost = new() { Id = 795, Name = "Zaishen Menagerie (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Zaishen_Menagerie_(outpost)" };
public static readonly Map CodexArenaOutpost = new() { Id = 796, Name = "Codex Arena (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Codex_Arena_(outpost)" };
public static readonly Map TheUnderworldSomethingWickedThisWayComes = new() { Id = 797, Name = "The Underworld Something Wicked This Way Comes", WikiUrl = "https://wiki.guildwars.com/wiki/Something_Wicked_This_Way_Comes" };
public static readonly Map TheUnderworldDontFeartheReapers = new() { Id = 807, Name = "The Underworld Don't Fear the Reapers", WikiUrl = "https://wiki.guildwars.com/wiki/Don't_Fear_the_Reapers" };
public static readonly Map LionsArchHalloweenOutpost = new() { Id = 808, Name = "Lions Arch Halloween (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Lion's_Arch_(Halloween)" };
public static readonly Map LionsArchWintersdayOutpost = new() { Id = 809, Name = "Lions Arch Wintersday (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Lion's_Arch_(Wintersday)" };
public static readonly Map LionsArchCanthanNewYearOutpost = new() { Id = 810, Name = "Lions Arch Canthan New Year (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Lion's_Arch" };
public static readonly Map AscalonCityWintersdayOutpost = new() { Id = 811, Name = "Ascalon City Wintersday (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Ascalon_City_(Wintersday)" };
public static readonly Map DroknarsForgeHalloweenOutpost = new() { Id = 812, Name = "Droknars Forge Halloween (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Droknar's_Forge_(Halloween)" };
public static readonly Map DroknarsForgeWintersdayOutpost = new() { Id = 813, Name = "Droknars Forge Wintersday (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Droknar's_Forge_(Halloween)" };
public static readonly Map TombofthePrimevalKingsHalloweenOutpost = new() { Id = 814, Name = "Tomb of the Primeval Kings Halloween (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Tomb_of_the_Primeval_Kings_(Halloween)" };
public static readonly Map ShingJeaMonasteryDragonFestivalOutpost = new() { Id = 815, Name = "Shing Jea Monastery Dragon Festival (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Shing_Jea_Monastery" };
public static readonly Map ShingJeaMonasteryCanthanNewYearOutpost = new() { Id = 816, Name = "Shing Jea Monastery Canthan New Year (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Shing_Jea_Monastery" };
public static readonly Map KainengCenterCanthanNewYearOutpost = new() { Id = 817, Name = "Kaineng Center Canthan New Year (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Kaineng_Center" };
public static readonly Map KamadanJewelofIstanHalloweenOutpost = new() { Id = 818, Name = "Kamadan Jewel of Istan Halloween (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Kamadan,_Jewel_of_Istan_(Halloween)" };
public static readonly Map KamadanJewelofIstanWintersdayOutpost = new() { Id = 819, Name = "Kamadan Jewel of Istan Wintersday (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Kamadan,_Jewel_of_Istan_(Wintersday)" };
public static readonly Map KamadanJewelofIstanCanthanNewYearOutpost = new() { Id = 820, Name = "Kamadan Jewel of Istan Canthan New Year (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Kamadan,_Jewel_of_Istan" };
public static readonly Map EyeoftheNorthOutpostWintersdayOutpost = new() { Id = 821, Name = "Eyeofthe North Outpost Wintersday (Outpost)", WikiUrl = "https://wiki.guildwars.com/wiki/Eye_of_the_North_(outpost)_(Wintersday)" };
public static readonly Map WarinKrytaTalmarkWilderness = new() { Id = 822, Name = "War in Kryta Talmark Wilderness", WikiUrl = "https://wiki.guildwars.com/wiki/Talmark_Wilderness_(War_in_Kryta)" };
public static readonly Map WarinKrytaTrialofZinn = new() { Id = 838, Name = "War in Kryta Trialof Zinn", WikiUrl = "https://wiki.guildwars.com/wiki/Trial_of_Zinn" };
public static readonly Map WarinKrytaDivinityCoast = new() { Id = 839, Name = "War in Kryta Divinity Coast", WikiUrl = "https://wiki.guildwars.com/wiki/Divinity_Coast_(explorable_area)" };
public static readonly Map WarinKrytaLionsArchKeep = new() { Id = 840, Name = "War in Kryta Lions Arch Keep", WikiUrl = "https://wiki.guildwars.com/wiki/Lion's_Arch_(War_in_Kryta)" };
public static readonly Map WarinKrytaDAlessioSeaboard = new() { Id = 841, Name = "War in Kryta D'Alessio Seaboard", WikiUrl = "https://wiki.guildwars.com/wiki/D'Alessio_Seaboard_(explorable_area)" };
public static readonly Map WarinKrytaTheBattleforLionsArch = new() { Id = 842, Name = "War in Kryta The Battle for Lions Arch", WikiUrl = "https://wiki.guildwars.com/wiki/The_Battle_for_Lion's_Arch" };
public static readonly Map WarinKrytaRiversideProvince = new() { Id = 843, Name = "War in Kryta Riverside Province", WikiUrl = "https://wiki.guildwars.com/wiki/Riverside_Province_(explorable_area)" };
public static readonly Map WarinKrytaLionsArch = new() { Id = 844, Name = "War in Kryta Lions Arch", WikiUrl = "https://wiki.guildwars.com/wiki/Lion's_Arch_(War_in_Kryta)" };
public static readonly Map WarinKrytaTheMausoleum = new() { Id = 845, Name = "War in Kryta The Mausoleum", WikiUrl = "https://wiki.guildwars.com/wiki/The_Mausoleum" };
public static readonly Map WarinKrytaRise = new() { Id = 846, Name = "War in Kryta Rise", WikiUrl = "https://wiki.guildwars.com/wiki/Rise" };
public static readonly Map WarinKrytaShadowsintheJungle = new() { Id = 847, Name = "War in Kryta Shadows in the Jungle", WikiUrl = "https://wiki.guildwars.com/wiki/Shadows_in_the_Jungle" };
public static readonly Map WarinKrytaAVengeanceofBlades = new() { Id = 848, Name = "War in Kryta A Vengeance of Blades", WikiUrl = "https://wiki.guildwars.com/wiki/A_Vengeance_of_Blades" };
public static readonly Map WarinKrytaAuspiciousBeginnings = new() { Id = 849, Name = "War in Kryta Auspicious Beginnings", WikiUrl = "https://wiki.guildwars.com/wiki/Auspicious_Beginnings" };
public static readonly Map Olafsteadcinematic = new() { Id = 850, Name = "Olafstead (cinematic)", WikiUrl = "https://wiki.guildwars.com/wiki/Olafstead" };
public static readonly Map TheGreatSnowballFightoftheGodsOperationCrushSpirits = new() { Id = 855, Name = "The Great Snowball Fight of the Gods Operation Crush Spirits", WikiUrl = "https://wiki.guildwars.com/wiki/The_Great_Snowball_Fight_of_the_Gods_(outpost)" };
public static readonly Map TheGreatSnowballFightoftheGodsFightinginaWinterWonderland = new() { Id = 856, Name = "The Great Snowball Fight of the Gods Fighting in a Winter Wonderland", WikiUrl = "https://wiki.guildwars.com/wiki/The_Great_Snowball_Fight_of_the_Gods" };
public static readonly Map EmbarkBeach = new() { Id = 857, Name = "Embark Beach", WikiUrl = "https://wiki.guildwars.com/wiki/_Embark_Beach" };
public static readonly Map DragonsThroatareaWhatWaitsinShadow = new() { Id = 858, Name = "What Waits in Shadow", WikiUrl = "https://wiki.guildwars.com/wiki/What_Waits_in_Shadow" };
public static readonly Map KainengCenterWindsofChangeAChanceEncounter = new() { Id = 861, Name = "Winds of Change A Chance Encounter", WikiUrl = "https://wiki.guildwars.com/wiki/A_Chance_Encounter" };
public static readonly Map TheMarketplaceareaTrackingtheCorruption = new() { Id = 862, Name = "Tracking The Corruption", WikiUrl = "https://wiki.guildwars.com/wiki/Tracking_the_Corruption" };
@@ -697,7 +697,7 @@ public sealed class Map
public static readonly Map SeitungHarborareaDeadlyCargo = new() { Id = 865, Name = "Deadly Cargo", WikiUrl = "https://wiki.guildwars.com/wiki/Deadly_Cargo" };
public static readonly Map TahnnakaiTempleWindsofChangeTheRescueAttempt = new() { Id = 866, Name = "The Rescue Attempt", WikiUrl = "https://wiki.guildwars.com/wiki/The_Rescue_Attempt" };
public static readonly Map WajjunBazaarWindsofChangeViolenceintheStreets = new() { Id = 867, Name = "Violence in the Streets", WikiUrl = "https://wiki.guildwars.com/wiki/Violence_in_the_Streets" };
public static readonly Map ScarredPsycheMission = new() { Id = 868, Name = " Scarred Psyche Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Scarred_Psyche" };
public static readonly Map ScarredPsycheMission = new() { Id = 868, Name = "Scarred Psyche Mission", WikiUrl = "https://wiki.guildwars.com/wiki/Scarred_Psyche" };
public static readonly Map ShadowsPassageWindsofChangeCallingAllThugs = new() { Id = 869, Name = "Calling All Thugs", WikiUrl = "https://wiki.guildwars.com/wiki/Calling_All_Thugs" };
public static readonly Map AltrummRuinsFindingJinnai = new() { Id = 870, Name = "Finding Jinnai", WikiUrl = "https://wiki.guildwars.com/wiki/Finding_Jinnai" };
public static readonly Map ShingJeaMonasteryRaidonShingJeaMonastery = new() { Id = 871, Name = "Raid on Shing Jea Monastery", WikiUrl = "https://wiki.guildwars.com/wiki/Raid_on_Shing_Jea_Monastery" };
+2 -2
View File
@@ -6,7 +6,7 @@ namespace Daybreak.Models.Guildwars;
public sealed class Profession
{
public static Profession None { get; } = new() { Name = "None", Id = 0, };
public static Profession None { get; } = new() { Name = "None", Id = 0 };
public static Profession Warrior { get; } = new() { Name = "Warrior", Id = 1, PrimaryAttribute = Attribute.Strength, Attributes = new List<Attribute> { Attribute.AxeMastery, Attribute.HammerMastery, Attribute.Swordsmanship, Attribute.Tactics } };
public static Profession Ranger { get; } = new() { Name = "Ranger", Id = 2, PrimaryAttribute = Attribute.Expertise, Attributes = new List<Attribute> { Attribute.BeastMastery, Attribute.Marksmanship, Attribute.WildernessSurvival } };
public static Profession Monk { get; } = new() { Name = "Monk", Id = 3, PrimaryAttribute = Attribute.DivineFavor, Attributes = new List<Attribute> { Attribute.HealingPrayers, Attribute.SmitingPrayers, Attribute.ProtectionPrayers } };
@@ -73,7 +73,7 @@ public sealed class Profession
public string? Name { get; private set; }
public int Id { get; set; }
public Attribute? PrimaryAttribute { get; private set; }
public List<Attribute>? Attributes { get; private set; }
public List<Attribute> Attributes { get; private set; } = new List<Attribute>();
private Profession()
{
}
@@ -4,4 +4,5 @@ public sealed class SessionInformation
{
public uint FoesKilled { get; init; }
public uint FoesToKill { get; init; }
public Map? CurrentMap { get; init; }
}
@@ -0,0 +1,6 @@
namespace Daybreak.Models.Guildwars;
public class WorldPlayerInformation : PlayerInformation
{
public string? Name { get; init; }
}
-25
View File
@@ -11,29 +11,4 @@ public readonly struct EntityContext
[FieldOffset(0x9C)]
public readonly EntityType Type;
// From here, all fields are only valid for living entities.
[FieldOffset(0xC4)]
public readonly uint OwnerId;
[FieldOffset(0x114)]
public readonly float EnergyRegen;
[FieldOffset(0x11C)]
public readonly float Energy;
[FieldOffset(0x120)]
public readonly uint MaxEnergy;
[FieldOffset(0x128)]
public readonly float HealthRegen;
[FieldOffset(0x130)]
public readonly float Health;
[FieldOffset(0x134)]
public readonly uint MaxHealth;
[FieldOffset(0x1BC)]
public readonly ushort H01BC;
}
+4 -1
View File
@@ -6,8 +6,11 @@ namespace Daybreak.Models.Interop;
[StructLayout(LayoutKind.Explicit)]
public readonly struct GlobalContext
{
[FieldOffset(0x0008)]
public readonly IntPtr InstanceContext;
[FieldOffset(0x002C)]
public readonly IntPtr CharacterContext;
public readonly IntPtr GameContext;
[FieldOffset(0x0044)]
public readonly IntPtr UserContext;
@@ -0,0 +1,11 @@
namespace Daybreak.Models.Interop;
public readonly struct InstanceContext
{
public const int BaseOffset = 0x01AC;
/// <summary>
/// Milliseconds since the instance was joined.
/// </summary>
public readonly uint Timer;
}
@@ -9,14 +9,23 @@ public readonly struct MapEntityContext
public readonly float CurrentEnergy;
[FieldOffset(0x04)]
public readonly float MaxEnergy;
/// <summary>
/// Amount of regen in one second.
/// </summary>
[FieldOffset(0x08)]
public readonly float EnergyRegen;
/// <summary>
/// Milliseconds since the last information update from the server.
/// </summary>
[FieldOffset(0x0C)]
public readonly int SkillTimestamp;
[FieldOffset(0x20)]
public readonly float CurrentHealth;
[FieldOffset(0x24)]
public readonly float MaxHealth;
/// <summary>
/// Amount of regen in one second.
/// </summary>
[FieldOffset(0x28)]
public readonly float HealthRegen;
/// <summary>
+3
View File
@@ -11,6 +11,9 @@ public readonly struct UserContext
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x28)]
public readonly byte[] PlayerNameBytes;
[FieldOffset(0x0124)]
public readonly uint MapId;
[FieldOffset(0x01B0)]
public readonly int Language;
@@ -0,0 +1,52 @@
using Daybreak.Models.Guildwars;
using System;
using System.Collections.Generic;
namespace Daybreak.Services.BuildTemplates;
/// <summary>
/// Attribute point calculator.
/// Based on https://wiki.guildwars.com/wiki/Attribute_point.
/// </summary>
public sealed class AttributePointCalculator : IAttributePointCalculator
{
private const int MaxRank = 12;
private const int MinRank = 0;
private static readonly List<int> PointsRequiredToIncreaseRankMapping = new()
{
1, 2, 3, 4, 5, 6, 7, 9, 11, 13, 16, 20, int.MaxValue
};
public int MaximumAttributePoints => 200;
public int GetPointsRequiredToIncreaseRank(int currentRank)
{
if (currentRank < MinRank ||
currentRank > MaxRank)
{
throw new ArgumentException($"Current rank must be between {MinRank} and {MaxRank}");
}
return PointsRequiredToIncreaseRankMapping[currentRank];
}
public int GetRemainingFreePoints(Build build)
{
return this.MaximumAttributePoints - this.GetUsedPoints(build);
}
public int GetUsedPoints(Build build)
{
var totalPoints = 0;
foreach(var attribute in build.Attributes)
{
for(var i = 0; i < attribute.Points; i++)
{
totalPoints += this.GetPointsRequiredToIncreaseRank(i);
}
}
return totalPoints;
}
}
@@ -190,15 +190,31 @@ public sealed class BuildTemplateManager : IBuildTemplateManager
}
build.Secondary = secondaryProfession;
/*
* Prepopulate the attributes first and then populate the attribute points based on ids.
*/
if (primaryProfession != Profession.None)
{
build.Attributes.Add(new AttributeEntry { Attribute = primaryProfession.PrimaryAttribute });
build.Attributes.AddRange(primaryProfession.Attributes!.Select(a => new AttributeEntry { Attribute = a }));
}
if (secondaryProfession != Profession.None)
{
build.Attributes.AddRange(secondaryProfession.Attributes!.Select(a => new AttributeEntry { Attribute = a }));
}
for(int i = 0; i < buildMetadata.AttributeCount; i++)
{
if (Daybreak.Models.Guildwars.Attribute.TryParse(buildMetadata.AttributesIds[i], out var attribute) is false)
var attributeId = buildMetadata.AttributesIds[i];
var maybeAttribute = build.Attributes.FirstOrDefault(a => a.Attribute!.Id == attributeId);
if (maybeAttribute is null)
{
this.logger.LogError($"Failed to parse attribute with id {buildMetadata.AttributesIds[i]}");
return new InvalidOperationException($"Failed to parse template");
this.logger.LogError($"Failed to parse attribute with id {attributeId} for professions {primaryProfession.Name}/{secondaryProfession.Name}");
}
build.Attributes.Add(new AttributeEntry { Attribute = attribute, Points = buildMetadata.AttributePoints[i] });
maybeAttribute!.Points = buildMetadata.AttributePoints[i];
}
for(int i = 0; i < 8; i++)
@@ -0,0 +1,14 @@
using Daybreak.Models.Guildwars;
namespace Daybreak.Services.BuildTemplates;
public interface IAttributePointCalculator
{
int MaximumAttributePoints { get; }
int GetPointsRequiredToIncreaseRank(int currentRank);
int GetRemainingFreePoints(Build build);
int GetUsedPoints(Build build);
}
@@ -44,7 +44,7 @@ public sealed class ExperienceCalculator : IExperienceCalculator
var previousLevelExperience = 0U;
foreach (var threshold in ExperienceThreshold)
{
if (currentTotalExperience >= threshold)
if (threshold >= currentTotalExperience)
{
break;
}
@@ -96,6 +96,7 @@ public sealed class ExperienceCalculator : IExperienceCalculator
if (currentTotalExperience < ExperienceCalculationThreshold)
{
var totalXpForNextLevel = 0U;
var totalXpForPreviousLevel = 0U;
foreach (var threshold in ExperienceThreshold)
{
if (currentTotalExperience < threshold)
@@ -103,9 +104,11 @@ public sealed class ExperienceCalculator : IExperienceCalculator
totalXpForNextLevel = threshold;
break;
}
totalXpForPreviousLevel = threshold;
}
return totalXpForNextLevel;
return totalXpForNextLevel - totalXpForPreviousLevel;
}
else
{
@@ -108,6 +108,7 @@ public sealed class IconBrowser : IIconBrowser
logger.LogInformation($"Looking for icon at {skillIconUrl}");
this.browserWrapper.Address = skillIconUrl;
this.browserWrapper.WebBrowser.CoreWebView2.Navigate(skillIconUrl);
for (var i = 0; i < 5; i++)
{
@@ -49,25 +49,14 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
return;
}
scoppedLogger.LogInformation($"Initializing {nameof(GuildwarsMemoryReader)}");
if (this.memoryScanner.Process?.MainModule?.FileName != process?.MainModule?.FileName)
try
{
if (this.memoryScanner.Scanning)
{
scoppedLogger.LogInformation("Scanner is already scanning a different process. Restart scanner and target the new process");
this.memoryScanner.EndScanner();
}
await this.ResilientBeginScanner(scoppedLogger, process!);
await this.InitializeSafe(process, scoppedLogger);
}
if (!this.memoryScanner.Scanning)
catch(Exception e)
{
await this.ResilientBeginScanner(scoppedLogger, process!);
scoppedLogger.LogError(e, "Encountered exception during initialization");
}
this.cancellationTokenSource?.Cancel();
this.PeriodicallyReadGuildwarsMemory();
}
public void Stop()
@@ -77,6 +66,31 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
this.cancellationTokenSource?.Cancel();
}
private async Task InitializeSafe(Process process, ScopedLogger<GuildwarsMemoryReader> scopedLogger)
{
scopedLogger.LogInformation($"Initializing {nameof(GuildwarsMemoryReader)}");
if (this.memoryScanner.Process is null ||
this.memoryScanner.Process.HasExited ||
this.memoryScanner.Process.MainModule?.FileName != process?.MainModule?.FileName)
{
if (this.memoryScanner.Scanning)
{
scopedLogger.LogInformation("Scanner is already scanning a different process. Restart scanner and target the new process");
this.memoryScanner.EndScanner();
}
await this.ResilientBeginScanner(scopedLogger, process!);
}
if (!this.memoryScanner.Scanning)
{
await this.ResilientBeginScanner(scopedLogger, process!);
}
this.cancellationTokenSource?.Cancel();
this.PeriodicallyReadGuildwarsMemory();
}
private async Task ResilientBeginScanner(ScopedLogger<GuildwarsMemoryReader> scopedLogger, Process process)
{
for (var i = 0; i < RetryInitializationCount; i++)
@@ -99,7 +113,19 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
{
var cancellationTokenSource = new CancellationTokenSource();
this.cancellationTokenSource = cancellationTokenSource;
System.Extensions.TaskExtensions.RunPeriodicAsync(() => this.ReadGameMemory(cancellationTokenSource.Token), TimeSpan.Zero, TimeSpan.FromSeconds(1), cancellationTokenSource.Token);
System.Extensions.TaskExtensions.RunPeriodicAsync(() => this.SafeReadGameMemory(cancellationTokenSource.Token), TimeSpan.Zero, TimeSpan.FromSeconds(1), cancellationTokenSource.Token);
}
private void SafeReadGameMemory(CancellationToken cancellationToken)
{
try
{
this.ReadGameMemory(cancellationToken);
}
catch(Exception e)
{
this.logger.LogError(e, "Exception encountered when reading game memory");
}
}
private void ReadGameMemory(CancellationToken cancellationToken)
@@ -121,9 +147,12 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
var globalContext = this.memoryScanner.ReadPtrChain<GlobalContext>(this.memoryScanner.ModuleStartAddress, finalPointerOffset: 0x0, 0x00629244, 0xC, 0xC);
// GameContext struct is offset by 0x07C due to the memory layout of the structure.
var gameContext = this.memoryScanner.Read<GameContext>(globalContext.CharacterContext + GameContext.BaseOffset);
var gameContext = this.memoryScanner.Read<GameContext>(globalContext.GameContext + GameContext.BaseOffset);
// UserContext struct is offset by 0x074 due to the memory layout of the structure.
var userContext = this.memoryScanner.Read<UserContext>(globalContext.UserContext + UserContext.BaseOffset);
// InstanceContext struct is offset by 0x01AC due to memory layout of the structure.
var instanceContext = this.memoryScanner.Read<InstanceContext>(globalContext.InstanceContext + InstanceContext.BaseOffset);
var mapEntities = this.memoryScanner.ReadArray<MapEntityContext>(gameContext.MapEntities);
var professions = this.memoryScanner.ReadArray<ProfessionsContext>(gameContext.Professions);
var players = this.memoryScanner.ReadArray<PlayerContext>(gameContext.Players);
@@ -134,10 +163,7 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
//var entityArray = this.memoryScanner.ReadPtrChain<GuildwarsArray>(this.GetEntityArrayPointer(), 0x0, 0x0);
//var entities = this.memoryScanner.ReadArray<EntityContext>(entityArray);
var email = ParseAndCleanWCharArray(userContext.PlayerEmailBytes);
var name = ParseAndCleanWCharArray(userContext.PlayerNameBytes);
this.GameData = AggregateGameData(gameContext, mapEntities, professions, quests, email, name, playerEntityId);
this.GameData = this.AggregateGameData(gameContext, instanceContext, mapEntities, players, professions, quests, userContext, playerEntityId);
}
private IntPtr GetPlayerIdPointer()
@@ -160,22 +186,33 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
return this.entityArrayPointer;
}
private static GameData AggregateGameData(
private GameData AggregateGameData(
GameContext gameContext,
InstanceContext instanceContext,
MapEntityContext[] entities,
PlayerContext[] players,
ProfessionsContext[] professions,
QuestContext[] quests,
string email,
string name,
UserContext userContext,
int mainPlayerEntityId)
{
var email = ParseAndCleanWCharArray(userContext.PlayerEmailBytes);
var name = ParseAndCleanWCharArray(userContext.PlayerNameBytes);
_ = Map.TryParse((int)userContext.MapId, out var currentMap);
var partyMembers = professions
.Where(p => p.AgentId != mainPlayerEntityId)
.Select(p => GetPlayerInformation((int)p.AgentId, entities, professions))
.Select(p => GetPlayerInformation((int)p.AgentId, instanceContext, entities, professions))
.ToList();
var mainPlayer = GetMainPlayerInformation(mainPlayerEntityId, name, gameContext, entities, professions, quests);
var mainPlayer = GetMainPlayerInformation(mainPlayerEntityId, name, gameContext, instanceContext, entities, professions, quests);
var worldPlayers = players
.Where(p => p.AgentId != mainPlayerEntityId)
.Select(p => GetWorldPlayerInformation(p, this.memoryScanner.ReadWString(p.NamePointer, 0x40), instanceContext, entities, professions))
.ToList();
var userInformation = new UserInformation
{
Email = email,
@@ -198,7 +235,8 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
var sessionInformation = new SessionInformation
{
FoesKilled = gameContext.FoesKilled,
FoesToKill = gameContext.FoesToKill
FoesToKill = gameContext.FoesToKill,
CurrentMap = currentMap
};
return new GameData
@@ -206,7 +244,8 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
Party = partyMembers,
MainPlayer = mainPlayer,
Session = sessionInformation,
User = userInformation
User = userInformation,
WorldPlayers = worldPlayers
};
}
@@ -214,11 +253,12 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
int mainPlayerId,
string name,
GameContext gameContext,
InstanceContext instanceContext,
MapEntityContext[] entities,
ProfessionsContext[] professions,
QuestContext[] quests)
{
var playerInformation = GetPlayerInformation(mainPlayerId, entities, professions);
var playerInformation = GetPlayerInformation(mainPlayerId, instanceContext, entities, professions);
_ = Quest.TryParse((int)gameContext.QuestId, out var quest);
var questLog = quests
.Select(q =>
@@ -252,8 +292,32 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
};
}
private static WorldPlayerInformation GetWorldPlayerInformation(
PlayerContext playerContext,
string name,
InstanceContext instanceContext,
MapEntityContext[] entities,
ProfessionsContext[] professions)
{
var playerInformation = GetPlayerInformation(playerContext.AgentId, instanceContext, entities, professions);
return new WorldPlayerInformation
{
PrimaryProfession = playerInformation.PrimaryProfession,
SecondaryProfession = playerInformation.SecondaryProfession,
UnlockedProfession = playerInformation.UnlockedProfession,
CurrentEnergy = playerInformation.CurrentEnergy,
CurrentHealth = playerInformation.CurrentHealth,
MaxEnergy = playerInformation.MaxEnergy,
MaxHealth = playerInformation.MaxHealth,
EnergyRegen = playerInformation.EnergyRegen,
HealthRegen = playerInformation.HealthRegen,
Name = name,
};
}
private static PlayerInformation GetPlayerInformation(
int playerId,
InstanceContext instanceContext,
MapEntityContext[] entities,
ProfessionsContext[] professions)
{
@@ -264,16 +328,17 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
var unlockedProfessions = Profession.Professions
.Where(p => professionContext.ProfessionUnlocked(p.Id))
.Append(primaryProfession)
.Where(p => p != Profession.None)
.Where(p => p is not null && p != Profession.None)
.OrderBy(p => p.Id)
.ToList();
(var currentHp, var currentEnergy) = ApplyEnergyAndHealthRegen(instanceContext, entityContext);
return new PlayerInformation
{
PrimaryProfession = primaryProfession,
SecondaryProfession = secondaryProfession,
UnlockedProfession = unlockedProfessions,
CurrentHealth = entityContext.CurrentHealth,
CurrentEnergy = entityContext.CurrentEnergy,
CurrentHealth = currentHp,
CurrentEnergy = currentEnergy,
MaxHealth = entityContext.MaxHealth,
MaxEnergy = entityContext.MaxEnergy,
HealthRegen = entityContext.HealthRegen,
@@ -281,6 +346,20 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
};
}
private static (float CurrentHp, float CurrentEnergy) ApplyEnergyAndHealthRegen(InstanceContext instanceContext, MapEntityContext entityContext)
{
var lastKnownHp = entityContext.CurrentHealth;
var lastKnownEnergy = entityContext.CurrentEnergy;
var hpRegen = entityContext.HealthRegen;
var energyRegen = entityContext.EnergyRegen;
var millisSinceLastKnownInformation = instanceContext.Timer - (uint)entityContext.SkillTimestamp;
var currentHp = lastKnownHp + (hpRegen * ((float)millisSinceLastKnownInformation / 1000));
var currentEnergy = lastKnownEnergy + (energyRegen * ((float)millisSinceLastKnownInformation / 1000));
return (
currentHp > entityContext.MaxHealth ? entityContext.MaxHealth : currentHp,
currentEnergy > entityContext.MaxEnergy ? entityContext.MaxEnergy : currentEnergy);
}
private static string ParseAndCleanWCharArray(byte[] bytes)
{
var str = Encoding.Unicode.GetString(bytes);
@@ -99,6 +99,7 @@ public sealed class MemoryScanner : IMemoryScanner
this.Memory = default;
this.Size = default;
this.ModuleStartAddress = default;
this.Scanning = false;
Monitor.Exit(LockObject);
}
+1 -1
View File
@@ -8,7 +8,7 @@
xmlns:controls="clr-namespace:Daybreak.Controls"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="#A0202020">
<Grid Background="#F0212121">
<TextBlock HorizontalAlignment="Center" Text="Accounts settings" FontSize="22" Foreground="White"></TextBlock>
<controls:AddButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Right" Margin="5, 5, 45, 5"
Clicked="AddButton_Clicked" VerticalAlignment="Top"></controls:AddButton>
+7 -1
View File
@@ -1,8 +1,10 @@
using Daybreak.Controls;
using Daybreak.Models;
using Daybreak.Services.Credentials;
using Daybreak.Services.Navigation;
using System;
using System.Collections.ObjectModel;
using System.Core.Extensions;
using System.Extensions;
using System.Linq;
using System.Windows.Controls;
@@ -15,14 +17,17 @@ namespace Daybreak.Views;
/// </summary>
public partial class AccountsView : UserControl
{
private readonly IViewManager viewManager;
private readonly ICredentialManager credentialManager;
public ObservableCollection<LoginCredentials> Accounts { get; } = new();
public AccountsView(
IViewManager viewManager,
ICredentialManager credentialManager)
{
this.credentialManager = credentialManager.ThrowIfNull(nameof(credentialManager));
this.viewManager = viewManager.ThrowIfNull();
this.credentialManager = credentialManager.ThrowIfNull();
this.InitializeComponent();
this.GetCredentials();
}
@@ -46,6 +51,7 @@ public partial class AccountsView : UserControl
private async void SaveButton_Clicked(object sender, EventArgs e)
{
await this.credentialManager.StoreCredentials(this.Accounts.ToList()).ConfigureAwait(true);
this.viewManager.ShowView<LauncherView>();
}
private void AccountTemplate_RemoveClicked(object sender, EventArgs e)
+6 -6
View File
@@ -8,19 +8,19 @@
xmlns:controls="clr-namespace:Daybreak.Controls"
d:DesignHeight="450" d:DesignWidth="800">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Width="400" Height="200"
Background="White">
Background="#F0212121">
<StackPanel VerticalAlignment="Center" Orientation="Vertical">
<TextBlock Text="An update has been detected. Do you want to download the update?" HorizontalAlignment="Center"
FontSize="16" Foreground="Black" Margin="10, 0, 10, 10" TextWrapping="Wrap"></TextBlock>
FontSize="16" Foreground="White" Margin="10, 0, 10, 10" TextWrapping="Wrap"></TextBlock>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<controls:OpaqueButton Text="Yes" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
Clicked="YesButton_Clicked" Foreground="Black" FontSize="16"></controls:OpaqueButton>
<controls:OpaqueButton Text="No" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
Clicked="NoButton_Clicked" Foreground="Black" Grid.Column="1" FontSize="16"></controls:OpaqueButton>
<controls:OpaqueButton Text="Yes" BackgroundOpacity="0.2" TransparentBackground="Gray" Highlight="White" HighlightOpacity="0.6" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
Clicked="YesButton_Clicked" Foreground="White" FontSize="16" Cursor="Hand"></controls:OpaqueButton>
<controls:OpaqueButton Text="No" BackgroundOpacity="0.2" TransparentBackground="Gray" Highlight="White" HighlightOpacity="0.6" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
Clicked="NoButton_Clicked" Foreground="White" Grid.Column="1" FontSize="16" Cursor="Hand"></controls:OpaqueButton>
</Grid>
</StackPanel>
</Grid>
+6 -1
View File
@@ -9,7 +9,7 @@
mc:Ignorable="d"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="#A0202020">
<Grid Background="#F0212121">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
@@ -40,10 +40,15 @@
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock Text="Code: " Foreground="White" Background="Transparent" FontSize="16"></TextBlock>
<TextBox Grid.Column="1" Foreground="White" Background="Transparent" FontSize="16"
Text="{Binding ElementName=_this, Path=CurrentBuildCode, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
<TextBlock Grid.Column="2" Text="Points: " Foreground="White" Background="Transparent" FontSize="16" Margin="10, 0, 0, 0"></TextBlock>
<TextBox Grid.Column="3" Foreground="White" Background="Transparent" FontSize="16"
Text="{Binding ElementName=_this, Path=AttributePoints, Mode=OneWay, UpdateSourceTrigger=PropertyChanged}"></TextBox>
</Grid>
<controls:BuildTemplate x:Name="BuildTemplate" Grid.Row="2" DataContext="{Binding ElementName=_this, Path=CurrentBuild, Mode=OneWay}"
BuildChanged="BuildTemplate_BuildChanged">
+17 -11
View File
@@ -8,6 +8,7 @@ using Daybreak.Services.Navigation;
using Microsoft.Extensions.Logging;
using System;
using System.Configuration;
using System.Core.Extensions;
using System.Extensions;
using System.Linq;
using System.Windows;
@@ -23,10 +24,9 @@ public partial class BuildTemplateView : UserControl
{
private const string DisallowedChars = "\r\n/.";
private bool supressDecode = false;
private readonly IViewManager viewManager;
private readonly IBuildTemplateManager buildTemplateManager;
private readonly IAttributePointCalculator attributePointCalculator;
private readonly ILogger<BuildTemplateView> logger;
[GenerateDependencyProperty(InitialValue = false)]
@@ -35,28 +35,33 @@ public partial class BuildTemplateView : UserControl
private BuildEntry currentBuild = default!;
[GenerateDependencyProperty]
private string currentBuildCode = string.Empty;
[GenerateDependencyProperty]
private int attributePoints;
public BuildTemplateView(
IViewManager viewManager,
IBuildTemplateManager buildTemplateManager,
IIconCache iconRetriever,
IIconBrowser iconBrowser,
IAttributePointCalculator attributePointCalculator,
ILiveOptions<ApplicationConfiguration> liveOptions,
ILogger<ChromiumBrowserWrapper> chromiumLogger,
ILogger<BuildTemplateView> logger)
{
this.buildTemplateManager = buildTemplateManager.ThrowIfNull(nameof(buildTemplateManager));
this.logger = logger.ThrowIfNull(nameof(logger));
this.viewManager = viewManager.ThrowIfNull(nameof(viewManager));
this.buildTemplateManager = buildTemplateManager.ThrowIfNull();
this.logger = logger.ThrowIfNull();
this.viewManager = viewManager.ThrowIfNull();
this.attributePointCalculator = attributePointCalculator.ThrowIfNull();
this.InitializeComponent();
this.BuildTemplate.InitializeTemplate(iconRetriever, iconBrowser, liveOptions, buildTemplateManager, chromiumLogger);
this.BuildTemplate.InitializeTemplate(attributePointCalculator, iconRetriever, iconBrowser, liveOptions, buildTemplateManager, chromiumLogger);
this.DataContextChanged += (sender, contextArgs) =>
{
if (contextArgs.NewValue is BuildEntry)
if (contextArgs.NewValue is BuildEntry buildEntry)
{
this.logger.LogInformation("Received data context. Setting current build");
this.CurrentBuild = contextArgs.NewValue.As<BuildEntry>();
this.CurrentBuild = buildEntry;
this.CurrentBuildCode = this.buildTemplateManager.EncodeTemplate(this.CurrentBuild.Build!);
this.AttributePoints = this.attributePointCalculator.GetRemainingFreePoints(this.CurrentBuild.Build!);
}
};
}
@@ -64,7 +69,7 @@ public partial class BuildTemplateView : UserControl
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == CurrentBuildCodeProperty && this.supressDecode is false)
if (e.Property == CurrentBuildCodeProperty)
{
this.logger.LogInformation($"Attempting to decode provided template {this.CurrentBuildCode}");
try
@@ -88,6 +93,8 @@ public partial class BuildTemplateView : UserControl
Build = new Build()
};
}
this.AttributePoints = this.attributePointCalculator.GetRemainingFreePoints(this.CurrentBuild.Build!);
}
}
@@ -95,12 +102,11 @@ public partial class BuildTemplateView : UserControl
{
try
{
this.supressDecode = true;
this.CurrentBuildCode = this.buildTemplateManager.EncodeTemplate(this.CurrentBuild.Build!);
this.AttributePoints = this.attributePointCalculator.GetRemainingFreePoints(this.CurrentBuild.Build!);
}
finally
{
this.supressDecode = false;
}
}
private void BackButton_Clicked(object sender, EventArgs e)
+8 -1
View File
@@ -8,7 +8,10 @@
mc:Ignorable="d"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="#A0202020">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
</UserControl.Resources>
<Grid Background="#F0212121">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
@@ -30,5 +33,9 @@
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<controls:CircularLoadingWidget Grid.RowSpan="3"
Width="100"
Height="100"
Visibility="{Binding ElementName=_this, Path=Loading, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"></controls:CircularLoadingWidget>
</Grid>
</UserControl>
+6
View File
@@ -8,6 +8,7 @@ using System.Collections.ObjectModel;
using System.Extensions;
using System.Linq;
using System.Windows.Controls;
using System.Windows.Extensions;
namespace Daybreak.Views;
@@ -21,6 +22,9 @@ public partial class BuildsListView : UserControl
private IEnumerable<BuildEntry>? buildEntries;
[GenerateDependencyProperty]
private bool loading;
public ObservableCollection<BuildEntry> BuildEntries { get; } = new ObservableCollection<BuildEntry>();
public BuildsListView(
@@ -35,8 +39,10 @@ public partial class BuildsListView : UserControl
private async void LoadBuilds()
{
this.Loading = true;
this.buildEntries = await this.buildTemplateManager.GetBuilds().ToListAsync();
this.BuildEntries.ClearAnd().AddRange(this.buildEntries.OrderBy(b => b.Name));
this.Loading = false;
}
private void ListView_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
@@ -17,7 +17,7 @@
<BooleanToVisibilityConverter x:Key="BaseBooleanToVisibilityConverter"></BooleanToVisibilityConverter>
</UserControl.Resources>
<Grid
Background="#A0202020">
Background="#F0212121">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
@@ -174,7 +174,7 @@
</StackPanel>
</Grid>
<Grid Grid.RowSpan="3"
Background="#A0202020"
Background="#F0212121"
Visibility="{Binding ElementName=_this, Path=ShowLoading, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
<controls:CircularLoadingWidget MaxWidth="200"
MaxHeight="200"/>
+1 -1
View File
@@ -8,7 +8,7 @@
xmlns:controls="clr-namespace:Daybreak.Controls"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="#A0202020">
<Grid Background="#F0212121">
<TextBlock HorizontalAlignment="Center" Text="Executables settings" FontSize="22" Foreground="White"></TextBlock>
<controls:AddButton Foreground="White" Height="30" Width="30" Grid.Column="0" HorizontalAlignment="Right" Margin="5, 5, 45, 5"
Clicked="AddButton_Clicked" VerticalAlignment="Top"></controls:AddButton>
+7 -1
View File
@@ -1,9 +1,11 @@
using Daybreak.Configuration;
using Daybreak.Controls;
using Daybreak.Models;
using Daybreak.Services.Navigation;
using System;
using System.Collections.ObjectModel;
using System.Configuration;
using System.Core.Extensions;
using System.Extensions;
using System.Linq;
using System.Windows.Controls;
@@ -16,13 +18,16 @@ namespace Daybreak.Views;
/// </summary>
public partial class ExecutablesView : UserControl
{
private readonly IViewManager viewManager;
private readonly ILiveUpdateableOptions<ApplicationConfiguration> liveUpdateableOptions;
public ObservableCollection<GuildwarsPath> Paths { get; } = new();
public ExecutablesView(
IViewManager viewManager,
ILiveUpdateableOptions<ApplicationConfiguration> liveUpdateableOptions)
{
this.liveUpdateableOptions = liveUpdateableOptions.ThrowIfNull(nameof(liveUpdateableOptions));
this.viewManager = viewManager.ThrowIfNull();
this.liveUpdateableOptions = liveUpdateableOptions.ThrowIfNull();
this.InitializeComponent();
this.GetPaths();
}
@@ -46,6 +51,7 @@ public partial class ExecutablesView : UserControl
{
this.liveUpdateableOptions.Value.GuildwarsPaths = this.Paths.ToList();
this.liveUpdateableOptions.UpdateOption();
this.viewManager.ShowView<LauncherView>();
}
private void GuildwarsPathTemplate_DefaultClicked(object sender, EventArgs e)
+1 -1
View File
@@ -66,7 +66,7 @@
</Style>
</ResourceDictionary>
</UserControl.Resources>
<Grid Background="#A0202020">
<Grid Background="#F0212121">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
@@ -1,6 +1,8 @@
using Daybreak.Configuration;
using Daybreak.Services.Navigation;
using System;
using System.Configuration;
using System.Core.Extensions;
using System.Extensions;
using System.Linq;
using System.Windows.Controls;
@@ -14,6 +16,8 @@ namespace Daybreak.Views;
/// </summary>
public partial class ExperimentalSettingsView : UserControl
{
private readonly IViewManager viewManager;
[GenerateDependencyProperty]
private bool launchAsCurrentUser;
[GenerateDependencyProperty]
@@ -32,9 +36,11 @@ public partial class ExperimentalSettingsView : UserControl
private readonly ILiveUpdateableOptions<ApplicationConfiguration> liveUpdateableOptions;
public ExperimentalSettingsView(
IViewManager viewManager,
ILiveUpdateableOptions<ApplicationConfiguration> liveUpdateableOptions)
{
this.liveUpdateableOptions = liveUpdateableOptions.ThrowIfNull(nameof(liveUpdateableOptions));
this.viewManager = viewManager.ThrowIfNull();
this.liveUpdateableOptions = liveUpdateableOptions.ThrowIfNull();
this.InitializeComponent();
this.LoadExperimentalSettings();
}
@@ -66,6 +72,7 @@ public partial class ExperimentalSettingsView : UserControl
}
this.liveUpdateableOptions.UpdateOption();
this.viewManager.ShowView<LauncherView>();
}
private void SaveButton_Clicked(object sender, EventArgs e)
+255 -87
View File
@@ -7,6 +7,7 @@
xmlns:controls="clr-namespace:Daybreak.Controls"
xmlns:templates="clr-namespace:Daybreak.Controls.Templates"
mc:Ignorable="d"
Foreground="White"
Loaded="FocusView_Loaded"
Unloaded="FocusView_Unloaded"
x:Name="_this"
@@ -16,123 +17,291 @@
</UserControl.Resources>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="0.1*"></ColumnDefinition>
<ColumnDefinition Width="0.01*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="0.1*"></ColumnDefinition>
<ColumnDefinition Width="0.01*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Grid.RowDefinitions>
<RowDefinition Height="0.1*"></RowDefinition>
<RowDefinition Height="0.6*"></RowDefinition>
<RowDefinition Height="0.2*"></RowDefinition>
<RowDefinition Height="0.1*"></RowDefinition>
<RowDefinition Height="0*"></RowDefinition>
<RowDefinition Height="1*"></RowDefinition>
<RowDefinition Height="118"></RowDefinition>
</Grid.RowDefinitions>
<controls:ResourceBar
<Grid
Margin="0, 2, 0, 0"
Grid.Column="1"
Grid.ColumnSpan="2"
Grid.Row="2"
Grid.RowSpan="2">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="0.1*"></ColumnDefinition>
<ColumnDefinition Width="0.3*"></ColumnDefinition>
<ColumnDefinition Width="0.1*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<controls:VerticalResourceBar
Grid.Column="1"
Grid.ColumnSpan="2"
Grid.Row="2"
Height="30"
Grid.ColumnSpan="3"
CurrentResourceValue="{Binding ElementName=_this, Path=CurrentExperienceInLevel, Mode=OneWay}"
MaxResourceValue="{Binding ElementName=_this, Path=NextLevelExperienceThreshold, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Center"
BarColor="#a026ff00"
Text="{Binding ElementName=_this,Path=ExperienceBarText, Mode=OneWay}"
BarColor="#6076FF03"
Foreground="White"
FontSize="22"
Height="113"
FontSize="14"
Cursor="Hand"
Text="{Binding ElementName=_this, Path=ExperienceBarText, Mode=OneWay}"
MouseLeftButtonDown="ExperienceBar_MouseLeftButtonDown"></controls:ResourceBar>
<controls:ResourceBar
Grid.Column="1"
MouseLeftButtonDown="ExperienceBar_MouseLeftButtonDown">
<controls:VerticalResourceBar.OpacityMask>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#FFFFFFFF">
<GeometryDrawing.Geometry>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure IsClosed="True" StartPoint="5, 1">
<PathSegmentCollection>
<LineSegment Point="1, 5"></LineSegment>
<LineSegment Point="5, 9"></LineSegment>
<LineSegment Point="15, 9"></LineSegment>
<LineSegment Point="19, 5"></LineSegment>
<LineSegment Point="15, 1"></LineSegment>
</PathSegmentCollection>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</controls:VerticalResourceBar.OpacityMask>
</controls:VerticalResourceBar>
<StackPanel
VerticalAlignment="Center"
Grid.Column="0"
Grid.ColumnSpan="2"
Grid.Row="2"
Margin="0, 0, 20, 80"
Height="30"
CurrentResourceValue="{Binding ElementName=_this, Path=GameData.User.CurrentKurzickPoints, Mode=OneWay}"
MaxResourceValue="{Binding ElementName=_this, Path=GameData.User.TotalKurzickPoints, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="#a00b00ff"
Foreground="White"
FontSize="22"
Cursor="Hand"
Text="{Binding ElementName=_this, Path=KurzickBarText, Mode=OneWay}"
MouseLeftButtonDown="KurzickBar_MouseLeftButtonDown"></controls:ResourceBar>
<controls:ResourceBar
Grid.Column="1"
Grid.RowSpan="2">
<StackPanel.OpacityMask>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#FFFFFFFF">
<GeometryDrawing.Geometry>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure IsClosed="True" StartPoint="1, 1">
<PathSegmentCollection>
<LineSegment Point="1, 9"></LineSegment>
<LineSegment Point="21, 9"></LineSegment>
<LineSegment Point="19, 5"></LineSegment>
<LineSegment Point="21, 1"></LineSegment>
</PathSegmentCollection>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</StackPanel.OpacityMask>
<controls:HorizontalResourceBar
Margin="0, 2, 0, 2"
Height="{Binding ElementName=_this, Path=LeftSideBarSize, Mode=OneWay}"
CurrentResourceValue="{Binding ElementName=_this, Path=GameData.User.CurrentKurzickPoints, Mode=OneWay}"
MaxResourceValue="{Binding ElementName=_this, Path=GameData.User.MaxKurzickPoints, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="#603D5AFE"
Foreground="White"
FontSize="14"
Cursor="Hand"
Text="{Binding ElementName=_this, Path=KurzickBarText, Mode=OneWay}"
MouseLeftButtonDown="KurzickBar_MouseLeftButtonDown"></controls:HorizontalResourceBar>
<controls:HorizontalResourceBar
Margin="0, 2, 0, 2"
Height="{Binding ElementName=_this, Path=LeftSideBarSize, Mode=OneWay}"
CurrentResourceValue="{Binding ElementName=_this, Path=GameData.User.CurrentLuxonPoints, Mode=OneWay}"
MaxResourceValue="{Binding ElementName=_this, Path=GameData.User.MaxLuxonPoints, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="#60FF9100"
Foreground="White"
FontSize="14"
Cursor="Hand"
Text="{Binding ElementName=_this, Path=LuxonBarText, Mode=OneWay}"
MouseLeftButtonDown="LuxonBar_MouseLeftButtonDown"></controls:HorizontalResourceBar>
<controls:HorizontalResourceBar
Margin="0, 2, 0, 2"
Height="{Binding ElementName=_this, Path=LeftSideBarSize, Mode=OneWay}"
CurrentResourceValue="{Binding ElementName=_this, Path=GameData.User.CurrentImperialPoints, Mode=OneWay}"
MaxResourceValue="{Binding ElementName=_this, Path=GameData.User.MaxImperialPoints, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="#60651FFF"
Foreground="White"
FontSize="14"
Cursor="Hand"
Text="{Binding ElementName=_this, Path=ImperialBarText, Mode=OneWay}"
MouseLeftButtonDown="ImperialBar_MouseLeftButtonDown"></controls:HorizontalResourceBar>
<controls:HorizontalResourceBar
Margin="0, 2, 0, 2"
Height="{Binding ElementName=_this, Path=LeftSideBarSize, Mode=OneWay}"
CurrentResourceValue="{Binding ElementName=_this, Path=GameData.User.CurrentBalthazarPoints, Mode=OneWay}"
MaxResourceValue="{Binding ElementName=_this, Path=GameData.User.MaxBalthazarPoints, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="#60FFEA00"
Foreground="White"
FontSize="14"
Cursor="Hand"
Text="{Binding ElementName=_this, Path=BalthazarBarText, Mode=OneWay}"
MouseLeftButtonDown="BalthazarBar_MouseLeftButtonDown"></controls:HorizontalResourceBar>
</StackPanel>
<StackPanel
VerticalAlignment="Center"
Grid.Column="3"
Grid.ColumnSpan="2"
Grid.Row="2"
Margin="0, 0, 20, 40"
Height="30"
CurrentResourceValue="{Binding ElementName=_this, Path=GameData.User.CurrentLuxonPoints, Mode=OneWay}"
MaxResourceValue="{Binding ElementName=_this, Path=GameData.User.TotalLuxonPoints, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="#a0ff3400"
Foreground="White"
FontSize="22"
Cursor="Hand"
Text="{Binding ElementName=_this, Path=LuxonBarText, Mode=OneWay}"
MouseLeftButtonDown="LuxonBar_MouseLeftButtonDown"></controls:ResourceBar>
<controls:ResourceBar
Grid.Column="2"
Grid.Row="2"
Margin="20, 0, 0, 80"
Height="30"
CurrentResourceValue="{Binding ElementName=_this, Path=GameData.User.CurrentImperialPoints, Mode=OneWay}"
MaxResourceValue="{Binding ElementName=_this, Path=GameData.User.TotalImperialPoints, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="#a0a800ff"
Foreground="White"
FontSize="22"
Cursor="Hand"
Text="{Binding ElementName=_this, Path=ImperialBarText, Mode=OneWay}"
MouseLeftButtonDown="ImperialBar_MouseLeftButtonDown"></controls:ResourceBar>
<controls:ResourceBar
Grid.Column="2"
Grid.Row="2"
Margin="20, 0, 0, 40"
Height="30"
CurrentResourceValue="{Binding ElementName=_this, Path=GameData.User.CurrentBalthazarPoints, Mode=OneWay}"
MaxResourceValue="{Binding ElementName=_this, Path=GameData.User.TotalBalthazarPoints, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="#a0ffb200"
Foreground="White"
FontSize="22"
Cursor="Hand"
Text="{Binding ElementName=_this, Path=BalthazarBarText, Mode=OneWay}"
MouseLeftButtonDown="BalthazarBar_MouseLeftButtonDown"></controls:ResourceBar>
Grid.RowSpan="2">
<StackPanel.OpacityMask>
<DrawingBrush>
<DrawingBrush.Drawing>
<DrawingGroup>
<GeometryDrawing Brush="#FFFFFFFF">
<GeometryDrawing.Geometry>
<PathGeometry>
<PathGeometry.Figures>
<PathFigure IsClosed="True" StartPoint="1, 1">
<PathSegmentCollection>
<LineSegment Point="3, 5"></LineSegment>
<LineSegment Point="1, 9"></LineSegment>
<LineSegment Point="21, 9"></LineSegment>
<LineSegment Point="21, 1"></LineSegment>
</PathSegmentCollection>
</PathFigure>
</PathGeometry.Figures>
</PathGeometry>
</GeometryDrawing.Geometry>
</GeometryDrawing>
</DrawingGroup>
</DrawingBrush.Drawing>
</DrawingBrush>
</StackPanel.OpacityMask>
<controls:HorizontalResourceBar
Margin="0, 2, 0, 2"
Height="{Binding ElementName=_this, Path=RightSideBarSize, Mode=OneWay}"
CurrentResourceValue="{Binding ElementName=_this, Path=GameData.Session.FoesKilled, Mode=OneWay}"
MaxResourceValue="{Binding ElementName=_this, Path=TotalFoes, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="#60DD2C00"
Foreground="White"
FontSize="14"
Cursor="Hand"
Text="{Binding ElementName=_this, Path=VanquishingText, Mode=OneWay}"
FillAlignment="Right"
Visibility="{Binding ElementName=_this, Path=Vanquishing, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
MouseLeftButtonDown="VanquishingBar_MouseLeftButtonDown"></controls:HorizontalResourceBar>
<controls:HorizontalResourceBar
Margin="0, 2, 0, 2"
Height="{Binding ElementName=_this, Path=RightSideBarSize, Mode=OneWay}"
CurrentResourceValue="{Binding ElementName=_this, Path=GameData.MainPlayer.CurrentHealth, Mode=OneWay}"
MaxResourceValue="{Binding ElementName=_this, Path=GameData.MainPlayer.MaxHealth, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="#60D50000"
Foreground="White"
FontSize="14"
Cursor="Hand"
Text="{Binding ElementName=_this, Path=HealthBarText, Mode=OneWay}"
FillAlignment="Right"
MouseLeftButtonDown="HealthBar_MouseLeftButtonDown"></controls:HorizontalResourceBar>
<controls:HorizontalResourceBar
Margin="0, 2, 0, 2"
Height="{Binding ElementName=_this, Path=RightSideBarSize, Mode=OneWay}"
CurrentResourceValue="{Binding ElementName=_this, Path=GameData.MainPlayer.CurrentEnergy, Mode=OneWay}"
MaxResourceValue="{Binding ElementName=_this, Path=GameData.MainPlayer.MaxEnergy, Mode=OneWay}"
VerticalAlignment="Bottom"
HorizontalAlignment="Left"
BarColor="#6018FFFF"
Foreground="White"
FontSize="14"
Cursor="Hand"
Text="{Binding ElementName=_this, Path=EnergyBarText, Mode=OneWay}"
FillAlignment="Right"
MouseLeftButtonDown="EnergyBar_MouseLeftButtonDown"></controls:HorizontalResourceBar>
</StackPanel>
</Grid>
<Grid
Grid.Row="1"
Grid.Column="1">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="10"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="10"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
</Grid.RowDefinitions>
<StackPanel
Background="#F0212121"
Grid.Row="0"
Margin="0, 0, 10, 0">
Margin="0, 0, 5, 0">
<TextBlock
FontSize="18"
Margin="5"
Text="Current Quest"></TextBlock>
<TextBlock
Text="{Binding ElementName=_this, Path=GameData.MainPlayer.Quest.Name, Mode=OneWay}"
Cursor="Hand"
FontSize="20"
MouseLeftButtonDown="CurrentQuest_MouseLeftButtonDown"></TextBlock>
Margin="10"
Text="Current Map"></TextBlock>
<Rectangle
Height="1"
Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"></Rectangle>
<controls:OpaqueButton
Text="{Binding ElementName=_this, Path=GameData.Session.CurrentMap.Name, Mode=OneWay}"
Highlight="White"
HighlightOpacity="0.6"
HorizontalAlignment="Stretch"
TextHorizontalAlignment="Left"
Cursor="Hand"
FontSize="20"
MouseLeftButtonDown="CurrentMap_MouseLeftButtonDown"></controls:OpaqueButton>
</StackPanel>
<StackPanel
Background="#F0212121"
Grid.Row="2"
Margin="0, 0, 5, 0">
<TextBlock
FontSize="18"
Margin="5"
Text="Quest Log"></TextBlock>
Margin="10"
Text="Current Quest"></TextBlock>
<Rectangle
Height="1"
Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"></Rectangle>
<controls:OpaqueButton
Text="{Binding ElementName=_this, Path=GameData.MainPlayer.Quest.Name, Mode=OneWay}"
Highlight="White"
HighlightOpacity="0.6"
HorizontalAlignment="Stretch"
TextHorizontalAlignment="Left"
Cursor="Hand"
FontSize="20"
MouseLeftButtonDown="CurrentQuest_MouseLeftButtonDown"></controls:OpaqueButton>
</StackPanel>
<TextBlock
Background="#F0212121"
Grid.Row="4"
FontSize="18"
Margin="0, 0, 5, 0"
Padding="10"
Text="Quest Log"></TextBlock>
<ScrollViewer
Background="#F0212121"
VerticalScrollBarVisibility="Auto"
HorizontalScrollBarVisibility="Disabled"
Grid.Row="1">
Grid.Row="5"
Margin="0, 0, 5, 0">
<templates:QuestLogTemplate
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Quests="{Binding ElementName=_this,Path=GameData.MainPlayer.QuestLog, Mode=OneWay}"
@@ -141,12 +310,11 @@
</templates:QuestLogTemplate>
</ScrollViewer>
</Grid>
<controls:ChromiumBrowserWrapper
x:Name="Browser"
Grid.Column="2"
Grid.Row="1"
Margin="10, 0, 0, 0"
Margin="5, 0, 0, 0"
ControlsEnabled="True"
AddressBarReadonly="False"
CanNavigate="True"
+217 -35
View File
@@ -9,6 +9,7 @@ using Microsoft.Extensions.Logging;
using System;
using System.Configuration;
using System.Core.Extensions;
using System.Drawing.Printing;
using System.Extensions;
using System.Threading;
using System.Windows;
@@ -22,6 +23,8 @@ namespace Daybreak.Views;
/// </summary>
public partial class FocusView : UserControl
{
private const double BarsTotalSize = 116; // Size of the bars on one side of the screen.
private readonly IApplicationLauncher applicationLauncher;
private readonly IGuildwarsMemoryReader guildwarsMemoryReader;
private readonly IExperienceCalculator experienceCalculator;
@@ -38,7 +41,6 @@ public partial class FocusView : UserControl
private double nextLevelExperienceThreshold;
[GenerateDependencyProperty]
private string experienceBarText = string.Empty;
[GenerateDependencyProperty]
private string luxonBarText = string.Empty;
[GenerateDependencyProperty]
@@ -48,6 +50,23 @@ public partial class FocusView : UserControl
[GenerateDependencyProperty]
private string balthazarBarText = string.Empty;
[GenerateDependencyProperty]
private string healthBarText = string.Empty;
[GenerateDependencyProperty]
private string energyBarText = string.Empty;
[GenerateDependencyProperty]
private bool vanquishing;
[GenerateDependencyProperty]
private uint totalFoes;
[GenerateDependencyProperty]
private string vanquishingText = string.Empty;
[GenerateDependencyProperty]
private double leftSideBarSize;
[GenerateDependencyProperty]
private double rightSideBarSize;
[GenerateDependencyProperty]
private string browserAddress = string.Empty;
@@ -72,6 +91,7 @@ public partial class FocusView : UserControl
this.InitializeComponent();
this.LeftSideBarSize = 25;
this.InitializeBrowser();
}
@@ -84,6 +104,11 @@ public partial class FocusView : UserControl
this.liveUpdateableOptions.Value.FocusViewOptions.BrowserUrl = this.BrowserAddress;
this.liveUpdateableOptions.UpdateOption();
}
else if (e.Property == VanquishingProperty &&
e.OldValue != e.NewValue)
{
this.UpdateRightSideBarsLayout();
}
base.OnPropertyChanged(e);
}
@@ -101,42 +126,68 @@ public partial class FocusView : UserControl
this.viewManager.ShowView<LauncherView>();
}
if (this.guildwarsMemoryReader.Running is false)
{
this.guildwarsMemoryReader.Initialize(this.applicationLauncher.RunningGuildwarsProcess!);
}
this.Dispatcher.Invoke(() =>
{
this.GameData = this.guildwarsMemoryReader.GameData;
if (this.GameData?.MainPlayer is null)
if (this.GameData?.MainPlayer is null ||
this.GameData?.User is null ||
this.GameData?.Session is null)
{
return;
}
this.CurrentExperienceInLevel = this.experienceCalculator.GetExperienceForCurrentLevel(this.GameData.MainPlayer!.Experience);
this.NextLevelExperienceThreshold = this.experienceCalculator.GetNextExperienceThreshold(this.GameData.MainPlayer!.Experience);
this.TotalFoes = this.GameData.Session.FoesKilled + this.GameData.Session.FoesToKill;
this.Vanquishing = this.GameData.Session.FoesToKill + this.GameData.Session.FoesKilled > 0U;
this.UpdateExperienceText();
this.UpdateLuxonText();
this.UpdateKurzickText();
this.UpdateImperialText();
this.UpdateBalthazarText();
this.UpdateVanquishingText();
this.UpdateHealthText();
this.UpdateEnergyText();
},
System.Windows.Threading.DispatcherPriority.ApplicationIdle);
}
private void UpdateRightSideBarsLayout()
{
// If vanquishing, there's 3 bars, otherwise there's only 2 bars
var bars = this.Vanquishing ? 3 : 2;
var marginsTotalSize = 4 * bars;
var finalBarSize = (BarsTotalSize - marginsTotalSize) / bars;
this.RightSideBarSize = finalBarSize;
}
private void UpdateExperienceText()
{
switch (this.liveUpdateableOptions.Value.FocusViewOptions.EnergyDisplay)
switch (this.liveUpdateableOptions.Value.FocusViewOptions.ExperienceDisplay)
{
case Configuration.FocusView.EnergyDisplay.CurrentLevelCurrentAndCurrentLevelMax:
case Configuration.FocusView.ExperienceDisplay.CurrentLevelCurrentAndCurrentLevelMax:
var currentExperienceInLevel = this.experienceCalculator.GetExperienceForCurrentLevel(this.GameData.MainPlayer!.Experience);
var nextLevelExperienceThreshold = this.experienceCalculator.GetNextExperienceThreshold(this.GameData.MainPlayer!.Experience);
this.ExperienceBarText = $"{currentExperienceInLevel} / {nextLevelExperienceThreshold} XP";
this.ExperienceBarText = $"{(int)currentExperienceInLevel} / {(int)nextLevelExperienceThreshold} XP";
break;
case Configuration.FocusView.EnergyDisplay.TotalCurretAndTotalMax:
case Configuration.FocusView.ExperienceDisplay.TotalCurretAndTotalMax:
var currentTotalExperience = this.GameData.MainPlayer!.Experience;
var requiredTotalExperience = this.experienceCalculator.GetTotalExperienceForNextLevel(currentTotalExperience);
this.ExperienceBarText = $"{currentTotalExperience} / {requiredTotalExperience} XP";
this.ExperienceBarText = $"{(int)currentTotalExperience} / {(int)requiredTotalExperience} XP";
break;
case Configuration.FocusView.EnergyDisplay.RemainingUntilNextLevel:
case Configuration.FocusView.ExperienceDisplay.RemainingUntilNextLevel:
var remainingExperience = this.experienceCalculator.GetRemainingExperienceForNextLevel(this.GameData.MainPlayer!.Experience);
this.ExperienceBarText = $"Remaining {remainingExperience} XP";
this.ExperienceBarText = $"Remaining {(int)remainingExperience} XP";
break;
case Configuration.FocusView.ExperienceDisplay.Percentage:
var currentExperienceInLevel2 = this.experienceCalculator.GetExperienceForCurrentLevel(this.GameData.MainPlayer!.Experience);
var nextLevelExperienceThreshold2 = this.experienceCalculator.GetNextExperienceThreshold(this.GameData.MainPlayer!.Experience);
this.ExperienceBarText = $"{(int)((double)currentExperienceInLevel2 / (double)nextLevelExperienceThreshold2 * 100)}% XP";
break;
}
}
@@ -146,10 +197,13 @@ public partial class FocusView : UserControl
switch (this.liveUpdateableOptions.Value.FocusViewOptions.LuxonPointsDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.LuxonBarText = $"{this.GameData.User!.CurrentLuxonPoints} / {this.GameData.User!.MaxLuxonPoints} Luxon Points";
this.LuxonBarText = $"{this.GameData.User!.CurrentLuxonPoints} / {this.GameData.User.MaxLuxonPoints} Luxon Points";
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.LuxonBarText = $"Remaining {this.GameData.User!.MaxLuxonPoints - this.GameData.User!.CurrentLuxonPoints} Luxon Points";
this.LuxonBarText = $"Remaining {this.GameData.User!.MaxLuxonPoints - this.GameData.User.CurrentLuxonPoints} Luxon Points";
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.LuxonBarText = $"{(int)((double)this.GameData.User!.CurrentLuxonPoints / (double)this.GameData.User.MaxLuxonPoints * 100)}% Luxon Points";
break;
}
}
@@ -159,10 +213,13 @@ public partial class FocusView : UserControl
switch (this.liveUpdateableOptions.Value.FocusViewOptions.KurzickPointsDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.KurzickBarText = $"{this.GameData.User!.CurrentKurzickPoints} / {this.GameData.User!.MaxKurzickPoints} Kurzick Points";
this.KurzickBarText = $"{this.GameData.User!.CurrentKurzickPoints} / {this.GameData.User.MaxKurzickPoints} Kurzick Points";
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.KurzickBarText = $"Remaining {this.GameData.User!.MaxKurzickPoints - this.GameData.User!.CurrentKurzickPoints} Kurzick Points";
this.KurzickBarText = $"Remaining {this.GameData.User!.MaxKurzickPoints - this.GameData.User.CurrentKurzickPoints} Kurzick Points";
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.KurzickBarText = $"{(int)((double)this.GameData.User!.CurrentKurzickPoints / (double)this.GameData.User.MaxKurzickPoints * 100)}% Kurzick Points";
break;
}
}
@@ -172,10 +229,13 @@ public partial class FocusView : UserControl
switch (this.liveUpdateableOptions.Value.FocusViewOptions.ImperialPointsDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.ImperialBarText = $"{this.GameData.User!.CurrentImperialPoints} / {this.GameData.User!.MaxImperialPoints} Imperial Points";
this.ImperialBarText = $"{this.GameData.User!.CurrentImperialPoints} / {this.GameData.User.MaxImperialPoints} Imperial Points";
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.ImperialBarText = $"Remaining {this.GameData.User!.MaxImperialPoints - this.GameData.User!.CurrentImperialPoints} Imperial Points";
this.ImperialBarText = $"Remaining {this.GameData.User!.MaxImperialPoints - this.GameData.User.CurrentImperialPoints} Imperial Points";
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.ImperialBarText = $"{(int)((double)this.GameData.User!.CurrentImperialPoints / (double)this.GameData.User.MaxImperialPoints * 100)}% Imperial Points";
break;
}
}
@@ -185,28 +245,69 @@ public partial class FocusView : UserControl
switch (this.liveUpdateableOptions.Value.FocusViewOptions.BalthazarPointsDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.BalthazarBarText = $"{this.GameData.User!.CurrentBalthazarPoints} / {this.GameData.User!.MaxBalthazarPoints} Balthazar Points";
this.BalthazarBarText = $"{this.GameData.User!.CurrentBalthazarPoints} / {this.GameData.User.MaxBalthazarPoints} Balthazar Points";
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.BalthazarBarText = $"Remaining {this.GameData.User!.MaxBalthazarPoints - this.GameData.User!.CurrentBalthazarPoints} Balthazar Points";
this.BalthazarBarText = $"Remaining {this.GameData.User!.MaxBalthazarPoints - this.GameData.User.CurrentBalthazarPoints} Balthazar Points";
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.BalthazarBarText = $"{(int)((double)this.GameData.User!.CurrentBalthazarPoints / (double)this.GameData.User.MaxBalthazarPoints * 100)}% Balthazar Points";
break;
}
}
private void UpdateVanquishingText()
{
switch (this.liveUpdateableOptions.Value.FocusViewOptions.VanquishingDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.VanquishingText = $"{this.GameData.Session!.FoesKilled} / {(int)this.TotalFoes} Foes Killed";
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.VanquishingText = $"Remaining {this.GameData.Session!.FoesToKill} Foes";
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.VanquishingText = $"{(int)((double)this.GameData.Session!.FoesKilled / (double)this.TotalFoes * 100)}% Foes Killed";
break;
}
}
private void UpdateHealthText()
{
switch (this.liveUpdateableOptions.Value.FocusViewOptions.HealthDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.HealthBarText = $"{(int)this.GameData.MainPlayer!.CurrentHealth} / {(int)this.GameData.MainPlayer.MaxHealth} Health";
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.HealthBarText = $"Remaining {(int)this.GameData.MainPlayer!.CurrentHealth} Health";
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.HealthBarText = $"{(int)(this.GameData.MainPlayer!.CurrentHealth / this.GameData.MainPlayer.MaxHealth * 100)}% Health";
break;
}
}
private void UpdateEnergyText()
{
switch (this.liveUpdateableOptions.Value.FocusViewOptions.EnergyDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.EnergyBarText = $"{(int)this.GameData.MainPlayer!.CurrentEnergy} / {(int)this.GameData.MainPlayer.MaxEnergy} Energy";
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.EnergyBarText = $"Remaining {(int)this.GameData.MainPlayer!.CurrentEnergy} Energy";
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.EnergyBarText = $"{(int)(this.GameData.MainPlayer!.CurrentEnergy / this.GameData.MainPlayer.MaxEnergy * 100)}% Energy";
break;
}
}
private void FocusView_Loaded(object sender, RoutedEventArgs e)
{
this.UpdateRightSideBarsLayout();
this.BrowserAddress = this.liveUpdateableOptions.Value.FocusViewOptions.BrowserUrl;
if (!this.guildwarsMemoryReader.Running)
{
this.guildwarsMemoryReader.Initialize(this.applicationLauncher.RunningGuildwarsProcess!);
}
if (this.guildwarsMemoryReader.TargetProcess?.MainModule?.FileName != this.applicationLauncher.RunningGuildwarsProcess?.MainModule?.FileName)
{
this.guildwarsMemoryReader.Initialize(this.applicationLauncher.RunningGuildwarsProcess!);
}
this.cancellationTokenSource?.Cancel();
this.cancellationTokenSource = new CancellationTokenSource();
TaskExtensions.RunPeriodicAsync(this.UpdateGameData, TimeSpan.Zero, TimeSpan.FromSeconds(1), this.cancellationTokenSource.Token);
@@ -216,20 +317,24 @@ public partial class FocusView : UserControl
{
this.cancellationTokenSource?.Cancel();
this.cancellationTokenSource = null;
this.guildwarsMemoryReader?.Stop();
}
private void ExperienceBar_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
switch (this.liveUpdateableOptions.Value.FocusViewOptions.EnergyDisplay)
switch (this.liveUpdateableOptions.Value.FocusViewOptions.ExperienceDisplay)
{
case Configuration.FocusView.EnergyDisplay.CurrentLevelCurrentAndCurrentLevelMax:
this.liveUpdateableOptions.Value.FocusViewOptions.EnergyDisplay = Configuration.FocusView.EnergyDisplay.TotalCurretAndTotalMax;
case Configuration.FocusView.ExperienceDisplay.CurrentLevelCurrentAndCurrentLevelMax:
this.liveUpdateableOptions.Value.FocusViewOptions.ExperienceDisplay = Configuration.FocusView.ExperienceDisplay.TotalCurretAndTotalMax;
break;
case Configuration.FocusView.EnergyDisplay.TotalCurretAndTotalMax:
this.liveUpdateableOptions.Value.FocusViewOptions.EnergyDisplay = Configuration.FocusView.EnergyDisplay.RemainingUntilNextLevel;
case Configuration.FocusView.ExperienceDisplay.TotalCurretAndTotalMax:
this.liveUpdateableOptions.Value.FocusViewOptions.ExperienceDisplay = Configuration.FocusView.ExperienceDisplay.RemainingUntilNextLevel;
break;
case Configuration.FocusView.EnergyDisplay.RemainingUntilNextLevel:
this.liveUpdateableOptions.Value.FocusViewOptions.EnergyDisplay = Configuration.FocusView.EnergyDisplay.CurrentLevelCurrentAndCurrentLevelMax;
case Configuration.FocusView.ExperienceDisplay.RemainingUntilNextLevel:
this.liveUpdateableOptions.Value.FocusViewOptions.ExperienceDisplay = Configuration.FocusView.ExperienceDisplay.Percentage;
break;
case Configuration.FocusView.ExperienceDisplay.Percentage:
this.liveUpdateableOptions.Value.FocusViewOptions.ExperienceDisplay = Configuration.FocusView.ExperienceDisplay.CurrentLevelCurrentAndCurrentLevelMax;
break;
}
@@ -245,6 +350,9 @@ public partial class FocusView : UserControl
this.liveUpdateableOptions.Value.FocusViewOptions.LuxonPointsDisplay = Configuration.FocusView.PointsDisplay.Remaining;
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.liveUpdateableOptions.Value.FocusViewOptions.LuxonPointsDisplay = Configuration.FocusView.PointsDisplay.Percentage;
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.liveUpdateableOptions.Value.FocusViewOptions.LuxonPointsDisplay = Configuration.FocusView.PointsDisplay.CurrentAndMax;
break;
}
@@ -261,6 +369,9 @@ public partial class FocusView : UserControl
this.liveUpdateableOptions.Value.FocusViewOptions.KurzickPointsDisplay = Configuration.FocusView.PointsDisplay.Remaining;
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.liveUpdateableOptions.Value.FocusViewOptions.KurzickPointsDisplay = Configuration.FocusView.PointsDisplay.Percentage;
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.liveUpdateableOptions.Value.FocusViewOptions.KurzickPointsDisplay = Configuration.FocusView.PointsDisplay.CurrentAndMax;
break;
}
@@ -277,6 +388,9 @@ public partial class FocusView : UserControl
this.liveUpdateableOptions.Value.FocusViewOptions.ImperialPointsDisplay = Configuration.FocusView.PointsDisplay.Remaining;
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.liveUpdateableOptions.Value.FocusViewOptions.ImperialPointsDisplay = Configuration.FocusView.PointsDisplay.Percentage;
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.liveUpdateableOptions.Value.FocusViewOptions.ImperialPointsDisplay = Configuration.FocusView.PointsDisplay.CurrentAndMax;
break;
}
@@ -293,6 +407,9 @@ public partial class FocusView : UserControl
this.liveUpdateableOptions.Value.FocusViewOptions.BalthazarPointsDisplay = Configuration.FocusView.PointsDisplay.Remaining;
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.liveUpdateableOptions.Value.FocusViewOptions.BalthazarPointsDisplay = Configuration.FocusView.PointsDisplay.Percentage;
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.liveUpdateableOptions.Value.FocusViewOptions.BalthazarPointsDisplay = Configuration.FocusView.PointsDisplay.CurrentAndMax;
break;
}
@@ -301,6 +418,63 @@ public partial class FocusView : UserControl
this.UpdateBalthazarText();
}
private void VanquishingBar_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
switch (this.liveUpdateableOptions.Value.FocusViewOptions.VanquishingDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.liveUpdateableOptions.Value.FocusViewOptions.VanquishingDisplay = Configuration.FocusView.PointsDisplay.Remaining;
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.liveUpdateableOptions.Value.FocusViewOptions.VanquishingDisplay = Configuration.FocusView.PointsDisplay.Percentage;
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.liveUpdateableOptions.Value.FocusViewOptions.VanquishingDisplay = Configuration.FocusView.PointsDisplay.CurrentAndMax;
break;
}
this.liveUpdateableOptions.UpdateOption();
this.UpdateVanquishingText();
}
private void HealthBar_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
switch (this.liveUpdateableOptions.Value.FocusViewOptions.HealthDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.liveUpdateableOptions.Value.FocusViewOptions.HealthDisplay = Configuration.FocusView.PointsDisplay.Remaining;
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.liveUpdateableOptions.Value.FocusViewOptions.HealthDisplay = Configuration.FocusView.PointsDisplay.Percentage;
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.liveUpdateableOptions.Value.FocusViewOptions.HealthDisplay = Configuration.FocusView.PointsDisplay.CurrentAndMax;
break;
}
this.liveUpdateableOptions.UpdateOption();
this.UpdateHealthText();
}
private void EnergyBar_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
switch (this.liveUpdateableOptions.Value.FocusViewOptions.EnergyDisplay)
{
case Configuration.FocusView.PointsDisplay.CurrentAndMax:
this.liveUpdateableOptions.Value.FocusViewOptions.EnergyDisplay = Configuration.FocusView.PointsDisplay.Remaining;
break;
case Configuration.FocusView.PointsDisplay.Remaining:
this.liveUpdateableOptions.Value.FocusViewOptions.EnergyDisplay = Configuration.FocusView.PointsDisplay.Percentage;
break;
case Configuration.FocusView.PointsDisplay.Percentage:
this.liveUpdateableOptions.Value.FocusViewOptions.EnergyDisplay = Configuration.FocusView.PointsDisplay.CurrentAndMax;
break;
}
this.liveUpdateableOptions.UpdateOption();
this.UpdateEnergyText();
}
private void CurrentQuest_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (this.GameData.MainPlayer?.Quest?.WikiUrl is string url)
@@ -309,6 +483,14 @@ public partial class FocusView : UserControl
}
}
private void CurrentMap_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (this.GameData.Session?.CurrentMap?.WikiUrl is string url)
{
this.BrowserAddress = url;
}
}
private void Browser_MaximizeClicked(object sender, EventArgs e)
{
this.browserMaximized = !this.browserMaximized;
+1 -1
View File
@@ -10,7 +10,7 @@
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Grid
Background="#A0202020">
Background="#F0212121">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
+4 -3
View File
@@ -12,13 +12,14 @@
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
</UserControl.Resources>
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Background="White" MinHeight="200" MinWidth="400">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Background="#F0212121" MinHeight="200" MinWidth="400">
<StackPanel VerticalAlignment="Center">
<TextBlock Text="{Binding ElementName=_this, Path=Description, Mode=OneWay}" Margin="10, 0, 10, 0" Foreground="Black"
<TextBlock Text="{Binding ElementName=_this, Path=Description, Mode=OneWay}" Margin="10, 0, 10, 0" Foreground="White"
TextWrapping="Wrap" HorizontalAlignment="Center" FontSize="16"></TextBlock>
<ProgressBar Minimum="0" Maximum="100" Value="{Binding ElementName=_this, Path=ProgressValue, Mode=OneWay}" Width="300" Height="20"></ProgressBar>
<controls:OpaqueButton Text="Continue" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="80" Height="25"
Clicked="OpaqueButton_Clicked" Foreground="Black" FontSize="16"></controls:OpaqueButton>
Clicked="OpaqueButton_Clicked" Foreground="White" Highlight="White" HighlightOpacity="0.6" FontSize="16"
Cursor="Hand"></controls:OpaqueButton>
</StackPanel>
</Grid>
</UserControl>
+6 -3
View File
@@ -24,20 +24,23 @@
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<controls:OpaqueButton Text="Launch game" Highlight="White" TransparentBackground="Black" HighlightOpacity="0.3" BackgroundOpacity="0.2"
<controls:OpaqueButton Text="Launch game" Highlight="White" TransparentBackground="#F0212121" HighlightOpacity="0.3" BackgroundOpacity="1"
Foreground="White" FontSize="36" Width="250" Height="60" VerticalAlignment="Bottom"
IsEnabled="{Binding ElementName=_this, Path=LaunchButtonEnabled, Mode=OneWay}"
Clicked="LaunchButton_Clicked" Grid.Row="0" Grid.ColumnSpan="3"
Cursor="Hand"
Visibility="{Binding ElementName=_this, Path=ButtonsVisible, Mode=OneWay, Converter={StaticResource BooleanToVisibility}}"></controls:OpaqueButton>
<controls:OpaqueButton Text="Launch toolbox" Highlight="White" TransparentBackground="Black" HighlightOpacity="0.3" BackgroundOpacity="0.2"
<controls:OpaqueButton Text="Launch toolbox" Highlight="White" TransparentBackground="#F0212121" HighlightOpacity="0.3" BackgroundOpacity="1"
Foreground="White" FontSize="24" Width="200" Height="40" Margin="0, 10, 0, 10"
IsEnabled="{Binding ElementName=_this, Path=LaunchToolboxButtonEnabled, Mode=OneWay}"
Clicked="LaunchToolboxButton_Clicked" Grid.Row="1" Grid.ColumnSpan="2"
Cursor="Hand"
Visibility="{Binding ElementName=_this, Path=ButtonsVisible, Mode=OneWay, Converter={StaticResource BooleanToVisibility}}"></controls:OpaqueButton>
<controls:OpaqueButton Text="Launch texmod" Highlight="White" TransparentBackground="Black" HighlightOpacity="0.3" BackgroundOpacity="0.2"
<controls:OpaqueButton Text="Launch texmod" Highlight="White" TransparentBackground="#F0212121" HighlightOpacity="0.3" BackgroundOpacity="1"
Foreground="White" FontSize="24" Width="200" Height="40" Margin="0, 10, 0, 10"
IsEnabled="{Binding ElementName=_this, Path=LaunchTexmodButtonEnabled, Mode=OneWay}"
Clicked="LaunchTexmodButton_Clicked" Grid.Row="1" Grid.ColumnSpan="2" Grid.Column="1"
Cursor="Hand"
Visibility="{Binding ElementName=_this, Path=ButtonsVisible, Mode=OneWay, Converter={StaticResource BooleanToVisibility}}"></controls:OpaqueButton>
<Grid Grid.Column="2" Margin="10" Visibility="{Binding ElementName=_this, Path=BrowsersEnabled, Mode=OneWay, Converter={StaticResource BooleanToVisibility}}">
<controls:ChromiumBrowserWrapper x:Name="RightWebBrowser"
+1 -1
View File
@@ -13,7 +13,7 @@
<Setter Property="TextWrapping" Value="Wrap"/>
</Style>
</UserControl.Resources>
<Grid Background="#A0202020">
<Grid Background="#F0212121">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
+3 -3
View File
@@ -10,12 +10,12 @@
x:Name="_this"
DataContextChanged="UserControl_DataContextChanged"
d:DesignHeight="450" d:DesignWidth="800">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Background="White" MinHeight="200" MinWidth="400">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Background="#F0212121" MinHeight="200" MinWidth="400">
<StackPanel VerticalAlignment="Center">
<TextBlock Text="{Binding ElementName=_this, Path=Description, Mode=OneWay}" Margin="10, 0, 10, 0" Foreground="Black"
<TextBlock Text="{Binding ElementName=_this, Path=Description, Mode=OneWay}" Margin="10, 0, 10, 0" Foreground="White"
TextWrapping="Wrap" HorizontalAlignment="Center" FontSize="16"></TextBlock>
<controls:OpaqueButton Text="Ok" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
Clicked="OpaqueButton_Clicked" Foreground="Black" FontSize="16"></controls:OpaqueButton>
Clicked="OpaqueButton_Clicked" Foreground="White" Highlight="White" HighlightOpacity="0.6" FontSize="16" Cursor="Hand"></controls:OpaqueButton>
</StackPanel>
</Grid>
</UserControl>
+5 -5
View File
@@ -7,21 +7,21 @@
mc:Ignorable="d"
xmlns:controls="clr-namespace:Daybreak.Controls"
d:DesignHeight="450" d:DesignWidth="800">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Background="White" MinHeight="200" MinWidth="400">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Background="#F0212121" MinHeight="200" MinWidth="400">
<StackPanel VerticalAlignment="Center" Orientation="Vertical">
<TextBlock Text="{Binding MessageToUser}" Margin="10, 0, 10, 0" Foreground="Black"
<TextBlock Text="{Binding MessageToUser}" Margin="10, 0, 10, 0" Foreground="White"
TextWrapping="Wrap" HorizontalAlignment="Center" FontSize="16"></TextBlock>
<TextBlock Text="Do you want to restart the application with administrator rights?" HorizontalAlignment="Center"
FontSize="16" Foreground="Black" Margin="10, 0, 10, 10" TextWrapping="Wrap"></TextBlock>
FontSize="16" Foreground="White" Margin="10, 0, 10, 10" TextWrapping="Wrap"></TextBlock>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<controls:OpaqueButton Text="Yes" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
Clicked="YesButton_Clicked" Foreground="Black" FontSize="16"></controls:OpaqueButton>
Clicked="YesButton_Clicked" Foreground="White" Highlight="White" HighlightOpacity="0.6" FontSize="16" Cursor="Hand"></controls:OpaqueButton>
<controls:OpaqueButton Text="No" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
Clicked="NoButton_Clicked" Foreground="Black" Grid.Column="1" FontSize="16"></controls:OpaqueButton>
Clicked="NoButton_Clicked" Foreground="White" Highlight="White" HighlightOpacity="0.6" Grid.Column="1" FontSize="16" Cursor="Hand"></controls:OpaqueButton>
</Grid>
</StackPanel>
</Grid>
+1 -1
View File
@@ -8,7 +8,7 @@
mc:Ignorable="d"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="#A0202020">
<Grid Background="#F0212121">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
+1 -1
View File
@@ -67,7 +67,7 @@
</ResourceDictionary>
</UserControl.Resources>
<ScrollViewer VerticalScrollBarVisibility="Auto">
<Grid Background="#A0202020">
<Grid Background="#F0212121">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
+1
View File
@@ -86,6 +86,7 @@ public partial class SettingsView : System.Windows.Controls.UserControl
currentConfig.PlaceShortcut = this.ShortcutPlaced;
currentConfig.AutoCheckUpdate = this.AutoCheckUpdate;
this.liveUpdateableOptions.UpdateOption();
this.viewManager.ShowView<LauncherView>();
}
private void ToolboxFilePickerGlyph_Clicked(object sender, EventArgs e)
+3 -3
View File
@@ -12,13 +12,13 @@
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
</UserControl.Resources>
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Background="White" MinHeight="200" MinWidth="400">
<Grid VerticalAlignment="Center" HorizontalAlignment="Center" Background="#F0212121" MinHeight="200" MinWidth="400">
<StackPanel VerticalAlignment="Center">
<TextBlock Text="{Binding ElementName=_this, Path=Description, Mode=OneWay}" Margin="10, 0, 10, 0" Foreground="Black"
<TextBlock Text="{Binding ElementName=_this, Path=Description, Mode=OneWay}" Margin="10, 0, 10, 0" Foreground="White"
TextWrapping="Wrap" HorizontalAlignment="Center" FontSize="16"></TextBlock>
<ProgressBar Minimum="0" Maximum="100" Value="{Binding ElementName=_this, Path=ProgressValue, Mode=OneWay}" Width="300" Height="20"></ProgressBar>
<controls:OpaqueButton Text="Ok" BackgroundOpacity="0.2" TransparentBackground="Gray" HorizontalAlignment="Center" Margin="10" Width="50" Height="25"
Clicked="OpaqueButton_Clicked" Foreground="Black" FontSize="16"
Clicked="OpaqueButton_Clicked" Foreground="White" FontSize="16" Highlight="White" HighlightOpacity="0.6"
Visibility="{Binding ElementName=_this, Path=ContinueButtonEnabled, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"></controls:OpaqueButton>
</StackPanel>
</Grid>
+8 -1
View File
@@ -8,7 +8,10 @@
mc:Ignorable="d"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<Grid Background="#A0202020">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
</UserControl.Resources>
<Grid Background="#F0212121">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
@@ -40,5 +43,9 @@
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
<controls:CircularLoadingWidget Grid.RowSpan="4"
Width="100"
Height="100"
Visibility="{Binding ElementName=_this, Path=Loading, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"></controls:CircularLoadingWidget>
</Grid>
</UserControl>
@@ -25,6 +25,9 @@ public partial class VersionManagementView : UserControl
[GenerateDependencyProperty]
private Version currentVersion;
[GenerateDependencyProperty]
private bool loading;
public ObservableCollection<Version> Versions { get; } = new();
public VersionManagementView(
@@ -41,7 +44,9 @@ public partial class VersionManagementView : UserControl
private async void LoadVersionList()
{
this.Loading = true;
this.Versions.ClearAnd().AddRange((await this.applicationUpdater.GetVersions()).Reverse());
this.Loading = false;
}
private void CurrentVersion_Clicked(object sender, EventArgs e)