From 794e69016f164d075e910e028fa98e05368d75e6 Mon Sep 17 00:00:00 2001 From: Erik Rogers Date: Tue, 27 Jun 2023 16:09:32 -0400 Subject: [PATCH 01/12] feat: add conditionals, fix autoload --- CHANGELOG.md | 20 ++++ SleepHunter/Metadata/SkillMetadata.cs | 22 +++- SleepHunter/Metadata/SpellMetadata.cs | 30 ++++++ SleepHunter/Models/Player.cs | 148 ++++++++++++++++++-------- SleepHunter/Views/MainWindow.xaml.cs | 29 +++-- 5 files changed, 196 insertions(+), 53 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 5a61af8..c66d982 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,26 @@ All notable changes to this library will be documented in this file. The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). +## [4.10.1] - 2023-06-27 + +### Added + +- `MinHealthPercent` to spell/skill metadata +- `MaxHealthPercent` to spell/skill metadata (Crasher/Animal Feast/Execute) +- `OpensDialog` to spell metadata (was only skills before) + +### Changed + +- Use new metadata properties for HP based conditions + +### Removed + +- Removed hard-coded names for HP-based skills + +### Fixed + +- Autosave load error popup info + ## [4.10.0] - 2023-06-27 ### Added diff --git a/SleepHunter/Metadata/SkillMetadata.cs b/SleepHunter/Metadata/SkillMetadata.cs index e7f10bb..f3ac3e8 100644 --- a/SleepHunter/Metadata/SkillMetadata.cs +++ b/SleepHunter/Metadata/SkillMetadata.cs @@ -19,6 +19,8 @@ public sealed class SkillMetadata : ObservableObject private bool canImprove = true; private TimeSpan cooldown; private bool requiresDisarm; + private int? minHealthPercent; + private int? maxHealthPercent; [XmlAttribute("Name")] public string Name @@ -91,13 +93,29 @@ public double CooldownSeconds } [XmlAttribute("RequiresDisarm")] - [DefaultValueAttribute(false)] + [DefaultValue(false)] public bool RequiresDisarm { get => requiresDisarm; set => SetProperty(ref requiresDisarm, value); } + [XmlAttribute("MinHealthPercent")] + [DefaultValue(null)] + public int? MinHealthPercent + { + get => minHealthPercent; + set => SetProperty(ref minHealthPercent, value); + } + + [XmlAttribute("MaxHealthPercent")] + [DefaultValue(null)] + public int? MaxHealthPercent + { + get => maxHealthPercent; + set => SetProperty(ref maxHealthPercent, value); + } + public SkillMetadata() { } public override string ToString() => Name ?? "Unknown Skill"; @@ -113,6 +131,8 @@ public void CopyTo(SkillMetadata other) other.OpensDialog = OpensDialog; other.CanImprove = CanImprove; other.RequiresDisarm = RequiresDisarm; + other.MinHealthPercent = MinHealthPercent; + other.MaxHealthPercent = MaxHealthPercent; } } } diff --git a/SleepHunter/Metadata/SpellMetadata.cs b/SleepHunter/Metadata/SpellMetadata.cs index 0e744ab..0bf5456 100644 --- a/SleepHunter/Metadata/SpellMetadata.cs +++ b/SleepHunter/Metadata/SpellMetadata.cs @@ -14,9 +14,12 @@ public sealed class SpellMetadata : ObservableObject private PlayerClass playerClass; private string groupName; private int manaCost; + private bool opensDialog; private int numberOfLines; private TimeSpan cooldown; private bool canImprove = true; + private int? minHealthPercent; + private int? maxHealthPercent; [XmlAttribute("Name")] public string Name @@ -49,6 +52,14 @@ public int ManaCost set => SetProperty(ref manaCost, value); } + [XmlAttribute("OpensDialog")] + [DefaultValue(false)] + public bool OpensDialog + { + get => opensDialog; + set => SetProperty(ref opensDialog, value); + } + [XmlAttribute("Lines")] [DefaultValue(0)] public int NumberOfLines @@ -80,6 +91,22 @@ public bool CanImprove set => SetProperty(ref canImprove, value); } + [XmlAttribute("MinHealthPercent")] + [DefaultValue(null)] + public int? MinHealthPercent + { + get => minHealthPercent; + set => SetProperty(ref minHealthPercent, value); + } + + [XmlAttribute("MaxHealthPercent")] + [DefaultValue(null)] + public int? MaxHealthPercent + { + get => maxHealthPercent; + set => SetProperty(ref maxHealthPercent, value); + } + public SpellMetadata() { } public override string ToString() => Name ?? "Unknown Spell"; @@ -90,9 +117,12 @@ public void CopyTo(SpellMetadata other) other.Class = Class; other.GroupName = GroupName; other.ManaCost = ManaCost; + other.OpensDialog = OpensDialog; other.NumberOfLines = NumberOfLines; other.Cooldown = Cooldown; other.CanImprove = CanImprove; + other.MinHealthPercent = MinHealthPercent; + other.MaxHealthPercent = MaxHealthPercent; } } } diff --git a/SleepHunter/Models/Player.cs b/SleepHunter/Models/Player.cs index 4092a81..b6a0d70 100644 --- a/SleepHunter/Models/Player.cs +++ b/SleepHunter/Models/Player.cs @@ -1,8 +1,7 @@ using System; -using System.Collections.Concurrent; using System.IO; using System.Text; - +using System.Threading; using SleepHunter.Common; using SleepHunter.IO.Process; using SleepHunter.Macro; @@ -15,6 +14,8 @@ public sealed class Player : ObservableObject, IDisposable { private const string CharacterNameKey = @"CharacterName"; + private readonly SemaphoreSlim updateLock = new(1); + private bool isDisposed; private ClientVersion version; private readonly ProcessMemoryAccessor accessor; @@ -279,66 +280,121 @@ private void Dispose(bool isDisposing) { skillbook?.Dispose(); accessor?.Dispose(); + + updateLock?.Dispose(); } isDisposed = true; } - public void Update(PlayerFieldFlags updateFields = PlayerFieldFlags.All) + public async void Update(PlayerFieldFlags updateFields = PlayerFieldFlags.All) { - GameClient.VersionKey = Version?.Key ?? "Unknown"; + await updateLock.WaitAsync(); - var wasLoggedIn = IsLoggedIn; + GameClient.VersionKey = Version?.Key ?? "Unknown"; try { - if (updateFields.HasFlag(PlayerFieldFlags.Name)) - UpdateName(accessor); - - if (updateFields.HasFlag(PlayerFieldFlags.Guild)) - UpdateGuild(accessor); - - if (updateFields.HasFlag(PlayerFieldFlags.GuildRank)) - UpdateGuildRank(accessor); - - if (updateFields.HasFlag(PlayerFieldFlags.Title)) - UpdateTitle(accessor); - - if (updateFields.HasFlag(PlayerFieldFlags.Inventory)) - inventory.Update(accessor); - - if (updateFields.HasFlag(PlayerFieldFlags.Equipment)) - equipment.Update(accessor); - - if (updateFields.HasFlag(PlayerFieldFlags.Skillbook)) - skillbook.Update(accessor); - - if (updateFields.HasFlag(PlayerFieldFlags.Spellbook)) - spellbook.Update(accessor); - - if (updateFields.HasFlag(PlayerFieldFlags.Stats)) - stats.Update(accessor); - - if (updateFields.HasFlag(PlayerFieldFlags.Modifiers)) - modifiers.Update(accessor); - - if (updateFields.HasFlag(PlayerFieldFlags.Location)) - location.Update(accessor); - - if (updateFields.HasFlag(PlayerFieldFlags.GameClient)) - gameClient.Update(accessor); - - if (updateFields.HasFlag(PlayerFieldFlags.Window)) - Process.Update(); + try + { + if (updateFields.HasFlag(PlayerFieldFlags.GameClient)) + gameClient.Update(accessor); + } + catch { } + + try + { + if (updateFields.HasFlag(PlayerFieldFlags.Window)) + Process.Update(); + } + catch { } + + try + { + if (updateFields.HasFlag(PlayerFieldFlags.Name)) + UpdateName(accessor); + } + catch { } + + try + { + if (updateFields.HasFlag(PlayerFieldFlags.Guild)) + UpdateGuild(accessor); + } + catch { } + + try + { + if (updateFields.HasFlag(PlayerFieldFlags.GuildRank)) + UpdateGuildRank(accessor); + } + catch { } + + try + { + if (updateFields.HasFlag(PlayerFieldFlags.Title)) + UpdateTitle(accessor); + } + catch { } + + try + { + if (updateFields.HasFlag(PlayerFieldFlags.Stats)) + stats.Update(accessor); + } + catch { } + + try + { + if (updateFields.HasFlag(PlayerFieldFlags.Modifiers)) + modifiers.Update(accessor); + } + catch { } + + try + { + if (updateFields.HasFlag(PlayerFieldFlags.Location)) + location.Update(accessor); + } + catch { } + + try + { + if (updateFields.HasFlag(PlayerFieldFlags.Inventory)) + inventory.Update(accessor); + } + catch { } + + try + { + if (updateFields.HasFlag(PlayerFieldFlags.Equipment)) + equipment.Update(accessor); + } + catch { } + + try + { + if (updateFields.HasFlag(PlayerFieldFlags.Skillbook)) + skillbook.Update(accessor); + } + catch { } + + try + { + if (updateFields.HasFlag(PlayerFieldFlags.Spellbook)) + spellbook.Update(accessor); + } + catch { } } - catch { } finally { - IsLoggedIn = !string.IsNullOrWhiteSpace(Name) && stats.Level > 0; + updateLock.Release(); } + var wasLoggedIn = IsLoggedIn; + IsLoggedIn = !string.IsNullOrWhiteSpace(Name) && stats.Level > 0; var isNowLoggedIn = IsLoggedIn; - + if (isNowLoggedIn && !wasLoggedIn) OnLoggedIn(); else if (wasLoggedIn && !isNowLoggedIn) diff --git a/SleepHunter/Views/MainWindow.xaml.cs b/SleepHunter/Views/MainWindow.xaml.cs index e0729c6..695d4c4 100644 --- a/SleepHunter/Views/MainWindow.xaml.cs +++ b/SleepHunter/Views/MainWindow.xaml.cs @@ -338,7 +338,7 @@ private void OnPlayerPropertyChanged(object sender, PropertyChangedEventArgs e) Dispatcher.InvokeIfRequired(() => { - if (string.Equals("IsLoggedIn", e.PropertyName, StringComparison.OrdinalIgnoreCase)) + if (string.Equals(nameof(player.IsLoggedIn), e.PropertyName, StringComparison.OrdinalIgnoreCase)) { if (!player.IsLoggedIn) OnPlayerLoggedOut(player); @@ -1357,7 +1357,7 @@ private void SaveMacroState(PlayerMacroState state, string filename, bool showEr logger.LogError($"Unable to save macro state file: {filename}"); if (showError) - this.ShowMessageBox("Failed to Save Macro", "Unable to save the macro state.", ex.Message); + this.ShowMessageBox("Failed to Save Macro", $"Unable to save the macro state for {state.Client.Name}.", ex.Message); } } @@ -1382,10 +1382,24 @@ private void AutoLoadMacroState(PlayerMacroState state, bool showError = true) return; } - LoadMacroState(state, autosaveFilePath, showError); + var didLoad = LoadMacroState(state, autosaveFilePath, showError); + + // File is probably broken, delete it + if (!didLoad && File.Exists(autosaveFilePath)) + { + try + { + File.Delete(autosaveFilePath); + } + catch(Exception ex) + { + logger.LogException(ex); + logger.LogWarn($"Unable to delete autosave file: {autosaveFilePath}"); + } + } } - private void LoadMacroState(PlayerMacroState state, string filename, bool showError = true) + private bool LoadMacroState(PlayerMacroState state, string filename, bool showError = true) { if (state == null) throw new ArgumentNullException(nameof(state)); @@ -1409,9 +1423,9 @@ private void LoadMacroState(PlayerMacroState state, string filename, bool showEr logger.LogError($"Unable to load macro state file: {filename}"); if (showError) - this.ShowMessageBox("Failed to Load Macro", "Unable to load the macro state.", ex.Message); + this.ShowMessageBox("Failed to Load Macro", $"Unable to load the macro state for {state.Client.Name}.", ex.Message); - return; + return false; } try @@ -1517,6 +1531,8 @@ private void LoadMacroState(PlayerMacroState state, string filename, bool showEr { state.LocalStorage.Add(keyValuePair.Key, keyValuePair.Value); } + + return true; } catch (Exception ex) { @@ -1524,6 +1540,7 @@ private void LoadMacroState(PlayerMacroState state, string filename, bool showEr logger.LogError($"Unable to update {state.Client.Name} macro state from deserialized data"); this.ShowMessageBox("Failed to Load Macro", "Unable to load the macro state.", ex.Message); + return false; } finally { From e342b89a9fdd6934d4322b70aad84134a82092d6 Mon Sep 17 00:00:00 2001 From: Erik Rogers Date: Tue, 27 Jun 2023 17:38:23 -0400 Subject: [PATCH 02/12] feat: add skill editor support for conditions --- CHANGELOG.md | 1 + SleepHunter/Metadata/SkillMetadata.cs | 8 +- SleepHunter/Metadata/SpellMetadata.cs | 12 +-- SleepHunter/Views/FlowerTargetWindow.xaml | 2 +- SleepHunter/Views/SkillEditorWindow.xaml | 112 ++++++++++++++++++-- SleepHunter/Views/SkillEditorWindow.xaml.cs | 35 ++++-- 6 files changed, 144 insertions(+), 26 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index c66d982..78475e1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -15,6 +15,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Changed - Use new metadata properties for HP based conditions +- Increase dialog size for skill/spell metadata editors ### Removed diff --git a/SleepHunter/Metadata/SkillMetadata.cs b/SleepHunter/Metadata/SkillMetadata.cs index f3ac3e8..0c5b46b 100644 --- a/SleepHunter/Metadata/SkillMetadata.cs +++ b/SleepHunter/Metadata/SkillMetadata.cs @@ -19,8 +19,8 @@ public sealed class SkillMetadata : ObservableObject private bool canImprove = true; private TimeSpan cooldown; private bool requiresDisarm; - private int? minHealthPercent; - private int? maxHealthPercent; + private double minHealthPercent; + private double maxHealthPercent; [XmlAttribute("Name")] public string Name @@ -102,7 +102,7 @@ public bool RequiresDisarm [XmlAttribute("MinHealthPercent")] [DefaultValue(null)] - public int? MinHealthPercent + public double MinHealthPercent { get => minHealthPercent; set => SetProperty(ref minHealthPercent, value); @@ -110,7 +110,7 @@ public int? MinHealthPercent [XmlAttribute("MaxHealthPercent")] [DefaultValue(null)] - public int? MaxHealthPercent + public double MaxHealthPercent { get => maxHealthPercent; set => SetProperty(ref maxHealthPercent, value); diff --git a/SleepHunter/Metadata/SpellMetadata.cs b/SleepHunter/Metadata/SpellMetadata.cs index 0bf5456..2f94f95 100644 --- a/SleepHunter/Metadata/SpellMetadata.cs +++ b/SleepHunter/Metadata/SpellMetadata.cs @@ -18,8 +18,8 @@ public sealed class SpellMetadata : ObservableObject private int numberOfLines; private TimeSpan cooldown; private bool canImprove = true; - private int? minHealthPercent; - private int? maxHealthPercent; + private double minHealthPercent; + private double maxHealthPercent; [XmlAttribute("Name")] public string Name @@ -92,16 +92,16 @@ public bool CanImprove } [XmlAttribute("MinHealthPercent")] - [DefaultValue(null)] - public int? MinHealthPercent + [DefaultValue(0)] + public double MinHealthPercent { get => minHealthPercent; set => SetProperty(ref minHealthPercent, value); } [XmlAttribute("MaxHealthPercent")] - [DefaultValue(null)] - public int? MaxHealthPercent + [DefaultValue(0)] + public double MaxHealthPercent { get => maxHealthPercent; set => SetProperty(ref maxHealthPercent, value); diff --git a/SleepHunter/Views/FlowerTargetWindow.xaml b/SleepHunter/Views/FlowerTargetWindow.xaml index 716d25b..2290a8d 100644 --- a/SleepHunter/Views/FlowerTargetWindow.xaml +++ b/SleepHunter/Views/FlowerTargetWindow.xaml @@ -346,7 +346,7 @@ TextAlignment="Center" Text=" OR  " Margin="8,0" - ToolTip="Why would this have a tooltip?"> + ToolTip="Either condition will trigger a flowering."> diff --git a/SleepHunter/Views/SkillEditorWindow.xaml b/SleepHunter/Views/SkillEditorWindow.xaml index f1e678c..fb11f09 100644 --- a/SleepHunter/Views/SkillEditorWindow.xaml +++ b/SleepHunter/Views/SkillEditorWindow.xaml @@ -3,7 +3,7 @@ xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml" xmlns:ctl="clr-namespace:SleepHunter.Controls" Title="Skill Editor" - Width="500" Height="460" + Width="600" Height="600" Style="{StaticResource ObsidianWindow}" ResizeMode="NoResize" WindowStartupLocation="CenterOwner" @@ -18,6 +18,9 @@ + + + @@ -82,7 +85,86 @@ TextAlignment="Center" Margin="4"/> - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + @@ -93,6 +175,21 @@ + + + + + + + - - + -