mirror of
https://github.com/gwdevhub/Daybreak.git
synced 2026-07-15 15:19:57 +00:00
Integrate builds with Focus view (#164)
* Integrate builds with Focus view Detect when GameData is invalid Detect when reading memory is faulty Show current character data in Focus View Closes #161 Closes #162 Closes #163 * Increment version * Bugfix version comparison script
This commit is contained in:
@@ -13,7 +13,7 @@
|
||||
<LangVersion>preview</LangVersion>
|
||||
<ApplicationIcon>Daybreak.ico</ApplicationIcon>
|
||||
<IncludePackageReferencesDuringMarkupCompilation>true</IncludePackageReferencesDuringMarkupCompilation>
|
||||
<Version>0.9.8.9</Version>
|
||||
<Version>0.9.8.10</Version>
|
||||
<EnableWindowsTargeting>true</EnableWindowsTargeting>
|
||||
<UserSecretsId>cfb2a489-db80-448d-a969-80270f314c46</UserSecretsId>
|
||||
</PropertyGroup>
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Daybreak.Models;
|
||||
|
||||
public sealed class GameData
|
||||
{
|
||||
public bool Valid { get; init; }
|
||||
public MainPlayerInformation? MainPlayer { get; init; }
|
||||
public List<PlayerInformation>? Party { get; init; }
|
||||
public UserInformation? User { get; init; }
|
||||
|
||||
@@ -7,6 +7,7 @@ public class PlayerInformation
|
||||
public Profession? PrimaryProfession { get; init; }
|
||||
public Profession? SecondaryProfession { get; init; }
|
||||
public List<Profession>? UnlockedProfession { get; init; }
|
||||
public Build? CurrentBuild { get; init; }
|
||||
public float CurrentHealth { get; init; }
|
||||
public float MaxHealth { get; init; }
|
||||
public float CurrentEnergy { get; init; }
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public sealed class SkillMetadata
|
||||
{
|
||||
public Skill? Skill { get; init; }
|
||||
public uint Adrenaline1 { get; init; }
|
||||
public uint Adrenaline2 { get; init; }
|
||||
public uint Recharge { get; init; }
|
||||
public uint Id { get; init; }
|
||||
public uint Event { get; init; }
|
||||
}
|
||||
@@ -1,4 +1,6 @@
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Daybreak.Models.Guildwars;
|
||||
|
||||
public class WorldPlayerInformation : PlayerInformation
|
||||
{
|
||||
|
||||
@@ -0,0 +1,10 @@
|
||||
namespace Daybreak.Models.Interop;
|
||||
|
||||
public readonly struct AttributeContext
|
||||
{
|
||||
public readonly uint Id;
|
||||
public readonly uint BaseLevel;
|
||||
public readonly uint ActualLevel;
|
||||
public readonly uint DecrementPoints;
|
||||
public readonly uint IncrementPoints;
|
||||
}
|
||||
@@ -12,6 +12,9 @@ public readonly struct GameContext
|
||||
///Array of type <see cref="MapEntityContext"/>.
|
||||
public readonly GuildwarsArray MapEntities;
|
||||
|
||||
[FieldOffset(0x0030)]
|
||||
public readonly GuildwarsArray PartyAttributes;
|
||||
|
||||
[FieldOffset(0x04AC)]
|
||||
public readonly uint QuestId;
|
||||
|
||||
@@ -21,10 +24,13 @@ public readonly struct GameContext
|
||||
[FieldOffset(0x0608)]
|
||||
public readonly uint HardModeUnlocked;
|
||||
|
||||
[FieldOffset(0x640)]
|
||||
[FieldOffset(0x0640)]
|
||||
///Array of type <see cref="ProfessionsContext"/>
|
||||
public readonly GuildwarsArray Professions;
|
||||
|
||||
[FieldOffset(0x0674)]
|
||||
public readonly GuildwarsArray Skillbars;
|
||||
|
||||
[FieldOffset(0x06C4)]
|
||||
public readonly uint Experience;
|
||||
|
||||
|
||||
@@ -0,0 +1,11 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Daybreak.Models.Interop;
|
||||
|
||||
public readonly struct PartyAttributesContext
|
||||
{
|
||||
public readonly uint AgentId;
|
||||
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x36)]
|
||||
public readonly AttributeContext[] Attributes;
|
||||
}
|
||||
@@ -0,0 +1,17 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Daybreak.Models.Interop;
|
||||
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public readonly struct SkillContext
|
||||
{
|
||||
public readonly uint Adrenaline1;
|
||||
|
||||
public readonly uint Adrenaline2;
|
||||
|
||||
public readonly uint Recharge;
|
||||
|
||||
public readonly uint Id;
|
||||
|
||||
public readonly uint Event;
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace Daybreak.Models.Interop;
|
||||
|
||||
[StructLayout(LayoutKind.Explicit)]
|
||||
public readonly struct SkillbarContext
|
||||
{
|
||||
[FieldOffset(0x0000)]
|
||||
public readonly uint AgentId;
|
||||
|
||||
[FieldOffset(0x0004)]
|
||||
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 0x08)]
|
||||
public readonly SkillContext[] Skills;
|
||||
|
||||
[FieldOffset(0x00A4)]
|
||||
public readonly uint Disabled;
|
||||
|
||||
[FieldOffset(0x00B0)]
|
||||
public readonly uint Casting;
|
||||
|
||||
[FieldOffset(0x00B8)]
|
||||
public readonly uint H00B8;
|
||||
}
|
||||
@@ -1,17 +1,19 @@
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Models.Builds;
|
||||
using Daybreak.Models.Guildwars;
|
||||
using Daybreak.Models.Interop;
|
||||
using Microsoft.Extensions.Logging;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Core.Extensions;
|
||||
using System.Diagnostics;
|
||||
using System.Extensions;
|
||||
using System.Linq;
|
||||
using System.Logging;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
using System.Windows.Documents;
|
||||
|
||||
namespace Daybreak.Services.Scanner;
|
||||
|
||||
@@ -26,6 +28,8 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
|
||||
private IntPtr entityArrayPointer;
|
||||
private volatile CancellationTokenSource? cancellationTokenSource;
|
||||
|
||||
public bool Faulty { get; private set; }
|
||||
|
||||
public bool Running => this.cancellationTokenSource is not null && this.cancellationTokenSource.IsCancellationRequested is false;
|
||||
|
||||
public GameData? GameData { get; private set; }
|
||||
@@ -121,9 +125,12 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
|
||||
try
|
||||
{
|
||||
this.ReadGameMemory(cancellationToken);
|
||||
this.Faulty = false;
|
||||
}
|
||||
catch(Exception e)
|
||||
{
|
||||
this.GameData = null;
|
||||
this.Faulty = true;
|
||||
this.logger.LogError(e, "Exception encountered when reading game memory");
|
||||
}
|
||||
}
|
||||
@@ -157,13 +164,16 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
|
||||
var professions = this.memoryScanner.ReadArray<ProfessionsContext>(gameContext.Professions);
|
||||
var players = this.memoryScanner.ReadArray<PlayerContext>(gameContext.Players);
|
||||
var quests = this.memoryScanner.ReadArray<QuestContext>(gameContext.QuestLog);
|
||||
var skills = this.memoryScanner.ReadArray<SkillbarContext>(gameContext.Skillbars);
|
||||
var partyAttributes = this.memoryScanner.ReadArray<PartyAttributesContext>(gameContext.PartyAttributes);
|
||||
var playerEntityId = this.memoryScanner.ReadPtrChain<int>(this.GetPlayerIdPointer(), 0x0, 0x0);
|
||||
|
||||
|
||||
// The following lines would retrieve all entities, including item entities.
|
||||
//var entityArray = this.memoryScanner.ReadPtrChain<GuildwarsArray>(this.GetEntityArrayPointer(), 0x0, 0x0);
|
||||
//var entities = this.memoryScanner.ReadArray<EntityContext>(entityArray);
|
||||
|
||||
this.GameData = this.AggregateGameData(gameContext, instanceContext, mapEntities, players, professions, quests, userContext, playerEntityId);
|
||||
this.GameData = this.AggregateGameData(gameContext, instanceContext, mapEntities, players, professions, quests, userContext, skills, partyAttributes, playerEntityId);
|
||||
}
|
||||
|
||||
private IntPtr GetPlayerIdPointer()
|
||||
@@ -185,7 +195,7 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
|
||||
|
||||
return this.entityArrayPointer;
|
||||
}
|
||||
|
||||
|
||||
private GameData AggregateGameData(
|
||||
GameContext gameContext,
|
||||
InstanceContext instanceContext,
|
||||
@@ -194,6 +204,8 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
|
||||
ProfessionsContext[] professions,
|
||||
QuestContext[] quests,
|
||||
UserContext userContext,
|
||||
SkillbarContext[] skills,
|
||||
PartyAttributesContext[] partyAttributes,
|
||||
int mainPlayerEntityId)
|
||||
{
|
||||
var email = ParseAndCleanWCharArray(userContext.PlayerEmailBytes);
|
||||
@@ -201,14 +213,14 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
|
||||
_ = Map.TryParse((int)userContext.MapId, out var currentMap);
|
||||
var partyMembers = professions
|
||||
.Where(p => p.AgentId != mainPlayerEntityId)
|
||||
.Select(p => GetPlayerInformation((int)p.AgentId, instanceContext, entities, professions))
|
||||
.Select(p => GetPlayerInformation((int)p.AgentId, instanceContext, entities, professions, skills, partyAttributes))
|
||||
.ToList();
|
||||
|
||||
var mainPlayer = GetMainPlayerInformation(mainPlayerEntityId, name, gameContext, instanceContext, entities, professions, quests);
|
||||
var mainPlayer = GetMainPlayerInformation(mainPlayerEntityId, name, gameContext, instanceContext, entities, professions, quests, skills, partyAttributes);
|
||||
|
||||
var worldPlayers = players
|
||||
.Where(p => p.AgentId != mainPlayerEntityId)
|
||||
.Select(p => GetWorldPlayerInformation(p, this.memoryScanner.ReadWString(p.NamePointer, 0x40), instanceContext, entities, professions))
|
||||
.Select(p => GetWorldPlayerInformation(p, this.memoryScanner.ReadWString(p.NamePointer, 0x40), instanceContext, entities, professions, skills, partyAttributes))
|
||||
.ToList();
|
||||
|
||||
|
||||
@@ -245,7 +257,8 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
|
||||
MainPlayer = mainPlayer,
|
||||
Session = sessionInformation,
|
||||
User = userInformation,
|
||||
WorldPlayers = worldPlayers
|
||||
WorldPlayers = worldPlayers,
|
||||
Valid = entities.Length > 0 || players.Length > 0 || professions.Length > 0 || quests.Length > 0 || skills.Length > 0 || partyAttributes.Length > 0
|
||||
};
|
||||
}
|
||||
|
||||
@@ -256,9 +269,11 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
|
||||
InstanceContext instanceContext,
|
||||
MapEntityContext[] entities,
|
||||
ProfessionsContext[] professions,
|
||||
QuestContext[] quests)
|
||||
QuestContext[] quests,
|
||||
SkillbarContext[] skillbars,
|
||||
PartyAttributesContext[] partyAttributes)
|
||||
{
|
||||
var playerInformation = GetPlayerInformation(mainPlayerId, instanceContext, entities, professions);
|
||||
var playerInformation = GetPlayerInformation(mainPlayerId, instanceContext, entities, professions, skillbars, partyAttributes);
|
||||
_ = Quest.TryParse((int)gameContext.QuestId, out var quest);
|
||||
var questLog = quests
|
||||
.Select(q =>
|
||||
@@ -288,7 +303,8 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
|
||||
Name = name,
|
||||
Experience = gameContext.Experience,
|
||||
Level = gameContext.Level,
|
||||
Morale = gameContext.Morale
|
||||
Morale = gameContext.Morale,
|
||||
CurrentBuild = playerInformation.CurrentBuild
|
||||
};
|
||||
}
|
||||
|
||||
@@ -297,9 +313,11 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
|
||||
string name,
|
||||
InstanceContext instanceContext,
|
||||
MapEntityContext[] entities,
|
||||
ProfessionsContext[] professions)
|
||||
ProfessionsContext[] professions,
|
||||
SkillbarContext[] skillbars,
|
||||
PartyAttributesContext[] partyAttributes)
|
||||
{
|
||||
var playerInformation = GetPlayerInformation(playerContext.AgentId, instanceContext, entities, professions);
|
||||
var playerInformation = GetPlayerInformation(playerContext.AgentId, instanceContext, entities, professions, skillbars, partyAttributes);
|
||||
return new WorldPlayerInformation
|
||||
{
|
||||
PrimaryProfession = playerInformation.PrimaryProfession,
|
||||
@@ -312,6 +330,7 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
|
||||
EnergyRegen = playerInformation.EnergyRegen,
|
||||
HealthRegen = playerInformation.HealthRegen,
|
||||
Name = name,
|
||||
CurrentBuild = playerInformation.CurrentBuild
|
||||
};
|
||||
}
|
||||
|
||||
@@ -319,7 +338,9 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
|
||||
int playerId,
|
||||
InstanceContext instanceContext,
|
||||
MapEntityContext[] entities,
|
||||
ProfessionsContext[] professions)
|
||||
ProfessionsContext[] professions,
|
||||
SkillbarContext[] skillbars,
|
||||
PartyAttributesContext[] partyAttributes)
|
||||
{
|
||||
var entityContext = entities.Skip(playerId).FirstOrDefault();
|
||||
var professionContext = professions.Where(p => p.AgentId == playerId).FirstOrDefault();
|
||||
@@ -331,6 +352,44 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
|
||||
.Where(p => p is not null && p != Profession.None)
|
||||
.OrderBy(p => p.Id)
|
||||
.ToList();
|
||||
var maybeSkillbarContext = skillbars.Select(s => (SkillbarContext?) s).FirstOrDefault(s => s?.AgentId == playerId);
|
||||
var maybePartyAttributesContext = partyAttributes.Select(p => (PartyAttributesContext?) p).FirstOrDefault(p => p?.AgentId == playerId);
|
||||
Build? build = null;
|
||||
if (maybeSkillbarContext is SkillbarContext skillbarContext &&
|
||||
maybePartyAttributesContext is PartyAttributesContext attributesContext &&
|
||||
primaryProfession is not null &&
|
||||
secondaryProfession is not null)
|
||||
{
|
||||
var attributes = new List<Models.Guildwars.Attribute> { primaryProfession.PrimaryAttribute! }
|
||||
.Concat(primaryProfession.Attributes)
|
||||
.Concat(secondaryProfession.Attributes)
|
||||
.Select(a => new AttributeEntry { Attribute = a })
|
||||
.ToList();
|
||||
foreach(var attribute in attributesContext.Attributes)
|
||||
{
|
||||
if (attribute.Id < 0 || attribute.Id > 44)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
var maybeAttributeEntry = attributes.FirstOrDefault(a => a.Attribute!.Id == attribute.Id);
|
||||
if (maybeAttributeEntry is not AttributeEntry attributeEntry)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
attributeEntry.Points = (int)attribute.BaseLevel;
|
||||
}
|
||||
|
||||
build = new Build
|
||||
{
|
||||
Primary = primaryProfession,
|
||||
Secondary = secondaryProfession,
|
||||
Attributes = attributes,
|
||||
Skills = skillbarContext.Skills.Select(s => Skill.Parse((int)s.Id)).ToList()
|
||||
};
|
||||
}
|
||||
|
||||
(var currentHp, var currentEnergy) = ApplyEnergyAndHealthRegen(instanceContext, entityContext);
|
||||
return new PlayerInformation
|
||||
{
|
||||
@@ -342,7 +401,8 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
|
||||
MaxHealth = entityContext.MaxHealth,
|
||||
MaxEnergy = entityContext.MaxEnergy,
|
||||
HealthRegen = entityContext.HealthRegen,
|
||||
EnergyRegen = entityContext.EnergyRegen
|
||||
EnergyRegen = entityContext.EnergyRegen,
|
||||
CurrentBuild = build
|
||||
};
|
||||
}
|
||||
|
||||
@@ -377,5 +437,6 @@ public sealed class GuildwarsMemoryReader : IGuildwarsMemoryReader, IDisposable
|
||||
this.cancellationTokenSource?.Cancel();
|
||||
this.cancellationTokenSource?.Dispose();
|
||||
this.cancellationTokenSource = null;
|
||||
this.Faulty = false;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Daybreak.Services.Scanner;
|
||||
|
||||
public interface IGuildwarsMemoryReader
|
||||
{
|
||||
bool Faulty { get; }
|
||||
bool Running { get; }
|
||||
GameData? GameData { get; }
|
||||
Process? TargetProcess { get; }
|
||||
|
||||
@@ -5,6 +5,7 @@
|
||||
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
|
||||
xmlns:local="clr-namespace:Daybreak.Views"
|
||||
xmlns:controls="clr-namespace:Daybreak.Controls"
|
||||
xmlns:converters="clr-namespace:Daybreak.Converters"
|
||||
xmlns:templates="clr-namespace:Daybreak.Controls.Templates"
|
||||
mc:Ignorable="d"
|
||||
Foreground="White"
|
||||
@@ -14,6 +15,7 @@
|
||||
d:DesignHeight="450" d:DesignWidth="800">
|
||||
<UserControl.Resources>
|
||||
<BooleanToVisibilityConverter x:Key="BooleanToVisibilityConverter"></BooleanToVisibilityConverter>
|
||||
<converters:BooleanToVisibilityConverter x:Key="InverseBooleanToVisibilityConverter" TriggerValue="True" ></converters:BooleanToVisibilityConverter>
|
||||
</UserControl.Resources>
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
@@ -32,7 +34,8 @@
|
||||
Grid.Column="1"
|
||||
Grid.ColumnSpan="2"
|
||||
Grid.Row="2"
|
||||
Grid.RowSpan="2">
|
||||
Grid.RowSpan="2"
|
||||
Visibility="{Binding ElementName=_this, Path=GameData.Valid, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
<ColumnDefinition Width="0.1*"></ColumnDefinition>
|
||||
@@ -238,19 +241,90 @@
|
||||
</Grid>
|
||||
<Grid
|
||||
Grid.Row="1"
|
||||
Grid.Column="1">
|
||||
Grid.Column="1"
|
||||
Visibility="{Binding ElementName=_this, Path=GameData.Valid, Mode=OneWay, Converter={StaticResource BooleanToVisibilityConverter}}">
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="10"></RowDefinition>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="10"></RowDefinition>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="10"></RowDefinition>
|
||||
<RowDefinition Height="auto"></RowDefinition>
|
||||
<RowDefinition Height="*"></RowDefinition>
|
||||
</Grid.RowDefinitions>
|
||||
<StackPanel
|
||||
Background="#F0212121"
|
||||
Grid.Row="0"
|
||||
Margin="0, 0, 5, 0">
|
||||
<Grid>
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
<ColumnDefinition Width="*"></ColumnDefinition>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock
|
||||
Grid.Column="0"
|
||||
FontSize="18"
|
||||
Margin="10"
|
||||
Text="Current Character"
|
||||
HorizontalAlignment="Left"></TextBlock>
|
||||
<TextBlock
|
||||
Grid.Column="1"
|
||||
FontSize="18"
|
||||
Margin="10"
|
||||
Text="{Binding ElementName=_this, Path=GameData.MainPlayer.Name, Mode=OneWay}"
|
||||
HorizontalAlignment="Center"></TextBlock>
|
||||
<controls:OpaqueButton
|
||||
Grid.Column="2"
|
||||
Text="Edit Build"
|
||||
Highlight="White"
|
||||
HighlightOpacity="0.6"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
TextHorizontalAlignment="Center"
|
||||
Cursor="Hand"
|
||||
FontSize="18"
|
||||
MouseLeftButtonDown="EditBuild_MouseLeftButtonDown"></controls:OpaqueButton>
|
||||
<controls:OpaqueButton
|
||||
Grid.Column="3"
|
||||
Text="Meta Builds"
|
||||
Highlight="White"
|
||||
HighlightOpacity="0.6"
|
||||
Margin="10"
|
||||
HorizontalAlignment="Stretch"
|
||||
TextHorizontalAlignment="Right"
|
||||
Cursor="Hand"
|
||||
FontSize="18"
|
||||
MouseLeftButtonDown="MetaBuilds_MouseLeftButtonDown"></controls:OpaqueButton>
|
||||
</Grid>
|
||||
<Rectangle
|
||||
Height="1"
|
||||
Fill="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"></Rectangle>
|
||||
<controls:OpaqueButton
|
||||
Text="{Binding ElementName=_this, Path=GameData.MainPlayer.PrimaryProfession.Name, Mode=OneWay}"
|
||||
Highlight="White"
|
||||
HighlightOpacity="0.6"
|
||||
HorizontalAlignment="Stretch"
|
||||
TextHorizontalAlignment="Left"
|
||||
Cursor="Hand"
|
||||
FontSize="20"
|
||||
MouseLeftButtonDown="PrimaryProfession_MouseLeftButtonDown"></controls:OpaqueButton>
|
||||
<controls:OpaqueButton
|
||||
Text="{Binding ElementName=_this, Path=GameData.MainPlayer.SecondaryProfession.Name, Mode=OneWay}"
|
||||
Highlight="White"
|
||||
HighlightOpacity="0.6"
|
||||
HorizontalAlignment="Stretch"
|
||||
TextHorizontalAlignment="Left"
|
||||
Cursor="Hand"
|
||||
FontSize="20"
|
||||
MouseLeftButtonDown="SecondaryProfession_MouseLeftButtonDown"></controls:OpaqueButton>
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Background="#F0212121"
|
||||
Grid.Row="2"
|
||||
Margin="0, 0, 5, 0">
|
||||
<TextBlock
|
||||
FontSize="18"
|
||||
Margin="10"
|
||||
@@ -270,7 +344,7 @@
|
||||
</StackPanel>
|
||||
<StackPanel
|
||||
Background="#F0212121"
|
||||
Grid.Row="2"
|
||||
Grid.Row="4"
|
||||
Margin="0, 0, 5, 0">
|
||||
<TextBlock
|
||||
FontSize="18"
|
||||
@@ -291,7 +365,7 @@
|
||||
</StackPanel>
|
||||
<TextBlock
|
||||
Background="#F0212121"
|
||||
Grid.Row="4"
|
||||
Grid.Row="6"
|
||||
FontSize="18"
|
||||
Margin="0, 0, 5, 0"
|
||||
Padding="10"
|
||||
@@ -300,7 +374,7 @@
|
||||
Background="#F0212121"
|
||||
VerticalScrollBarVisibility="Auto"
|
||||
HorizontalScrollBarVisibility="Disabled"
|
||||
Grid.Row="5"
|
||||
Grid.Row="7"
|
||||
Margin="0, 0, 5, 0">
|
||||
<templates:QuestLogTemplate
|
||||
Foreground="{Binding ElementName=_this, Path=Foreground, Mode=OneWay}"
|
||||
@@ -321,5 +395,10 @@
|
||||
Address="{Binding ElementName=_this, Path=BrowserAddress, Mode=TwoWay}"
|
||||
MaximizeClicked="Browser_MaximizeClicked"
|
||||
PreventDispose="True"></controls:ChromiumBrowserWrapper>
|
||||
<controls:CircularLoadingWidget Grid.ColumnSpan="4"
|
||||
Grid.RowSpan="3"
|
||||
Width="100"
|
||||
Height="100"
|
||||
Visibility="{Binding ElementName=_this, Path=GameData.Valid, Mode=OneWay, Converter={StaticResource InverseBooleanToVisibilityConverter}}"></controls:CircularLoadingWidget>
|
||||
</Grid>
|
||||
</UserControl>
|
||||
|
||||
@@ -1,7 +1,9 @@
|
||||
using Daybreak.Configuration;
|
||||
using Daybreak.Models;
|
||||
using Daybreak.Models.Builds;
|
||||
using Daybreak.Models.Guildwars;
|
||||
using Daybreak.Services.ApplicationLauncher;
|
||||
using Daybreak.Services.BuildTemplates;
|
||||
using Daybreak.Services.Experience;
|
||||
using Daybreak.Services.Navigation;
|
||||
using Daybreak.Services.Scanner;
|
||||
@@ -15,6 +17,7 @@ using System.Threading;
|
||||
using System.Windows;
|
||||
using System.Windows.Controls;
|
||||
using System.Windows.Extensions;
|
||||
using System.Windows.Media.Animation;
|
||||
|
||||
namespace Daybreak.Views;
|
||||
|
||||
@@ -23,8 +26,12 @@ namespace Daybreak.Views;
|
||||
/// </summary>
|
||||
public partial class FocusView : UserControl
|
||||
{
|
||||
private const string InfoNamePlaceholder = "[NAME]";
|
||||
private const string WikiBaseAddress = $"https://wiki.guildwars.com/wiki/{InfoNamePlaceholder}";
|
||||
private const string BuildsAddress = $"https://gwpvx.fandom.com/wiki/Category:Builds_by_category";
|
||||
private const double BarsTotalSize = 116; // Size of the bars on one side of the screen.
|
||||
|
||||
private readonly IBuildTemplateManager buildTemplateManager;
|
||||
private readonly IApplicationLauncher applicationLauncher;
|
||||
private readonly IGuildwarsMemoryReader guildwarsMemoryReader;
|
||||
private readonly IExperienceCalculator experienceCalculator;
|
||||
@@ -74,6 +81,7 @@ public partial class FocusView : UserControl
|
||||
private bool browserMaximized = false;
|
||||
|
||||
public FocusView(
|
||||
IBuildTemplateManager buildTemplateManager,
|
||||
IApplicationLauncher applicationLauncher,
|
||||
IGuildwarsMemoryReader guildwarsMemoryReader,
|
||||
IExperienceCalculator experienceCalculator,
|
||||
@@ -81,6 +89,7 @@ public partial class FocusView : UserControl
|
||||
ILiveUpdateableOptions<ApplicationConfiguration> liveUpdateableOptions,
|
||||
ILogger<FocusView> logger)
|
||||
{
|
||||
this.buildTemplateManager = buildTemplateManager.ThrowIfNull();
|
||||
this.applicationLauncher = applicationLauncher.ThrowIfNull();
|
||||
this.guildwarsMemoryReader = guildwarsMemoryReader.ThrowIfNull();
|
||||
this.experienceCalculator = experienceCalculator.ThrowIfNull();
|
||||
@@ -126,7 +135,8 @@ public partial class FocusView : UserControl
|
||||
this.viewManager.ShowView<LauncherView>();
|
||||
}
|
||||
|
||||
if (this.guildwarsMemoryReader.Running is false)
|
||||
if (this.guildwarsMemoryReader.Running is false ||
|
||||
this.guildwarsMemoryReader.Faulty)
|
||||
{
|
||||
this.guildwarsMemoryReader.Initialize(this.applicationLauncher.RunningGuildwarsProcess!);
|
||||
}
|
||||
@@ -134,6 +144,7 @@ public partial class FocusView : UserControl
|
||||
this.Dispatcher.Invoke(() =>
|
||||
{
|
||||
this.GameData = this.guildwarsMemoryReader.GameData;
|
||||
this.Browser.Visibility = this.GameData?.Valid is true ? Visibility.Visible : Visibility.Collapsed;
|
||||
if (this.GameData?.MainPlayer is null ||
|
||||
this.GameData?.User is null ||
|
||||
this.GameData?.Session is null)
|
||||
@@ -491,6 +502,37 @@ public partial class FocusView : UserControl
|
||||
}
|
||||
}
|
||||
|
||||
private void MetaBuilds_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
this.BrowserAddress = BuildsAddress;
|
||||
}
|
||||
|
||||
private void PrimaryProfession_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
if (this.GameData.MainPlayer?.PrimaryProfession?.Name is string professionName)
|
||||
{
|
||||
this.BrowserAddress = WikiBaseAddress.Replace(InfoNamePlaceholder, professionName);
|
||||
}
|
||||
}
|
||||
|
||||
private void SecondaryProfession_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
if (this.GameData.MainPlayer?.SecondaryProfession?.Name is string professionName)
|
||||
{
|
||||
this.BrowserAddress = WikiBaseAddress.Replace(InfoNamePlaceholder, professionName);
|
||||
}
|
||||
}
|
||||
|
||||
private void EditBuild_MouseLeftButtonDown(object sender, System.Windows.Input.MouseButtonEventArgs e)
|
||||
{
|
||||
if (this.GameData.MainPlayer?.CurrentBuild is Build build)
|
||||
{
|
||||
var buildEntry = this.buildTemplateManager.CreateBuild();
|
||||
buildEntry.Build = build;
|
||||
this.viewManager.ShowView<BuildTemplateView>(buildEntry);
|
||||
}
|
||||
}
|
||||
|
||||
private void Browser_MaximizeClicked(object sender, EventArgs e)
|
||||
{
|
||||
this.browserMaximized = !this.browserMaximized;
|
||||
|
||||
@@ -13,10 +13,36 @@ if ($lastVersion.StartsWith("v")){
|
||||
$lastVersion = $lastVersion.Substring(1)
|
||||
}
|
||||
|
||||
$isNewer = $currentVersion.CompareTo($lastVersion) -eq 1
|
||||
if ($isNewer -eq $false){
|
||||
throw "Version is not incremented. Current version " + $currentVersion + ". Last version " + $lastVersion
|
||||
$currentVersionTokens = $currentVersion.Split('.')
|
||||
$lastVersionTokens = $lastVersion.Split('.')
|
||||
if ($currentVersionTokens.Length -eq $lastVersionTokens.Length)
|
||||
{
|
||||
for($i = 0 ; $i -lt $currentVersionTokens.Length; $i++)
|
||||
{
|
||||
$currentVersionToken = [int]$currentVersionTokens[$i]
|
||||
$lastVersionToken = [int]$lastVersionTokens[$i]
|
||||
if ($currentVersionToken -lt $lastVersionToken){
|
||||
throw "Version is not incremented. Current version " + $currentVersion + ". Last version " + $lastVersion
|
||||
}
|
||||
}
|
||||
|
||||
if ($currentVersionToken -le $lastVersionToken){
|
||||
throw "Version is not incremented. Current version " + $currentVersion + ". Last version " + $lastVersion
|
||||
}
|
||||
|
||||
Write-Host "Version has been incremented"
|
||||
}
|
||||
else{
|
||||
elseif ($currentVersionTokens.Length -gt $lastVersionTokens.Length){
|
||||
if ($currentVersionTokens[$lastVersionTokens.Length - 1] -lt $lastVersionTokens[$lastVersionTokens.Length - 1]){
|
||||
throw "Version is not incremented. Current version " + $currentVersion + ". Last version " + $lastVersion
|
||||
}
|
||||
|
||||
Write-Host "Version has been incremented"
|
||||
}
|
||||
elseif ($currentVersionTokens.Length -lt $lastVersionTokens.Length){
|
||||
if ($currentVersionTokens[$currentVersionTokens.Length - 1] -le $lastVersionTokens[$currentVersionTokens.Length - 1]){
|
||||
throw "Version is not incremented. Current version " + $currentVersion + ". Last version " + $lastVersion
|
||||
}
|
||||
|
||||
Write-Host "Version has been incremented"
|
||||
}
|
||||
Reference in New Issue
Block a user