From 3aeb9a98f1df468d565ef95436040523701647be Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Sat, 14 Dec 2024 23:20:19 -0500 Subject: [PATCH 1/5] Fix BossInfo documentation --- src/TrackerCouncil.Smz3.Data.SchemaGenerator/Program.cs | 2 +- .../Configuration/ConfigTypes/BossInfo.cs | 3 +-- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/TrackerCouncil.Smz3.Data.SchemaGenerator/Program.cs b/src/TrackerCouncil.Smz3.Data.SchemaGenerator/Program.cs index b6fd3bc34..4df42bd71 100644 --- a/src/TrackerCouncil.Smz3.Data.SchemaGenerator/Program.cs +++ b/src/TrackerCouncil.Smz3.Data.SchemaGenerator/Program.cs @@ -100,7 +100,7 @@ private static void CreateTemplates(string outputPath) // Boss Template var bossConfig = configProvider.GetBossConfig(new List(), null); var templateBossConfig = new BossConfig(); - templateBossConfig.AddRange(bossConfig.Select(boss => new BossInfo(boss.Boss))); + templateBossConfig.AddRange(bossConfig.Select(boss => new BossInfo() { Boss = boss.Boss })); var exampleBossConfig = BossConfig.Example(); WriteTemplate(templatePath, "bosses", templateBossConfig, exampleBossConfig); diff --git a/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/BossInfo.cs b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/BossInfo.cs index 883300c81..e1fdc12e2 100644 --- a/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/BossInfo.cs +++ b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/BossInfo.cs @@ -15,7 +15,6 @@ public class BossInfo : IMergeable { public BossInfo() { - Name = []; } /// @@ -46,7 +45,7 @@ public BossInfo(string name) /// /// Gets the name of the boss. /// - public SchrodingersString Name { get; set; } + public SchrodingersString? Name { get; set; } /// /// Gets the phrases to respond with when the boss has been tracked (but From bfc382ccffb3194d94634fbc557b55e777bf193e Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Sat, 14 Dec 2024 23:20:37 -0500 Subject: [PATCH 2/5] Update NaturalLanguage.Join to prioritize important items --- .../NaturalLanguage.cs | 64 ++++++++++++++++--- 1 file changed, 55 insertions(+), 9 deletions(-) diff --git a/src/TrackerCouncil.Smz3.Tracking/NaturalLanguage.cs b/src/TrackerCouncil.Smz3.Tracking/NaturalLanguage.cs index 9d635c429..20ee97be1 100644 --- a/src/TrackerCouncil.Smz3.Tracking/NaturalLanguage.cs +++ b/src/TrackerCouncil.Smz3.Tracking/NaturalLanguage.cs @@ -10,6 +10,45 @@ namespace TrackerCouncil.Smz3.Tracking; /// internal static class NaturalLanguage { + /// + /// Returns a string representing a collection of locations as a + /// comma-separated list, but where the last location is separated with + /// "and" instead. + /// + /// The locations. + /// The cap of locations to fully name. + /// + /// A string containing each item in separated + /// with a comma, except for the two items, which are separated with + /// "and". + /// + /// + /// NaturalLanguage.Join(new[]{"one", "two", "three"}) returns + /// "one, two and three". + /// + public static string Join(ICollection locations, int cap = 4) + { + if (locations.Count == 0) + { + return string.Empty; + } + else if (locations.Count == 1) + { + return locations.First().RandomName; + } + else if (locations.Count <= cap) + { + var last = locations.Last().RandomName; + var remainder = locations.SkipLast(1).Select(x => x.RandomName); + return $"{string.Join(", ", remainder)} and {last}"; + } + else + { + var namedLocations = locations.Take(cap).Select(x => x.RandomName); + return $"{string.Join(", ", namedLocations)} and {locations.Count - cap} other locations"; + } + } + /// /// Returns a string representing a collection of items as a /// comma-separated list, but where the last item is separated with @@ -57,21 +96,28 @@ public static string Join(IEnumerable items, Config config) var item = innerItems.First(); // Just pick the first. It's possible (though unlikely) there's multiple items for a single item type var count = innerItems.Count(); return (item, count); - }); + }).ToList(); - var interestingItems = groupedItems.Where(x => x.item.Metadata.IsJunk(config) == false).ToList(); - var junkItems = groupedItems.Where(x => x.item.Metadata.IsJunk(config)).ToList(); + var interestingItems = groupedItems.Where(x => x.item.Metadata.IsProgression(config)).ToList(); + var junkItems = groupedItems.Where(x => !x.item.Metadata.IsProgression(config)).OrderBy(x => x.item.IsDungeonItem).ToList(); if (junkItems.Count == 0) + { return Join(interestingItems.Select(GetPhrase)); + } + else if (interestingItems.Count + junkItems.Count < 5) + { + return Join(interestingItems.Concat(junkItems).Select(GetPhrase)); + } - if (interestingItems.Count == 0) - return Join(junkItems.Select(GetPhrase)); - - if (junkItems.Count > 1) - return Join(interestingItems.Select(GetPhrase).Concat(new[] { $"{junkItems.Count} other items" })); + if (interestingItems.Count <= 3) + { + var numToTake = 3 - interestingItems.Count; + interestingItems.AddRange(junkItems.Take(numToTake)); + junkItems = junkItems.Skip(numToTake).ToList(); + } - return Join(groupedItems.Select(GetPhrase)); + return Join(interestingItems.Select(GetPhrase).Concat([$"{junkItems.Count} other items"])); static string GetPhrase((Item item, int count) x) => x.count > 1 ? $"{x.count} {x.item.Metadata.Plural ?? $"{x.item.Name}s"}": $"{x.item.Name}"; From ec6b288ed9c8256d5b84ab5831253cb5d6839547 Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Sat, 14 Dec 2024 23:21:57 -0500 Subject: [PATCH 3/5] Add RandomName property for easier SchrodingersString usage --- src/TrackerCouncil.Smz3.Data/WorldData/Boss.cs | 5 +++++ .../WorldData/Location.cs | 15 +++++++++++++++ .../WorldData/Regions/IHasBoss.cs | 10 ++++++++++ .../WorldData/Regions/Region.cs | 5 +++++ src/TrackerCouncil.Smz3.Data/WorldData/Room.cs | 2 ++ .../TrackingServices/TrackerBossService.cs | 14 +++++++------- .../VoiceCommands/MultiplayerModule.cs | 6 +++--- .../VoiceCommands/TrackerModule.cs | 11 +++++++++-- 8 files changed, 56 insertions(+), 12 deletions(-) diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Boss.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Boss.cs index 5ca43f814..2df6444f8 100644 --- a/src/TrackerCouncil.Smz3.Data/WorldData/Boss.cs +++ b/src/TrackerCouncil.Smz3.Data/WorldData/Boss.cs @@ -125,4 +125,9 @@ public void UpdateAccessibility(Progression actualProgression, Progression withK /// langword="false"/>. public bool Is(BossType type, string name) => (Type != BossType.None && Type == type) || (Type == BossType.None && Name == name); + + /// + /// Returns a random name from the boss's metadata + /// + public string RandomName => Metadata.Name?.ToString() ?? Name; } diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Location.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Location.cs index c95ee290e..5fb354d83 100644 --- a/src/TrackerCouncil.Smz3.Data/WorldData/Location.cs +++ b/src/TrackerCouncil.Smz3.Data/WorldData/Location.cs @@ -392,6 +392,21 @@ public override string ToString() : $"{Region} - {Name}"; } + /// + /// Returns a random name from the location's metadata + /// + public string RandomName + { + get + { + var randomLocationName = Metadata.Name?.ToString() ?? Name; + return Room != null + ? $"{Room.RandomName} - {randomLocationName}" + : $"{Region.RandomName} - {randomLocationName}"; + } + } + + public IHasTreasure? GetTreasureRegion() => Region as IHasTreasure; public event EventHandler? ClearedUpdated; diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasBoss.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasBoss.cs index a5c14b6bf..fcd869c6f 100644 --- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasBoss.cs +++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasBoss.cs @@ -86,4 +86,14 @@ public void ApplyState(TrackerState? state) /// . /// bool CanBeatBoss(Progression items); + + /// + /// Returns a randomized name from the metadata for the region + /// + public string RandomRegionName => Region.RandomName; + + /// + /// Returns a randomized name from the metadata for the boss + /// + public string RandomBossName => Boss.RandomName; } diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Region.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Region.cs index af8fd1988..d14cf1180 100644 --- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Region.cs +++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Region.cs @@ -153,6 +153,11 @@ private bool MatchesItemPlacementRule(Item item) /// A new string that represents the region. public override string ToString() => Name; + /// + /// Returns a random string from the region's metadata + /// + public string RandomName => Metadata.Name?.ToString() ?? Name; + /// /// Determines whether the region can be entered with the specified /// items. diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Room.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Room.cs index c1bb68661..aa79803f8 100644 --- a/src/TrackerCouncil.Smz3.Data/WorldData/Room.cs +++ b/src/TrackerCouncil.Smz3.Data/WorldData/Room.cs @@ -75,4 +75,6 @@ public Room(Region region, string name, IMetadataService? metadata, params strin /// /// A new string that represents this room. public override string ToString() => $"{Region} - {Name}"; + + public string RandomName => $"{Region.RandomName} - {Metadata.Name?.ToString() ?? Name}"; } diff --git a/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerBossService.cs b/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerBossService.cs index b62a5bf37..0cc8c9141 100644 --- a/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerBossService.cs +++ b/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerBossService.cs @@ -17,7 +17,7 @@ public void MarkBossAsDefeated(IHasBoss region, float? confidence = null, bool a { if (region.BossDefeated && !autoTracked) { - Tracker.Say(response: Responses.DungeonBossAlreadyCleared, args: [region.Metadata.Name, region.BossMetadata.Name]); + Tracker.Say(response: Responses.DungeonBossAlreadyCleared, args: [region.RandomRegionName, region.RandomBossName]); return; } @@ -26,7 +26,7 @@ public void MarkBossAsDefeated(IHasBoss region, float? confidence = null, bool a var addedEvent = History.AddEvent( HistoryEventType.BeatBoss, true, - region.BossMetadata.Name.ToString() ?? $"boss of {region.Metadata.Name}" + region.RandomBossName ); region.Boss.Defeated = true; @@ -49,14 +49,14 @@ public void MarkBossAsDefeated(IHasBoss region, float? confidence = null, bool a } } - Tracker.Say(response: Responses.DungeonBossCleared, args: [region.Metadata.Name, region.BossMetadata.Name]); + Tracker.Say(response: Responses.DungeonBossCleared, args: [region.RandomRegionName, region.RandomBossName]); } else { if (!admittedGuilt && region.BossMetadata.WhenTracked != null) - Tracker.Say(response: region.BossMetadata.WhenTracked, args: [region.BossMetadata.Name]); + Tracker.Say(response: region.BossMetadata.WhenTracked, args: [region.RandomBossName]); else - Tracker.Say(response: region.BossMetadata.WhenDefeated ?? Responses.BossDefeated, args: [region.BossMetadata.Name]); + Tracker.Say(response: region.BossMetadata.WhenDefeated ?? Responses.BossDefeated, args: [region.RandomBossName]); } // Auto track the region's reward @@ -166,13 +166,13 @@ public void MarkBossAsNotDefeated(IHasBoss region, float? confidence = null) { if (!region.BossDefeated) { - Tracker.Say(response: Responses.DungeonBossNotYetCleared, args: [region.Metadata.Name, region.BossMetadata.Name]); + Tracker.Say(response: Responses.DungeonBossNotYetCleared, args: [region.RandomRegionName, region.RandomBossName]); return; } region.BossDefeated = false; BossUpdated?.Invoke(this, new BossTrackedEventArgs(region.Boss, confidence, false)); - Tracker.Say(response: Responses.DungeonBossUncleared, args: [region.Metadata.Name, region.BossMetadata.Name]); + Tracker.Say(response: Responses.DungeonBossUncleared, args: [region.RandomRegionName, region.RandomBossName]); // Try to untrack the associated boss reward item List undoActions = []; diff --git a/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/MultiplayerModule.cs b/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/MultiplayerModule.cs index 32bd2e3a8..64bf4cb19 100644 --- a/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/MultiplayerModule.cs +++ b/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/MultiplayerModule.cs @@ -191,19 +191,19 @@ private void PlayerTrackedBoss(PlayerTrackedBossEventHandlerArgs args) TrackerBase.Say(x => x.Multiplayer.OtherPlayerClearedDungeonWithReward, args: [ args.PhoneticName, - rewardRegion.Metadata.Name, boss.Region.BossMetadata.Name, rewardRegion.RewardMetadata.Name, + rewardRegion.Metadata.Name, boss.Region.RandomBossName, rewardRegion.RewardMetadata.Name, rewardRegion.RewardMetadata.NameWithArticle ]); } else { TrackerBase.Say(x => x.Multiplayer.OtherPlayerClearedDungeonWithoutReward, - args: [args.PhoneticName, treasureRegion.Metadata.Name, boss.Region.BossMetadata.Name]); + args: [args.PhoneticName, treasureRegion.Metadata.Name, boss.Region.RandomBossName]); } } else { - TrackerBase.Say(x => x.Multiplayer.OtherPlayerDefeatedBoss, args: [args.PhoneticName, boss.Metadata.Name]); + TrackerBase.Say(x => x.Multiplayer.OtherPlayerDefeatedBoss, args: [args.PhoneticName, boss.RandomName]); } } diff --git a/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/TrackerModule.cs b/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/TrackerModule.cs index fb4a4c1b2..da6d814f8 100644 --- a/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/TrackerModule.cs +++ b/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/TrackerModule.cs @@ -487,8 +487,15 @@ protected virtual Choices GetBossNames() var bossNames = new Choices(); foreach (var boss in TrackerBase.World.AllBosses) { - foreach (var name in boss.Metadata.Name) - bossNames.Add(new SemanticResultValue(name.Text, boss.Name)); + if (boss.Metadata.Name != null) + { + foreach (var name in boss.Metadata.Name) + bossNames.Add(new SemanticResultValue(name.Text, boss.Name)); + } + else + { + bossNames.Add(new SemanticResultValue(boss.Name, boss.Name)); + } } return bossNames; } From a54de48c37229efda29e264a15a0396ff38266ac Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Sat, 14 Dec 2024 23:22:15 -0500 Subject: [PATCH 4/5] Add location names to LocationCleared spoiler lines --- .../Configuration/ConfigTypes/SpoilerConfig.cs | 10 +++++++++- .../VoiceCommands/SpoilerModule.cs | 17 +++++++++++++++-- 2 files changed, 24 insertions(+), 3 deletions(-) diff --git a/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/SpoilerConfig.cs b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/SpoilerConfig.cs index 908268deb..ffdf5394f 100644 --- a/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/SpoilerConfig.cs +++ b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/SpoilerConfig.cs @@ -91,10 +91,18 @@ public class SpoilerConfig : IMergeable /// public SchrodingersString? ItemsNotFound { get; init; } + /// + /// Gets the phrases to respond with when an item's location has been cleared. + /// {0} is a placeholder for the name of the item, with "a", "an" + /// or "the" and 1 is a placeholder for the name of the location where the item was. + /// + public SchrodingersString? LocationCleared { get; init; } + /// /// Gets the phrases to respond with when all locations that have the item are cleared. /// {0} is a placeholder for the name of the item, with "a", "an" - /// or "the". + /// or "the". {1} is a placeholder for a list of locations where the item was (up + /// to 4). /// public SchrodingersString? LocationsCleared { get; init; } diff --git a/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/SpoilerModule.cs b/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/SpoilerModule.cs index 223c32b6b..a41f8b799 100644 --- a/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/SpoilerModule.cs +++ b/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/SpoilerModule.cs @@ -218,10 +218,23 @@ private void RevealItemLocation(Item item) TrackerBase.Say(x => x.Spoilers.ItemNotFound, args: [item.Metadata.NameWithArticle]); return; } + + // The item exists, but all locations are cleared else if (locations.Count > 0 && locations.All(x => x.Cleared)) { - // The item exists, but all locations are cleared - TrackerBase.Say(x => x.Spoilers.LocationsCleared, args: [item.Metadata.NameWithArticle]); + // Prioritize locations that haven't been auto tracked + var nonAutoTrackedLocations = locations.Where(x => !x.Autotracked); + var locationsToAnnounce = (nonAutoTrackedLocations.Any() ? nonAutoTrackedLocations : locations).ToList(); + + if (locationsToAnnounce.Count == 1) + { + TrackerBase.Say(x => x.Spoilers.LocationCleared, args: [item.Metadata.NameWithArticle, locationsToAnnounce.First().RandomName]); + } + else + { + TrackerBase.Say(x => x.Spoilers.LocationsCleared, args: [item.Metadata.NameWithArticle, NaturalLanguage.Join(locationsToAnnounce)]); + } + return; } From 8fd7a9803c327b785896d5e2ff5bea5a32764f1c Mon Sep 17 00:00:00 2001 From: MattEqualsCoder Date: Sat, 14 Dec 2024 23:27:51 -0500 Subject: [PATCH 5/5] Update spoiler lines for backwards compatibility --- .../Configuration/ConfigTypes/SpoilerConfig.cs | 11 +++++++++-- .../VoiceCommands/SpoilerModule.cs | 4 ++-- 2 files changed, 11 insertions(+), 4 deletions(-) diff --git a/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/SpoilerConfig.cs b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/SpoilerConfig.cs index ffdf5394f..d42c6e988 100644 --- a/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/SpoilerConfig.cs +++ b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/SpoilerConfig.cs @@ -91,12 +91,19 @@ public class SpoilerConfig : IMergeable /// public SchrodingersString? ItemsNotFound { get; init; } + /// + /// Gets the phrases to respond with when all locations that have the item are cleared. + /// {0} is a placeholder for the name of the item, with "a", "an" + /// or "the". + /// + public SchrodingersString? LocationsCleared { get; init; } + /// /// Gets the phrases to respond with when an item's location has been cleared. /// {0} is a placeholder for the name of the item, with "a", "an" /// or "the" and 1 is a placeholder for the name of the location where the item was. /// - public SchrodingersString? LocationCleared { get; init; } + public SchrodingersString? ItemLocationCleared { get; init; } /// /// Gets the phrases to respond with when all locations that have the item are cleared. @@ -104,7 +111,7 @@ public class SpoilerConfig : IMergeable /// or "the". {1} is a placeholder for a list of locations where the item was (up /// to 4). /// - public SchrodingersString? LocationsCleared { get; init; } + public SchrodingersString? ItemLocationsCleared { get; init; } /// /// Gets the phrases that spoil the item that is at the requested diff --git a/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/SpoilerModule.cs b/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/SpoilerModule.cs index a41f8b799..8b5d35dbf 100644 --- a/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/SpoilerModule.cs +++ b/src/TrackerCouncil.Smz3.Tracking/VoiceCommands/SpoilerModule.cs @@ -228,11 +228,11 @@ private void RevealItemLocation(Item item) if (locationsToAnnounce.Count == 1) { - TrackerBase.Say(x => x.Spoilers.LocationCleared, args: [item.Metadata.NameWithArticle, locationsToAnnounce.First().RandomName]); + TrackerBase.Say(x => x.Spoilers.ItemLocationCleared, args: [item.Metadata.NameWithArticle, locationsToAnnounce.First().RandomName]); } else { - TrackerBase.Say(x => x.Spoilers.LocationsCleared, args: [item.Metadata.NameWithArticle, NaturalLanguage.Join(locationsToAnnounce)]); + TrackerBase.Say(x => x.Spoilers.ItemLocationsCleared, args: [item.Metadata.NameWithArticle, NaturalLanguage.Join(locationsToAnnounce)]); } return;