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)