Compare commits

...
36 Commits
Author SHA1 Message Date
amacocian 28c3b2f8c1 Fix CD pipeline 2024-04-11 18:29:04 +02:00
amacocianandGitHub deeccd81b4 Release 0.9.9.26 (#628)
* Event Calendar (#625)

* Event calendar view

* Update GWCA (#626)

* Publish changelog (#627)

* Update dependencies
2024-04-11 16:26:22 +00:00
amacocianandGitHub 6a27245ae8 Release 0.9.9.25 (#618) 2024-04-02 16:59:21 +00:00
amacocianandGitHub 6f4063abb7 Release v0.9.9.24 (#617)
Fix price history functionality
Implement all runes
Update dependencies
2024-03-27 21:33:30 +00:00
amacocianandGitHub f307bcd665 Release 0.9.9.23 (#615)
* Create 0.9.9.23 release

* Update dependencies
2024-03-18 17:52:51 +00:00
amacocianandGitHub 02ca41ba6d Release 0.9.9.22 (#612)
* Fix Daybreak restart commands

* Option to disable dynamic backgrounds

* Improve game running detection
2024-03-04 15:33:00 +00:00
amacocianandGitHub a0ebc87b7c Release 0.9.9.21 (#609)
Fix updater packaging logic
2024-02-27 14:08:02 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
d21c55deb0 Bump Xotl/cool-github-releases from 1.1.8 to 1.1.10 (#608)
Bumps [Xotl/cool-github-releases](https://github.com/xotl/cool-github-releases) from 1.1.8 to 1.1.10.
- [Release notes](https://github.com/xotl/cool-github-releases/releases)
- [Commits](https://github.com/xotl/cool-github-releases/compare/v1.1.8...v1.1.10)

---
updated-dependencies:
- dependency-name: Xotl/cool-github-releases
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-27 12:53:07 +00:00
amacocianandGitHub 50a8131ec2 Update dependencies (#607)
Improve updater
2024-02-27 12:42:39 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
e70cfa3d99 Bump coverlet.collector from 6.0.0 to 6.0.1 (#596)
Bumps [coverlet.collector](https://github.com/coverlet-coverage/coverlet) from 6.0.0 to 6.0.1.
- [Release notes](https://github.com/coverlet-coverage/coverlet/releases)
- [Commits](https://github.com/coverlet-coverage/coverlet/compare/v6.0.0...v6.0.1)

---
updated-dependencies:
- dependency-name: coverlet.collector
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-24 20:46:19 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
4c0c45f1c7 Bump MSTest.TestFramework from 3.2.1 to 3.2.2 (#600)
Bumps [MSTest.TestFramework](https://github.com/microsoft/testfx) from 3.2.1 to 3.2.2.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v3.2.1...v3.2.2)

---
updated-dependencies:
- dependency-name: MSTest.TestFramework
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-24 20:32:37 +00:00
amacocianandGitHub a9e819a83a Release 0.9.9.19 (#602)
Disable GWCA component when Focus View is disabled
2024-02-24 20:20:11 +00:00
amacocianandGitHub 65830f9125 Release 0.9.9.18 (#595)
* Setup release 0.9.9.18

* Minimap transparency
Closes #594

* Toggle to display exploration percentage
Closes #593
2024-02-18 22:55:07 +01:00
amacocianandGitHub 3d9106addf Release 0.9.9.17 (#592)
* Setup release 0.9.9.17

* Save extracted minimap window position (#591)

Closes #590

* Add ability to pin minimap to screen
Closes #585

* Fix ObjectDisposedException
Closes #589
2024-02-18 16:04:52 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>amacocian
2863032d9a Bump MSTest.TestAdapter from 3.2.0 to 3.2.1 (#587)
Bumps [MSTest.TestAdapter](https://github.com/microsoft/testfx) from 3.2.0 to 3.2.1.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v.3.2.0...v3.2.1)

---
updated-dependencies:
- dependency-name: MSTest.TestAdapter
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Macocian Alexandru Victor <amacocian@yahoo.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2024-02-14 10:48:58 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
aada189b1b Bump MSTest.TestFramework from 3.2.0 to 3.2.1 (#588)
Bumps [MSTest.TestFramework](https://github.com/microsoft/testfx) from 3.2.0 to 3.2.1.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v.3.2.0...v3.2.1)

---
updated-dependencies:
- dependency-name: MSTest.TestFramework
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-14 11:40:09 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
22ea4de66e Bump NSubstitute.Analyzers.CSharp from 1.0.16 to 1.0.17 (#586)
Bumps [NSubstitute.Analyzers.CSharp](https://github.com/nsubstitute/NSubstitute.Analyzers) from 1.0.16 to 1.0.17.
- [Changelog](https://github.com/nsubstitute/NSubstitute.Analyzers/blob/1.0.17/ReleaseNotes.md)
- [Commits](https://github.com/nsubstitute/NSubstitute.Analyzers/compare/1.0.16...1.0.17)

---
updated-dependencies:
- dependency-name: NSubstitute.Analyzers.CSharp
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-12 05:46:15 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
9859617a74 Bump Microsoft.NET.Test.Sdk from 17.8.0 to 17.9.0 (#578)
Bumps [Microsoft.NET.Test.Sdk](https://github.com/microsoft/vstest) from 17.8.0 to 17.9.0.
- [Release notes](https://github.com/microsoft/vstest/releases)
- [Changelog](https://github.com/microsoft/vstest/blob/main/docs/releases.md)
- [Commits](https://github.com/microsoft/vstest/compare/v17.8.0...v17.9.0)

---
updated-dependencies:
- dependency-name: Microsoft.NET.Test.Sdk
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-07 15:41:59 +01:00
amacocianandGitHub 0629c1929d Release 0.9.9.16 (#582)
* Setup release 0.9.9.16

* Copy buttons for build templates (#580)

* Extract minimap functionality (#581)

* Improve performance of RowAutoMargin functionality
2024-02-07 15:24:47 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
6ec45396c3 Bump microsoft/setup-msbuild from 1.3.2 to 2.0.0 (#575)
Bumps [microsoft/setup-msbuild](https://github.com/microsoft/setup-msbuild) from 1.3.2 to 2.0.0.
- [Release notes](https://github.com/microsoft/setup-msbuild/releases)
- [Changelog](https://github.com/microsoft/setup-msbuild/blob/main/building-release.md)
- [Commits](https://github.com/microsoft/setup-msbuild/compare/v1.3.2...v2.0.0)

---
updated-dependencies:
- dependency-name: microsoft/setup-msbuild
  dependency-type: direct:production
  update-type: version-update:semver-major
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-02-05 10:23:57 +01:00
amacocianandGitHub b76a9b80fb Release 0.9.9.15 (#574)
* Setup release 0.9.9.15

* Export team build from focus view (#573)

Closes #573

* Fix load skill template
2024-02-04 19:30:22 +01:00
amacocianandGitHub f4bc58bbf7 Release 0.9.9.14 (#570)
* Team build support (#565)

* Setup support for team builds
Fix parser to load team builds
Fix bug in attribute loading
Adjust encoder to order and skip attributes for consistency

* Small buildtemplate binding fixes
Setup TeamBuildTemplateView

* Setup button to create team build

* Fix launch button (#566)

Closes #563

* Team build improvements (#568)

Closes #564

* Closes #562 (#569)

Closes #561

* Increment version
2024-02-04 14:11:01 +00:00
amacocianandGitHub 09fdbb6f95 Release 0.9.9.13 (#560) 2024-02-01 14:52:15 +00:00
amacocianandGitHub 8ee7ed23e4 Release 0.9.9.12 (#558)
* Release 0.9.9.12

* Move injection to injector executable (#554)

* Fix logview missing history (#556)

Closes #526

* Detect unresponsive Guild Wars instance (#557)

Closes #534
Closes #422
2024-01-31 22:02:14 +00:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>amacocian
8d8a78cf88 Bump microsoft/setup-msbuild from 1.3.1 to 1.3.2 (#552)
Bumps [microsoft/setup-msbuild](https://github.com/microsoft/setup-msbuild) from 1.3.1 to 1.3.2.
- [Release notes](https://github.com/microsoft/setup-msbuild/releases)
- [Changelog](https://github.com/microsoft/setup-msbuild/blob/main/building-release.md)
- [Commits](https://github.com/microsoft/setup-msbuild/compare/v1.3.1...v1.3.2)

---
updated-dependencies:
- dependency-name: microsoft/setup-msbuild
  dependency-type: direct:production
  update-type: version-update:semver-patch
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2024-01-29 17:06:36 +01:00
amacocianandGitHub f0652231cb Fix minimap mouse interaction (#553)
* Rework minimap renderer

* Fix dragging

* Fix mouse over detection
2024-01-29 15:50:32 +00:00
amacocianandGitHub d9aa760256 Release 0.9.9.10 (#550)
* Release 0.9.9.10
Option to rotate minimap with the camera
Fix launch button not disabling properly when launching
Remove minimap id overlay
Closes #549
Closes #533
Closes #532

* Increment version

* Increment Daybreak.GWCA version
2024-01-28 14:56:51 +00:00
amacocianandGitHub a65f375255 Release 0.9.9.9 (#548)
Closes #547
Closes #546
Closes #536
2024-01-27 19:34:47 +00:00
amacocianandGitHub 3ee58e2711 Release 0.9.9.8 (#545)
* Synchronize browser history

* Intercept mouse button navigation
Closes #535
Closes #544
2024-01-26 14:24:32 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>amacocian
9b3b083aec Bump MSTest.TestAdapter from 3.1.1 to 3.2.0 (#542)
Bumps [MSTest.TestAdapter](https://github.com/microsoft/testfx) from 3.1.1 to 3.2.0.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v3.1.1...v.3.2.0)

---
updated-dependencies:
- dependency-name: MSTest.TestAdapter
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Macocian Alexandru Victor <amacocian@yahoo.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Macocian Alexandru Victor <amacocian@yahoo.com>
2024-01-26 13:09:38 +01:00
dependabot[bot]GitHubdependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
ecb9b8f172 Bump MSTest.TestFramework from 3.1.1 to 3.2.0 (#543)
Bumps [MSTest.TestFramework](https://github.com/microsoft/testfx) from 3.1.1 to 3.2.0.
- [Release notes](https://github.com/microsoft/testfx/releases)
- [Changelog](https://github.com/microsoft/testfx/blob/main/docs/Changelog.md)
- [Commits](https://github.com/microsoft/testfx/compare/v3.1.1...v.3.2.0)

---
updated-dependencies:
- dependency-name: MSTest.TestFramework
  dependency-type: direct:production
  update-type: version-update:semver-minor
...

Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
2024-01-26 13:08:22 +01:00
amacocianandGitHub 5765c27240 Release 0.9.9.7 (#541)
* Introduce support for browser extensions

* Setup ublock origin

* Release 0.9.9.7 branch

* Automatically backup settings
Closes #539

* Compare settings with remote
2024-01-23 18:25:31 +00:00
amacocianandGitHub c53456112a Update GWCA (#538)
Fix inventory bug
2024-01-20 10:19:46 +00:00
amacocianandGitHub ccea21886e Release 0.9.9.5 (#531)
* Remove character name from credentials
Update vulnerable packages

* Update SharpCompress
2024-01-17 20:00:12 +00:00
amacocianandGitHub 37be8de5ac Update httplib (#529)
Fix missing data when player has no titles
Fix http requests stalling
2024-01-15 19:20:55 +00:00
amacocianandGitHub 107e43a15f Update GWCA (#528) 2024-01-15 13:34:41 +00:00
176 changed files with 13889 additions and 9362 deletions
+4 -2
View File
@@ -46,6 +46,8 @@ jobs:
run: |
$changeLog = git log --no-merges --pretty="%h - %s (%an)<br />" ${{ env.LatestReleaseTag }}..HEAD
echo "::set-env name=Changelog::$changeLog"
mkdir Publish
echo $changeLog > .\Publish\changelog.txt
env:
LatestReleaseTag: ${{steps.getLatestTag.outputs.tag}}
@@ -55,7 +57,7 @@ jobs:
dotnet-version: '8.x'
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1.3.1
uses: microsoft/setup-msbuild@v2.0.0
- name: Fetch submodules
run: |
@@ -110,7 +112,7 @@ jobs:
shell: pwsh
- name: Create release draft
uses: Xotl/cool-github-releases@v1.1.8
uses: Xotl/cool-github-releases@v1.1.10
with:
mode: update
tag_name: v${{ env.Version }}
+2 -2
View File
@@ -41,7 +41,7 @@ jobs:
dotnet-version: '8.x'
- name: Setup MSBuild.exe
uses: microsoft/setup-msbuild@v1.3.1
uses: microsoft/setup-msbuild@v2.0.0
- name: Fetch submodules
run: |
@@ -51,7 +51,7 @@ jobs:
run: cmake -S . -B build -A Win32
- name: Execute Unit Tests
run: dotnet test $env:Test_Project_Path --property:SolutionDir=$env:GITHUB_WORKSPACE
run: dotnet test $env:Test_Project_Path --property:SolutionDir=$env:GITHUB_WORKSPACE -p:ParallelizeTestCollections=false
- name: Restore the Wpf application to populate the obj folder
run: msbuild $env:Solution_Path /t:Restore /p:Configuration=$env:Configuration /p:RuntimeIdentifier=$env:RuntimeIdentifier --property:SolutionDir=$env:GITHUB_WORKSPACE
@@ -10,7 +10,7 @@
</PropertyGroup>
<ItemGroup>
<PackageReference Include="SharpCompress" Version="0.35.0" />
<PackageReference Include="SharpCompress" Version="0.36.0" />
</ItemGroup>
</Project>
+17 -4
View File
@@ -2,17 +2,28 @@
# project specific logic here.
#
project(Daybreak.GWCA)
project(Daybreak.GWCA VERSION 1.0.0)
if(CMAKE_SIZEOF_VOID_P EQUAL 8)
message(FATAL_ERROR "You are configuring a 64bit build, this is not supported. Run cmake with `-A Win32`")
endif()
set(VERSION_MAJOR 0)
set(VERSION_MINOR 9)
set(VERSION_PATCH 9)
set(VERSION_TWEAK 25)
set(VERSION_RC "${CMAKE_CURRENT_BINARY_DIR}/version.rc")
configure_file("${CMAKE_CURRENT_SOURCE_DIR}/version.rc.in" "${VERSION_RC}" @ONLY)
set_property(GLOBAL PROPERTY USE_FOLDERS ON)
set(CMAKE_CXX_STANDARD 17)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY "${PROJECT_SOURCE_DIR}/bin")
set(CMAKE_CXX_STANDARD 23)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(Daybreak.GWCA_FOLDER "${CMAKE_CURRENT_LIST_DIR}/")
set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>")
set(CMAKE_INSTALL_PREFIX ${PROJECT_SOURCE_DIR}/bin/install)
add_compile_options(/MP /permissive-)
list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}/cmake/")
add_library(Daybreak.GWCA SHARED)
@@ -23,11 +34,13 @@ file(GLOB SOURCES
"header/*.h*"
"header/*/*.h")
source_group(TREE "${CMAKE_CURRENT_SOURCE_DIR}" FILES ${SOURCES})
target_sources(Daybreak.GWCA PRIVATE ${SOURCES})
source_group("Generated Files" FILES ${VERSION_RC})
target_sources(Daybreak.GWCA PRIVATE ${SOURCES} ${VERSION_RC})
target_compile_features(Daybreak.GWCA PUBLIC cxx_std_17)
target_compile_definitions(Daybreak.GWCA PRIVATE
$<$<CONFIG:Debug>:BUILD_TYPE_DEBUG>
$<$<CONFIG:Release>:BUILD_TYPE_RELEASE>
NOMINMAX
)
target_precompile_headers(Daybreak.GWCA PRIVATE "header/pch.h")
+6
View File
@@ -0,0 +1,6 @@
#pragma once
#include "httplib.h"
namespace Daybreak::Modules::TitleInfoModule {
void GetTitleInfo(const httplib::Request& req, httplib::Response& res);
}
File diff suppressed because it is too large Load Diff
+14
View File
@@ -0,0 +1,14 @@
#pragma once
#include <cstdint>
#include <json.hpp>
using json = nlohmann::json;
namespace Daybreak {
struct Camera {
float Yaw;
float Pitch;
};
void to_json(json& j, const Camera& p);
}
@@ -2,11 +2,13 @@
#include <cstdint>
#include <json.hpp>
#include <payloads/StatePayload.h>
#include <payloads/Camera.h>
using json = nlohmann::json;
namespace Daybreak {
struct GameStatePayload {
Camera Camera;
std::list<StatePayload> States;
};
@@ -0,0 +1,19 @@
#pragma once
#include <cstdint>
#include <json.hpp>
using json = nlohmann::json;
namespace Daybreak {
struct TitleInfoPayload {
uint32_t TitleId = 0;
uint32_t TitleTierId = 0;
uint32_t CurrentTier = 0;
uint32_t CurrentPoints = 0;
uint32_t PointsNeededNextRank = 0;
bool IsPercentageBased = false;
std::string TitleName = "";
};
void to_json(json& j, const TitleInfoPayload& p);
}
+4 -7
View File
@@ -93,7 +93,6 @@ namespace Daybreak::Modules::EntityNameModule {
}
void GetName(const httplib::Request& req, httplib::Response& res) {
auto callbackEntry = new GW::HookEntry;
uint32_t id = 0;
auto it = req.params.find("id");
if (it == req.params.end()) {
@@ -114,16 +113,14 @@ namespace Daybreak::Modules::EntityNameModule {
id = static_cast<uint32_t>(result);
}
auto response = new std::tuple<uint32_t, std::promise<NamePayload>>();
std::get<0>(*response) = id;
std::promise<NamePayload>& promise = std::get<1>(*response);
auto response = std::tuple<uint32_t, std::promise<NamePayload>>();
std::get<0>(response) = id;
std::promise<NamePayload>& promise = std::get<1>(response);
EnsureInitialized();
PromiseQueue.emplace(response);
PromiseQueue.emplace(&response);
json responsePayload = promise.get_future().get();
delete callbackEntry;
delete response;
res.set_content(responsePayload.dump(), "text/json");
}
}
+8 -12
View File
@@ -287,6 +287,11 @@ namespace Daybreak::Modules::GameModule {
auto name = Daybreak::Utils::WStringToString(nameString);
player.Name = name;
if (titles.empty() ||
titleTiers.empty()) {
return;
}
for (const auto &t : titles) {
if (t.current_title_tier_index == gwPlayer->active_title_tier) {
gwTitle = t;
@@ -360,14 +365,8 @@ namespace Daybreak::Modules::GameModule {
}
auto titles = GetTitles();
if (titles.empty()) {
return gamePayload;
}
auto titleTiers = GetTitleTiers();
if (titleTiers.empty()) {
return gamePayload;
}
auto players = GetPlayers();
if (players.empty()) {
@@ -594,15 +593,12 @@ namespace Daybreak::Modules::GameModule {
}
void GetGameInfo(const httplib::Request&, httplib::Response& res) {
auto callbackEntry = new GW::HookEntry;
auto response = new std::promise<GamePayload>;
auto response = std::promise<GamePayload>();
EnsureInitialized();
PromiseQueue.emplace(response);
json responsePayload = response->get_future().get();
PromiseQueue.emplace(&response);
json responsePayload = response.get_future().get();
delete callbackEntry;
delete response;
res.set_content(responsePayload.dump(), "text/json");
}
}
+11 -6
View File
@@ -2,7 +2,9 @@
#include "GameStateModule.h"
#include <GWCA/Managers/GameThreadMgr.h>
#include <GWCA/Managers/AgentMgr.h>
#include <GWCA/Managers/CameraMgr.h>
#include <GWCA/GameEntities/Agent.h>
#include <GWCA/GameEntities/Camera.h>
#include <GWCA/Managers/MapMgr.h>
#include <future>
#include <payloads/GameStatePayload.h>
@@ -64,6 +66,12 @@ namespace Daybreak::Modules::GameStateModule {
auto states = GetStates(agents);
gamePayload.States = states;
auto camera = GW::CameraMgr::GetCamera();
Daybreak::Camera cameraPayload{};
cameraPayload.Pitch = camera->pitch;
cameraPayload.Yaw = camera->yaw;
gamePayload.Camera = cameraPayload;
return gamePayload;
}
@@ -92,15 +100,12 @@ namespace Daybreak::Modules::GameStateModule {
}
void GetGameStateInfo(const httplib::Request&, httplib::Response& res) {
auto callbackEntry = new GW::HookEntry;
auto response = new std::promise<GameStatePayload>;
auto response = std::promise<GameStatePayload>();
EnsureInitialized();
PromiseQueue.emplace(response);
json responsePayload = response->get_future().get();
PromiseQueue.emplace(&response);
json responsePayload = response.get_future().get();
delete callbackEntry;
delete response;
res.set_content(responsePayload.dump(), "text/json");
}
}
+3 -6
View File
@@ -102,15 +102,12 @@ namespace Daybreak::Modules::InventoryModule {
}
void GetInventoryInfo(const httplib::Request&, httplib::Response& res) {
auto callbackEntry = new GW::HookEntry;
auto response = new std::promise<InventoryPayload>;
auto response = std::promise<InventoryPayload>();
EnsureInitialized();
PromiseQueue.emplace(response);
json responsePayload = response->get_future().get();
PromiseQueue.emplace(&response);
json responsePayload = response.get_future().get();
delete callbackEntry;
delete response;
res.set_content(responsePayload.dump(), "text/json");
}
}
+5 -8
View File
@@ -103,7 +103,6 @@ namespace Daybreak::Modules::ItemNameModule {
}
void GetName(const httplib::Request& req, httplib::Response& res) {
auto callbackEntry = new GW::HookEntry;
uint32_t id = 0;
std::vector<uint32_t> modifiers;
@@ -146,17 +145,15 @@ namespace Daybreak::Modules::ItemNameModule {
}
}
auto response = new std::tuple<uint32_t, std::vector<uint32_t>, std::promise<NamePayload>>();
std::get<0>(*response) = id;
std::promise<NamePayload>& promise = std::get<2>(*response);
std::get<1>(*response) = modifiers;
auto response = std::tuple<uint32_t, std::vector<uint32_t>, std::promise<NamePayload>>();
std::get<0>(response) = id;
std::promise<NamePayload>& promise = std::get<2>(response);
std::get<1>(response) = modifiers;
EnsureInitialized();
PromiseQueue.emplace(response);
PromiseQueue.emplace(&response);
json responsePayload = promise.get_future().get();
delete callbackEntry;
delete response;
res.set_content(responsePayload.dump(), "text/json");
}
}
+3 -6
View File
@@ -51,15 +51,12 @@ namespace Daybreak::Modules::LoginModule {
}
void GetLoginInfo(const httplib::Request&, httplib::Response& res) {
auto callbackEntry = new GW::HookEntry;
auto response = new std::promise<LoginPayload>;
auto response = std::promise<LoginPayload>();
EnsureInitialized();
PromiseQueue.emplace(response);
json responsePayload = response->get_future().get();
PromiseQueue.emplace(&response);
json responsePayload = response.get_future().get();
delete callbackEntry;
delete response;
res.set_content(responsePayload.dump(), "text/json");
}
}
+8 -12
View File
@@ -287,6 +287,11 @@ namespace Daybreak::Modules::MainPlayerModule {
auto name = Daybreak::Utils::WStringToString(gwPlayerName);
player.Name = name;
if (titles.empty() ||
titleTiers.empty()) {
return;
}
for (const auto &t : titles) {
if (t.current_title_tier_index == gwPlayer->active_title_tier) {
gwTitle = t;
@@ -358,14 +363,8 @@ namespace Daybreak::Modules::MainPlayerModule {
}
auto titles = GetTitles();
if (titles.empty()) {
return gamePayload;
}
auto titleTiers = GetTitleTiers();
if (titleTiers.empty()) {
return gamePayload;
}
auto players = GetPlayers();
if (players.empty()) {
@@ -468,15 +467,12 @@ namespace Daybreak::Modules::MainPlayerModule {
}
void GetMainPlayer(const httplib::Request&, httplib::Response& res) {
auto callbackEntry = new GW::HookEntry;
auto response = new std::promise<MainPlayer>;
auto response = std::promise<MainPlayer>();
EnsureInitialized();
PromiseQueue.emplace(response);
json responsePayload = response->get_future().get();
PromiseQueue.emplace(&response);
json responsePayload = response.get_future().get();
delete callbackEntry;
delete response;
res.set_content(responsePayload.dump(), "text/json");
}
}
+3 -6
View File
@@ -58,15 +58,12 @@ namespace Daybreak::Modules::MapModule {
}
void GetMapInfo(const httplib::Request&, httplib::Response& res) {
auto callbackEntry = new GW::HookEntry;
auto response = new std::promise<MapPayload>;
auto response = std::promise<MapPayload>();
EnsureInitialized();
PromiseQueue.emplace(response);
json responsePayload = response->get_future().get();
PromiseQueue.emplace(&response);
json responsePayload = response.get_future().get();
delete callbackEntry;
delete response;
res.set_content(responsePayload.dump(), "text/json");
}
}
@@ -57,15 +57,12 @@ namespace Daybreak::Modules::PathingMetadataModule {
}
void GetPathingMetadata(const httplib::Request&, httplib::Response& res) {
auto callbackEntry = new GW::HookEntry;
auto response = new std::promise<PathingMetadataPayload>;
auto response = std::promise<PathingMetadataPayload>();
EnsureInitialized();
PromiseQueue.emplace(response);
json responsePayload = response->get_future().get();
PromiseQueue.emplace(&response);
json responsePayload = response.get_future().get();
delete callbackEntry;
delete response;
res.set_content(responsePayload.dump(), "text/json");
}
}
+3 -6
View File
@@ -84,15 +84,12 @@ namespace Daybreak::Modules::PathingModule {
}
void GetPathingData(const httplib::Request&, httplib::Response& res) {
auto callbackEntry = new GW::HookEntry;
auto response = new std::promise<PathingPayload>;
auto response = std::promise<PathingPayload>();
EnsureInitialized();
PromiseQueue.emplace(response);
json responsePayload = response->get_future().get();
PromiseQueue.emplace(&response);
json responsePayload = response.get_future().get();
delete callbackEntry;
delete response;
res.set_content(responsePayload.dump(), "text/json");
}
}
+3 -6
View File
@@ -60,15 +60,12 @@ namespace Daybreak::Modules::PreGameModule {
}
void GetPreGameInfo(const httplib::Request&, httplib::Response& res) {
auto callbackEntry = new GW::HookEntry;
auto response = new std::promise<PreGamePayload>;
auto response = std::promise<PreGamePayload>();
EnsureInitialized();
PromiseQueue.emplace(response);
json responsePayload = response->get_future().get();
PromiseQueue.emplace(&response);
json responsePayload = response.get_future().get();
delete callbackEntry;
delete response;
res.set_content(responsePayload.dump(), "text/json");
}
}
+3
View File
@@ -62,6 +62,9 @@ namespace http {
int port = FindPort();
if (port > 0) {
std::cout << "Initializing server on port " << port << std::endl;
server.set_read_timeout(1, 0);
server.set_write_timeout(1, 0);
server.set_idle_interval(0, 100000);
server.listen("0.0.0.0", port);
return true;
}
+3 -6
View File
@@ -62,15 +62,12 @@ namespace Daybreak::Modules::SessionModule {
}
void GetSessionInfo(const httplib::Request&, httplib::Response& res) {
auto callbackEntry = new GW::HookEntry;
auto response = new std::promise<SessionPayload>;
auto response = std::promise<SessionPayload>();
EnsureInitialized();
PromiseQueue.emplace(response);
json responsePayload = response->get_future().get();
PromiseQueue.emplace(&response);
json responsePayload = response.get_future().get();
delete callbackEntry;
delete response;
res.set_content(responsePayload.dump(), "text/json");
}
}
+161
View File
@@ -0,0 +1,161 @@
#include "pch.h"
#include "TitleInfoModule.h"
#include "payloads/TitleInfoPayload.h"
#include <GWCA/GameContainers/Array.h>
#include <GWCA/Managers/GameThreadMgr.h>
#include <GWCA/Managers/MapMgr.h>
#include <GWCA/Managers/PlayerMgr.h>
#include <GWCA/Constants/AgentIDs.h>
#include <GWCA/GameEntities/Title.h>
#include <GWCA/Context/WorldContext.h>
#include <GWCA/Constants/Constants.h>
#include <GWCA/Managers/UIMgr.h>
#include <future>
#include <json.hpp>
#include <tuple>
#include <queue>
#include <Windows.h>
#include <cstdint>
#include <limits>
#include "Utils.h"
namespace Daybreak::Modules::TitleInfoModule {
const int MaxTries = 20;
std::vector<std::tuple<TitleInfoPayload, std::promise<TitleInfoPayload>*, std::wstring*, int>> WaitingList;
std::queue<std::tuple<uint32_t, std::promise<TitleInfoPayload>>*> PromiseQueue;
std::mutex GameThreadMutex;
GW::HookEntry GameThreadHook;
volatile bool initialized = false;
std::wstring* GetAsyncName(uint32_t titleTierIndex) {
const auto worldContext = GW::GetWorldContext();
const auto tiers = &worldContext->title_tiers;
const auto tier = &tiers->at(titleTierIndex);
auto description = new std::wstring();
GW::UI::AsyncDecodeStr(tier->tier_name_enc, description);
return description;
}
TitleInfoPayload GetPayload(uint32_t id) {
TitleInfoPayload payload;
if (!GW::Map::GetIsMapLoaded()) {
return payload;
}
const auto title = GW::PlayerMgr::GetTitleTrack((GW::Constants::TitleID)id);
if (!title) {
return payload;
}
if (title->current_points == 0 &&
title->current_title_tier_index == 0 &&
title->next_title_tier_index == 0 &&
title->max_title_rank == 0) {
return payload;
}
const auto tierIndex = title->current_title_tier_index;
const auto worldContext = GW::GetWorldContext();
const auto tiers = &worldContext->title_tiers;
const auto tier = &tiers->at(tierIndex);
payload.TitleId = id;
payload.TitleTierId = tierIndex;
payload.CurrentPoints = title->current_points;
payload.PointsNeededNextRank = title->points_needed_next_rank;
payload.IsPercentageBased = title->is_percentage_based();
payload.CurrentTier = tier->tier_number;
return payload;
}
void EnsureInitialized() {
GameThreadMutex.lock();
if (!initialized) {
GW::GameThread::RegisterGameThreadCallback(&GameThreadHook, [&](GW::HookStatus*) {
while (!PromiseQueue.empty()) {
auto promiseRequest = PromiseQueue.front();
std::promise<TitleInfoPayload> &promise = std::get<1>(*promiseRequest);
uint32_t id = std::get<0>(*promiseRequest);
PromiseQueue.pop();
try {
const auto payload = GetPayload(id);
if (payload.TitleId == 0 &&
payload.CurrentPoints == 0 &&
payload.PointsNeededNextRank == 0 &&
payload.TitleTierId == 0) {
TitleInfoPayload payload;
promise.set_value(payload);
continue;
}
auto name = GetAsyncName(payload.TitleTierId);
if (!name) {
continue;
}
WaitingList.emplace_back(payload, &promise, name, 0);
}
catch (...) {
TitleInfoPayload payload;
promise.set_value(payload);
}
}
for (auto i = 0; i < WaitingList.size(); ) {
auto item = &WaitingList[i];
auto name = std::get<2>(*item);
auto& tries = std::get<3>(*item);
if (name->empty() &&
tries < MaxTries) {
tries += 1;
i++;
continue;
}
auto promise = std::get<1>(*item);
auto payload = std::get<0>(*item);
payload.TitleName = Utils::WStringToString(*name);
WaitingList.erase(WaitingList.begin() + i);
delete(name);
promise->set_value(payload);
}
});
initialized = true;
}
GameThreadMutex.unlock();
}
void GetTitleInfo(const httplib::Request& req, httplib::Response& res) {
uint32_t id = 0;
auto it = req.params.find("id");
if (it == req.params.end()) {
res.status = 400;
res.set_content("Missing id parameter", "text/plain");
return;
}
else {
auto idStr = it->second;
size_t pos = 0;
auto result = std::stoul(idStr, &pos);
if (pos != idStr.size()) {
res.status = 400;
res.set_content("Invalid id parameter", "text/plain");
return;
}
id = static_cast<uint32_t>(result);
}
auto response = std::tuple<uint32_t, std::promise<TitleInfoPayload>>();
std::get<0>(response) = id;
std::promise<TitleInfoPayload>& promise = std::get<1>(response);
EnsureInitialized();
PromiseQueue.emplace(&response);
json responsePayload = promise.get_future().get();
res.set_content(responsePayload.dump(), "text/json");
}
}
+3 -6
View File
@@ -66,15 +66,12 @@ namespace Daybreak::Modules::UserModule {
}
void GetUserInfo(const httplib::Request&, httplib::Response& res) {
auto callbackEntry = new GW::HookEntry;
auto response = new std::promise<UserPayload>;
auto response = std::promise<UserPayload>();
EnsureInitialized();
PromiseQueue.emplace(response);
json responsePayload = response->get_future().get();
PromiseQueue.emplace(&response);
json responsePayload = response.get_future().get();
delete callbackEntry;
delete response;
res.set_content(responsePayload.dump(), "text/json");
}
}
+2
View File
@@ -24,6 +24,7 @@
#include "EntityNameModule.h"
#include "GameStateModule.h"
#include "ItemNameModule.h"
#include "TitleInfoModule.h"
#include <mutex>
volatile bool initialized;
@@ -72,6 +73,7 @@ static DWORD WINAPI StartHttpServer(LPVOID)
http::server::Get("/session", Daybreak::Modules::SessionModule::GetSessionInfo);
http::server::Get("/entities/name", Daybreak::Modules::EntityNameModule::GetName);
http::server::Get("/items/name", Daybreak::Modules::ItemNameModule::GetName);
http::server::Get("/titles/info", Daybreak::Modules::TitleInfoModule::GetTitleInfo);
http::server::StartServer();
return 0;
}
+16
View File
@@ -0,0 +1,16 @@
#pragma once
#include <payloads/Camera.h>
#include <cstdint>
#include <json.hpp>
using json = nlohmann::json;
namespace Daybreak {
void to_json(json& j, const Camera& p) {
j = json
{
{"Yaw", p.Yaw},
{"Pitch", p.Pitch}
};
}
}
@@ -8,6 +8,7 @@ namespace Daybreak {
void to_json(json& j, const GameStatePayload& p) {
j = json
{
{"Camera", p.Camera},
{"States", p.States},
};
}
@@ -0,0 +1,20 @@
#include <cstdint>
#include <json.hpp>
#include <payloads/TitleInfoPayload.h>
using json = nlohmann::json;
namespace Daybreak {
void to_json(json& j, const TitleInfoPayload& p) {
j = json
{
{"TitleId", p.TitleId},
{"TitleTierId", p.TitleTierId},
{"TitleName", p.TitleName},
{"CurrentPoints", p.CurrentPoints},
{"PointsNeededNextRank", p.PointsNeededNextRank},
{"IsPercentageBased", p.IsPercentageBased},
{"CurrentTier", p.CurrentTier}
};
}
}
-106
View File
@@ -1,106 +0,0 @@
#include "pch.h"
#include <iostream>
#include <windows.h>
#include <vector>
#include <thread>
namespace PipeServer {
// Function to handle a single client connection
void HandleClient(HANDLE hPipe)
{
while (true)
{
char buffer[1024];
DWORD bytesRead;
if (!ReadFile(hPipe, buffer, sizeof(buffer), &bytesRead, NULL) || bytesRead == 0)
{
break; // Error or client closed the connection
}
// Process data received from the client (e.g., display or manipulate it)
std::cout << "Received from client: " << buffer << std::endl;
// Send a response back to the client (optional)
const char* response = "Hello from server!";
DWORD bytesWritten;
WriteFile(hPipe, response, static_cast<DWORD>(strlen(response) + 1), &bytesWritten, NULL);
}
// Close the pipe handle when the client disconnects
CloseHandle(hPipe);
}
void StartServer()
{
DWORD processId = GetCurrentProcessId();
// Create a buffer to store the formatted process ID as a wide string
wchar_t processIdStr[12]; // Assuming DWORD can be represented in 10 characters
// Format the process ID into the buffer as a wide string
swprintf_s(processIdStr, L"%u", processId);
std::wstring pipeName = L"\\\\.\\pipe\\Daybreak";
pipeName += processIdStr;
HANDLE hPipe = CreateNamedPipeW(
pipeName.c_str(), // Use the generated pipe name
PIPE_ACCESS_DUPLEX,
PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE | PIPE_WAIT,
1,
1024, 1024,
1000,
NULL
);
if (hPipe == INVALID_HANDLE_VALUE)
{
std::cerr << "Error: CreateNamedPipe failed with error code " << GetLastError() << std::endl;
return;
}
std::cout << "Created named pipe ";
wprintf(L"%s\n", pipeName.c_str());
std::cout << "Waiting for clients to connect..." << std::endl;
// Wait for client connections and handle them in separate threads
std::vector<std::thread> clientThreads;
HANDLE io_event = CreateEvent(NULL, FALSE, FALSE, NULL);
OVERLAPPED overlapped;
if (io_event == NULL) {
std::cout << "Failed to create event" << std::endl;
return;
}
while (true)
{
overlapped.hEvent = io_event;
if (ConnectNamedPipe(hPipe, &overlapped))
{
// A client has connected, create a thread to handle it
std::cout << "Client connected" << std::endl;
clientThreads.emplace_back(HandleClient, hPipe);
clientThreads.back().detach();
}
else {
DWORD err = GetLastError();
std::cout << "Failed to connect named pipe " << err << std::endl;
if (err == ERROR_PIPE_CONNECTED) {
std::cout << "Pipe connection was successful. Client connected" << std::endl;
clientThreads.emplace_back(HandleClient, hPipe);
clientThreads.back().detach();
}
if (err == ERROR_IO_PENDING)
{
std::cout << "Error IO Pending" << std::endl;
}
if (err == ERROR_PIPE_LISTENING)
{
std::cout << "Error Pipe Listening" << std::endl;
}
}
}
CloseHandle(hPipe); // Close the server pipe when done
return;
}
}
+34
View File
@@ -0,0 +1,34 @@
#include <winver.h>
VS_VERSION_INFO VERSIONINFO
FILEVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@,@VERSION_TWEAK@
PRODUCTVERSION @VERSION_MAJOR@,@VERSION_MINOR@,@VERSION_PATCH@,@VERSION_TWEAK@
FILEFLAGSMASK VS_FFI_FILEFLAGSMASK
#ifdef _DEBUG
FILEFLAGS VS_FF_DEBUG
#else
FILEFLAGS 0x0L
#endif
FILEOS VOS_NT_WINDOWS32
FILETYPE VFT_DLL
FILESUBTYPE VFT2_UNKNOWN
BEGIN
BLOCK "StringFileInfo"
BEGIN
BLOCK "040904b0"
BEGIN
VALUE "CompanyName", "gwdevhub"
VALUE "FileDescription", "Daybreak.GWCA"
VALUE "FileVersion", "@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@.@VERSION_TWEAK@"
VALUE "InternalName", "Daybreak.GWCA"
VALUE "LegalCopyright", ""
VALUE "OriginalFilename", "Daybreak.GWCA.dll"
VALUE "ProductName", "Daybreak.GWCA"
VALUE "ProductVersion", "@VERSION_MAJOR@.@VERSION_MINOR@.@VERSION_PATCH@.@VERSION_TWEAK@"
END
END
BLOCK "VarFileInfo"
BEGIN
VALUE "Translation", 0x409, 0x4B0
END
END
-16
View File
@@ -113,22 +113,6 @@ else
return;
}
Console.WriteLine("Deleting browser caches");
try
{
Directory.Delete("BrowserData", true);
}
catch(Exception)
{
}
try
{
Directory.Delete("Daybreak.exe.WebView2", true);
}
catch(Exception)
{
}
Console.WriteLine("Launching application");
var process = new Process
{
+5 -5
View File
@@ -9,15 +9,15 @@
<ItemGroup>
<PackageReference Include="FluentAssertions" Version="6.12.0" />
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.8.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.1.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.1.1" />
<PackageReference Include="coverlet.collector" Version="6.0.0">
<PackageReference Include="Microsoft.NET.Test.Sdk" Version="17.9.0" />
<PackageReference Include="MSTest.TestAdapter" Version="3.3.1" />
<PackageReference Include="MSTest.TestFramework" Version="3.3.1" />
<PackageReference Include="coverlet.collector" Version="6.0.2">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
<PackageReference Include="NSubstitute" Version="5.1.0" />
<PackageReference Include="NSubstitute.Analyzers.CSharp" Version="1.0.16">
<PackageReference Include="NSubstitute.Analyzers.CSharp" Version="1.0.17">
<PrivateAssets>all</PrivateAssets>
<IncludeAssets>runtime; build; native; contentfiles; analyzers; buildtransitive</IncludeAssets>
</PackageReference>
+2 -2
View File
@@ -14,7 +14,7 @@ public abstract class ItemTestsBase<T>
protected abstract IEnumerable<T> AllItems { get; }
[TestMethod]
public void InscriptionList_Should_ContainOnlyUniqueItems()
public void List_Should_ContainOnlyUniqueItems()
{
var itemMap = new Dictionary<T, int>();
@@ -45,7 +45,7 @@ public abstract class ItemTestsBase<T>
}
[TestMethod]
public void InscriptionList_Should_ContainAllNpcs()
public void List_Should_ContainAllItems()
{
var itemMap = new Dictionary<T, bool>();
var definedItemFields = typeof(T).GetFields(BindingFlags.Static | BindingFlags.Public).Where(f => f.FieldType == typeof(T));
@@ -11,7 +11,8 @@ namespace Daybreak.Tests.Services;
[TestClass]
public class BuildTemplateManagerTests
{
private const string EncodedTemplate = "OwBk0texXNu0Dj/z+TDzBj+TN4AE";
private const string EncodedSingleTemplate = "OwBk0texXNu0Dj/z+TDzBj+TN4AE";
private const string EncodedTeamTemplate = "OQJUAMxOEMLnw0nDXQGY6aw2B OQNCA8wDP9B8DyzmOUi1veC OggjYxXTIPWbp5krZfxEAAAoD OwUTM4HDn5gc9TJSh6xddmETA";
private IBuildTemplateManager buildTemplateManager;
[TestInitialize]
@@ -21,35 +22,258 @@ public class BuildTemplateManagerTests
}
[TestMethod]
public void TestDecode()
public void TestSingleDecode()
{
var build = this.buildTemplateManager.DecodeTemplate(EncodedTemplate);
build.Primary.Should().Be(Profession.Assassin);
build.Secondary.Should().Be(Profession.None);
build.Attributes.Count.Should().Be(4);
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.Skills.Count.Should().Be(8);
build.Skills[0].Should().Be(Skill.UnsuspectingStrike);
build.Skills[1].Should().Be(Skill.WildStrike);
build.Skills[2].Should().Be(Skill.CriticalStrike);
build.Skills[3].Should().Be(Skill.MoebiusStrike);
build.Skills[4].Should().Be(Skill.DeathBlossom);
build.Skills[5].Should().Be(Skill.CriticalEye);
build.Skills[6].Should().Be(Skill.CriticalAgility);
build.Skills[7].Should().Be(Skill.CriticalDefenses);
var build = this.buildTemplateManager.DecodeTemplate(EncodedSingleTemplate);
var singleBuildEntry = build.As<SingleBuildEntry>();
singleBuildEntry.Should().NotBeNull();
singleBuildEntry.Primary.Should().Be(Profession.Assassin);
singleBuildEntry.Secondary.Should().Be(Profession.None);
singleBuildEntry.Attributes.Count.Should().Be(4);
singleBuildEntry.Attributes[1].Attribute.Should().Be(Attribute.DaggerMastery);
singleBuildEntry.Attributes[1].Points.Should().Be(11);
singleBuildEntry.Attributes[2].Attribute.Should().Be(Attribute.DeadlyArts);
singleBuildEntry.Attributes[2].Points.Should().Be(1);
singleBuildEntry.Attributes[3].Attribute.Should().Be(Attribute.ShadowArts);
singleBuildEntry.Attributes[3].Points.Should().Be(5);
singleBuildEntry.Attributes[0].Attribute.Should().Be(Attribute.CriticalStrikes);
singleBuildEntry.Attributes[0].Points.Should().Be(11);
singleBuildEntry.Skills.Count.Should().Be(8);
singleBuildEntry.Skills[0].Should().Be(Skill.UnsuspectingStrike);
singleBuildEntry.Skills[1].Should().Be(Skill.WildStrike);
singleBuildEntry.Skills[2].Should().Be(Skill.CriticalStrike);
singleBuildEntry.Skills[3].Should().Be(Skill.MoebiusStrike);
singleBuildEntry.Skills[4].Should().Be(Skill.DeathBlossom);
singleBuildEntry.Skills[5].Should().Be(Skill.CriticalEye);
singleBuildEntry.Skills[6].Should().Be(Skill.CriticalAgility);
singleBuildEntry.Skills[7].Should().Be(Skill.CriticalDefenses);
}
[TestMethod]
public void TestEncode()
public void TestTeamDecode()
{
var build = new Build()
var build = this.buildTemplateManager.DecodeTemplate(EncodedTeamTemplate);
var teamBuildEntry = build.As<TeamBuildEntry>();
teamBuildEntry.Should().NotBeNull();
teamBuildEntry.Builds.Should().HaveCount(4);
var firstBuild = teamBuildEntry.Builds[0];
firstBuild.Primary.Should().Be(Profession.Mesmer);
firstBuild.Secondary.Should().Be(Profession.Ranger);
firstBuild.Attributes.Should().BeEquivalentTo(
[
new AttributeEntry
{
Attribute = Attribute.FastCasting,
Points = 6
},
new AttributeEntry
{
Attribute = Attribute.DominationMagic,
Points = 11
},
new AttributeEntry
{
Attribute = Attribute.IllusionMagic,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.InspirationMagic,
Points = 2
},
new AttributeEntry
{
Attribute = Attribute.BeastMastery,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.Marksmanship,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.WildernessSurvival,
Points = 12
},
]);
firstBuild.Skills.Should().BeEquivalentTo(
[
Skill.EnergySurge,
Skill.Mistrust,
Skill.CryofFrustration,
Skill.PowerSpike,
Skill.PowerDrain,
Skill.UnnaturalSignet,
Skill.Empathy,
Skill.QuickeningZephyr
]);
var secondBuild = teamBuildEntry.Builds[1];
secondBuild.Primary.Should().Be(Profession.Mesmer);
secondBuild.Secondary.Should().Be(Profession.Monk);
secondBuild.Attributes.Should().BeEquivalentTo(
[
new AttributeEntry
{
Attribute = Attribute.FastCasting,
Points = 12
},
new AttributeEntry
{
Attribute = Attribute.DominationMagic,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.IllusionMagic,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.InspirationMagic,
Points = 12
},
new AttributeEntry
{
Attribute = Attribute.HealingPrayers,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.SmitingPrayers,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.ProtectionPrayers,
Points = 0
},
]);
secondBuild.Skills.Should().BeEquivalentTo(
[
Skill.SymbolicCelerity,
Skill.MantraofInscriptions,
Skill.KeystoneSignet,
Skill.SignetofClumsiness,
Skill.UnnaturalSignet,
Skill.BaneSignet,
Skill.CastigationSignet,
Skill.SignetofRage
]);
var thirdBuild = teamBuildEntry.Builds[2];
thirdBuild.Primary.Should().Be(Profession.Ranger);
thirdBuild.Secondary.Should().Be(Profession.Ritualist);
thirdBuild.Attributes.Should().BeEquivalentTo(
[
new AttributeEntry
{
Attribute = Attribute.Expertise,
Points = 3
},
new AttributeEntry
{
Attribute = Attribute.BeastMastery,
Points = 12
},
new AttributeEntry
{
Attribute = Attribute.Marksmanship,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.WildernessSurvival,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.ChannelingMagic,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.Communing,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.RestorationMagic,
Points = 12
},
]);
thirdBuild.Skills.Should().BeEquivalentTo(
[
Skill.XinraesWeapon,
Skill.MendBodyandSoul,
Skill.SpiritLight,
Skill.GhostmirrorLight,
Skill.FleshofMyFlesh,
Skill.ResurrectionSignet,
Skill.NoSkill,
Skill.EdgeofExtinction
]);
var fourthBuild = teamBuildEntry.Builds[3];
fourthBuild.Primary.Should().Be(Profession.Monk);
fourthBuild.Secondary.Should().Be(Profession.Mesmer);
fourthBuild.Attributes.Should().BeEquivalentTo(
[
new AttributeEntry
{
Attribute = Attribute.DivineFavor,
Points = 3
},
new AttributeEntry
{
Attribute = Attribute.HealingPrayers,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.SmitingPrayers,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.ProtectionPrayers,
Points = 12
},
new AttributeEntry
{
Attribute = Attribute.DominationMagic,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.IllusionMagic,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.InspirationMagic,
Points = 12
},
]);
fourthBuild.Skills.Should().BeEquivalentTo(
[
Skill.ProtectiveBond,
Skill.PurifyingVeil,
Skill.BlessedSignet,
Skill.MantraofRecall,
Skill.ProtectiveSpirit,
Skill.ShieldofAbsorption,
Skill.ReversalofFortune,
Skill.Resurrect
]);
}
[TestMethod]
public void TestSingleEncode()
{
var build = new SingleBuildEntry()
{
Primary = Profession.Assassin,
Secondary = Profession.None,
@@ -90,6 +314,227 @@ public class BuildTemplateManagerTests
};
var encoded = this.buildTemplateManager.EncodeTemplate(build);
encoded.Should().Be(EncodedTemplate);
encoded.Should().Be(EncodedSingleTemplate);
}
[TestMethod]
public void TestTeamEncode()
{
var build = new TeamBuildEntry
{
Builds = [
new SingleBuildEntry
{
Primary = Profession.Mesmer,
Secondary = Profession.Ranger,
Attributes = [
new AttributeEntry
{
Attribute = Attribute.FastCasting,
Points = 6
},
new AttributeEntry
{
Attribute = Attribute.DominationMagic,
Points = 11
},
new AttributeEntry
{
Attribute = Attribute.IllusionMagic,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.InspirationMagic,
Points = 2
},
new AttributeEntry
{
Attribute = Attribute.BeastMastery,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.Marksmanship,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.WildernessSurvival,
Points = 12
},
],
Skills = [
Skill.EnergySurge,
Skill.Mistrust,
Skill.CryofFrustration,
Skill.PowerSpike,
Skill.PowerDrain,
Skill.UnnaturalSignet,
Skill.Empathy,
Skill.QuickeningZephyr
]
},
new SingleBuildEntry
{
Primary = Profession.Mesmer,
Secondary = Profession.Monk,
Attributes = [
new AttributeEntry
{
Attribute = Attribute.FastCasting,
Points = 12
},
new AttributeEntry
{
Attribute = Attribute.DominationMagic,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.IllusionMagic,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.InspirationMagic,
Points = 12
},
new AttributeEntry
{
Attribute = Attribute.HealingPrayers,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.SmitingPrayers,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.ProtectionPrayers,
Points = 0
},
],
Skills = [
Skill.SymbolicCelerity,
Skill.MantraofInscriptions,
Skill.KeystoneSignet,
Skill.SignetofClumsiness,
Skill.UnnaturalSignet,
Skill.BaneSignet,
Skill.CastigationSignet,
Skill.SignetofRage
]
},
new SingleBuildEntry
{
Primary = Profession.Ranger,
Secondary = Profession.Ritualist,
Attributes = [
new AttributeEntry
{
Attribute = Attribute.Expertise,
Points = 3
},
new AttributeEntry
{
Attribute = Attribute.BeastMastery,
Points = 12
},
new AttributeEntry
{
Attribute = Attribute.Marksmanship,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.WildernessSurvival,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.ChannelingMagic,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.Communing,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.RestorationMagic,
Points = 12
},
],
Skills = [
Skill.XinraesWeapon,
Skill.MendBodyandSoul,
Skill.SpiritLight,
Skill.GhostmirrorLight,
Skill.FleshofMyFlesh,
Skill.ResurrectionSignet,
Skill.NoSkill,
Skill.EdgeofExtinction
]
},
new SingleBuildEntry
{
Primary = Profession.Monk,
Secondary = Profession.Mesmer,
Attributes = [
new AttributeEntry
{
Attribute = Attribute.DivineFavor,
Points = 3
},
new AttributeEntry
{
Attribute = Attribute.HealingPrayers,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.SmitingPrayers,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.ProtectionPrayers,
Points = 12
},
new AttributeEntry
{
Attribute = Attribute.DominationMagic,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.IllusionMagic,
Points = 0
},
new AttributeEntry
{
Attribute = Attribute.InspirationMagic,
Points = 12
},
],
Skills = [
Skill.ProtectiveBond,
Skill.PurifyingVeil,
Skill.BlessedSignet,
Skill.MantraofRecall,
Skill.ProtectiveSpirit,
Skill.ShieldofAbsorption,
Skill.ReversalofFortune,
Skill.Resurrect
]
}
]
};
var encoded = this.buildTemplateManager.EncodeTemplate(build);
encoded.Should().Be(EncodedTeamTemplate);
}
}
@@ -57,6 +57,7 @@ public class JsonLoggerProviderTests
[TestMethod]
public void LoggerLogsAndReaderReads()
{
this.logsManager.DeleteLogs();
var logger = this.loggerProvider.CreateLogger("SomeCategory");
logger.LogInformation("Logging some stuff");
logger.LogError("Logging some error");
+4 -4
View File
@@ -70,8 +70,8 @@ Global
{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|x64.ActiveCfg = Debug|x86
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Debug|x64.Build.0 = Debug|x86
{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
@@ -82,8 +82,8 @@ Global
{4E2BB805-135D-4F02-8C53-3D8B6876D323}.Release|x86.Build.0 = Release|x86
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|Any CPU.Build.0 = Debug|Any CPU
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|x64.ActiveCfg = Debug|Any CPU
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|x64.Build.0 = Debug|Any CPU
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|x64.ActiveCfg = Debug|x64
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|x64.Build.0 = Debug|x64
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|x86.ActiveCfg = Debug|x64
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Debug|x86.Build.0 = Debug|x64
{CA60F9AC-A496-4DB6-970E-ECE73B051C05}.Release|Any CPU.ActiveCfg = Release|Any CPU
+12 -5
View File
@@ -20,12 +20,12 @@ public sealed class RowAutoMargin : Behavior<Grid>
}
// TODO #367: Super hacky code below. To be reworked
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
public void RecalculateRows()
{
/*
* Tag each child with the desired row. Skip children already tagged
*/
foreach(var child in this.AssociatedObject.Children.OfType<FrameworkElement>())
foreach (var child in this.AssociatedObject.Children.OfType<FrameworkElement>())
{
if (child.Tag is int desiredRow)
{
@@ -72,9 +72,12 @@ public sealed class RowAutoMargin : Behavior<Grid>
var addedRows = 0;
for (var i = 0; i < this.AssociatedObject.RowDefinitions.Count - 1; i++)
{
if (this.AssociatedObject.RowDefinitions[i + addedRows].Height.Value > 0)
var rowDefinition = this.AssociatedObject.RowDefinitions[i];
if (rowDefinition.Height.Value > 0 &&
(rowDefinition.Tag is not string tagString ||
tagString is not GeneratedRowsTag))
{
this.AssociatedObject.RowDefinitions.Insert(i + 1,
this.AssociatedObject.RowDefinitions.Insert(i + 1,
new RowDefinition
{
Height = new GridLength(this.Margin, GridUnitType.Pixel),
@@ -89,9 +92,13 @@ public sealed class RowAutoMargin : Behavior<Grid>
}
}
i++;
addedRows++;
}
}
}
private void AssociatedObject_Loaded(object sender, RoutedEventArgs e)
{
this.RecalculateRows();
}
}
@@ -1,5 +1,6 @@
using Daybreak.Attributes;
using Daybreak.Configuration.FocusView;
using Daybreak.Models.Browser;
using Newtonsoft.Json;
namespace Daybreak.Configuration.Options;
@@ -49,7 +50,19 @@ public sealed class FocusViewOptions
[OptionName(Name = "Energy Display Mode", Description = "Sets how should the energy display show the information")]
public PointsDisplay EnergyDisplay { get; set; }
[JsonProperty(nameof(BrowserUrl))]
[JsonProperty(nameof(MinimapRotationEnabled))]
[OptionName(Name = "Minimap Rotation", Description = "When enabled, the minimap will rotate according to the player camera")]
public bool MinimapRotationEnabled { get; set; } = true;
[JsonProperty(nameof(MinimapTransparency))]
[OptionName(Name = "Minimap Transparency", Description = "When enabled, the minimap window will be transparent")]
public bool MinimapTransparency { get; set; } = true;
[JsonProperty(nameof(ShowCartoProgress))]
[OptionName(Name = "Show Cartographer Progress", Description = "When enabled, the minimap will show live cartographer progress")]
public bool ShowCartoProgress { get; set; } = false;
[JsonProperty(nameof(BrowserHistory))]
[OptionIgnore]
public string? BrowserUrl { get; set; } = "https://wiki.guildwars.com/wiki/Main_Page";
public BrowserHistory BrowserHistory { get; set; } = new();
}
@@ -8,6 +8,11 @@ namespace Daybreak.Configuration.Options;
[OptionsName(Name = "Launcher")]
public sealed class LauncherOptions
{
[JsonProperty(nameof(DynamicBackgrounds))]
[OptionName(Name = "Dyamic Backgrounds", Description = "If enabled, Daybreak will periodically change the background image. Otherwise, Daybreak will show a plain background")]
[OptionSynchronizationIgnore]
public bool DynamicBackgrounds { get; set; } = true;
[JsonProperty(nameof(SetGuildwarsWindowSizeOnLaunch))]
[OptionName(Name = "Set GuildWars Window Size On Launch", Description = "Sets the GuildWars window size and position on launch")]
[OptionSynchronizationIgnore]
@@ -56,4 +61,8 @@ public sealed class LauncherOptions
[JsonProperty(nameof(BetaUpdate))]
[OptionName(Name = "Beta Update", Description = "If true, the launcher will use the new update procedure")]
public bool BetaUpdate { get; set; } = true;
[JsonProperty(nameof(AutoBackupSettings))]
[OptionName(Name = "Auto Backup Settings", Description = "If true, the launcher will attempt to backup settings periodically")]
public bool AutoBackupSettings { get; set; } = false;
}
@@ -0,0 +1,16 @@
using Daybreak.Attributes;
namespace Daybreak.Configuration.Options;
[OptionsIgnore]
[OptionsName(Name = "Minimap Window")]
[OptionsSynchronizationIgnore]
public sealed class MinimapWindowOptions
{
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; }
}
@@ -16,5 +16,5 @@ internal sealed class PriceHistoryOptions : ILiteCollectionOptions<TraderQuoteDT
public TimeSpan UpdateInterval { get; set; } = TimeSpan.FromHours(1);
public Dictionary<string, DateTime> ItemHistoryMetadata { get; set; } = [];
public Dictionary<string, DateTime> PriceHistoryMetadata { get; set; } = [];
}
+28 -7
View File
@@ -82,6 +82,8 @@ using Daybreak.Services.DirectSong;
using Daybreak.Views.Onboarding.DirectSong;
using Daybreak.Services.SevenZip;
using Daybreak.Services.ReShade.Notifications;
using Daybreak.Services.UBlockOrigin;
using Daybreak.Services.Browser;
namespace Daybreak.Configuration;
@@ -155,6 +157,10 @@ public class ProjectConfiguration : PluginConfigurationBase
.RegisterHttpClient<UModService>()
.WithMessageHandler(this.SetupLoggingAndMetrics<UModService>)
.WithDefaultRequestHeadersSetup(this.SetupDaybreakUserAgent)
.Build()
.RegisterHttpClient<UBlockOriginService>()
.WithMessageHandler(this.SetupLoggingAndMetrics<UBlockOriginService>)
.WithDefaultRequestHeadersSetup(this.SetupDaybreakUserAgent)
.Build();
}
@@ -190,19 +196,19 @@ public class ProjectConfiguration : PluginConfigurationBase
services.AddSingleton<IPostUpdateActionProducer>(sp => sp.GetRequiredService<PostUpdateActionManager>());
services.AddSingleton<IPostUpdateActionProvider>(sp => sp.GetRequiredService<PostUpdateActionManager>());
services.AddSingleton<IMenuService, MenuService>();
services.AddSingleton<IMenuServiceInitializer, MenuService>(sp => sp.GetRequiredService<IMenuService>().As<MenuService>()!);
services.AddSingleton<IMenuServiceInitializer, MenuService>(sp => sp.GetRequiredService<IMenuService>().Cast<MenuService>());
services.AddSingleton<ILiteDatabase, LiteDatabase>(sp => new LiteDatabase("Daybreak.db"));
services.AddSingleton<IMutexHandler, MutexHandler>();
services.AddSingleton<IShortcutManager, ShortcutManager>();
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.AddSingleton<IOptionsProvider, OptionsManager>(sp => sp.GetRequiredService<IOptionsManager>().As<OptionsManager>()!);
services.AddSingleton<IOptionsProducer, OptionsManager>(sp => sp.GetRequiredService<IOptionsManager>().Cast<OptionsManager>());
services.AddSingleton<IOptionsUpdateHook, OptionsManager>(sp => sp.GetRequiredService<IOptionsManager>().Cast<OptionsManager>());
services.AddSingleton<IOptionsProvider, OptionsManager>(sp => sp.GetRequiredService<IOptionsManager>().Cast<OptionsManager>());
services.AddSingleton<IThemeManager, ThemeManager>();
services.AddSingleton<INotificationService, NotificationService>();
services.AddSingleton<INotificationProducer, NotificationService>(sp => sp.GetRequiredService<INotificationService>().As<NotificationService>()!);
services.AddSingleton<INotificationHandlerProducer, NotificationService>(sp => sp.GetRequiredService<INotificationService>().As<NotificationService>()!);
services.AddSingleton<INotificationProducer, NotificationService>(sp => sp.GetRequiredService<INotificationService>().Cast<NotificationService>());
services.AddSingleton<INotificationHandlerProducer, NotificationService>(sp => sp.GetRequiredService<INotificationService>().Cast<NotificationService>());
services.AddSingleton<ILiveChartInitializer, LiveChartInitializer>();
services.AddSingleton<IImageCache, ImageCache>();
services.AddSingleton<ISoundService, SoundService>();
@@ -220,6 +226,8 @@ public class ProjectConfiguration : PluginConfigurationBase
services.AddSingleton<ISevenZipExtractor, SevenZipExtractor>();
services.AddSingleton<IGraphClient, GraphClient>();
services.AddSingleton<IOptionsSynchronizationService, OptionsSynchronizationService>();
services.AddScoped<IBrowserExtensionsManager, BrowserExtensionsManager>();
services.AddScoped<IBrowserExtensionsProducer, BrowserExtensionsManager>(sp => sp.GetRequiredService<IBrowserExtensionsManager>().Cast<BrowserExtensionsManager>());
services.AddScoped<ICredentialManager, CredentialManager>();
services.AddScoped<IApplicationLauncher, ApplicationLauncher>();
services.AddScoped<IScreenshotProvider, ScreenshotProvider>();
@@ -253,6 +261,8 @@ public class ProjectConfiguration : PluginConfigurationBase
services.AddScoped<IToolboxClient, ToolboxClient>();
services.AddScoped<IProcessInjector, ProcessInjector>();
services.AddScoped<ILaunchConfigurationService, LaunchConfigurationService>();
services.AddScoped<IBrowserHistoryManager, BrowserHistoryManager>();
services.AddScoped<IEventService, EventService>();
}
public override void RegisterViews(IViewProducer viewProducer)
@@ -264,7 +274,7 @@ public class ProjectConfiguration : PluginConfigurationBase
viewProducer.RegisterView<UpdateView>();
viewProducer.RegisterView<AccountsView>();
viewProducer.RegisterView<ExecutablesView>();
viewProducer.RegisterView<BuildTemplateView>();
viewProducer.RegisterView<SingleBuildTemplateView>();
viewProducer.RegisterView<BuildsListView>();
viewProducer.RegisterView<RequestElevationView>();
viewProducer.RegisterView<RequestDelevationView>();
@@ -321,6 +331,8 @@ public class ProjectConfiguration : PluginConfigurationBase
viewProducer.RegisterView<DirectSongOnboardingEntryView>();
viewProducer.RegisterView<DirectSongSwitchView>();
viewProducer.RegisterView<SettingsSynchronizationView>();
viewProducer.RegisterView<TeamBuildTemplateView>();
viewProducer.RegisterView<EventCalendarView>();
}
public override void RegisterStartupActions(IStartupActionProducer startupActionProducer)
@@ -330,6 +342,7 @@ public class ProjectConfiguration : PluginConfigurationBase
startupActionProducer.RegisterAction<RenameInstallerAction>();
startupActionProducer.RegisterAction<FixSymbolicLinkStartupAction>();
startupActionProducer.RegisterAction<UpdateUModAction>();
startupActionProducer.RegisterAction<FixPriceHistoryEntries>();
}
public override void RegisterPostUpdateActions(IPostUpdateActionProducer postUpdateActionProducer)
@@ -423,6 +436,7 @@ public class ProjectConfiguration : PluginConfigurationBase
optionsProducer.RegisterOptions<CredentialManagerOptions>();
optionsProducer.RegisterOptions<LaunchConfigurationServiceOptions>();
optionsProducer.RegisterOptions<DirectSongOptions>();
optionsProducer.RegisterOptions<MinimapWindowOptions>();
}
public override void RegisterNotificationHandlers(INotificationHandlerProducer notificationHandlerProducer)
@@ -433,6 +447,7 @@ public class ProjectConfiguration : PluginConfigurationBase
notificationHandlerProducer.RegisterNotificationHandler<FixSymbolicLinkNotificationHandler>();
notificationHandlerProducer.RegisterNotificationHandler<UpdateNotificationHandler>();
notificationHandlerProducer.RegisterNotificationHandler<ReShadeConfigChangedHandler>();
notificationHandlerProducer.RegisterNotificationHandler<NavigateToCalendarViewHandler>();
}
public override void RegisterMods(IModsManager modsManager)
@@ -446,6 +461,12 @@ public class ProjectConfiguration : PluginConfigurationBase
modsManager.RegisterMod<IDirectSongService, DirectSongService>(singleton: true);
}
public override void RegisterBrowserExtensions(IBrowserExtensionsProducer browserExtensionsProducer)
{
browserExtensionsProducer.ThrowIfNull();
browserExtensionsProducer.RegisterExtension<UBlockOriginService>();
}
private void RegisterLiteCollections(IServiceCollection services)
{
this.RegisterLiteCollection<Models.Log, LoggingOptions>(services);
+8 -7
View File
@@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Controls.Buttons"
xmlns:glyphs="clr-namespace:Daybreak.Controls.Glyphs"
mc:Ignorable="d"
x:Name="_this"
Cursor="Hand"
@@ -13,13 +14,13 @@
Highlight="{StaticResource MahApps.Brushes.Accent}"
Clicked="HighlightButton_Clicked">
<local:CircularButton.Content>
<Viewbox Stretch="Fill" Width="60" Height="60">
<Grid Width="25" Height="25">
<Path Data="m13,20a1,1 0 0 1 -1,-1l0,-12a1,1 0 0 1 2,0l0,12a1,1 0 0 1 -1,1z"
Fill="{StaticResource MahApps.Brushes.ThemeForeground}"></Path>
<Path Data="m19,14l-12,0a1,1 0 0 1 0,-2l12,0a1,1 0 0 1 0,2z"
Fill="{StaticResource MahApps.Brushes.ThemeForeground}"></Path>
</Grid>
<Viewbox>
<glyphs:PlusGlyph Foreground="{StaticResource MahApps.Brushes.ThemeForeground}"
Height="50"
Width="50"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Margin="0, 0, 14, 14"/>
</Viewbox>
</local:CircularButton.Content>
</local:CircularButton>
+2 -4
View File
@@ -4,6 +4,7 @@
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:local="clr-namespace:Daybreak.Controls.Buttons"
xmlns:glyphs="clr-namespace:Daybreak.Controls.Glyphs"
mc:Ignorable="d"
x:Name="_this"
Cursor="Hand"
@@ -14,10 +15,7 @@
Clicked="HighlightButton_Clicked">
<local:CircularButton.Content>
<Viewbox Stretch="Fill" Width="60" Height="60">
<Grid Width="30" Height="30" Margin="-2, 0, 0 ,0">
<Path Fill="{DynamicResource MahApps.Brushes.ThemeForeground}" VerticalAlignment="Top" HorizontalAlignment="Left"
Data="m18.87,9.41l-5.58,5.59l5.58,5.59a1,1 0 0 1 0,1.41l0,0a1,1 0 0 1 -1.41,0l-6.36,-6.36a0.91,0.91 0 0 1 0,-1.28l6.36,-6.36a1,1 0 0 1 1.41,0l0,0a1,1 0 0 1 0,1.41z"></Path>
</Grid>
<glyphs:ArrowGlyph />
</Viewbox>
</local:CircularButton.Content>
</local:CircularButton>
@@ -10,6 +10,7 @@
mc:Ignorable="d"
Loaded="UserControl_Loaded"
Unloaded="UserControl_Unloaded"
PreviewMouseDown="Browser_PreviewMouseDown"
x:Name="_this"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
@@ -51,11 +52,11 @@
<WrapPanel>
<buttons:BackButton Height="30" Width="30"
Clicked="BackButton_Clicked" Margin="5" Cursor="Hand"
IsEnabled="{Binding ElementName=WebBrowser, Path=CanGoBack, Mode=OneWay}"
IsEnabled="{Binding ElementName=_this, Path=BrowserHistoryManager.CanGoBack, Mode=OneWay}"
ToolTip="Go back"/>
<buttons:BackButton Height="30" Width="30"
Clicked="ForwardButton_Clicked" Margin="5" Cursor="Hand"
IsEnabled="{Binding ElementName=WebBrowser, Path=CanGoForward, Mode=OneWay}"
IsEnabled="{Binding ElementName=_this, Path=BrowserHistoryManager.CanGoForward, Mode=OneWay}"
ToolTip="Go forward">
<buttons:BackButton.RenderTransform>
<RotateTransform Angle="180" CenterX="15" CenterY="15"></RotateTransform>
@@ -94,6 +95,7 @@
<buttons:MaximizeButton Height="30" Width="30" Margin="5"
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
IsEnabled="{Binding ElementName=_this, Path=BrowserSupported, Mode=OneWay}"
Visibility="{Binding ElementName=_this, Path=MaximizeButtonVisible, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"
Clicked="MaximizeButton_Clicked"
ToolTip="Maximize"/>
</StackPanel>
@@ -2,13 +2,16 @@
using Daybreak.Configuration.Options;
using Daybreak.Models;
using Daybreak.Models.Browser;
using Daybreak.Models.Builds;
using Daybreak.Models.Guildwars;
using Daybreak.Services.Browser;
using Daybreak.Services.BuildTemplates;
using Daybreak.Utils;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Web.WebView2.Core;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Core.Extensions;
using System.Diagnostics;
@@ -21,6 +24,7 @@ using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
using System.Windows.Input;
namespace Daybreak.Controls;
@@ -39,10 +43,10 @@ public partial class ChromiumBrowserWrapper : UserControl
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 readonly object Lock = new();
private static readonly Regex WebAddressRegex = BuildWebAddressRegex();
private static readonly SemaphoreSlim SemaphoreSlim = new(1, 1);
private static CoreWebView2Environment? CoreWebView2Environment;
private static CoreWebView2Environment? CoreWebView2Environment;
public event EventHandler<string>? FavoriteUriChanged;
public event EventHandler? MaximizeClicked;
@@ -50,12 +54,14 @@ public partial class ChromiumBrowserWrapper : UserControl
public event EventHandler<DownloadPayload>? DownloadingFile;
public event EventHandler<string>? DownloadedFile;
private readonly HashSet<ulong> domNavigationIds = [];
private readonly Task initializationTask;
private readonly IHttpClient<ChromiumBrowserWrapper> httpClient;
private readonly ILiveOptions<BrowserOptions> liveOptions;
private readonly ILogger<ChromiumBrowserWrapper> logger;
private readonly IBuildTemplateManager buildTemplateManager;
private readonly IBrowserExtensionsManager browserExtensionsManager;
[GenerateDependencyProperty(InitialValue = true)]
private bool canDownloadBuild;
[GenerateDependencyProperty(InitialValue = true)]
@@ -86,6 +92,8 @@ public partial class ChromiumBrowserWrapper : UserControl
private bool showBrowserDisabledMessage;
[GenerateDependencyProperty(InitialValue = true)]
private bool showDownloadsDialog = true;
[GenerateDependencyProperty(InitialValue = true)]
private bool maximizeButtonVisible = true;
private bool browserInitialized = false;
@@ -95,8 +103,13 @@ public partial class ChromiumBrowserWrapper : UserControl
set => this.SetValue(AddressProperty, value);
}
public IBrowserHistoryManager BrowserHistoryManager { get; }
public ChromiumBrowserWrapper()
: this(Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IHttpClient<ChromiumBrowserWrapper>>(),
: this(
Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IBrowserHistoryManager>(),
Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IBrowserExtensionsManager>(),
Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IHttpClient<ChromiumBrowserWrapper>>(),
Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<ILiveOptions<BrowserOptions>>(),
Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IBuildTemplateManager>(),
Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<ILogger<ChromiumBrowserWrapper>>())
@@ -104,18 +117,22 @@ public partial class ChromiumBrowserWrapper : UserControl
}
public ChromiumBrowserWrapper(
IBrowserHistoryManager historyManager,
IBrowserExtensionsManager browserExtensionsManager,
IHttpClient<ChromiumBrowserWrapper> httpClient,
ILiveOptions<BrowserOptions> liveOptions,
IBuildTemplateManager buildTemplateManager,
ILogger<ChromiumBrowserWrapper> logger)
{
this.BrowserHistoryManager = historyManager.ThrowIfNull();
this.browserExtensionsManager = browserExtensionsManager.ThrowIfNull();
this.httpClient = httpClient.ThrowIfNull();
this.liveOptions = liveOptions.ThrowIfNull();
this.buildTemplateManager = buildTemplateManager.ThrowIfNull();
this.logger = logger.ThrowIfNull();
this.InitializeEnvironment();
this.InitializeComponent();
this.BrowserHistoryManager.InitializeHistoryManager(this);
this.initializationTask = Task.Run(this.InitializeBrowserSafe);
}
@@ -149,24 +166,22 @@ public partial class ChromiumBrowserWrapper : UserControl
{
try
{
lock (Lock)
if (CoreWebView2Environment is not null)
{
if (CoreWebView2Environment is not null)
{
this.BrowserSupported = true;
return;
}
CoreWebView2Environment ??= System.Extensions.TaskExtensions.RunSync(() => CoreWebView2Environment.CreateAsync(null, "BrowserData", new CoreWebView2EnvironmentOptions
{
EnableTrackingPrevention = true,
AllowSingleSignOnUsingOSPrimaryAccount = true
}));
this.BrowserSupported = true;
return;
}
CoreWebView2Environment ??= System.Extensions.TaskExtensions.RunSync(() => CoreWebView2Environment.CreateAsync(null, "BrowserData", new CoreWebView2EnvironmentOptions
{
EnableTrackingPrevention = true,
AllowSingleSignOnUsingOSPrimaryAccount = true,
AreBrowserExtensionsEnabled = true,
}));
this.BrowserSupported = true;
}
catch(Exception e)
catch (Exception e)
{
this.logger!.LogWarning($"Browser initialization failed. Details: {e}");
this.BrowserSupported = false;
@@ -177,10 +192,7 @@ public partial class ChromiumBrowserWrapper : UserControl
{
await this.Dispatcher.InvokeAsync(async () =>
{
while (!Monitor.TryEnter(Lock))
{
await Task.Delay(100);
}
await SemaphoreSlim.WaitAsync();
try
{
@@ -189,7 +201,7 @@ public partial class ChromiumBrowserWrapper : UserControl
}
finally
{
Monitor.Exit(Lock);
SemaphoreSlim.Release();
}
});
}
@@ -201,12 +213,18 @@ public partial class ChromiumBrowserWrapper : UserControl
if (!this.BrowserSupported ||
!this.BrowserEnabled)
{
return;
}
if (this.WebBrowser.CoreWebView2 is not null)
{
return;
}
this.ShowBrowserDisabledMessage = false;
await this.WebBrowser.EnsureCoreWebView2Async(CoreWebView2Environment);
await this.WebBrowser.EnsureCoreWebView2Async(CoreWebView2Environment).ConfigureAwait(true);
await this.browserExtensionsManager.InitializeBrowserEnvironment(this.WebBrowser.CoreWebView2!.Profile, CoreWebView2Environment!.BrowserVersionString).ConfigureAwait(true);
if (this.Address is not null &&
Uri.TryCreate(this.Address, UriKind.RelativeOrAbsolute, out var uri))
{
@@ -217,6 +235,21 @@ public partial class ChromiumBrowserWrapper : UserControl
this.AddressBarReadonly = this.liveOptions!.Value.AddressBarReadonly;
this.CanDownloadBuild = this.liveOptions.Value.DynamicBuildLoading;
this.WebBrowser.CoreWebView2.NewWindowRequested += (browser, args) => args.Handled = true;
this.WebBrowser.KeyDown += (sender, e) =>
{
if ((e.Key == Key.Left && Keyboard.Modifiers == ModifierKeys.Alt) ||
(e.Key == Key.BrowserBack))
{
this.BrowserHistoryManager.GoBack();
e.Handled = true; // Prevent default behavior
}
else if ((e.Key == Key.Right && Keyboard.Modifiers == ModifierKeys.Alt) ||
(e.Key == Key.BrowserForward))
{
this.BrowserHistoryManager.GoForward();
e.Handled = true; // Prevent default behavior
}
};
this.WebBrowser.NavigationStarting += (browser, args) =>
{
if (this.CanNavigate is false && args.Uri != this.Address)
@@ -271,6 +304,13 @@ public partial class ChromiumBrowserWrapper : UserControl
};
this.WebBrowser.CoreWebView2.DOMContentLoaded += async (browser, args) =>
{
if (this.domNavigationIds.Contains(args.NavigationId))
{
return;
}
this.domNavigationIds.Add(args.NavigationId);
await this.WebBrowser.CoreWebView2.ExecuteScriptAsync(Scripts.CaptureNavigationButtons);
if (this.CanDownloadBuild)
{
await this.WebBrowser.CoreWebView2.ExecuteScriptAsync(Scripts.SendSelectionOnContextMenu);
@@ -376,12 +416,12 @@ public partial class ChromiumBrowserWrapper : UserControl
{
payload = args.WebMessageAsJson.Deserialize<BrowserPayload>();
}
catch(Exception e)
catch (Exception e)
{
this.logger!.LogError(e, $"Exception encountered when deserializing {nameof(BrowserPayload)}");
}
if (payload?.Key == BrowserPayload.PayloadKeys.ContextMenu)
if (payload?.Key is BrowserPayload.PayloadKeys.ContextMenu)
{
var contextMenuPayload = args.WebMessageAsJson.Deserialize<BrowserPayload<OnContextMenuPayload>>();
var maybeTemplate = contextMenuPayload?.Value?.Selection?.Trim();
@@ -411,12 +451,20 @@ public partial class ChromiumBrowserWrapper : UserControl
this.ContextMenu.IsOpen = true;
});
}
catch(Exception e)
catch (Exception e)
{
this.logger!.LogWarning($"Exception when decoding template {maybeTemplate}. Details {e}");
}
});
}
else if (payload?.Key is BrowserPayload.PayloadKeys.XButton1Pressed)
{
this.BrowserHistoryManager.GoBack();
}
else if (payload?.Key is BrowserPayload.PayloadKeys.XButton2Pressed)
{
this.BrowserHistoryManager.GoForward();
}
}
private void DownloadOperation_StateChanged(object? sender, object? e)
@@ -439,7 +487,7 @@ public partial class ChromiumBrowserWrapper : UserControl
private void LoadBuildTemplateButton_Click(object sender, EventArgs e)
{
var build = this.ContextMenu.DataContext.As<Build>();
var build = this.ContextMenu.DataContext.As<IBuildEntry>();
this.ContextMenu.IsOpen = false;
if (build is null)
{
@@ -461,26 +509,24 @@ public partial class ChromiumBrowserWrapper : UserControl
return;
}
this.BrowserHistoryManager.UnInitializeHistoryManager();
this.WebBrowser?.Dispose();
this.browserInitialized = false;
}
private void BackButton_Clicked(object sender, EventArgs e)
{
this.WebBrowser.GoBack();
this.Address = this.WebBrowser.Source.ToString();
this.BrowserHistoryManager.GoBack();
}
private void ForwardButton_Clicked(object sender, EventArgs e)
{
this.WebBrowser.GoForward();
this.Address = this.WebBrowser.Source.ToString();
this.BrowserHistoryManager.GoForward();
}
private void RefreshGlyph_Clicked(object sender, EventArgs e)
{
this.WebBrowser.Reload();
this.Address = this.WebBrowser.Source.ToString();
this.BrowserHistoryManager.Reload();
}
private void CancelGlyph_Clicked(object sender, EventArgs e)
@@ -504,6 +550,11 @@ public partial class ChromiumBrowserWrapper : UserControl
this.MaximizeClicked?.Invoke(this, e);
}
private void Browser_PreviewMouseDown(object sender, MouseButtonEventArgs e)
{
}
private void CheckFavoriteAddress()
{
if (this.Address == this.FavoriteAddress)
@@ -541,4 +592,7 @@ public partial class ChromiumBrowserWrapper : UserControl
Uri.TryCreate(address, UriKind.Absolute, out var uri);
return uri?.ToString() ?? string.Empty;
}
[GeneratedRegex("^((http|ftp|https)://)?([\\w_-]+(?:(?:\\.[\\w_-]+)+))([\\w.,@?^=%&:/~+#-]*[\\w@?^=%&/~+#-])?", RegexOptions.Compiled)]
private static partial Regex BuildWebAddressRegex();
}
+76
View File
@@ -0,0 +1,76 @@
<UserControl x:Class="Daybreak.Controls.DropDownButton"
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"
xmlns:glyphs="clr-namespace:Daybreak.Controls.Glyphs"
xmlns:converters="clr-namespace:Daybreak.Converters"
mc:Ignorable="d"
x:Name="_this"
Tag="{Binding RelativeSource={RelativeSource Self}}"
MouseRightButtonDown="IgnoreRightMouseButton"
MouseRightButtonUp="IgnoreRightMouseButton"
d:DesignHeight="450" d:DesignWidth="800">
<UserControl.Resources>
<converters:BooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter" TriggerValue="True"/>
</UserControl.Resources>
<UserControl.ContextMenu>
<ContextMenu Placement="Bottom">
<ContextMenu.Template>
<ControlTemplate>
<local:DropDownButtonContextMenu
Items="{Binding PlacementTarget.Tag.Items, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
ItemTemplate="{Binding PlacementTarget.Tag.ItemTemplate, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
Background="{Binding PlacementTarget.Tag.Background, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
Width="{Binding PlacementTarget.Tag.ActualWidth, RelativeSource={RelativeSource AncestorType=ContextMenu}}"
ItemClicked="DropDownButtonContextMenu_ItemClicked"/>
</ControlTemplate>
</ContextMenu.Template>
</ContextMenu>
</UserControl.ContextMenu>
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
<ColumnDefinition Width="auto"/>
</Grid.ColumnDefinitions>
<buttons:HighlightButton Grid.Column="0"
Foreground="{DynamicResource MahApps.Brushes.ThemeForeground}"
HighlightColor="{DynamicResource MahApps.Brushes.Accent}"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
VerticalContentAlignment="Center"
HorizontalContentAlignment="Center"
Clicked="MainButton_Clicked">
<buttons:HighlightButton.ButtonContent>
<ContentControl ContentTemplate="{Binding ItemTemplate, RelativeSource={RelativeSource AncestorType={x:Type local:DropDownButton}}}"
Content="{Binding SelectedItem, RelativeSource={RelativeSource AncestorType={x:Type local:DropDownButton}}}" />
</buttons:HighlightButton.ButtonContent>
</buttons:HighlightButton>
<buttons:HighlightButton Grid.Column="1"
Foreground="{DynamicResource MahApps.Brushes.ThemeForeground}"
HighlightColor="{DynamicResource MahApps.Brushes.Accent}"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Clicked="ArrowButton_Clicked">
<buttons:HighlightButton.ButtonContent>
<glyphs:ArrowGlyph Foreground="{DynamicResource MahApps.Brushes.ThemeForeground}"
Height="30"
Width="30"
VerticalAlignment="Center"
HorizontalAlignment="Center"
RenderTransformOrigin="0.5 0.5">
<glyphs:ArrowGlyph.RenderTransform>
<RotateTransform Angle="270"/>
</glyphs:ArrowGlyph.RenderTransform>
</glyphs:ArrowGlyph>
</buttons:HighlightButton.ButtonContent>
</buttons:HighlightButton>
<Rectangle Opacity="0.2"
Fill="Black"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Visibility="{Binding ElementName=_this, Path=ClickEnabled, Mode=OneWay, Converter={StaticResource InverseBooleanToVisibilityConverter}}"/>
</Grid>
</UserControl>
+60
View File
@@ -0,0 +1,60 @@
using System;
using System.Collections;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
using System.Windows.Input;
namespace Daybreak.Controls;
/// <summary>
/// Interaction logic for DropdownButton.xaml
/// </summary>
public partial class DropDownButton : UserControl
{
[GenerateDependencyProperty]
private DataTemplate itemTemplate = default!;
[GenerateDependencyProperty]
private object selectedItem = default!;
[GenerateDependencyProperty]
private IEnumerable items = default!;
[GenerateDependencyProperty(InitialValue = true)]
private bool clickEnabled = true;
public event EventHandler<object> Clicked = default!;
public event EventHandler<object> SelectionChanged = default!;
public DropDownButton()
{
this.InitializeComponent();
}
private void MainButton_Clicked(object sender, EventArgs e)
{
this.Clicked?.Invoke(this, this.SelectedItem);
}
private void ArrowButton_Clicked(object sender, EventArgs e)
{
var contextMenu = this.ContextMenu;
contextMenu.PlacementTarget = this;
contextMenu.IsOpen = true;
}
private void DropDownButtonContextMenu_ItemClicked(object _, object e)
{
this.SelectedItem = e;
this.ContextMenu.IsOpen = false;
this.SelectionChanged?.Invoke(this, e);
}
private void IgnoreRightMouseButton(object sender, MouseButtonEventArgs e)
{
if (e.ChangedButton is MouseButton.Right)
{
e.Handled = true;
}
}
}
@@ -0,0 +1,29 @@
<UserControl x:Class="Daybreak.Controls.DropDownButtonContextMenu"
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">
<Grid>
<ItemsControl ItemsSource="{Binding ElementName=_this, Path=Items, Mode=OneWay}">
<ItemsControl.ItemTemplate>
<DataTemplate>
<buttons:HighlightButton HighlightColor="{DynamicResource MahApps.Brushes.Accent}"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Tag="{Binding Mode=OneWay}"
Clicked="HighlightButton_Clicked">
<buttons:HighlightButton.ButtonContent>
<ContentControl DataContext="{Binding DataContext, RelativeSource={RelativeSource AncestorType={x:Type buttons:HighlightButton}}}"
ContentTemplate="{Binding ItemTemplate, RelativeSource={RelativeSource AncestorType={x:Type local:DropDownButtonContextMenu}}}"/>
</buttons:HighlightButton.ButtonContent>
</buttons:HighlightButton>
</DataTemplate>
</ItemsControl.ItemTemplate>
</ItemsControl>
</Grid>
</UserControl>
@@ -0,0 +1,32 @@
using Daybreak.Controls.Buttons;
using System;
using System.Collections;
using System.Extensions;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
namespace Daybreak.Controls;
/// <summary>
/// Interaction logic for DropDownButtonContextMenu.xaml
/// </summary>
public partial class DropDownButtonContextMenu : UserControl
{
[GenerateDependencyProperty]
private DataTemplate itemTemplate = default!;
[GenerateDependencyProperty]
private IEnumerable items = default!;
public event EventHandler<object> ItemClicked = default!;
public DropDownButtonContextMenu()
{
this.InitializeComponent();
}
private void HighlightButton_Clicked(object sender, EventArgs e)
{
this.ItemClicked?.Invoke(this, sender.Cast<HighlightButton>().DataContext);
}
}
@@ -16,6 +16,7 @@
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
</Grid.ColumnDefinitions>
<TextBlock
Grid.Column="0"
@@ -27,7 +28,7 @@
HorizontalAlignment="Left" />
<buttons:HighlightButton
Grid.Column="1"
Title="Edit Build"
Title="Export Build"
HighlightColor="{DynamicResource MahApps.Brushes.Accent}"
Foreground="{DynamicResource MahApps.Brushes.ThemeForeground}"
Margin="10, 0, 10, 0"
@@ -40,6 +41,19 @@
ToolTip="Load current build"/>
<buttons:HighlightButton
Grid.Column="2"
Title="Export Team Build"
HighlightColor="{DynamicResource MahApps.Brushes.Accent}"
Foreground="{DynamicResource MahApps.Brushes.ThemeForeground}"
Margin="10, 0, 10, 0"
HorizontalAlignment="Stretch"
HorizontalContentAlignment="Center"
VerticalContentAlignment="Center"
Cursor="Hand"
FontSize="18"
MouseLeftButtonDown="EditTeamBuild_MouseLeftButtonDown"
ToolTip="Load current team build"/>
<buttons:HighlightButton
Grid.Column="3"
Title="Meta Builds"
HighlightColor="{DynamicResource MahApps.Brushes.Accent}"
Foreground="{DynamicResource MahApps.Brushes.ThemeForeground}"
@@ -2,10 +2,13 @@
using Daybreak.Models.Guildwars;
using Daybreak.Services.BuildTemplates;
using Daybreak.Services.Navigation;
using Daybreak.Services.Scanner;
using Daybreak.Views;
using Microsoft.Extensions.DependencyInjection;
using NAudio.CoreAudioApi;
using System;
using System.Core.Extensions;
using System.Threading;
using System.Windows.Controls;
namespace Daybreak.Controls.FocusViewComponents;
@@ -14,6 +17,7 @@ namespace Daybreak.Controls.FocusViewComponents;
/// </summary>
public partial class MainPlayerInformationComponent : UserControl
{
private readonly IGuildwarsMemoryCache guildwarsMemoryCache;
private readonly IBuildTemplateManager buildTemplateManager;
private readonly IViewManager viewManager;
@@ -21,15 +25,18 @@ public partial class MainPlayerInformationComponent : UserControl
public MainPlayerInformationComponent()
: this(
Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IGuildwarsMemoryCache>(),
Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IBuildTemplateManager>(),
Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IViewManager>())
{
}
public MainPlayerInformationComponent(
IGuildwarsMemoryCache guildwarsMemoryCache,
IBuildTemplateManager buildTemplateManager,
IViewManager viewManager)
{
this.guildwarsMemoryCache = guildwarsMemoryCache.ThrowIfNull();
this.buildTemplateManager = buildTemplateManager.ThrowIfNull();
this.viewManager = viewManager.ThrowIfNull();
this.InitializeComponent();
@@ -89,9 +96,46 @@ public partial class MainPlayerInformationComponent : UserControl
if (mainPlayer.CurrentBuild is Build build)
{
var buildEntry = this.buildTemplateManager.CreateBuild();
buildEntry.Build = build;
this.viewManager.ShowView<BuildTemplateView>(buildEntry);
var buildEntry = this.buildTemplateManager.CreateSingleBuild();
buildEntry.Primary = build.Primary;
buildEntry.Secondary = build.Secondary;
buildEntry.Attributes = build.Attributes;
buildEntry.Skills = build.Skills;
this.viewManager.ShowView<SingleBuildTemplateView>(buildEntry);
}
}
private async void EditTeamBuild_MouseLeftButtonDown(object _, System.Windows.Input.MouseButtonEventArgs e)
{
var gameData = await this.guildwarsMemoryCache.ReadGameData(CancellationToken.None);
if (gameData is null)
{
return;
}
var teamBuild = this.buildTemplateManager.CreateTeamBuild();
teamBuild.Builds.Clear();
var mainPlayerBuild = this.buildTemplateManager.CreateSingleBuild();
mainPlayerBuild.Primary = gameData.MainPlayer?.CurrentBuild?.Primary!;
mainPlayerBuild.Secondary = gameData.MainPlayer?.CurrentBuild?.Secondary!;
mainPlayerBuild.Attributes = gameData.MainPlayer?.CurrentBuild?.Attributes!;
mainPlayerBuild.Skills = gameData.MainPlayer?.CurrentBuild?.Skills!;
teamBuild.Builds.Add(mainPlayerBuild);
foreach (var player in gameData.Party!)
{
if (player.CurrentBuild is null)
{
continue;
}
var singleBuildEntry = this.buildTemplateManager.CreateSingleBuild();
singleBuildEntry.Primary = player.CurrentBuild.Primary;
singleBuildEntry.Secondary = player.CurrentBuild.Secondary;
singleBuildEntry.Attributes = player.CurrentBuild.Attributes;
singleBuildEntry.Skills = player.CurrentBuild.Skills;
teamBuild.Builds.Add(singleBuildEntry);
}
this.viewManager.ShowView<TeamBuildTemplateView>(teamBuild);
}
}
+15
View File
@@ -0,0 +1,15 @@
<UserControl x:Class="Daybreak.Controls.Glyphs.ArrowGlyph"
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"
d:DesignHeight="450" d:DesignWidth="800">
<Viewbox>
<Grid Width="30" Height="30" Margin="-2, 0, 0 ,0">
<Path Fill="{DynamicResource MahApps.Brushes.ThemeForeground}" VerticalAlignment="Top" HorizontalAlignment="Left"
Data="m18.87,9.41l-5.58,5.59l5.58,5.59a1,1 0 0 1 0,1.41l0,0a1,1 0 0 1 -1.41,0l-6.36,-6.36a0.91,0.91 0 0 1 0,-1.28l6.36,-6.36a1,1 0 0 1 1.41,0l0,0a1,1 0 0 1 0,1.41z"></Path>
</Grid>
</Viewbox>
</UserControl>
@@ -0,0 +1,13 @@
using System.Windows.Controls;
namespace Daybreak.Controls.Glyphs;
/// <summary>
/// Interaction logic for ArrowGlyph.xaml
/// </summary>
public partial class ArrowGlyph : UserControl
{
public ArrowGlyph()
{
this.InitializeComponent();
}
}
+16
View File
@@ -0,0 +1,16 @@
<UserControl x:Class="Daybreak.Controls.Glyphs.PersonGlyph"
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">
<Grid>
<Viewbox>
<Path Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m12.5,11c3.03757,0 5.5,-2.46243 5.5,-5.5c0,-3.03757 -2.46243,-5.5 -5.5,-5.5c-3.03757,0 -5.5,2.46243 -5.5,5.5c0,3.03757 2.46243,5.5 5.5,5.5l0,0zm12.5,12l0,0l0,0l-25,0c0.74635,-6.19634 6.05817,-11 12.5,-11c6.44183,0 11.75365,4.80366 12.5,11l0,0z"/>
</Viewbox>
</Grid>
</UserControl>
@@ -0,0 +1,13 @@
using System.Windows.Controls;
namespace Daybreak.Controls.Glyphs;
/// <summary>
/// Interaction logic for PersonGlyph.xaml
/// </summary>
public partial class PersonGlyph : UserControl
{
public PersonGlyph()
{
this.InitializeComponent();
}
}
+20
View File
@@ -0,0 +1,20 @@
<UserControl x:Class="Daybreak.Controls.Glyphs.PinGlyph"
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>
<Grid>
<Path Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m15.0058,27.53079a1,1 0 0 1 -0.71,-0.29l-11.53,-11.53a1,1 0 0 1 0,-1.42l1.52,-1.51a6.06,6.06 0 0 1 6.37,-1.43l5.41,-5.41l-0.94,-0.94a1,1 0 0 1 0,-1.41l3.29,-3.3a1,1 0 0 1 1.41,0l9.89,9.89a1,1 0 0 1 0,1.41l-3.3,3.29a1,1 0 0 1 -1.41,0l-0.94,-0.94l-5.41,5.41a6.08,6.08 0 0 1 -1.43,6.37l-1.51,1.52a1,1 0 0 1 -0.71,0.29zm-10.12,-12.53l10.12,10.12l0.81,-0.81a4.12,4.12 0 0 0 0.77,-4.75a1,1 0 0 1 0.18,-1.15l6.59,-6.59a1,1 0 0 1 1.42,0l0.94,0.94l1.88,-1.88l-8.47,-8.47l-1.88,1.88l0.94,0.94a1,1 0 0 1 0,1.42l-6.59,6.59a1,1 0 0 1 -1.15,0.18a4.12,4.12 0 0 0 -4.75,0.77l-0.81,0.81z"/>
<Path Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m1.0058,30.00079a1,1 0 0 1 -0.71,-0.29a1,1 0 0 1 0,-1.42l8.24,-8.23a1,1 0 0 1 1.41,1.41l-8.23,8.24a1,1 0 0 1 -0.71,0.29z"/>
<Path Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m24.0658,13.53079a1,1 0 0 1 -0.71,-0.29l-2.47,-2.47a1,1 0 1 1 1.42,-1.42l2.47,2.47a1,1 0 0 1 0,1.42a1,1 0 0 1 -0.71,0.29z"/>
</Grid>
</Viewbox>
</UserControl>
+13
View File
@@ -0,0 +1,13 @@
using System.Windows.Controls;
namespace Daybreak.Controls.Glyphs;
/// <summary>
/// Interaction logic for PinGlyph.xaml
/// </summary>
public partial class PinGlyph : UserControl
{
public PinGlyph()
{
this.InitializeComponent();
}
}
+18
View File
@@ -0,0 +1,18 @@
<UserControl x:Class="Daybreak.Controls.Glyphs.PlusGlyph"
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 Stretch="Fill">
<Grid>
<Path Data="m13,20a1,1 0 0 1 -1,-1l0,-12a1,1 0 0 1 2,0l0,12a1,1 0 0 1 -1,1z"
Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"></Path>
<Path Data="m19,14l-12,0a1,1 0 0 1 0,-2l12,0a1,1 0 0 1 0,2z"
Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"></Path>
</Grid>
</Viewbox>
</UserControl>
@@ -0,0 +1,13 @@
using System.Windows.Controls;
namespace Daybreak.Controls.Glyphs;
/// <summary>
/// Interaction logic for PlusGlyph.xaml
/// </summary>
public partial class PlusGlyph : UserControl
{
public PlusGlyph()
{
this.InitializeComponent();
}
}
+40
View File
@@ -0,0 +1,40 @@
<UserControl x:Class="Daybreak.Controls.Glyphs.TeamGlyph"
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">
<Grid>
<Viewbox>
<Grid>
<Ellipse Margin="97,25,0,0"
Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Width="13"
Height="13"/>
<Ellipse Margin="20,25,0,0"
VerticalAlignment="Top"
HorizontalAlignment="Left"
Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Width="13"
Height="13"/>
<Ellipse Margin="0,18,0,0"
VerticalAlignment="Top"
HorizontalAlignment="Center"
Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Width="18"
Height="18"/>
<Path Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m34.6,51.17l0,0q-0.43,0.61 -0.84,1.24l0,0a35.82,35.82 0 0 0 -5.76,19.53l72,0a35.82,35.82 0 0 0 -5.71,-19.44l0,0q-0.41,-0.63 -0.84,-1.24l0,0a36,36 0 0 0 -58.8,0l-0.05,-0.09z"/>
<Path Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m0,63.94l20.74,0a44,44 0 0 1 13.72,-24.58a26,26 0 0 0 -34.46,24.58z"/>
<Path Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
Data="m102,37.94a26,26 0 0 0 -8.46,1.42a44,44 0 0 1 13.72,24.58l20.74,0a26,26 0 0 0 -26,-26z"/>
</Grid>
</Viewbox>
</Grid>
</UserControl>
@@ -0,0 +1,13 @@
using System.Windows.Controls;
namespace Daybreak.Controls.Glyphs;
/// <summary>
/// Interaction logic for TeamGlyph.xaml
/// </summary>
public partial class TeamGlyph : UserControl
{
public TeamGlyph()
{
this.InitializeComponent();
}
}
@@ -58,7 +58,7 @@ public partial class LivingEntityContextMenu : UserControl
this.PrimaryProfessionVisible = false;
}
await this.guildwarsMemoryReader.EnsureInitialized(context.GuildWarsApplicationLaunchContext!.GuildWarsProcess, CancellationToken.None);
await this.guildwarsMemoryReader.EnsureInitialized(context.GuildWarsApplicationLaunchContext!.ProcessId, CancellationToken.None);
this.EntityName = await this.guildwarsMemoryReader.GetEntityName(context.LivingEntity!, CancellationToken.None).ConfigureAwait(true);
}
+7
View File
@@ -51,6 +51,13 @@
Cursor="Hand"
Clicked="CopyGuildwarsButton_Clicked"
ToolTip="Copy Guild Wars from an existing installation"/>
<buttons:MenuButton Title="Event Calendar"
Foreground="{DynamicResource MahApps.Brushes.ThemeForeground}"
HighlightColor="{DynamicResource MahApps.Brushes.Accent}"
Height="30"
Cursor="Hand"
Clicked="CalendarButton_Clicked"
ToolTip="Show current and upcoming events"/>
</local:ExpandableMenuSection.Children>
</local:ExpandableMenuSection>
<Grid>
+5
View File
@@ -163,6 +163,11 @@ public partial class MenuList : UserControl
this.viewManager.ShowView<TradeAlertsView>();
}
private void CalendarButton_Clicked(object sender, EventArgs e)
{
this.viewManager.ShowView<EventCalendarView>();
}
private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
{
this.cancellationTokenSource = new CancellationTokenSource();
@@ -68,8 +68,13 @@
</ContextMenu>
</UserControl.Resources>
<Grid>
<Image x:Name="MapDrawingHost" VerticalAlignment="Top" HorizontalAlignment="Left" Stretch="Fill"/>
<Image x:Name="EntitiesDrawingHost" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
<Grid RenderTransformOrigin="0.5 0.5">
<Grid.RenderTransform>
<RotateTransform x:Name="RotateTransform" Angle="{Binding ElementName=_this, Path=Angle, Mode=OneWay}"/>
</Grid.RenderTransform>
<Image x:Name="MapDrawingHost" VerticalAlignment="Top" HorizontalAlignment="Left" Stretch="Fill" />
<Image x:Name="EntitiesDrawingHost" VerticalAlignment="Stretch" HorizontalAlignment="Stretch" />
</Grid>
<Grid Background="{DynamicResource Daybreak.Brushes.Background}"
VerticalAlignment="Bottom"
HorizontalAlignment="Right"
@@ -79,15 +84,5 @@
Clicked="MaximizeButton_Clicked"
ToolTip="Maximize"/>
</Grid>
<WrapPanel Background="{DynamicResource Daybreak.Brushes.Background}"
VerticalAlignment="Top"
HorizontalAlignment="Right" >
<TextBlock Text="{Binding ElementName=_this, Path=TargetEntityId, Mode=OneWay}" Foreground="{DynamicResource MahApps.Brushes.ThemeForeground}"
FontSize="16" Padding="5" />
<TextBlock Text=" - " Foreground="{DynamicResource MahApps.Brushes.ThemeForeground}"
FontSize="16" Padding="5" />
<TextBlock Text="{Binding ElementName=_this, Path=TargetEntityModelId, Mode=OneWay}" Foreground="{DynamicResource MahApps.Brushes.ThemeForeground}"
FontSize="16" Padding="5" />
</WrapPanel>
</Grid>
</UserControl>
@@ -24,6 +24,8 @@ using System.Diagnostics.Metrics;
using System.Diagnostics;
using Daybreak.Models.FocusView;
using Daybreak.Models.LaunchConfigurations;
using System.Configuration;
using System.Runtime.CompilerServices;
namespace Daybreak.Controls.Minimap;
@@ -78,6 +80,10 @@ public partial class GuildwarsMinimap : UserControl
[GenerateDependencyProperty]
private bool controlsVisible;
[GenerateDependencyProperty]
private bool canRotate;
[GenerateDependencyProperty]
private double angle;
[GenerateDependencyProperty]
private int targetEntityId;
[GenerateDependencyProperty]
private int targetEntityModelId;
@@ -187,6 +193,8 @@ public partial class GuildwarsMinimap : UserControl
entity.Health = state?.Health ?? 0;
entity.Energy = state?.Energy ?? 0;
}
this.Angle = this.CanRotate ? (this.GameState.Camera.Yaw - (Math.PI / 2)) * (180 / Math.PI) : 0;
}
private void UpdateGameData()
@@ -368,8 +376,10 @@ public partial class GuildwarsMinimap : UserControl
{
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);
var entityPoint = new Point(x, y);
var centerPoint = new Point(this.ActualWidth / 2, this.ActualHeight / 2);
var finalEntityPoint = RotatePointAroundPivot(entityPoint, centerPoint, this.Angle);
return Math.Pow(mousePosition.X - finalEntityPoint.X, 2) + Math.Pow(mousePosition.Y - finalEntityPoint.Y, 2) < Math.Pow(EntitySize * this.Zoom, 2);
}
private void DragMinimap()
@@ -379,9 +389,14 @@ public partial class GuildwarsMinimap : UserControl
return;
}
var rad = -this.Angle * Math.PI / 180;
var mousePosition = Mouse.GetPosition(this);
var offset = mousePosition - this.initialClickPoint;
var transformedOffset = new Vector((offset.X * Math.Cos(rad)) - (offset.Y * Math.Sin(rad)),
(offset.X * Math.Sin(rad)) + (offset.Y * Math.Cos(rad)));
this.initialClickPoint = mousePosition;
this.offsetRevert = 0;
this.originOffset = mousePosition - this.initialClickPoint;
this.originOffset += transformedOffset;
this.offsetRevertTime = DateTime.Now + this.offsetRevertDelay;
this.UpdateGameData();
}
@@ -518,7 +533,7 @@ public partial class GuildwarsMinimap : UserControl
private void GuildwarsMinimap_MouseLeftButtonDown(object sender, MouseButtonEventArgs e)
{
this.initialClickPoint = Mouse.GetPosition(this) - this.originOffset;
this.initialClickPoint = Mouse.GetPosition(this);
this.offsetRevert = 0;
this.offsetRevertTime = DateTime.Now + this.offsetRevertDelay;
this.dragging = true;
@@ -694,7 +709,7 @@ public partial class GuildwarsMinimap : UserControl
{
var delta = e.Delta > 0 ? 0.1 : -0.1;
var previousZoom = this.Zoom;
var newZoom = this.Zoom + this.Zoom * delta;
var newZoom = this.Zoom + (this.Zoom * delta);
this.originOffset *= newZoom / previousZoom;
this.initialClickPoint = new Point(
this.initialClickPoint.X * newZoom / previousZoom,
@@ -785,6 +800,26 @@ public partial class GuildwarsMinimap : UserControl
}
}
private static Point RotatePointAroundPivot(Point point, Point pivot, double angle)
{
// Translate the point to the pivot point
point = new Point(point.X - pivot.X, point.Y - pivot.Y);
// Convert angle to radians for the rotation
double radians = Math.PI / 180 * angle;
// Rotate the point around the pivot
double rotatedX = (point.X * Math.Cos(radians)) - (point.Y * Math.Sin(radians));
double rotatedY = (point.X * Math.Sin(radians)) + (point.Y * Math.Cos(radians));
// Translate the point back
rotatedX += pivot.X;
rotatedY += pivot.Y;
return new Point(rotatedX, rotatedY);
}
private static bool PositionsCollide(Position position1, Position position2)
{
var a = PositionRadius + PositionRadius;
+1 -1
View File
@@ -43,7 +43,7 @@ public partial class PlayerContextMenu : UserControl
return;
}
await this.guildwarsMemoryReader.EnsureInitialized(context.GuildWarsApplicationLaunchContext!.GuildWarsProcess, CancellationToken.None);
await this.guildwarsMemoryReader.EnsureInitialized(context.GuildWarsApplicationLaunchContext!.ProcessId, CancellationToken.None);
this.PlayerName = await this.guildwarsMemoryReader.GetEntityName(context.Player!, CancellationToken.None).ConfigureAwait(true);
}
@@ -23,7 +23,6 @@
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto"></ColumnDefinition>
@@ -31,7 +30,6 @@
</Grid.ColumnDefinitions>
<TextBlock FontSize="16" Text="Username:" Foreground="{StaticResource MahApps.Brushes.ThemeForeground}" Margin="5" Grid.Row="0" HorizontalAlignment="Right" VerticalAlignment="Center"></TextBlock>
<TextBlock FontSize="16" Text="Password:" Foreground="{StaticResource MahApps.Brushes.ThemeForeground}" Margin="5" Grid.Row="1" HorizontalAlignment="Right" VerticalAlignment="Center"></TextBlock>
<TextBlock FontSize="16" Text="Character name:" Foreground="{StaticResource MahApps.Brushes.ThemeForeground}" Margin="5" Grid.Row="2" HorizontalAlignment="Right" VerticalAlignment="Center"></TextBlock>
<TextBox Text="{Binding ElementName=_this, Path=Username, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Foreground="{StaticResource MahApps.Brushes.ThemeForeground}" Background="Transparent"
BorderThickness="1" HorizontalAlignment="Stretch" Grid.Row="0" Grid.Column="1"
FontSize="16" TextChanged="UsernameTextbox_TextChanged" Margin="5"
@@ -40,10 +38,6 @@
BorderThickness="1" HorizontalAlignment="Stretch" Grid.Row="1" Grid.Column="1"
FontSize="16" PasswordChanged="Passwordbox_PasswordChanged" Margin="5"
ToolTip="Password" />
<TextBox Text="{Binding ElementName=_this, Path=CharacterName, Mode=TwoWay, UpdateSourceTrigger=PropertyChanged}" Foreground="{StaticResource MahApps.Brushes.ThemeForeground}" Background="Transparent"
BorderThickness="1" HorizontalAlignment="Stretch" Grid.Row="2" Grid.Column="1"
FontSize="16" TextChanged="CharacterNameTextbox_TextChanged" Margin="5"
ToolTip="Character name" />
</Grid>
<buttons:BinButton Grid.Column="2" Height="30" Width="30" Foreground="{StaticResource MahApps.Brushes.ThemeForeground}" VerticalAlignment="Center" Margin="5, 5, 5, 5"
Clicked="BinButton_Clicked"
@@ -21,8 +21,6 @@ public partial class AccountTemplate : UserControl
private string username = string.Empty;
[GenerateDependencyProperty]
private string password = string.Empty;
[GenerateDependencyProperty]
private string characterName = string.Empty;
public AccountTemplate()
{
@@ -36,7 +34,6 @@ public partial class AccountTemplate : UserControl
{
this.PasswordBox.Password = loginCredentials.Password;
this.Username = loginCredentials.Username;
this.CharacterName = loginCredentials.CharacterName;
}
}
@@ -50,11 +47,6 @@ public partial class AccountTemplate : UserControl
this.DataContext.As<LoginCredentials>()!.Username = this.Username;
}
private void CharacterNameTextbox_TextChanged(object sender, EventArgs e)
{
this.DataContext.As<LoginCredentials>()!.CharacterName = this.CharacterName;
}
private void Passwordbox_PasswordChanged(object sender, EventArgs e)
{
this.Password = sender.As<PasswordBox>()?.Password;
@@ -1,5 +1,6 @@
using Daybreak.Models.Builds;
using Daybreak.Services.BuildTemplates;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Core.Extensions;
using System.Extensions;
@@ -29,7 +30,14 @@ public partial class AttributeTemplate : UserControl
private int attributePoints;
public AttributeTemplate()
: this(Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IAttributePointCalculator>())
{
}
public AttributeTemplate(
IAttributePointCalculator attributePointCalculator)
{
this.attributePointCalculator = attributePointCalculator.ThrowIfNull();
this.InitializeComponent();
this.DataContextChanged += this.AttributeTemplate_DataContextChanged;
}
@@ -53,12 +61,6 @@ public partial class AttributeTemplate : UserControl
}
}
public void InitializeAttributeTemplate(
IAttributePointCalculator attributePointCalculator)
{
this.attributePointCalculator = attributePointCalculator.ThrowIfNull();
}
private void AttributeTemplate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if (e.NewValue is AttributeEntry attributeEntry)
@@ -9,8 +9,8 @@ namespace Daybreak.Controls;
/// </summary>
public partial class BuildEntryTemplate : UserControl
{
public event EventHandler<BuildEntry>? RemoveClicked;
public event EventHandler<BuildEntry>? EntryClicked;
public event EventHandler<IBuildEntry>? RemoveClicked;
public event EventHandler<IBuildEntry>? EntryClicked;
public BuildEntryTemplate()
{
@@ -19,7 +19,7 @@ public partial class BuildEntryTemplate : UserControl
private void BinButton_Clicked(object _, EventArgs __)
{
if (this.DataContext is BuildEntry buildEntry)
if (this.DataContext is IBuildEntry buildEntry)
{
this.RemoveClicked?.Invoke(this, buildEntry);
}
@@ -27,7 +27,7 @@ public partial class BuildEntryTemplate : UserControl
private void HighlightButton_Clicked(object _, EventArgs __)
{
if (this.DataContext is BuildEntry buildEntry)
if (this.DataContext is IBuildEntry buildEntry)
{
this.EntryClicked?.Invoke(this, buildEntry);
}
+33 -5
View File
@@ -16,6 +16,7 @@
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
<RowDefinition Height="*"></RowDefinition>
<RowDefinition Height="auto"></RowDefinition>
</Grid.RowDefinitions>
@@ -64,6 +65,34 @@
ToolTip="Open secondary profession info"/>
</Grid>
<Grid Grid.Row="3">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="auto" />
<ColumnDefinition Width="*" />
<ColumnDefinition Width="auto" />
</Grid.ColumnDefinitions>
<buttons:HelpButton Grid.Column="0"
ToolTip="Open attribute points info"
Clicked="HelpButtonAttributePoints_Clicked"
Width="20"
Height="20"
Margin="3, 5, 3, 5"/>
<TextBlock Grid.Column="1"
Text="Remaining Attribute Points:"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}"
FontSize="16"
VerticalAlignment="Center"/>
<TextBox Grid.Column="2"
Width="50"
Margin="3, 0, 0, 5"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}"
BorderBrush="{StaticResource MahApps.Brushes.ThemeForeground}"
BorderThickness="1"
Text="{Binding ElementName=_this, Path=AttributePoints, Mode=OneWay}"
FontSize="16"
Background="Transparent"
IsReadOnly="True"/>
</Grid>
<Grid Grid.Row="4">
<ListView Background="Transparent" ItemsSource="{Binding ElementName=_this, Path=BuildEntry.Attributes, Mode=OneWay}"
HorizontalContentAlignment="Stretch" BorderThickness="0">
<ListView.ItemContainerStyle>
@@ -75,13 +104,12 @@
<DataTemplate>
<templates:AttributeTemplate HelpClicked="AttributeTemplate_HelpClicked"
AttributeChanged="AttributeTemplate_AttributeChanged"
Loaded="AttributeTemplate_Loaded"
AttributePoints="{Binding Path=AttributePoints, Mode=OneWay, RelativeSource={RelativeSource AncestorType={x:Type UserControl}}}" />
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</Grid>
<Grid Grid.Row="4">
<Grid Grid.Row="5">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="*"></ColumnDefinition>
@@ -153,7 +181,7 @@
<TextBlock Grid.Column="7" Grid.Row="1" TextWrapping="Wrap" FontSize="16"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}" Text="{Binding ElementName=_this, Path=BuildEntry.EigthSkill.Name, Mode=OneWay}"></TextBlock>
</Grid>
<Grid Grid.Column="1" Grid.RowSpan="6"
<Grid Grid.Column="1" Grid.RowSpan="7"
x:Name="SideHolder">
<local:ChromiumBrowserWrapper x:Name="SkillBrowser"
MaxWidth="0"
@@ -170,7 +198,7 @@
PreventDispose="True"
MaximizeClicked="SkillBrowser_MaximizeClicked" />
</Grid>
<Grid Grid.Column="1" Grid.RowSpan="6">
<Grid Grid.Column="1" Grid.RowSpan="7">
<Grid x:Name="SkillListContainer">
<Grid.RowDefinitions>
<RowDefinition Height="auto"></RowDefinition>
@@ -231,7 +259,7 @@
</ItemsControl>
</Grid>
</Grid>
<Grid Grid.RowSpan="6"
<Grid Grid.RowSpan="7"
Grid.ColumnSpan="2"
Visibility="Hidden"
x:Name="FullScreenHolder">
@@ -1,5 +1,4 @@
using Daybreak.Controls.Buttons;
using Daybreak.Controls.Templates;
using Daybreak.Launch;
using Daybreak.Models;
using Daybreak.Models.Builds;
@@ -8,6 +7,7 @@ using Daybreak.Services.BuildTemplates;
using Daybreak.Services.Navigation;
using Daybreak.Utils;
using Daybreak.Views;
using MahApps.Metro.Controls;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Collections.Generic;
@@ -34,10 +34,11 @@ public partial class BuildTemplate : UserControl
{
private const string InfoNamePlaceholder = "[NAME]";
private const string BaseAddress = $"https://wiki.guildwars.com/wiki/{InfoNamePlaceholder}";
private const string AttributePointInfo = "Attribute_point";
private readonly IBuildTemplateManager buildTemplateManager;
private readonly IViewManager viewManager;
private readonly IAttributePointCalculator? attributePointCalculator;
private readonly IAttributePointCalculator attributePointCalculator;
private readonly CancellationTokenSource? cancellationTokenSource = new();
private bool browserMaximized = false;
@@ -51,7 +52,7 @@ public partial class BuildTemplate : UserControl
[GenerateDependencyProperty]
private string skillSearchText = string.Empty;
[GenerateDependencyProperty]
private BuildEntry buildEntry;
private SingleBuildEntry buildEntry;
[GenerateDependencyProperty]
private int attributePoints;
@@ -82,7 +83,7 @@ public partial class BuildTemplate : UserControl
this.InitializeComponent();
this.HideSkillListView();
this.HideInfoBrowser();
this.buildEntry = new BuildEntry();
this.buildEntry = new SingleBuildEntry();
this.DataContextChanged += this.BuildTemplate_DataContextChanged;
}
@@ -99,16 +100,11 @@ public partial class BuildTemplate : UserControl
private void BuildTemplate_DataContextChanged(object sender, DependencyPropertyChangedEventArgs e)
{
if(e.NewValue is BuildEntry buildEntry)
if(e.NewValue is SingleBuildEntry buildEntry)
{
if (this.BuildEntry is not null)
{
this.BuildEntry.PropertyChanged -= this.BuildEntry_Changed;
}
this.BuildEntry = buildEntry;
this.BuildEntry.PropertyChanged += this.BuildEntry_Changed;
this.AttributePoints = this.attributePointCalculator!.GetRemainingFreePoints(this.BuildEntry.Build!);
this.AttributePoints = this.attributePointCalculator!.GetRemainingFreePoints(this.BuildEntry);
this.SetupProfessions();
this.LoadSkills();
}
@@ -118,12 +114,13 @@ public partial class BuildTemplate : UserControl
{
this.SetupProfessions();
this.LoadSkills();
this.AttributePoints = this.attributePointCalculator.GetRemainingFreePoints(this.BuildEntry);
this.BuildChanged?.Invoke(this, propertyChangedEventArgs);
}
private void SetupProfessions()
{
if (this.BuildEntry is not BuildEntry buildEntry ||
if (this.BuildEntry is not SingleBuildEntry buildEntry ||
buildEntry.Primary is null ||
buildEntry.Secondary is null)
{
@@ -308,6 +305,15 @@ public partial class BuildTemplate : UserControl
}
}
private void HelpButtonAttributePoints_Clicked(object sender, EventArgs e)
{
this.BrowseToInfo(AttributePointInfo);
if (e is RoutedEventArgs routedEventArgs)
{
routedEventArgs.Handled = true;
}
}
private void AttributeTemplate_HelpClicked(object _, AttributeEntry e)
{
this.BrowseToInfo(e.Attribute?.Name!);
@@ -317,7 +323,7 @@ public partial class BuildTemplate : UserControl
{
e.ThrowIfNull();
this.BuildChanged?.Invoke(this, new EventArgs());
this.AttributePoints = this.attributePointCalculator!.GetRemainingFreePoints(this.BuildEntry.Build!);
this.AttributePoints = this.attributePointCalculator!.GetRemainingFreePoints(this.BuildEntry);
}
private void SkillTemplate_Clicked(object sender, RoutedEventArgs e)
@@ -348,42 +354,45 @@ public partial class BuildTemplate : UserControl
if (sender == this.SkillTemplate0)
{
this.BuildEntry.FirstSkill = Skill.NoSkill;
this.SkillTemplate0.DataContext = Skill.NoSkill;
}
else if (sender == this.SkillTemplate1)
{
this.BuildEntry.SecondSkill = Skill.NoSkill;
this.SkillTemplate1.DataContext = Skill.NoSkill;
}
else if (sender == this.SkillTemplate2)
{
this.BuildEntry.ThirdSkill = Skill.NoSkill;
this.SkillTemplate2.DataContext = Skill.NoSkill;
}
else if (sender == this.SkillTemplate3)
{
this.BuildEntry.FourthSkill = Skill.NoSkill;
this.SkillTemplate3.DataContext = Skill.NoSkill;
}
else if (sender == this.SkillTemplate4)
{
this.BuildEntry.FifthSkill = Skill.NoSkill;
this.SkillTemplate4.DataContext = Skill.NoSkill;
}
else if (sender == this.SkillTemplate5)
{
this.BuildEntry.SixthSkill = Skill.NoSkill;
this.SkillTemplate5.DataContext = Skill.NoSkill;
}
else if (sender == this.SkillTemplate6)
{
this.BuildEntry.SeventhSkill = Skill.NoSkill;
this.SkillTemplate6.DataContext = Skill.NoSkill;
}
else if (sender == this.SkillTemplate7)
{
this.BuildEntry.EigthSkill = Skill.NoSkill;
this.SkillTemplate7.DataContext = Skill.NoSkill;
}
}
private void AttributeTemplate_Loaded(object sender, RoutedEventArgs e)
{
sender.As<AttributeTemplate>()?.InitializeAttributeTemplate(this.attributePointCalculator!);
}
private void HighlightButton_Clicked(object sender, EventArgs e)
{
if (this.selectingSkillTemplate is null)
@@ -396,34 +405,42 @@ public partial class BuildTemplate : UserControl
if (this.selectingSkillTemplate == this.SkillTemplate0)
{
this.BuildEntry.FirstSkill = selectedSkilll;
this.SkillTemplate0.DataContext = selectedSkilll;
}
else if (this.selectingSkillTemplate == this.SkillTemplate1)
{
this.BuildEntry.SecondSkill = selectedSkilll;
this.SkillTemplate1.DataContext = selectedSkilll;
}
else if (this.selectingSkillTemplate == this.SkillTemplate2)
{
this.BuildEntry.ThirdSkill = selectedSkilll;
this.SkillTemplate2.DataContext = selectedSkilll;
}
else if (this.selectingSkillTemplate == this.SkillTemplate3)
{
this.BuildEntry.FourthSkill = selectedSkilll;
this.SkillTemplate3.DataContext = selectedSkilll;
}
else if (this.selectingSkillTemplate == this.SkillTemplate4)
{
this.BuildEntry.FifthSkill = selectedSkilll;
this.SkillTemplate4.DataContext = selectedSkilll;
}
else if (this.selectingSkillTemplate == this.SkillTemplate5)
{
this.BuildEntry.SixthSkill = selectedSkilll;
this.SkillTemplate5.DataContext = selectedSkilll;
}
else if (this.selectingSkillTemplate == this.SkillTemplate6)
{
this.BuildEntry.SeventhSkill = selectedSkilll;
this.SkillTemplate6.DataContext = selectedSkilll;
}
else if (this.selectingSkillTemplate == this.SkillTemplate7)
{
this.BuildEntry.EigthSkill = selectedSkilll;
this.SkillTemplate7.DataContext = selectedSkilll;
}
this.HideSkillListView();
@@ -460,9 +477,7 @@ public partial class BuildTemplate : UserControl
return;
}
var buildEntry = this.buildTemplateManager.CreateBuild();
buildEntry.Build = e.Build;
buildEntry.Name = e.PreferredName ?? buildEntry.Name;
this.viewManager.ShowView<BuildTemplateView>(buildEntry);
e.Build!.Name = e.PreferredName ?? Guid.NewGuid().ToString();
this.viewManager.ShowView<SingleBuildTemplateView>(e.Build);
}
}
@@ -8,6 +8,7 @@
xmlns:configs="clr-namespace:Daybreak.Models.LaunchConfigurations"
xmlns:converters="clr-namespace:Daybreak.Converters"
mc:Ignorable="d"
DataContextChanged="UserControl_DataContextChanged"
Loaded="UserControl_Loaded"
Unloaded="UserControl_Unloaded"
x:Name="_this"
@@ -18,47 +19,56 @@
</UserControl.Resources>
<Grid>
<StackPanel
Visibility="{Binding ElementName=_this, Path=CanShowFocusView, Mode=OneWay, Converter={StaticResource InverseBooleanToVisibilityConverter}}">
Visibility="{Binding ElementName=_this, Path=CanLaunch, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
<WrapPanel VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextBlock Text="Launch "
FontSize="22"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}"
Visibility="{Binding ElementName=_this, Path=GameRunning, Mode=OneWay, Converter={StaticResource InverseBooleanToVisibilityConverter}}"/>
<TextBlock Text="{Binding Credentials.Username}"
<TextBlock Text="{Binding Configuration.Credentials.Username}"
FontSize="22"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}"/>
<TextBlock Text=" running"
FontSize="22"
Foreground="{StaticResource MahApps.Brushes.Accent}"
Visibility="{Binding ElementName=_this, Path=GameRunning, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</WrapPanel>
<TextBlock Text="{Binding Credentials.CharacterName}"
FontSize="16"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}"/>
<TextBlock Text="{Binding ExecutablePath}"
<TextBlock Text="{Binding Configuration.ExecutablePath}"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}"
FontSize="12"
TextWrapping="Wrap"/>
TextWrapping="Wrap"
HorizontalAlignment="Center"/>
</StackPanel>
<StackPanel
Visibility="{Binding ElementName=_this, Path=CanShowFocusView, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
Visibility="{Binding ElementName=_this, Path=CanAttach, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
<WrapPanel VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextBlock Text="Attach "
FontSize="22"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}" />
<TextBlock Text="{Binding Credentials.Username}"
<TextBlock Text="{Binding Configuration.Credentials.Username}"
FontSize="22"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}"/>
</WrapPanel>
<TextBlock Text="{Binding Credentials.CharacterName}"
FontSize="16"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}"/>
<TextBlock Text="{Binding ExecutablePath}"
<TextBlock Text="{Binding Configuration.ExecutablePath}"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}"
FontSize="12"
TextWrapping="Wrap"
HorizontalAlignment="Center"/>
</StackPanel>
<StackPanel
Visibility="{Binding ElementName=_this, Path=CanKill, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
<WrapPanel VerticalAlignment="Center"
HorizontalAlignment="Center">
<TextBlock Text="Kill "
FontSize="22"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}" />
<TextBlock Text="{Binding Configuration.Credentials.Username}"
FontSize="22"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}"/>
</WrapPanel>
<TextBlock Text="{Binding Configuration.ExecutablePath}"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}"
FontSize="12"
TextWrapping="Wrap"/>
TextWrapping="Wrap"
HorizontalAlignment="Center"/>
</StackPanel>
</Grid>
</UserControl>
@@ -1,7 +1,11 @@
using Daybreak.Configuration.Options;
using Daybreak.Controls.Buttons;
using Daybreak.Models;
using Daybreak.Models.LaunchConfigurations;
using Daybreak.Services.ApplicationLauncher;
using Daybreak.Services.LaunchConfigurations;
using Daybreak.Services.Scanner;
using Daybreak.Utils;
using Microsoft.Extensions.DependencyInjection;
using Slim.Attributes;
using System;
@@ -9,6 +13,7 @@ using System.Configuration;
using System.Core.Extensions;
using System.Threading;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Extensions;
@@ -20,12 +25,19 @@ public partial class LaunchButtonTemplate : UserControl
{
private static readonly TimeSpan CheckGameDelay = TimeSpan.FromSeconds(1);
private readonly IGuildwarsMemoryReader guildwarsMemoryReader;
private readonly ILaunchConfigurationService launchConfigurationService;
private readonly IApplicationLauncher applicationLauncher;
private readonly ILiveOptions<FocusViewOptions> liveOptions;
[GenerateDependencyProperty(InitialValue = true)]
private bool canLaunch;
[GenerateDependencyProperty]
private bool canShowFocusView;
private bool canKill;
[GenerateDependencyProperty]
private bool canAttach;
[GenerateDependencyProperty]
private bool gameRunning;
@@ -33,10 +45,12 @@ public partial class LaunchButtonTemplate : UserControl
private CancellationTokenSource? tokenSource;
public LaunchButtonTemplate(
IGuildwarsMemoryReader guildwarsMemoryReader,
ILaunchConfigurationService launchConfigurationService,
IApplicationLauncher applicationLauncher,
ILiveOptions<FocusViewOptions> liveOptions)
{
this.guildwarsMemoryReader = guildwarsMemoryReader.ThrowIfNull();
this.launchConfigurationService = launchConfigurationService.ThrowIfNull();
this.applicationLauncher = applicationLauncher.ThrowIfNull();
this.liveOptions = liveOptions.ThrowIfNull();
@@ -46,47 +60,97 @@ public partial class LaunchButtonTemplate : UserControl
[DoNotInject]
public LaunchButtonTemplate()
:this(
Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IGuildwarsMemoryReader>(),
Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<ILaunchConfigurationService>(),
Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IApplicationLauncher>(),
Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<ILiveOptions<FocusViewOptions>>())
{
}
private void UserControl_Loaded(object sender, System.Windows.RoutedEventArgs e)
private void UserControl_Loaded(object sender, RoutedEventArgs e)
{
this.tokenSource?.Dispose();
this.tokenSource = new CancellationTokenSource();
this.CheckGameState(this.tokenSource.Token);
this.PeriodicallyCheckGameState(this.tokenSource.Token);
}
private void UserControl_Unloaded(object sender, System.Windows.RoutedEventArgs e)
private void UserControl_Unloaded(object sender, RoutedEventArgs e)
{
this.tokenSource?.Dispose();
this.tokenSource = default;
}
private async void CheckGameState(CancellationToken cancellationToken)
private async void UserControl_DataContextChanged(object _, DependencyPropertyChangedEventArgs e)
{
await this.CheckGameState(this.tokenSource?.Token ?? CancellationToken.None);
}
private async void PeriodicallyCheckGameState(CancellationToken cancellationToken)
{
while (!cancellationToken.IsCancellationRequested)
{
if (this.DataContext is not LaunchConfigurationWithCredentials config)
// The following if case is needed due to a fault in the ContentPresenter not passing the DataContext to the content it presents
if (this.FindParent<HighlightButton>()?.DataContext is LauncherViewContext parentContext &&
this.DataContext != parentContext)
{
await Task.Delay(CheckGameDelay, cancellationToken);
continue;
}
if (this.applicationLauncher.GetGuildwarsProcess(config) is null)
{
this.GameRunning = false;
}
else
{
this.GameRunning = true;
this.DataContext = parentContext;
}
this.CanShowFocusView = this.liveOptions.Value.Enabled && this.GameRunning;
await this.CheckGameState(cancellationToken);
await Task.Delay(CheckGameDelay, cancellationToken);
}
}
private async Task CheckGameState(CancellationToken cancellationToken)
{
if (this.DataContext is not LauncherViewContext launcherViewContext ||
launcherViewContext.Configuration is null)
{
this.CanLaunch = false;
this.CanAttach = false;
this.CanKill = false;
return;
}
if (this.applicationLauncher.GetGuildwarsProcess(launcherViewContext.Configuration) is not GuildWarsApplicationLaunchContext context)
{
this.GameRunning = false;
this.CanLaunch = true;
this.CanAttach = this.liveOptions.Value.Enabled && this.GameRunning;
this.CanKill = false;
launcherViewContext.CanLaunch = true;
launcherViewContext.CanKill = false;
return;
}
if (!await this.guildwarsMemoryReader.IsInitialized(context.ProcessId, cancellationToken))
{
this.GameRunning = false;
this.CanLaunch = false;
this.CanAttach = this.liveOptions.Value.Enabled && this.GameRunning;
this.CanKill = !this.CanAttach;
launcherViewContext.CanLaunch = false;
launcherViewContext.CanKill = true;
return;
}
var loginInfo = await this.guildwarsMemoryReader.ReadLoginData(cancellationToken);
if (loginInfo?.Email != context.LaunchConfiguration.Credentials?.Username)
{
this.GameRunning = false;
this.CanAttach = false;
this.CanLaunch = true;
this.CanKill = false;
launcherViewContext.CanLaunch = false;
launcherViewContext.CanKill = false;
return;
}
this.GameRunning = true;
this.CanAttach = this.liveOptions.Value.Enabled && this.GameRunning;
launcherViewContext.CanLaunch = this.CanAttach;
launcherViewContext.CanKill = false;
this.CanLaunch = !this.CanAttach;
this.CanKill = false;
}
}
@@ -16,25 +16,27 @@
<Grid>
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"></ColumnDefinition>
<ColumnDefinition Width="80"></ColumnDefinition>
<ColumnDefinition Width="auto"></ColumnDefinition>
</Grid.ColumnDefinitions>
<Image Source="{Binding ElementName=_this, Path=ImageSource, Mode=OneWay}"
Height="40"
Width="40"
HorizontalAlignment="Right"
Grid.Column="1"
HorizontalAlignment="Left"
Grid.Column="0"
Visibility="{Binding ElementName=_this, Path=ImageVisible, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
</Image>
<TextBlock
Text="{Binding ElementName=_this, Path=DataContext.Item.Name, Mode=OneWay}"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}"
VerticalAlignment="Center"
HorizontalAlignment="Right"
HorizontalAlignment="Left"
Margin="50, 0, 0, 0"
FontSize="16"
Grid.Column="1"
Visibility="{Binding ElementName=_this, Path=ImageVisible, Mode=OneWay, Converter={StaticResource InverseBooleanToVisibilityConverter}}"
Grid.Column="0"
TextWrapping="Wrap"/>
<WrapPanel VerticalAlignment="Center">
<WrapPanel VerticalAlignment="Center"
Grid.Column="1"
Margin="0, 0, 5, 0">
<TextBlock
Text="{Binding ElementName=_this, Path=DataContext.Price, Mode=OneWay}"
Foreground="{StaticResource MahApps.Brushes.ThemeForeground}"
@@ -0,0 +1,25 @@
using System;
using System.Globalization;
using System.Windows;
using System.Windows.Data;
namespace Daybreak.Converters;
public sealed class EqualityToVisibilityConverter : IMultiValueConverter
{
public object Convert(object[] values, Type targetType, object parameter, CultureInfo culture)
{
if (values.Length != 2)
{
return Visibility.Collapsed;
}
return values[0] == values[1] ? Visibility.Visible : Visibility.Collapsed;
}
public object[] ConvertBack(object value, Type[] targetTypes, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
@@ -0,0 +1,71 @@
using Daybreak.Models.Guildwars;
using Daybreak.Services.Events;
using ExCSS;
using Microsoft.Extensions.DependencyInjection;
using System;
using System.Globalization;
using System.Linq;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Data;
using System.Windows.Media;
using System.Windows.Shapes;
namespace Daybreak.Converters;
public sealed class EventCalendarDayToBrushConverter : IValueConverter
{
private readonly EventToBrushConverter eventToBrushConverter = new();
private readonly IEventService eventService = Launch.Launcher.Instance.ApplicationServiceProvider.GetRequiredService<IEventService>();
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is not DateTime dayDate)
{
throw new InvalidOperationException($"Unable to convert from {value.GetType().Name}");
}
dayDate = new DateTime(dayDate.Year, dayDate.Month, dayDate.Day, 19, 0, 0);
var activeEvents = this.eventService.GetActiveEvents(dayDate).ToList();
if (activeEvents.Count == 0)
{
return Brushes.Transparent;
}
if (activeEvents.Count == 1 &&
activeEvents.FirstOrDefault() is Event ev)
{
return this.eventToBrushConverter.Convert(ev, typeof(Brush), parameter, culture);
}
var brushes = this.eventService.GetActiveEvents(dayDate).Select(e => this.eventToBrushConverter.Convert(e, typeof(Brush), parameter, culture)).OfType<SolidColorBrush>().ToList();
var canvas = new Canvas();
double width = 1.0 / brushes.Count; // Assuming a normalized width for each color segment
for (int i = 0; i < brushes.Count; i++)
{
var rect = new Rectangle
{
Width = width,
Height = 1,
Fill = brushes[i],
RenderTransform = new TranslateTransform(i * width, 0)
};
canvas.Children.Add(rect);
}
return new VisualBrush
{
Visual = canvas,
Stretch = Stretch.None,
TileMode = TileMode.None,
Viewbox = new Rect(0, 0, 1, 1),
ViewboxUnits = BrushMappingMode.RelativeToBoundingBox,
Transform = new RotateTransform(45, 0.5, 0.5)
};
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
@@ -0,0 +1,45 @@
using Daybreak.Models;
using Daybreak.Models.Guildwars;
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Windows.Data;
using System.Windows.Media;
namespace Daybreak.Converters;
internal sealed class EventToBrushConverter : IValueConverter
{
private static readonly Dictionary<Event, Brush> EventMapping = new()
{
{ Event.CanthanNewYear, new SolidColorBrush(ColorPalette.Amber) },
{ Event.LuckyTreatsWeek, new SolidColorBrush(ColorPalette.Green) },
{ Event.AprilFoolsDay, new SolidColorBrush(ColorPalette.DeepPurple) },
{ Event.SweetTreatsWeek, new SolidColorBrush(ColorPalette.Indigo) },
{ Event.AnniversaryCelebration, new SolidColorBrush(ColorPalette.Yellow) },
{ Event.DragonFestival, new SolidColorBrush(ColorPalette.DeepOrange) },
{ Event.WintersdayInJuly, new SolidColorBrush(ColorPalette.LightBlue) },
{ Event.WayfarersReverie, new SolidColorBrush(ColorPalette.Magenta) },
{ Event.PirateWeek, new SolidColorBrush(ColorPalette.Gold) },
{ Event.BreastCancerAwarenessMonth, new SolidColorBrush(ColorPalette.Pink) },
{ Event.Halloween, new SolidColorBrush(ColorPalette.Orange) },
{ Event.SpecialTreatsWeek, new SolidColorBrush(ColorPalette.Lime) },
{ Event.WintersdayJanuary, new SolidColorBrush(ColorPalette.BlueGrey) },
{ Event.WintersdayDecember, new SolidColorBrush(ColorPalette.BlueGrey) }
};
public object Convert(object value, Type targetType, object parameter, CultureInfo culture)
{
if (value is not Event e ||
targetType != typeof(Brush))
{
throw new InvalidOperationException($"Unable to convert from {value.GetType().Name} to {targetType.Name}");
}
return EventMapping[e];
}
public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture)
{
throw new NotImplementedException();
}
}
+10 -8
View File
@@ -11,7 +11,7 @@
<LangVersion>preview</LangVersion>
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
<Version>0.9.9.2</Version>
<Version>0.9.9.26</Version>
<EnableWindowsTargeting>true</EnableWindowsTargeting>
<UserSecretsId>cfb2a489-db80-448d-a969-80270f314c46</UserSecretsId>
<AllowUnsafeBlocks>True</AllowUnsafeBlocks>
@@ -66,7 +66,7 @@
<ItemGroup>
<None Include="..\.editorconfig" Link=".editorconfig" />
<None Include="..\build\Daybreak.GWCA\$(Configuration)\Daybreak.GWCA.dll" Link="GWCA\Daybreak.GWCA.dll">
<None Include="..\Daybreak.GWCA\bin\$(Configuration)\Daybreak.GWCA.dll" Link="GWCA\Daybreak.GWCA.dll">
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
</None>
</ItemGroup>
@@ -89,16 +89,16 @@
<PackageReference Include="AvalonEdit" Version="6.3.0.90" />
<PackageReference Include="DiffPlex" Version="1.7.2" />
<PackageReference Include="DotNetZip" Version="1.16.0" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.55" />
<PackageReference Include="HtmlAgilityPack" Version="1.11.60" />
<PackageReference Include="ini-parser-netstandard" Version="2.5.2" />
<PackageReference Include="LiteDB" Version="5.0.17" />
<PackageReference Include="LiteDB" Version="5.0.19" />
<PackageReference Include="LiveChartsCore.SkiaSharpView.WPF" Version="2.0.0-rc2" />
<PackageReference Include="MahApps.Metro" Version="2.4.10" />
<PackageReference Include="Microsoft.CorrelationVector" Version="1.0.42" />
<PackageReference Include="Microsoft.Extensions.Configuration.UserSecrets" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Http" Version="8.0.0" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.0" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2210.55" />
<PackageReference Include="Microsoft.Extensions.Logging.Abstractions" Version="8.0.1" />
<PackageReference Include="Microsoft.Web.WebView2" Version="1.0.2420.47" />
<PackageReference Include="Microsoft.Xaml.Behaviors.Wpf" Version="1.1.77" />
<PackageReference Include="NAudio" Version="2.2.1" />
<PackageReference Include="Newtonsoft.Json" Version="13.0.3" />
@@ -106,15 +106,17 @@
<PackageReference Include="securifybv.ShellLink" Version="0.1.0" />
<PackageReference Include="SharpNav" Version="0.9.2" />
<PackageReference Include="Slim" Version="1.9.2" />
<PackageReference Include="Svg" Version="3.4.6" />
<PackageReference Include="Svg" Version="3.4.7" />
<PackageReference Include="System.Linq.Async" Version="6.0.1" />
<PackageReference Include="System.Net.Http" Version="4.3.4" />
<PackageReference Include="System.Reflection.Metadata" Version="8.0.0" />
<PackageReference Include="System.Text.Encoding.CodePages" Version="8.0.0" />
<PackageReference Include="System.Text.RegularExpressions" Version="4.3.1" />
<PackageReference Include="SystemExtensions.NetCore" Version="1.0.1" />
<PackageReference Include="SystemExtensions.NetStandard" Version="1.6.2" />
<PackageReference Include="SystemExtensions.NetStandard.DependencyInjection" Version="1.3.1" />
<PackageReference Include="WpfExtended" Version="0.7.7" />
<PackageReference Include="WpfExtended.SourceGeneration" Version="0.1.2" />
<PackageReference Include="WpfExtended.SourceGeneration" Version="0.3.0" />
<PackageReference Include="WpfScreenHelper" Version="2.1.0" />
<PackageReference Include="WriteableBitmapEx" Version="1.6.8" />
</ItemGroup>
+7 -1
View File
@@ -1,5 +1,6 @@
using Daybreak.Configuration;
using Daybreak.Models.Progress;
using Daybreak.Services.Browser;
using Daybreak.Services.Drawing;
using Daybreak.Services.ExceptionHandling;
using Daybreak.Services.Mods;
@@ -95,6 +96,7 @@ public sealed class Launcher : ExtendedApplication<MainWindow>
var drawingModuleProducer = this.ServiceProvider.GetRequiredService<IDrawingModuleProducer>();
var notificationHandlerProducer = this.ServiceProvider.GetRequiredService<INotificationHandlerProducer>();
var modsManager = this.ServiceProvider.GetRequiredService<IModsManager>();
var browserExtensionsProducer = this.ServiceProvider.GetRequiredService<IBrowserExtensionsProducer>();
startupStatus.CurrentStep = StartupStatus.Custom("Loading views");
this.projectConfiguration.RegisterViews(viewProducer);
@@ -108,8 +110,11 @@ public sealed class Launcher : ExtendedApplication<MainWindow>
this.projectConfiguration.RegisterNotificationHandlers(notificationHandlerProducer);
startupStatus.CurrentStep = StartupStatus.Custom("Loading mods");
this.projectConfiguration.RegisterMods(modsManager);
startupStatus.CurrentStep = StartupStatus.Custom("Loading browser extensions");
this.projectConfiguration.RegisterBrowserExtensions(browserExtensionsProducer);
this.logger = this.ServiceProvider.GetRequiredService<ILogger<Launcher>>();
this.logger.LogInformation($"Running in {Environment.CurrentDirectory}");
this.exceptionHandler = this.ServiceProvider.GetRequiredService<IExceptionHandler>();
try
{
@@ -123,7 +128,8 @@ public sealed class Launcher : ExtendedApplication<MainWindow>
startupActionProducer,
drawingModuleProducer,
notificationHandlerProducer,
modsManager);
modsManager,
browserExtensionsProducer);
}
catch(Exception e)
{
+1 -1
View File
@@ -180,7 +180,7 @@
HorizontalAlignment="Right"
Grid.Column="1"
Margin="0, 0, 140, 0"
Cursor="Arrow"
Cursor="Hand"
Opacity="0.8"
HighlightColor="White"
ToolTip="Synchronize Settings"
+7 -1
View File
@@ -112,9 +112,9 @@ public partial class MainWindow : MetroWindow
private void Window_Loaded(object sender, RoutedEventArgs e)
{
this.splashScreenService.HideSplashScreen();
this.SetupImageCycle();
this.viewManager.ShowView<LauncherView>();
this.splashScreenService.HideSplashScreen();
}
private void SynchronizeButton_Click(object sender, EventArgs e)
@@ -147,6 +147,12 @@ public partial class MainWindow : MetroWindow
private async void UpdateRandomImage()
{
if (!this.launcherOptions.Value.DynamicBackgrounds)
{
// Dynamic backgrounds are disabled
return;
}
var response = await this.backgroundProvider.GetBackground();
if (response.ImageSource is null)
{
+70
View File
@@ -0,0 +1,70 @@
<mah:MetroWindow x:Class="Daybreak.Launch.MinimapWindow"
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:local="clr-namespace:Daybreak.Launch"
xmlns:mah="http://metro.mahapps.com/winfx/xaml/controls"
xmlns:buttons="clr-namespace:Daybreak.Controls.Buttons"
xmlns:glyphs="clr-namespace:Daybreak.Controls.Glyphs"
xmlns:launch="clr-namespace:Daybreak.Launch"
mc:Ignorable="d"
WindowStyle="None"
ResizeMode="CanResize"
ShowTitleBar="False"
ShowMaxRestoreButton="True"
ShowIconOnTitleBar="False"
ShowMinButton="True"
ShowSystemMenu="False"
ShowSystemMenuOnRightClick="False"
ShowInTaskbar="True"
ShowCloseButton="True"
Background="Transparent"
AllowsTransparency="True"
Title="Daybreak Minimap"
Topmost="{Binding ElementName=_this, Path=Pinned, Mode=OneWay}"
x:Name="_this"
Height="450" Width="800">
<Grid Background="{StaticResource Daybreak.Brushes.Background}">
<Grid.RowDefinitions>
<RowDefinition Height="30" />
<RowDefinition Height="*" />
</Grid.RowDefinitions>
<Rectangle Fill="{StaticResource MahApps.Brushes.ThemeBackground}"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Grid.RowSpan="2"
Visibility="{Binding ElementName=_this, Path=Opaque, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}"/>
<ContentPresenter Grid.RowSpan="2"
Content="{Binding ElementName=_this, Path=Content, Mode=OneWay}"/>
<Rectangle Height="30"
VerticalAlignment="Stretch"
HorizontalAlignment="Stretch"
Grid.Row="0"
Fill="Transparent"
MouseLeftButtonDown="Grid_MouseLeftButtonDown" />
<buttons:HighlightButton Height="30"
Width="50"
Margin="0, 0, 140, 0"
HighlightColor="LightGray"
HorizontalAlignment="Right"
Clicked="HighlightButton_Clicked"
Cursor="Arrow"
ToolTip="Pin Topmost">
<buttons:HighlightButton.ButtonContent>
<Grid>
<glyphs:PinGlyph Foreground="{StaticResource MahApps.Brushes.ThemeForeground}"
Height="15"/>
<Line X1="21"
X2="31"
Y1="10"
Y2="20"
StrokeThickness="1"
Stroke="{StaticResource MahApps.Brushes.ThemeForeground}"
Visibility="{Binding Path=Pinned, RelativeSource={RelativeSource AncestorType={x:Type launch:MinimapWindow}}, Converter={StaticResource BooleanToVisibilityConverter}}"/>
</Grid>
</buttons:HighlightButton.ButtonContent>
</buttons:HighlightButton>
</Grid>
</mah:MetroWindow>
+36
View File
@@ -0,0 +1,36 @@
using MahApps.Metro.Controls;
using System.Windows;
using System.Windows.Extensions;
namespace Daybreak.Launch;
/// <summary>
/// Interaction logic for MinimapWindow.xaml
/// </summary>
public partial class MinimapWindow : MetroWindow
{
[GenerateDependencyProperty]
private bool opaque = false;
[GenerateDependencyProperty]
private bool pinned = false;
[GenerateDependencyProperty]
private FrameworkElement content = default!;
public MinimapWindow()
{
this.InitializeComponent();
}
private void Grid_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
{
if (e.ChangedButton is System.Windows.Input.MouseButton.Left)
{
e.Handled = true;
this.DragMove();
}
}
private void HighlightButton_Clicked(object sender, System.EventArgs e)
{
this.Pinned = !this.Pinned;
}
}
@@ -5,4 +5,5 @@ public readonly struct ApplicationLauncherContext
{
public string ExecutablePath { get; init; }
public Process Process { get; init; }
public uint ProcessId { get; init; }
}
@@ -0,0 +1,8 @@
using System.Collections.Generic;
namespace Daybreak.Models.Browser;
public sealed class BrowserHistory
{
public List<string> History { get; set; } = [];
public int CurrentPosition { get; set; } = -1;
}
+3 -1
View File
@@ -9,7 +9,9 @@ public class BrowserPayload
public enum PayloadKeys
{
None,
ContextMenu
ContextMenu,
XButton1Pressed,
XButton2Pressed
}
[JsonProperty(nameof(Key))]
+1 -1
View File
@@ -4,6 +4,6 @@ namespace Daybreak.Models;
public sealed class BuildWithTemplateCode
{
public BuildEntry? Build { get; set; }
public IBuildEntry? Build { get; set; }
public string? TemplateCode { get; set; }
}
+3 -2
View File
@@ -8,6 +8,8 @@ public sealed class AttributeEntry : INotifyPropertyChanged
private Attribute? attribute;
private int points;
public event PropertyChangedEventHandler? PropertyChanged;
public Attribute? Attribute
{
get => this.attribute;
@@ -26,6 +28,5 @@ public sealed class AttributeEntry : INotifyPropertyChanged
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Points)));
}
}
public event PropertyChangedEventHandler? PropertyChanged;
public override string ToString() => $"{this.Attribute?.Name} - {this.Points}";
}
+7
View File
@@ -0,0 +1,7 @@
namespace Daybreak.Models.Builds;
public interface IBuildEntry
{
public string? SourceUrl { get; set; }
public string? PreviousName { get; set; }
public string? Name { get; set; }
}
@@ -1,15 +1,20 @@
using Daybreak.Models.Guildwars;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using Attribute = Daybreak.Models.Guildwars.Attribute;
namespace Daybreak.Models.Builds;
public sealed class BuildEntry : INotifyPropertyChanged
public sealed class SingleBuildEntry : IBuildEntry, INotifyPropertyChanged, IEquatable<SingleBuildEntry>
{
public event PropertyChangedEventHandler? PropertyChanged;
private string? name;
private Build? build;
private string? sourceUrl;
private Profession primary = Profession.None;
private Profession secondary = Profession.None;
private List<AttributeEntry> attributes = [];
private List<Skill> skills = [Skill.NoSkill, Skill.NoSkill, Skill.NoSkill, Skill.NoSkill, Skill.NoSkill, Skill.NoSkill, Skill.NoSkill, Skill.NoSkill];
public string? PreviousName { get; set; }
public string? Name
@@ -21,114 +26,123 @@ public sealed class BuildEntry : INotifyPropertyChanged
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Name)));
}
}
public Build? Build
public string? SourceUrl
{
get => this.build;
get => this.sourceUrl;
set
{
this.build = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Build)));
this.sourceUrl = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.SourceUrl)));
}
}
public Profession Primary
{
get => this.Build!.Primary;
get => this.primary;
set
{
this.Build!.Primary = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Primary)));
this.primary = value;
this.UpdateAttributes();
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Primary)));
}
}
public Profession Secondary
{
get => this.Build!.Secondary;
get => this.secondary;
set
{
this.Build!.Secondary = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Secondary)));
this.secondary = value;
this.UpdateAttributes();
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Secondary)));
}
}
public List<AttributeEntry> Attributes
{
get => this.Build!.Attributes;
get => this.attributes;
set
{
this.Build!.Attributes = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Attributes)));
this.attributes = value;
this.UpdateSkills();
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Attributes)));
}
}
public List<Skill> Skills
{
get => this.skills;
set
{
this.skills = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Skills)));
}
}
public Skill FirstSkill
{
get => this.Build!.Skills[0];
get => this.Skills[0];
set
{
this.Build!.Skills[0] = value;
this.Skills[0] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.FirstSkill)));
}
}
public Skill SecondSkill
{
get => this.Build!.Skills[1];
get => this.Skills[1];
set
{
this.Build!.Skills[1] = value;
this.Skills[1] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.SecondSkill)));
}
}
public Skill ThirdSkill
{
get => this.Build!.Skills[2];
get => this.Skills[2];
set
{
this.Build!.Skills[2] = value;
this.Skills[2] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.ThirdSkill)));
}
}
public Skill FourthSkill
{
get => this.Build!.Skills[3];
get => this.Skills[3];
set
{
this.Build!.Skills[3] = value;
this.Skills[3] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.FourthSkill)));
}
}
public Skill FifthSkill
{
get => this.Build!.Skills[4];
get => this.Skills[4];
set
{
this.Build!.Skills[4] = value;
this.Skills[4] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.FifthSkill)));
}
}
public Skill SixthSkill
{
get => this.Build!.Skills[5];
get => this.Skills[5];
set
{
this.Build!.Skills[5] = value;
this.Skills[5] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.SixthSkill)));
}
}
public Skill SeventhSkill
{
get => this.Build!.Skills[6];
get => this.Skills[6];
set
{
this.Build!.Skills[6] = value;
this.Skills[6] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.SeventhSkill)));
}
}
public Skill EigthSkill
{
get => this.Build!.Skills[7];
get => this.Skills[7];
set
{
this.Build!.Skills[7] = value;
this.Skills[7] = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.EigthSkill)));
}
}
@@ -212,4 +226,14 @@ public sealed class BuildEntry : INotifyPropertyChanged
this.EigthSkill = Skill.NoSkill;
}
}
public bool Equals(SingleBuildEntry? other)
{
return this.Name == other?.Name &&
this.PreviousName == other?.PreviousName &&
this.SourceUrl == other?.SourceUrl &&
this.Primary == other?.Primary &&
this.Secondary == other?.Secondary &&
this.Skills.SequenceEqual(other.Skills);
}
}
+57
View File
@@ -0,0 +1,57 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Extensions;
using System.Linq;
namespace Daybreak.Models.Builds;
public sealed class TeamBuildEntry : IBuildEntry, INotifyPropertyChanged, IEquatable<TeamBuildEntry>
{
public event PropertyChangedEventHandler? PropertyChanged;
private string? name;
private string? sourceUrl;
private List<SingleBuildEntry> builds = [];
public string? PreviousName { get; set; }
public string? Name
{
get => this.name;
set
{
this.name = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Name)));
}
}
public string? SourceUrl
{
get => this.sourceUrl;
set
{
this.sourceUrl = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.SourceUrl)));
}
}
public List<SingleBuildEntry> Builds
{
get => this.builds;
set
{
this.builds = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Builds)));
}
}
public bool Equals(TeamBuildEntry? other)
{
return this.Name == other?.Name &&
this.PreviousName == other?.PreviousName &&
this.SourceUrl == other?.SourceUrl &&
this.Builds.Select((thisBuild, index) =>
{
var otherBuild = other?.Builds.Skip(index).FirstOrDefault();
return thisBuild.Primary == otherBuild?.Primary &&
thisBuild.Secondary == otherBuild?.Secondary &&
thisBuild.Skills.SequenceEqual(otherBuild.Skills);
}).All(result => result);
}
}
+2 -2
View File
@@ -1,8 +1,8 @@
using Daybreak.Models.Guildwars;
using Daybreak.Models.Builds;
namespace Daybreak.Models;
public sealed class DownloadedBuild
{
public string? PreferredName { get; set; }
public Build? Build { get; set; }
public IBuildEntry? Build { get; set; }
}
+16
View File
@@ -0,0 +1,16 @@
using Daybreak.Models.Guildwars;
using System.ComponentModel;
using System.Windows.Extensions;
namespace Daybreak.Models;
public partial class EventViewModel : INotifyPropertyChanged
{
public event PropertyChangedEventHandler? PropertyChanged;
[GenerateNotifyPropertyChanged]
private Event? seasonalEvent;
[GenerateNotifyPropertyChanged]
private bool active;
[GenerateNotifyPropertyChanged]
private bool upcoming;
}
@@ -0,0 +1,64 @@
using Daybreak.Models.Guildwars;
using System.ComponentModel;
namespace Daybreak.Models.FocusView;
public sealed class CartoProgressContext : INotifyPropertyChanged
{
private TitleInformationExtended? titleInformationExtended;
private double percentage;
private Continent? continent;
private string? resolvedName;
private string? titleName;
public event PropertyChangedEventHandler? PropertyChanged;
public TitleInformationExtended? TitleInformationExtended
{
get => this.titleInformationExtended;
set
{
this.titleInformationExtended = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.TitleInformationExtended)));
}
}
public double Percentage
{
get => this.percentage;
set
{
this.percentage = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Percentage)));
}
}
public Continent? Continent
{
get => this.continent;
set
{
this.continent = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.Continent)));
}
}
public string? ResolvedName
{
get => this.resolvedName;
set
{
this.resolvedName = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.ResolvedName)));
}
}
public string? TitleName
{
get => this.titleName;
set
{
this.titleName = value;
this.PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(nameof(this.TitleName)));
}
}
}
+3 -1
View File
@@ -3,9 +3,11 @@
public readonly struct ConnectionContext
{
public readonly int Port;
public readonly uint ProcessId;
public ConnectionContext(int port)
public ConnectionContext(int port, uint processId)
{
this.Port = port;
this.ProcessId = processId;
}
}
+1
View File
@@ -140,6 +140,7 @@ public sealed class Attribute
public int Id { get; private set; }
public string? Name { get; private set; }
public Profession? Profession { get; private set; }
public override string ToString() => this.Name ?? string.Empty;
private Attribute()
{
}
-1
View File
@@ -10,5 +10,4 @@ public sealed class Build
public Profession Secondary { get; set; } = Profession.None;
public List<AttributeEntry> Attributes { get; set; } = [];
public List<Skill> Skills { get; set; } = [Skill.NoSkill, Skill.NoSkill, Skill.NoSkill, Skill.NoSkill, Skill.NoSkill, Skill.NoSkill, Skill.NoSkill, Skill.NoSkill];
public string? SourceUrl { get; set; }
}

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