From 67ea70a7ab7cefbb56aaf4d241a61eb28172042f Mon Sep 17 00:00:00 2001 From: gonlad_x Date: Mon, 20 Jul 2026 19:32:04 +0200 Subject: [PATCH 1/4] Add Activate On Launch feature Port TarnishedTool's Activate On Launch feature: ActivateOnLaunchManager for persisted settings storage, per-tool feature checklist, and the same lifecycle-driven architecture as TarnishedTool/SilkySouls3 - a master enable toggle, a singleton settings window opened via a bound command from SettingsViewModel (with reuse/Activate() instead of spawning duplicates), and AppStart/Attached/Loaded/GameStart state subscriptions matching the reference implementation. Feature list for Sekiro: - Player: Infinite Confetti/Gachiin, One Shot Health/Posture, No Goods/ Emblem Consume, Infinite Revival, Player Hide/Silent, Infinite Poise, No Death (+ ex-killbox variant), No Damage, and a Damage Multiplier value (defaults to 1.0, matching PlayerViewModel's default) instead of the relative increase/decrease toggles TarnishedTool uses. - Target: Enable Target Options. - Travel: Unlock All Idols, wired to TravelViewModel's existing UnlockIdolsCommand. - New Game Cycle: Auto Set NG+7, Demon Bell on/off, No Kuro's Charm on/off. No Enemies or Boss Skips sections - those are one-shot actions tied to specific fight state rather than persistent toggles, and didn't fit the Activate On Launch model cleanly for this game. --- SekiroTool/Enums/State.cs | 2 +- SekiroTool/MainWindow.xaml.cs | 8 +- .../Utilities/ActivateOnLaunchManager.cs | 86 ++++ SekiroTool/Utilities/SettingsManager.cs | 2 + .../ViewModels/ActivateOnLaunchViewModel.cs | 392 ++++++++++++++++++ SekiroTool/ViewModels/SettingsViewModel.cs | 30 +- SekiroTool/Views/Tabs/SettingsTab.xaml | 22 + SekiroTool/Views/Tabs/SettingsTab.xaml.cs | 4 +- .../Views/Windows/ActivateOnLaunchWindow.xaml | 186 +++++++++ .../Windows/ActivateOnLaunchWindow.xaml.cs | 18 + 10 files changed, 743 insertions(+), 7 deletions(-) create mode 100644 SekiroTool/Utilities/ActivateOnLaunchManager.cs create mode 100644 SekiroTool/ViewModels/ActivateOnLaunchViewModel.cs create mode 100644 SekiroTool/Views/Windows/ActivateOnLaunchWindow.xaml create mode 100644 SekiroTool/Views/Windows/ActivateOnLaunchWindow.xaml.cs diff --git a/SekiroTool/Enums/State.cs b/SekiroTool/Enums/State.cs index 5c6715b..eaf4cf5 100644 --- a/SekiroTool/Enums/State.cs +++ b/SekiroTool/Enums/State.cs @@ -9,6 +9,6 @@ public enum State NotLoaded, GameStart, AppClosing, - AppStarting, + AppStart, EventTabActivated, } \ No newline at end of file diff --git a/SekiroTool/MainWindow.xaml.cs b/SekiroTool/MainWindow.xaml.cs index 71891fa..aa8eb13 100644 --- a/SekiroTool/MainWindow.xaml.cs +++ b/SekiroTool/MainWindow.xaml.cs @@ -79,7 +79,11 @@ public MainWindow() ItemViewModel itemViewModel = new ItemViewModel(itemService, _stateService); EventViewModel eventViewModel = new EventViewModel(eventService, _stateService, debugDrawService, itemService, _hotkeyManager); - SettingsViewModel settingsViewModel = new SettingsViewModel(settingsService, _stateService, _hotkeyManager); + var activateOnLaunchManager = new ActivateOnLaunchManager(); + ActivateOnLaunchViewModel activateOnLaunchViewModel = new ActivateOnLaunchViewModel(playerViewModel, + targetViewModel, eventViewModel, travelViewModel, activateOnLaunchManager, _stateService); + SettingsViewModel settingsViewModel = new SettingsViewModel(settingsService, _stateService, _hotkeyManager, + activateOnLaunchViewModel); var playerTab = new PlayerTab(playerViewModel); var travelTab = new TravelTab(travelViewModel); @@ -103,6 +107,8 @@ public MainWindow() settingsViewModel.ApplyStartUpOptions(); + _stateService.Publish(State.AppStart); + Closing += MainWindow_Closing; _gameLoadedTimer = new DispatcherTimer diff --git a/SekiroTool/Utilities/ActivateOnLaunchManager.cs b/SekiroTool/Utilities/ActivateOnLaunchManager.cs new file mode 100644 index 0000000..f90b8e8 --- /dev/null +++ b/SekiroTool/Utilities/ActivateOnLaunchManager.cs @@ -0,0 +1,86 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; + +namespace SekiroTool.Utilities; + +public class ActivateOnLaunchManager +{ + private readonly Dictionary _values = new(); + + public ActivateOnLaunchManager() + { + Load(); + } + + public bool GetBool(string actionId) + { + return _values.TryGetValue(actionId, out var v) && bool.TryParse(v, out var b) && b; + } + + public void SetBool(string actionId, bool value) + { + _values[actionId] = value.ToString(); + Save(); + } + + public int GetInt(string actionId, int defaultValue = 0) + { + return _values.TryGetValue(actionId, out var v) && int.TryParse(v, out var i) ? i : defaultValue; + } + + public void SetInt(string actionId, int value) + { + _values[actionId] = value.ToString(); + Save(); + } + + public double GetDouble(string actionId, double defaultValue = 0) + { + return _values.TryGetValue(actionId, out var v) && + double.TryParse(v, NumberStyles.Float, CultureInfo.InvariantCulture, out var d) + ? d + : defaultValue; + } + + public void SetDouble(string actionId, double value) + { + _values[actionId] = value.ToString(CultureInfo.InvariantCulture); + Save(); + } + + public void Save() + { + try + { + SettingsManager.Default.ActivateOnLaunchActionIds = string.Join(",", _values.Select(kv => $"{kv.Key}:{kv.Value}")); + SettingsManager.Default.Save(); + } + catch (Exception ex) + { + Console.WriteLine($@"Error saving activate on launch settings: {ex.Message}"); + } + } + + public void Load() + { + try + { + _values.Clear(); + var raw = SettingsManager.Default.ActivateOnLaunchActionIds; + if (string.IsNullOrEmpty(raw)) return; + + foreach (var token in raw.Split(new[] { ',' }, StringSplitOptions.RemoveEmptyEntries)) + { + var separatorIndex = token.IndexOf(':'); + if (separatorIndex <= 0) continue; + _values[token.Substring(0, separatorIndex)] = token.Substring(separatorIndex + 1); + } + } + catch (Exception ex) + { + Console.WriteLine($@"Error loading activate on launch settings: {ex.Message}"); + } + } +} diff --git a/SekiroTool/Utilities/SettingsManager.cs b/SekiroTool/Utilities/SettingsManager.cs index 9848be7..f7dbb74 100644 --- a/SekiroTool/Utilities/SettingsManager.cs +++ b/SekiroTool/Utilities/SettingsManager.cs @@ -11,6 +11,8 @@ public class SettingsManager public static SettingsManager Default => _default ??= Load(); public string HotkeyActionIds { get; set; } = ""; + public bool ActivateOnLaunchEnabled { get; set; } + public string ActivateOnLaunchActionIds { get; set; } = ""; public bool EnableHotkeys { get; set; } public bool NoLogo { get; set; } public bool AlwaysOnTop { get; set; } diff --git a/SekiroTool/ViewModels/ActivateOnLaunchViewModel.cs b/SekiroTool/ViewModels/ActivateOnLaunchViewModel.cs new file mode 100644 index 0000000..ba901fb --- /dev/null +++ b/SekiroTool/ViewModels/ActivateOnLaunchViewModel.cs @@ -0,0 +1,392 @@ +using System.Threading.Tasks; +using SekiroTool.Enums; +using SekiroTool.Interfaces; +using SekiroTool.Utilities; + +namespace SekiroTool.ViewModels; + +public class ActivateOnLaunchViewModel : BaseViewModel +{ + private readonly PlayerViewModel _playerViewModel; + private readonly TargetViewModel _targetViewModel; + private readonly EventViewModel _eventViewModel; + private readonly TravelViewModel _travelViewModel; + private readonly ActivateOnLaunchManager _aol; + + public ActivateOnLaunchViewModel(PlayerViewModel playerViewModel, TargetViewModel targetViewModel, + EventViewModel eventViewModel, TravelViewModel travelViewModel, + ActivateOnLaunchManager activateOnLaunchManager, IStateService stateService) + { + _playerViewModel = playerViewModel; + _targetViewModel = targetViewModel; + _eventViewModel = eventViewModel; + _travelViewModel = travelViewModel; + _aol = activateOnLaunchManager; + + RegisterActions(); + + stateService.Subscribe(State.AppStart, OnAppStart); + stateService.Subscribe(State.Attached, OnGameAttached); + stateService.Subscribe(State.Loaded, OnGameLoaded); + stateService.Subscribe(State.GameStart, OnNewGameStart); + } + + #region Properties + + // Master toggle + private bool _isEnabled = SettingsManager.Default.ActivateOnLaunchEnabled; + + public bool IsEnabled + { + get => _isEnabled; + set + { + if (!SetProperty(ref _isEnabled, value)) return; + SettingsManager.Default.ActivateOnLaunchEnabled = value; + SettingsManager.Default.Save(); + } + } + + // Helper macros + private bool Get(string id) => _aol.GetBool(id); + private void Set(string id, bool value) => _aol.SetBool(id, value); + + // Player + private bool _isInfiniteConfettiChecked; + + public bool IsInfiniteConfettiChecked + { + get => _isInfiniteConfettiChecked; + set + { + if (SetProperty(ref _isInfiniteConfettiChecked, value)) Set(nameof(IsInfiniteConfettiChecked), value); + } + } + + private bool _isInfiniteGachiinChecked; + + public bool IsInfiniteGachiinChecked + { + get => _isInfiniteGachiinChecked; + set + { + if (SetProperty(ref _isInfiniteGachiinChecked, value)) Set(nameof(IsInfiniteGachiinChecked), value); + } + } + + private bool _isOneShotHealthChecked; + + public bool IsOneShotHealthChecked + { + get => _isOneShotHealthChecked; + set + { + if (SetProperty(ref _isOneShotHealthChecked, value)) Set(nameof(IsOneShotHealthChecked), value); + } + } + + private bool _isOneShotPostureChecked; + + public bool IsOneShotPostureChecked + { + get => _isOneShotPostureChecked; + set + { + if (SetProperty(ref _isOneShotPostureChecked, value)) Set(nameof(IsOneShotPostureChecked), value); + } + } + + private bool _isNoGoodsConsumeChecked; + + public bool IsNoGoodsConsumeChecked + { + get => _isNoGoodsConsumeChecked; + set + { + if (SetProperty(ref _isNoGoodsConsumeChecked, value)) Set(nameof(IsNoGoodsConsumeChecked), value); + } + } + + private bool _isNoEmblemConsumeChecked; + + public bool IsNoEmblemConsumeChecked + { + get => _isNoEmblemConsumeChecked; + set + { + if (SetProperty(ref _isNoEmblemConsumeChecked, value)) Set(nameof(IsNoEmblemConsumeChecked), value); + } + } + + private bool _isInfiniteRevivalChecked; + + public bool IsInfiniteRevivalChecked + { + get => _isInfiniteRevivalChecked; + set + { + if (SetProperty(ref _isInfiniteRevivalChecked, value)) Set(nameof(IsInfiniteRevivalChecked), value); + } + } + + private bool _isPlayerHideChecked; + + public bool IsPlayerHideChecked + { + get => _isPlayerHideChecked; + set + { + if (SetProperty(ref _isPlayerHideChecked, value)) Set(nameof(IsPlayerHideChecked), value); + } + } + + private bool _isPlayerSilentChecked; + + public bool IsPlayerSilentChecked + { + get => _isPlayerSilentChecked; + set + { + if (SetProperty(ref _isPlayerSilentChecked, value)) Set(nameof(IsPlayerSilentChecked), value); + } + } + + private bool _isInfinitePoiseChecked; + + public bool IsInfinitePoiseChecked + { + get => _isInfinitePoiseChecked; + set + { + if (SetProperty(ref _isInfinitePoiseChecked, value)) Set(nameof(IsInfinitePoiseChecked), value); + } + } + + private bool _isNoDeathChecked; + + public bool IsNoDeathChecked + { + get => _isNoDeathChecked; + set + { + if (SetProperty(ref _isNoDeathChecked, value)) Set(nameof(IsNoDeathChecked), value); + } + } + + private bool _isNoDeathExKillboxChecked; + + public bool IsNoDeathExKillboxChecked + { + get => _isNoDeathExKillboxChecked; + set + { + if (SetProperty(ref _isNoDeathExKillboxChecked, value)) Set(nameof(IsNoDeathExKillboxChecked), value); + } + } + + private bool _isNoDamageChecked; + + public bool IsNoDamageChecked + { + get => _isNoDamageChecked; + set + { + if (SetProperty(ref _isNoDamageChecked, value)) Set(nameof(IsNoDamageChecked), value); + } + } + + private bool _isToggleDamageMultiplierChecked; + + public bool IsToggleDamageMultiplierChecked + { + get => _isToggleDamageMultiplierChecked; + set + { + if (SetProperty(ref _isToggleDamageMultiplierChecked, value)) + Set(nameof(IsToggleDamageMultiplierChecked), value); + } + } + + private double _damageMultiplierValue; + + public double DamageMultiplierValue + { + get => _damageMultiplierValue; + set + { + if (SetProperty(ref _damageMultiplierValue, value)) + _aol.SetDouble(nameof(DamageMultiplierValue), value); + } + } + + // Target + private bool _isEnableTargetOptionsChecked; + + public bool IsEnableTargetOptionsChecked + { + get => _isEnableTargetOptionsChecked; + set + { + if (SetProperty(ref _isEnableTargetOptionsChecked, value)) + Set(nameof(IsEnableTargetOptionsChecked), value); + } + } + + // Travel + private bool _isUnlockIdolsChecked; + + public bool IsUnlockIdolsChecked + { + get => _isUnlockIdolsChecked; + set + { + if (SetProperty(ref _isUnlockIdolsChecked, value)) Set(nameof(IsUnlockIdolsChecked), value); + } + } + + // New Game Cycle + private bool _isAutoSetNewGame7Checked; + + public bool IsAutoSetNewGame7Checked + { + get => _isAutoSetNewGame7Checked; + set + { + if (SetProperty(ref _isAutoSetNewGame7Checked, value)) Set(nameof(IsAutoSetNewGame7Checked), value); + } + } + + private bool _isDemonBellOnChecked; + + public bool IsDemonBellOnChecked + { + get => _isDemonBellOnChecked; + set + { + if (SetProperty(ref _isDemonBellOnChecked, value)) Set(nameof(IsDemonBellOnChecked), value); + } + } + + private bool _isDemonBellOffChecked; + + public bool IsDemonBellOffChecked + { + get => _isDemonBellOffChecked; + set + { + if (SetProperty(ref _isDemonBellOffChecked, value)) Set(nameof(IsDemonBellOffChecked), value); + } + } + + private bool _isNoKurosCharmOnChecked; + + public bool IsNoKurosCharmOnChecked + { + get => _isNoKurosCharmOnChecked; + set + { + if (SetProperty(ref _isNoKurosCharmOnChecked, value)) Set(nameof(IsNoKurosCharmOnChecked), value); + } + } + + private bool _isNoKurosCharmOffChecked; + + public bool IsNoKurosCharmOffChecked + { + get => _isNoKurosCharmOffChecked; + set + { + if (SetProperty(ref _isNoKurosCharmOffChecked, value)) Set(nameof(IsNoKurosCharmOffChecked), value); + } + } + + #endregion + + private void RegisterActions() + { + _isInfiniteConfettiChecked = Get(nameof(IsInfiniteConfettiChecked)); + _isInfiniteGachiinChecked = Get(nameof(IsInfiniteGachiinChecked)); + _isOneShotHealthChecked = Get(nameof(IsOneShotHealthChecked)); + _isOneShotPostureChecked = Get(nameof(IsOneShotPostureChecked)); + _isNoGoodsConsumeChecked = Get(nameof(IsNoGoodsConsumeChecked)); + _isNoEmblemConsumeChecked = Get(nameof(IsNoEmblemConsumeChecked)); + _isInfiniteRevivalChecked = Get(nameof(IsInfiniteRevivalChecked)); + _isPlayerHideChecked = Get(nameof(IsPlayerHideChecked)); + _isPlayerSilentChecked = Get(nameof(IsPlayerSilentChecked)); + _isInfinitePoiseChecked = Get(nameof(IsInfinitePoiseChecked)); + _isNoDeathChecked = Get(nameof(IsNoDeathChecked)); + _isNoDeathExKillboxChecked = Get(nameof(IsNoDeathExKillboxChecked)); + _isNoDamageChecked = Get(nameof(IsNoDamageChecked)); + _isToggleDamageMultiplierChecked = Get(nameof(IsToggleDamageMultiplierChecked)); + _damageMultiplierValue = _aol.GetDouble(nameof(DamageMultiplierValue), defaultValue: 1.0); + + _isEnableTargetOptionsChecked = Get(nameof(IsEnableTargetOptionsChecked)); + + _isUnlockIdolsChecked = Get(nameof(IsUnlockIdolsChecked)); + + _isAutoSetNewGame7Checked = Get(nameof(IsAutoSetNewGame7Checked)); + _isDemonBellOnChecked = Get(nameof(IsDemonBellOnChecked)); + _isDemonBellOffChecked = Get(nameof(IsDemonBellOffChecked)); + _isNoKurosCharmOnChecked = Get(nameof(IsNoKurosCharmOnChecked)); + _isNoKurosCharmOffChecked = Get(nameof(IsNoKurosCharmOffChecked)); + } + + private void OnAppStart() + { + if (!IsEnabled) return; + + // Player + if (IsInfiniteConfettiChecked) _playerViewModel.IsConfettiFlagEnabled = true; + if (IsInfiniteGachiinChecked) _playerViewModel.IsGachiinFlagEnabled = true; + if (IsOneShotHealthChecked) _playerViewModel.IsOneShotEnabled = true; + if (IsOneShotPostureChecked) _playerViewModel.IsOneShotPostureEnabled = true; + if (IsNoGoodsConsumeChecked) _playerViewModel.IsNoGoodsConsumeEnabled = true; + if (IsNoEmblemConsumeChecked) _playerViewModel.IsNoEmblemConsumeEnabled = true; + if (IsInfiniteRevivalChecked) _playerViewModel.IsNoRevivalConsumeEnabled = true; + if (IsPlayerHideChecked) _playerViewModel.IsPlayerHideEnabled = true; + if (IsPlayerSilentChecked) _playerViewModel.IsPlayerSilentEnabled = true; + if (IsInfinitePoiseChecked) _playerViewModel.IsInfinitePoiseEnabled = true; + if (IsNoDeathChecked) _playerViewModel.IsNoDeathEnabled = true; + if (IsNoDeathExKillboxChecked) _playerViewModel.IsNoDeathEnabledWithoutKillbox = true; + if (IsNoDamageChecked) _playerViewModel.IsNoDamageEnabled = true; + if (IsToggleDamageMultiplierChecked) + { + _playerViewModel.DamageMultiplier = DamageMultiplierValue; + _playerViewModel.IsDamageMultiplierEnabled = true; + } + + // Target + if (IsEnableTargetOptionsChecked) _targetViewModel.IsTargetOptionsEnabled = true; + } + + private void OnGameAttached() + { + // No attach-gated Activate On Launch options for Sekiro yet (e.g. TarnishedTool's launch FPS); + // kept for lifecycle parity with the other tools. + if (!IsEnabled) return; + } + + private void OnGameLoaded() + { + if (!IsEnabled) return; + } + + private async void OnNewGameStart() + { + if (!IsEnabled) return; + + if (IsAutoSetNewGame7Checked) _playerViewModel.IsAutoSetNewGameSevenEnabled = true; + + if (IsDemonBellOnChecked) _eventViewModel.SetDemonBellCommand.Execute(true); + if (IsDemonBellOffChecked) _eventViewModel.SetDemonBellCommand.Execute(false); + if (IsNoKurosCharmOnChecked) _eventViewModel.SetNoKurosCharmCommand.Execute(true); + if (IsNoKurosCharmOffChecked) _eventViewModel.SetNoKurosCharmCommand.Execute(false); + + // Travel + if (IsUnlockIdolsChecked && _travelViewModel.UnlockIdolsCommand.CanExecute(null)) + { + await Task.Delay(1500); + _travelViewModel.UnlockIdolsCommand.Execute(null); + } + } +} diff --git a/SekiroTool/ViewModels/SettingsViewModel.cs b/SekiroTool/ViewModels/SettingsViewModel.cs index 572f666..dea9b53 100644 --- a/SekiroTool/ViewModels/SettingsViewModel.cs +++ b/SekiroTool/ViewModels/SettingsViewModel.cs @@ -7,6 +7,7 @@ using SekiroTool.Enums; using SekiroTool.Interfaces; using SekiroTool.Utilities; +using SekiroTool.Views.Windows; using Key = H.Hooks.Key; using KeyboardEventArgs = H.Hooks.KeyboardEventArgs; @@ -16,6 +17,8 @@ public class SettingsViewModel : BaseViewModel { private readonly ISettingsService _settingsService; private readonly HotkeyManager _hotkeyManager; + private readonly ActivateOnLaunchViewModel _activateOnLaunchViewModel; + private ActivateOnLaunchWindow _activateOnLaunchWindow; private readonly Dictionary _hotkeyLookup; @@ -31,10 +34,11 @@ public class SettingsViewModel : BaseViewModel public ObservableCollection BossSkipHotkeys { get; } public SettingsViewModel(ISettingsService settingsService, IStateService stateService, - HotkeyManager hotkeyManager) + HotkeyManager hotkeyManager, ActivateOnLaunchViewModel activateOnLaunchViewModel) { _settingsService = settingsService; _hotkeyManager = hotkeyManager; + _activateOnLaunchViewModel = activateOnLaunchViewModel; stateService.Subscribe(State.Attached, OnGameAttached); stateService.Subscribe(State.EarlyAttached, OnGameEarlyAttached); @@ -148,15 +152,17 @@ public SettingsViewModel(ISettingsService settingsService, IStateService stateSe .Concat((EventHotkeys)) .ToDictionary(h => h.ActionId); LoadHotkeyDisplays(); - + ClearHotkeysCommand = new DelegateCommand(ClearHotkeys); + OpenActivateOnLaunchCommand = new DelegateCommand(OpenActivateOnLaunch); } - + #region Commands public ICommand ClearHotkeysCommand { get; set; } + public ICommand OpenActivateOnLaunchCommand { get; set; } #endregion @@ -564,5 +570,23 @@ private void ClearHotkeys() LoadHotkeyDisplays(); } + private void OpenActivateOnLaunch() + { + if (_activateOnLaunchWindow != null && _activateOnLaunchWindow.IsVisible) + { + _activateOnLaunchWindow.Activate(); + return; + } + + _activateOnLaunchWindow = new ActivateOnLaunchWindow(_activateOnLaunchViewModel) + { + DataContext = _activateOnLaunchViewModel, + Owner = Application.Current.MainWindow + }; + + _activateOnLaunchWindow.Closed += (_, _) => _activateOnLaunchWindow = null; + _activateOnLaunchWindow.ShowDialog(); + } + #endregion } \ No newline at end of file diff --git a/SekiroTool/Views/Tabs/SettingsTab.xaml b/SekiroTool/Views/Tabs/SettingsTab.xaml index 1c042f2..d518209 100644 --- a/SekiroTool/Views/Tabs/SettingsTab.xaml +++ b/SekiroTool/Views/Tabs/SettingsTab.xaml @@ -112,6 +112,28 @@ IsChecked="{Binding IsDisableMenuMusicEnabled}" Margin="25,0,0,0" /> + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/SekiroTool/Views/Windows/ActivateOnLaunchWindow.xaml.cs b/SekiroTool/Views/Windows/ActivateOnLaunchWindow.xaml.cs new file mode 100644 index 0000000..615acc2 --- /dev/null +++ b/SekiroTool/Views/Windows/ActivateOnLaunchWindow.xaml.cs @@ -0,0 +1,18 @@ +using System.Windows; +using System.Windows.Input; +using SekiroTool.ViewModels; + +namespace SekiroTool.Views.Windows; + +public partial class ActivateOnLaunchWindow : Window +{ + public ActivateOnLaunchWindow(ActivateOnLaunchViewModel viewModel) + { + InitializeComponent(); + DataContext = viewModel; + } + + private void TitleBar_MouseLeftButtonDown(object sender, MouseButtonEventArgs e) => DragMove(); + + private void CloseButton_Click(object sender, RoutedEventArgs e) => Close(); +} From 0db51958bcdaf9425350a7c2646d678f7216ac27 Mon Sep 17 00:00:00 2001 From: gonlad_x Date: Mon, 20 Jul 2026 19:33:38 +0200 Subject: [PATCH 2/4] Fix Activate On Launch event-ordering bugs - Move Enable Target Options from AppStart to Loaded. The property setter it calls (TargetService.ToggleTargetHook -> ChangeIdolIcon) allocates and immediately executes shellcode built with offsets that are only resolved once PatchChecker.Initialize/AllocCodeCave have run - which is guaranteed by the time Loaded fires, but not by AppStart if the tool is opened after the game is already running. That gap reliably crashed the game. - Move Auto Set NG+7 from GameStart to AppStart. It was previously primed on the same GameStart event that PlayerViewModel's own OnGameStart handler reads it on, and since PlayerViewModel subscribes to GameStart first, its check always ran before the flag was set - the option could never actually trigger. --- SekiroTool/ViewModels/ActivateOnLaunchViewModel.cs | 13 +++++++++---- 1 file changed, 9 insertions(+), 4 deletions(-) diff --git a/SekiroTool/ViewModels/ActivateOnLaunchViewModel.cs b/SekiroTool/ViewModels/ActivateOnLaunchViewModel.cs index ba901fb..d98c43d 100644 --- a/SekiroTool/ViewModels/ActivateOnLaunchViewModel.cs +++ b/SekiroTool/ViewModels/ActivateOnLaunchViewModel.cs @@ -355,8 +355,12 @@ private void OnAppStart() _playerViewModel.IsDamageMultiplierEnabled = true; } - // Target - if (IsEnableTargetOptionsChecked) _targetViewModel.IsTargetOptionsEnabled = true; + // New Game Cycle + // Must be primed here (State.AppStart) rather than on State.GameStart: PlayerViewModel's own + // OnGameStart handler reads IsAutoSetNewGameSevenEnabled on that same event, and subscribes to + // it before ActivateOnLaunchViewModel does, so setting the flag on GameStart would always run + // one publish too late to be read. + if (IsAutoSetNewGame7Checked) _playerViewModel.IsAutoSetNewGameSevenEnabled = true; } private void OnGameAttached() @@ -369,14 +373,15 @@ private void OnGameAttached() private void OnGameLoaded() { if (!IsEnabled) return; + + // Target + if (IsEnableTargetOptionsChecked) _targetViewModel.IsTargetOptionsEnabled = true; } private async void OnNewGameStart() { if (!IsEnabled) return; - if (IsAutoSetNewGame7Checked) _playerViewModel.IsAutoSetNewGameSevenEnabled = true; - if (IsDemonBellOnChecked) _eventViewModel.SetDemonBellCommand.Execute(true); if (IsDemonBellOffChecked) _eventViewModel.SetDemonBellCommand.Execute(false); if (IsNoKurosCharmOnChecked) _eventViewModel.SetNoKurosCharmCommand.Execute(true); From cd732512bba1e933db4d25e2fb821932507ad608 Mon Sep 17 00:00:00 2001 From: gonlad_x Date: Mon, 20 Jul 2026 15:11:09 +0200 Subject: [PATCH 3/4] Move Activate On Launch button beside Disable Cutscenes --- SekiroTool/Views/Tabs/SettingsTab.xaml | 41 ++++++++++++-------------- 1 file changed, 19 insertions(+), 22 deletions(-) diff --git a/SekiroTool/Views/Tabs/SettingsTab.xaml b/SekiroTool/Views/Tabs/SettingsTab.xaml index d518209..2c47043 100644 --- a/SekiroTool/Views/Tabs/SettingsTab.xaml +++ b/SekiroTool/Views/Tabs/SettingsTab.xaml @@ -73,6 +73,25 @@ +