diff --git a/src/TrackerCouncil.Smz3.Abstractions/TrackerBase.cs b/src/TrackerCouncil.Smz3.Abstractions/TrackerBase.cs
index c66910f5d..dfaf28686 100644
--- a/src/TrackerCouncil.Smz3.Abstractions/TrackerBase.cs
+++ b/src/TrackerCouncil.Smz3.Abstractions/TrackerBase.cs
@@ -75,7 +75,7 @@ public abstract class TrackerBase : IDisposable
///
/// Gets the configured responses.
///
- public ResponseConfig Responses { get; protected init; } = null!;
+ public ResponseConfig Responses { get; protected set; } = null!;
///
/// Gets a collection of basic requests and responses.
@@ -370,5 +370,11 @@ protected virtual void OnVoiceRecognitionEnabledChanged()
VoiceRecognitionEnabledChanged?.Invoke(this, EventArgs.Empty);
}
+ ///
+ /// Updates the responses to reflect the selected tracker image pack
+ ///
+ /// The name of the pack
+ public abstract void SetImagePack(string? packName);
+
public abstract void Dispose();
}
diff --git a/src/TrackerCouncil.Smz3.Data/Configuration/ConfigFiles/TrackerProfileConfig.cs b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigFiles/TrackerProfileConfig.cs
new file mode 100644
index 000000000..ca0293b05
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigFiles/TrackerProfileConfig.cs
@@ -0,0 +1,7 @@
+namespace TrackerCouncil.Smz3.Data.Configuration.ConfigFiles;
+
+public class TrackerProfileConfig
+{
+ public bool UseAltVoice { get; set; }
+ public ResponseConfig? ResponseConfig { get; set; }
+}
diff --git a/src/TrackerCouncil.Smz3.Data/Options/GeneralOptions.cs b/src/TrackerCouncil.Smz3.Data/Options/GeneralOptions.cs
index 7d4e27c09..c58a8d12a 100644
--- a/src/TrackerCouncil.Smz3.Data/Options/GeneralOptions.cs
+++ b/src/TrackerCouncil.Smz3.Data/Options/GeneralOptions.cs
@@ -265,6 +265,7 @@ public bool Validate()
AutoMapUpdateBehavior = AutoMapUpdateBehavior ?? Options.AutoMapUpdateBehavior.Disabled,
VoiceFrequency = TrackerVoiceFrequency,
TrackerProfiles = SelectedProfiles,
+ TrackerImagePackName = TrackerSpeechImagePack,
UndoExpirationTime = UndoExpirationTime,
TrackDisplayFormat = TrackDisplayFormat,
MsuTrackOutputPath = MsuTrackOutputPath,
diff --git a/src/TrackerCouncil.Smz3.Data/Options/TrackerOptions.cs b/src/TrackerCouncil.Smz3.Data/Options/TrackerOptions.cs
index 1a91ea75a..228bdf890 100644
--- a/src/TrackerCouncil.Smz3.Data/Options/TrackerOptions.cs
+++ b/src/TrackerCouncil.Smz3.Data/Options/TrackerOptions.cs
@@ -100,6 +100,11 @@ public record TrackerOptions
///
public ICollection TrackerProfiles { get; set; } = new List() { "Sassy" };
+ ///
+ /// Gets the name of the tracker images that the user selected
+ ///
+ public string? TrackerImagePackName { get; set; }
+
///
/// The output style for the current song (Deprecated)
///
diff --git a/src/TrackerCouncil.Smz3.Data/Options/TrackerSpeechReactionImages.cs b/src/TrackerCouncil.Smz3.Data/Options/TrackerSpeechReactionImages.cs
index fe270dbe3..509d5d775 100644
--- a/src/TrackerCouncil.Smz3.Data/Options/TrackerSpeechReactionImages.cs
+++ b/src/TrackerCouncil.Smz3.Data/Options/TrackerSpeechReactionImages.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using TrackerCouncil.Smz3.Data.Configuration.ConfigFiles;
namespace TrackerCouncil.Smz3.Data.Options;
@@ -38,6 +39,8 @@ public class TrackerSpeechImagePack
///
public required Dictionary Reactions { get; set; }
+ public required TrackerProfileConfig? ProfileConfig { get; set; }
+
///
/// Gets the reaction images for a given reaction type. Will return the default reaction type if not specified
/// or the requested reaction type is not present in this pack.
diff --git a/src/TrackerCouncil.Smz3.Data/Services/TrackerSpriteService.cs b/src/TrackerCouncil.Smz3.Data/Services/TrackerSpriteService.cs
index 965c150eb..5a9dae786 100644
--- a/src/TrackerCouncil.Smz3.Data/Services/TrackerSpriteService.cs
+++ b/src/TrackerCouncil.Smz3.Data/Services/TrackerSpriteService.cs
@@ -1,8 +1,12 @@
+using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using Microsoft.Extensions.Logging;
+using TrackerCouncil.Smz3.Data.Configuration.ConfigFiles;
using TrackerCouncil.Smz3.Data.Options;
+using YamlDotNet.Serialization;
+using YamlDotNet.Serialization.NamingConventions;
namespace TrackerCouncil.Smz3.Data.Services;
@@ -10,7 +14,7 @@ namespace TrackerCouncil.Smz3.Data.Services;
/// Service for loading tracker speech sprites
///
///
-public class TrackerSpriteService(OptionsFactory optionsFactory)
+public class TrackerSpriteService(OptionsFactory optionsFactory, ILogger logger)
{
private List _packs = [];
@@ -82,6 +86,9 @@ private void LoadPack(string folder, bool isUserPack)
packName += " (Custom)";
}
+ var config = GetConfig(Path.Combine(folder, "config.yml"))
+ ?? GetConfig(Path.Combine(folder, "config.yaml"));
+
_packs.Add(new TrackerSpeechImagePack
{
Name = packName,
@@ -90,7 +97,31 @@ private void LoadPack(string folder, bool isUserPack)
IdleImage = Path.Combine(folder, "default_idle.png"),
TalkingImage = Path.Combine(folder, "default_talk.png"),
},
- Reactions = reactions
+ Reactions = reactions,
+ ProfileConfig = config,
});
}
+
+ private TrackerProfileConfig? GetConfig(string path)
+ {
+ if (!File.Exists(path))
+ {
+ return null;
+ }
+
+ var yml = File.ReadAllText(path);
+ var deserializer = new DeserializerBuilder()
+ .WithNamingConvention(PascalCaseNamingConvention.Instance)
+ .IgnoreUnmatchedProperties()
+ .Build();
+ try
+ {
+ return deserializer.Deserialize(yml);
+ }
+ catch (Exception e)
+ {
+ logger.LogError(e, "Unable to parse tracker profile config {Path}", path);
+ return null;
+ }
+ }
}
diff --git a/src/TrackerCouncil.Smz3.Tracking/Services/ICommunicator.cs b/src/TrackerCouncil.Smz3.Tracking/Services/ICommunicator.cs
index 7b773343d..54dea9c92 100644
--- a/src/TrackerCouncil.Smz3.Tracking/Services/ICommunicator.cs
+++ b/src/TrackerCouncil.Smz3.Tracking/Services/ICommunicator.cs
@@ -42,7 +42,7 @@ public void Abort() { }
/// When overridden in an implementing class, uses an alternate voice
/// when communicating with the player.
///
- public void UseAlternateVoice() { }
+ public void UseAlternateVoice(bool useAlt = true) { }
///
/// When overridden in an implementing class, increases the
diff --git a/src/TrackerCouncil.Smz3.Tracking/Services/TextToSpeechCommunicator.cs b/src/TrackerCouncil.Smz3.Tracking/Services/TextToSpeechCommunicator.cs
index 1c85d211c..6591b4920 100644
--- a/src/TrackerCouncil.Smz3.Tracking/Services/TextToSpeechCommunicator.cs
+++ b/src/TrackerCouncil.Smz3.Tracking/Services/TextToSpeechCommunicator.cs
@@ -72,14 +72,14 @@ public TextToSpeechCommunicator(TrackerOptionsAccessor trackerOptionsAccessor, I
///
/// Selects a different text-to-speech voice.
///
- public void UseAlternateVoice()
+ public void UseAlternateVoice(bool useAlt = true)
{
if (!OperatingSystem.IsWindows())
{
return;
}
- _tts.SelectVoiceByHints(VoiceGender.Male);
+ _tts.SelectVoiceByHints(useAlt ? VoiceGender.Male : VoiceGender.Female);
}
///
diff --git a/src/TrackerCouncil.Smz3.Tracking/Tracker.cs b/src/TrackerCouncil.Smz3.Tracking/Tracker.cs
index a60222bf2..1ccfb4100 100644
--- a/src/TrackerCouncil.Smz3.Tracking/Tracker.cs
+++ b/src/TrackerCouncil.Smz3.Tracking/Tracker.cs
@@ -47,10 +47,13 @@ public sealed class Tracker : TrackerBase, IDisposable
private readonly ITrackerStateService _stateService;
private readonly ITrackerTimerService _timerService;
private readonly ISpeechRecognitionService _recognizer;
+ private readonly TrackerSpriteService _trackerSpriteService;
+ private readonly HashSet _saidLines = new();
+
private bool _disposed;
private string? _lastSpokenText;
- private readonly bool _alternateTracker;
- private readonly HashSet _saidLines = new();
+ private string? _previousImagePackName;
+ private ResponseConfig _defaultResponseConfig;
///
/// Initializes a new instance of the class.
@@ -71,6 +74,7 @@ public sealed class Tracker : TrackerBase, IDisposable
///
///
///
+ ///
public Tracker(IWorldAccessor worldAccessor,
TrackerModuleFactory moduleFactory,
IChatClient chatClient,
@@ -82,7 +86,8 @@ public Tracker(IWorldAccessor worldAccessor,
Configs configs,
ITrackerStateService stateService,
ITrackerTimerService timerService,
- IServiceProvider serviceProvider)
+ IServiceProvider serviceProvider,
+ TrackerSpriteService trackerSpriteService)
{
if (trackerOptions.Options == null)
throw new InvalidOperationException("Tracker options have not yet been activated.");
@@ -98,10 +103,12 @@ public Tracker(IWorldAccessor worldAccessor,
_communicator = communicator;
_stateService = stateService;
_timerService = timerService;
+ _trackerSpriteService = trackerSpriteService;
// Initialize the tracker configuration
Configs = configs;
- Responses = configs.Responses;
+ _defaultResponseConfig = Responses = configs.Responses;
+ SetImagePack(trackerOptions.Options.TrackerImagePackName);
Requests = configs.Requests;
PlayerProgressionService.ResetProgression();
@@ -119,14 +126,6 @@ public Tracker(IWorldAccessor worldAccessor,
_idleTimers = new();
}
-
- // Initialize the text-to-speech
- if (s_random.NextDouble() <= 0.01)
- {
- _alternateTracker = true;
- _communicator.UseAlternateVoice();
- }
-
// Initialize the speech recognition engine
if (_trackerOptions.Options.SpeechRecognitionMode == SpeechRecognitionMode.Disabled ||
!OperatingSystem.IsWindows())
@@ -217,6 +216,30 @@ public override bool Load(GeneratedRom rom, string romPath)
return false;
}
+ public override void SetImagePack(string? packName)
+ {
+ if (packName == _previousImagePackName)
+ {
+ return;
+ }
+
+ _previousImagePackName = packName;
+ var profileConfig = _trackerSpriteService.GetPack(packName).ProfileConfig;
+ _communicator.UseAlternateVoice(profileConfig?.UseAltVoice ?? false);
+
+ if (profileConfig?.ResponseConfig == null)
+ {
+ Responses = _defaultResponseConfig;
+ return;
+ }
+
+ var newResponseConfig = ResponseConfig.Default();
+ IMergeable mergeableConfig = newResponseConfig;
+ mergeableConfig.MergeFrom(_defaultResponseConfig);
+ mergeableConfig.MergeFrom(profileConfig.ResponseConfig);
+ Responses = newResponseConfig;
+ }
+
private void LoadServices(IServiceProvider serviceProvider)
{
var interfaceNamespace = typeof(TrackerBase).Namespace;
@@ -319,7 +342,7 @@ public override bool TryStartTracking()
loadError = true;
}
- Say(response: _alternateTracker ? Responses.StartingTrackingAlternate : Responses.StartedTracking);
+ Say(response: Responses.StartedTracking);
RestartIdleTimers();
return !loadError;
}
diff --git a/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/GoModeModule.cs b/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/GoModeModule.cs
index 83dd69b57..06b2839fb 100644
--- a/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/GoModeModule.cs
+++ b/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/GoModeModule.cs
@@ -2,7 +2,6 @@
using System.Diagnostics.CodeAnalysis;
using Microsoft.Extensions.Logging;
using TrackerCouncil.Smz3.Abstractions;
-using TrackerCouncil.Smz3.Data.Configuration.ConfigFiles;
using TrackerCouncil.Smz3.Tracking.Services;
namespace TrackerCouncil.Smz3.Tracking.VoiceCommands;
@@ -12,8 +11,6 @@ namespace TrackerCouncil.Smz3.Tracking.VoiceCommands;
///
public class GoModeModule : TrackerModule
{
- private ResponseConfig _responseConfig;
-
///
/// Initializes a new instance of the class.
///
@@ -21,11 +18,9 @@ public class GoModeModule : TrackerModule
/// Service to get item information
/// Service to get world information
/// Used to log information.
- ///
- public GoModeModule(TrackerBase tracker, IPlayerProgressionService playerProgressionService, IWorldQueryService worldQueryService, ILogger logger, ResponseConfig responseConfig)
+ public GoModeModule(TrackerBase tracker, IPlayerProgressionService playerProgressionService, IWorldQueryService worldQueryService, ILogger logger)
: base(tracker, playerProgressionService, worldQueryService, logger)
{
- _responseConfig = responseConfig;
}
private GrammarBuilder GetGoModeRule(List prompts)
@@ -38,12 +33,12 @@ private GrammarBuilder GetGoModeRule(List prompts)
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")]
public override void AddCommands()
{
- if (_responseConfig.GoModePrompts == null)
+ if (TrackerBase.Responses.GoModePrompts == null)
{
return;
}
- AddCommand("Toggle Go Mode", GetGoModeRule(_responseConfig.GoModePrompts), (result) =>
+ AddCommand("Toggle Go Mode", GetGoModeRule(TrackerBase.Responses.GoModePrompts), (result) =>
{
TrackerBase.ModeTracker.ToggleGoMode(result.Confidence);
});
diff --git a/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/GoalModule.cs b/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/GoalModule.cs
index 343254eed..4f13e93c6 100644
--- a/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/GoalModule.cs
+++ b/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/GoalModule.cs
@@ -14,7 +14,6 @@ namespace TrackerCouncil.Smz3.Tracking.VoiceCommands;
///
public class GoalModule : TrackerModule
{
- private ResponseConfig _responseConfig;
private const string ItemCountKey = "ItemCount";
///
@@ -24,11 +23,9 @@ public class GoalModule : TrackerModule
/// Service to get item information
/// Service to get world information
/// Used to log information.
- ///
- public GoalModule(TrackerBase tracker, IPlayerProgressionService playerProgressionService, IWorldQueryService worldQueryService, ILogger logger, ResponseConfig responseConfig)
+ public GoalModule(TrackerBase tracker, IPlayerProgressionService playerProgressionService, IWorldQueryService worldQueryService, ILogger logger)
: base(tracker, playerProgressionService, worldQueryService, logger)
{
- _responseConfig = responseConfig;
}
[SuppressMessage("Interoperability", "CA1416:Validate platform compatibility")]