├── SleepHunter ├── SleepHunter.ico ├── SleepHunter.png ├── Common │ ├── DeferredAction.cs │ ├── ICopyable.cs │ ├── DisplayValue.cs │ ├── UpdatableObject.cs │ ├── DeferredDispatcher.cs │ └── ObservableObject.cs ├── Services │ ├── ServiceLifetime.cs │ ├── IServiceProvider.cs │ ├── Serialization │ │ ├── SerializedSkillState.cs │ │ ├── SerializedKeyValue.cs │ │ ├── SerializedHotkey.cs │ │ ├── IMacroStateSerializer.cs │ │ ├── SerializedLocalStorage.cs │ │ ├── SerializedSpellState.cs │ │ ├── SerializedFlowerState.cs │ │ └── SerializedMacroState.cs │ ├── ServiceMapping.cs │ ├── Releases │ │ └── IReleaseService.cs │ ├── Logging │ │ └── ILogger.cs │ └── ServiceCollection.cs ├── Models │ ├── AbilityTargetType.cs │ ├── PlayerSortOrder.cs │ ├── SpellTargetMode.cs │ ├── PlayerClass.cs │ ├── ClientProcessEventArgs.cs │ ├── SpellQueueItemEventArgs.cs │ ├── FlowerQueueItemEventArgs.cs │ ├── EquipmentSlot.cs │ ├── PlayerEventArgs.cs │ ├── PlayerModifiers.cs │ ├── ReleaseVersion.cs │ ├── PlayerFieldFlags.cs │ ├── ReleaseAsset.cs │ ├── Skill.cs │ ├── Spell.cs │ └── InventoryItem.cs ├── Macro │ ├── SpellRotationMode.cs │ ├── MacroAction.cs │ ├── PlayerMacroStatus.cs │ ├── LocalStorageKey.cs │ ├── MacroStateEventArgs.cs │ ├── MacroManager.cs │ └── HotkeyManager.cs ├── Win32 │ ├── ProcessCreationFlags.cs │ ├── VirtualKeyMapMode.cs │ ├── VirtualMemoryStatus.cs │ ├── VirtualMemoryType.cs │ ├── ProcessAccessFlags.cs │ ├── VirtualMemoryProtection.cs │ ├── SecurityAttributes.cs │ ├── NativeExtensions.cs │ ├── ProcessInformation.cs │ ├── Rect.cs │ ├── SystemInfo.cs │ └── MemoryBasicInformation.cs ├── Extensions │ ├── DispatcherExtensions.cs │ ├── VersionExtensions.cs │ ├── StringExtensions.cs │ ├── ControlExtensions.cs │ ├── CharacterExtensions.cs │ ├── BinaryReaderExtensions.cs │ └── WindowExtensions.cs ├── Settings │ ├── ColorThemeEventArgs.cs │ ├── ClientVersionEventArgs.cs │ ├── FeatureFlag.cs │ ├── UserSetting.cs │ ├── ColorThemeCollection.cs │ ├── ClientSignature.cs │ └── ClientVersionCollection.cs ├── Metadata │ ├── SkillMetadataEventArgs.cs │ ├── SpellMetadataEventArgs.cs │ ├── StaffMetadataEventArgs.cs │ ├── SpellLineModifiersEventArgs.cs │ ├── ComputedSpellLines.cs │ ├── StaffMetadataCollection.cs │ ├── SkillMetadataCollection.cs │ └── SpellMetadataCollection.cs ├── Converters │ ├── BooleanInverter.cs │ ├── VisibilityInverter.cs │ ├── FeatureVisibilityConverter.cs │ ├── EquipmentSlotConverter.cs │ ├── VisibilityConverter.cs │ └── ByteStringConverter.cs ├── Threading │ ├── UITask.cs │ ├── UIThreadAwaitable.cs │ └── UITaskMethodBuilder.cs ├── Media │ ├── EpfFrame.cs │ ├── RenderedBitmap.cs │ ├── ColorPalette.cs │ ├── HueSaturationValue.cs │ ├── RenderManager.cs │ └── EpfImage.cs ├── IO │ ├── Process │ │ ├── ProcessAccess.cs │ │ ├── DynamicMemoryVariable.cs │ │ ├── SearchMemoryVariable.cs │ │ ├── MemoryOffset.cs │ │ ├── MemoryVariable.cs │ │ └── ProcessMemoryAccessor.cs │ ├── FileArchiveEntry.cs │ ├── PathHelper.cs │ └── FileArchiveManager.cs ├── Templates │ ├── ClientVersionDataTemplate.xaml │ ├── TimeSpanDataTemplate.xaml │ ├── ColorThemeDataTemplate.xaml │ ├── StringDataTemplate.xaml │ ├── ToolTipTemplate.xaml │ └── EquipmentItemDataTemplate.xaml ├── SleepHunter.sln ├── SleepHunter.csproj ├── App.xaml.cs ├── Obsidian.xaml.cs └── Views │ └── MessageBoxWindow.xaml ├── docs ├── src │ ├── CHANGELOG.md │ ├── screenshots │ │ ├── tool-bar.png │ │ ├── skills-tab.png │ │ ├── spells-tab.png │ │ ├── SleepHunter.png │ │ ├── flowering-tab.png │ │ ├── main-window.png │ │ ├── spell-queue.png │ │ ├── character-list.png │ │ ├── settings-about.png │ │ ├── settings-debug.png │ │ ├── settings-flowering.png │ │ ├── settings-general.png │ │ ├── settings-updates.png │ │ ├── spell-target-none.png │ │ ├── spell-target-self.png │ │ ├── settings-all-macros.png │ │ ├── settings-game-client.png │ │ ├── tile-radius-example.png │ │ ├── flower-target-character.png │ │ ├── metadata-skill-dialog.png │ │ ├── metadata-skills-editor.png │ │ ├── metadata-spell-dialog.png │ │ ├── metadata-spells-editor.png │ │ ├── metadata-staff-dialog.png │ │ ├── metadata-staves-editor.png │ │ ├── settings-skill-macros.png │ │ ├── settings-spell-macros.png │ │ ├── settings-user-interface.png │ │ ├── spell-target-character.png │ │ ├── metadata-modifiers-dialog.png │ │ ├── flower-target-absolute-tile.png │ │ ├── flower-target-relative-tile.png │ │ ├── spell-target-absolute-tile.png │ │ ├── spell-target-relative-tile.png │ │ ├── flower-target-absolute-tile-area.png │ │ ├── flower-target-relative-tile-area.png │ │ ├── flower-target-screen-coordinates.png │ │ ├── spell-target-absolute-tile-area.png │ │ ├── spell-target-relative-tile-area.png │ │ └── spell-target-screen-coordinates.png │ ├── spell-target │ │ ├── no-target.md │ │ ├── screen-position.md │ │ ├── alternate-character.md │ │ ├── self.md │ │ ├── absolute-tile.md │ │ ├── relative-tile.md │ │ ├── relative-tile-area.md │ │ └── absolute-tile-area.md │ ├── settings │ │ ├── about.md │ │ ├── flowering.md │ │ ├── debug.md │ │ ├── skill-macros.md │ │ ├── general.md │ │ ├── updates.md │ │ ├── all-macros.md │ │ ├── game-client.md │ │ └── user-interface.md │ ├── flower-target │ │ ├── screen-position.md │ │ ├── relative-tile.md │ │ ├── absolute-tile.md │ │ ├── alternate-character.md │ │ ├── relative-tile-area.md │ │ └── absolute-tile-area.md │ ├── main-window │ │ ├── items-tab.md │ │ ├── features-tab.md │ │ ├── character-list.md │ │ ├── skills-tab.md │ │ ├── spells-tab.md │ │ ├── spell-queue.md │ │ ├── flowering-tab.md │ │ └── toolbar.md │ ├── LICENSE.md │ ├── metadata-editor │ │ ├── spells-editor.md │ │ ├── skills-editor.md │ │ └── staves-editor.md │ ├── SUMMARY.md │ └── installation.md └── book.toml ├── SleepHunter.Updater ├── SleepHunter-Updater.ico ├── SleepHunter-Updater.png ├── MainWindow.xaml.cs ├── Properties │ └── launchSettings.json ├── Win32 │ └── NativeMethods.cs ├── App.xaml ├── Commands │ └── DelegateCommand.cs ├── SleepHunter.Updater.csproj └── Obsidian.xaml.cs ├── .github └── workflows │ ├── test-docs.yml │ └── deploy-docs.yml ├── LICENSE └── .gitattributes /SleepHunter/SleepHunter.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/SleepHunter/SleepHunter.ico -------------------------------------------------------------------------------- /SleepHunter/SleepHunter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/SleepHunter/SleepHunter.png -------------------------------------------------------------------------------- /docs/src/CHANGELOG.md: -------------------------------------------------------------------------------- 1 | # Changelog 2 | 3 | 4 | -------------------------------------------------------------------------------- /docs/src/screenshots/tool-bar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/tool-bar.png -------------------------------------------------------------------------------- /docs/src/screenshots/skills-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/skills-tab.png -------------------------------------------------------------------------------- /docs/src/screenshots/spells-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/spells-tab.png -------------------------------------------------------------------------------- /docs/src/screenshots/SleepHunter.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/SleepHunter.png -------------------------------------------------------------------------------- /docs/src/screenshots/flowering-tab.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/flowering-tab.png -------------------------------------------------------------------------------- /docs/src/screenshots/main-window.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/main-window.png -------------------------------------------------------------------------------- /docs/src/screenshots/spell-queue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/spell-queue.png -------------------------------------------------------------------------------- /docs/src/screenshots/character-list.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/character-list.png -------------------------------------------------------------------------------- /docs/src/screenshots/settings-about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/settings-about.png -------------------------------------------------------------------------------- /docs/src/screenshots/settings-debug.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/settings-debug.png -------------------------------------------------------------------------------- /SleepHunter.Updater/SleepHunter-Updater.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/SleepHunter.Updater/SleepHunter-Updater.ico -------------------------------------------------------------------------------- /SleepHunter.Updater/SleepHunter-Updater.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/SleepHunter.Updater/SleepHunter-Updater.png -------------------------------------------------------------------------------- /docs/src/screenshots/settings-flowering.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/settings-flowering.png -------------------------------------------------------------------------------- /docs/src/screenshots/settings-general.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/settings-general.png -------------------------------------------------------------------------------- /docs/src/screenshots/settings-updates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/settings-updates.png -------------------------------------------------------------------------------- /docs/src/screenshots/spell-target-none.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/spell-target-none.png -------------------------------------------------------------------------------- /docs/src/screenshots/spell-target-self.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/spell-target-self.png -------------------------------------------------------------------------------- /docs/src/screenshots/settings-all-macros.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/settings-all-macros.png -------------------------------------------------------------------------------- /docs/src/screenshots/settings-game-client.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/settings-game-client.png -------------------------------------------------------------------------------- /docs/src/screenshots/tile-radius-example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/tile-radius-example.png -------------------------------------------------------------------------------- /docs/book.toml: -------------------------------------------------------------------------------- 1 | [book] 2 | authors = ["Erik 'SiLo' Rogers"] 3 | language = "en" 4 | multilingual = false 5 | src = "src" 6 | title = "SleepHunter v4" 7 | -------------------------------------------------------------------------------- /docs/src/screenshots/flower-target-character.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/flower-target-character.png -------------------------------------------------------------------------------- /docs/src/screenshots/metadata-skill-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/metadata-skill-dialog.png -------------------------------------------------------------------------------- /docs/src/screenshots/metadata-skills-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/metadata-skills-editor.png -------------------------------------------------------------------------------- /docs/src/screenshots/metadata-spell-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/metadata-spell-dialog.png -------------------------------------------------------------------------------- /docs/src/screenshots/metadata-spells-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/metadata-spells-editor.png -------------------------------------------------------------------------------- /docs/src/screenshots/metadata-staff-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/metadata-staff-dialog.png -------------------------------------------------------------------------------- /docs/src/screenshots/metadata-staves-editor.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/metadata-staves-editor.png -------------------------------------------------------------------------------- /docs/src/screenshots/settings-skill-macros.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/settings-skill-macros.png -------------------------------------------------------------------------------- /docs/src/screenshots/settings-spell-macros.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/settings-spell-macros.png -------------------------------------------------------------------------------- /docs/src/screenshots/settings-user-interface.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/settings-user-interface.png -------------------------------------------------------------------------------- /docs/src/screenshots/spell-target-character.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/spell-target-character.png -------------------------------------------------------------------------------- /docs/src/screenshots/metadata-modifiers-dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/metadata-modifiers-dialog.png -------------------------------------------------------------------------------- /docs/src/screenshots/flower-target-absolute-tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/flower-target-absolute-tile.png -------------------------------------------------------------------------------- /docs/src/screenshots/flower-target-relative-tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/flower-target-relative-tile.png -------------------------------------------------------------------------------- /docs/src/screenshots/spell-target-absolute-tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/spell-target-absolute-tile.png -------------------------------------------------------------------------------- /docs/src/screenshots/spell-target-relative-tile.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/spell-target-relative-tile.png -------------------------------------------------------------------------------- /docs/src/screenshots/flower-target-absolute-tile-area.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/flower-target-absolute-tile-area.png -------------------------------------------------------------------------------- /docs/src/screenshots/flower-target-relative-tile-area.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/flower-target-relative-tile-area.png -------------------------------------------------------------------------------- /docs/src/screenshots/flower-target-screen-coordinates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/flower-target-screen-coordinates.png -------------------------------------------------------------------------------- /docs/src/screenshots/spell-target-absolute-tile-area.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/spell-target-absolute-tile-area.png -------------------------------------------------------------------------------- /docs/src/screenshots/spell-target-relative-tile-area.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/spell-target-relative-tile-area.png -------------------------------------------------------------------------------- /docs/src/screenshots/spell-target-screen-coordinates.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ewrogers/SleepHunter4/HEAD/docs/src/screenshots/spell-target-screen-coordinates.png -------------------------------------------------------------------------------- /SleepHunter/Common/DeferredAction.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Common 4 | { 5 | public readonly record struct DeferredAction(Action Action, DateTime ExecutionTime) { } 6 | } 7 | -------------------------------------------------------------------------------- /SleepHunter/Services/ServiceLifetime.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Services 3 | { 4 | public enum ServiceLifetime 5 | { 6 | Transient, 7 | Singleton 8 | } 9 | } 10 | -------------------------------------------------------------------------------- /SleepHunter/Models/AbilityTargetType.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Models 3 | { 4 | public enum AbilityTargetType : byte 5 | { 6 | None = 5, 7 | Target = 2, 8 | TextInput = 1 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /SleepHunter/Macro/SpellRotationMode.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Macro 3 | { 4 | public enum SpellRotationMode 5 | { 6 | Default, 7 | None, 8 | Singular, 9 | RoundRobin 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /SleepHunter/Services/IServiceProvider.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Services 4 | { 5 | public interface IServiceProvider : IDisposable 6 | { 7 | bool IsRegistered(); 8 | 9 | T GetService(); 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /SleepHunter/Macro/MacroAction.cs: -------------------------------------------------------------------------------- 1 | namespace SleepHunter.Macro 2 | { 3 | public enum MacroAction 4 | { 5 | None = 0, 6 | Start, 7 | Resume, 8 | Restart, 9 | Pause, 10 | Stop, 11 | ForceQuit 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SleepHunter/Win32/ProcessCreationFlags.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Win32 3 | { 4 | internal enum ProcessCreationFlags : uint 5 | { 6 | None = 0x0, 7 | Debug = 0x1, 8 | DebugOnlyThis = 0x2, 9 | Suspended = 0x4 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /SleepHunter/Models/PlayerSortOrder.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Models 3 | { 4 | public enum PlayerSortOrder 5 | { 6 | LoginTime, 7 | Alphabetical, 8 | HighestHealth, 9 | HighestMana, 10 | HighestCombined 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SleepHunter/Win32/VirtualKeyMapMode.cs: -------------------------------------------------------------------------------- 1 | namespace SleepHunter.Win32 2 | { 3 | internal enum VirtualKeyMapMode 4 | { 5 | VirtualToScanCode = 0, 6 | ScanCodeToVirtual = 1, 7 | VirtualToChar = 2, 8 | ScanCodeToVirtualEx = 3 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /SleepHunter/Common/ICopyable.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Common 3 | { 4 | public interface ICopyable 5 | { 6 | void CopyTo(T other); 7 | void CopyTo(T other, bool copyId); 8 | void CopyTo(T other, bool copyId, bool copyTimestamp); 9 | } 10 | } 11 | -------------------------------------------------------------------------------- /SleepHunter.Updater/MainWindow.xaml.cs: -------------------------------------------------------------------------------- 1 | 2 | using System.Windows; 3 | 4 | namespace SleepHunter.Updater 5 | { 6 | public partial class MainWindow : Window 7 | { 8 | public MainWindow() 9 | { 10 | InitializeComponent(); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SleepHunter/Win32/VirtualMemoryStatus.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Win32 4 | { 5 | [Flags] 6 | internal enum VirtualMemoryStatus : uint 7 | { 8 | None = 0, 9 | Commit = 0x1000, 10 | Free = 0x10000, 11 | Reserve = 0x2000 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SleepHunter/Win32/VirtualMemoryType.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Win32 4 | { 5 | [Flags] 6 | internal enum VirtualMemoryType : uint 7 | { 8 | None = 0, 9 | Image = 0x1000000, 10 | Mapped = 0x40000, 11 | Private = 0x20000, 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SleepHunter/Models/SpellTargetMode.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Models 3 | { 4 | public enum SpellTargetMode 5 | { 6 | None = 0, 7 | Self, 8 | Character, 9 | RelativeTile, 10 | AbsoluteTile, 11 | AbsoluteXY, 12 | RelativeRadius, 13 | AbsoluteRadius 14 | } 15 | } 16 | -------------------------------------------------------------------------------- /SleepHunter.Updater/Properties/launchSettings.json: -------------------------------------------------------------------------------- 1 | { 2 | "profiles": { 3 | "SleepHunter.Updater": { 4 | "commandName": "Project", 5 | "commandLineArgs": "\"C:\\Users\\Erik\\AppData\\Local\\Temp\\SleepHunter-4.5.2.zip\"\r\n\"C:\\Users\\Erik\\Projects\\SleepHunter4\\SleepHunter\\bin\\Debug\\net7.0-windows\\win-x64\"\r\n" 6 | } 7 | } 8 | } -------------------------------------------------------------------------------- /SleepHunter/Models/PlayerClass.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Models 4 | { 5 | [Flags] 6 | public enum PlayerClass 7 | { 8 | Peasant = 0, 9 | Warrior = 0x1, 10 | Wizard = 0x2, 11 | Priest = 0x4, 12 | Rogue = 0x8, 13 | Monk = 0x10, 14 | All = Warrior | Wizard | Priest | Rogue | Monk 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SleepHunter/Win32/ProcessAccessFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Win32 4 | { 5 | [Flags] 6 | internal enum ProcessAccessFlags 7 | { 8 | None = 0x0, 9 | Terminate = 0x01, 10 | CreateThread = 0x2, 11 | VmOperation = 0x8, 12 | VmRead = 0x10, 13 | VmWrite = 0x20, 14 | QueryInformation = 0x400 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SleepHunter/Extensions/DispatcherExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Windows.Threading; 2 | using SleepHunter.Threading; 3 | 4 | namespace SleepHunter.Extensions 5 | { 6 | public static class DispatcherExtensions 7 | { 8 | public static UIThreadAwaitable SwitchToUIThread(this Dispatcher dispatcher) 9 | { 10 | return new UIThreadAwaitable(dispatcher); 11 | } 12 | } 13 | } 14 | -------------------------------------------------------------------------------- /SleepHunter/Extensions/VersionExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Extensions 4 | { 5 | public static class VersionExtensions 6 | { 7 | public static bool IsNewerThan(this Version current, Version otherVersion) => current.CompareTo(otherVersion) > 0; 8 | 9 | public static bool IsOlderThan(this Version current, Version otherVersion) => current.CompareTo(otherVersion) < 0; 10 | } 11 | } 12 | -------------------------------------------------------------------------------- /SleepHunter.Updater/Win32/NativeMethods.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace SleepHunter.Updater.Win32 5 | { 6 | internal static class NativeMethods 7 | { 8 | [DllImport("user32", EntryPoint = "SetForegroundWindow", CharSet = CharSet.Auto)] 9 | [return: MarshalAs(UnmanagedType.Bool)] 10 | internal static extern bool SetForegroundWindow(nint windowHandle); 11 | } 12 | } 13 | -------------------------------------------------------------------------------- /SleepHunter/Services/Serialization/SerializedSkillState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Xml.Serialization; 3 | 4 | namespace SleepHunter.Services.Serialization 5 | { 6 | [Serializable] 7 | public sealed class SerializedSkillState 8 | { 9 | [XmlAttribute("Name")] 10 | public string SkillName { get; set; } 11 | 12 | public SerializedSkillState() { } 13 | 14 | public override string ToString() => SkillName; 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SleepHunter/Models/ClientProcessEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Models 4 | { 5 | public delegate void ClientProcessEventHandler(object sender, ClientProcessEventArgs e); 6 | 7 | public sealed class ClientProcessEventArgs : EventArgs 8 | { 9 | public ClientProcess Process { get; init; } 10 | 11 | public ClientProcessEventArgs(ClientProcess process) 12 | { 13 | Process = process; 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SleepHunter/Services/Serialization/SerializedKeyValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Xml.Serialization; 3 | 4 | namespace SleepHunter.Services.Serialization 5 | { 6 | [Serializable] 7 | public sealed class SerializedKeyValue 8 | { 9 | [XmlAttribute("Key")] 10 | public K Key { get; set; } 11 | 12 | [XmlAttribute("Value")] 13 | public V Value { get; set; } 14 | 15 | public override string ToString() => $"{Key} = {Value}"; 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SleepHunter/Settings/ColorThemeEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Settings 4 | { 5 | public delegate void ColorThemeEventHandler(object sender, ColorThemeEventArgs e); 6 | 7 | public sealed class ColorThemeEventArgs : EventArgs 8 | { 9 | public ColorTheme Theme { get; } 10 | 11 | public ColorThemeEventArgs(ColorTheme theme) 12 | { 13 | Theme = theme ?? throw new ArgumentNullException(nameof(theme)); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SleepHunter/Services/ServiceMapping.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Services 4 | { 5 | public class ServiceMapping 6 | { 7 | public Type Type { get; } 8 | public ServiceLifetime Lifetime { get; } 9 | 10 | public ServiceMapping(Type type, ServiceLifetime lifetime) 11 | { 12 | Type = type; 13 | Lifetime = lifetime; 14 | } 15 | 16 | public override string ToString() => $"{Type.Name} ({Lifetime})"; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /docs/src/spell-target/no-target.md: -------------------------------------------------------------------------------- 1 | # No Target 2 | 3 | ![image](../screenshots/spell-target-none.png) 4 | 5 | This is used when a spell does not require a target, such as a self-only or group spell. 6 | 7 | **NOTE:** This option cannot be selected, it is automatically selected when the spell does not require a target. 8 | 9 | ## Options 10 | 11 | - `Cast Spell Until Reaches` - sets the maximum level before this spell will no longer be cast in the [Spell Queue](../user-interface/main-window.md#spell-queue). 12 | -------------------------------------------------------------------------------- /SleepHunter/Metadata/SkillMetadataEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Metadata 4 | { 5 | public delegate void SkillMetadataEventHandler(object sender, SkillMetadataEventArgs e); 6 | 7 | public sealed class SkillMetadataEventArgs : EventArgs 8 | { 9 | public SkillMetadata Skill { get; } 10 | 11 | public SkillMetadataEventArgs(SkillMetadata skill) 12 | { 13 | Skill = skill ?? throw new ArgumentNullException(nameof(skill)); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SleepHunter/Metadata/SpellMetadataEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Metadata 4 | { 5 | public delegate void SpellMetadataEventHandler(object sender, SpellMetadataEventArgs e); 6 | 7 | public sealed class SpellMetadataEventArgs : EventArgs 8 | { 9 | public SpellMetadata Spell { get; } 10 | 11 | public SpellMetadataEventArgs(SpellMetadata spell) 12 | { 13 | Spell = spell ?? throw new ArgumentNullException(nameof(spell)); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SleepHunter/Metadata/StaffMetadataEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Metadata 4 | { 5 | public delegate void StaffMetadataEventHandler(object sender, StaffMetadataEventArgs e); 6 | 7 | public sealed class StaffMetadataEventArgs : EventArgs 8 | { 9 | public StaffMetadata Staff { get; } 10 | 11 | public StaffMetadataEventArgs(StaffMetadata staff) 12 | { 13 | Staff = staff ?? throw new ArgumentNullException(nameof(staff)); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SleepHunter/Models/SpellQueueItemEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Models 4 | { 5 | public delegate void SpellQueueItemEventHandler(object sender, SpellQueueItemEventArgs e); 6 | 7 | public sealed class SpellQueueItemEventArgs : EventArgs 8 | { 9 | public SpellQueueItem Spell { get; } 10 | 11 | public SpellQueueItemEventArgs(SpellQueueItem spell) 12 | { 13 | Spell = spell ?? throw new ArgumentNullException(nameof(spell)); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SleepHunter/Services/Releases/IReleaseService.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Threading.Tasks; 3 | using SleepHunter.Models; 4 | 5 | namespace SleepHunter.Services.Releases 6 | { 7 | public interface IReleaseService 8 | { 9 | Uri GetLatestReleaseNotesUri(); 10 | 11 | Task GetLatestReleaseVersionAsync(); 12 | 13 | Task GetLatestReleaseAsync(); 14 | 15 | Task DownloadLatestReleaseAsync(Uri downloadUri, IProgress progress = null); 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /SleepHunter/Settings/ClientVersionEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Settings 4 | { 5 | public delegate void ClientVersionEventHandler(object sender, ClientVersionEventArgs e); 6 | 7 | public sealed class ClientVersionEventArgs : EventArgs 8 | { 9 | public ClientVersion Version { get; } 10 | 11 | public ClientVersionEventArgs(ClientVersion version) 12 | { 13 | Version = version ?? throw new ArgumentNullException(nameof(version)); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SleepHunter/Models/FlowerQueueItemEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Models 4 | { 5 | public delegate void FlowerQueueItemEventHandler(object sender, FlowerQueueItemEventArgs e); 6 | 7 | public sealed class FlowerQueueItemEventArgs : EventArgs 8 | { 9 | public FlowerQueueItem Flower { get; } 10 | 11 | public FlowerQueueItemEventArgs(FlowerQueueItem flower) 12 | { 13 | Flower = flower ?? throw new ArgumentNullException(nameof(flower)); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /SleepHunter/Models/EquipmentSlot.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Models 3 | { 4 | public enum EquipmentSlot : byte 5 | { 6 | Weapon, 7 | Armor, 8 | Shield, 9 | Helmet, 10 | Earring, 11 | Necklace, 12 | LeftRing, 13 | RightRing, 14 | LeftGauntlet, 15 | RightGauntlet, 16 | Belt, 17 | Greaves, 18 | Boots, 19 | Accessory1, 20 | Overcoat, 21 | Hat, 22 | Accessory2, 23 | Accessory3 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SleepHunter/Models/PlayerEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Models 4 | { 5 | public delegate void PlayerEventHandler(object sender, PlayerEventArgs e); 6 | 7 | public sealed class PlayerEventArgs : EventArgs 8 | { 9 | public Player Player { get; } 10 | 11 | public PlayerEventArgs(Player player) 12 | { 13 | Player = player ?? throw new ArgumentNullException(nameof(player)); 14 | } 15 | 16 | public override string ToString() => Player.Name; 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /SleepHunter/Models/PlayerModifiers.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using SleepHunter.Common; 4 | 5 | namespace SleepHunter.Models 6 | { 7 | public sealed class PlayerModifiers : UpdatableObject 8 | { 9 | public Player Owner { get; init; } 10 | 11 | public PlayerModifiers(Player owner) 12 | { 13 | Owner = owner ?? throw new ArgumentNullException(nameof(owner)); 14 | } 15 | 16 | protected override void OnUpdate() 17 | { 18 | // Does nothing yet 19 | } 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SleepHunter.Updater/App.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | -------------------------------------------------------------------------------- /SleepHunter/Models/ReleaseVersion.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Models 4 | { 5 | public class ReleaseVersion 6 | { 7 | public int Id { get; } 8 | public Version Version { get; } 9 | public string VersionString => $"{Version.Major}.{Version.Minor}.{Version.Build}"; 10 | 11 | public ReleaseVersion(int id, Version version) 12 | { 13 | Id = id; 14 | Version = version; 15 | } 16 | 17 | public override string ToString() => VersionString; 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SleepHunter/Win32/VirtualMemoryProtection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Win32 4 | { 5 | [Flags] 6 | internal enum VirtualMemoryProtection : uint 7 | { 8 | None = 0, 9 | Execute = 0x10, 10 | ExecuteRead = 0x20, 11 | ExecuteReadWrite = 0x40, 12 | ExecuteWriteCopy = 0x80, 13 | NoAccess = 0x01, 14 | ReadOnly = 0x02, 15 | ReadWrite = 0x4, 16 | WriteCopy = 0x8, 17 | Guard = 0x100, 18 | NoCache = 0x200, 19 | WriteCombine = 0x400 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /SleepHunter/Metadata/SpellLineModifiersEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Metadata 4 | { 5 | public delegate void SpellLineModifiersEventHandler(object sender, SpellLineModifiersEventArgs e); 6 | 7 | public sealed class SpellLineModifiersEventArgs : EventArgs 8 | { 9 | public SpellLineModifiers Modifiers { get; } 10 | 11 | public SpellLineModifiersEventArgs(SpellLineModifiers modifiers) 12 | { 13 | Modifiers = modifiers ?? throw new ArgumentNullException(nameof(modifiers)); 14 | } 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /.github/workflows/test-docs.yml: -------------------------------------------------------------------------------- 1 | name: test-docs 2 | on: [push, pull_request] 3 | 4 | jobs: 5 | test: 6 | name: Test Docs 7 | runs-on: ubuntu-latest 8 | steps: 9 | - uses: actions/checkout@v3 10 | - name: Install mdbook 11 | run: | 12 | mkdir bin 13 | curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.30/mdbook-v0.4.30-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin 14 | echo "$(pwd)/bin" >> $GITHUB_PATH 15 | - name: Run tests 16 | run: | 17 | cd docs 18 | mdbook test -------------------------------------------------------------------------------- /SleepHunter/Macro/PlayerMacroStatus.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Macro 3 | { 4 | public enum PlayerMacroStatus 5 | { 6 | Idle = 0, 7 | Waiting, 8 | WaitingForMana, 9 | WaitingOnVineyard, 10 | ReadyToFlower, 11 | UsingSkills, 12 | Assailing, 13 | SwitchingStaff, 14 | Disarming, 15 | Casting, 16 | FasSpiorad, 17 | Flowering, 18 | Vineyarding, 19 | Following, 20 | Walking, 21 | Thinking, 22 | ChatIsUp, 23 | Nothing = -1 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SleepHunter/Macro/LocalStorageKey.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Macro 3 | { 4 | public static class LocalStorageKey 5 | { 6 | public static class UseWaterAndBeds 7 | { 8 | public const string IsEnabled = $"{nameof(UseWaterAndBeds)}.{nameof(IsEnabled)}"; 9 | public const string TileX = $"{nameof(UseWaterAndBeds)}.{nameof(TileX)}"; 10 | public const string TileY = $"{nameof(UseWaterAndBeds)}.{nameof(TileY)}"; 11 | public const string ManaThreshold = $"{nameof(UseWaterAndBeds)}.{nameof(ManaThreshold)}"; 12 | } 13 | } 14 | } 15 | -------------------------------------------------------------------------------- /docs/src/settings/about.md: -------------------------------------------------------------------------------- 1 | # About SleepHunter 2 | 3 | ![image](../screenshots/settings-about.png) 4 | 5 | The `About` settings tab contains information about the application, author, and computer. 6 | 7 | ## Application Version 8 | 9 | Displays the current version of the application. 10 | 11 | ## Framework Version 12 | 13 | Displays the current version of .NET used by the application. 14 | 15 | ## OS Version 16 | 17 | Displays the current version of the operating system, including CPU architecture. 18 | 19 | ## System Specs 20 | 21 | Displays the current system specifications, including CPU cores and installed RAM. -------------------------------------------------------------------------------- /docs/src/spell-target/screen-position.md: -------------------------------------------------------------------------------- 1 | # Screen Position Target 2 | 3 | ![image](../screenshots/spell-target-screen-coordinates.png) 4 | 5 | This will cast the spell on a specific location on the screen, regardless of the character's current position. 6 | 7 | The `Screen Position X/Y` is used to select the location on the screen to cast the spell on, in pixels. 8 | 9 | ## Options 10 | 11 | - `Screen Position X/Y` - the screen coordinates to cast the spell on. 12 | - `Cast Spell Until Reaches` - sets the maximum level before this spell will no longer be cast in the [Spell Queue](../user-interface/main-window.md#spell-queue). 13 | -------------------------------------------------------------------------------- /SleepHunter/Common/DisplayValue.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Common 3 | { 4 | public class DisplayValue : ObservableObject 5 | { 6 | private string displayName = string.Empty; 7 | private object value; 8 | 9 | public string DisplayName 10 | { 11 | get => displayName; 12 | set => SetProperty(ref displayName, value); 13 | } 14 | 15 | public object Value 16 | { 17 | get => value; 18 | set => SetProperty(ref this.value, value); 19 | } 20 | 21 | public override string ToString() => DisplayName; 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /SleepHunter/Models/PlayerFieldFlags.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Models 4 | { 5 | [Flags] 6 | public enum PlayerFieldFlags : uint 7 | { 8 | None = 0x0, 9 | Name = 0x1, 10 | Guild = 0x2, 11 | GuildRank = 0x4, 12 | Title = 0x8, 13 | Inventory = 0x10, 14 | Equipment = 0x20, 15 | Skillbook = 0x40, 16 | Spellbook = 0x80, 17 | Stats = 0x100, 18 | Modifiers = 0x200, 19 | Location = 0x400, 20 | GameClient = 0x800, 21 | Status = 0x1000, 22 | Window = 0x2000, 23 | All = 0xFFFFFFFF 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /docs/src/flower-target/screen-position.md: -------------------------------------------------------------------------------- 1 | # Screen Position Flower 2 | 3 | ![image](../screenshots/flower-target-screen-coordinates.png) 4 | 5 | This will cast the `Lyliac Plant` spell on a specific location on the screen, regardless of the character's current position. 6 | 7 | The `Screen Position X/Y` is used to select the location on the screen to cast flower on, in pixels. 8 | 9 | ## Options 10 | 11 | - `Screen Position X/Y` - the screen coordinates to cast flower on. 12 | - `Flower Interval` - the time interval between casting the spell on the same screen location. 13 | 14 | **NOTE:** The `Flower Threshold` option is not available for this target type. 15 | -------------------------------------------------------------------------------- /SleepHunter/Extensions/StringExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.Text; 2 | 3 | namespace SleepHunter.Extensions 4 | { 5 | public static class StringExtensions 6 | { 7 | public static string StripNumbers(this string text) 8 | { 9 | if (text == null) 10 | return null; 11 | 12 | var sb = new StringBuilder(text.Length); 13 | 14 | foreach (var c in text) 15 | { 16 | if (char.IsDigit(c) || char.IsNumber(c)) 17 | continue; 18 | 19 | sb.Append(c); 20 | } 21 | 22 | return sb.ToString(); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /docs/src/spell-target/alternate-character.md: -------------------------------------------------------------------------------- 1 | # Alternate Character Target 2 | 3 | ![image](../screenshots/spell-target-character.png) 4 | 5 | This will cast the spell on another character logged in on the same computer. 6 | 7 | **NOTE:** The other character must be within visible range of the character casting the spell. 8 | 9 | ## Options 10 | 11 | - `Character` - specifies the other character you wish to cast the spell on. 12 | - `Mouse Offset X/Y` - offsets the mouse screen coordinates by the specified amount. 13 | - `Cast Spell Until Reaches` - sets the maximum level before this spell will no longer be cast in the [Spell Queue](../user-interface/main-window.md#spell-queue). 14 | -------------------------------------------------------------------------------- /docs/src/spell-target/self.md: -------------------------------------------------------------------------------- 1 | # Self Target 2 | 3 | ![image](../screenshots/spell-target-self.png) 4 | 5 | This will cast the spell on yourself as the target. 6 | 7 | The `Mouse Offset X/Y` is used to fine-tune the mouse screen coordinates, if needed. 8 | 9 | **NOTE:** The older `Relative Coordinates` target type has been replaced with this new `Offset X/Y` target type on `Self`, as it was redundant. 10 | 11 | ## Options 12 | 13 | - `Mouse Offset X/Y` - offsets the mouse screen coordinates by the specified amount. 14 | - `Cast Spell Until Reaches` - sets the maximum level before this spell will no longer be cast in the [Spell Queue](../user-interface/main-window.md#spell-queue). 15 | -------------------------------------------------------------------------------- /SleepHunter/Converters/BooleanInverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Data; 3 | 4 | namespace SleepHunter.Converters 5 | { 6 | public sealed class BooleanInverter : IValueConverter 7 | { 8 | public object Convert(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 9 | { 10 | var boolean = (bool)value; 11 | return !boolean; 12 | } 13 | 14 | public object ConvertBack(object value, Type targetType, object parameter, System.Globalization.CultureInfo culture) 15 | { 16 | var boolean = (bool)value; 17 | return !boolean; 18 | } 19 | } 20 | } 21 | -------------------------------------------------------------------------------- /SleepHunter/Services/Serialization/SerializedHotkey.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Windows.Input; 4 | using System.Xml.Serialization; 5 | 6 | namespace SleepHunter.Services.Serialization 7 | { 8 | [Serializable] 9 | public sealed class SerializedHotkey 10 | { 11 | [XmlAttribute] 12 | [DefaultValue(Key.None)] 13 | public Key Key { get; set; } 14 | 15 | [XmlAttribute] 16 | [DefaultValue(ModifierKeys.None)] 17 | public ModifierKeys Modifiers { get; set; } 18 | 19 | public override string ToString() => Modifiers != ModifierKeys.None ? $"{Modifiers} + {Key}" : Key.ToString(); 20 | } 21 | } 22 | -------------------------------------------------------------------------------- /docs/src/main-window/items-tab.md: -------------------------------------------------------------------------------- 1 | # Items Tab 2 | 3 | The items tab shows either the inventory of items and gold, or equipped gear and accessories. 4 | 5 | You can toggle between the two by clicking the buttons at the top of the panel area. 6 | 7 | ## Inventory Grid Layout 8 | 9 | By default, items are arranged with 12 items per row. 10 | You can change the number of items displayed per row in the [Settings](../settings.md) window. 11 | 12 | The character's gold will be displayed in the last slot. 13 | 14 | ## Equipment List Layout 15 | 16 | Equipment that is worn by the character is displayed in a list. 17 | 18 | ## Tooltip Help 19 | 20 | You can mouse over an item to see the tooltip for that item. 21 | -------------------------------------------------------------------------------- /SleepHunter/Threading/UITask.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Threading.Tasks; 4 | 5 | namespace SleepHunter.Threading 6 | { 7 | [AsyncMethodBuilder(typeof(UITaskMethodBuilder))] 8 | public class UITask 9 | { 10 | private readonly TaskCompletionSource tcs = new(); 11 | 12 | public Task AsTask() => tcs.Task; 13 | 14 | public TaskAwaiter GetAwaiter() => tcs.Task.GetAwaiter(); 15 | 16 | public void SetResult() => tcs.SetResult(); 17 | 18 | public void SetException(Exception exception) => tcs.SetException(exception); 19 | 20 | 21 | public static implicit operator Task(UITask task) => task.AsTask(); 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docs/src/main-window/features-tab.md: -------------------------------------------------------------------------------- 1 | # Features Tab 2 | 3 | Client-specific features that are enabled via feature flags will be displayed here. 4 | 5 | These are defined in the Client Version definitions under `FeatureFlags` section. 6 | 7 | **NOTE:** If there are no **Enabled** features for a client, this tab will **not** be displayed! 8 | 9 | ## Use Water & Beds (Zolian) 10 | 11 | Allows the automatic clicking of water coolers and beds to recovery MP instantly while macroing. 12 | 13 | Can be set to a certain MP threshold and must target an absolute tile on the map. 14 | 15 | **This feature is only available in Zolian.** 16 | 17 | ## Tooltip Help 18 | 19 | You can mouse over an option to see the tooltip for that feature flag. 20 | -------------------------------------------------------------------------------- /SleepHunter/Services/Serialization/IMacroStateSerializer.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using SleepHunter.Macro; 3 | 4 | namespace SleepHunter.Services.Serialization 5 | { 6 | public interface IMacroStateSerializer 7 | { 8 | void Serialize(PlayerMacroState state, TextWriter writer); 9 | void Serialize(PlayerMacroState state, Stream stream, bool leaveOpen = true); 10 | void Serialize(PlayerMacroState state, string file); 11 | 12 | SerializedMacroState Deserialize(PlayerMacroState state, TextReader reader); 13 | SerializedMacroState Deserialize(PlayerMacroState state, Stream stream, bool leaveOpen = true); 14 | SerializedMacroState Deserialize(PlayerMacroState state, string file); 15 | } 16 | } 17 | -------------------------------------------------------------------------------- /docs/src/spell-target/absolute-tile.md: -------------------------------------------------------------------------------- 1 | # Absolute Tile Target 2 | 3 | ![image](../screenshots/spell-target-absolute-tile.png) 4 | 5 | This will cast the spell on a tile at a specific location on the map, regardless of the character's current position. 6 | 7 | **NOTE:** You must be within visible range of the tile for the spell to be cast. 8 | 9 | ## Options 10 | 11 | - `Absolute Tile` - the tile on the map to cast the spell on. It is **not** relative to your character's current position. 12 | - `Mouse Offset X/Y` - offsets the mouse screen coordinates by the specified amount. 13 | - `Cast Spell Until Reaches` - sets the maximum level before this spell will no longer be cast in the [Spell Queue](../user-interface/main-window.md#spell-queue). 14 | -------------------------------------------------------------------------------- /SleepHunter/Media/EpfFrame.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Media 3 | { 4 | public sealed class EpfFrame 5 | { 6 | public int Index { get; set; } 7 | 8 | public int X { get; set; } 9 | 10 | public int Y { get; set; } 11 | 12 | public int Width { get; set; } 13 | 14 | public int Height { get; set; } 15 | 16 | public byte[] RawData { get; set; } 17 | 18 | public EpfFrame() { } 19 | 20 | public EpfFrame(int index, int x, int y, int width, int height, byte[] rawData) 21 | { 22 | Index = index; 23 | X = x; 24 | Y = y; 25 | Width = width; 26 | Height = height; 27 | RawData = rawData; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SleepHunter/Models/ReleaseAsset.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Models 4 | { 5 | public sealed class ReleaseAsset 6 | { 7 | public int Id { get; } 8 | 9 | public Version Version { get; } 10 | public string VersionString => $"{Version.Major}.{Version.Minor}.{Version.Build}"; 11 | 12 | public Uri DownloadUri { get; } 13 | 14 | public long ContentSize { get; } 15 | 16 | public ReleaseAsset(int id, Version version, Uri downloadUri, long contentSize) 17 | { 18 | Id = id; 19 | Version = version; 20 | DownloadUri = downloadUri; 21 | ContentSize = contentSize; 22 | } 23 | 24 | public override string ToString() => VersionString; 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs/src/spell-target/relative-tile.md: -------------------------------------------------------------------------------- 1 | # Relative Tile Target 2 | 3 | ![image](../screenshots/spell-target-relative-tile.png) 4 | 5 | This will cast the spell on a tile relative to the character's current position. 6 | For example, "2 Left, 1 Down" would be the tile located 2 tiles left and 1 tile down of your character's current position. 7 | 8 | This relative tile will be preserved even if the character moves to a different tile. 9 | 10 | ## Options 11 | 12 | - `Relative Tile` - the tile relative to your character's current position. 13 | - `Mouse Offset X/Y` - offsets the mouse screen coordinates by the specified amount. 14 | - `Cast Spell Until Reaches` - sets the maximum level before this spell will no longer be cast in the [Spell Queue](../user-interface/main-window.md#spell-queue). 15 | -------------------------------------------------------------------------------- /SleepHunter/Macro/MacroStateEventArgs.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Macro 4 | { 5 | public delegate void MacroStateEventHandler(object sender, MacroStateEventArgs e); 6 | 7 | public delegate void MacroStatusEventHandler(object sender, MacroStatusEventArgs e); 8 | 9 | public sealed class MacroStateEventArgs : EventArgs 10 | { 11 | public MacroState State { get; } 12 | 13 | public MacroStateEventArgs(MacroState state) 14 | { 15 | State = state; 16 | } 17 | } 18 | 19 | public sealed class MacroStatusEventArgs : EventArgs 20 | { 21 | public MacroStatus Status { get; } 22 | 23 | public MacroStatusEventArgs(MacroStatus status) 24 | { 25 | Status = status; 26 | } 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /SleepHunter/Converters/VisibilityInverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace SleepHunter.Converters 7 | { 8 | public class VisibilityInverter : IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | if (value is Visibility visibility) 13 | return visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible; 14 | 15 | return value; 16 | } 17 | 18 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 19 | { 20 | return Convert(value, targetType, parameter, culture); 21 | } 22 | } 23 | } 24 | -------------------------------------------------------------------------------- /docs/src/flower-target/relative-tile.md: -------------------------------------------------------------------------------- 1 | # Relative Tile Flower 2 | 3 | ![image](../screenshots/flower-target-relative-tile.png) 4 | 5 | This will cast the `Lyliac Plant` spell on a tile relative to the character's current position. 6 | For example, "2 Left, 1 Down" would be the tile located 2 tiles left and 1 tile down of your character's current position. 7 | 8 | This relative tile will be preserved even if the character moves to a different tile. 9 | 10 | ## Options 11 | 12 | - `Relative Tile` - the tile relative to your character's current position. 13 | - `Mouse Offset X/Y` - offsets the mouse screen coordinates by the specified amount. 14 | - `Flower Interval` - the time interval between casting flower on the same tile location. 15 | 16 | **NOTE:** The `Flower Threshold` option is not available for this target type. 17 | -------------------------------------------------------------------------------- /SleepHunter/Win32/SecurityAttributes.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace SleepHunter.Win32 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | internal struct SecurityAttributes 7 | { 8 | private int size; 9 | private nint securityDescriptor; 10 | private bool inheritHandle; 11 | 12 | public int Size 13 | { 14 | readonly get => size; 15 | set => size = value; 16 | } 17 | 18 | public nint SecurityDescriptor 19 | { 20 | readonly get => securityDescriptor; 21 | set => securityDescriptor = value; 22 | } 23 | 24 | public bool InheritHandle 25 | { 26 | readonly get => inheritHandle; 27 | set => inheritHandle = value; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /SleepHunter/Threading/UIThreadAwaitable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Windows.Threading; 4 | 5 | namespace SleepHunter.Threading 6 | { 7 | public readonly struct UIThreadAwaitable : INotifyCompletion 8 | { 9 | private readonly Dispatcher dispatcher; 10 | 11 | public UIThreadAwaitable(Dispatcher dispatcher) 12 | { 13 | this.dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); 14 | } 15 | 16 | public readonly UIThreadAwaitable GetAwaiter() => this; 17 | 18 | public readonly void GetResult() { } 19 | 20 | public readonly bool IsCompleted => dispatcher.CheckAccess(); 21 | 22 | public readonly void OnCompleted(Action continuation) => dispatcher.BeginInvoke(continuation); 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /docs/src/flower-target/absolute-tile.md: -------------------------------------------------------------------------------- 1 | # Absolute Tile Flower 2 | 3 | ![image](../screenshots/flower-target-absolute-tile.png) 4 | 5 | This will cast the `Lyliac Plant` spell on a tile at a specific location on the map, regardless of the character's current position. 6 | This will restore the mana of the character standing on the tile. 7 | 8 | **NOTE:** You must be within visible range of the tile for the spell to be cast. 9 | 10 | ## Options 11 | 12 | - `Absolute Tile` - the tile on the map to cast flower on. It is **not** relative to your character's current position. 13 | - `Mouse Offset X/Y` - offsets the mouse screen coordinates by the specified amount. 14 | - `Flower Interval` - the time interval between casting flower on the same tile location. 15 | 16 | **NOTE:** The `Flower Threshold` option is not available for this target type. 17 | -------------------------------------------------------------------------------- /SleepHunter/IO/Process/ProcessAccess.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using SleepHunter.Win32; 4 | 5 | namespace SleepHunter.IO.Process 6 | { 7 | [Flags] 8 | public enum ProcessAccess 9 | { 10 | Read = 0x1, 11 | Write = 0x2, 12 | ReadWrite = Read | Write 13 | } 14 | 15 | public static class ProcessAccessExtender 16 | { 17 | internal static ProcessAccessFlags ToWin32Flags(this ProcessAccess access) 18 | { 19 | var flags = ProcessAccessFlags.VmOperation | ProcessAccessFlags.QueryInformation; 20 | 21 | if (access.HasFlag(ProcessAccess.Read)) 22 | flags |= ProcessAccessFlags.VmRead; 23 | 24 | if (access.HasFlag(ProcessAccess.Write)) 25 | flags |= ProcessAccessFlags.VmWrite; 26 | 27 | return flags; 28 | } 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /docs/src/settings/flowering.md: -------------------------------------------------------------------------------- 1 | # Flowering Settings 2 | 3 | ![image](../screenshots/settings-flowering.png) 4 | 5 | The `Flowering` settings tab contains settings for the casting `Lyliac Plant` (flower) spell on others. 6 | 7 | ## Prioritize Alternate Characters 8 | 9 | This setting determines whether the application will prioritize casting the `Lyliac Plant` spell on alternate characters over other target types. 10 | By default, this is `Enabled`. 11 | 12 | ## Prioritize Flowering over Spell Casting 13 | 14 | This setting determines whether the application will prioritize casting the `Lyliac Plant` spell over other spell casting macros. 15 | By default, this is `Enabled`. 16 | 17 | ## Minimum Flower Mana 18 | 19 | This setting determines the minimum mana required to cast the `Lyliac Plant` spell. 20 | By default, this is `10000` but can be decreased as necessary. 21 | -------------------------------------------------------------------------------- /SleepHunter/Templates/ClientVersionDataTemplate.xaml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /docs/src/settings/debug.md: -------------------------------------------------------------------------------- 1 | # Debug Settings 2 | 3 | ![image](../screenshots/settings-debug.png) 4 | 5 | The `Debug` settings tab contains settings for debugging the application, not intended for normal use. 6 | 7 | ## Show All Game Processes 8 | 9 | This setting determines whether the application will show all running Dark Ages clients, including those that are not logged into a character. 10 | By default, this is `Disabled`. 11 | 12 | This is useful for debugging the application when client instances are not being detected properly. 13 | Typically, this is used with custom Dark Ages clients testing client version variations. 14 | 15 | ## Enable Debug Logging 16 | 17 | This setting determines whether the application will write debug logs to the `logs` folder. 18 | By default, this is `Disabled`. 19 | 20 | This can be useful for debugging strange behavior in the application. 21 | -------------------------------------------------------------------------------- /docs/src/settings/skill-macros.md: -------------------------------------------------------------------------------- 1 | # Skill Macro Settings 2 | 3 | ![image](../screenshots/settings-skill-macros.png) 4 | 5 | The `Skill Macro Settings` settings tab contains settings for skill macros. 6 | 7 | ## Assail Skills Mode 8 | 9 | This setting determines how the `Assail Skills` macro will performed. 10 | By default, this is `Use Space Bar` which will perform all assails. Alternatively, they can be clicked individually. 11 | 12 | ## Disarm Before Using Assail Skills 13 | 14 | This setting determines whether the character will automatically un-equip any weapons before using assail skills. 15 | By default, this is `Enabled`. 16 | 17 | This prevents unnecessary repairs to weapons when using assail skills. 18 | 19 | **NOTE:** This setting is for historical purposes when certain sub-classes would not be able to re-equip certain weapons or staves from their previous class. 20 | -------------------------------------------------------------------------------- /SleepHunter/Services/Logging/ILogger.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | 4 | namespace SleepHunter.Services.Logging 5 | { 6 | public interface ILogger : IDisposable 7 | { 8 | bool AutoFlush { get; set; } 9 | 10 | void LogInfo(string message, string category = ""); 11 | void LogWarn(string message, string category = ""); 12 | void LogError(string message, string category = ""); 13 | 14 | void LogException(Exception exception, string category = "", [CallerMemberName] string memberName = "", [CallerFilePath] string filePath = "", [CallerLineNumber] int lineNumber = 1); 15 | void LogDebug(string message, string category = "", [CallerMemberName] string memberName = "", [CallerFilePath] string filePath = "", [CallerLineNumber] int lineNumber = 1); 16 | 17 | void AddFileTransport(string filePath); 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /SleepHunter/IO/FileArchiveEntry.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.IO 3 | { 4 | public sealed class FileArchiveEntry 5 | { 6 | private int index; 7 | private string name; 8 | private long offset; 9 | private long size; 10 | 11 | public int Index 12 | { 13 | get => index; 14 | set => index = value; 15 | } 16 | 17 | public string Name 18 | { 19 | get => name; 20 | set => name = value; 21 | } 22 | 23 | public long Offset 24 | { 25 | get => offset; 26 | set => offset = value; 27 | } 28 | 29 | public long Size 30 | { 31 | get => size; 32 | set => size = value; 33 | } 34 | 35 | public FileArchiveEntry() { } 36 | 37 | public override string ToString() => Name; 38 | } 39 | } 40 | -------------------------------------------------------------------------------- /SleepHunter/Services/Serialization/SerializedLocalStorage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Xml.Serialization; 4 | 5 | namespace SleepHunter.Services.Serialization 6 | { 7 | [Serializable] 8 | public sealed class SerializedLocalStorage 9 | { 10 | [XmlArray("Entries")] 11 | [XmlArrayItem("Entry")] 12 | public List> Entries { get; set; } = new(); 13 | 14 | public SerializedLocalStorage() { } 15 | 16 | public SerializedLocalStorage(IDictionary collection) 17 | { 18 | if (collection == null) 19 | throw new ArgumentNullException(nameof(collection)); 20 | 21 | foreach (var pair in collection) 22 | Entries.Add(new SerializedKeyValue { Key = pair.Key, Value = pair.Value }); 23 | } 24 | } 25 | } 26 | -------------------------------------------------------------------------------- /SleepHunter.Updater/Commands/DelegateCommand.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Input; 3 | 4 | namespace SleepHunter.Updater.Commands 5 | { 6 | internal class DelegateCommand : ICommand 7 | { 8 | private readonly Action onExecute; 9 | private readonly Predicate canExecute; 10 | 11 | public event EventHandler CanExecuteChanged 12 | { 13 | add { CommandManager.RequerySuggested += value; } 14 | remove { CommandManager.RequerySuggested -= value; } 15 | } 16 | 17 | public DelegateCommand(Action onExecute, Predicate canExecute = null) 18 | { 19 | this.onExecute = onExecute; 20 | this.canExecute = canExecute; 21 | } 22 | 23 | public bool CanExecute(object param) => canExecute?.Invoke(param) ?? true; 24 | public void Execute(object param) => onExecute?.Invoke(); 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /docs/src/flower-target/alternate-character.md: -------------------------------------------------------------------------------- 1 | # Alternate Character Flower 2 | 3 | ![image](../screenshots/flower-target-character.png) 4 | 5 | This will cast the `Lyliac Plant` spell on another character logged in on the same computer. 6 | 7 | **NOTE:** The other character must be within visible range of the character casting flower. 8 | 9 | ## Options 10 | 11 | - `Character` - specifies the other character you wish to cast flower on. 12 | - `Mouse Offset X/Y` - offsets the mouse screen coordinates by the specified amount. 13 | - `Flower Interval` - the time interval between casting flower on the same character. 14 | - `Flower Threshold` - the minimum mana percentage of the character before flower will be cast on them. 15 | 16 | **NOTE:** The `Flower Interval` and `Flower Threshold` conditions are evaluated independent of each other. 17 | They act as a logical `OR` condition, where **either** condition can be met to cast flower. 18 | -------------------------------------------------------------------------------- /SleepHunter/Media/RenderedBitmap.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Media; 3 | using System.Windows.Media.Imaging; 4 | 5 | namespace SleepHunter.Media 6 | { 7 | public sealed class RenderedBitmap 8 | { 9 | public int Width { get; } 10 | public int Height { get; } 11 | public int Stride { get; } 12 | public PixelFormat Format { get; } 13 | public byte[] Bits { get; } 14 | 15 | public RenderedBitmap(int width, int height, int stride, PixelFormat format, byte[] bits) 16 | { 17 | Width = width; 18 | Height = height; 19 | Stride = stride; 20 | Format = format; 21 | Bits = bits ?? throw new ArgumentNullException(nameof(bits)); 22 | } 23 | 24 | public BitmapSource CreateBitmapSource(double dpiX = 96.0, double dpiY = 96.0) 25 | => BitmapSource.Create(Width, Height, dpiX, dpiY, Format, null, Bits, Stride); 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /SleepHunter/IO/PathHelper.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.IO; 3 | 4 | namespace SleepHunter.IO 5 | { 6 | public static class PathHelper 7 | { 8 | public static string RootPath(string baseDirectory, string path) 9 | { 10 | if (Path.IsPathRooted(path)) 11 | return path; 12 | 13 | var attributes = File.GetAttributes(baseDirectory); 14 | 15 | var directory = baseDirectory; 16 | 17 | if (!attributes.HasFlag(FileAttributes.Directory)) 18 | directory = Path.GetDirectoryName(baseDirectory); 19 | 20 | return Path.Combine(directory, path); 21 | } 22 | 23 | public static string MakeApplicationPath(string path) 24 | { 25 | if (Path.IsPathRooted(path)) 26 | return path; 27 | 28 | var appPath = AppDomain.CurrentDomain.BaseDirectory; 29 | return Path.Combine(appPath, path); 30 | } 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /docs/src/settings/general.md: -------------------------------------------------------------------------------- 1 | # General Settings 2 | 3 | ![image](../screenshots/settings-general.png) 4 | 5 | The `General` settings tab contains general settings for the application. 6 | 7 | ## Process Update Interval 8 | 9 | The process update interval determines how often the application will check for new Dark Ages game client instances. 10 | 11 | ## Client Update Interval 12 | 13 | The client update interval determines how often the application will read character state information from the Dark Ages game clients. 14 | 15 | ## Auto-Save Macro Stats 16 | 17 | This setting will automatically save the macro stats for each character when the application is closed. 18 | They will automatically be loaded when the application is started and that character is logged in. 19 | 20 | ## Reset All Settings 21 | 22 | This button will reset all settings to their default values, and be applied instantly. 23 | 24 | **NOTE:** User settings are automatically saved when the application is closed. 25 | -------------------------------------------------------------------------------- /SleepHunter/Win32/NativeExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | using FILETIME = System.Runtime.InteropServices.ComTypes.FILETIME; 4 | 5 | namespace SleepHunter.Win32 6 | { 7 | internal static class NativeExtensions 8 | { 9 | public static DateTime FiletimeToDateTime(this FILETIME filetime) 10 | { 11 | ulong nanoSeconds = ((ulong)filetime.dwHighDateTime) << 32; 12 | nanoSeconds = unchecked(nanoSeconds | (uint)filetime.dwLowDateTime); 13 | 14 | var ticks = (long)nanoSeconds; 15 | 16 | return DateTime.FromFileTime(ticks); 17 | } 18 | 19 | public static TimeSpan FiletimeToTimeSpan(this FILETIME filetime) 20 | { 21 | ulong nanoSeconds = ((ulong)filetime.dwHighDateTime) << 32; 22 | nanoSeconds = unchecked(nanoSeconds | (uint)filetime.dwLowDateTime); 23 | 24 | var ticks = (long)nanoSeconds; 25 | 26 | return TimeSpan.FromTicks(ticks); 27 | } 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /SleepHunter/Settings/FeatureFlag.cs: -------------------------------------------------------------------------------- 1 | using System.Xml.Serialization; 2 | using SleepHunter.Common; 3 | 4 | namespace SleepHunter.Settings 5 | { 6 | public sealed class FeatureFlag : ObservableObject 7 | { 8 | private string key; 9 | private bool enabled = true; 10 | 11 | [XmlAttribute("Key")] 12 | public string Key 13 | { 14 | get => key; 15 | set => SetProperty(ref key, value); 16 | } 17 | 18 | [XmlAttribute("Enabled")] 19 | public bool IsEnabled 20 | { 21 | get => enabled; 22 | set => SetProperty(ref enabled, value); 23 | } 24 | 25 | public FeatureFlag() : 26 | this("Key") 27 | { } 28 | 29 | public FeatureFlag(string key, bool enabled = true) 30 | { 31 | Key = key; 32 | IsEnabled = enabled; 33 | } 34 | 35 | public override string ToString() => $"{Key} = {IsEnabled}"; 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SleepHunter/Templates/TimeSpanDataTemplate.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 17 | 18 | 19 | 20 | -------------------------------------------------------------------------------- /SleepHunter/Win32/ProcessInformation.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace SleepHunter.Win32 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | internal struct ProcessInformation 7 | { 8 | private nint processHandle; 9 | private nint threadHandle; 10 | private int processId; 11 | private int threadId; 12 | 13 | public nint ProcessHandle 14 | { 15 | readonly get => processHandle; 16 | set => processHandle = value; 17 | } 18 | 19 | public nint ThreadHandle 20 | { 21 | readonly get => threadHandle; 22 | set => threadHandle = value; 23 | } 24 | 25 | public int ProcessId 26 | { 27 | readonly get => processId; 28 | set => processId = value; 29 | } 30 | 31 | public int ThreadId 32 | { 33 | readonly get => threadId; 34 | set => threadId = value; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /SleepHunter/Win32/Rect.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace SleepHunter.Win32 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | internal struct Rect 8 | { 9 | private int left; 10 | private int top; 11 | private int right; 12 | private int bottom; 13 | 14 | public int Left 15 | { 16 | readonly get => left; 17 | set => left = value; 18 | } 19 | 20 | public int Top 21 | { 22 | readonly get => top; 23 | set => top = value; 24 | } 25 | 26 | public int Right 27 | { 28 | readonly get => right; 29 | set => right = value; 30 | } 31 | 32 | public int Bottom 33 | { 34 | readonly get => bottom; 35 | set => bottom = value; 36 | } 37 | 38 | public readonly int Width => Math.Abs(right - left); 39 | 40 | public readonly int Height => Math.Abs(bottom - top); 41 | } 42 | } 43 | -------------------------------------------------------------------------------- /docs/src/settings/updates.md: -------------------------------------------------------------------------------- 1 | # Update Settings 2 | 3 | ![image](../screenshots/settings-updates.png) 4 | 5 | The `Updates` settings tab contains settings for the application update system. 6 | 7 | ## Current Version 8 | 9 | Displays the current version of the application. 10 | 11 | ## Latest Version 12 | 13 | Displays the latest version of the application, available from the [GitHub releases page](https://github.com/ewrogers/SleepHunter4/releases). 14 | 15 | If an update is available, an `Update` button will be displayed and allow the user to download and install it. 16 | 17 | ## Release Notes 18 | 19 | Opens a browser to show the release notes for the latest version of the application. 20 | 21 | ## Check for Updates 22 | 23 | Manually checks for updates to the application. 24 | 25 | ## Check for Updates on Startup 26 | 27 | This setting determines whether the application will automatically check for updates on startup. 28 | By default, this is `Enabled`. 29 | 30 | If an update is available, the user will be prompted to download and install it. 31 | -------------------------------------------------------------------------------- /SleepHunter/IO/Process/DynamicMemoryVariable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Xml.Serialization; 4 | 5 | namespace SleepHunter.IO.Process 6 | { 7 | [Serializable] 8 | public class DynamicMemoryVariable : MemoryVariable 9 | { 10 | protected List offsets = new(); 11 | 12 | [XmlArray("Offsets")] 13 | [XmlArrayItem("Offset", typeof(MemoryOffset))] 14 | public List Offsets 15 | { 16 | get => offsets; 17 | set => offsets = value; 18 | } 19 | 20 | public DynamicMemoryVariable() 21 | : this(string.Empty, 0, 0) { } 22 | 23 | public DynamicMemoryVariable(string key, long address, int maxLength = 0, int size = 0, int count = 0, params long[] offsets) 24 | : base(key, address, maxLength, size, count) 25 | { 26 | if (offsets == null) 27 | return; 28 | 29 | foreach (var offset in offsets) 30 | this.offsets.Add(new MemoryOffset(offset)); 31 | } 32 | } 33 | } 34 | -------------------------------------------------------------------------------- /.github/workflows/deploy-docs.yml: -------------------------------------------------------------------------------- 1 | name: deploy-docs 2 | on: 3 | push: 4 | branches: 5 | - main 6 | 7 | jobs: 8 | deploy: 9 | name: Deploy Docs 10 | runs-on: ubuntu-latest 11 | steps: 12 | - uses: actions/checkout@v3 13 | with: 14 | fetch-depth: 0 15 | - name: Install mdbook 16 | run: | 17 | mkdir bin 18 | curl -sSL https://github.com/rust-lang/mdBook/releases/download/v0.4.30/mdbook-v0.4.30-x86_64-unknown-linux-gnu.tar.gz | tar -xz --directory=bin 19 | echo "$(pwd)/bin" >> $GITHUB_PATH 20 | - name: Deploy Docs 21 | run: | 22 | cp CHANGELOG.md docs/src 23 | cd docs 24 | mdbook build 25 | git worktree add gh-pages 26 | git config user.name "Deploy from CI" 27 | git config user.email "" 28 | cd gh-pages 29 | git update-ref -d refs/heads/gh-pages 30 | rm -rf * 31 | mv ../book/* . 32 | git add . 33 | git commit -m "Deploy $GITHUB_SHA to gh-pages" 34 | git push --force --set-upstream origin gh-pages -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2004-2023 Erik Rogers 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /SleepHunter/Extensions/ControlExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Windows.Controls; 3 | 4 | namespace SleepHunter.Extensions 5 | { 6 | public static class ControlExtensions 7 | { 8 | public static T FindItem(this ItemsControl control, Func selector) where T : class 9 | { 10 | if (control == null) 11 | throw new ArgumentNullException(nameof(control)); 12 | 13 | if (selector == null) 14 | throw new ArgumentNullException(nameof(selector)); 15 | 16 | foreach (T item in control.Items) 17 | { 18 | var isMatch = selector(item); 19 | 20 | if (isMatch) 21 | return item; 22 | } 23 | 24 | return null; 25 | } 26 | 27 | public static T FindItemOrDefault(this ItemsControl control, Func selector, T defaultValue = default(T)) where T : class 28 | { 29 | var value = FindItem(control, selector); 30 | 31 | if (value == null) 32 | return defaultValue; 33 | else 34 | return value; 35 | } 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /docs/src/LICENSE.md: -------------------------------------------------------------------------------- 1 | # License 2 | 3 | The MIT License (MIT) 4 | 5 | Copyright (c) 2004-2023 Erik Rogers 6 | 7 | Permission is hereby granted, free of charge, to any person obtaining a copy 8 | of this software and associated documentation files (the "Software"), to deal 9 | in the Software without restriction, including without limitation the rights 10 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 11 | copies of the Software, and to permit persons to whom the Software is 12 | furnished to do so, subject to the following conditions: 13 | 14 | The above copyright notice and this permission notice shall be included in all 15 | copies or substantial portions of the Software. 16 | 17 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 18 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 19 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 20 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 21 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 22 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 23 | SOFTWARE. 24 | -------------------------------------------------------------------------------- /SleepHunter/Extensions/CharacterExtensions.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Extensions 3 | { 4 | public static class CharacterExtensions 5 | { 6 | public static bool IsValidHexDigit(this char c, bool allowControl = true) 7 | { 8 | var isControl = char.IsControl(c); 9 | var isDigit = char.IsDigit(c); 10 | var isHexDigit = (c == 'a' || c == 'A') || 11 | (c == 'b' || c == 'B') || 12 | (c == 'c' || c == 'C') || 13 | (c == 'd' || c == 'D') || 14 | (c == 'e' || c == 'E') || 15 | (c == 'f' || c == 'F'); 16 | 17 | var isHex = isDigit || isHexDigit; 18 | 19 | if (allowControl) 20 | isHex |= isControl; 21 | 22 | return isHex; 23 | } 24 | 25 | public static bool IsValidDecimalCharacter(this char c, bool allowControl = true) 26 | { 27 | var isControl = char.IsControl(c); 28 | var isDigit = char.IsDigit(c); 29 | 30 | var isDec = isDigit; 31 | 32 | if (allowControl) 33 | isDec |= isControl; 34 | 35 | return isDec; 36 | } 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /SleepHunter/Templates/ColorThemeDataTemplate.xaml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 16 | 17 | 18 | 24 | 25 | 26 | 27 | -------------------------------------------------------------------------------- /docs/src/settings/all-macros.md: -------------------------------------------------------------------------------- 1 | # All Macro Settings 2 | 3 | ![image](../screenshots/settings-all-macros.png) 4 | 5 | The `All Macros` settings tab contains settings for all macros, both skills and spells. 6 | 7 | ## Action on Map Change 8 | 9 | This setting determines what happens when the character changes maps. 10 | By default, this is `Stop Macro`. 11 | 12 | This is typically used to automatically stop macroing when the character leaves the Dojo. 13 | 14 | ## Action on X/Y Change 15 | 16 | This setting determines what happens when the character changes X/Y coordinates. 17 | By default, this is `Continue Macro`. 18 | 19 | This is typically used to automatically stop macroing when the character is thrown or otherwise moved. 20 | 21 | ## Medenia Panel Switching 22 | 23 | This setting determines how the Medenia panel switching feature should be used within the game client. 24 | By default, this is `Shift-Click`. 25 | 26 | If you have a different in-game setting, you can change this to match. 27 | 28 | ## Preserve Selected Panel 29 | 30 | This setting determines whether the currently selected panel in the game client should be preserved while macroing. 31 | By default, this is `Enabled`. 32 | 33 | For example, if the chat window is up it will be set back to that while macroing to avoid having to click on it again. 34 | -------------------------------------------------------------------------------- /SleepHunter/Converters/FeatureVisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Linq; 4 | using System.Windows; 5 | using System.Windows.Data; 6 | using SleepHunter.Settings; 7 | 8 | namespace SleepHunter.Converters 9 | { 10 | public sealed class FeatureVisibilityConverter : IValueConverter 11 | { 12 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 13 | { 14 | if (value is not ClientVersion version) 15 | throw new ArgumentException("Value must be a ClientVersion instance", nameof(value)); 16 | 17 | if (parameter is not string flagKey) 18 | throw new ArgumentException("Feature flag key is required as a parameter", nameof(parameter)); 19 | 20 | if (version == null || string.IsNullOrWhiteSpace(flagKey)) 21 | return Visibility.Collapsed; 22 | 23 | var flag = version.Features.FirstOrDefault(flag => flag.IsEnabled && string.Equals(flagKey, flag.Key, StringComparison.OrdinalIgnoreCase)); 24 | return flag != null ? Visibility.Visible : Visibility.Collapsed; 25 | } 26 | 27 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException(); 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /docs/src/main-window/character-list.md: -------------------------------------------------------------------------------- 1 | # Character List 2 | 3 | ![image](../screenshots/character-list.png) 4 | 5 | Shows information about the character, including current location, health, mana, and activity status (when macroing). 6 | The selected character will be displayed with a highlighted indicator on the left side of the list item. 7 | 8 | The main window title will also display the name of the selected character. 9 | 10 | ## Quick Select 11 | 12 | Double-clicking on a character will bring that Dark Ages game client window to the foreground. 13 | This is useful when you are trying to find that particular game client window that is hidden behind other windows. 14 | 15 | ## Hotkey Binding 16 | You can also bind a hotkey combination to a character by selecting the character and pressing the hotkey combination (ex: `Ctrl+1`). 17 | This will act as a global hotkey that can be used to pause/resume macroing for that particular character. 18 | 19 | If a hotkey is bound to a character, the hotkey combination will be displayed in the character window. 20 | You can unbind a hotkey by selecting the character and pressing the `Delete` or `Backspace` key. 21 | 22 | ## Character Sorting 23 | 24 | By default, characters are sorted by their login time (oldest to newest). 25 | This sorting can be modified in the [User Interface Settings](../settings/user-interface.md) window. 26 | -------------------------------------------------------------------------------- /SleepHunter/Win32/SystemInfo.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.InteropServices; 3 | 4 | namespace SleepHunter.Win32 5 | { 6 | [StructLayout(LayoutKind.Sequential)] 7 | internal readonly struct SystemInfo 8 | { 9 | private readonly uint oemId; 10 | private readonly uint pageSize; 11 | private readonly nint minimumApplicationAddress; 12 | private readonly nint maximumApplicationAddress; 13 | private readonly nint activeProcessorMask; 14 | private readonly uint processorType; 15 | private readonly uint allocationGranularity; 16 | private readonly ushort processorLevel; 17 | private readonly ushort processorRevision; 18 | 19 | public readonly uint OemId => oemId; 20 | 21 | public readonly uint PageSize => pageSize; 22 | public readonly nint MinimumApplicationAddress => minimumApplicationAddress; 23 | public readonly nint MaximumApplicationAddress => maximumApplicationAddress; 24 | 25 | public readonly nint ActiveProcessorMask => activeProcessorMask; 26 | public readonly uint ProcessorType => processorType; 27 | public readonly uint AllocationGranularity => allocationGranularity; 28 | public readonly ushort ProcessorLevel => processorLevel; 29 | public readonly ushort ProcessorRevision => processorRevision; 30 | } 31 | } 32 | -------------------------------------------------------------------------------- /docs/src/main-window/skills-tab.md: -------------------------------------------------------------------------------- 1 | # Skills Tab 2 | 3 | ![image](../screenshots/skills-tab.png) 4 | 5 | Skills are arranged as a grid of icon buttons similar to the game client, displaying the current level of the skill and the cooldown status (if on cooldown). 6 | 7 | ## Skill Organization 8 | 9 | Skills are organized into 3 tabs: 10 | 11 | - `Temuair` - skills learned from your original class path 12 | - `Medenia` - skills learned from your Medenian class path 13 | - `World` - skills learned for world-related activities (`Look`, `Swimming`, etc.) 14 | 15 | ## Toggling Skills 16 | 17 | Double-clicking a skill will toggle it on or off for macroing. 18 | When a skill is "enabled" for macroing, the icon will be highlighted white and a thick border will be displayed around the skill. 19 | 20 | ## Cooldown Display 21 | 22 | When a skill is on cooldown it will be shaded the theme color to indicate that it is not available for use. 23 | 24 | ## Grid Layout 25 | 26 | By default, skills are arranged with 12 skills per row. 27 | You can change the number of skills displayed per row in the [Settings](../settings.md) window. 28 | 29 | ## Tooltip Help 30 | 31 | You can mouse over a skill to see the tooltip for that skill. 32 | 33 | ## Additional Settings 34 | 35 | Additional settings for the `Skills` tab can be found in the [Skill Macros Settings](../settings/skill-macros.md) window. 36 | -------------------------------------------------------------------------------- /SleepHunter/Settings/UserSetting.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Xml.Serialization; 4 | 5 | using SleepHunter.Common; 6 | 7 | namespace SleepHunter.Settings 8 | { 9 | [Serializable] 10 | public sealed class UserSetting : ObservableObject 11 | { 12 | private string key; 13 | private string displayText; 14 | private object value; 15 | 16 | [XmlAttribute("Key")] 17 | public string Key 18 | { 19 | get => key; 20 | set => SetProperty(ref key, value); 21 | } 22 | 23 | [XmlIgnore] 24 | public string DisplayText 25 | { 26 | get => displayText; 27 | set => SetProperty(ref displayText, value); 28 | } 29 | 30 | [XmlAttribute("Value")] 31 | [DefaultValue(null)] 32 | public object Value 33 | { 34 | get => value; 35 | set => SetProperty(ref this.value, value); 36 | } 37 | 38 | // Needed for XAML -- Do not remove! 39 | public UserSetting() { } 40 | 41 | public UserSetting(string key, string displayText, object value = null) 42 | { 43 | this.key = key; 44 | this.displayText = displayText; 45 | this.value = value; 46 | } 47 | 48 | public override string ToString() => DisplayText ?? Key; 49 | } 50 | } 51 | -------------------------------------------------------------------------------- /SleepHunter/IO/Process/SearchMemoryVariable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Xml.Serialization; 4 | 5 | namespace SleepHunter.IO.Process 6 | { 7 | [Serializable] 8 | public class SearchMemoryVariable : DynamicMemoryVariable 9 | { 10 | protected MemoryOffset offset = new(); 11 | 12 | [XmlIgnore] 13 | public MemoryOffset Offset 14 | { 15 | get => offset; 16 | private set => offset = value; 17 | } 18 | 19 | [XmlAttribute("Offset")] 20 | public string OffsetString 21 | { 22 | get => offset.OffsetHex; 23 | set => offset.OffsetHex = value; 24 | } 25 | 26 | [XmlAttribute("IsNegative")] 27 | [DefaultValue(false)] 28 | public bool IsOffsetNegative 29 | { 30 | get => offset.IsNegative; 31 | set => offset.IsNegative = value; 32 | } 33 | 34 | public SearchMemoryVariable() 35 | : this(null, 0, 0, false) { } 36 | 37 | public SearchMemoryVariable(string key, long address, long offset, bool isNegative = false, int maxLength = 0, int size = 0, int count = 0, params long[] offsets) 38 | : base(key, address, maxLength, size, count, offsets) 39 | { 40 | Offset.Offset = offset; 41 | Offset.IsNegative = isNegative; 42 | } 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /docs/src/main-window/spells-tab.md: -------------------------------------------------------------------------------- 1 | # Spells Tab 2 | 3 | ![image](../screenshots/spells-tab.png) 4 | 5 | Spells are arranged as a grid of icon buttons similar to the game client, displaying the current level of the spell and the cooldown status (if on cooldown). 6 | 7 | ## Spell Organization 8 | 9 | Spells are organized into 3 tabs: 10 | 11 | - `Temuair` - spells learned from your original class path 12 | - `Medenia` - spells learned from your Medenian class path 13 | - `World` - spells learned for world-related activities (`nis`, etc.) 14 | 15 | ## Queueing Spells 16 | 17 | Double-clicking a spell will bring up the `Spell Target` dialog for that casting spell. 18 | This dialog allows you to select a target for the spell and add it to the [Spell Queue](./spell-queue.md). 19 | 20 | When a spell is being cast, it will be highlighted white and a thick border will be displayed around the spell icon. 21 | 22 | For more information about the `Spell Target` dialog, see the sidebar. 23 | 24 | ## Grid Layout 25 | 26 | By default, spells are arranged with 12 spells per row. 27 | You can change the number of spells displayed per row in the [Settings](../settings.md) window. 28 | 29 | ## Tooltip Help 30 | 31 | You can mouse over a spell to see the tooltip for that spell. 32 | 33 | ## Additional Settings 34 | 35 | Additional settings for spell macros can be found in the [Spell Macros Settings](../settings/spell-macros.md) window. 36 | -------------------------------------------------------------------------------- /SleepHunter/Services/ServiceCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | 4 | namespace SleepHunter.Services 5 | { 6 | public class ServiceCollection 7 | { 8 | private readonly Dictionary typeMappings = new Dictionary(); 9 | 10 | public void AddSingleton() where T : class 11 | => AddServiceMapping(typeof(T), typeof(T), ServiceLifetime.Singleton); 12 | 13 | public void AddSingleton() where T : class, TInterface 14 | => AddServiceMapping(typeof(TInterface), typeof(T), ServiceLifetime.Singleton); 15 | 16 | public void AddTransient() where T : class 17 | => AddServiceMapping(typeof(T), typeof(T), ServiceLifetime.Transient); 18 | 19 | public void AddTransient() where T : class, TInterface 20 | => AddServiceMapping(typeof(TInterface), typeof(T), ServiceLifetime.Transient); 21 | 22 | void AddServiceMapping(Type type, Type actualType, ServiceLifetime lifetime) 23 | { 24 | if (typeMappings.ContainsKey(type)) 25 | throw new InvalidOperationException($"{type.Name} is already registered"); 26 | 27 | typeMappings.Add(type, new ServiceMapping(actualType, lifetime)); 28 | } 29 | 30 | public IServiceProvider BuildServiceProvider() => new ServiceProvider(typeMappings); 31 | } 32 | } 33 | -------------------------------------------------------------------------------- /SleepHunter/IO/Process/MemoryOffset.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Globalization; 4 | using System.Xml.Serialization; 5 | 6 | namespace SleepHunter.IO.Process 7 | { 8 | [Serializable] 9 | public sealed class MemoryOffset 10 | { 11 | private long offset; 12 | private bool isNegative; 13 | 14 | [XmlIgnore] 15 | public long Offset 16 | { 17 | get => offset; 18 | set 19 | { 20 | isNegative = value < 0; 21 | offset = Math.Abs(value); 22 | } 23 | } 24 | 25 | [XmlAttribute("Value")] 26 | public string OffsetHex 27 | { 28 | get => offset.ToString("X"); 29 | set 30 | { 31 | if (long.TryParse(value, NumberStyles.HexNumber, null, out var parsedLong)) 32 | offset = parsedLong; 33 | } 34 | } 35 | 36 | [XmlAttribute("IsNegative")] 37 | [DefaultValue(false)] 38 | public bool IsNegative 39 | { 40 | get => isNegative; 41 | set => isNegative = value; 42 | } 43 | 44 | public MemoryOffset() 45 | : this(0) { } 46 | 47 | public MemoryOffset(long offset) 48 | { 49 | Offset = offset; 50 | } 51 | 52 | public override string ToString() => OffsetHex; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /docs/src/settings/game-client.md: -------------------------------------------------------------------------------- 1 | # Game Client Settings 2 | 3 | ![image](../screenshots/settings-game-client.png) 4 | 5 | The `Game Client` settings tab contains settings for the Dark Ages game client. 6 | 7 | ## Client Path 8 | 9 | This setting determines the path to the Dark Ages game client executable. 10 | It is also used to determine the path to the Dark Ages game client data files for rendering icons. 11 | 12 | ## Client Version 13 | 14 | This setting determines the version of the Dark Ages game client and how any runtime patches should be applied. 15 | The default is `Auto-Detect` and will automatically detect the version of the Dark Ages game client based on the client signature. 16 | 17 | You should not change this unless you are using a custom Dark Ages game client. 18 | 19 | ## Allow Multiple Instances 20 | 21 | This setting determines when the "multiple instances" patch should be applied when the Dark Ages game client is started. 22 | By default, this is `Enabled`. 23 | 24 | ## Skip Intro Video 25 | 26 | This setting determines when the "skip intro video" patch should be applied when the Dark Ages game client is started. 27 | By default, this is `Enabled`. 28 | 29 | ## No Foreground Walls 30 | 31 | This setting determines when the "no foreground walls" patch should be applied when the Dark Ages game client is started. 32 | By default, this is `Disabled`. 33 | 34 | This is useful when trying to find items that are hidden behind walls. 35 | -------------------------------------------------------------------------------- /docs/src/spell-target/relative-tile-area.md: -------------------------------------------------------------------------------- 1 | # Relative Tile Area Target 2 | 3 | ![image](../screenshots/spell-target-relative-tile-area.png) 4 | 5 | This will cast the spell in an circular area relative to the character's current position. 6 | The spell will be cast on the tiles in a clockwise order. 7 | 8 | ![image](../screenshots/tile-radius-example.png) 9 | 10 | In the above example, the `Inner Radius` is set to **2**, and the `Outer Radius` is set to **4**. 11 | The dead-zone is the area in the center of the circle, where no spells will be cast. 12 | The solid blue tiles are the tiles that will be selected for casting the spell, in clockwise order. 13 | 14 | This can be useful in some instances, such as periodically casting a spell on targets within an area without knowing their exact location. 15 | 16 | ## Options 17 | 18 | - `Relative Tile` - the tile relative to your character's current position. This will be the center of the circular region. 19 | - `Inner Radius` - the inner radius of the circular region. This is the dead-zone, where no spells will be cast. 20 | - `Outer Radius` - the outer radius of the circular region. This is the maximum distance from the center where spells will be cast. 21 | - `Mouse Offset X/Y` - offsets the mouse screen coordinates by the specified amount. 22 | - `Cast Spell Until Reaches` - sets the maximum level before this spell will no longer be cast in the [Spell Queue](../user-interface/main-window.md#spell-queue). 23 | -------------------------------------------------------------------------------- /SleepHunter/Metadata/ComputedSpellLines.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Concurrent; 3 | using System.Collections.Generic; 4 | using System.Linq; 5 | 6 | namespace SleepHunter.Metadata 7 | { 8 | public sealed class ComputedSpellLines 9 | { 10 | private readonly ConcurrentDictionary spellLines = new(StringComparer.OrdinalIgnoreCase); 11 | 12 | public int SpellCount => spellLines.Count; 13 | 14 | public IEnumerable> SpellLines => from s in spellLines select s; 15 | 16 | public void SetLines(string spellName, int lines) 17 | { 18 | spellName = spellName.Trim(); 19 | spellLines[spellName] = lines; 20 | } 21 | 22 | public bool ContainsLines(string spellName) 23 | { 24 | spellName = spellName.Trim(); 25 | return spellLines.ContainsKey(spellName); 26 | } 27 | 28 | public int? GetLines(string spellName) 29 | { 30 | spellName = spellName.Trim(); 31 | 32 | if (!spellLines.TryGetValue(spellName, out var lines)) 33 | return null; 34 | 35 | return lines; 36 | } 37 | 38 | public bool RemoveLines(string spellName) 39 | { 40 | spellName = spellName.Trim(); 41 | return spellLines.TryRemove(spellName, out _); 42 | } 43 | 44 | public void ClearLines() => spellLines.Clear(); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /docs/src/flower-target/relative-tile-area.md: -------------------------------------------------------------------------------- 1 | # Relative Tile Area Flower 2 | 3 | ![image](../screenshots/flower-target-relative-tile-area.png) 4 | 5 | This will cast the `Lyliac Plant` spell in an circular area relative to the character's current position. 6 | The spell will be cast on the tiles in a clockwise order. 7 | 8 | ![image](../screenshots/tile-radius-example.png) 9 | 10 | In the above example, the `Inner Radius` is set to **2**, and the `Outer Radius` is set to **4**. 11 | The dead-zone is the area in the center of the circle, where no flower will be cast. 12 | The solid blue tiles are the tiles that will be selected for casting flower, in clockwise order. 13 | 14 | This can be useful in some instances, such as periodically casting flower on targets within an area without knowing their exact location. 15 | 16 | ## Options 17 | 18 | - `Relative Tile` - the tile relative to your character's current position. This will be the center of the circular region. 19 | - `Inner Radius` - the inner radius of the circular region. This is the dead-zone, where no flower will be cast. 20 | - `Outer Radius` - the outer radius of the circular region. This is the maximum distance from the center where flower will be cast. 21 | - `Mouse Offset X/Y` - offsets the mouse screen coordinates by the specified amount. 22 | - `Flower Interval` - the time interval between casting flower on the same tile area. 23 | 24 | **NOTE:** The `Flower Threshold` option is not available for this target type. 25 | -------------------------------------------------------------------------------- /SleepHunter/Metadata/StaffMetadataCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Xml.Serialization; 5 | 6 | namespace SleepHunter.Metadata 7 | { 8 | [Serializable] 9 | [XmlRoot("StaffMetadata")] 10 | public sealed class StaffMetadataCollection 11 | { 12 | private string version; 13 | private List staves; 14 | 15 | [XmlAttribute("FileVersion")] 16 | [DefaultValue(null)] 17 | public string Version 18 | { 19 | get => version; 20 | set => version = value; 21 | } 22 | 23 | [XmlArray("Staves")] 24 | [XmlArrayItem("Staff")] 25 | public List Staves 26 | { 27 | get => staves; 28 | private set => staves = value; 29 | } 30 | 31 | [XmlIgnore] 32 | public int Count => staves.Count; 33 | 34 | public StaffMetadataCollection() 35 | { 36 | staves = new List(); 37 | } 38 | 39 | public StaffMetadataCollection(int capacity) 40 | { 41 | staves = new List(capacity); 42 | } 43 | 44 | public StaffMetadataCollection(IEnumerable collection) 45 | { 46 | staves = new List(collection); 47 | } 48 | 49 | public override string ToString() => $"Count = {Count}"; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /SleepHunter/Extensions/BinaryReaderExtensions.cs: -------------------------------------------------------------------------------- 1 | using System.IO; 2 | using System.Text; 3 | 4 | namespace SleepHunter.Extensions 5 | { 6 | public static class BinaryReaderExtensions 7 | { 8 | public static string ReadFixedString(this BinaryReader reader, int length) 9 | { 10 | var byteBuffer = new byte[length]; 11 | var asciiBuffer = new char[length]; 12 | var stringBuffer = new StringBuilder(length); 13 | 14 | reader.Read(byteBuffer, 0, length); 15 | int charCount = Encoding.ASCII.GetChars(byteBuffer, 0, length, asciiBuffer, 0); 16 | 17 | for (int i = 0; i < charCount; i++) 18 | { 19 | var c = asciiBuffer[i]; 20 | 21 | if (c == '\0') 22 | break; 23 | 24 | stringBuffer.Append(c); 25 | } 26 | 27 | return stringBuffer.ToString(); 28 | } 29 | 30 | public static string ReadNullTerminatedString(this BinaryReader reader, int maxLength = -1) 31 | { 32 | var buffer = new StringBuilder(256); 33 | 34 | var c = reader.ReadChar(); 35 | 36 | while (c != '\0') 37 | { 38 | buffer.Append(c); 39 | 40 | if (buffer.Length >= maxLength && maxLength > 0) 41 | break; 42 | 43 | c = reader.ReadChar(); 44 | } 45 | 46 | return buffer.ToString(); 47 | } 48 | } 49 | } 50 | -------------------------------------------------------------------------------- /docs/src/main-window/spell-queue.md: -------------------------------------------------------------------------------- 1 | # Spell Queue 2 | 3 | ![image](../screenshots/spell-queue.png) 4 | 5 | Shows the queued targets for casting spells. 6 | 7 | The user-selected spell in the queue will have a left-side highlight indicator. 8 | While being cast, the spell will be highlighted white and a thick border will be displayed around the spell. 9 | 10 | ## Spell Rotation 11 | 12 | Each character can have their own spell queue rotation mode set in the `Spell Queue`. 13 | This setting is preserved with their macro state when saved. 14 | 15 | ## Progress Display 16 | 17 | If set to only cast to a maximum level, the progress bar will be displayed. 18 | Once the spell reaches the maximum level desired, it will be ignored while in the queue. 19 | 20 | ## Modifying Spell Targets 21 | 22 | Double-clicking a spell will bring up the `Spell Target` dialog for modifying the cast target. 23 | You can re-arrange the order of the targets by dragging and dropping them. 24 | 25 | ## Removing Spell Targets 26 | 27 | Targets can be removed from the queue by clicking the `Remove` or `Clear All` buttons at the bottom. 28 | Alternatively, you can select a target and press the `Delete` or `Backspace` key. 29 | 30 | ## Show/Hide Spell Queue 31 | 32 | The Spell Queue visibility can be toggled using the show/hide button. 33 | It will be shown when adding a new spell target for a character. 34 | 35 | ## Additional Settings 36 | 37 | Additional settings for the `Spell Queue` can be found in the [Spell Macros Settings](../settings/spell-macros.md) window. -------------------------------------------------------------------------------- /SleepHunter/Metadata/SkillMetadataCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Xml.Serialization; 5 | 6 | namespace SleepHunter.Metadata 7 | { 8 | [Serializable] 9 | [XmlRoot("SkillMetadata")] 10 | public sealed class SkillMetadataCollection 11 | { 12 | private string version; 13 | private List skills; 14 | 15 | [XmlAttribute("FileVersion")] 16 | [DefaultValue(null)] 17 | public string Version 18 | { 19 | get => version; 20 | set => version = value; 21 | } 22 | 23 | [XmlIgnore] 24 | public int Count => skills.Count; 25 | 26 | [XmlArray("Skills")] 27 | [XmlArrayItem("Skill")] 28 | public List Skills 29 | { 30 | get => skills; 31 | private set => skills = value; 32 | } 33 | 34 | public SkillMetadataCollection() 35 | { 36 | skills = new List(); 37 | } 38 | 39 | public SkillMetadataCollection(int capacity) 40 | { 41 | skills = new List(capacity); 42 | } 43 | 44 | public SkillMetadataCollection(IEnumerable collection) 45 | : this() 46 | { 47 | if (collection != null) 48 | skills.AddRange(collection); 49 | } 50 | 51 | public override string ToString() => $"Count = {Count}"; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /SleepHunter/Metadata/SpellMetadataCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Xml.Serialization; 5 | 6 | namespace SleepHunter.Metadata 7 | { 8 | [Serializable] 9 | [XmlRoot("SpellMetadata")] 10 | public sealed class SpellMetadataCollection 11 | { 12 | private string version; 13 | private List spells; 14 | 15 | [XmlAttribute("FileVersion")] 16 | [DefaultValue(null)] 17 | public string Version 18 | { 19 | get => version; 20 | set => version = value; 21 | } 22 | 23 | [XmlIgnore] 24 | public int Count => spells.Count; 25 | 26 | [XmlArray("Spells")] 27 | [XmlArrayItem("Spell")] 28 | public List Spells 29 | { 30 | get => spells; 31 | private set => spells = value; 32 | } 33 | 34 | public SpellMetadataCollection() 35 | { 36 | spells = new List(); 37 | } 38 | 39 | public SpellMetadataCollection(int capacity) 40 | { 41 | spells = new List(capacity); 42 | } 43 | 44 | public SpellMetadataCollection(IEnumerable collection) 45 | : this() 46 | { 47 | if (collection != null) 48 | spells.AddRange(collection); 49 | } 50 | 51 | public override string ToString() => $"Count = {Count}"; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /docs/src/spell-target/absolute-tile-area.md: -------------------------------------------------------------------------------- 1 | # Absolute Tile Area Target 2 | 3 | ![image](../screenshots/spell-target-absolute-tile-area.png) 4 | 5 | This will cast the spell in an circular area around a location on the map, regardless of the character's current position. 6 | The spell will be cast on the tiles in a clockwise order. 7 | 8 | ![image](../screenshots/tile-radius-example.png) 9 | 10 | In the above example, the `Inner Radius` is set to **2**, and the `Outer Radius` is set to **4**. 11 | The dead-zone is the area in the center of the circle, where no spells will be cast. 12 | The solid blue tiles are the tiles that will be selected for casting the spell, in clockwise order. 13 | 14 | This can be useful in some instances, such as periodically casting a spell on targets within an area without worrying about the character's current position. 15 | 16 | ## Options 17 | 18 | - `Absolute Tile` - the tile on the map to cast the spell on. It is **not** relative to your character's current position. This will be the center of the circular region. 19 | - `Inner Radius` - the inner radius of the circular region. This is the dead-zone, where no spells will be cast. 20 | - `Outer Radius` - the outer radius of the circular region. This is the maximum distance from the center where spells will be cast. 21 | - `Mouse Offset X/Y` - offsets the mouse screen coordinates by the specified amount. 22 | - `Cast Spell Until Reaches` - sets the maximum level before this spell will no longer be cast in the [Spell Queue](../user-interface/main-window.md#spell-queue). 23 | -------------------------------------------------------------------------------- /SleepHunter/Extensions/WindowExtensions.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using SleepHunter.Views; 3 | using System.Windows; 4 | 5 | namespace SleepHunter.Extensions 6 | { 7 | public static class WindowExtensions 8 | { 9 | public static bool? ShowMessageBox(this Window owner, string windowTitle, string messageText, string subText = null, MessageBoxButton buttons = MessageBoxButton.OK, int width = 420, int height = 280) 10 | { 11 | if (owner == null) 12 | throw new ArgumentNullException(nameof(owner)); 13 | 14 | var messageBox = new MessageBoxWindow 15 | { 16 | Title = windowTitle ?? string.Empty, 17 | Width = width, 18 | Height = height, 19 | MessageText = messageText ?? string.Empty, 20 | SubText = subText ?? string.Empty 21 | }; 22 | 23 | if (buttons == MessageBoxButton.OK) 24 | { 25 | messageBox.CancelButtonColumnWidth = new GridLength(1, GridUnitType.Auto); 26 | messageBox.CancelButtonVisibility = Visibility.Collapsed; 27 | } 28 | 29 | if (buttons.HasFlag(MessageBoxButton.YesNo)) 30 | { 31 | messageBox.OkButtonText = "_Yes"; 32 | messageBox.CancelButtonText = "_No"; 33 | } 34 | 35 | if (!owner.IsLoaded) 36 | owner.Show(); 37 | 38 | messageBox.Owner = owner; 39 | 40 | return messageBox.ShowDialog(); 41 | } 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /SleepHunter/Services/Serialization/SerializedSpellState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Xml.Serialization; 4 | using SleepHunter.Models; 5 | 6 | namespace SleepHunter.Services.Serialization 7 | { 8 | [Serializable] 9 | public sealed class SerializedSpellState 10 | { 11 | [XmlAttribute("Name")] 12 | public string SpellName { get; set; } 13 | 14 | [XmlAttribute("Mode")] 15 | public SpellTargetMode TargetMode { get; set; } 16 | 17 | [XmlAttribute("Target")] 18 | [DefaultValue(null)] 19 | public string TargetName { get; set; } 20 | 21 | [XmlAttribute("X")] 22 | [DefaultValue(0)] 23 | public double LocationX { get; set; } 24 | 25 | [XmlAttribute("Y")] 26 | [DefaultValue(0)] 27 | public double LocationY { get; set; } 28 | 29 | [XmlAttribute("OffsetX")] 30 | [DefaultValue(0)] 31 | public double OffsetX { get; set; } 32 | 33 | [XmlAttribute("OffsetY")] 34 | [DefaultValue(0)] 35 | public double OffsetY { get; set; } 36 | 37 | [XmlAttribute("InnerRadius")] 38 | [DefaultValue(0)] 39 | public int InnerRadius { get; set; } 40 | 41 | [XmlAttribute("OuterRadius")] 42 | [DefaultValue(0)] 43 | public int OuterRadius { get; set; } 44 | 45 | [XmlAttribute("TargetLevel")] 46 | [DefaultValue(0)] 47 | public int TargetLevel { get; set; } 48 | 49 | public override string ToString() => SpellName; 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /SleepHunter/Settings/ColorThemeCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Xml.Serialization; 5 | 6 | namespace SleepHunter.Settings 7 | { 8 | [Serializable] 9 | [XmlRoot("ColorThemes")] 10 | public sealed class ColorThemeCollection 11 | { 12 | private string version; 13 | private List themes; 14 | 15 | [XmlAttribute("FileVersion")] 16 | [DefaultValue(null)] 17 | public string Version 18 | { 19 | get => version; 20 | set => version = value; 21 | } 22 | 23 | [XmlIgnore] 24 | public int Count => themes.Count; 25 | 26 | [XmlArray("Themes")] 27 | [XmlArrayItem("Theme")] 28 | public List Themes 29 | { 30 | get => themes; 31 | private set => themes = value; 32 | } 33 | 34 | public ColorThemeCollection() 35 | { 36 | themes = new List(); 37 | } 38 | 39 | public ColorThemeCollection(int capacity) 40 | { 41 | themes = new List(capacity); 42 | } 43 | 44 | public ColorThemeCollection(IEnumerable collection) 45 | { 46 | if (collection == null) 47 | throw new ArgumentNullException(nameof(collection)); 48 | 49 | themes = new List(collection); 50 | } 51 | 52 | public override string ToString() => $"Count = {Count}"; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /docs/src/flower-target/absolute-tile-area.md: -------------------------------------------------------------------------------- 1 | # Absolute Tile Area Flower 2 | 3 | ![image](../screenshots/flower-target-absolute-tile-area.png) 4 | 5 | This will cast the `Lyliac Plant` spell in an circular area around a location on the map, regardless of the character's current position. 6 | The spell will be cast on the tiles in a clockwise order. 7 | 8 | ![image](../screenshots/tile-radius-example.png) 9 | 10 | In the above example, the `Inner Radius` is set to **2**, and the `Outer Radius` is set to **4**. 11 | The dead-zone is the area in the center of the circle, where no flower will be cast. 12 | The solid blue tiles are the tiles that will be selected for casting flower, in clockwise order. 13 | 14 | This can be useful in some instances, such as periodically casting flower on targets within an area without worrying about the character's current position. 15 | 16 | ## Options 17 | 18 | - `Absolute Tile` - the tile on the map to cast flower on. It is **not** relative to your character's current position. This will be the center of the circular region. 19 | - `Inner Radius` - the inner radius of the circular region. This is the dead-zone, where no flower will be cast. 20 | - `Outer Radius` - the outer radius of the circular region. This is the maximum distance from the center where flower will be cast. 21 | - `Mouse Offset X/Y` - offsets the mouse screen coordinates by the specified amount. 22 | - `Flower Interval` - the time interval between casting flower on the same tile area. 23 | 24 | **NOTE:** The `Flower Threshold` option is not available for this target type. 25 | -------------------------------------------------------------------------------- /SleepHunter/Models/Skill.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Models 3 | { 4 | public sealed class Skill : Ability 5 | { 6 | private bool isAssail; 7 | private bool opensDialog; 8 | private bool requiresDisarm; 9 | private double? minHealthPercent; 10 | private double? maxHealthPercent; 11 | 12 | public bool IsAssail 13 | { 14 | get => isAssail; 15 | set => SetProperty(ref isAssail, value); 16 | } 17 | 18 | public bool OpensDialog 19 | { 20 | get => opensDialog; 21 | set => SetProperty(ref opensDialog, value); 22 | } 23 | 24 | public bool RequiresDisarm 25 | { 26 | get => requiresDisarm; 27 | set => SetProperty(ref requiresDisarm, value); 28 | } 29 | 30 | public double? MinHealthPercent 31 | { 32 | get => minHealthPercent; 33 | set => SetProperty(ref minHealthPercent, value); 34 | } 35 | 36 | public double? MaxHealthPercent 37 | { 38 | get => maxHealthPercent; 39 | set => SetProperty(ref maxHealthPercent, value); 40 | } 41 | 42 | public static Skill MakeEmpty(int slot) 43 | { 44 | return new Skill 45 | { 46 | Slot = slot, 47 | Panel = GetSkillPanelForSlot(slot), 48 | IsEmpty = true 49 | }; 50 | } 51 | 52 | public override string ToString() => Name ?? "Unknown Skill"; 53 | } 54 | 55 | } 56 | -------------------------------------------------------------------------------- /SleepHunter/Settings/ClientSignature.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Globalization; 4 | using System.Xml.Serialization; 5 | using SleepHunter.Common; 6 | 7 | namespace SleepHunter.Settings 8 | { 9 | [Serializable] 10 | public sealed class ClientSignature : ObservableObject 11 | { 12 | private long address; 13 | private string value; 14 | 15 | [XmlIgnore] 16 | public long Address 17 | { 18 | get => address; 19 | set => SetProperty(ref address, value, onChanged: (s) => { RaisePropertyChanged(nameof(AddressHex)); }); 20 | } 21 | 22 | [XmlAttribute("Address")] 23 | [DefaultValue("0")] 24 | public string AddressHex 25 | { 26 | get => address.ToString("X"); 27 | set 28 | { 29 | if (long.TryParse(value, NumberStyles.HexNumber, null, out var parsedLong)) 30 | address = parsedLong; 31 | } 32 | } 33 | 34 | [XmlAttribute] 35 | [DefaultValue("")] 36 | public string Value 37 | { 38 | get => value; 39 | set => SetProperty(ref this.value, value); 40 | } 41 | 42 | public ClientSignature() 43 | : this(0, string.Empty) { } 44 | 45 | public ClientSignature(long address, string value) 46 | { 47 | Address = address; 48 | Value = value; 49 | } 50 | 51 | public override string ToString() => $"{AddressHex} = {Value}"; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /SleepHunter/Templates/StringDataTemplate.xaml: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 32 | 33 | 34 | 35 | -------------------------------------------------------------------------------- /SleepHunter/Settings/ClientVersionCollection.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Xml.Serialization; 5 | 6 | namespace SleepHunter.Settings 7 | { 8 | [Serializable] 9 | [XmlRoot("ClientVersions")] 10 | public sealed class ClientVersionCollection 11 | { 12 | private string version; 13 | private List versions; 14 | 15 | [XmlAttribute("FileVersion")] 16 | [DefaultValue(null)] 17 | public string Version 18 | { 19 | get => version; 20 | set => version = value; 21 | } 22 | 23 | [XmlIgnore] 24 | public int Count => versions.Count; 25 | 26 | [XmlArray("Clients")] 27 | [XmlArrayItem("Client")] 28 | public List Versions 29 | { 30 | get => versions; 31 | private set => versions = value; 32 | } 33 | 34 | public ClientVersionCollection() 35 | { 36 | versions = new List(); 37 | } 38 | 39 | public ClientVersionCollection(int capacity) 40 | { 41 | versions = new List(capacity); 42 | } 43 | 44 | public ClientVersionCollection(IEnumerable collection) 45 | { 46 | if (collection == null) 47 | throw new ArgumentNullException(nameof(collection)); 48 | 49 | versions = new List(collection); 50 | } 51 | 52 | public override string ToString() => $"Count = {Count}"; 53 | } 54 | } 55 | -------------------------------------------------------------------------------- /SleepHunter/Common/UpdatableObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | 3 | namespace SleepHunter.Common 4 | { 5 | public abstract class UpdatableObject : ObservableObject, IDisposable 6 | { 7 | protected bool isDisposed; 8 | 9 | public event EventHandler Updated; 10 | 11 | public void Update() 12 | { 13 | CheckIfDisposed(); 14 | 15 | OnUpdate(); 16 | RaiseUpdated(); 17 | } 18 | 19 | public bool TryUpdate() 20 | { 21 | CheckIfDisposed(); 22 | 23 | try 24 | { 25 | Update(); 26 | return true; 27 | } 28 | catch 29 | { 30 | return false; 31 | } 32 | } 33 | 34 | 35 | ~UpdatableObject() => Dispose(false); 36 | 37 | public void RaiseUpdated() 38 | { 39 | CheckIfDisposed(); 40 | Updated?.Invoke(this, EventArgs.Empty); 41 | } 42 | 43 | public void Dispose() 44 | { 45 | Dispose(true); 46 | GC.SuppressFinalize(this); 47 | } 48 | 49 | protected virtual void Dispose(bool isDisposing) 50 | { 51 | if (isDisposed) 52 | return; 53 | 54 | if (isDisposing) 55 | { 56 | 57 | } 58 | 59 | isDisposed = true; 60 | } 61 | 62 | protected abstract void OnUpdate(); 63 | 64 | protected void CheckIfDisposed() 65 | { 66 | if (isDisposed) 67 | throw new ObjectDisposedException(GetType().Name); 68 | } 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /SleepHunter/Win32/MemoryBasicInformation.cs: -------------------------------------------------------------------------------- 1 | using System.Runtime.InteropServices; 2 | 3 | namespace SleepHunter.Win32 4 | { 5 | [StructLayout(LayoutKind.Sequential)] 6 | internal struct MemoryBasicInformation 7 | { 8 | private nint baseAddress; 9 | private nint allocationBase; 10 | private VirtualMemoryProtection allocationProtect; 11 | private nint regionSize; 12 | private VirtualMemoryStatus state; 13 | private VirtualMemoryProtection protect; 14 | private VirtualMemoryType type; 15 | 16 | public nint BaseAddress 17 | { 18 | readonly get => baseAddress; 19 | set => baseAddress = value; 20 | } 21 | 22 | public nint AllocationBase 23 | { 24 | readonly get => allocationBase; 25 | set => allocationBase = value; 26 | } 27 | 28 | public VirtualMemoryProtection AllocationProtect 29 | { 30 | readonly get => allocationProtect; 31 | set => allocationProtect = value; 32 | } 33 | 34 | public nint RegionSize 35 | { 36 | readonly get => regionSize; 37 | set => regionSize = value; 38 | } 39 | 40 | public VirtualMemoryStatus State 41 | { 42 | readonly get => state; 43 | set => state = value; 44 | } 45 | 46 | public VirtualMemoryProtection Protect 47 | { 48 | readonly get => protect; 49 | set => protect = value; 50 | } 51 | 52 | public VirtualMemoryType Type 53 | { 54 | readonly get => type; 55 | set => type = value; 56 | } 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /SleepHunter/SleepHunter.sln: -------------------------------------------------------------------------------- 1 | 2 | Microsoft Visual Studio Solution File, Format Version 12.00 3 | # Visual Studio Version 17 4 | VisualStudioVersion = 17.5.33516.290 5 | MinimumVisualStudioVersion = 10.0.40219.1 6 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SleepHunter", "SleepHunter.csproj", "{9B964768-970E-4190-8574-EBEF7BE09E7E}" 7 | EndProject 8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SleepHunter.Updater", "..\SleepHunter.Updater\SleepHunter.Updater.csproj", "{344FCCBB-ECF5-4C77-BB34-079B98928752}" 9 | EndProject 10 | Global 11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution 12 | Debug|Any CPU = Debug|Any CPU 13 | Release|Any CPU = Release|Any CPU 14 | EndGlobalSection 15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution 16 | {9B964768-970E-4190-8574-EBEF7BE09E7E}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 17 | {9B964768-970E-4190-8574-EBEF7BE09E7E}.Debug|Any CPU.Build.0 = Debug|Any CPU 18 | {9B964768-970E-4190-8574-EBEF7BE09E7E}.Release|Any CPU.ActiveCfg = Release|Any CPU 19 | {9B964768-970E-4190-8574-EBEF7BE09E7E}.Release|Any CPU.Build.0 = Release|Any CPU 20 | {344FCCBB-ECF5-4C77-BB34-079B98928752}.Debug|Any CPU.ActiveCfg = Debug|Any CPU 21 | {344FCCBB-ECF5-4C77-BB34-079B98928752}.Debug|Any CPU.Build.0 = Debug|Any CPU 22 | {344FCCBB-ECF5-4C77-BB34-079B98928752}.Release|Any CPU.ActiveCfg = Release|Any CPU 23 | {344FCCBB-ECF5-4C77-BB34-079B98928752}.Release|Any CPU.Build.0 = Release|Any CPU 24 | EndGlobalSection 25 | GlobalSection(SolutionProperties) = preSolution 26 | HideSolutionNode = FALSE 27 | EndGlobalSection 28 | GlobalSection(ExtensibilityGlobals) = postSolution 29 | SolutionGuid = {DEDE977A-35A4-420E-B7E0-9581D43A88D4} 30 | EndGlobalSection 31 | EndGlobal 32 | -------------------------------------------------------------------------------- /SleepHunter.Updater/SleepHunter.Updater.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0-windows7.0 4 | WinExe 5 | true 6 | Updater 7 | true 8 | true 9 | true 10 | true 11 | 12 | 13 | SleepHunter.Updater.App 14 | 15 | 16 | SleepHunter-Updater.ico 17 | SleepHunter Updater 18 | 2025 Erik 'SiLo' Rogers 19 | https://github.com/ewrogers/SleepHunter4 20 | https://github.com/ewrogers/SleepHunter4 21 | git 22 | Auto-Update utility for SleepHunter 23 | 1.1.3.0 24 | 1.1.3.0 25 | x64 26 | 7.0 27 | 28 | 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /docs/src/main-window/flowering-tab.md: -------------------------------------------------------------------------------- 1 | # Flowering Tab 2 | 3 | ![image](../screenshots/flowering-tab.png) 4 | 5 | Shows the targets for casting `Lyliac Plant` and option to enable `Lyliac Vineyard` (if available). 6 | 7 | The user-selected flower target in the queue will have a left-side highlight indicator. 8 | 9 | **NOTE:** The `Flowering` tab is only available if the `Lyliac Plant` or `Lyliac Vineyard` spells are available. 10 | 11 | ## Queueing Flower Targets 12 | 13 | The `Add New` button at the bottom will bring up the `Flower Target` dialog for flowering. 14 | This dialog allows you to select a target for the spell and add it to the `Flower Queue`. 15 | 16 | The `Flower Queue` is the list contained in the `Flowering` tab itself. 17 | Here all targets for `Lyliac Planet` will be displayed, with the condition set as well as a countdown timer. 18 | 19 | For more information about the `Flower Target` dialog, see the sidebar. 20 | 21 | ## Modifying Flower Targets 22 | 23 | Double-clicking a flower target will bring up the `Flower Target` dialog for modifying the cast target. 24 | You can re-arrange the order of the targets by dragging and dropping them. 25 | 26 | ## Flowering Timer 27 | 28 | While the character macro is running, the timer will tick down and show the remaining time. 29 | When the timer reaches zero, the target will be flowered (when possible), and timer reset. 30 | 31 | ## Removing Flower Targets 32 | 33 | Targets can be removed from the queue by clicking the `Remove` or `Clear All` buttons at the bottom. 34 | Alternatively, you can select a target and press the `Delete` or `Backspace` key. 35 | 36 | ## Additional Settings 37 | 38 | Additional settings for the `Flowering` tab can be found in the [Flowering Settings](../settings/flowering.md) window. 39 | -------------------------------------------------------------------------------- /SleepHunter/Models/Spell.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Models 3 | { 4 | public sealed class Spell : Ability 5 | { 6 | public const string LyliacPlantKey = @"Lyliac Plant"; 7 | public const string LyliacVineyardKey = @"Lyliac Vineyard"; 8 | public const string FasSpioradKey = @"Fas Spiorad"; 9 | 10 | private AbilityTargetType targetType; 11 | private string prompt; 12 | private bool opensDialog; 13 | private double? minHealthPercent; 14 | private double? maxHealthPercent; 15 | 16 | public AbilityTargetType TargetType 17 | { 18 | get { return targetType; } 19 | set { SetProperty(ref targetType, value); } 20 | } 21 | 22 | public string Prompt 23 | { 24 | get { return prompt; } 25 | set { SetProperty(ref prompt, value); } 26 | } 27 | 28 | public bool OpensDialog 29 | { 30 | get => opensDialog; 31 | set => SetProperty(ref opensDialog, value); 32 | } 33 | 34 | public double? MinHealthPercent 35 | { 36 | get => minHealthPercent; 37 | set => SetProperty(ref minHealthPercent, value); 38 | } 39 | 40 | public double? MaxHealthPercent 41 | { 42 | get => maxHealthPercent; 43 | set => SetProperty(ref maxHealthPercent, value); 44 | } 45 | 46 | public static Spell MakeEmpty(int slot) 47 | { 48 | return new Spell 49 | { 50 | Slot = slot, 51 | Panel = GetSpellPanelForSlot(slot), 52 | IsEmpty = true 53 | }; 54 | } 55 | 56 | public override string ToString() => Name ?? "Unknown Spell"; 57 | } 58 | } 59 | -------------------------------------------------------------------------------- /SleepHunter/SleepHunter.csproj: -------------------------------------------------------------------------------- 1 | 2 | 3 | net9.0-windows7.0 4 | WinExe 5 | true 6 | true 7 | true 8 | true 9 | true 10 | 11 | 12 | SleepHunter.ico 13 | 14 | 15 | SleepHunter.App 16 | 17 | 18 | true 19 | git 20 | https://github.com/ewrogers/SleepHunter4 21 | https://github.com/ewrogers/SleepHunter4 22 | 2025 Erik 'SiLo' Rogers 23 | Dark Ages Automation Tool 24 | SleepHunter 25 | 4.10.3.0 26 | 4.10.3.0 27 | x64 28 | 7.0 29 | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /SleepHunter/Converters/EquipmentSlotConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows.Data; 4 | using SleepHunter.Models; 5 | 6 | namespace SleepHunter.Converters 7 | { 8 | public sealed class EquipmentSlotConverter : IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | var intValue = System.Convert.ToInt32(value); 13 | var slot = (EquipmentSlot)(intValue - 1); 14 | 15 | return slot switch 16 | { 17 | EquipmentSlot.Weapon => "Main Hand", 18 | EquipmentSlot.Shield => "Off-Hand", 19 | EquipmentSlot.Armor => "Armor", 20 | EquipmentSlot.Helmet => "Helm", 21 | EquipmentSlot.Earring => "Ear", 22 | EquipmentSlot.Necklace => "Neck", 23 | EquipmentSlot.LeftRing => "Left Finger", 24 | EquipmentSlot.RightRing => "Right Finger", 25 | EquipmentSlot.LeftGauntlet => "Left Arm", 26 | EquipmentSlot.RightGauntlet => "Right Arm", 27 | EquipmentSlot.Belt => "Belt", 28 | EquipmentSlot.Greaves => "Legs", 29 | EquipmentSlot.Boots => "Feet", 30 | EquipmentSlot.Overcoat => "Overcoat", 31 | EquipmentSlot.Hat => "Head Accessory", 32 | EquipmentSlot.Accessory1 => "Accessory 1", 33 | EquipmentSlot.Accessory2 => "Accessory 2", 34 | EquipmentSlot.Accessory3 => "Accessory 3", 35 | _ => "Unknown" 36 | }; 37 | } 38 | 39 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) => throw new NotSupportedException(); 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /SleepHunter/Services/Serialization/SerializedFlowerState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using SleepHunter.Models; 4 | using System.Xml.Serialization; 5 | 6 | namespace SleepHunter.Services.Serialization 7 | { 8 | [Serializable] 9 | public sealed class SerializedFlowerState 10 | { 11 | [XmlAttribute("Mode")] 12 | public SpellTargetMode TargetMode { get; set; } 13 | 14 | [XmlAttribute("Target")] 15 | [DefaultValue(null)] 16 | public string TargetName { get; set; } 17 | 18 | [XmlAttribute("X")] 19 | [DefaultValue(0)] 20 | public double LocationX { get; set; } 21 | 22 | [XmlAttribute("Y")] 23 | [DefaultValue(0)] 24 | public double LocationY { get; set; } 25 | 26 | [XmlAttribute("OffsetX")] 27 | [DefaultValue(0)] 28 | public double OffsetX { get; set; } 29 | 30 | [XmlAttribute("OffsetY")] 31 | [DefaultValue(0)] 32 | public double OffsetY { get; set; } 33 | 34 | [XmlAttribute("InnerRadius")] 35 | [DefaultValue(0)] 36 | public int InnerRadius { get; set; } 37 | 38 | [XmlAttribute("OuterRadius")] 39 | [DefaultValue(0)] 40 | public int OuterRadius { get; set; } 41 | 42 | [XmlIgnore] 43 | public TimeSpan Interval { get; set; } 44 | 45 | [XmlAttribute("HasInterval")] 46 | [DefaultValue(true)] 47 | public bool HasInterval { get; set; } 48 | 49 | [XmlAttribute("Interval")] 50 | [DefaultValue(0.0)] 51 | public double IntervalSeconds 52 | { 53 | get => Interval.TotalSeconds; 54 | set => Interval = TimeSpan.FromSeconds(value); 55 | } 56 | 57 | [XmlAttribute("IfManaLessThan")] 58 | [DefaultValue(0)] 59 | public int ManaThreshold { get; set; } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /SleepHunter/Converters/VisibilityConverter.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Globalization; 3 | using System.Windows; 4 | using System.Windows.Data; 5 | 6 | namespace SleepHunter.Converters 7 | { 8 | public class VisibilityConverter : IValueConverter 9 | { 10 | public object Convert(object value, Type targetType, object parameter, CultureInfo culture) 11 | { 12 | var parameterString = parameter as string; 13 | var isReversed = string.Equals("Reverse", parameterString, StringComparison.OrdinalIgnoreCase); 14 | 15 | if (value is string stringValue) 16 | return string.IsNullOrWhiteSpace(stringValue) ? Visibility.Collapsed : Visibility.Visible; 17 | 18 | if (value is Visibility visibility) 19 | { 20 | if (isReversed) 21 | return visibility == Visibility.Visible ? Visibility.Collapsed : Visibility.Visible; 22 | else 23 | return visibility; 24 | } 25 | 26 | var boolean = (bool)value; 27 | 28 | if (isReversed) 29 | boolean = !boolean; 30 | 31 | if (boolean) 32 | return Visibility.Visible; 33 | else 34 | { 35 | if (string.Equals("Collapse", parameterString, StringComparison.OrdinalIgnoreCase)) 36 | return Visibility.Collapsed; 37 | else 38 | return Visibility.Hidden; 39 | } 40 | } 41 | 42 | public object ConvertBack(object value, Type targetType, object parameter, CultureInfo culture) 43 | { 44 | var visibility = (Visibility)value; 45 | 46 | if (visibility == Visibility.Visible) 47 | return true; 48 | else 49 | return false; 50 | } 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /SleepHunter/IO/FileArchiveManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Concurrent; 4 | using System.IO; 5 | using System.Linq; 6 | 7 | namespace SleepHunter.IO 8 | { 9 | public sealed class FileArchiveManager 10 | { 11 | private static readonly FileArchiveManager instance = new(); 12 | 13 | public static FileArchiveManager Instance => instance; 14 | 15 | private FileArchiveManager() { } 16 | 17 | private readonly ConcurrentDictionary archives = new(StringComparer.OrdinalIgnoreCase); 18 | 19 | public int Count => archives.Count; 20 | 21 | public IEnumerable Archives => from a in archives.Values select a; 22 | 23 | public bool ContainsArchive(string filename) => archives.ContainsKey(filename); 24 | 25 | public FileArchive GetArchive(string filename) 26 | { 27 | if (archives.ContainsKey(filename)) 28 | return archives[filename]; 29 | 30 | if (!File.Exists(filename)) 31 | return null; 32 | 33 | try 34 | { 35 | var archive = new FileArchive(filename); 36 | archives[filename] = archive; 37 | 38 | return archive; 39 | } 40 | catch { return null; } 41 | } 42 | 43 | public bool RemoveArchive(string filename) 44 | { 45 | if (!archives.ContainsKey(filename)) 46 | return false; 47 | 48 | var archive = archives[filename]; 49 | archive.Dispose(); 50 | 51 | return archives.TryRemove(filename, out _); 52 | } 53 | 54 | public void ClearArchives() 55 | { 56 | foreach (var archive in archives.Values) 57 | archive.Dispose(); 58 | 59 | archives.Clear(); 60 | } 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /SleepHunter/Services/Serialization/SerializedMacroState.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Xml.Serialization; 5 | using SleepHunter.Macro; 6 | 7 | namespace SleepHunter.Services.Serialization 8 | { 9 | [Serializable] 10 | [XmlRoot("MacroState")] 11 | public sealed class SerializedMacroState 12 | { 13 | private const string SaveFileVersion = "4.10"; 14 | 15 | [XmlAttribute("Version")] 16 | public string Version { get; set; } = SaveFileVersion; 17 | 18 | [XmlElement("Name")] 19 | [DefaultValue(null)] 20 | public string Name { get; set; } 21 | 22 | [XmlElement("Description")] 23 | [DefaultValue(null)] 24 | public string Description { get; set; } 25 | 26 | [XmlElement("Hotkey")] 27 | [DefaultValue(null)] 28 | public SerializedHotkey Hotkey { get; set; } 29 | 30 | [XmlArray("Skills")] 31 | [XmlArrayItem("Skill")] 32 | public List Skills { get; set; } = new(); 33 | 34 | [XmlElement("SpellRotation")] 35 | [DefaultValue(SpellRotationMode.Default)] 36 | public SpellRotationMode SpellRotation { get; set; } 37 | 38 | [XmlArray("Spells")] 39 | [XmlArrayItem("Spell")] 40 | public List Spells { get; set; } = new(); 41 | 42 | [XmlElement("UseLyliacVineyard")] 43 | [DefaultValue(false)] 44 | public bool UseLyliacVineyard { get; set; } 45 | 46 | [XmlElement("FlowerAlternateCharacters")] 47 | [DefaultValue(false)] 48 | public bool FlowerAlternateCharacters { get; set; } 49 | 50 | [XmlArray("Flowering")] 51 | [XmlArrayItem("Flower")] 52 | public List FlowerTargets { get; set; } = new(); 53 | 54 | [XmlElement("LocalStorage")] 55 | public SerializedLocalStorage LocalStorage { get; set; } = new(); 56 | 57 | public override string ToString() => Name; 58 | } 59 | } 60 | -------------------------------------------------------------------------------- /SleepHunter/App.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Threading; 3 | using SleepHunter.Services; 4 | using SleepHunter.Services.Logging; 5 | using SleepHunter.Services.Releases; 6 | using SleepHunter.Services.Serialization; 7 | using SleepHunter.Views; 8 | 9 | namespace SleepHunter 10 | { 11 | public partial class App : Application 12 | { 13 | public const string USER_MANUAL_URL = @"https://ewrogers.github.io/SleepHunter4/"; 14 | 15 | private ILogger logger; 16 | 17 | public static new App Current => (App)Application.Current; 18 | 19 | public IServiceProvider Services { get; } 20 | 21 | public App() 22 | { 23 | Services = ConfigureServices(); 24 | InitializeComponent(); 25 | 26 | Current.Dispatcher.UnhandledException += Dispatcher_UnhandledException; 27 | } 28 | 29 | private void Dispatcher_UnhandledException(object sender, DispatcherUnhandledExceptionEventArgs e) 30 | { 31 | if (logger == null) 32 | logger = Services.GetService(); 33 | 34 | logger.LogError("Unhandled exception!"); 35 | logger.LogException(e.Exception); 36 | 37 | e.Handled = true; 38 | } 39 | 40 | protected override void OnStartup(StartupEventArgs e) 41 | { 42 | base.OnStartup(e); 43 | 44 | var mainWindow = new MainWindow(); 45 | mainWindow.Show(); 46 | } 47 | 48 | protected override void OnExit(ExitEventArgs e) 49 | { 50 | Services.Dispose(); 51 | base.OnExit(e); 52 | } 53 | 54 | private static IServiceProvider ConfigureServices() 55 | { 56 | var services = new ServiceCollection(); 57 | 58 | // Services 59 | services.AddSingleton(); 60 | services.AddSingleton(); 61 | 62 | services.AddTransient(); 63 | 64 | // ViewModels 65 | 66 | return services.BuildServiceProvider(); 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /SleepHunter/Converters/ByteStringConverter.cs: -------------------------------------------------------------------------------- 1 | 2 | namespace SleepHunter.Converters 3 | { 4 | public static class ByteStringConverter 5 | { 6 | private const long KilobyteCount = 1024; 7 | private const long MegabyteCount = 1024L * 1024; 8 | private const long GigabyteCount = 1024L * 1024L * 1024L; 9 | private const long TerabyteCount = 1024L * 1024U * 1024L * 1024L; 10 | 11 | private const string ByteSuffix = "bytes"; 12 | private const string KilobyteSuffix = "KB"; 13 | private const string MegabyteSuffix = "MB"; 14 | private const string GigabyteSuffix = "GB"; 15 | private const string TerabyteSuffix = "TB"; 16 | 17 | public static string GetEnglishString(double value, string format = null) 18 | { 19 | long divisor = GetDenominator(value, out var suffix); 20 | value /= divisor; 21 | 22 | if (format == null) 23 | return string.Format("{0} {1}", value.ToString(), suffix); 24 | else 25 | return string.Format("{0} {1}", value.ToString(format), suffix); 26 | } 27 | 28 | public static long GetDenominator(double value) => GetDenominator(value, out _); 29 | 30 | public static long GetDenominator(double value, out string denominatorString) 31 | { 32 | denominatorString = ByteSuffix; 33 | 34 | if (value >= TerabyteCount) 35 | { 36 | denominatorString = TerabyteSuffix; 37 | return TerabyteCount; 38 | } 39 | 40 | if (value >= GigabyteCount) 41 | { 42 | denominatorString = GigabyteSuffix; 43 | return GigabyteCount; 44 | } 45 | 46 | if (value >= MegabyteCount) 47 | { 48 | denominatorString = MegabyteSuffix; 49 | return MegabyteCount; 50 | } 51 | 52 | if (value >= KilobyteCount) 53 | { 54 | denominatorString = KilobyteSuffix; 55 | return KilobyteCount; 56 | } 57 | 58 | return 1; 59 | } 60 | } 61 | } 62 | -------------------------------------------------------------------------------- /SleepHunter/Models/InventoryItem.cs: -------------------------------------------------------------------------------- 1 | using System.Text.RegularExpressions; 2 | using System.Windows.Media; 3 | using SleepHunter.Common; 4 | 5 | namespace SleepHunter.Models 6 | { 7 | public sealed class InventoryItem : ObservableObject 8 | { 9 | private static readonly Regex ColorTextRegex = new(@"{=[a-z]", RegexOptions.IgnoreCase | RegexOptions.Compiled); 10 | 11 | private bool isEmpty; 12 | private int slot; 13 | private int iconIndex; 14 | private string name; 15 | private int quantity; 16 | private ImageSource icon; 17 | 18 | public bool IsEmpty 19 | { 20 | get => isEmpty; 21 | set => SetProperty(ref isEmpty, value); 22 | } 23 | 24 | public int Slot 25 | { 26 | get => slot; 27 | set => SetProperty(ref slot, value); 28 | } 29 | 30 | public int IconIndex 31 | { 32 | get => iconIndex; 33 | set => SetProperty(ref iconIndex, value); 34 | } 35 | 36 | public string Name 37 | { 38 | get => name; 39 | set => SetProperty(ref name, value, nameof(Name), (_) => RaisePropertyChanged(nameof(DisplayName))); 40 | } 41 | 42 | public string DisplayName => ColorTextRegex.Replace(Name ?? string.Empty, string.Empty); 43 | 44 | public int Quantity 45 | { 46 | get => quantity; 47 | set => SetProperty(ref quantity, value); 48 | } 49 | 50 | public ImageSource Icon 51 | { 52 | get => icon; 53 | set => SetProperty(ref icon, value); 54 | } 55 | 56 | private InventoryItem() { } 57 | 58 | public InventoryItem(int slot, string name, int iconIndex = 0, int quantity = 1) 59 | { 60 | this.slot = slot; 61 | this.name = name; 62 | this.iconIndex = iconIndex; 63 | this.quantity = quantity; 64 | 65 | isEmpty = false; 66 | } 67 | 68 | public override string ToString() => Name ?? "Unknown Item"; 69 | 70 | public static InventoryItem MakeEmpty(int slot) => new() { Slot = slot, IsEmpty = true, Quantity = 0 }; 71 | } 72 | } 73 | -------------------------------------------------------------------------------- /SleepHunter/IO/Process/MemoryVariable.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.Globalization; 4 | using System.Xml; 5 | using System.Xml.Serialization; 6 | 7 | namespace SleepHunter.IO.Process 8 | { 9 | [Serializable] 10 | public class MemoryVariable 11 | { 12 | protected string key; 13 | protected long address; 14 | protected int maxLength; 15 | protected int size; 16 | protected int count; 17 | 18 | [XmlAttribute("Key")] 19 | public string Key 20 | { 21 | get => key; 22 | set => key = value; 23 | } 24 | 25 | [XmlIgnore] 26 | public long Address 27 | { 28 | get => address; 29 | set => address = value; 30 | } 31 | 32 | [XmlAttribute("Address")] 33 | public string AddressHex 34 | { 35 | get => address.ToString("X"); 36 | set 37 | { 38 | if (long.TryParse(value, NumberStyles.HexNumber, null, out var parsedLong)) 39 | address = parsedLong; 40 | } 41 | } 42 | 43 | [XmlAttribute("MaxLength")] 44 | [DefaultValue(0)] 45 | public int MaxLength 46 | { 47 | get => maxLength; 48 | set => maxLength = value; 49 | } 50 | 51 | [XmlAttribute("Size")] 52 | [DefaultValue(0)] 53 | public int Size 54 | { 55 | get => size; 56 | set => size = value; 57 | } 58 | 59 | [XmlAttribute("Count")] 60 | [DefaultValue(0)] 61 | public int Count 62 | { 63 | get => count; 64 | set => count = value; 65 | } 66 | 67 | public MemoryVariable() 68 | : this(string.Empty, 0, 0) { } 69 | 70 | public MemoryVariable(string key, long address, int maxLength = 0, int size = 0, int count = 0) 71 | { 72 | this.key = key; 73 | this.address = address; 74 | this.maxLength = maxLength; 75 | this.size = size; 76 | this.count = count; 77 | } 78 | 79 | public override string ToString() => Key ?? string.Empty; 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /docs/src/metadata-editor/spells-editor.md: -------------------------------------------------------------------------------- 1 | # Spells Editor 2 | 3 | ![image](../screenshots/metadata-spells-editor.png) 4 | 5 | The `Spells` tab in the `Metadata Editor` allows you to edit the database of known spells. 6 | This allows SleepHunter to recognize spells and configure behaviors when using them. 7 | 8 | ### Adding a New Spell 9 | 10 | To add a new spell, click the `Add` button in the bottom bar. You will be prompted to enter the spell information. 11 | 12 | ### Editing a Spell 13 | 14 | To edit a spell, double-click the spell in the list or select it and click the `Edit` button in the bottom bar. 15 | You will be prompted to enter the spell information, similar to the `Add Spell` dialog. 16 | 17 | ### Removing a Spell 18 | 19 | To remove a spell, select it and click the `Remove` button in the bottom bar. 20 | 21 | ### Clearing All Spells 22 | 23 | To clear all spells, click the `Clear All` button in the bottom bar. 24 | 25 | ### Save Changes 26 | 27 | Save all changes to the database file. This will overwrite the existing file. 28 | 29 | ### Revert Changes 30 | 31 | Discard all changes and revert to the last saved state (from file). 32 | 33 | ## Add/Edit Spell Dialog 34 | 35 | ![image](../screenshots/metadata-spell-dialog.png) 36 | 37 | The `Spell` dialog allows you to enter the information for a spell, either for adding a new spell or editing an existing one. 38 | 39 | ### Spell Name 40 | 41 | The name of the spell. This is the name that will be displayed in the UI. 42 | 43 | ### Group Name 44 | 45 | The group name of the spell, used for categorizing spells for certain interactions like staff-switching. 46 | 47 | ### Mana Cost 48 | 49 | The mana cost of the spell, in MP. 50 | 51 | ### Cast Time 52 | 53 | The cast time of the spell, in lines. Each line is about one second. 54 | 55 | ### Cooldown 56 | 57 | The cooldown of the spell, in seconds (if applicable). A zero-cool down means the spell has no cooldown. 58 | 59 | ### Does Not Level 60 | 61 | Whether the spell does not increase on level. If this is set the spell level will not be displayed in the UI. 62 | 63 | ### Character Class 64 | 65 | The character classes that can use the spell. 66 | 67 | **NOTE:** This is currently unused but may be used in the future for certain behaviors. 68 | -------------------------------------------------------------------------------- /docs/src/SUMMARY.md: -------------------------------------------------------------------------------- 1 | # Summary 2 | 3 | # Overview 4 | 5 | - [Introduction](./introduction.md) 6 | - [History](./history.md) 7 | - [Features](./features.md) 8 | - [Installation](./installation.md) 9 | - [Getting Started](./getting-started.md) 10 | - [Release Notes](./CHANGELOG.md) 11 | - [License](./LICENSE.md) 12 | 13 | # Main Window 14 | 15 | - [Toolbar](./main-window/toolbar.md) 16 | - [Character List](./main-window/character-list.md) 17 | - [Items Tab](./main-window/items-tab.md) 18 | - [Skills Tab](./main-window/skills-tab.md) 19 | - [Spells Tab](./main-window/spells-tab.md) 20 | - [Flowering Tab](./main-window/flowering-tab.md) 21 | - [Features Tab](./main-window/features-tab.md) 22 | - [Spell Queue](./main-window/spell-queue.md) 23 | 24 | # Spell Targets 25 | 26 | - [No Target](./spell-target/no-target.md) 27 | - [Self Target](./spell-target/self.md) 28 | - [Alternate Character](./spell-target/alternate-character.md) 29 | - [Relative Tile](./spell-target/relative-tile.md) 30 | - [Relative Tile Area](./spell-target/relative-tile-area.md) 31 | - [Absolute Tile](./spell-target/absolute-tile.md) 32 | - [Absolute Tile Area](./spell-target/absolute-tile-area.md) 33 | - [Screen Position](./spell-target/screen-position.md) 34 | 35 | # Flower Targets 36 | 37 | - [Alternate Character](./flower-target/alternate-character.md) 38 | - [Relative Tile](./flower-target/relative-tile.md) 39 | - [Relative Tile Area](./flower-target/relative-tile-area.md) 40 | - [Absolute Tile](./flower-target/absolute-tile.md) 41 | - [Absolute Tile Area](./flower-target/absolute-tile-area.md) 42 | - [Screen Position](./flower-target/screen-position.md) 43 | 44 | # Settings 45 | 46 | - [General Settings](./settings/general.md) 47 | - [User Interface](./settings/user-interface.md) 48 | - [Game Client](./settings/game-client.md) 49 | - [All Macros](./settings/all-macros.md) 50 | - [Skill Macros](./settings/skill-macros.md) 51 | - [Spell Macros](./settings/spell-macros.md) 52 | - [Flowering](./settings/flowering.md) 53 | - [Updates](./settings/updates.md) 54 | - [Debug](./settings/debug.md) 55 | - [About](./settings/about.md) 56 | 57 | # Metadata Editor 58 | 59 | - [Skills Editor](./metadata-editor/skills-editor.md) 60 | - [Spells Editor](./metadata-editor/spells-editor.md) 61 | - [Staves Editor](./metadata-editor/staves-editor.md) 62 | 63 | -------------------------------------------------------------------------------- /SleepHunter/Common/DeferredDispatcher.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Threading; 4 | 5 | namespace SleepHunter.Common 6 | { 7 | public sealed class DeferredDispatcher 8 | { 9 | private readonly ReaderWriterLockSlim readerWriterLock = new(); 10 | private readonly List actions = new(); 11 | 12 | public DeferredDispatcher() { } 13 | 14 | public void DispatchAfter(Action action, TimeSpan delay) => DispatchAt(action, DateTime.Now + delay); 15 | 16 | public void DispatchAt(Action action, DateTime timestamp) 17 | { 18 | if (action == null) 19 | throw new ArgumentNullException(nameof(action)); 20 | 21 | readerWriterLock.EnterWriteLock(); 22 | 23 | try 24 | { 25 | var deferred = new DeferredAction(action, timestamp); 26 | actions.Add(deferred); 27 | } 28 | finally 29 | { 30 | readerWriterLock.ExitWriteLock(); 31 | } 32 | } 33 | 34 | public void Tick() 35 | { 36 | readerWriterLock.EnterUpgradeableReadLock(); 37 | 38 | try 39 | { 40 | for (var i = actions.Count - 1; i >= 0; i--) 41 | { 42 | var action = actions[i]; 43 | 44 | // If execution time is in the future, wait 45 | if (action.ExecutionTime > DateTime.Now) 46 | continue; 47 | 48 | // Remove the entry from the deferred action queue 49 | readerWriterLock.EnterWriteLock(); 50 | try 51 | { 52 | actions.RemoveAt(i); 53 | } 54 | finally 55 | { 56 | readerWriterLock.ExitWriteLock(); 57 | } 58 | 59 | // Perform the action (outside of the write lock) 60 | action.Action(); 61 | } 62 | } 63 | finally 64 | { 65 | readerWriterLock.ExitUpgradeableReadLock(); 66 | } 67 | } 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /docs/src/installation.md: -------------------------------------------------------------------------------- 1 | # Installation 2 | 3 | ## Requirements 4 | 5 | - [Dark Ages](https://www.darkages.com) Client 7.41 (current latest) 6 | - .NET 7.0 Runtime 7 | - Windows arm64 - [Download Link](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-7.0.5-windows-arm64-installer) 8 | - Windows x64 - [Download Link](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-7.0.5-windows-x64-installer) 9 | - Windows x86 - [Download Link](https://dotnet.microsoft.com/en-us/download/dotnet/thank-you/runtime-desktop-7.0.5-windows-x86-installer) 10 | - Windows 7, 10, 11 (64-bit) 11 | 12 | ## Installation 13 | 14 | 1. Download the latest version from the [Releases](https://github.com/ewrogers/SleepHunter4/releases) page. 15 | 2. Extract the `SleepHunter-4.x.x.zip` file to a folder of your choice. 16 | 3. Run `SleepHunter.exe` to start the application. 17 | 4. Configure your game client settings in the `Settings->GameClient` section. 18 | 5. Read the rest of this documentation to learn how to use the application. 19 | 20 | It is very important that you extract **all** of the files from the `SleepHunter-4.x.x.zip` file. 21 | 22 | ## Auto-Update 23 | 24 | As of version **4.1.0**, SleepHunter will automatically check for updates when it starts. 25 | It checks the same GitHub releases page that you downloaded the application from. 26 | 27 | If an update is available, you will be prompted to download and install it. 28 | SleepHunter will automatically close and restart after the update is applied. 29 | 30 | You can also manually check for updates by clicking the `Check for Updates` button in the `Settings->Updates` section. 31 | 32 | **NOTE:** Your `Settings.xml` file will **not** be overwritten when you update SleepHunter. 33 | 34 | ## Manual Update 35 | 36 | If you are unable to update SleepHunter automatically, you can manually download and install the latest version. 37 | Follow the same steps as the [Installation](#installation) section, but replace the existing files with the new ones. 38 | 39 | In most cases, you do not have to overwrite the `Settings.xml` file. 40 | 41 | ## Uninstall 42 | 43 | To uninstall SleepHunter, simply delete the folder that you extracted the application to. 44 | There are no registry entries or other files that need to be removed. 45 | 46 | -------------------------------------------------------------------------------- /SleepHunter/Common/ObservableObject.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.ComponentModel; 4 | using System.Runtime.CompilerServices; 5 | 6 | namespace SleepHunter.Common 7 | { 8 | public abstract class ObservableObject : INotifyPropertyChanged, INotifyPropertyChanging 9 | { 10 | public event PropertyChangedEventHandler PropertyChanged; 11 | public event PropertyChangingEventHandler PropertyChanging; 12 | 13 | protected ObservableObject() { } 14 | 15 | public void RaisePropertyChanged(string propertyName) 16 | { 17 | if (propertyName == null) 18 | throw new ArgumentNullException(nameof(propertyName)); 19 | 20 | OnPropertyChanged(propertyName); 21 | } 22 | 23 | public void RaisePropertyChanging(string propertyName) 24 | { 25 | if (propertyName == null) 26 | throw new ArgumentNullException(nameof(propertyName)); 27 | 28 | OnPropertyChanging(propertyName); 29 | } 30 | 31 | protected virtual bool SetProperty(ref T backingStore, 32 | T newValue, 33 | [CallerMemberName] string propertyName = "", 34 | Action onChanged = null, 35 | Action onChanging = null) 36 | { 37 | if (EqualityComparer.Default.Equals(backingStore, newValue)) 38 | return false; 39 | 40 | onChanging?.Invoke(newValue); 41 | OnPropertyChanging(propertyName); 42 | 43 | backingStore = newValue; 44 | 45 | onChanged?.Invoke(newValue); 46 | OnPropertyChanged(propertyName); 47 | return true; 48 | } 49 | 50 | #region INotifyPropertyChanged Methods 51 | protected virtual void OnPropertyChanged([CallerMemberName] string propertyName = "") 52 | { 53 | PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName)); 54 | } 55 | #endregion 56 | 57 | #region INotifyPropertyChanging Methods 58 | protected virtual void OnPropertyChanging([CallerMemberName] string propertyName = "") 59 | { 60 | PropertyChanging?.Invoke(this, new PropertyChangingEventArgs(propertyName)); 61 | } 62 | #endregion 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /SleepHunter/Threading/UITaskMethodBuilder.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Windows; 4 | using System.Windows.Threading; 5 | 6 | namespace SleepHunter.Threading 7 | { 8 | public sealed class UITaskMethodBuilder 9 | { 10 | private readonly Dispatcher dispatcher; 11 | 12 | public UITask Task { get; init; } = new UITask(); 13 | 14 | public UITaskMethodBuilder(Dispatcher dispatcher) 15 | { 16 | this.dispatcher = dispatcher ?? throw new ArgumentNullException(nameof(dispatcher)); 17 | } 18 | 19 | public void Start(ref TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine 20 | { 21 | if (!dispatcher.CheckAccess()) 22 | dispatcher.BeginInvoke(new Action(stateMachine.MoveNext)); 23 | else 24 | stateMachine.MoveNext(); 25 | } 26 | 27 | public static UITaskMethodBuilder Create() => new(Application.Current.Dispatcher); 28 | 29 | public void SetStateMachine(IAsyncStateMachine _) { } 30 | 31 | public void SetResult() => Task.SetResult(); 32 | 33 | public void SetException(Exception exception) => Task.SetException(exception); 34 | 35 | public void AwaitOnCompleted(ref TAwaiter awaiter, TStateMachine stateMachine) 36 | where TAwaiter: INotifyCompletion 37 | where TStateMachine : IAsyncStateMachine 38 | { 39 | awaiter.OnCompleted(ResumeAfterAwait(stateMachine)); 40 | } 41 | 42 | public void AwaitUnsafeOnCompleted(ref TAwaiter awaiter, TStateMachine stateMachine) 43 | where TAwaiter: ICriticalNotifyCompletion 44 | where TStateMachine: IAsyncStateMachine 45 | { 46 | awaiter.UnsafeOnCompleted(ResumeAfterAwait(stateMachine)); 47 | } 48 | 49 | private Action ResumeAfterAwait(TStateMachine stateMachine) where TStateMachine : IAsyncStateMachine 50 | { 51 | return () => 52 | { 53 | if (!dispatcher.CheckAccess()) 54 | dispatcher.BeginInvoke(new Action(stateMachine.MoveNext)); 55 | else 56 | stateMachine.MoveNext(); 57 | }; 58 | } 59 | } 60 | } 61 | -------------------------------------------------------------------------------- /docs/src/main-window/toolbar.md: -------------------------------------------------------------------------------- 1 | # Toolbar 2 | 3 | ![image](../screenshots/tool-bar.png) 4 | 5 | The toolbar has the following buttons: 6 | 7 | - [Launch Client](#launch-client) 8 | - [Load State](#load-state) 9 | - [Save State](#save-state) 10 | - [Start Macro](#start-macro) 11 | - [Pause Macro](#pause-macro) 12 | - [Stop Macro](#stop-macro) 13 | - [Stop All](#stop-all) 14 | - [Metadata Editor](#metadata-editor) 15 | - [App Settings](#app-settings). 16 | 17 | ## Launch Client 18 | 19 | This button will launch a new game client, applying any tweaks that are enabled in the [Settings](./settings.md#game-client) window. 20 | 21 | By default, game clients that are not actively "logged in" will **not** be displayed in the list. 22 | 23 | ## Load State 24 | 25 | This button will open a dialog to manually open the current character's state from a file. 26 | 27 | **NOTE:** Any skills/spells that are not available on the character will be ignored and not added to the queue. 28 | 29 | ## Save State 30 | 31 | This button will open a dialog to manually save the current character's state to a file. 32 | 33 | ## Start Macro 34 | 35 | This button will start macroing the selected character, performing any actions that they have queued. 36 | If paused, this will resume macroing the selected character. 37 | 38 | ## Pause Macro 39 | 40 | This button will pause macroing the selected character, retaining the current state of the macro. 41 | 42 | ## Stop Macro 43 | 44 | This button will stop macroing the selected character, resetting the macro state. 45 | 46 | ## Stop All 47 | 48 | This button will stop macroing on all characters, resetting the macro state. 49 | It is equivalent to clicking the `Stop Macro` button for each character. 50 | 51 | ### Pause vs Stop Macro 52 | 53 | The main difference between `Pause Macro` and `Stop Macro` is that pause acts as a temporary stop, while stop will reset the macro state for that character. 54 | You can notice this with certain timers like flower targets when you pause/resume versus stopping and re-starting. 55 | 56 | **NOTE:** This does **not** mean that your skills and spells will be removed from the queue, only that the macro state will be reset. 57 | 58 | ## Metadata Editor 59 | 60 | This button will open the [Metadata Editor](./metadata-editor.md) window. 61 | 62 | ## App Settings 63 | 64 | This button will open the [Settings](../settings/general.md) window. 65 | -------------------------------------------------------------------------------- /SleepHunter/Macro/MacroManager.cs: -------------------------------------------------------------------------------- 1 | using System.Collections.Concurrent; 2 | using System.Collections.Generic; 3 | using System.Linq; 4 | 5 | using SleepHunter.Models; 6 | 7 | namespace SleepHunter.Macro 8 | { 9 | public sealed class MacroManager 10 | { 11 | private static readonly MacroManager instance = new(); 12 | 13 | public static MacroManager Instance => instance; 14 | 15 | private MacroManager() { } 16 | 17 | private readonly ConcurrentDictionary clientMacros = new(); 18 | 19 | public int Count => clientMacros.Count; 20 | 21 | public IEnumerable Macros => from m in clientMacros.Values select m; 22 | 23 | public PlayerMacroState GetMacroState(Player player) 24 | { 25 | if (clientMacros.ContainsKey(player.Process.ProcessId)) 26 | return clientMacros[player.Process.ProcessId]; 27 | 28 | var state = new PlayerMacroState(player); 29 | clientMacros[player.Process.ProcessId] = state; 30 | 31 | return state; 32 | } 33 | 34 | public bool RemoveMacroState(int processId) 35 | { 36 | var wasRemoved = clientMacros.TryRemove(processId, out var removedClient); 37 | 38 | if (wasRemoved && removedClient != null) 39 | removedClient.Stop(); 40 | 41 | return wasRemoved; 42 | } 43 | 44 | public void ClearMacros() 45 | { 46 | foreach (var processId in clientMacros.Keys.ToArray()) 47 | RemoveMacroState(processId); 48 | 49 | clientMacros.Clear(); 50 | } 51 | 52 | public void StartAll() 53 | { 54 | foreach (var macro in clientMacros.Values) 55 | macro.Start(); 56 | } 57 | 58 | public void ResumeAll() 59 | { 60 | foreach (var macro in clientMacros.Values) 61 | if (macro.Status == MacroStatus.Paused) 62 | macro.Start(); 63 | } 64 | 65 | public void PauseAll() 66 | { 67 | foreach (var macro in clientMacros.Values) 68 | if (macro.Status == MacroStatus.Running) 69 | macro.Pause(); 70 | } 71 | 72 | public void StopAll() 73 | { 74 | foreach (var macro in clientMacros.Values) 75 | macro.Stop(); 76 | } 77 | } 78 | } 79 | -------------------------------------------------------------------------------- /SleepHunter/IO/Process/ProcessMemoryAccessor.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.ComponentModel; 3 | using System.IO; 4 | using System.Runtime.InteropServices; 5 | 6 | using SleepHunter.Win32; 7 | 8 | namespace SleepHunter.IO.Process 9 | { 10 | public sealed class ProcessMemoryAccessor : IDisposable 11 | { 12 | private bool isDisposed; 13 | private readonly int processId; 14 | private nint processHandle; 15 | private readonly ProcessAccess access; 16 | 17 | public int ProcessId => processId; 18 | public nint ProcessHandle => processHandle; 19 | public ProcessAccess Access => access; 20 | 21 | public ProcessMemoryAccessor(int processId, ProcessAccess access = ProcessAccess.ReadWrite) 22 | { 23 | this.processId = processId; 24 | this.access = access; 25 | 26 | processHandle = NativeMethods.OpenProcess(access.ToWin32Flags(), false, processId); 27 | 28 | if (processHandle == 0) 29 | throw new Win32Exception(Marshal.GetLastPInvokeError(), $"Unable to open process {processId}: {Marshal.GetLastPInvokeErrorMessage()}"); 30 | } 31 | 32 | public Stream GetStream() 33 | { 34 | CheckIfDisposed(); 35 | return new ProcessMemoryStream(processHandle, ProcessAccess.Read, leaveOpen: true); 36 | } 37 | 38 | public Stream GetWriteableStream() 39 | { 40 | CheckIfDisposed(); 41 | 42 | if (!access.HasFlag(ProcessAccess.Write)) 43 | throw new InvalidOperationException("Accessor is not writeable"); 44 | 45 | return new ProcessMemoryStream(processHandle, ProcessAccess.ReadWrite, leaveOpen: true); 46 | } 47 | 48 | ~ProcessMemoryAccessor() => Dispose(false); 49 | 50 | public void Dispose() 51 | { 52 | Dispose(true); 53 | GC.SuppressFinalize(this); 54 | } 55 | 56 | private void Dispose(bool isDisposing) 57 | { 58 | if (isDisposed) 59 | return; 60 | 61 | if (isDisposing) 62 | { 63 | // Do any additional cleanup of managed resources here 64 | } 65 | 66 | if (processHandle != 0) 67 | NativeMethods.CloseHandle(processHandle); 68 | 69 | processHandle = 0; 70 | isDisposed = true; 71 | } 72 | 73 | private void CheckIfDisposed() 74 | { 75 | if (isDisposed) 76 | throw new ObjectDisposedException(GetType().Name); 77 | } 78 | } 79 | } 80 | -------------------------------------------------------------------------------- /SleepHunter/Media/ColorPalette.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections; 3 | using System.Collections.Generic; 4 | using System.IO; 5 | using System.Windows.Media; 6 | 7 | namespace SleepHunter.Media 8 | { 9 | public sealed class ColorPalette : IEnumerable 10 | { 11 | public const int ColorCount = 256; 12 | 13 | private readonly List colors; 14 | 15 | public string Name { get; set; } 16 | 17 | public int Count => colors.Count; 18 | 19 | public ColorPalette(string filename) 20 | : this(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read), leaveOpen: false) 21 | { 22 | Name = filename; 23 | } 24 | 25 | public Color this[int index] 26 | { 27 | get => colors[index]; 28 | set => colors[index] = value; 29 | } 30 | 31 | public ColorPalette(Stream stream, bool leaveOpen = true) 32 | { 33 | colors = new List(ColorCount); 34 | 35 | for (int i = 0; i < ColorCount; i++) 36 | { 37 | var red = (byte)stream.ReadByte(); 38 | var green = (byte)stream.ReadByte(); 39 | var blue = (byte)stream.ReadByte(); 40 | 41 | var color = Color.FromRgb(red, green, blue); 42 | colors.Add(color); 43 | } 44 | 45 | if (!leaveOpen) 46 | stream.Dispose(); 47 | } 48 | 49 | public ColorPalette(IEnumerable collection) 50 | { 51 | if (collection != null) 52 | colors = new List(collection); 53 | else 54 | colors = new List(); 55 | } 56 | 57 | public ColorPalette MakeGrayscale(double redWeight = 0.30, double greenWeight = 0.59, double blueWeight = 0.11) 58 | { 59 | var grayColors = new List(Count); 60 | 61 | foreach (var color in this) 62 | { 63 | var luma = color.R * redWeight + color.G * greenWeight + color.B * blueWeight; 64 | luma = Math.Min(luma, 255); 65 | 66 | var gray = Color.FromRgb((byte)luma, (byte)luma, (byte)luma); 67 | grayColors.Add(gray); 68 | } 69 | 70 | return new ColorPalette(grayColors); 71 | } 72 | 73 | public IEnumerator GetEnumerator() 74 | { 75 | foreach (var color in colors) 76 | yield return color; 77 | } 78 | 79 | IEnumerator IEnumerable.GetEnumerator() => GetEnumerator(); 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /docs/src/metadata-editor/skills-editor.md: -------------------------------------------------------------------------------- 1 | # Skills Editor 2 | 3 | ![image](../screenshots/metadata-skills-editor.png) 4 | 5 | The `Skills` tab in the `Metadata Editor` allows you to edit the database of known skills. 6 | This allows SleepHunter to recognize skills and configure behaviors when using them. 7 | 8 | ### Adding a New Skill 9 | 10 | To add a new skill, click the `Add` button in the bottom bar. You will be prompted to enter the skill information. 11 | 12 | ### Editing a Skill 13 | 14 | To edit a skill, double-click the skill in the list or select it and click the `Edit` button in the bottom bar. 15 | You will be prompted to enter the skill information, similar to the `Add Skill` dialog. 16 | 17 | ### Removing a Skill 18 | 19 | To remove a skill, select it and click the `Remove` button in the bottom bar. 20 | 21 | ### Clearing All Skills 22 | 23 | To clear all skills, click the `Clear All` button in the bottom bar. 24 | 25 | ### Save Changes 26 | 27 | Save all changes to the database file. This will overwrite the existing file. 28 | 29 | ### Revert Changes 30 | 31 | Discard all changes and revert to the last saved state (from file). 32 | 33 | ## Add/Edit Skill Dialog 34 | 35 | ![image](../screenshots/metadata-skill-dialog.png) 36 | 37 | The `Skill` dialog allows you to enter the information for a skill, either for adding a new skill or editing an existing one. 38 | 39 | ### Skill Name 40 | 41 | The name of the skill. This is the name that will be displayed in the UI. 42 | 43 | ### Group Name 44 | 45 | The group name of the skill, used for categorizing skills for certain interactions. 46 | 47 | ### Mana Cost 48 | 49 | The mana cost of the skill, in MP. 50 | 51 | ### Cooldown 52 | 53 | The cooldown of the skill, in seconds (if applicable). A zero-cool down means the skill has no cooldown. 54 | 55 | ### Opens Dialog on Use 56 | 57 | Whether the skill opens a dialog when used. This is used to determine if the popup should be dismissed to continue macroing. 58 | For example `Peek`, `Sense`, `Martial Awareness`, etc. 59 | 60 | ### Does Not Level 61 | 62 | Whether the skill does not increase on level. If this is set the skill level will not be displayed in the UI. 63 | 64 | ### Is an Assail Skill 65 | 66 | Whether the skill is an `Assail` skill. This is used to determine if the skill should be treated as an `Assail` skill for certain behaviors, like space-bar performing. 67 | 68 | ### Disarm Before Using 69 | 70 | Whether the character should disarm before using the skill. This is useful when using skills like monk kicks. 71 | 72 | ### Character Class 73 | 74 | The character classes that can use the skill. 75 | 76 | **NOTE:** This is currently unused but may be used in the future for certain behaviors. 77 | -------------------------------------------------------------------------------- /SleepHunter/Templates/ToolTipTemplate.xaml: -------------------------------------------------------------------------------- 1 | 2 | 3 | 45 | -------------------------------------------------------------------------------- /SleepHunter/Media/HueSaturationValue.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Runtime.CompilerServices; 3 | using System.Windows.Media; 4 | 5 | namespace SleepHunter.Media 6 | { 7 | [Serializable] 8 | public struct HueSaturationValue 9 | { 10 | [NonSerialized] 11 | Color color; 12 | 13 | private double hue; 14 | private double saturation; 15 | private double value; 16 | 17 | public Color Color => color; 18 | public double Hue => hue; 19 | public double Saturation => saturation; 20 | public double Value => value; 21 | 22 | public HueSaturationValue(Color color) 23 | : this() 24 | { 25 | this.color = color; 26 | CalculateHSV(); 27 | } 28 | 29 | private void CalculateHSV() 30 | { 31 | var rgbMax = (double)Math.Max(Math.Max(color.R, color.G), color.B); 32 | var rgbMin = (double)Math.Min(Math.Min(color.R, color.G), color.B); 33 | 34 | value = rgbMax; 35 | 36 | if (value == 0) 37 | { 38 | hue = 0; 39 | saturation = 0; 40 | return; 41 | } 42 | 43 | var r = color.R / rgbMax; 44 | var g = color.G / rgbMax; 45 | var b = color.B / rgbMax; 46 | 47 | rgbMax = Max3(r, g, b); 48 | rgbMin = Min3(r, g, b); 49 | 50 | saturation = rgbMax - rgbMin; 51 | 52 | r = (r - rgbMin) / (rgbMax - rgbMin); 53 | g = (g - rgbMin) / (rgbMax - rgbMin); 54 | b = (b - rgbMin) / (rgbMax - rgbMin); 55 | 56 | rgbMax = Max3(r, g, b); 57 | rgbMin = Min3(r, g, b); 58 | 59 | if (rgbMax == r) 60 | { 61 | hue = 60 * (g - b); 62 | 63 | while (hue < 0) 64 | hue += 360; 65 | } 66 | else if (rgbMax == g) 67 | { 68 | hue = 120 + 60 * (b - r); 69 | } 70 | else if (rgbMax == b) 71 | { 72 | hue = 240 + 60 * (r - g); 73 | } 74 | } 75 | 76 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 77 | private static double Max3(double a, double b, double c) 78 | { 79 | return (b >= c) ? 80 | (a >= b) ? a : b : 81 | (a >= c) ? a : c; 82 | } 83 | 84 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 85 | private static double Min3(double a, double b, double c) 86 | { 87 | return (b <= c) ? 88 | (a <= b) ? a : b : 89 | (a <= c) ? a : c; 90 | } 91 | } 92 | } 93 | -------------------------------------------------------------------------------- /.gitattributes: -------------------------------------------------------------------------------- 1 | ############################################################################### 2 | # Set default behavior to automatically normalize line endings. 3 | ############################################################################### 4 | * text=auto 5 | 6 | ############################################################################### 7 | # Set default behavior for command prompt diff. 8 | # 9 | # This is need for earlier builds of msysgit that does not have it on by 10 | # default for csharp files. 11 | # Note: This is only used by command line 12 | ############################################################################### 13 | #*.cs diff=csharp 14 | 15 | ############################################################################### 16 | # Set the merge driver for project and solution files 17 | # 18 | # Merging from the command prompt will add diff markers to the files if there 19 | # are conflicts (Merging from VS is not affected by the settings below, in VS 20 | # the diff markers are never inserted). Diff markers may cause the following 21 | # file extensions to fail to load in VS. An alternative would be to treat 22 | # these files as binary and thus will always conflict and require user 23 | # intervention with every merge. To do so, just uncomment the entries below 24 | ############################################################################### 25 | #*.sln merge=binary 26 | #*.csproj merge=binary 27 | #*.vbproj merge=binary 28 | #*.vcxproj merge=binary 29 | #*.vcproj merge=binary 30 | #*.dbproj merge=binary 31 | #*.fsproj merge=binary 32 | #*.lsproj merge=binary 33 | #*.wixproj merge=binary 34 | #*.modelproj merge=binary 35 | #*.sqlproj merge=binary 36 | #*.wwaproj merge=binary 37 | 38 | ############################################################################### 39 | # behavior for image files 40 | # 41 | # image files are treated as binary by default. 42 | ############################################################################### 43 | #*.jpg binary 44 | #*.png binary 45 | #*.gif binary 46 | 47 | ############################################################################### 48 | # diff behavior for common document formats 49 | # 50 | # Convert binary document formats to text before diffing them. This feature 51 | # is only available from the command line. Turn it on by uncommenting the 52 | # entries below. 53 | ############################################################################### 54 | #*.doc diff=astextplain 55 | #*.DOC diff=astextplain 56 | #*.docx diff=astextplain 57 | #*.DOCX diff=astextplain 58 | #*.dot diff=astextplain 59 | #*.DOT diff=astextplain 60 | #*.pdf diff=astextplain 61 | #*.PDF diff=astextplain 62 | #*.rtf diff=astextplain 63 | #*.RTF diff=astextplain 64 | -------------------------------------------------------------------------------- /docs/src/settings/user-interface.md: -------------------------------------------------------------------------------- 1 | # User Interface Settings 2 | 3 | ![image](../screenshots/settings-user-interface.png) 4 | 5 | The `User Interface` settings tab contains settings for the user interface. 6 | 7 | ## Color Theme 8 | 9 | The color theme determines the color scheme used for the application. 10 | The default is a blue `Sapphire` color. 11 | 12 | ## Sort Characters By 13 | 14 | This setting determines how characters are sorted in the [Character List](../main-window/character-list.md). 15 | By default, characters are sorted by their `Login Time` (oldest to newest). 16 | 17 | The `Max Health & Mana` option will sort characters by their maximum health and mana, similar to the in-game list. 18 | The total sort value is `Max HP + (Max MP * 2)`. 19 | 20 | ## Spell/Skill Icon Size 21 | 22 | This setting determines the size of the spell and skill icons in the [Spells Tab](../main-window/spells-tab.md) and [Skills Tab](../main-window/skills-tab.md). 23 | The default size is `Medium`. 24 | 25 | ## Tem/Med Skills Per Row 26 | 27 | This setting determines how many skills are displayed per row in the [Skills Tab](../main-window/skills-tab.md) for Temuair and Medenian skills. 28 | The default is `12`, similar to the game client. 29 | 30 | ## World Skills Per Row 31 | 32 | This setting determines how many skills are displayed per row in the [Skills Tab](../main-window/skills-tab.md) for World skills. 33 | The default is `6`, similar to the game client. 34 | 35 | ## Tem/Med Spells Per Row 36 | 37 | This setting determines how many spells are displayed per row in the [Spells Tab](../main-window/spells-tab.md) for Temuair and Medenian spells. 38 | The default is `12`, similar to the game client. 39 | 40 | ## World Spells Per Row 41 | 42 | This setting determines how many spells are displayed per row in the [Spells Tab](../main-window/spells-tab.md) for World spells. 43 | The default is `6`, similar to the game client. 44 | 45 | ## Show Skill Names 46 | 47 | This setting determines whether skill names are displayed in the [Skills Tab](../main-window/skills-tab.md). 48 | They are displayed underneath the skill icon. 49 | 50 | By default, this is `Disabled`. 51 | 52 | ## Show Spell Names 53 | 54 | This setting determines whether spell names are displayed in the [Spells Tab](../main-window/spells-tab.md). 55 | They are displayed underneath the spell icon. 56 | 57 | By default, this is `Disabled`. 58 | 59 | ## Show Skill Levels 60 | 61 | This setting determines whether skill levels are displayed in the [Skills Tab](../main-window/skills-tab.md). 62 | They are displayed in the bottom-right corner of the skill icon (if the skill can improve). 63 | 64 | By default, this is `Enabled`. 65 | 66 | ## Show Spell Levels 67 | 68 | This setting determines whether spell levels are displayed in the [Spells Tab](../main-window/spells-tab.md). 69 | They are displayed in the bottom-right corner of the spell icon (if the spell can improve). 70 | 71 | By default, this is `Enabled`. 72 | -------------------------------------------------------------------------------- /SleepHunter/Media/RenderManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Runtime.CompilerServices; 4 | using System.Windows.Media; 5 | 6 | namespace SleepHunter.Media 7 | { 8 | public sealed class RenderManager 9 | { 10 | private static readonly RenderManager instance = new(); 11 | public static RenderManager Instance => instance; 12 | 13 | private RenderManager() { } 14 | 15 | public static IEnumerable Render(EpfImage image, ColorPalette palette) 16 | { 17 | if (image == null) 18 | throw new ArgumentNullException(nameof(image)); 19 | 20 | if (palette == null) 21 | throw new ArgumentNullException(nameof(palette)); 22 | 23 | foreach (var frame in image.Frames) 24 | yield return Render(frame, palette); 25 | } 26 | 27 | public static RenderedBitmap Render(EpfFrame frame, ColorPalette palette) 28 | { 29 | if (frame == null) 30 | throw new ArgumentNullException(nameof(frame)); 31 | 32 | if (palette == null) 33 | throw new ArgumentNullException(nameof(palette)); 34 | 35 | palette = palette.MakeGrayscale(); 36 | var format = PixelFormats.Bgra32; 37 | 38 | var width = frame.Width; 39 | var height = frame.Height; 40 | var stride = (width * 4) + (width % 4); 41 | 42 | var bits = new byte[height * stride]; 43 | 44 | for (int x = 0; x < width; x++) 45 | for (int y = 0; y < height; y++) 46 | { 47 | var fixedX = x; 48 | var fixedY = y; 49 | 50 | var threshold = 12; 51 | 52 | if (fixedX < threshold) 53 | { 54 | if (width >= threshold) 55 | fixedX = width - (threshold - fixedX); 56 | 57 | if (fixedY > 0) 58 | fixedY--; 59 | } 60 | else 61 | fixedX -= threshold; 62 | 63 | var pixel = frame.RawData[x + y * width]; 64 | 65 | if (pixel > 0) 66 | SetPixel(bits, fixedX, fixedY, stride, palette[pixel]); 67 | else 68 | SetPixel(bits, fixedX, fixedY, stride, Colors.Transparent); 69 | } 70 | 71 | var bitmap = new RenderedBitmap(width, height, stride, format, bits); 72 | return bitmap; 73 | } 74 | 75 | [MethodImpl(MethodImplOptions.AggressiveInlining)] 76 | static void SetPixel(byte[] bits, int x, int y, int stride, Color c) 77 | { 78 | var i = (x * 4) + (y * stride); 79 | 80 | bits[i] = c.B; 81 | bits[i + 1] = c.G; 82 | bits[i + 2] = c.R; 83 | bits[i + 3] = c.A; 84 | } 85 | } 86 | } 87 | -------------------------------------------------------------------------------- /SleepHunter/Macro/HotkeyManager.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.Collections.Concurrent; 4 | using System.Linq; 5 | using System.Windows.Input; 6 | 7 | using SleepHunter.Win32; 8 | 9 | namespace SleepHunter.Macro 10 | { 11 | public sealed class HotkeyManager 12 | { 13 | private static readonly HotkeyManager instance = new(); 14 | public static HotkeyManager Instance => instance; 15 | 16 | private HotkeyManager() { } 17 | 18 | 19 | private readonly ConcurrentDictionary hotkeys = new(); 20 | 21 | public int Count => hotkeys.Count; 22 | 23 | public IEnumerable Hotkeys => from h in hotkeys.Values select h; 24 | 25 | public bool RegisterHotkey(nint windowHandle, Hotkey hotkey) 26 | { 27 | if (hotkey == null) 28 | throw new ArgumentNullException(nameof(hotkey)); 29 | 30 | hotkey.AtomName = GetHotkeyUniqueName(hotkey); 31 | hotkey.Id = NativeMethods.GlobalAddAtom(hotkey.AtomName); 32 | 33 | if (hotkey.Id <= 0) 34 | return false; 35 | 36 | var vkey = KeyInterop.VirtualKeyFromKey(hotkey.Key); 37 | var success = NativeMethods.RegisterHotKey(windowHandle, hotkey.Id, hotkey.Modifiers, vkey); 38 | 39 | if (success) 40 | hotkeys[hotkey.Id] = hotkey; 41 | 42 | return success; 43 | } 44 | 45 | public bool ContainsHotkey(Key key, ModifierKeys modifiers) => GetHotkey(key, modifiers) != null; 46 | 47 | public Hotkey GetHotkey(Key key, ModifierKeys modifiers) 48 | { 49 | foreach (var hotkey in hotkeys.Values) 50 | if (hotkey.Key == key && hotkey.Modifiers == modifiers) 51 | return hotkey; 52 | 53 | return null; 54 | } 55 | 56 | public bool UnregisterHotkey(nint windowHandle, Hotkey hotkey) 57 | { 58 | if (hotkey == null) 59 | throw new ArgumentNullException(nameof(hotkey)); 60 | 61 | NativeMethods.UnregisterHotKey(windowHandle, hotkey.Id); 62 | 63 | var wasRemoved = hotkeys.TryRemove(hotkey.Id, out var removedHotkey); 64 | 65 | if (wasRemoved && hotkey.Id > 0) 66 | NativeMethods.GlobalDeleteAtom((ushort)hotkey.Id); 67 | 68 | return removedHotkey != null; 69 | } 70 | 71 | public void UnregisterAllHotkeys(nint windowHandle) 72 | { 73 | foreach (var hotkey in hotkeys.Values) 74 | UnregisterHotkey(windowHandle, hotkey); 75 | 76 | hotkeys.Clear(); 77 | } 78 | 79 | static string GetHotkeyUniqueName(Hotkey hotkey) 80 | { 81 | var hotkeyName = string.Format("{0}_{1}_{2}", 82 | Environment.CurrentManagedThreadId.ToString("X8"), 83 | hotkey.GetType().FullName, 84 | hotkey.ToString()); 85 | 86 | return hotkeyName; 87 | } 88 | } 89 | } 90 | -------------------------------------------------------------------------------- /SleepHunter/Media/EpfImage.cs: -------------------------------------------------------------------------------- 1 | using System; 2 | using System.Collections.Generic; 3 | using System.IO; 4 | using System.Linq; 5 | 6 | namespace SleepHunter.Media 7 | { 8 | public sealed class EpfImage 9 | { 10 | private readonly List frames = new(); 11 | 12 | public string Name { get; set; } 13 | 14 | public int Width { get; set; } 15 | 16 | public int Height { get; set; } 17 | 18 | public int FrameCount => frames.Count; 19 | public IEnumerable Frames => from f in frames select f; 20 | 21 | public EpfImage(string name, int width, int height, IEnumerable frameCollection) 22 | { 23 | Name = name; 24 | Width = width; 25 | Height = height; 26 | 27 | if (frameCollection != null) 28 | frames.AddRange(frameCollection); 29 | } 30 | 31 | public EpfImage(string filename) 32 | : this(File.Open(filename, FileMode.Open, FileAccess.Read, FileShare.Read), leaveOpen: false) 33 | { 34 | Name = filename; 35 | } 36 | 37 | public EpfImage(Stream stream, bool leaveOpen = true) 38 | { 39 | frames = new List(); 40 | 41 | var reader = new BinaryReader(stream); 42 | 43 | var frameCount = reader.ReadUInt16(); 44 | 45 | Width = reader.ReadInt16(); 46 | Height = reader.ReadInt16(); 47 | 48 | var _ = reader.ReadUInt16(); 49 | var tableOffset = reader.ReadUInt32(); 50 | 51 | reader.BaseStream.Position += tableOffset; 52 | 53 | for (int i = 0; i < frameCount; i++) 54 | { 55 | var left = reader.ReadInt16(); 56 | var top = reader.ReadInt16(); 57 | var right = reader.ReadInt16(); 58 | var bottom = reader.ReadInt16(); 59 | 60 | long startAddress = reader.ReadUInt32(); 61 | long endAddress = reader.ReadUInt32(); 62 | 63 | var frameWidth = Math.Abs(right - left); 64 | var frameHeight = Math.Abs(bottom - top); 65 | 66 | var size = frameWidth * frameHeight; 67 | var bits = new byte[size]; 68 | 69 | if (size > 0) 70 | { 71 | long previousPosition = reader.BaseStream.Position; 72 | 73 | reader.BaseStream.Position = startAddress; 74 | reader.Read(bits, 0, size); 75 | 76 | reader.BaseStream.Position = previousPosition; 77 | } 78 | 79 | var frame = new EpfFrame(i, left, top, frameWidth, frameHeight, bits); 80 | frames.Add(frame); 81 | } 82 | 83 | if (!leaveOpen) 84 | stream.Dispose(); 85 | } 86 | 87 | public bool HasFrame(int index) => frames.Count > index; 88 | 89 | public EpfFrame GetFrameAt(int index) => frames[index]; 90 | 91 | public override string ToString() => Name ?? ""; 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /SleepHunter/Obsidian.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | using System.Windows.Controls.Primitives; 4 | using System.Windows.Input; 5 | 6 | namespace SleepHunter.Themes 7 | { 8 | internal partial class Obsidian 9 | { 10 | protected virtual void WindowThumb_DragDelta(object sender, DragDeltaEventArgs e) 11 | { 12 | if (!(sender is Thumb thumb)) 13 | return; 14 | 15 | if (!(thumb.TemplatedParent is Window window)) 16 | return; 17 | 18 | window.Left += e.HorizontalChange; 19 | window.Top += e.VerticalChange; 20 | } 21 | 22 | protected virtual void WindowThumb_MouseDoubleClick(object sender, MouseEventArgs e) 23 | { 24 | if (!(sender is Thumb thumb)) 25 | return; 26 | 27 | if (!(thumb.TemplatedParent is Window window)) 28 | return; 29 | 30 | if (window.ResizeMode == ResizeMode.NoResize) 31 | return; 32 | 33 | if (e.LeftButton.HasFlag(MouseButtonState.Pressed)) 34 | { 35 | if (window.WindowState == WindowState.Maximized) 36 | window.WindowState = WindowState.Normal; 37 | else 38 | window.WindowState = WindowState.Maximized; 39 | } 40 | } 41 | 42 | protected virtual void WindowMinimize_Click(object sender, RoutedEventArgs e) 43 | { 44 | if (!(sender is Button button)) 45 | return; 46 | 47 | if (!(button.TemplatedParent is Window window)) 48 | return; 49 | 50 | if (window.WindowState == WindowState.Minimized) 51 | window.WindowState = WindowState.Normal; 52 | else 53 | window.WindowState = WindowState.Minimized; 54 | } 55 | 56 | protected virtual void WindowMaximize_Click(object sender, RoutedEventArgs e) 57 | { 58 | if (!(sender is Button button)) 59 | return; 60 | 61 | if (!(button.TemplatedParent is Window window)) 62 | return; 63 | 64 | if (window.WindowState == WindowState.Maximized) 65 | window.WindowState = WindowState.Normal; 66 | else 67 | window.WindowState = WindowState.Maximized; 68 | } 69 | 70 | protected virtual void WindowClose_Click(object sender, RoutedEventArgs e) 71 | { 72 | if (!(sender is Button button)) 73 | return; 74 | 75 | if (!(button.TemplatedParent is Window window)) 76 | return; 77 | 78 | window.Close(); 79 | } 80 | 81 | protected virtual void TextBoxClearButton_Click(object sender, RoutedEventArgs e) 82 | { 83 | if (!(sender is Button button)) 84 | return; 85 | 86 | if (!(button.TemplatedParent is TextBox textBox)) 87 | return; 88 | 89 | textBox.Clear(); 90 | textBox.Focus(); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /SleepHunter.Updater/Obsidian.xaml.cs: -------------------------------------------------------------------------------- 1 | using System.Windows; 2 | using System.Windows.Controls; 3 | using System.Windows.Controls.Primitives; 4 | using System.Windows.Input; 5 | 6 | namespace SleepHunter.Updater 7 | { 8 | internal partial class Obsidian 9 | { 10 | protected virtual void WindowThumb_DragDelta(object sender, DragDeltaEventArgs e) 11 | { 12 | if (!(sender is Thumb thumb)) 13 | return; 14 | 15 | if (!(thumb.TemplatedParent is Window window)) 16 | return; 17 | 18 | window.Left += e.HorizontalChange; 19 | window.Top += e.VerticalChange; 20 | } 21 | 22 | protected virtual void WindowThumb_MouseDoubleClick(object sender, MouseEventArgs e) 23 | { 24 | if (!(sender is Thumb thumb)) 25 | return; 26 | 27 | if (!(thumb.TemplatedParent is Window window)) 28 | return; 29 | 30 | if (window.ResizeMode == ResizeMode.NoResize) 31 | return; 32 | 33 | if (e.LeftButton.HasFlag(MouseButtonState.Pressed)) 34 | { 35 | if (window.WindowState == WindowState.Maximized) 36 | window.WindowState = WindowState.Normal; 37 | else 38 | window.WindowState = WindowState.Maximized; 39 | } 40 | } 41 | 42 | protected virtual void WindowMinimize_Click(object sender, RoutedEventArgs e) 43 | { 44 | if (!(sender is Button button)) 45 | return; 46 | 47 | if (!(button.TemplatedParent is Window window)) 48 | return; 49 | 50 | if (window.WindowState == WindowState.Minimized) 51 | window.WindowState = WindowState.Normal; 52 | else 53 | window.WindowState = WindowState.Minimized; 54 | } 55 | 56 | protected virtual void WindowMaximize_Click(object sender, RoutedEventArgs e) 57 | { 58 | if (!(sender is Button button)) 59 | return; 60 | 61 | if (!(button.TemplatedParent is Window window)) 62 | return; 63 | 64 | if (window.WindowState == WindowState.Maximized) 65 | window.WindowState = WindowState.Normal; 66 | else 67 | window.WindowState = WindowState.Maximized; 68 | } 69 | 70 | protected virtual void WindowClose_Click(object sender, RoutedEventArgs e) 71 | { 72 | if (!(sender is Button button)) 73 | return; 74 | 75 | if (!(button.TemplatedParent is Window window)) 76 | return; 77 | 78 | window.Close(); 79 | } 80 | 81 | protected virtual void TextBoxClearButton_Click(object sender, RoutedEventArgs e) 82 | { 83 | if (!(sender is Button button)) 84 | return; 85 | 86 | if (!(button.TemplatedParent is TextBox textBox)) 87 | return; 88 | 89 | textBox.Clear(); 90 | textBox.Focus(); 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /SleepHunter/Templates/EquipmentItemDataTemplate.xaml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 16 | 22 | 23 | 24 | 25 | 30 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | 54 | -------------------------------------------------------------------------------- /SleepHunter/Views/MessageBoxWindow.xaml: -------------------------------------------------------------------------------- 1 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 26 | 27 | 33 | 34 | 42 | 43 | 44 | 45 | 46 |