diff --git a/src/TrackerCouncil.Smz3.Abstractions/AutoTrackerBase.cs b/src/TrackerCouncil.Smz3.Abstractions/AutoTrackerBase.cs index 741cef69f..6150c195a 100644 --- a/src/TrackerCouncil.Smz3.Abstractions/AutoTrackerBase.cs +++ b/src/TrackerCouncil.Smz3.Abstractions/AutoTrackerBase.cs @@ -64,6 +64,11 @@ public abstract class AutoTrackerBase : IDisposable /// public event EventHandler? AutoTrackerDisconnected; + /// + /// Occurs when the tracker detects that the game has changed + /// + public event EventHandler? GameChanged; + /// /// The action to run when the player asks Tracker to look at the game /// @@ -156,5 +161,13 @@ protected virtual void OnAutoTrackerConnectorChanged() AutoTrackerConnectorChanged?.Invoke(this, EventArgs.Empty); } + /// + /// Invokes the GameChanged event + /// + protected virtual void OnGameChanged() + { + GameChanged?.Invoke(this, EventArgs.Empty); + } + public abstract void Dispose(); } diff --git a/src/TrackerCouncil.Smz3.Abstractions/ITrackerModeService.cs b/src/TrackerCouncil.Smz3.Abstractions/ITrackerModeService.cs index 3a710992d..4fc5e8860 100644 --- a/src/TrackerCouncil.Smz3.Abstractions/ITrackerModeService.cs +++ b/src/TrackerCouncil.Smz3.Abstractions/ITrackerModeService.cs @@ -71,7 +71,12 @@ public interface ITrackerModeService /// The speech recognition confidence. public void Peg(float? confidence = null); - public void SetPegs(int count); + /// + /// Updates the number of pegs by auto tracker + /// + /// The current number of pegs + /// True if the peg count was updated, false otherwise + public bool SetPegs(int count); /// /// Starts Peg World mode. diff --git a/src/TrackerCouncil.Smz3.Tracking/AutoTracking/AutoTracker.cs b/src/TrackerCouncil.Smz3.Tracking/AutoTracking/AutoTracker.cs index 9314f2b7a..7013f2678 100644 --- a/src/TrackerCouncil.Smz3.Tracking/AutoTracking/AutoTracker.cs +++ b/src/TrackerCouncil.Smz3.Tracking/AutoTracking/AutoTracker.cs @@ -105,6 +105,7 @@ private void SnesConnectorServiceOnDisconnected(object? sender, EventArgs e) HasStarted = false; CurrentGame = Game.Neither; + OnGameChanged(); _hasValidState = false; if (_numDisconnects > 10) @@ -200,6 +201,7 @@ public override void UpdateGame(Game game) { PreviousGame = CurrentGame; CurrentGame = game; + OnGameChanged(); } public override void UpdateValidState(bool hasValidState) diff --git a/src/TrackerCouncil.Smz3.Tracking/AutoTracking/MetroidStateChecks/Shaktool.cs b/src/TrackerCouncil.Smz3.Tracking/AutoTracking/MetroidStateChecks/Shaktool.cs index a0e345b98..852c7f526 100644 --- a/src/TrackerCouncil.Smz3.Tracking/AutoTracking/MetroidStateChecks/Shaktool.cs +++ b/src/TrackerCouncil.Smz3.Tracking/AutoTracking/MetroidStateChecks/Shaktool.cs @@ -1,4 +1,6 @@ -using System.Linq; +using System; +using System.Collections.Generic; +using System.Linq; using TrackerCouncil.Smz3.Abstractions; using TrackerCouncil.Smz3.Data.Tracking; using TrackerCouncil.Smz3.Shared.Enums; @@ -9,16 +11,19 @@ namespace TrackerCouncil.Smz3.Tracking.AutoTracking.MetroidStateChecks; /// Metroid state check for nearing Shaktool /// Player enters the room with the grapple block from the left /// -public class Shaktool : IMetroidStateCheck +public class Shaktool(TrackerBase tracker) : IMetroidStateCheck { + private readonly HashSet _shaktoolRooms = [70, 154, 197, 208]; + private bool _enabledOnGameChangedCheck; + /// /// Executes the check for the current state /// - /// The tracker instance + /// The tracker instance /// The current state in Super Metroid /// The previous state in Super Metroid /// True if the check was identified, false otherwise - public bool ExecuteCheck(TrackerBase tracker, AutoTrackerMetroidState currentState, AutoTrackerMetroidState prevState) + public bool ExecuteCheck(TrackerBase trackerBase, AutoTrackerMetroidState currentState, AutoTrackerMetroidState prevState) { if (currentState is { CurrentRegion: 4, CurrentRoomInRegion: 36 } && prevState.CurrentRoomInRegion == 28 && tracker.World.FindLocation(LocationId.InnerMaridiaSpringBall).Cleared != true && @@ -27,17 +32,46 @@ public bool ExecuteCheck(TrackerBase tracker, AutoTrackerMetroidState currentSta tracker.ShutUp(); tracker.Say(x => x.AutoTracker.NearShaktool, once: true); tracker.ModeTracker.StartShaktoolMode(); + EnableGameChangedCheck(); return true; } - if (tracker.ModeTracker.ShaktoolMode && - ((currentState is { CurrentRegion: 4, CurrentRoomInRegion: 28 } && prevState.CurrentRoomInRegion == 36) || - currentState.CurrentRegion != 4)) + if (tracker.ModeTracker.ShaktoolMode && (currentState.CurrentRoom == null || + !_shaktoolRooms.Contains(currentState.CurrentRoom.Value))) { - tracker.ModeTracker.StopShaktoolMode(); + DisableShaktoolMode(); return true; } return false; } + + private void EnableGameChangedCheck() + { + if (_enabledOnGameChangedCheck || tracker.AutoTracker == null) return; + _enabledOnGameChangedCheck = true; + tracker.AutoTracker.GameChanged += AutoTrackerOnGameChanged; + } + + private void DisableGameChangedCheck() + { + if (!_enabledOnGameChangedCheck || tracker.AutoTracker == null ) return; + _enabledOnGameChangedCheck = false; + tracker.AutoTracker.GameChanged -= AutoTrackerOnGameChanged; + } + + private void AutoTrackerOnGameChanged(object? sender, EventArgs e) + { + DisableShaktoolMode(); + } + + private void DisableShaktoolMode() + { + if (tracker.ModeTracker.ShaktoolMode) + { + tracker.ModeTracker.StopShaktoolMode(); + } + + DisableGameChangedCheck(); + } } diff --git a/src/TrackerCouncil.Smz3.Tracking/AutoTracking/ZeldaStateChecks/PegWorld.cs b/src/TrackerCouncil.Smz3.Tracking/AutoTracking/ZeldaStateChecks/PegWorld.cs index 1a224dded..95bb58102 100644 --- a/src/TrackerCouncil.Smz3.Tracking/AutoTracking/ZeldaStateChecks/PegWorld.cs +++ b/src/TrackerCouncil.Smz3.Tracking/AutoTracking/ZeldaStateChecks/PegWorld.cs @@ -1,4 +1,5 @@ -using System.Threading.Tasks; +using System; +using System.Threading.Tasks; using SnesConnectorLibrary; using SnesConnectorLibrary.Requests; using SNI; @@ -12,6 +13,8 @@ namespace TrackerCouncil.Smz3.Tracking.AutoTracking.ZeldaStateChecks; /// public class PegWorld(TrackerBase tracker, ISnesConnectorService snesConnector) : IZeldaStateCheck { + private bool _enabledOnGameChangedCheck; + /// /// Executes the check for the current state /// @@ -27,9 +30,43 @@ public bool ExecuteCheck(TrackerBase trackerBase, AutoTrackerZeldaState currentS return true; } + if (tracker.ModeTracker.PegWorldMode && !(currentState.OverworldScreen == 0x62 || currentState is { OverworldScreen: 0, CurrentRoom: 295 })) + { + DisablePegWorldNode(); + } + return false; } + private void EnableGameChangedCheck() + { + if (_enabledOnGameChangedCheck || tracker.AutoTracker == null) return; + _enabledOnGameChangedCheck = true; + tracker.AutoTracker.GameChanged += AutoTrackerOnGameChanged; + } + + private void DisableGameChangedCheck() + { + if (!_enabledOnGameChangedCheck || tracker.AutoTracker == null ) return; + _enabledOnGameChangedCheck = false; + tracker.AutoTracker.GameChanged -= AutoTrackerOnGameChanged; + } + + private void AutoTrackerOnGameChanged(object? sender, EventArgs e) + { + DisablePegWorldNode(); + } + + private void DisablePegWorldNode() + { + if (tracker.ModeTracker.PegWorldMode) + { + tracker.ModeTracker.StopPegWorldMode(); + } + + DisableGameChangedCheck(); + } + private async Task CountPegs() { var response = await snesConnector.MakeMemoryRequestAsync(new SnesSingleMemoryRequest() @@ -46,9 +83,9 @@ private async Task CountPegs() var count = response.Data.ReadUInt8(0); - if (count != null) + if (count != null && tracker.ModeTracker.SetPegs((int)count)) { - tracker.ModeTracker.SetPegs((int)count); + EnableGameChangedCheck(); } } } diff --git a/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerModeService.cs b/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerModeService.cs index 6e19346f4..4a7cfb933 100644 --- a/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerModeService.cs +++ b/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerModeService.cs @@ -72,15 +72,18 @@ public void Peg(float? confidence = null) RestartIdleTimers(); } - public void SetPegs(int count) + public bool SetPegs(int count) { if (count <= PegsPegged) - return; + return false; + + var updated = false; if (!PegWorldMode) { PegWorldMode = true; ToggledPegWorldModeOn?.Invoke(this, new TrackerEventArgs(null, true)); + updated = true; } if (count <= PegWorldModeModule.TotalPegs) @@ -90,7 +93,10 @@ public void SetPegs(int count) PegsPegged = count; Tracker.Say(responses: Responses.PegWorldModePeggedMultiple, tieredKey: delta, wait: true); PegPegged?.Invoke(this, new TrackerEventArgs(null, true)); + updated = true; } + + return updated; } public void StartPegWorldMode(float? confidence = null)