Compare commits

...
41 Commits
Author SHA1 Message Date
amacocianandGitHub d6623a8646 Automatically add downloaded tpfs to mod list (#228)
* Automatically add downloaded tpfs to mod list
Closes #227

* Increment version
2023-05-03 16:33:25 +00:00
amacocianandGitHub 403ba51a7d Add possibility to download tpf files from the uMod browser page (#226)
* Add possibility to download tpf files from the uMod browser page
* Organize menu by sections
* Limit logs to improve performance
* Fix screenmanageroptions name
* Fix focusview default webpage
2023-05-03 15:04:40 +00:00
amacocianandGitHub b185a606e6 Integrate with mods (#223)
* Integrate with GWToolboxpp and uMod
* Separate configuration into multiple extensible parts
* Generalize downloading service
2023-05-03 11:10:18 +00:00
amacocianandGitHub 342a9a4826 Add more minimap icons (#221)
Support for team colored icons
2023-05-01 11:36:22 +00:00
amacocianandGitHub 2df2e8a65e Added support for multi-color SVG icons (#219) 2023-04-30 16:04:09 +00:00
amacocianandGitHub 2d5e9231a9 Minimap improvements (#217)
Map icons have overload for minimum size
Pathfinding now finds paths even when start/end points are not on the map
Map can now draw SVGs
2023-04-30 12:31:11 +00:00
amacocianandGitHub 818e2bf1d0 Drawing bugfixes (#210) 2023-04-10 18:47:03 +02:00
amacocianandGitHub f0a876f7cc Rework minimap drawing (#208)
Setup extensible drawing modules
2023-04-10 13:57:35 +00:00
amacocianandGitHub 3fae8f91cb Add support for map icons (#207)
Add res-shrine icons to minimap
2023-04-09 20:26:15 +00:00
amacocianandGitHub 8b08056ca7 Show targeted npc details (#206) 2023-04-09 18:30:25 +00:00
amacocianandGitHub 39eaf53deb Setup startup actions (#204)
Rename temp installer on startup
2023-04-09 16:21:11 +00:00
amacocianandGitHub 4674d1d7e5 Add support for npc types and names (#200)
* Introduce npcs and support for npc names

* Update version
2023-04-07 10:34:11 +00:00
amacocianandGitHub b8b5cc8b1d Optimize pathfinding (#198)
* Optimize pathfinding
Simplified initial search to no longer take distance into account
Metrics for pathfinding

* Increment version
2023-04-05 15:30:48 +00:00
amacocianandGitHub dc987233a7 Retry pathfinding loading on failure (#197) 2023-04-04 21:35:16 +00:00
amacocianandGitHub 266b400c22 Pathfinding v1 (#196)
* Pathfinding algorithm for minimap

* Fix adjacency lists

* Better adjancency list generation

* Improve pathfinding algorithm

* Some other tries

* Pathfinding algorithm for minimap

* Fix adjacency lists

* Better adjancency list generation

* Improve pathfinding algorithm

* Some other tries

* Improvements to the minimap and pathing

* Improve pathfinder

* Draw path forests

* Implement working pathfinding solution
2023-04-04 20:43:14 +00:00
amacocianandGitHub a4ac083fd0 General client improvements (#195)
Hit-target for off-screen quest markers
Save window position on every move instead of only on closing
2023-04-03 20:21:12 +00:00
amacocianandGitHub 320dd16838 Show objective direction for loaded quests (#194) 2023-04-02 19:22:17 +00:00
amacocianandGitHub 5c05c636bc Add quest marker functionality to the minimap (#192) 2023-04-02 14:57:49 +00:00
amacocianandGitHub 7103a8a83a Show party member context (#190)
Fix one Map id
2023-03-25 18:30:29 +00:00
amacocianandGitHub ad749d801a Minimap improvements (#189)
Closes #187
Closes #188

Minimap performance improvements
Add context menues for main player and living entities
2023-03-25 08:36:15 +00:00
amacocianandGitHub 747e9a0490 Minimap improvements (#186)
Closes #185
Closes #184
Closes #183
Show player information when right clicking on minimap
Allow minimap fullscreen
Decouple scrolling from memory reading frequency
Ignore task cancelled exceptions
2023-03-24 22:39:52 +00:00
amacocianandGitHub 836402bd0f Draw player position history on the minimap (#182) 2023-03-21 15:19:25 +00:00
amacocianandGitHub a3d5e1ce1f Add another check for corrupted images (#181)
* Add another check for corrupted images

* Check validity of icons on startup
Improve screen position saving
2023-03-21 08:45:22 +00:00
f2cc59a4ab Daybreak 0.9.8.18 (#179)
* Minimap implementation

* Fix minimap

* Add debounce logic to entities positions

* Cleanup

* Fix x64 build memory reading
Move build to x64
Fix entity debouncing
Added some backstops to prevent reading corrupted memory

* Add minimap zooming and panning functionality
Add main window position saving procedure

* Increment version

---------

Co-authored-by: Alex Macocian <amacocian@hotmail.com>
2023-03-20 21:38:53 +02:00
amacocianandGitHub 6ea77f7e05 Bugfixes and improvements (#176)
Closes #175
Closes #174
Closes #173
2023-03-09 19:53:28 +00:00
amacocianandGitHub 5ea8df7cb9 Bugfixes to memory reader (#172) 2023-03-08 20:17:58 +00:00
amacocianandGitHub c70e8ec90d Implement Guildwars downloader (#171) 2023-03-08 13:24:14 +00:00
amacocianandGitHub 210e3a8cb1 Add title data to memory retrieval (#169)
Add metrics for diagnostics
Add metrics view
2023-03-07 17:20:20 +00:00
amacocianandGitHub a0c3bb094c Performance improvements (#168)
* Performance improvements and bugfixes

* Increment version

* Better builds links

* Unify loading widget size
2023-03-05 08:55:21 +01:00
amacocianandGitHub b4d932692a Reduce words in FocusView character line (#167) 2023-03-04 17:18:03 +00:00
amacocianandGitHub 667534a07a Fix version parsing (#166) 2023-03-04 18:06:53 +01:00
amacocianandGitHub 159928f24b Integrate builds with Focus view (#164)
* Integrate builds with Focus view
Detect when GameData is invalid
Detect when reading memory is faulty
Show current character data in Focus View
Closes #161
Closes #162
Closes #163

* Increment version

* Bugfix version comparison script
2023-03-04 17:21:08 +01:00
amacocianandGitHub 8a91ded610 Fix buildtemplate skill removal (#160)
Closes #159
2023-03-03 22:28:27 +00:00
amacocianandGitHub 4226b7eb32 Improve performance of buildtemplateview (#158) 2023-03-03 21:50:25 +00:00
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
amacocianandGitHub a4dc0d65cf Implement Focus View (#144)
* Implement Focus View
Read extra information from guildwars

* Prever focusview browser from being disposed
2023-03-01 18:42:53 +00:00
amacocianandGitHub 85e017423f #142 - Fix Vanguard Quests Missing (#143)
Closes #142
2023-02-28 18:24:37 +00:00
337 changed files with 21723 additions and 1978 deletions
+5 -2
View File
@@ -6,8 +6,8 @@ csharp_indent_labels = one_less_than_current
csharp_using_directive_placement = outside_namespace:error
csharp_prefer_simple_using_statement = true:error
csharp_prefer_braces = true:silent
csharp_style_namespace_declarations = block_scoped:silent
csharp_style_prefer_method_group_conversion = true:silent
csharp_style_namespace_declarations = file_scoped:error
csharp_style_prefer_method_group_conversion = true:suggestion
csharp_style_expression_bodied_methods = false:silent
csharp_style_expression_bodied_constructors = false:silent
csharp_style_expression_bodied_operators = false:silent
@@ -46,6 +46,9 @@ csharp_style_prefer_extended_property_pattern = true:suggestion
csharp_style_var_for_built_in_types = false:silent
csharp_style_var_when_type_is_apparent = false:silent
csharp_style_var_elsewhere = false:silent
csharp_style_prefer_top_level_statements = true:suggestion
csharp_style_prefer_utf8_string_literals = true:suggestion
csharp_style_prefer_readonly_struct = true:suggestion
[*.{cs,vb}]
#### Naming styles ####
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
strategy:
matrix:
targetplatform: [x86]
targetplatform: [x64]
runs-on: windows-latest
+1 -1
View File
@@ -19,7 +19,7 @@ jobs:
strategy:
matrix:
targetplatform: [x86]
targetplatform: [x64]
runs-on: windows-latest
+1 -1
View File
@@ -5,7 +5,7 @@
<TargetFramework>net6.0</TargetFramework>
<ImplicitUsings>enable</ImplicitUsings>
<Nullable>enable</Nullable>
<Platforms>AnyCPU;x86</Platforms>
<Platforms>AnyCPU;x86;x64</Platforms>
</PropertyGroup>
</Project>
+1 -1
View File
@@ -3,7 +3,7 @@
<PropertyGroup>
<TargetFramework>net6.0-windows</TargetFramework>
<IsPackable>false</IsPackable>
<Platforms>AnyCPU;x86</Platforms>
<Platforms>AnyCPU;x86;x64</Platforms>
</PropertyGroup>
<ItemGroup>
+51
View File
@@ -132,4 +132,55 @@ public class VersionTests
action.Should().NotThrow<ArgumentException>();
}
}
[DataRow("v0.1.0.0.1", "v0.1.0.0.1", true)]
[DataRow("v0.1.0.0.1.0.0.0.0", "v0.1.0.0.1", true)]
[DataRow("v0.1.0", "v0.1", true)]
[DataRow("0.1.0", "0.1", true)]
[DataRow("v0.1.0.0.0", "v0.1", true)]
[DataRow("0.1.0.0.0", "0.1", true)]
[DataRow("v0", "v0", true)]
[DataRow("0", "0", true)]
[DataRow("v0.1", "v0.1", true)]
[DataRow("0.1-release", "0.1-release", true)]
[DataRow("v0.1-release", "v0.1-release", true)]
[DataRow("v0.1.0.0", "v0.1.0.0.1", false)]
[DataRow("v0.1.0.0.1", "v0.1.0.0", false)]
[DataRow("v0.9.8.9", "v0.9.8.10", false)]
[DataRow("0.1-release", "0.2-release", false)]
[DataRow("v1.1-release", "v0.1-release", false)]
[TestMethod]
public void Equals(string version, string version2, bool result)
{
var parsedVersion1 = Version.Parse(version);
var parsedVersion2 = Version.Parse(version2);
parsedVersion1.Equals(parsedVersion2).Should().Be(result);
}
[DataRow("v0.1.0.0.1", "v0.1.0.0.1", 0)]
[DataRow("v0.1.0.0.1.0.0.0.0", "v0.1.0.0.1", 0)]
[DataRow("v0.1.0", "v0.1", 0)]
[DataRow("0.1.0", "0.1", 0)]
[DataRow("v0.1.0.0.0", "v0.1", 0)]
[DataRow("0.1.0.0.0", "0.1", 0)]
[DataRow("v0", "v0", 0)]
[DataRow("0", "0", 0)]
[DataRow("v0.1", "v0.1", 0)]
[DataRow("0.1-release", "0.1-release", 0)]
[DataRow("v0.1-release", "v0.1-release", 0)]
[DataRow("v0.1.0.0", "v0.1.0.0.1", -1)]
[DataRow("v0.1.0.0.1", "v0.1.0.0", 1)]
[DataRow("v0.9.8.9", "v0.9.8.10", -1)]
[DataRow("v0.9.8.10", "v0.9.8.9", 1)]
[DataRow("0.1-release", "0.2-release", -1)]
[DataRow("v1.1-release", "v0.1-release", 1)]
[TestMethod]
public void CompareTo(string version, string version2, int comparisonResult)
{
var parsedVersion1 = Version.Parse(version);
var parsedVersion2 = Version.Parse(version2);
parsedVersion1.CompareTo(parsedVersion2).Should().Be(comparisonResult);
}
}
@@ -1,62 +0,0 @@
using Daybreak.Configuration;
using Daybreak.Services.Configuration;
using Daybreak.Services.Options;
using FluentAssertions;
using Microsoft.VisualStudio.TestTools.UnitTesting;
using Moq;
using System;
namespace Daybreak.Tests.Services;
[TestClass]
public class ApplicationConfigurationOptionsManagerTests
{
private ApplicationConfigurationOptionsManager applicationConfigurationOptionsManager;
[TestInitialize]
public void TestInitialize()
{
var configurationManagerMock = new Mock<IConfigurationManager>();
configurationManagerMock
.Setup(u => u.GetConfiguration())
.Returns(new ApplicationConfiguration());
this.applicationConfigurationOptionsManager = new ApplicationConfigurationOptionsManager(configurationManagerMock.Object);
}
[TestMethod]
public void GetApplicationConfiguration_ReturnsObject()
{
var config = this.applicationConfigurationOptionsManager.GetOptions<ApplicationConfiguration>();
config.Should().NotBeNull();
}
[TestMethod]
public void GetOtherOptions_ThrowsInvalidOperationException()
{
var action = new Action(() =>
{
this.applicationConfigurationOptionsManager.GetOptions<object>();
});
action.Should().Throw<InvalidOperationException>();
}
[TestMethod]
public void UpdateOptions_OnApplicationConfiguration_Succeeds()
{
this.applicationConfigurationOptionsManager.UpdateOptions(new ApplicationConfiguration());
}
[TestMethod]
public void UpdateOptions_OnOthers_ThrowsInvalidOperationException()
{
var action = new Action(() =>
{
this.applicationConfigurationOptionsManager.UpdateOptions(new object());
});
action.Should().Throw<InvalidOperationException>();
}
}
@@ -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);
+14
View File
@@ -34,33 +34,47 @@ EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Debug|x64 = Debug|x64
Debug|x86 = Debug|x86
Release|Any CPU = Release|Any CPU
Release|x64 = Release|x64
Release|x86 = Release|x86
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|Any CPU.Build.0 = Debug|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|x64.ActiveCfg = Debug|x64
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|x64.Build.0 = Debug|x64
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|x86.ActiveCfg = Debug|x86
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Debug|x86.Build.0 = Debug|x86
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|Any CPU.ActiveCfg = Release|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|Any CPU.Build.0 = Release|Any CPU
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|x64.ActiveCfg = Release|x64
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|x64.Build.0 = Release|x64
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|x86.ActiveCfg = Release|x86
{AA45C2B1-8BD0-466C-9271-699F168905AF}.Release|x86.Build.0 = Release|x86
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Debug|Any CPU.Build.0 = Debug|Any CPU
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Debug|x64.ActiveCfg = Debug|x64
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Debug|x64.Build.0 = Debug|x64
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Debug|x86.ActiveCfg = Debug|x86
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Debug|x86.Build.0 = Debug|x86
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Release|Any CPU.ActiveCfg = Release|Any CPU
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Release|Any CPU.Build.0 = Release|Any CPU
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Release|x64.ActiveCfg = Release|x64
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Release|x64.Build.0 = Release|x64
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Release|x86.ActiveCfg = Release|x86
{C911C1C2-6566-419C-89C7-F802EB3FE709}.Release|x86.Build.0 = Release|x86
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Debug|Any CPU.Build.0 = Debug|Any CPU
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Debug|x64.ActiveCfg = Debug|x64
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Debug|x64.Build.0 = Debug|x64
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Debug|x86.ActiveCfg = Debug|x86
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Debug|x86.Build.0 = Debug|x86
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Release|Any CPU.ActiveCfg = Release|Any CPU
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Release|Any CPU.Build.0 = Release|Any CPU
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Release|x64.ActiveCfg = Release|x64
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Release|x64.Build.0 = Release|x64
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Release|x86.ActiveCfg = Release|x86
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Release|x86.Build.0 = Release|x86
EndGlobalSection
@@ -0,0 +1,9 @@
using System;
namespace Daybreak.Attributes;
[AttributeUsage(AttributeTargets.Class)]
public sealed class OptionsNameAttribute : Attribute
{
public string? Name { get; init; }
}
@@ -1,44 +0,0 @@
using Daybreak.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace Daybreak.Configuration;
public sealed class ApplicationConfiguration
{
[JsonProperty("SetGuildwarsWindowSizeOnLaunch")]
public bool SetGuildwarsWindowSizeOnLaunch { get; set; }
[JsonProperty("DesiredGuildwarsScreen")]
public int DesiredGuildwarsScreen { get; set; }
[JsonProperty("BrowsersEnabled")]
public bool BrowsersEnabled { get; set; } = true;
[JsonProperty("ToolboxPath")]
public string? ToolboxPath { get; set; }
[JsonProperty("TexmodPath")]
public string? TexmodPath { get; set; }
[JsonProperty("ToolboxAutoLaunch")]
public bool ToolboxAutoLaunch { get; set; }
[JsonProperty("LeftBrowserDefault")]
public string? LeftBrowserDefault { get; set; } = "https://gwpvx.fandom.com/wiki/PvX_wiki";
[JsonProperty("RightBrowserDefault")]
public string? RightBrowserDefault { get; set; } = "https://wiki.guildwars.com/wiki/Quick_access_links";
[JsonProperty("GuildwarsPaths")]
public List<GuildwarsPath> GuildwarsPaths { get; set; } = new();
[JsonProperty("ProtectedLoginCredentials")]
public List<ProtectedLoginCredentials> ProtectedLoginCredentials { get; set; } = new();
[JsonProperty("AddressBarReadonly")]
public bool AddressBarReadonly { get; set; } = true;
[JsonProperty("ExperimentalFeatures")]
public ExperimentalFeatures ExperimentalFeatures { get; set; } = new();
[JsonProperty("ShortcutLocation")]
public string? ShortcutLocation { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
[JsonProperty("PlaceShortcut")]
public bool PlaceShortcut { get; set; }
[JsonProperty("AutoCheckUpdate")]
public bool AutoCheckUpdate { get; set; } = true;
[JsonProperty("ProtectedGraphAccessToken")]
public string? ProtectedGraphAccessToken { get; set; }
[JsonProperty("ProtectedGraphRefreshToken")]
public string? ProtectedGraphRefreshToken { get; set; }
}
@@ -1,25 +0,0 @@
using Daybreak.Models;
using Newtonsoft.Json;
using System.Collections.Generic;
namespace Daybreak.Configuration;
public sealed class ExperimentalFeatures
{
[JsonProperty("MultiLaunchSupport")]
public bool MultiLaunchSupport { get; set; }
[JsonProperty("ToolboxAutoLaunchDelay")]
public int ToolboxAutoLaunchDelay { get; set; } = 5000;
[JsonProperty("DynamicBuildLoading")]
public bool DynamicBuildLoading { get; set; } = true;
[JsonProperty("LaunchGuildwarsAsCurrentUser")]
public bool LaunchGuildwarsAsCurrentUser { get; set; } = true;
[JsonProperty("CanInterceptKeys")]
public bool CanInterceptKeys { get; set; }
[JsonProperty("DownloadIcons")]
public bool DownloadIcons { get; set; }
[JsonProperty("FocusViewEnabled")]
public bool FocusViewEnabled { get; set; }
[JsonProperty("Macros")]
public List<KeyMacro> Macros { get; set; } = new();
}
@@ -0,0 +1,9 @@
namespace Daybreak.Configuration.FocusView;
public enum ExperienceDisplay
{
TotalCurretAndTotalMax,
CurrentLevelCurrentAndCurrentLevelMax,
RemainingUntilNextLevel,
Percentage
}
@@ -0,0 +1,8 @@
namespace Daybreak.Configuration.FocusView;
public enum PointsDisplay
{
CurrentAndMax,
Remaining,
Percentage
}
@@ -0,0 +1,17 @@
using Daybreak.Attributes;
using Newtonsoft.Json;
namespace Daybreak.Configuration.Options;
[OptionsName(Name = "Browser")]
public sealed class BrowserOptions
{
[JsonProperty(nameof(Enabled))]
public bool Enabled { get; set; } = true;
[JsonProperty(nameof(AddressBarReadonly))]
public bool AddressBarReadonly { get; set; } = true;
[JsonProperty(nameof(DynamicBuildLoading))]
public bool DynamicBuildLoading { get; set; } = true;
}
@@ -0,0 +1,13 @@
using Daybreak.Attributes;
using Newtonsoft.Json;
namespace Daybreak.Configuration.Options;
[OptionsName(Name = "Build Synchronization")]
public sealed class BuildSynchronizationOptions
{
[JsonProperty(nameof(ProtectedGraphAccessToken))]
public string? ProtectedGraphAccessToken { get; set; }
[JsonProperty(nameof(ProtectedGraphRefreshToken))]
public string? ProtectedGraphRefreshToken { get; set; }
}
@@ -0,0 +1,45 @@
using Daybreak.Attributes;
using Daybreak.Configuration.FocusView;
using Newtonsoft.Json;
namespace Daybreak.Configuration.Options;
[OptionsName(Name = "Focus View")]
public sealed class FocusViewOptions
{
[JsonProperty(nameof(Enabled))]
public bool Enabled { get; set; }
[JsonProperty(nameof(MemoryReaderFrequency))]
public double MemoryReaderFrequency { get; set; } = 16;
[JsonProperty(nameof(EnablePathfinding))]
public bool EnablePathfinding { get; set; } = true;
[JsonProperty(nameof(ExperienceDisplay))]
public ExperienceDisplay ExperienceDisplay { get; set; }
[JsonProperty(nameof(KurzickPointsDisplay))]
public PointsDisplay KurzickPointsDisplay { get; set; }
[JsonProperty(nameof(LuxonPointsDisplay))]
public PointsDisplay LuxonPointsDisplay { get; set; }
[JsonProperty(nameof(BalthazarPointsDisplay))]
public PointsDisplay BalthazarPointsDisplay { get; set; }
[JsonProperty(nameof(ImperialPointsDisplay))]
public PointsDisplay ImperialPointsDisplay { get; set; }
[JsonProperty(nameof(VanquishingDisplay))]
public PointsDisplay VanquishingDisplay { get; set; }
[JsonProperty(nameof(HealthDisplay))]
public PointsDisplay HealthDisplay { get; set; }
[JsonProperty(nameof(EnergyDisplay))]
public PointsDisplay EnergyDisplay { get; set; }
[JsonProperty(nameof(BrowserUrl))]
public string? BrowserUrl { get; set; } = "https://wiki.guildwars.com/wiki/Main_Page";
}
@@ -0,0 +1,41 @@
using Daybreak.Attributes;
using Daybreak.Models;
using Newtonsoft.Json;
using System;
using System.Collections.Generic;
namespace Daybreak.Configuration.Options;
[OptionsName(Name = "Launcher")]
public sealed class LauncherOptions
{
[JsonProperty(nameof(SetGuildwarsWindowSizeOnLaunch))]
public bool SetGuildwarsWindowSizeOnLaunch { get; set; }
[JsonProperty(nameof(DesiredGuildwarsScreen))]
public int DesiredGuildwarsScreen { get; set; }
[JsonProperty(nameof(LaunchGuildwarsAsCurrentUser))]
public bool LaunchGuildwarsAsCurrentUser { get; set; } = true;
[JsonProperty(nameof(GuildwarsPaths))]
public List<GuildwarsPath> GuildwarsPaths { get; set; } = new();
[JsonProperty(nameof(ProtectedLoginCredentials))]
public List<ProtectedLoginCredentials> ProtectedLoginCredentials { get; set; } = new();
[JsonProperty(nameof(ShortcutLocation))]
public string? ShortcutLocation { get; set; } = Environment.GetFolderPath(Environment.SpecialFolder.Desktop);
[JsonProperty(nameof(PlaceShortcut))]
public bool PlaceShortcut { get; set; }
[JsonProperty(nameof(AutoCheckUpdate))]
public bool AutoCheckUpdate { get; set; } = true;
[JsonProperty(nameof(MultiLaunchSupport))]
public bool MultiLaunchSupport { get; set; }
[JsonProperty(nameof(DownloadIcons))]
public bool DownloadIcons { get; set; }
}
@@ -0,0 +1,14 @@
using Daybreak.Attributes;
namespace Daybreak.Configuration.Options;
[OptionsName(Name = "Screen Manager")]
public sealed class ScreenManagerOptions
{
public double X { get; set; }
public double Y { get; set; }
public double Width { get; set; }
public double Height { get; set; }
public double DpiX { get; set; }
public double DpiY { get; set; }
}
@@ -0,0 +1,14 @@
using Daybreak.Attributes;
using Newtonsoft.Json;
namespace Daybreak.Configuration.Options;
[OptionsName(Name = "Toolbox")]
public sealed class ToolboxOptions
{
[JsonProperty(nameof(Path))]
public string? Path { get; set; }
[JsonProperty(nameof(Enabled))]
public bool Enabled { get; set; }
}
@@ -0,0 +1,17 @@
using Daybreak.Attributes;
using Newtonsoft.Json;
namespace Daybreak.Configuration.Options;
[OptionsName(Name = "uMod")]
public sealed class UModOptions
{
[JsonProperty(nameof(Path))]
public string? Path { get; set; }
[JsonProperty(nameof(Enabled))]
public bool Enabled { get; set; }
[JsonProperty(nameof(AutoEnableMods))]
public bool AutoEnableMods { get; set; }
}
+121 -7
View File
@@ -1,7 +1,6 @@
using Daybreak.Services.ApplicationLauncher;
using Daybreak.Services.Bloogum;
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.Configuration;
using Daybreak.Services.Credentials;
using Daybreak.Services.IconRetrieve;
using Daybreak.Services.Logging;
@@ -24,23 +23,44 @@ using Microsoft.CorrelationVector;
using System.Logging;
using Daybreak.Services.Updater.PostUpdate;
using System.Core.Extensions;
using Daybreak.Services.Updater.PostUpdate.Actions;
using Daybreak.Services.Graph;
using Microsoft.Extensions.DependencyInjection;
using Daybreak.Services.Navigation;
using Daybreak.Services.Onboarding;
using Daybreak.Services.Menu;
using Daybreak.Services.Scanner;
using Daybreak.Services.Experience;
using Daybreak.Services.Metrics;
using Daybreak.Services.Monitoring;
using System.Net.Http.Headers;
using Daybreak.Services.Downloads;
using Daybreak.Services.ExceptionHandling;
using Daybreak.Services.Pathfinding;
using Daybreak.Services.Startup;
using Daybreak.Services.Startup.Actions;
using Daybreak.Services.Drawing;
using Daybreak.Services.Drawing.Modules.Entities;
using Daybreak.Services.Drawing.Modules.MapIcons;
using Daybreak.Services.Drawing.Modules;
using Daybreak.Services.Guildwars;
using Daybreak.Configuration.Options;
using System.Configuration;
using Daybreak.Services.UMod;
using Daybreak.Services.Toolbox;
using Daybreak.Views.Onboarding.UMod;
using Daybreak.Views.Onboarding.Toolbox;
namespace Daybreak.Configuration;
public static class ProjectConfiguration
{
private const string DaybreakUserAgent = "Daybreak";
public static void RegisterResolvers(IServiceManager serviceManager)
{
serviceManager.ThrowIfNull();
serviceManager.RegisterOptionsManager<ApplicationConfigurationOptionsManager>();
serviceManager.RegisterOptionsManager<OptionsManager>();
serviceManager.RegisterResolver(new LoggerResolver());
serviceManager
.RegisterHttpClient<ApplicationUpdater>()
@@ -49,6 +69,7 @@ public static class ProjectConfiguration
var logger = sp.GetService<ILogger<ApplicationUpdater>>();
return new LoggingHttpMessageHandler(logger!) { InnerHandler = new HttpClientHandler() };
})
.WithDefaultRequestHeadersSetup(SetupDaybreakUserAgent)
.Build()
.RegisterHttpClient<BloogumClient>()
.WithMessageHandler(sp =>
@@ -56,6 +77,7 @@ public static class ProjectConfiguration
var logger = sp.GetService<ILogger<BloogumClient>>();
return new LoggingHttpMessageHandler(logger!) { InnerHandler = new HttpClientHandler() };
})
.WithDefaultRequestHeadersSetup(SetupDaybreakUserAgent)
.Build()
.RegisterHttpClient<GraphClient>()
.WithMessageHandler(sp =>
@@ -63,6 +85,15 @@ public static class ProjectConfiguration
var logger = sp.GetService<ILogger<GraphClient>>();
return new LoggingHttpMessageHandler(logger!) { InnerHandler = new HttpClientHandler() };
})
.WithDefaultRequestHeadersSetup(SetupDaybreakUserAgent)
.Build()
.RegisterHttpClient<DownloadService>()
.WithMessageHandler(sp =>
{
var logger = sp.GetService<ILogger<GraphClient>>();
return new LoggingHttpMessageHandler(logger!) { InnerHandler = new HttpClientHandler() };
})
.WithDefaultRequestHeadersSetup(SetupDaybreakUserAgent)
.Build();
}
@@ -86,6 +117,8 @@ public static class ProjectConfiguration
services.AddScoped((sp) => new ScopeMetadata(new CorrelationVector()));
services.AddSingleton<ViewManager>();
services.AddSingleton<ProcessorUsageMonitor>();
services.AddSingleton<MemoryUsageMonitor>();
services.AddSingleton<IViewManager, ViewManager>(sp => sp.GetRequiredService<ViewManager>());
services.AddSingleton<IViewProducer, ViewManager>(sp => sp.GetRequiredService<ViewManager>());
services.AddSingleton<PostUpdateActionManager>();
@@ -94,12 +127,15 @@ public static class ProjectConfiguration
services.AddSingleton<IPostUpdateActionProvider>(sp => sp.GetRequiredService<PostUpdateActionManager>());
services.AddSingleton<IMenuService, MenuService>();
services.AddSingleton<IMenuServiceInitializer, MenuService>(sp => sp.GetRequiredService<IMenuService>().As<MenuService>());
services.AddSingleton<IConfigurationManager, ConfigurationManager>();
services.AddSingleton<ILiteDatabase, LiteDatabase>(sp => new LiteDatabase("Daybreak.db"));
services.AddSingleton<IMutexHandler, MutexHandler>();
services.AddSingleton<IShortcutManager, ShortcutManager>();
services.AddSingleton<IIconBrowser, IconBrowser>();
services.AddSingleton<IIconDownloader, IconDownloader>();
services.AddSingleton<IMetricsService, MetricsService>();
services.AddSingleton<IStartupActionProducer, StartupActionManager>();
services.AddSingleton<IOptionsProducer, OptionsManager>(sp => sp.GetRequiredService<IOptionsManager>().As<OptionsManager>());
services.AddSingleton<IOptionsUpdateHook, OptionsManager>(sp => sp.GetRequiredService<IOptionsManager>().As<OptionsManager>());
services.AddScoped<ICredentialManager, CredentialManager>();
services.AddScoped<IApplicationLauncher, ApplicationLauncher>();
services.AddScoped<IScreenshotProvider, ScreenshotProvider>();
@@ -113,6 +149,17 @@ public static class ProjectConfiguration
services.AddScoped<IOnboardingService, OnboardingService>();
services.AddScoped<IGuildwarsMemoryReader, GuildwarsMemoryReader>();
services.AddScoped<IMemoryScanner, MemoryScanner>();
services.AddScoped<IExperienceCalculator, ExperienceCalculator>();
services.AddScoped<IAttributePointCalculator, AttributePointCalculator>();
services.AddScoped<IDownloadService, DownloadService>();
services.AddScoped<IGuildwarsInstaller, GuildwarsInstaller>();
services.AddScoped<IGuildwarsEntityDebouncer, GuildwarsEntityDebouncer>();
services.AddScoped<IExceptionHandler, ExceptionHandler>();
services.AddScoped<IPathfinder, StupidPathfinder>();
services.AddScoped<IDrawingService, DrawingService>();
services.AddScoped<IDrawingModuleProducer, DrawingService>(sp => sp.GetRequiredService<IDrawingService>().As<DrawingService>());
services.AddScoped<IUModService, UModService>();
services.AddScoped<IToolboxService, ToolboxService>();
}
public static void RegisterViews(IViewProducer viewProducer)
@@ -120,6 +167,7 @@ public static class ProjectConfiguration
viewProducer.ThrowIfNull();
viewProducer.RegisterPermanentView<LauncherView>();
viewProducer.RegisterPermanentView<Views.FocusView>();
viewProducer.RegisterView<SettingsView>();
viewProducer.RegisterView<AskUpdateView>();
viewProducer.RegisterView<UpdateView>();
@@ -135,14 +183,80 @@ public static class ProjectConfiguration
viewProducer.RegisterView<IconDownloadView>();
viewProducer.RegisterView<GraphAuthorizationView>();
viewProducer.RegisterView<BuildsSynchronizationView>();
viewProducer.RegisterView<OnboardingView>();
viewProducer.RegisterView<FocusView>();
viewProducer.RegisterView<LauncherOnboardingView>();
viewProducer.RegisterView<MetricsView>();
viewProducer.RegisterView<GuildwarsDownloadView>();
viewProducer.RegisterView<UModInstallingView>();
viewProducer.RegisterView<UModInstallationChoiceView>();
viewProducer.RegisterView<UModOnboardingEntryView>();
viewProducer.RegisterView<UModSwitchView>();
viewProducer.RegisterView<UModBrowserView>();
viewProducer.RegisterView<ToolboxInstallationView>();
viewProducer.RegisterView<ToolboxInstallationChoiceView>();
viewProducer.RegisterView<ToolboxOnboardingEntryView>();
viewProducer.RegisterView<ToolboxSwitchView>();
viewProducer.RegisterView<ToolboxHomepageView>();
}
public static void RegisterStartupActions(IStartupActionProducer startupActionProducer)
{
startupActionProducer.ThrowIfNull();
startupActionProducer.RegisterAction<RenameInstallerAction>();
}
public static void RegisterPostUpdateActions(IPostUpdateActionProducer postUpdateActionProducer)
{
postUpdateActionProducer.ThrowIfNull();
}
postUpdateActionProducer.AddPostUpdateAction<RenameInstallerAction>();
public static void RegisterDrawingModules(IDrawingModuleProducer drawingModuleProducer)
{
drawingModuleProducer.ThrowIfNull();
drawingModuleProducer.RegisterDrawingModule<DeadEntityDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<NeutralEntityDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<AlliedCreatureEntityDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<AllyEntityDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<BossEntityDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<EnemyEntityDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<NpcEntityDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<ResurrectionShrineDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<CollectorDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<GateDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<StairsDownDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<StairsUpDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<CircledStarDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<AreaMapDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<DungeonBossDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<DungeonKeyDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<PathfindingDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<PlayerPositionHistoryDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<QuestObjectiveDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<MainPlayerDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<PartyMemberDrawingModule>();
drawingModuleProducer.RegisterDrawingModule<WorldPlayerDrawingModule>();
}
public static void RegisterOptions(IOptionsProducer optionsProducer)
{
optionsProducer.ThrowIfNull();
optionsProducer.RegisterOptions<BrowserOptions>();
optionsProducer.RegisterOptions<BuildSynchronizationOptions>();
optionsProducer.RegisterOptions<FocusViewOptions>();
optionsProducer.RegisterOptions<LauncherOptions>();
optionsProducer.RegisterOptions<ToolboxOptions>();
optionsProducer.RegisterOptions<UModOptions>();
optionsProducer.RegisterOptions<ScreenManagerOptions>();
}
private static void SetupDaybreakUserAgent(HttpRequestHeaders httpRequestHeaders)
{
httpRequestHeaders.ThrowIfNull().TryAddWithoutValidation("User-Agent", DaybreakUserAgent);
}
}
+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>
+11 -9
View File
@@ -9,6 +9,7 @@
mc:Ignorable="d"
Unloaded="UserControl_Unloaded"
x:Name="_this"
Initialized="UserControl_Initialized"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<converters:BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter" TriggerValue="True"></converters:BooleanToVisibilityConverter>
@@ -19,7 +20,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 +34,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>
@@ -69,17 +70,19 @@
IsEnabled="{Binding ElementName=_this, Path=BrowserSupported, Mode=OneWay}"></TextBox>
<StackPanel Grid.Column="2" Orientation="Horizontal">
<local:HomeButton Width="30" Height="30" Margin="5"
Visibility="{Binding ElementName=_this, Path=HomeButtonVisible, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
IsEnabled="{Binding ElementName=_this, Path=BrowserSupported, Mode=OneWay}"
Clicked="HomeButton_Clicked"></local:HomeButton>
<local:StarGlyph Height="30" Width="30" Margin="5"
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
IsEnabled="{Binding ElementName=_this, Path=BrowserSupported, Mode=OneWay}"
Clicked="StarGlyph_Clicked" x:Name="FavoriteButton"></local:StarGlyph>
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Visibility="{Binding ElementName=_this, Path=HomeButtonVisible, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
IsEnabled="{Binding ElementName=_this, Path=BrowserSupported, Mode=OneWay}"
Clicked="StarGlyph_Clicked" x:Name="FavoriteButton"></local:StarGlyph>
<local:MaximizeButton Height="30" Width="30" Margin="5"
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
IsEnabled="{Binding ElementName=_this, Path=BrowserSupported, Mode=OneWay}"
Clicked="MaximizeButton_Clicked"></local:MaximizeButton>
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
IsEnabled="{Binding ElementName=_this, Path=BrowserSupported, Mode=OneWay}"
Clicked="MaximizeButton_Clicked"></local:MaximizeButton>
</StackPanel>
</Grid>
<Grid Grid.RowSpan="2" Background="Gray"
@@ -95,6 +98,5 @@
<TextBlock Foreground="White" Text="Restart after the installation." FontSize="26" TextWrapping="Wrap" />
</StackPanel>
</Grid>
</Grid>
</UserControl>
+114 -32
View File
@@ -1,4 +1,4 @@
using Daybreak.Configuration;
using Daybreak.Configuration.Options;
using Daybreak.Models.Browser;
using Daybreak.Models.Guildwars;
using Daybreak.Services.BuildTemplates;
@@ -8,8 +8,12 @@ using Microsoft.Extensions.Logging;
using Microsoft.Web.WebView2.Core;
using System;
using System.Configuration;
using System.Core.Extensions;
using System.Diagnostics;
using System.Extensions;
using System.Globalization;
using System.IO;
using System.Text.RegularExpressions;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
@@ -26,23 +30,32 @@ public partial class ChromiumBrowserWrapper : UserControl
{
public static readonly DependencyProperty AddressProperty =
DependencyPropertyExtensions.Register<ChromiumBrowserWrapper, string>(nameof(Address));
private const string BrowserSearchPlaceholder = "[PLACEHOLDER]";
private const string BrowserSearchLink = $"https://www.google.com/search?q={BrowserSearchPlaceholder}";
private const string BrowserDownloadLink = "https://developer.microsoft.com/en-us/microsoft-edge/webview2/";
private static readonly Regex WebAddressRegex = new("^((http|ftp|https)://)?([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?", RegexOptions.Compiled);
private static CoreWebView2Environment? coreWebView2Environment;
public event EventHandler<string>? FavoriteUriChanged;
public event EventHandler? MaximizeClicked;
public event EventHandler<Build>? BuildDecoded;
public event EventHandler<DownloadPayload>? DownloadingFile;
public event EventHandler<string>? DownloadedFile;
private ILiveOptions<ApplicationConfiguration>? liveOptions;
private ILogger<ChromiumBrowserWrapper>? logger;
private IBuildTemplateManager? buildTemplateManager;
private readonly Task initializationTask;
private readonly ILiveOptions<BrowserOptions>? liveOptions;
private readonly ILogger<ChromiumBrowserWrapper>? logger;
private readonly IBuildTemplateManager? buildTemplateManager;
[GenerateDependencyProperty(InitialValue = true)]
private bool canDownloadBuild;
[GenerateDependencyProperty(InitialValue = true)]
private bool canNavigate;
[GenerateDependencyProperty(InitialValue = false)]
private bool canDownloadFiles;
[GenerateDependencyProperty(InitialValue = true)]
private bool controlsEnabled;
[GenerateDependencyProperty(InitialValue = null)]
@@ -57,6 +70,13 @@ public partial class ChromiumBrowserWrapper : UserControl
private string favoriteAddress = string.Empty;
[GenerateDependencyProperty(InitialValue = false)]
private bool preventDispose;
[GenerateDependencyProperty(InitialValue = true)]
private bool homeButtonVisible = true;
[GenerateDependencyProperty(InitialValue = true)]
private bool favoriteButtonVisible = true;
[GenerateDependencyProperty]
private string downloadsDirectory = string.Empty;
public string Address
{
get => this.GetTypedValue<string>(AddressProperty);
@@ -70,9 +90,23 @@ public partial class ChromiumBrowserWrapper : UserControl
}
public ChromiumBrowserWrapper()
: this(Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<ILiveOptions<BrowserOptions>>(),
Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IBuildTemplateManager>(),
Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<ILogger<ChromiumBrowserWrapper>>())
{
}
public ChromiumBrowserWrapper(
ILiveOptions<BrowserOptions> liveOptions,
IBuildTemplateManager buildTemplateManager,
ILogger<ChromiumBrowserWrapper> logger)
{
this.liveOptions = liveOptions.ThrowIfNull();
this.buildTemplateManager = buildTemplateManager.ThrowIfNull();
this.logger = logger.ThrowIfNull();
this.initializationTask = this.InitializeBrowser();
this.InitializeComponent();
this.WebBrowser.IsEnabled = false;
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
@@ -84,35 +118,22 @@ public partial class ChromiumBrowserWrapper : UserControl
}
}
public async Task InitializeDefaultBrowser()
public async Task ReinitializeBrowser()
{
var options = Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<ILiveOptions<ApplicationConfiguration>>();
var buildTemplateManager = Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IBuildTemplateManager>();
var logger = Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<ILogger<ChromiumBrowserWrapper>>();
await this.InitializeDefaultBrowser(options, buildTemplateManager, logger);
}
public async Task InitializeDefaultBrowser(
ILiveOptions<ApplicationConfiguration> liveOptions,
IBuildTemplateManager buildTemplateManager,
ILogger<ChromiumBrowserWrapper> logger)
{
this.liveOptions = liveOptions;
this.buildTemplateManager = buildTemplateManager;
this.logger = logger;
this.InitializeEnvironment();
await this.InitializeBrowser();
}
public async void ReinitializeBrowser()
public Task InitializationTask() => this.initializationTask;
private async void UserControl_Initialized(object sender, EventArgs e)
{
this.InitializeEnvironment();
await this.InitializeBrowser();
}
private void InitializeEnvironment()
{
if (this.liveOptions!.Value.BrowsersEnabled is false)
if (this.liveOptions!.Value.Enabled is false)
{
this.BrowserSupported = false;
return;
@@ -120,7 +141,11 @@ public partial class ChromiumBrowserWrapper : UserControl
try
{
coreWebView2Environment ??= System.Extensions.TaskExtensions.RunSync(() => CoreWebView2Environment.CreateAsync(null, "BrowserData", null));
coreWebView2Environment ??= System.Extensions.TaskExtensions.RunSync(() => CoreWebView2Environment.CreateAsync(null, "BrowserData", new CoreWebView2EnvironmentOptions
{
EnableTrackingPrevention = true,
AllowSingleSignOnUsingOSPrimaryAccount = true
}));
this.BrowserSupported = true;
}
@@ -139,7 +164,7 @@ public partial class ChromiumBrowserWrapper : UserControl
this.BrowserEnabled = true;
await this.WebBrowser.EnsureCoreWebView2Async(coreWebView2Environment);
this.AddressBarReadonly = this.liveOptions!.Value.AddressBarReadonly;
this.CanDownloadBuild = this.liveOptions.Value.ExperimentalFeatures.DynamicBuildLoading;
this.CanDownloadBuild = this.liveOptions.Value.DynamicBuildLoading;
this.WebBrowser.CoreWebView2.NewWindowRequested += (browser, args) => args.Handled = true;
this.WebBrowser.NavigationStarting += (browser, args) =>
{
@@ -152,6 +177,35 @@ public partial class ChromiumBrowserWrapper : UserControl
this.Navigating = true;
}
};
this.WebBrowser.CoreWebView2.DownloadStarting += (browser, args) =>
{
if (this.CanDownloadFiles is false)
{
this.logger?.LogInformation("Downloads are disallowed. Cancelling download");
args.Cancel = true;
}
if (this.DownloadsDirectory.IsNullOrWhiteSpace())
{
this.logger?.LogInformation("No downloads directory specified. Downloading to default directory");
return;
}
var fileName = Path.GetFileName(args.ResultFilePath);
var finalPath = Path.GetFullPath(Path.Combine(this.DownloadsDirectory, fileName));
var downloadPayload = new DownloadPayload { ResultingFilePath = finalPath, CanDownload = true };
this.DownloadingFile?.Invoke(this, downloadPayload);
if (!downloadPayload.CanDownload)
{
args.Cancel = true;
this.logger?.LogInformation("Download blocked by handler. Cancelling download");
return;
}
args.ResultFilePath = finalPath;
args.DownloadOperation.StateChanged += this.DownloadOperation_StateChanged;
};
this.WebBrowser.NavigationCompleted += (browser, args) => this.Navigating = false;
this.WebBrowser.WebMessageReceived += this.CoreWebView2_WebMessageReceived!;
this.WebBrowser.CoreWebView2.Settings.AreDevToolsEnabled = false;
@@ -188,14 +242,19 @@ public partial class ChromiumBrowserWrapper : UserControl
{
if (e.Key == System.Windows.Input.Key.Enter)
{
var newAddress = sender.As<TextBox>().Text;
newAddress = SanitizeAddress(newAddress);
if (newAddress.IsNullOrWhiteSpace())
var input = sender.As<TextBox>().Text;
var maybeAddress = SanitizeAddress(input);
// If input is address, navigate to address. Otherwise search for the text input in Google
if (Uri.TryCreate(maybeAddress, UriKind.Absolute, out _))
{
return;
this.Address = maybeAddress;
}
else
{
var address = BrowserSearchLink.Replace(BrowserSearchPlaceholder, maybeAddress);
this.Address = address;
}
this.Address = newAddress;
this.WebBrowser.CoreWebView2.Navigate(this.Address);
e.Handled = true;
}
@@ -251,6 +310,24 @@ public partial class ChromiumBrowserWrapper : UserControl
}
}
private void DownloadOperation_StateChanged(object? sender, object? e)
{
if (sender is not CoreWebView2DownloadOperation downloadOperation)
{
return;
}
if (downloadOperation.State != CoreWebView2DownloadState.Completed &&
downloadOperation.State != CoreWebView2DownloadState.Interrupted)
{
return;
}
downloadOperation.StateChanged -= this.DownloadOperation_StateChanged;
var filePath = Path.GetFullPath(downloadOperation.ResultFilePath);
this.DownloadedFile?.Invoke(this, filePath);
}
private void LoadBuildTemplateButton_Click(object sender, EventArgs e)
{
var build = this.ContextMenu.DataContext.As<Build>();
@@ -326,6 +403,11 @@ public partial class ChromiumBrowserWrapper : UserControl
return default!;
}
if (WebAddressRegex.IsMatch(address) is false)
{
return address;
}
if (address.StartsWith("www") is false &&
address.StartsWith("http") is false)
{
@@ -0,0 +1,38 @@
<UserControl x:Class="Daybreak.Controls.ExpandableMenuSection"
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"
xmlns:buttons="clr-namespace:Daybreak.Controls.Buttons"
mc:Ignorable="d"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
</UserControl.Resources>
<Grid>
<StackPanel Orientation="Vertical"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch">
<buttons:MenuButton
Cursor="Hand"
HighlightColor="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Title="{Binding ElementName=_this, Path=SectionTitle, Mode=OneWay}"
FontSize="{Binding ElementName=_this, Path=FontSize, Mode=OneWay}"
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Clicked="MenuButton_Clicked"
IsEnabled="{Binding ElementName=_this, Path=ExpanderButtonEnabled, Mode=OneWay}"
Height="30"></buttons:MenuButton>
<Rectangle Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" Height="1"
HorizontalAlignment="Stretch"></Rectangle>
<Grid MaxHeight="{Binding ElementName=_this, Path=ExpanderHeight, Mode=OneWay}"
Margin="10, 0, 0, 0">
<ItemsControl x:Name="ContentListView"
ItemsSource="{Binding ElementName=_this, Path=Children, Mode=OneWay}"
Background="Transparent"
BorderThickness="0"></ItemsControl>
</Grid>
</StackPanel>
</Grid>
</UserControl>
@@ -0,0 +1,65 @@
using System;
using System.Collections.ObjectModel;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
using System.Windows.Media.Animation;
namespace Daybreak.Controls;
/// <summary>
/// Interaction logic for ExpandableMenuSection.xaml
/// </summary>
public partial class ExpandableMenuSection : UserControl
{
[GenerateDependencyProperty]
private string sectionTitle = string.Empty;
[GenerateDependencyProperty]
private double expanderHeight = 0;
[GenerateDependencyProperty(InitialValue = true)]
private bool expanderButtonEnabled;
[GenerateDependencyProperty(InitialValue = false)]
private bool expanded = false;
public ObservableCollection<FrameworkElement> Children { get; } = new ObservableCollection<FrameworkElement>();
public ExpandableMenuSection()
{
this.InitializeComponent();
}
private void MenuButton_Clicked(object sender, EventArgs e)
{
this.ExpanderButtonEnabled = false;
var desiredHeight = 0d;
foreach (var child in this.Children)
{
desiredHeight += child.DesiredSize.Height > 0 ?
child.DesiredSize.Height :
child.Height;
}
var doubleAnimation = new DoubleAnimation();
if (this.expanded)
{
doubleAnimation.From = desiredHeight;
doubleAnimation.To = 0;
}
else
{
doubleAnimation.From = 0;
doubleAnimation.To = desiredHeight;
}
doubleAnimation.AccelerationRatio = 0.3;
doubleAnimation.DecelerationRatio = 0.3;
doubleAnimation.Duration = TimeSpan.FromMilliseconds(100);
doubleAnimation.Completed += (_, _) =>
{
this.Expanded = !this.Expanded;
this.ExpanderButtonEnabled = true;
};
this.BeginAnimation(ExpanderHeightProperty, doubleAnimation);
}
}
@@ -0,0 +1,18 @@
<UserControl x:Class="Daybreak.Controls.Glyphs.DownloadGlyph"
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.Glyphs"
mc:Ignorable="d"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<Grid>
<Path Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" StrokeThickness="3"
Data="m32,30.454c0,0 8.312,0 9.098,0c4.365,0 7.902,-3.537 7.902,-7.901s-3.537,-7.902 -7.902,-7.902c-0.02,0 -0.038,0.003 -0.058,0.003c0.061,-0.494 0.103,-0.994 0.103,-1.504c0,-6.71 -5.439,-12.15 -12.15,-12.15c-5.229,0 -9.672,3.309 -11.386,7.941c-1.087,-1.089 -2.591,-1.764 -4.251,-1.764c-3.319,0 -6.009,2.69 -6.009,6.008c0,0.085 0.01,0.167 0.013,0.251c-3.665,1.013 -6.36,4.362 -6.36,8.349c0,4.788 3.881,8.669 8.67,8.669c0.827,0 8.33,0 8.33,0"></Path>
<Polyline Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" Points="30,36 25,41 20,36" StrokeThickness="3"></Polyline>
<Line X1="25" X2="25" Y1="21" Y2="41" Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}" StrokeThickness="3"></Line>
</Grid>
</Viewbox>
</UserControl>
@@ -0,0 +1,14 @@
using System.Windows.Controls;
namespace Daybreak.Controls.Glyphs;
/// <summary>
/// Interaction logic for DownloadGlyph.xaml
/// </summary>
public partial class DownloadGlyph : UserControl
{
public DownloadGlyph()
{
this.InitializeComponent();
}
}
@@ -1,17 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Controls;
namespace Daybreak.Controls;
+1 -14
View File
@@ -1,17 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Controls;
namespace Daybreak.Controls;
@@ -0,0 +1,13 @@
<UserControl x:Class="Daybreak.Controls.Glyphs.GWToolboxGlyph"
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.Glyphs"
mc:Ignorable="d"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<TextBlock Text="G" Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"></TextBlock>
</Viewbox>
</UserControl>
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Daybreak.Controls.Glyphs;
/// <summary>
/// Interaction logic for GWToolboxGlyph.xaml
/// </summary>
public partial class GWToolboxGlyph : UserControl
{
public GWToolboxGlyph()
{
InitializeComponent();
}
}
+20
View File
@@ -0,0 +1,20 @@
<UserControl x:Class="Daybreak.Controls.Glyphs.GaugeGlyph"
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.Glyphs"
mc:Ignorable="d"
Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<Grid>
<Path Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m13.5,13.5c1.44772,-1.44772 2.5,-3.29086 2.5,-5.5c0,-4.41828 -3.58172,-8 -8,-8s-8,3.58172 -8,8c0,2.20914 1.05228,4.05228 2.5,5.5"></Path>
<Path Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m8,8l-4,-4"></Path>
<Ellipse Margin="6, 7.5, 6, 6" Width="1.5" Height="1.5"
Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"></Ellipse>
</Grid>
</Viewbox>
</UserControl>
@@ -0,0 +1,14 @@
using System.Windows.Controls;
namespace Daybreak.Controls.Glyphs;
/// <summary>
/// Interaction logic for GaugeGlyph.xaml
/// </summary>
public partial class GaugeGlyph : UserControl
{
public GaugeGlyph()
{
this.InitializeComponent();
}
}
@@ -1,17 +1,4 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
using System.Windows.Controls;
namespace Daybreak.Controls;
+28
View File
@@ -0,0 +1,28 @@
<UserControl x:Class="Daybreak.Controls.Glyphs.MapGlyph"
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.Glyphs"
mc:Ignorable="d"
Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<Grid>
<Path Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m18.53,14.53a9.37,9.37 0 0 1 -9.26,9.47l0,0a9.5,9.5 0 0 1 -3.26,-18.33a9.61,9.61 0 0 0 0.77,1.82a14.22,14.22 0 0 0 2.51,3.25a14.06,14.06 0 0 0 2.49,-3.24a9.2,9.2 0 0 0 0.75,-1.83a9.47,9.47 0 0 1 6,8.86z"></Path>
<Polyline Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Points="9.270000457763672,24 9.270000457763672,23.989999771118164 9.270000457763672,10.739999771118164"></Polyline>
<Path Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m9.27,24l0,0c-0.32,-0.22 -5.27,-3.85 -4.94,-10.1a11.36,11.36 0 0 1 2.44,-6.4"></Path>
<Path Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m9.25,24a0,0 0 0 1 0,0l0,0c0.4,-0.29 5.25,-3.91 4.92,-10.1a11.35,11.35 0 0 0 -2.43,-6.39"></Path>
<Line Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
X1="0" X2="18.53" Y1="14.53" Y2="14.53"></Line>
<Path Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m12.35,1.89a3.81,3.81 0 0 0 -3.08,-1.89a3.82,3.82 0 0 0 -3.09,1.89a4.75,4.75 0 0 0 -0.19,3.78a9.61,9.61 0 0 0 0.77,1.82a14.22,14.22 0 0 0 2.51,3.25a14.06,14.06 0 0 0 2.49,-3.24a9.2,9.2 0 0 0 0.77,-1.83a4.79,4.79 0 0 0 -0.18,-3.78z"></Path>
<Ellipse Stroke="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Width="1.24" Height="1.26" Margin="8.6, 3" HorizontalAlignment="Left" VerticalAlignment="Top"></Ellipse>
</Grid>
</Viewbox>
</UserControl>
+14
View File
@@ -0,0 +1,14 @@
using System.Windows.Controls;
namespace Daybreak.Controls.Glyphs;
/// <summary>
/// Interaction logic for MapGlyph.xaml
/// </summary>
public partial class MapGlyph : UserControl
{
public MapGlyph()
{
this.InitializeComponent();
}
}
+13
View File
@@ -0,0 +1,13 @@
<UserControl x:Class="Daybreak.Controls.Glyphs.UModGlyph"
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.Glyphs"
x:Name="_this"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<TextBlock Text="U" Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"></TextBlock>
</Viewbox>
</UserControl>
@@ -0,0 +1,26 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Documents;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using System.Windows.Navigation;
using System.Windows.Shapes;
namespace Daybreak.Controls.Glyphs;
/// <summary>
/// Interaction logic for UModGlyph.xaml
/// </summary>
public partial class UModGlyph : UserControl
{
public UModGlyph()
{
InitializeComponent();
}
}
+87
View File
@@ -0,0 +1,87 @@
<UserControl x:Class="Daybreak.Controls.GuildwarsMinimap"
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"
x:Name="_this"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800"
MouseWheel="GuildwarsMinimap_MouseWheel"
MouseLeftButtonDown="GuildwarsMinimap_MouseLeftButtonDown"
MouseLeftButtonUp="GuildwarsMinimap_MouseLeftButtonUp"
MouseRightButtonDown="GuildwarsMinimap_MouseRightButtonDown"
MouseRightButtonUp="GuildwarsMinimap_MouseRightButtonUp"
MouseMove="GuildwarsMinimap_MouseMove"
Loaded="GuildwarsMinimap_Loaded"
Unloaded="GuildwarsMinimap_Unloaded">
<UserControl.Resources>
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
<ContextMenu x:Key="WorldPlayerContextMenu">
<ContextMenu.Template>
<ControlTemplate>
<local:WorldPlayerContextMenu DataContext="{Binding DataContext, RelativeSource={RelativeSource TemplatedParent}}"></local:WorldPlayerContextMenu>
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
<ContextMenu x:Key="MainPlayerContextMenu">
<ContextMenu.Template>
<ControlTemplate>
<local:MainPlayerContextMenu DataContext="{Binding DataContext, RelativeSource={RelativeSource TemplatedParent}}"></local:MainPlayerContextMenu>
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
<ContextMenu x:Key="LivingEntityContextMenu">
<ContextMenu.Template>
<ControlTemplate>
<local:LivingEntityContextMenu DataContext="{Binding DataContext, RelativeSource={RelativeSource TemplatedParent}}"
LivingEntityContextMenuClicked="LivingEntityContextMenu_LivingEntityContextMenuClicked"></local:LivingEntityContextMenu>
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
<ContextMenu x:Key="PlayerContextMenu">
<ContextMenu.Template>
<ControlTemplate>
<local:PlayerContextMenu DataContext="{Binding DataContext, RelativeSource={RelativeSource TemplatedParent}}"
PlayerContextMenuClicked="PlayerContextMenu_PlayerContextMenuClicked"></local:PlayerContextMenu>
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
<ContextMenu x:Key="QuestContextMenu">
<ContextMenu.Template>
<ControlTemplate>
<local:QuestContextMenu DataContext="{Binding DataContext, RelativeSource={RelativeSource TemplatedParent}}"
QuestContextMenuClicked="QuestContextMenu_QuestContextMenuClicked"></local:QuestContextMenu>
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
<ContextMenu x:Key="MapIconContextMenu">
<ContextMenu.Template>
<ControlTemplate>
<local:MapIconContextMenu DataContext="{Binding DataContext, RelativeSource={RelativeSource TemplatedParent}}"
MapIconContextMenuClicked="MapIconContextMenu_MapIconContextMenuClicked"></local:MapIconContextMenu>
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
</UserControl.Resources>
<Grid>
<Image x:Name="MapDrawingHost" VerticalAlignment="Top" HorizontalAlignment="Left" Stretch="Fill"/>
<Image x:Name="EntitiesDrawingHost" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
<Grid Background="#F0212121"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
Visibility="{Binding ElementName=_this, Path=ControlsVisible, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
<local:MaximizeButton Height="30" Width="30" Margin="5"
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Clicked="MaximizeButton_Clicked"></local:MaximizeButton>
</Grid>
<WrapPanel Background="#F0212121" VerticalAlignment="Top" HorizontalAlignment="Right" >
<TextBlock Text="{Binding ElementName=_this, Path=TargetEntityId, Mode=OneWay}" Foreground="White"
FontSize="16" Padding="5" ></TextBlock>
<TextBlock Text=" - " Foreground="White"
FontSize="16" Padding="5" ></TextBlock>
<TextBlock Text="{Binding ElementName=_this, Path=TargetEntityModelId, Mode=OneWay}" Foreground="White"
FontSize="16" Padding="5" ></TextBlock>
</WrapPanel>
</Grid>
</UserControl>
+699
View File
@@ -0,0 +1,699 @@
using Daybreak.Models.Guildwars;
using Daybreak.Models;
using System.Windows.Controls;
using System.Windows.Extensions;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Media;
using System;
using System.Extensions;
using Daybreak.Services.Scanner;
using Microsoft.Extensions.DependencyInjection;
using System.Core.Extensions;
using System.Windows.Input;
using System.Collections.Generic;
using System.Linq;
using Daybreak.Services.Pathfinding;
using Daybreak.Services.Pathfinding.Models;
using System.Windows.Threading;
using System.Threading;
using System.Threading.Tasks;
using Daybreak.Services.Drawing;
namespace Daybreak.Controls;
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Using source generators to auto-implement dependency properties")]
/// <summary>
/// Interaction logic for GuildwarsMinimap.xaml
/// </summary>
public partial class GuildwarsMinimap : UserControl
{
private const int MapDownscaleFactor = 10;
private const int EntitySize = 100;
private const float PositionRadius = 150;
// Minimum amount of milliseconds to pass between calculating paths to objectives
private const int MinPathCalculationFrequency = 500;
private readonly DispatcherTimer dispatcherTimer = new(DispatcherPriority.ApplicationIdle);
private readonly List<Position> mainPlayerPositionHistory = new();
private readonly IPathfinder pathfinder;
private readonly IDrawingService drawingService;
private readonly IGuildwarsEntityDebouncer guildwarsEntityDebouncer;
private readonly Color outlineColor = Colors.Chocolate;
private readonly TimeSpan offsetRevertDelay = TimeSpan.FromSeconds(3);
private bool calculatingPathToObjectives = false;
private bool resizeEntities;
private bool dragging;
private double mapVirtualMinWidth;
private double mapVirtualMinHeight;
private double mapWidth;
private double mapHeight;
private DateTime offsetRevertTime = DateTime.Now;
private double offsetRevert = 0;
private Point originPoint = new(0, 0);
private Vector originOffset = new(0, 0);
private Point initialClickPoint = new(0, 0);
private DebounceResponse? cachedDebounceResponse;
private PathfindingCache? pathfindingCache;
[GenerateDependencyProperty]
private PathingData pathingData = new();
[GenerateDependencyProperty]
private GameData gameData = new();
[GenerateDependencyProperty]
private double zoom = 0.08;
[GenerateDependencyProperty(InitialValue = true)]
private bool drawPositionHistory = true;
[GenerateDependencyProperty]
private bool controlsVisible;
[GenerateDependencyProperty]
private int targetEntityId;
[GenerateDependencyProperty]
private int targetEntityModelId;
public event EventHandler? MaximizeClicked;
public event EventHandler<QuestMetadata>? QuestMetadataClicked;
public event EventHandler<LivingEntity>? LivingEntityClicked;
public event EventHandler<PlayerInformation>? PlayerInformationClicked;
public event EventHandler<MapIcon>? MapIconClicked;
public GuildwarsMinimap()
:this(
Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IPathfinder>(),
Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IDrawingService>(),
Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IGuildwarsEntityDebouncer>())
{
}
public GuildwarsMinimap(
IPathfinder pathfinder,
IDrawingService drawingService,
IGuildwarsEntityDebouncer guildwarsEntityDebouncer)
{
this.pathfinder = pathfinder.ThrowIfNull();
this.drawingService = drawingService.ThrowIfNull();
this.guildwarsEntityDebouncer = guildwarsEntityDebouncer.ThrowIfNull();
this.dispatcherTimer.Tick += (_, _) => this.ApplyOffsetRevert();
this.dispatcherTimer.Interval = TimeSpan.FromMilliseconds(16);
this.InitializeComponent();
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == ActualHeightProperty ||
e.Property == ActualWidthProperty)
{
this.resizeEntities = true;
}
else if (e.Property == PathingDataProperty)
{
this.guildwarsEntityDebouncer.ClearCaches();
this.mainPlayerPositionHistory.Clear();
this.UpdateGameData();
this.DrawMap();
}
else if (e.Property == ZoomProperty)
{
this.UpdateGameData();
}
else if (e.Property == GameDataProperty &&
this.GameData.Valid)
{
this.UpdateGameData();
}
}
private void UpdateGameData()
{
if(this.GameData.Valid is false)
{
return;
}
var debounceResponse = this.guildwarsEntityDebouncer.DebounceEntities(this.GameData);
if (!double.IsFinite(this.mapWidth) ||
!double.IsFinite(this.mapHeight) ||
!double.IsFinite(this.mapVirtualMinWidth) ||
!double.IsFinite(this.mapVirtualMinHeight) ||
debounceResponse.MainPlayer.Position is null)
{
return;
}
this.TargetEntityId = this.GameData.Session?.CurrentTargetId ?? 0;
this.TargetEntityModelId = (int?)this.GameData.LivingEntities?.FirstOrDefault(e => e.Id == this.TargetEntityId).ModelType ?? 0;
var screenVirtualWidth = this.ActualWidth / this.Zoom;
var screenVirtualHeight = this.ActualHeight / this.Zoom;
var position = debounceResponse.MainPlayer.Position!.Value;
this.originPoint = new Point(
position.X - (screenVirtualWidth / 2) - (this.originOffset.X / this.Zoom),
position.Y + (screenVirtualHeight / 2) + (this.originOffset.Y / this.Zoom));
var adjustedPosition = new Point((int)((position.X - this.mapVirtualMinWidth) * this.Zoom), (int)(this.mapHeight - position.Y + this.mapVirtualMinHeight) * this.Zoom);
this.MapDrawingHost.Margin = new Thickness(
-adjustedPosition.X + (this.ActualWidth / 2) + this.originOffset.X,
-adjustedPosition.Y + (this.ActualHeight / 2) + this.originOffset.Y,
0,
0);
this.MapDrawingHost.Height = this.mapHeight * this.Zoom;
this.MapDrawingHost.Width = this.mapWidth * this.Zoom;
this.cachedDebounceResponse = debounceResponse;
this.ManageMainPlayerPositionHistory();
this.DrawEntities();
}
private void ManageMainPlayerPositionHistory()
{
if (this.cachedDebounceResponse is null)
{
return;
}
var currentPosition = this.cachedDebounceResponse.MainPlayer.Position ?? throw new InvalidOperationException("Unexpected main player null position");
if (this.mainPlayerPositionHistory.Any(oldPosition => PositionsCollide(oldPosition, currentPosition)))
{
return;
}
this.mainPlayerPositionHistory.Add(currentPosition);
}
private void DrawMap()
{
if (this.PathingData.Trapezoids is null)
{
return;
}
var maxWidth = double.MinValue;
var maxHeight = double.MinValue;
var minWidth = double.MaxValue;
var minHeight = double.MaxValue;
foreach (var trapezoid in this.PathingData.Trapezoids!)
{
if (trapezoid.YT < minHeight)
{
minHeight = trapezoid.YT;
}
if (trapezoid.YB > maxHeight)
{
maxHeight = trapezoid.YB;
}
if (trapezoid.XTL < minWidth)
{
minWidth = trapezoid.XTL;
}
if (trapezoid.XBL < minWidth)
{
minWidth = trapezoid.XBL;
}
if (trapezoid.XTR > maxWidth)
{
maxWidth = trapezoid.XTR;
}
if (trapezoid.XBR > maxWidth)
{
maxWidth = trapezoid.XBR;
}
}
var width = maxWidth - minWidth;
var height = maxHeight - minHeight;
this.mapVirtualMinHeight = minHeight;
this.mapVirtualMinWidth = minWidth;
this.mapWidth = width;
this.mapHeight = height;
if (width <= 0 || height <= 0 ||
!double.IsFinite(width) ||
!double.IsFinite(height))
{
return;
}
var bitmap = BitmapFactory.New((int)(width / MapDownscaleFactor), (int)(height / MapDownscaleFactor));
this.MapDrawingHost.Source = bitmap;
this.MapDrawingHost.Width = width / MapDownscaleFactor;
this.MapDrawingHost.Height = height / MapDownscaleFactor;
using var bitmapContext = bitmap.GetBitmapContext();
bitmap.Clear(Colors.Transparent);
foreach (var trapezoid in this.PathingData.Trapezoids)
{
var a = new Point((int)((trapezoid.XTL - minWidth) / MapDownscaleFactor), (int)((height - trapezoid.YT + minHeight) / MapDownscaleFactor));
var b = new Point((int)((trapezoid.XTR - minWidth) / MapDownscaleFactor), (int)((height - trapezoid.YT + minHeight) / MapDownscaleFactor));
var c = new Point((int)((trapezoid.XBR - minWidth) / MapDownscaleFactor), (int)((height - trapezoid.YB + minHeight) / MapDownscaleFactor));
var d = new Point((int)((trapezoid.XBL - minWidth) / MapDownscaleFactor), (int)((height - trapezoid.YB + minHeight) / MapDownscaleFactor));
bitmap.FillPolygon(new int[] { (int)a.X, (int)a.Y, (int)b.X, (int)b.Y, (int)c.X, (int)c.Y, (int)d.X, (int)d.Y, (int)a.X, (int)a.Y }, Colors.White);
}
}
private void DrawEntities()
{
if (this.ActualWidth == 0 || this.ActualHeight == 0)
{
return;
}
if (this.EntitiesDrawingHost.Source is not WriteableBitmap bitmap ||
this.resizeEntities ||
bitmap.PixelHeight != (int)this.ActualHeight ||
bitmap.PixelWidth != (int)this.ActualWidth)
{
bitmap = BitmapFactory.New((int)this.ActualWidth, (int)this.ActualHeight);
this.EntitiesDrawingHost.Source = bitmap;
this.resizeEntities = false;
}
this.CalculatePathsToObjectives();
bitmap.Clear(Colors.Transparent);
using var context = bitmap.GetBitmapContext();
this.drawingService.UpdateDrawingParameters(
(int)this.ActualWidth,
(int)this.ActualHeight,
this.originPoint,
this.Zoom,
PositionRadius,
EntitySize);
this.drawingService.DrawMainPlayerPositionHistory(bitmap, this.mainPlayerPositionHistory);
this.drawingService.DrawPaths(bitmap, this.pathfindingCache);
this.drawingService.DrawQuestObjectives(bitmap, this.GameData.MainPlayer?.QuestLog ?? new List<QuestMetadata>());
this.drawingService.DrawMapIcons(bitmap, this.GameData.MapIcons ?? new List<MapIcon>());
this.drawingService.DrawEntities(bitmap, this.cachedDebounceResponse ?? new DebounceResponse(), this.TargetEntityId);
}
private bool MouseOverEntity(IPositionalEntity entity, Point mousePosition)
{
var x = (int)((entity.Position!.Value.X - this.originPoint.X) * this.Zoom);
var y = 0 - (int)((entity.Position!.Value.Y - this.originPoint.Y) * this.Zoom);
return Math.Pow(mousePosition.X - x, 2) + Math.Pow(mousePosition.Y - y, 2) < Math.Pow(EntitySize * this.Zoom, 2);
}
private void DragMinimap()
{
if (this.dragging is false)
{
return;
}
var mousePosition = Mouse.GetPosition(this);
this.offsetRevert = 0;
this.originOffset = mousePosition - this.initialClickPoint;
this.offsetRevertTime = DateTime.Now + this.offsetRevertDelay;
this.UpdateGameData();
}
private void ApplyOffsetRevert()
{
if (DateTime.Now < this.offsetRevertTime)
{
return;
}
if (this.ContextMenu?.IsOpen is true)
{
return;
}
this.offsetRevert = 0.0001 + (this.offsetRevert * 1.0001);
this.offsetRevert = Math.Min(99, this.offsetRevert);
this.originOffset /= 1 + this.offsetRevert;
this.UpdateGameData();
}
private async void CalculatePathsToObjectives()
{
if (this.calculatingPathToObjectives)
{
return;
}
this.calculatingPathToObjectives = true;
var currentMapQuests = this.GameData.MainPlayer?.QuestLog?.Where(q => IsValidPositionalEntity(q)).ToList();
if (currentMapQuests is null)
{
this.calculatingPathToObjectives = false;
return;
}
var pathfindingTasks = currentMapQuests
.Select(questMetaData =>
{
return this.pathfinder.CalculatePath(
this.PathingData,
new Point
{
X = this.cachedDebounceResponse?.MainPlayer.Position?.X ?? 0,
Y = this.cachedDebounceResponse?.MainPlayer.Position?.Y ?? 0,
},
new Point
{
X = questMetaData.Position!.Value.X,
Y = questMetaData.Position!.Value.Y
},
CancellationToken.None);
})
.ToList();
await Task.WhenAll(pathfindingTasks.Append(Task.Delay(MinPathCalculationFrequency)));
var paths = new List<PathfindingResponse>();
var colors = new List<Color>();
for(var i = 0; i < currentMapQuests.Count; i++)
{
var quest = currentMapQuests[i];
var result = await pathfindingTasks[i];
if (result.TryExtractSuccess(out var pathfindingResponse))
{
paths.Add(pathfindingResponse);
var questColor = GetQuestColor(quest);
var pathColor = Color.FromArgb(100, questColor.R, questColor.G, questColor.B);
colors.Add(pathColor);
}
}
this.pathfindingCache = new PathfindingCache { PathfindingResponses = paths, Colors = colors };
this.calculatingPathToObjectives = false;
}
private IPositionalEntity? CheckMouseOverEntity(IEnumerable<IPositionalEntity>? maybeEntities)
{
if (maybeEntities is not IEnumerable<IPositionalEntity> entities)
{
return default;
}
if (this.GameData.Valid is false)
{
return default;
}
var mousePoint = Mouse.GetPosition(this);
foreach(var entity in entities)
{
if (this.MouseOverEntity(entity, mousePoint))
{
this.Cursor = Cursors.Hand;
return entity;
}
}
this.Cursor = default;
return default;
}
private Position ForceOnScreenPosition(Position entityPosition)
{
var screenVirtualWidth = this.ActualWidth / this.Zoom;
var screenVirtualHeight = this.ActualHeight / this.Zoom;
var finalEntitySize = EntitySize * this.Zoom;
return new Position
{
X = (float)Math.Clamp(
entityPosition.X,
Math.Min(this.originPoint.X + finalEntitySize, this.originPoint.X + screenVirtualWidth - finalEntitySize),
Math.Max(this.originPoint.X + finalEntitySize, this.originPoint.X + screenVirtualWidth - finalEntitySize)),
Y = (float)Math.Clamp(
entityPosition.Y,
Math.Min(this.originPoint.Y - screenVirtualHeight + finalEntitySize, this.originPoint.Y - finalEntitySize),
Math.Max(this.originPoint.Y - screenVirtualHeight + finalEntitySize, this.originPoint.Y - finalEntitySize)),
};
}
private void GuildwarsMinimap_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
this.initialClickPoint = Mouse.GetPosition(this) - this.originOffset;
this.offsetRevert = 0;
this.offsetRevertTime = DateTime.Now + this.offsetRevertDelay;
this.dragging = true;
}
private void GuildwarsMinimap_MouseLeftButtonUp(object sender, MouseButtonEventArgs e)
{
this.dragging = false;
}
private void GuildwarsMinimap_MouseRightButtonDown(object sender, MouseButtonEventArgs e)
{
var maybeOffScreenQuestMetadata = this.CheckMouseOverEntity(
this.GameData.MainPlayer?.QuestLog?
.Where(q => IsValidPositionalEntity(q))
.Where(entity => !this.drawingService.IsEntityOnScreen(entity.Position, out _, out _))
.Select(oldQuestMetadata =>
{
var onScreenPosition = this.ForceOnScreenPosition(oldQuestMetadata.Position!.Value);
return new QuestMetadata
{
From = oldQuestMetadata.From,
To = oldQuestMetadata.To,
Quest = oldQuestMetadata.Quest,
Position = onScreenPosition
};
})
.OfType<IPositionalEntity>());
if (maybeOffScreenQuestMetadata is QuestMetadata offscreenQuestMetadata &&
this.TryFindResource("QuestContextMenu") is ContextMenu offscreenQuestContextMenu)
{
this.ContextMenu = offscreenQuestContextMenu;
this.ContextMenu.DataContext = offscreenQuestMetadata;
this.ContextMenu.IsOpen = true;
return;
}
var maybeQuestMetadata = this.CheckMouseOverEntity(
this.GameData.MainPlayer?.QuestLog?
.OfType<IPositionalEntity>().Where(IsValidPositionalEntity)
.Where(entity => this.drawingService.IsEntityOnScreen(entity.Position, out _, out _)));
if (maybeQuestMetadata is QuestMetadata questMetadata &&
this.TryFindResource("QuestContextMenu") is ContextMenu questContextMenu)
{
this.ContextMenu = questContextMenu;
this.ContextMenu.DataContext = questMetadata;
this.ContextMenu.IsOpen = true;
return;
}
if (this.CheckMouseOverEntity(Enumerable.Repeat(this.GameData.MainPlayer.As<IEntity>(), 1)) is MainPlayerInformation &&
this.TryFindResource("MainPlayerContextMenu") is ContextMenu mainPlayerContextMenu)
{
this.ContextMenu = mainPlayerContextMenu;
this.ContextMenu.DataContext = this.GameData.MainPlayer;
this.ContextMenu.IsOpen = true;
return;
}
var maybeWorldPlayer = this.CheckMouseOverEntity(this.GameData.WorldPlayers?.OfType<IEntity>().Where(IsValidPositionalEntity).OrderByDescending(p => p.Id == this.TargetEntityId));
if (maybeWorldPlayer is WorldPlayerInformation worldPlayerInformation &&
this.TryFindResource("WorldPlayerContextMenu") is ContextMenu worldPlayerContextMenu)
{
this.ContextMenu = worldPlayerContextMenu;
this.ContextMenu.DataContext = worldPlayerInformation;
this.ContextMenu.IsOpen = true;
return;
}
var maybePartyMember = this.CheckMouseOverEntity(this.GameData.Party?.OfType<IEntity>().Where(IsValidPositionalEntity).OrderByDescending(p => p.Id == this.TargetEntityId));
if (maybePartyMember is PlayerInformation partyMember &&
this.TryFindResource("PlayerContextMenu") is ContextMenu playerContextMenu)
{
this.ContextMenu = playerContextMenu;
this.ContextMenu.DataContext = partyMember;
this.ContextMenu.IsOpen = true;
return;
}
var maybeLivingEntity = this.CheckMouseOverEntity(this.GameData.LivingEntities?.OfType<IEntity>().Where(IsValidPositionalEntity).OrderByDescending(p => p.Id == this.TargetEntityId));
if (maybeLivingEntity is LivingEntity livingEntity &&
this.TryFindResource("LivingEntityContextMenu") is ContextMenu livingEntityContextMenu)
{
this.ContextMenu = livingEntityContextMenu;
this.ContextMenu.DataContext = livingEntity;
this.ContextMenu.IsOpen = true;
return;
}
var maybeMapIcon = this.CheckMouseOverEntity(this.GameData.MapIcons?.OfType<IPositionalEntity>().Where(IsValidPositionalEntity));
if (maybeMapIcon is MapIcon mapIcon &&
this.TryFindResource("MapIconContextMenu") is ContextMenu mapIconContextMenu)
{
this.ContextMenu = mapIconContextMenu;
this.ContextMenu.DataContext = mapIcon;
this.ContextMenu.IsOpen = true;
return;
}
this.ContextMenu = default;
}
private void GuildwarsMinimap_MouseRightButtonUp(object sender, MouseButtonEventArgs e)
{
}
private void GuildwarsMinimap_MouseMove(object sender, MouseEventArgs e)
{
if (this.GameData.Valid is false)
{
return;
}
this.DragMinimap();
if (this.CheckMouseOverEntity(this.GameData.Party!.OfType<IEntity>()) is not null)
{
return;
}
if (this.CheckMouseOverEntity(this.GameData.WorldPlayers!.OfType<IEntity>()) is not null)
{
return;
}
if (this.CheckMouseOverEntity(Enumerable.Repeat(this.GameData.MainPlayer.As<IEntity>(), 1)) is not null)
{
return;
}
if (this.CheckMouseOverEntity(this.GameData.LivingEntities!.OfType<IEntity>()) is not null)
{
return;
}
if (this.CheckMouseOverEntity(this.GameData.MapIcons!.OfType<IPositionalEntity>()) is not null)
{
return;
}
if (this.CheckMouseOverEntity(this.GameData.MainPlayer!.Value.QuestLog!
.Where(entity => this.drawingService.IsEntityOnScreen(entity.Position, out _, out _))
.OfType<IPositionalEntity>()) is not null)
{
return;
}
if (this.CheckMouseOverEntity(this.GameData.MainPlayer!.Value.QuestLog!
.Where(entity => !this.drawingService.IsEntityOnScreen(entity.Position, out _, out _))
.Select(oldQuestMetadata =>
{
var onScreenPosition = this.ForceOnScreenPosition(oldQuestMetadata.Position!.Value);
return new QuestMetadata
{
From = oldQuestMetadata.From,
To = oldQuestMetadata.To,
Quest = oldQuestMetadata.Quest,
Position = onScreenPosition
};
})
.OfType<IPositionalEntity>()) is not null)
{
return;
}
}
private void GuildwarsMinimap_MouseWheel(object sender, MouseWheelEventArgs e)
{
var delta = e.Delta > 0 ? 0.1 : -0.1;
var previousZoom = this.Zoom;
var newZoom = this.Zoom + this.Zoom * delta;
this.originOffset *= newZoom / previousZoom;
this.initialClickPoint = new Point(
this.initialClickPoint.X * newZoom / previousZoom,
this.initialClickPoint.Y * newZoom / previousZoom);
this.Zoom = newZoom;
}
private void GuildwarsMinimap_Loaded(object sender, RoutedEventArgs e)
{
this.dispatcherTimer.Start();
}
private void GuildwarsMinimap_Unloaded(object sender, RoutedEventArgs e)
{
this.dispatcherTimer.Stop();
}
private void QuestContextMenu_QuestContextMenuClicked(object _, QuestMetadata? quest)
{
if (quest is not QuestMetadata)
{
return;
}
this.QuestMetadataClicked?.Invoke(this, quest.Value);
}
private void PlayerContextMenu_PlayerContextMenuClicked(object _, PlayerInformation? playerInformation)
{
if (playerInformation is not PlayerInformation)
{
return;
}
this.PlayerInformationClicked?.Invoke(this, playerInformation.Value);
}
private void LivingEntityContextMenu_LivingEntityContextMenuClicked(object _, LivingEntity? livingEntity)
{
if (livingEntity is not LivingEntity)
{
return;
}
this.LivingEntityClicked?.Invoke(this, livingEntity.Value);
}
private void MapIconContextMenu_MapIconContextMenuClicked(object _, MapIcon? mapIcon)
{
if (mapIcon is not MapIcon)
{
return;
}
this.MapIconClicked?.Invoke(this, mapIcon.Value);
}
private void MaximizeButton_Clicked(object sender, EventArgs e)
{
this.MaximizeClicked?.Invoke(this, e);
if (e is MouseButtonEventArgs mouseButtonEventArgs)
{
mouseButtonEventArgs.Handled = true;
}
}
private static bool PositionsCollide(Position position1, Position position2)
{
var a = PositionRadius + PositionRadius;
var dx = position1.X - position2.X;
var dy = position1.Y - position2.Y;
return a * a > ((dx * dx) + (dy * dy));
}
private static bool IsValidPositionalEntity(IPositionalEntity entity)
{
if (entity.Position?.X == 0 &&
entity.Position?.Y == 0)
{
return false;
}
return true;
}
private static Color GetQuestColor(QuestMetadata questMetadata)
{
return QuestObjectiveColors.Colors[questMetadata.Quest?.Id % QuestObjectiveColors.Colors.Count ?? 0];
}
}
@@ -0,0 +1,37 @@
<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"
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 Height="1">
<Rectangle
Fill="{Binding ElementName=_this, Path=BarColor, Mode=OneWay}"
VerticalAlignment="Stretch"
HorizontalAlignment="{Binding ElementName=_this, Path=FillAlignment, Mode=OneWay}"
Width="{Binding ElementName=_this, Path=MaxResourceValue, Mode=TwoWay}"></Rectangle>
<Rectangle
Fill="#F0202020"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"></Rectangle>
<Rectangle
Fill="{Binding ElementName=_this, Path=BarColor, Mode=OneWay}"
VerticalAlignment="Stretch"
HorizontalAlignment="{Binding ElementName=_this, Path=FillAlignment, Mode=OneWay}"
Width="{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>
@@ -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;
}
}
@@ -0,0 +1,25 @@
<UserControl x:Class="Daybreak.Controls.LivingEntityContextMenu"
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:guildwars="clr-namespace:Daybreak.Models.Guildwars"
mc:Ignorable="d"
Background="#F0212121"
d:DataContext="{d:DesignInstance Type=guildwars:LivingEntity, IsDesignTimeCreatable=True}"
d:DesignHeight="300" d:DesignWidth="100">
<StackPanel Margin="5, 0, 5, 0">
<TextBlock
Text="{Binding Allegiance, Mode=OneWay}" FontSize="16" Foreground="White"></TextBlock>
<TextBlock Text="{Binding NpcDefinition.Name}" Foreground="LightBlue" Cursor="Hand"
MouseLeftButtonDown="TextBlock_MouseLeftButtonDown"></TextBlock>
<WrapPanel>
<TextBlock Text="Id: " Foreground="White"></TextBlock>
<TextBlock Text="{Binding ModelType, Mode=OneWay}" Foreground="White"></TextBlock>
</WrapPanel>
<WrapPanel>
<TextBlock Text="Lvl: " Foreground="White"></TextBlock>
<TextBlock Text="{Binding Level, Mode=OneWay}" Foreground="White"></TextBlock>
</WrapPanel>
</StackPanel>
</UserControl>
@@ -0,0 +1,23 @@
using Daybreak.Models.Guildwars;
using System;
using System.Windows.Controls;
using System.Windows.Input;
namespace Daybreak.Controls;
/// <summary>
/// Interaction logic for LivingEntityContextMenu.xaml
/// </summary>
public partial class LivingEntityContextMenu : UserControl
{
public event EventHandler<LivingEntity?>? LivingEntityContextMenuClicked;
public LivingEntityContextMenu()
{
this.InitializeComponent();
}
private void TextBlock_MouseLeftButtonDown(object _, MouseButtonEventArgs e)
{
this.LivingEntityContextMenuClicked?.Invoke(this, this.DataContext as LivingEntity? ?? default);
}
}
@@ -0,0 +1,25 @@
<UserControl x:Class="Daybreak.Controls.MainPlayerContextMenu"
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"
xmlns:guildwars="clr-namespace:Daybreak.Models.Guildwars"
mc:Ignorable="d"
Background="#F0212121"
d:DataContext="{d:DesignInstance Type=guildwars:MainPlayerInformation, IsDesignTimeCreatable=True}"
d:DesignHeight="300" d:DesignWidth="100">
<StackPanel Margin="5, 0, 5, 0">
<TextBlock
Text="{Binding Name, Mode=OneWay}" FontSize="16" Foreground="White"></TextBlock>
<WrapPanel>
<TextBlock Text="Lvl: " Foreground="White"></TextBlock>
<TextBlock Text="{Binding Level, Mode=OneWay}" Foreground="White"></TextBlock>
</WrapPanel>
<WrapPanel>
<TextBlock Text="{Binding PrimaryProfession.Alias, Mode=OneWay}" Foreground="White"></TextBlock>
<TextBlock Text="/" Foreground="White"></TextBlock>
<TextBlock Text="{Binding SecondaryProfession.Alias, Mode=OneWay}" Foreground="White"></TextBlock>
</WrapPanel>
</StackPanel>
</UserControl>
@@ -0,0 +1,14 @@
using System.Windows.Controls;
namespace Daybreak.Controls;
/// <summary>
/// Interaction logic for MainPlayerContextMenu.xaml
/// </summary>
public partial class MainPlayerContextMenu : UserControl
{
public MainPlayerContextMenu()
{
this.InitializeComponent();
}
}
+17
View File
@@ -0,0 +1,17 @@
<UserControl x:Class="Daybreak.Controls.MapIconContextMenu"
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"
xmlns:guildwars="clr-namespace:Daybreak.Models.Guildwars"
mc:Ignorable="d"
Background="#F0212121"
d:DataContext="{d:DesignInstance Type=guildwars:MapIcon, IsDesignTimeCreatable=True}"
d:DesignHeight="300" d:DesignWidth="100">
<StackPanel Margin="5, 0, 5, 0">
<TextBlock Text="Map Icon" Foreground="White" FontSize="16"></TextBlock>
<TextBlock Text="{Binding Icon.Name}" Foreground="LightBlue" Cursor="Hand"
MouseLeftButtonDown="TextBlock_MouseLeftButtonDown" Margin="0, 0, 0, 10"></TextBlock>
</StackPanel>
</UserControl>
@@ -0,0 +1,24 @@
using Daybreak.Models.Guildwars;
using System;
using System.Windows.Controls;
using System.Windows.Input;
namespace Daybreak.Controls;
/// <summary>
/// Interaction logic for MapIconContextMenu.xaml
/// </summary>
public partial class MapIconContextMenu : UserControl
{
public event EventHandler<MapIcon?>? MapIconContextMenuClicked;
public MapIconContextMenu()
{
this.InitializeComponent();
}
private void TextBlock_MouseLeftButtonDown(object _, MouseButtonEventArgs e)
{
this.MapIconContextMenuClicked?.Invoke(this, this.DataContext as MapIcon? ?? default);
}
}
+128 -48
View File
@@ -5,89 +5,169 @@
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Controls"
xmlns:buttons="clr-namespace:Daybreak.Controls.Buttons"
xmlns:glyphs="clr-namespace:Daybreak.Controls.Glyphs"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel>
<buttons:MenuButton Title="Game companion"
Foreground="White"
HighlightColor="White"
Height="30"
Clicked="GameCompanionButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:GoldenArrowGlyph Foreground="White"
<local:ExpandableMenuSection
SectionTitle="Guild Wars" Foreground="White"
FontSize="16">
<local:ExpandableMenuSection.Children>
<buttons:MenuButton Title="Game companion"
FontSize="12"
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="GameCompanionButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:GoldenArrowGlyph Foreground="White"
Width="40"></local:GoldenArrowGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Manage builds"
Foreground="White"
HighlightColor="White"
Height="30"
Clicked="ManageBuildsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:FireballGlyph Foreground="White"
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Manage builds"
FontSize="12"
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="ManageBuildsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:FireballGlyph Foreground="White"
Width="40"></local:FireballGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Account settings"
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Download Guildwars"
FontSize="12"
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="DownloadGuildwarsButton_Clicked">
<buttons:MenuButton.InnerContent>
<glyphs:DownloadGlyph Foreground="White"
Width="40"></glyphs:DownloadGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
</local:ExpandableMenuSection.Children>
</local:ExpandableMenuSection>
<local:ExpandableMenuSection
SectionTitle="Mods" Foreground="White"
FontSize="16">
<local:ExpandableMenuSection.Children>
<buttons:MenuButton Title="uMod"
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="UModButton_Clicked">
<buttons:MenuButton.InnerContent>
<glyphs:UModGlyph Foreground="White"
Width="40"></glyphs:UModGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="GWToolboxpp"
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="ToolboxButton_Clicked">
<buttons:MenuButton.InnerContent>
<glyphs:GWToolboxGlyph Foreground="White"
Width="40"></glyphs:GWToolboxGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
</local:ExpandableMenuSection.Children>
</local:ExpandableMenuSection>
<local:ExpandableMenuSection
SectionTitle="Settings" Foreground="White"
FontSize="16">
<local:ExpandableMenuSection.Children>
<buttons:MenuButton Title="Accounts"
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="AccountSettingsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:AvatarGlyph Foreground="White"
<buttons:MenuButton.InnerContent>
<local:AvatarGlyph Foreground="White"
Width="40"></local:AvatarGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Guildwars settings"
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Executables"
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="GuildwarsSettingsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:FileGlyph Foreground="White"
<buttons:MenuButton.InnerContent>
<local:FileGlyph Foreground="White"
Width="40"></local:FileGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Launcher settings"
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Launcher"
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="LauncherSettingsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:CogGlyph Foreground="White"
<buttons:MenuButton.InnerContent>
<local:CogGlyph Foreground="White"
Width="40"
Height="20"></local:CogGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Experimental settings"
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Experimental"
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="ExperimentalSettingsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:ExperimentGlyph Foreground="White"
<buttons:MenuButton.InnerContent>
<local:ExperimentGlyph Foreground="White"
Width="40"></local:ExperimentGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Manage client version"
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
</local:ExpandableMenuSection.Children>
</local:ExpandableMenuSection>
<local:ExpandableMenuSection
SectionTitle="Diagnostics" Foreground="White"
FontSize="16">
<local:ExpandableMenuSection.Children>
<buttons:MenuButton Title="Manage client version"
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="VersionManagementButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:StaticGlyph Foreground="White"
<buttons:MenuButton.InnerContent>
<local:StaticGlyph Foreground="White"
Width="40"></local:StaticGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Logs"
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Logs"
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="LogsButton_Clicked">
<buttons:MenuButton.InnerContent>
<local:LogsGlyph Foreground="White"
<buttons:MenuButton.InnerContent>
<local:LogsGlyph Foreground="White"
Width="40"></local:LogsGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
<buttons:MenuButton Title="Metrics"
Foreground="White"
HighlightColor="White"
Height="30"
Cursor="Hand"
Clicked="MetricsButton_Clicked">
<buttons:MenuButton.InnerContent>
<glyphs:GaugeGlyph Foreground="White"
Width="40"></glyphs:GaugeGlyph>
</buttons:MenuButton.InnerContent>
</buttons:MenuButton>
</local:ExpandableMenuSection.Children>
</local:ExpandableMenuSection>
</StackPanel>
</UserControl>
+49 -21
View File
@@ -1,10 +1,13 @@
using Daybreak.Configuration;
using Daybreak.Configuration.Options;
using Daybreak.Launch;
using Daybreak.Services.Navigation;
using Daybreak.Views;
using Daybreak.Views.Onboarding.Toolbox;
using Daybreak.Views.Onboarding.UMod;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Configuration;
using System.Core.Extensions;
using System.Windows.Controls;
namespace Daybreak.Controls;
@@ -14,64 +17,89 @@ namespace Daybreak.Controls;
/// </summary>
public partial class MenuList : UserControl
{
private readonly IViewManager viewManager;
private readonly ILiveOptions<LauncherOptions> liveOptions;
public MenuList()
: this(
Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>(),
Launcher.Instance.ApplicationServiceProvider.GetRequiredService<ILiveOptions<LauncherOptions>>())
{
this.InitializeComponent();
}
private MenuList(
IViewManager viewManager,
ILiveOptions<LauncherOptions> liveOptions)
{
this.viewManager = viewManager.ThrowIfNull();
this.liveOptions = liveOptions.ThrowIfNull();
}
private void GameCompanionButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
viewManager.ShowView<LauncherView>();
this.viewManager.ShowView<LauncherView>();
}
private void AccountSettingsButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
viewManager.ShowView<AccountsView>();
this.viewManager.ShowView<AccountsView>();
}
private void GuildwarsSettingsButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
viewManager.ShowView<ExecutablesView>();
this.viewManager.ShowView<ExecutablesView>();
}
private void LauncherSettingsButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
viewManager.ShowView<SettingsView>();
this.viewManager.ShowView<SettingsView>();
}
private void ExperimentalSettingsButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
viewManager.ShowView<ExperimentalSettingsView>();
this.viewManager.ShowView<ExperimentalSettingsView>();
}
private void ManageBuildsButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
var options = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<ILiveOptions<ApplicationConfiguration>>();
if (options.Value.ExperimentalFeatures.DownloadIcons)
if (this.liveOptions.Value.DownloadIcons)
{
viewManager.ShowView<IconDownloadView>();
this.viewManager.ShowView<IconDownloadView>();
}
else
{
viewManager.ShowView<BuildsListView>();
this.viewManager.ShowView<BuildsListView>();
}
}
private void VersionManagementButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
viewManager.ShowView<VersionManagementView>();
this.viewManager.ShowView<VersionManagementView>();
}
private void LogsButton_Clicked(object sender, EventArgs e)
{
var viewManager = Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>();
viewManager.ShowView<LogsView>();
this.viewManager.ShowView<LogsView>();
}
private void MetricsButton_Clicked(object sender, EventArgs e)
{
this.viewManager.ShowView<MetricsView>();
}
private void DownloadGuildwarsButton_Clicked(object sender, EventArgs e)
{
this.viewManager.ShowView<GuildwarsDownloadView>();
}
private void UModButton_Clicked(object sender, EventArgs e)
{
this.viewManager.ShowView<UModOnboardingEntryView>();
}
private void ToolboxButton_Clicked(object sender, EventArgs e)
{
this.viewManager.ShowView<ToolboxOnboardingEntryView>();
}
}
+29
View File
@@ -0,0 +1,29 @@
<UserControl x:Class="Daybreak.Controls.PlayerContextMenu"
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:guildwars="clr-namespace:Daybreak.Models.Guildwars"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=guildwars:PlayerInformation, IsDesignTimeCreatable=True}"
d:DesignHeight="300" d:DesignWidth="100"
Margin="5, 0, 5, 0">
<StackPanel Background="#F0212121">
<TextBlock Text="Party Member" FontSize="16" Foreground="White"></TextBlock>
<TextBlock Text="{Binding NpcDefinition.Name}" Foreground="LightBlue" Cursor="Hand"
MouseLeftButtonDown="TextBlock_MouseLeftButtonDown"></TextBlock>
<WrapPanel>
<TextBlock Text="Id: " Foreground="White"></TextBlock>
<TextBlock Text="{Binding ModelType, Mode=OneWay}" Foreground="White"></TextBlock>
</WrapPanel>
<WrapPanel>
<TextBlock Text="Lvl: " Foreground="White"></TextBlock>
<TextBlock Text="{Binding Level, Mode=OneWay}" Foreground="White"></TextBlock>
</WrapPanel>
<WrapPanel>
<TextBlock Text="{Binding PrimaryProfession.Alias, Mode=OneWay}" Foreground="White"></TextBlock>
<TextBlock Text="/" Foreground="White"></TextBlock>
<TextBlock Text="{Binding SecondaryProfession.Alias, Mode=OneWay}" Foreground="White"></TextBlock>
</WrapPanel>
</StackPanel>
</UserControl>
@@ -0,0 +1,23 @@
using Daybreak.Models.Guildwars;
using System;
using System.Windows.Controls;
using System.Windows.Input;
namespace Daybreak.Controls;
/// <summary>
/// Interaction logic for PlayerContextMenu.xaml
/// </summary>
public partial class PlayerContextMenu : UserControl
{
public event EventHandler<PlayerInformation?>? PlayerContextMenuClicked;
public PlayerContextMenu()
{
this.InitializeComponent();
}
private void TextBlock_MouseLeftButtonDown(object _, MouseButtonEventArgs e)
{
this.PlayerContextMenuClicked?.Invoke(this, this.DataContext as PlayerInformation? ?? default);
}
}
+17
View File
@@ -0,0 +1,17 @@
<UserControl x:Class="Daybreak.Controls.QuestContextMenu"
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"
xmlns:guildwars="clr-namespace:Daybreak.Models.Guildwars"
mc:Ignorable="d"
Background="#F0212121"
d:DataContext="{d:DesignInstance Type=guildwars:QuestMetadata, IsDesignTimeCreatable=True}"
d:DesignHeight="300" d:DesignWidth="100">
<StackPanel Margin="5, 0, 5, 0">
<TextBlock Text="{Binding Quest.Name}" FontSize="16" Foreground="White" TextWrapping="Wrap"></TextBlock>
<TextBlock Text="{Binding Quest.WikiUrl}" TextWrapping="Wrap" Foreground="LightBlue" Cursor="Hand"
MouseLeftButtonDown="TextBlock_MouseLeftButtonDown"></TextBlock>
</StackPanel>
</UserControl>
@@ -0,0 +1,24 @@
using Daybreak.Models.Guildwars;
using System;
using System.Windows.Controls;
using System.Windows.Input;
namespace Daybreak.Controls;
/// <summary>
/// Interaction logic for QuestContextMenu.xaml
/// </summary>
public partial class QuestContextMenu : UserControl
{
public event EventHandler<QuestMetadata?>? QuestContextMenuClicked;
public QuestContextMenu()
{
this.InitializeComponent();
}
private void TextBlock_MouseLeftButtonDown(object _, MouseButtonEventArgs e)
{
this.QuestContextMenuClicked?.Invoke(this, this.DataContext as QuestMetadata? ?? default);
}
}
-1
View File
@@ -1,6 +1,5 @@
using System;
using System.Extensions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
@@ -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,11 +18,15 @@ 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)]
private bool canSubtract;
[GenerateDependencyProperty]
private int attributePoints;
public AttributeTemplate()
{
@@ -28,6 +34,31 @@ public partial class AttributeTemplate : UserControl
this.DataContextChanged += this.AttributeTemplate_DataContextChanged;
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == AttributePointsProperty &&
this.DataContext is AttributeEntry attributeEntry)
{
var remainingPoints = this.AttributePoints;
var requiredPointsForNextLevel = this.attributePointCalculator?.GetPointsRequiredToIncreaseRank(attributeEntry.Points) ?? 0;
if (remainingPoints < requiredPointsForNextLevel)
{
this.CanAdd = false;
}
else if (this.DataContext.As<AttributeEntry>().Points < 12)
{
this.CanAdd = true;
}
}
}
public void InitializeAttributeTemplate(
IAttributePointCalculator attributePointCalculator)
{
this.attributePointCalculator = attributePointCalculator.ThrowIfNull();
}
private void AttributeTemplate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is AttributeEntry attributeEntry)
+68 -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,27 @@
<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"
AttributePoints="{Binding Path=AttributePoints, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}"></local:AttributeTemplate>
</DataTemplate>
</ListBox.ItemTemplate>
</ListBox>
@@ -98,60 +94,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 +166,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 +185,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>
+223 -300
View File
@@ -1,14 +1,12 @@
using Daybreak.Configuration;
using Daybreak.Launch;
using Daybreak.Models.Builds;
using Daybreak.Models.Guildwars;
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.IconRetrieve;
using Daybreak.Utils;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Configuration;
using System.ComponentModel;
using System.Core.Extensions;
using System.Extensions;
using System.Linq;
@@ -30,11 +28,12 @@ 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 IIconBrowser? iconBrowser;
private BuildEntry? loadedBuild;
private bool showingSkillList = false;
private bool replacingSecondaryProfession;
private bool replacingPrimaryProfession;
private IAttributePointCalculator? attributePointCalculator;
private SkillTemplate? selectingSkillTemplate;
private List<Skill>? skillListCache;
private CancellationTokenSource? cancellationTokenSource = new();
public event EventHandler? BuildChanged;
@@ -42,107 +41,31 @@ public partial class BuildTemplate : UserControl
[GenerateDependencyProperty]
private string skillSearchText = string.Empty;
[GenerateDependencyProperty]
private Profession primaryProfession = default!;
private BuildEntry buildEntry;
[GenerateDependencyProperty]
private Profession secondaryProfession = default!;
private int attributePoints;
[GenerateDependencyProperty]
private Skill skill0 = default!;
private List<Skill> availableSkills = new();
[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!;
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);
private List<Profession> professions = new();
public BuildTemplate()
: this(Launcher.Instance.ApplicationServiceProvider.GetService<IAttributePointCalculator>()!)
{
this.InitializeComponent();
this.InitializeProperties();
this.DataContextChanged += this.BuildTemplate_DataContextChanged;
}
public async void InitializeTemplate(
IIconCache iconRetriever,
IIconBrowser iconBrowser,
ILiveOptions<ApplicationConfiguration> liveOptions,
IBuildTemplateManager buildTemplateManager,
ILogger<ChromiumBrowserWrapper> logger)
public BuildTemplate(
IAttributePointCalculator attributePointCalculator)
{
this.iconBrowser = iconBrowser.ThrowIfNull();
await this.SkillBrowser.InitializeDefaultBrowser(liveOptions, buildTemplateManager, logger);
this.SkillTemplate0.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate1.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate2.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate3.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate4.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate5.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate6.InitializeSkillTemplate(iconRetriever);
this.SkillTemplate7.InitializeSkillTemplate(iconRetriever);
this.attributePointCalculator = attributePointCalculator.ThrowIfNull();
this.InitializeComponent();
this.HideSkillListView();
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());
}
}
this.buildEntry = new BuildEntry();
this.DataContextChanged += this.BuildTemplate_DataContextChanged;
}
private void BuildTemplate_Unloaded(object sender, RoutedEventArgs e)
@@ -150,144 +73,31 @@ public partial class BuildTemplate : UserControl
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!);
this.LoadSkills();
}
}
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 +111,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 +127,104 @@ 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.AvailableSkills is not null &&
this.skillListCache?.Except(this.AvailableSkills).None() is true &&
this.skillListCache.Count == this.AvailableSkills.Count)
{
return;
}
this.BrowseToInfo(this.PrimaryProfession.Name!);
this.AvailableSkills = 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 +233,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 +254,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 +282,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,77 +324,76 @@ 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 ListView_NavigateWithMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
private void ProfessionListView_MouseDoubleClick(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
e.Handled = true;
if (e.Delta > 0)
var selected = sender.As<ListView>().SelectedItem.As<Profession>();
if (this.replacingPrimaryProfession)
{
sender.As<ListView>().SelectedIndex = sender.As<ListView>().SelectedIndex > 0 ?
sender.As<ListView>().SelectedIndex - 1 :
0;
this.BuildEntry!.Primary = selected;
}
else
else if (this.replacingSecondaryProfession)
{
sender.As<ListView>().SelectedIndex = sender.As<ListView>().SelectedIndex < sender.As<ListView>().Items.Count - 1 ?
sender.As<ListView>().SelectedIndex + 1 :
sender.As<ListView>().Items.Count;
this.BuildEntry!.Secondary = selected;
}
this.HideProfessionListView();
}
private void ScrollViewer_PreviewMouseWheel(object sender, System.Windows.Input.MouseWheelEventArgs e)
private void SecondaryProfessionButton_Clicked(object sender, EventArgs e)
{
if (sender is not ScrollViewer scrollViewer)
{
return;
}
e.Handled = true;
scrollViewer.ScrollToVerticalOffset(scrollViewer.VerticalOffset - e.Delta);
this.replacingPrimaryProfession = false;
this.replacingSecondaryProfession = true;
this.Professions = Profession.Professions.Where(p => p != this.BuildEntry?.Secondary).ToList();
this.ShowProfessionListView();
}
private async IAsyncEnumerable<Skill> FilterSkills(string searchTerm)
private void PrimaryProfessionButton_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 = true;
this.replacingSecondaryProfession = false;
this.Professions.ClearAnd().AddRange(Profession.Professions.Where(p => p != this.BuildEntry?.Primary));
this.ShowProfessionListView();
}
if (skill.Profession != this.PrimaryProfession &&
skill.Profession != this.SecondaryProfession &&
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 AttributeTemplate_Loaded(object sender, RoutedEventArgs e)
{
sender.As<AttributeTemplate>().InitializeAttributeTemplate(this.attributePointCalculator!);
}
}
@@ -0,0 +1,11 @@
<UserControl x:Class="Daybreak.Controls.Templates.QuestLogTemplate"
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.Templates"
mc:Ignorable="d"
d:DesignHeight="450" d:DesignWidth="800">
<StackPanel x:Name="ItemStackPanel">
</StackPanel>
</UserControl>
@@ -0,0 +1,170 @@
using Daybreak.Models.Guildwars;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows;
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;
/// <summary>
/// Interaction logic for QuestLogTemplate.xaml
/// </summary>
public partial class QuestLogTemplate : UserControl
{
private const string UncategorizedQuestsString = "Uncategorized Quests";
private List<IGrouping<Map?, QuestMetadata>>? questLogCache;
[GenerateDependencyProperty]
private List<QuestMetadata> quests;
public event EventHandler<Map?>? MapClicked;
public event EventHandler<Quest?>? QuestClicked;
public QuestLogTemplate()
{
this.quests = new List<QuestMetadata>();
this.InitializeComponent();
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (e.Property == QuestsProperty)
{
this.DrawQuestLogLayout();
}
}
private void DrawQuestLogLayout()
{
var questLog = this.Quests?.GroupBy(q => q.From).OrderBy(g => g.Key?.Name).ToList();
if (!this.DetectQuestLogChange(questLog))
{
return;
}
this.questLogCache = questLog;
if (this.questLogCache is null)
{
return;
}
this.ItemStackPanel.Children.Clear();
foreach(var grouping in this.questLogCache)
{
var location = grouping.Key;
var locationTextBlock = new OpaqueButton
{
Text = grouping.Key?.Name ?? UncategorizedQuestsString,
FontSize = 18,
Cursor = grouping.Key is null ? Cursors.Arrow : Cursors.Hand,
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);
var rectangle = new Rectangle
{
Height = 1,
};
var rectangleForegroundBinding = new Binding("Foreground")
{
Source = this
};
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
});
}
}
private bool DetectQuestLogChange(List<IGrouping<Map?, QuestMetadata>>? newQuestLog)
{
if (this.questLogCache is null)
{
return true;
}
if (newQuestLog is null)
{
return true;
}
if (this.questLogCache.Count != newQuestLog.Count)
{
return true;
}
for (var i = 0; i < this.questLogCache.Count; i++)
{
var grouping1 = this.questLogCache[i];
var grouping2 = newQuestLog[i];
if (grouping1.Key?.Id != grouping2.Key?.Id)
{
return true;
}
var cachedQuests = grouping1.ToList();
var newQuests = grouping2.ToList();
if (cachedQuests.Count != newQuests.Count)
{
return true;
}
for (var j = 0; j < cachedQuests.Count; j++)
{
if (cachedQuests[j].Quest?.Id != newQuests[j].Quest?.Id)
{
return true;
}
}
}
return false;
}
private void OnMapClicked(Map? map)
{
this.MapClicked?.Invoke(this, map);
}
private void OnQuestClicked(Quest? quest)
{
this.QuestClicked?.Invoke(this, quest);
}
}
@@ -1,6 +1,9 @@
using Daybreak.Models.Guildwars;
using Daybreak.Launch;
using Daybreak.Models.Guildwars;
using Daybreak.Services.IconRetrieve;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Core.Extensions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
@@ -27,15 +30,16 @@ public partial class SkillTemplate : UserControl
private double borderOpacity;
public SkillTemplate()
: this(Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IIconCache>())
{
this.InitializeComponent();
this.DataContextChanged += this.SkillTemplate_DataContextChanged;
}
public void InitializeSkillTemplate(IIconCache iconRetriever)
public SkillTemplate(
IIconCache iconCache)
{
this.iconRetriever = iconRetriever;
this.SkillTemplate_DataContextChanged(this, new DependencyPropertyChangedEventArgs(UserControl.DataContextProperty, null, this.DataContext));
this.iconRetriever = iconCache.ThrowIfNull();
this.InitializeComponent();
this.DataContextChanged += this.SkillTemplate_DataContextChanged;
}
private async void SkillTemplate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
@@ -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>
@@ -0,0 +1,32 @@
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 VerticalResourceBar : UserControl
{
[GenerateDependencyProperty]
private double currentResourceValue;
[GenerateDependencyProperty]
private double maxResourceValue;
[GenerateDependencyProperty]
private Brush barColor;
[GenerateDependencyProperty]
private string text;
[GenerateDependencyProperty(InitialValue = VerticalAlignment.Bottom)]
private VerticalAlignment fillAlignment;
public VerticalResourceBar()
{
this.InitializeComponent();
this.MaxResourceValue = 1;
this.CurrentResourceValue = 0;
this.barColor = Brushes.Transparent;
this.text = string.Empty;
}
}
@@ -0,0 +1,21 @@
<UserControl x:Class="Daybreak.Controls.WorldPlayerContextMenu"
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"
xmlns:guildwars="clr-namespace:Daybreak.Models.Guildwars"
mc:Ignorable="d"
d:DataContext="{d:DesignInstance Type=guildwars:WorldPlayerInformation, IsDesignTimeCreatable=True}"
d:DesignHeight="300" d:DesignWidth="100">
<StackPanel Background="#F0212121">
<TextBlock
Margin="5, 0, 5, 0"
Text="{Binding Name, Mode=OneWay}" FontSize="16" Foreground="White"></TextBlock>
<WrapPanel
Margin="5, 0, 5, 0">
<TextBlock Text="Lvl: " Foreground="White"></TextBlock>
<TextBlock Text="{Binding Level, Mode=OneWay}" Foreground="White"></TextBlock>
</WrapPanel>
</StackPanel>
</UserControl>
@@ -0,0 +1,14 @@
using System.Windows.Controls;
namespace Daybreak.Controls;
/// <summary>
/// Interaction logic for WorldPlayerContextMenu.xaml
/// </summary>
public partial class WorldPlayerContextMenu : UserControl
{
public WorldPlayerContextMenu()
{
this.InitializeComponent();
}
}
+37 -57
View File
@@ -8,16 +8,44 @@
<UseWPF>true</UseWPF>
<Nullable>enable</Nullable>
<UseWindowsForms>true</UseWindowsForms>
<Platforms>AnyCPU;x86</Platforms>
<Platforms>AnyCPU;x86;x64</Platforms>
<CopyLocalLockFileAssemblies>true</CopyLocalLockFileAssemblies>
<LangVersion>preview</LangVersion>
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
<Version>0.9.8</Version>
<Version>0.9.8.41</Version>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UserSecretsId>cfb2a489-db80-448d-a969-80270f314c46</UserSecretsId>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
</PropertyGroup>
<ItemGroup>
<None Remove="Services\Drawing\Resources\Bag.svg" />
<None Remove="Services\Drawing\Resources\CircledStar.svg" />
<None Remove="Services\Drawing\Resources\Dungeon.svg" />
<None Remove="Services\Drawing\Resources\DungeonBoss.svg" />
<None Remove="Services\Drawing\Resources\DungeonKey.svg" />
<None Remove="Services\Drawing\Resources\Scroll.svg" />
<None Remove="Services\Drawing\Resources\Skull.svg" />
<None Remove="Services\Drawing\Resources\StairsDown.svg" />
<None Remove="Services\Drawing\Resources\StairsUp.svg" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Services\Drawing\Resources\Bag.svg" />
<EmbeddedResource Include="Services\Drawing\Resources\CircledStar.svg" />
<EmbeddedResource Include="Services\Drawing\Resources\Dungeon.svg" />
<EmbeddedResource Include="Services\Drawing\Resources\DungeonBoss.svg" />
<EmbeddedResource Include="Services\Drawing\Resources\Scroll.svg" />
<EmbeddedResource Include="Services\Drawing\Resources\Skull.svg" />
<EmbeddedResource Include="Services\Drawing\Resources\StairsDown.svg" />
<EmbeddedResource Include="Services\Drawing\Resources\StairsUp.svg" />
</ItemGroup>
<ItemGroup>
<EmbeddedResource Include="Services\Drawing\Resources\DungeonKey.svg" />
</ItemGroup>
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
</ItemGroup>
@@ -37,76 +65,28 @@
</Target>
<ItemGroup>
<PackageReference Include="LiteDB" Version="5.0.15" />
<PackageReference Include="LiteDB" Version="5.0.16" />
<PackageReference Include="Microsoft.CorrelationVector" Version="1.0.42" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="7.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="7.0.0" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1587.40" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.1661.34" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.39" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.2" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
<PackageReference Include="ScottPlot.WPF" Version="4.1.63" />
<PackageReference Include="securifybv.ShellLink" Version="0.1.0" />
<PackageReference Include="Slim" Version="1.9.2" />
<PackageReference Include="Svg" Version="3.4.4" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="SystemExtensions.NetCore" Version="1.0.1" />
<PackageReference Include="SystemExtensions.NetStandard" Version="1.5.0" />
<PackageReference Include="WCL" Version="1.0.2" />
<PackageReference Include="WpfExtended" Version="0.7.1" />
<PackageReference Include="WpfExtended" Version="0.7.2" />
<PackageReference Include="WpfExtended.SourceGeneration" Version="0.1.1" />
<PackageReference Include="WpfScreenHelper" Version="2.1.0" />
<PackageReference Include="WriteableBitmapEx" Version="1.6.8" />
</ItemGroup>
<ItemGroup>
<Compile Update="Controls\Buttons\SynchronizeButton.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Controls\Buttons\HelpButton.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Controls\Buttons\MinusButton.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Controls\Glyphs\StarGlyph.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Controls\Buttons\CancelButton.xaml.cs">
<SubType>Code</SubType>
</Compile>
<Compile Update="Views\IconDownloadView.xaml.cs">
<SubType>Code</SubType>
</Compile>
</ItemGroup>
<ItemGroup>
<Page Update="Controls\Buttons\SynchronizeButton.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType>
</Page>
<Page Update="Controls\Buttons\HelpButton.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType>
</Page>
<Page Update="Controls\Buttons\MinusButton.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType>
</Page>
<Page Update="Controls\Buttons\MaximizeButton.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType>
</Page>
<Page Update="Controls\Glyphs\StarGlyph.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType>
</Page>
<Page Update="Controls\Buttons\CancelButton.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType>
</Page>
<Page Update="Views\IconDownloadView.xaml">
<XamlRuntime>$(DefaultXamlRuntime)</XamlRuntime>
<SubType>Designer</SubType>
</Page>
</ItemGroup>
<Target Name="PostBuild" AfterTargets="PostBuildEvent" Condition="'$(Configuration)' == 'Release'">
<Exec Command="echo.&gt;$(Version).version" />
+17 -58
View File
@@ -1,17 +1,15 @@
using Daybreak.Configuration;
using Daybreak.Exceptions;
using Daybreak.Services.Drawing;
using Daybreak.Services.ExceptionHandling;
using Daybreak.Services.Navigation;
using Daybreak.Services.Options;
using Daybreak.Services.Startup;
using Daybreak.Services.Updater.PostUpdate;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Slim;
using Slim.Integration.ServiceCollection;
using System;
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Windows;
using System.Windows.Extensions;
namespace Daybreak.Launch;
@@ -21,6 +19,7 @@ public sealed class Launcher : ExtendedApplication<MainWindow>
public readonly static Launcher Instance = new();
private ILogger? logger;
private IExceptionHandler? exceptionHandler;
public System.IServiceProvider ApplicationServiceProvider => this.ServiceProvider;
@@ -36,71 +35,30 @@ public sealed class Launcher : ExtendedApplication<MainWindow>
ProjectConfiguration.RegisterResolvers(serviceManager);
return services.BuildSlimServiceProvider(serviceManager);
}
protected override void RegisterServices(IServiceCollection services)
{
ProjectConfiguration.RegisterServices(services);
}
protected override bool HandleException(Exception e)
{
if (e is null)
{
return false;
}
if (this.logger is null)
{
return false;
}
if (e is FatalException fatalException)
{
this.logger.LogCritical(e, $"{nameof(FatalException)} encountered. Closing application");
MessageBox.Show(fatalException.ToString());
File.WriteAllText("crash.log", e.ToString());
return false;
}
else if (e is TargetInvocationException targetInvocationException && e.InnerException is FatalException innerFatalException)
{
this.logger.LogCritical(e, $"{nameof(FatalException)} encountered. Closing application");
MessageBox.Show(innerFatalException.ToString());
File.WriteAllText("crash.log", e.ToString());
return false;
}
else if (e is AggregateException aggregateException)
{
if (aggregateException.InnerExceptions.FirstOrDefault() is COMException comException &&
comException.Message.Contains("Invalid window handle"))
{
/*
* Ignore exception caused by browser failing to initialize due to missing window.
* Likely caused by switching views before browser was initialized.
*/
this.logger.LogError(e, "Failed to initialize browser");
return true;
}
}
else if (e.Message.Contains("Invalid window handle.") && e.StackTrace?.Contains("CoreWebView2Environment.CreateCoreWebView2ControllerAsync") is true)
{
/*
* Ignore exception caused by browser failing to initialize due to missing window.
* Likely caused by switching views before the browser was initialized.
*/
this.logger.LogError(e, "Failed to initialize browser");
return true;
}
this.logger.LogError(e, $"Unhandled exception caught {e.GetType()}");
MessageBox.Show(e.ToString());
return true;
return this.exceptionHandler?.HandleException(e) is true;
}
protected override void ApplicationStarting()
{
ProjectConfiguration.RegisterViews(this.ServiceProvider.GetService<IViewManager>()!);
ProjectConfiguration.RegisterPostUpdateActions(this.ServiceProvider.GetService<IPostUpdateActionProducer>()!);
ProjectConfiguration.RegisterOptions(this.ServiceProvider.GetRequiredService<IOptionsProducer>()!);
ProjectConfiguration.RegisterViews(this.ServiceProvider.GetRequiredService<IViewManager>()!);
ProjectConfiguration.RegisterPostUpdateActions(this.ServiceProvider.GetRequiredService<IPostUpdateActionProducer>()!);
ProjectConfiguration.RegisterStartupActions(this.ServiceProvider.GetRequiredService<IStartupActionProducer>()!);
ProjectConfiguration.RegisterDrawingModules(this.ServiceProvider.GetRequiredService<IDrawingModuleProducer>()!);
this.logger = this.ServiceProvider.GetRequiredService<ILogger<Launcher>>();
this.exceptionHandler = this.ServiceProvider.GetRequiredService<IExceptionHandler>();
this.RegisterViewContainer();
}
protected override void ApplicationClosing()
{
}
@@ -111,6 +69,7 @@ public sealed class Launcher : ExtendedApplication<MainWindow>
var mainWindow = this.ServiceProvider.GetRequiredService<MainWindow>();
viewManager.RegisterContainer(mainWindow.Container);
}
private static int LaunchMainWindow()
{
return Instance.Run();
+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>
+25 -8
View File
@@ -1,4 +1,4 @@
using Daybreak.Configuration;
using Daybreak.Configuration.Options;
using Daybreak.Services.Bloogum;
using Daybreak.Services.IconRetrieve;
using Daybreak.Services.Menu;
@@ -16,7 +16,6 @@ using System.Extensions;
using System.Threading;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Controls.Primitives;
using System.Windows.Extensions;
using System.Windows.Input;
using System.Windows.Interop;
@@ -29,7 +28,7 @@ namespace Daybreak.Launch;
/// <summary>
/// Interaction logic for MainWindow.xaml
/// </summary>
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add readonly modifier", Justification = "Fields used by source generator for DependencyProperty")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("Style", "IDE0044:Add read-only modifier", Justification = "Fields used by source generator for DependencyProperty")]
[System.Diagnostics.CodeAnalysis.SuppressMessage("CodeQuality", "IDE0052:Remove unread private members", Justification = "Used by source generators")]
public partial class MainWindow : Window
{
@@ -40,7 +39,7 @@ public partial class MainWindow : Window
private readonly IApplicationUpdater applicationUpdater;
private readonly IPrivilegeManager privilegeManager;
private readonly IIconDownloader iconDownloader;
private readonly ILiveOptions<ApplicationConfiguration> liveOptions;
private readonly ILiveOptions<LauncherOptions> launcherOptions;
private readonly CancellationTokenSource cancellationToken = new();
[GenerateDependencyProperty]
@@ -52,6 +51,8 @@ public partial class MainWindow : Window
[GenerateDependencyProperty]
private bool isShowingDropdown;
public event EventHandler<MainWindow>? WindowParametersChanged;
public MainWindow(
IMenuServiceInitializer menuServiceInitializer,
IViewManager viewManager,
@@ -60,7 +61,7 @@ public partial class MainWindow : Window
IApplicationUpdater applicationUpdater,
IPrivilegeManager privilegeManager,
IIconDownloader iconDownloader,
ILiveOptions<ApplicationConfiguration> liveOptions)
ILiveOptions<LauncherOptions> launcherOptions)
{
this.menuServiceInitializer = menuServiceInitializer.ThrowIfNull();
this.viewManager = viewManager.ThrowIfNull();
@@ -69,13 +70,29 @@ public partial class MainWindow : Window
this.applicationUpdater = applicationUpdater.ThrowIfNull();
this.privilegeManager = privilegeManager.ThrowIfNull();
this.iconDownloader = iconDownloader.ThrowIfNull();
this.liveOptions = liveOptions.ThrowIfNull();
this.launcherOptions = launcherOptions.ThrowIfNull();
this.InitializeComponent();
this.CurrentVersionText = this.applicationUpdater.CurrentVersion.ToString();
this.IsRunningAsAdmin = this.privilegeManager.AdminPrivileges;
this.SetupMenuService();
}
protected override void OnPropertyChanged(DependencyPropertyChangedEventArgs e)
{
base.OnPropertyChanged(e);
if (this.IsLoaded is false)
{
return;
}
if (e.Property == LeftProperty ||
e.Property == TopProperty ||
e.Property == ActualWidthProperty ||
e.Property == ActualHeightProperty)
{
this.WindowParametersChanged?.Invoke(this, this);
}
}
private void Window_Loaded(object sender, RoutedEventArgs e)
{
@@ -263,7 +280,7 @@ public partial class MainWindow : Window
private async void CheckForUpdates()
{
var updateAvailable = this.liveOptions.Value.AutoCheckUpdate is true && await this.applicationUpdater.UpdateAvailable().ConfigureAwait(true);
var updateAvailable = this.launcherOptions.Value.AutoCheckUpdate is true && await this.applicationUpdater.UpdateAvailable().ConfigureAwait(true);
if (updateAvailable)
{
this.viewManager.ShowView<AskUpdateView>();
@@ -320,6 +337,6 @@ public partial class MainWindow : Window
double R = color.ScR;
double G = color.ScG;
double B = color.ScB;
return 0.299 * R + 0.587 * G + 0.114 * B;
return (0.299 * R) + (0.587 * G) + (0.114 * B);
}
}
+2 -2
View File
@@ -12,12 +12,12 @@ public class BrowserPayload
ContextMenu
}
[JsonProperty("Key")]
[JsonProperty(nameof(Key))]
public PayloadKeys Key { get; set; }
}
public sealed class BrowserPayload<T> : BrowserPayload
{
[JsonProperty("Value")]
[JsonProperty(nameof(Value))]
public T? Value { get; set; }
}
@@ -0,0 +1,7 @@
namespace Daybreak.Models.Browser;
public sealed class DownloadPayload
{
public string? ResultingFilePath { get; init; }
public bool CanDownload { get; set; }
}
@@ -4,10 +4,10 @@ namespace Daybreak.Models.Browser;
public sealed class OnContextMenuPayload
{
[JsonProperty("X")]
[JsonProperty(nameof(X))]
public double X { get; set; }
[JsonProperty("Y")]
[JsonProperty(nameof(Y))]
public double Y { get; set; }
[JsonProperty("Selection")]
[JsonProperty(nameof(Selection))]
public string? Selection { get; set; }
}
+184
View File
@@ -1,5 +1,7 @@
using Daybreak.Models.Guildwars;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
namespace Daybreak.Models.Builds;
@@ -28,4 +30,186 @@ 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 != Profession.None &&
this.FirstSkill.Profession != this.Primary &&
this.FirstSkill.Profession != this.Secondary)
{
this.FirstSkill = Skill.NoSkill;
}
if (this.SecondSkill.Profession != Profession.None &&
this.SecondSkill.Profession != this.Primary &&
this.SecondSkill.Profession != this.Secondary)
{
this.SecondSkill = Skill.NoSkill;
}
if (this.ThirdSkill.Profession != Profession.None &&
this.ThirdSkill.Profession != this.Primary &&
this.ThirdSkill.Profession != this.Secondary)
{
this.ThirdSkill = Skill.NoSkill;
}
if (this.FourthSkill.Profession != Profession.None &&
this.FourthSkill.Profession != this.Primary &&
this.FourthSkill.Profession != this.Secondary)
{
this.FourthSkill = Skill.NoSkill;
}
if (this.FifthSkill.Profession != Profession.None &&
this.FifthSkill.Profession != this.Primary &&
this.FifthSkill.Profession != this.Secondary)
{
this.FifthSkill = Skill.NoSkill;
}
if (this.SixthSkill.Profession != Profession.None &&
this.SixthSkill.Profession != this.Primary &&
this.SixthSkill.Profession != this.Secondary)
{
this.SixthSkill = Skill.NoSkill;
}
if (this.SeventhSkill.Profession != Profession.None &&
this.SeventhSkill.Profession != this.Primary &&
this.SeventhSkill.Profession != this.Secondary)
{
this.SeventhSkill = Skill.NoSkill;
}
if (this.EigthSkill.Profession != Profession.None &&
this.EigthSkill.Profession != this.Primary &&
this.EigthSkill.Profession != this.Secondary)
{
this.EigthSkill = Skill.NoSkill;
}
}
}
+14
View File
@@ -0,0 +1,14 @@
namespace Daybreak.Models.Guildwars;
public enum Affiliation
{
Gray = 0b00000001,
Blue = 0b00000010,
Red = 0b00000100,
Yellow = 0b00001000,
Teal = 0b00010000,
Purple = 0b00100000,
Green = 0b01000000,
GrayNeutral = 0b10000000,
Any = 0b11111111
}
+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()
{
}
-20
View File
@@ -1,20 +0,0 @@
using System.Runtime.InteropServices;
namespace Daybreak.Models.Guildwars;
[StructLayout(LayoutKind.Explicit)]
public readonly struct CharContext
{
public const int BaseOffset = 0x0074;
[FieldOffset(0x0000)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x28)]
public readonly byte[] PlayerNameBytes;
[FieldOffset(0x01B0)]
public readonly int Language;
[FieldOffset(0x0344)]
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x80)]
public readonly byte[] PlayerEmailBytes;
}
@@ -0,0 +1,11 @@
using System.Collections.Generic;
namespace Daybreak.Models.Guildwars;
public sealed class DebounceResponse
{
public MainPlayerInformation MainPlayer { get; init; } = default;
public IEnumerable<WorldPlayerInformation> WorldPlayers { get; init; } = default!;
public IEnumerable<PlayerInformation> Party { get; init; } = default!;
public IEnumerable<LivingEntity> LivingEntities { get; init; } = default!;
}
-14
View File
@@ -1,14 +0,0 @@
using System;
using System.Runtime.InteropServices;
namespace Daybreak.Models.Guildwars;
[StructLayout(LayoutKind.Explicit)]
public readonly struct GameContext
{
[FieldOffset(0x002C)]
public readonly IntPtr WorldContext;
[FieldOffset(0x0044)]
public readonly IntPtr CharContext;
}
+16
View File
@@ -0,0 +1,16 @@
using Daybreak.Models.Guildwars;
using System.Collections.Generic;
namespace Daybreak.Models;
public readonly struct GameData
{
public bool Valid { get; init; }
public MainPlayerInformation? MainPlayer { get; init; }
public List<PlayerInformation>? Party { get; init; }
public UserInformation? User { get; init; }
public SessionInformation? Session { get; init; }
public List<WorldPlayerInformation>? WorldPlayers { get; init; }
public List<LivingEntity>? LivingEntities { get; init; }
public List<MapIcon>? MapIcons { get; init; }
}
@@ -0,0 +1,82 @@
using System;
using System.Collections.Generic;
using System.Linq;
namespace Daybreak.Models.Guildwars;
public sealed class GuildwarsIcon
{
public static GuildwarsIcon ResurrectionShrine { get; } = new GuildwarsIcon { Id = 191474, Name = "Resurrection Shrine", WikiUrl = "https://wiki.guildwars.com/wiki/Resurrection_Shrine" };
public static GuildwarsIcon Collector { get; } = new GuildwarsIcon { Id = 191482, Name = "Collector", WikiUrl = "https://wiki.guildwars.com/wiki/Collector" };
public static GuildwarsIcon AreaMap { get; } = new GuildwarsIcon { Id = 302781, Name = "Area Map" };
public static GuildwarsIcon StairsUp { get; } = new GuildwarsIcon { Id = 302783, Name = "Stairs Up" };
public static GuildwarsIcon StairsDown { get; } = new GuildwarsIcon { Id = 302784, Name = "Stairs Down" };
public static GuildwarsIcon Gate { get; } = new GuildwarsIcon { Id = 191464, Name = "Gate", WikiUrl = "" };
public static GuildwarsIcon Star { get; } = new GuildwarsIcon { Id = 191472, Name = "Star", WikiUrl = "" };
public static GuildwarsIcon DungeonBoss { get; } = new GuildwarsIcon { Id = 302779, Name = "Dungeon Boss" };
public static GuildwarsIcon DungeonKey { get; } = new GuildwarsIcon { Id = 302777, Name = "Dungeon Key" };
public static List<GuildwarsIcon> Icons { get; } = new()
{
ResurrectionShrine,
Collector,
AreaMap,
StairsUp,
StairsDown,
Gate,
Star,
DungeonBoss,
DungeonKey
};
public static bool TryParse(int id, out GuildwarsIcon icon)
{
icon = Icons.Where(icon => icon.Id == id).FirstOrDefault()!;
if (icon is null)
{
return false;
}
return true;
}
public static bool TryParse(string name, out GuildwarsIcon icon)
{
icon = Icons.Where(map => map.Name == name).FirstOrDefault()!;
if (icon is null)
{
return false;
}
return true;
}
public static GuildwarsIcon Parse(int id)
{
if (TryParse(id, out var icon) is false)
{
throw new InvalidOperationException($"Could not find an icon with id {id}");
}
return icon;
}
public static GuildwarsIcon Parse(string name)
{
if (TryParse(name, out var icon) is false)
{
throw new InvalidOperationException($"Could not find an icon with name {name}");
}
return icon;
}
public int Id { get; init; }
public Affiliation Affiliation { get; init; }
public string? Name { get; init; }
public string? WikiUrl { get; init; }
private GuildwarsIcon()
{
}
}
+7
View File
@@ -0,0 +1,7 @@
namespace Daybreak.Models.Guildwars;
public interface IEntity : IPositionalEntity
{
int Id { get; }
uint Timer { get; }
}
@@ -0,0 +1,5 @@
namespace Daybreak.Models.Guildwars;
public interface IPositionalEntity
{
Position? Position { get; }
}
+24
View File
@@ -0,0 +1,24 @@
namespace Daybreak.Models.Guildwars;
public readonly struct LivingEntity : IEntity
{
public int Id { get; init; }
public uint Timer { get; init; }
public uint? ModelType { get; init; }
public Npc? NpcDefinition { get; init; }
public Position? Position { get; init; }
public Profession? PrimaryProfession { get; init; }
public Profession? SecondaryProfession { get; init; }
public int Level { get; init; }
public LivingEntityState State { get; init; }
public LivingEntityAllegiance Allegiance { get; init; }
}
@@ -0,0 +1,12 @@
namespace Daybreak.Models.Guildwars;
public enum LivingEntityAllegiance
{
Unknown,
AllyNonAttackable,
Neutral,
Enemy,
SpiritOrPet,
Minion,
NpcOrMinipet
}

Some files were not shown because too many files have changed in this diff Show More