Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Clear dungeon fix #90

Merged
merged 3 commits into from
Mar 23, 2022
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion src/Randomizer.SMZ3.Tracking/Configuration/DungeonInfo.cs
Original file line number Diff line number Diff line change
Expand Up @@ -222,7 +222,7 @@ public bool IsInRegion(Region region)
public IReadOnlyCollection<Location> GetLocations(World world)
{
var region = GetRegion(world);
return region.Locations.ToImmutableList();
return region.Locations.Where(x => x.Type != LocationType.NotInDungeon).ToImmutableList();
}
}
}
55 changes: 50 additions & 5 deletions src/Randomizer.SMZ3.Tracking/Configuration/ResponseConfig.cs
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,8 @@ public class ResponseConfig
public SchrodingersString? StartedTracking { get; init; }

/// <summary>
/// Gets the phrases to respond with when tracker starts in "alternate" mode.
/// Gets the phrases to respond with when tracker starts in "alternate"
/// mode.
/// </summary>
public SchrodingersString? StartingTrackingAlternate { get; init; }

Expand Down Expand Up @@ -290,14 +291,58 @@ public class ResponseConfig
public SchrodingersString NoRemainingDungeons { get; init; }
= new SchrodingersString("You already marked every dungeon.");

/// <summary>
/// Gets the phrases to respond with when clearing all locations in a
/// dungeon.
/// </summary>
/// <remarks>
/// <c>{0}</c> is a placeholder for the name of the dungeon.
/// </remarks>
public SchrodingersString DungeonCleared { get; init; }
= new("Cleared everything in {0}.");

/// <summary>
/// Gets the phrases to respond with when clearing all location in a
/// dungeon, but all locations are already cleared.
/// </summary>
/// <remarks>
/// <c>{0}</c> is a placeholder for the name of the dungeon.
/// </remarks>
public SchrodingersString DungeonAlreadyCleared { get; init; }
= new("But you already got everything in {0}.");

/// <summary>
/// Gets the phrases to respond with when clearing all locations in a
/// dungeon, but some of the cleared locations were out of logic.
/// </summary>
/// <remarks>
/// <c>{0}</c> is a placeholder for the name of the dungeon. <c>{1}</c>
/// is a placeholder for the name of a location that was missed.
/// <c>{2}</c> is a placeholder for the items that are required for a
/// missed location.
/// </remarks>
public SchrodingersString DungeonClearedWithInaccessibleItems { get; init; }
= new("Including some out of logic checks that require {2}, such as {1}.");

/// <summary>
/// Gets the phrases to respond with when clearing all locations in a
/// dungeon, but some of the cleared locations were out of logic with too many missing items.
/// </summary>
/// <remarks>
/// <c>{0}</c> is a placeholder for the name of the dungeon. <c>{1}</c>
/// is a placeholder for the name of a location that was missed.
/// </remarks>
public SchrodingersString DungeonClearedWithTooManyInaccessibleItems { get; init; }
= new("Are you sure you got everything?");

/// <summary>
/// Gets the phrases to respond with when clearing a dungeon.
/// </summary>
/// <remarks>
/// <c>{0}</c> is a placeholder for the name of the dungeon that was
/// cleared. <c>{1}</c> is a placeholder for the boss of the dungeon.
/// </remarks>
public SchrodingersString DungeonCleared { get; init; }
public SchrodingersString DungeonBossCleared { get; init; }
= new SchrodingersString("Cleared {0}.", "Marked {1} as defeated.");

/// <summary>
Expand All @@ -308,7 +353,7 @@ public class ResponseConfig
/// <c>{0}</c> is a placeholder for the name of the dungeon that was
/// cleared. <c>{1}</c> is a placeholder for the boss of the dungeon.
/// </remarks>
public SchrodingersString DungeonAlreadyCleared { get; init; }
public SchrodingersString DungeonBossAlreadyCleared { get; init; }
= new SchrodingersString("You already cleared {0}.", "You already defeated {1}.");

/// <summary>
Expand All @@ -319,7 +364,7 @@ public class ResponseConfig
/// <c>{0}</c> is a placeholder for the name of the dungeon. <c>{1}</c>
/// is a placeholder for the boss of the dungeon.
/// </remarks>
public SchrodingersString DungeonUncleared { get; init; }
public SchrodingersString DungeonBossUncleared { get; init; }
= new SchrodingersString("Reset {0}.", "Marked {1} as still alive.");

/// <summary>
Expand All @@ -330,7 +375,7 @@ public class ResponseConfig
/// <c>{0}</c> is a placeholder for the name of the dungeon. <c>{1}</c>
/// is a placeholder for the boss of the dungeon.
/// </remarks>
public SchrodingersString DungeonNotYetCleared { get; init; }
public SchrodingersString DungeonBossNotYetCleared { get; init; }
= new SchrodingersString("You haven't cleared {0} yet.", "You never defeated {1} in the first place.");

/// <summary>
Expand Down
94 changes: 64 additions & 30 deletions src/Randomizer.SMZ3.Tracking/Tracker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1381,17 +1381,8 @@ public void TrackItemAmount(ItemData item, int count, float confidence)
/// </param>
public void ClearArea(IHasLocations area, bool trackItems, bool includeUnavailable = false, float? confidence = null, bool assumeKeys = false)
{
Action? undoTrackDungeon = null;

var dungeon = WorldInfo.Dungeons.SingleOrDefault(x => area is Region region && x.Is(region));
if (dungeon != null)
{
assumeKeys = true; // Always assume keys when clearing the dungeon itself
}

var locations = area.Locations
.Where(x => !x.Cleared)
.WhereIf(dungeon != null, x => x.Type != LocationType.NotInDungeon)
.WhereUnless(includeUnavailable, x => x.IsAvailable(GetProgression(assumeKeys)))
.ToImmutableList();

Expand All @@ -1401,7 +1392,6 @@ public void ClearArea(IHasLocations area, bool trackItems, bool includeUnavailab
{
var outOfLogicLocations = area.Locations
.Where(x => !x.Cleared)
.WhereIf(dungeon != null, x => x.Type != LocationType.NotInDungeon)
.Count();

if (outOfLogicLocations > 1)
Expand All @@ -1413,12 +1403,6 @@ public void ClearArea(IHasLocations area, bool trackItems, bool includeUnavailab
}
else
{
if (dungeon != null)
{
dungeon.Cleared = true;
OnDungeonUpdated(new(confidence));
}

// If there is only one (available) item here, just call the
// regular TrackItem instead
var onlyLocation = locations.TrySingle();
Expand Down Expand Up @@ -1502,12 +1486,6 @@ public void ClearArea(IHasLocations area, bool trackItems, bool includeUnavailab
{
Say(x => x.ClearedMultipleItems, itemsCleared, area.GetName());
}

if (dungeon != null && treasureTracked > 0)
{
TrackDungeonTreasure(dungeon, amount: treasureTracked);
undoTrackDungeon = _undoHistory.Pop();
}
}
OnItemTracked(new ItemTrackedEventArgs(null, confidence));
}
Expand All @@ -1516,9 +1494,6 @@ public void ClearArea(IHasLocations area, bool trackItems, bool includeUnavailab

AddUndo(() =>
{
if (dungeon != null)
dungeon.Cleared = false;

foreach (var location in locations)
{
if (trackItems)
Expand All @@ -1529,11 +1504,70 @@ public void ClearArea(IHasLocations area, bool trackItems, bool includeUnavailab
}
location.Cleared = false;
}
undoTrackDungeon?.Invoke();
UpdateTrackerProgression = true;
});
}

/// <summary>
/// Marks all locations and treasure within a dungeon as cleared.
/// </summary>
/// <param name="dungeon">The dungeon to clear.</param>
/// <param name="confidence">The speech recognition confidence.</param>
public void ClearDungeon(DungeonInfo dungeon, float? confidence = null)
{
var remaining = dungeon.TreasureRemaining;
if (remaining > 0)
{
dungeon.TreasureRemaining = 0;
dungeon.Cleared = true;
}

var progress = GetProgression();
var locations = dungeon.GetLocations(World).Where(x => !x.Cleared).ToList();
var inaccessibleLocations = locations.Where(x => !x.IsAvailable(progress)).ToList();
if (locations.Count > 0)
{
locations.ForEach(x => x.Cleared = true);
}

if (remaining > 0 || locations.Count > 0)
{
Say(x => x.DungeonCleared, dungeon.Name);
if (inaccessibleLocations.Count > 0)
{
var anyMissedLocation = inaccessibleLocations.Random(s_random);
var locationInfo = WorldInfo.Location(anyMissedLocation);
var missingItemCombinations = Logic.GetMissingRequiredItems(anyMissedLocation, progress);
if (missingItemCombinations.Any())
{
var missingItems = missingItemCombinations.Random(s_random)
.Select(FindItemByType)
.NonNull();
var missingItemsText = NaturalLanguage.Join(missingItems, World.Config);
Say(x => x.DungeonClearedWithInaccessibleItems, dungeon.Name, locationInfo.Name, missingItemsText);
}
else
{
Say(x => x.DungeonClearedWithTooManyInaccessibleItems, dungeon.Name, locationInfo.Name);
}
}
}
else
{
Say(x => x.DungeonAlreadyCleared, dungeon.Name);
}

OnDungeonUpdated(new(confidence));
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Minor, but is there a reason to do this and the below if it doesn't actually do anything?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

No, I was lazy but you're right, I should fix this.


AddUndo(() =>
{
dungeon.TreasureRemaining = remaining;
if (remaining > 0)
dungeon.Cleared = false;
locations.ForEach(x => x.Cleared = false);
});
}

/// <summary>
/// Clears an item from the specified location.
/// </summary>
Expand Down Expand Up @@ -1595,12 +1629,12 @@ public void MarkDungeonAsCleared(DungeonInfo dungeon, float? confidence = null)

if (dungeon.Cleared)
{
Say(Responses.DungeonAlreadyCleared.Format(dungeon.Name, dungeon.Boss));
Say(Responses.DungeonBossAlreadyCleared.Format(dungeon.Name, dungeon.Boss));
return;
}

dungeon.Cleared = true;
Say(Responses.DungeonCleared.Format(dungeon.Name, dungeon.Boss));
Say(Responses.DungeonBossCleared.Format(dungeon.Name, dungeon.Boss));

// Try to track the associated boss reward item
Action? undoTrack = null;
Expand Down Expand Up @@ -1697,13 +1731,13 @@ public void MarkDungeonAsIncomplete(DungeonInfo dungeon, float? confidence = nul
{
if (!dungeon.Cleared)
{
Say(Responses.DungeonNotYetCleared.Format(dungeon.Name, dungeon.Boss));
Say(Responses.DungeonBossNotYetCleared.Format(dungeon.Name, dungeon.Boss));
return;
}

UpdateTrackerProgression = true;
dungeon.Cleared = false;
Say(Responses.DungeonUncleared.Format(dungeon.Name, dungeon.Boss));
Say(Responses.DungeonBossUncleared.Format(dungeon.Name, dungeon.Boss));

// Try to untrack the associated boss reward item
Action? undoUnclear = null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,11 @@ public LocationTrackingModule(Tracker tracker, ILogger<LocationTrackingModule> l
includeUnavailable: false,
confidence: result.Confidence);
}
else if (result.Semantics.ContainsKey(DungeonKey))
{
var dungeon = GetDungeonFromResult(tracker, result);
tracker.ClearDungeon(dungeon, result.Confidence);
}
else if (result.Semantics.ContainsKey(RegionKey))
{
var region = GetRegionFromResult(tracker, result);
Expand Down Expand Up @@ -106,8 +111,14 @@ private GrammarBuilder GetClearLocationRule()

private GrammarBuilder GetClearAreaRule()
{
var dungeonNames = GetDungeonNames(includeDungeonsWithoutReward: true);
var roomNames = GetRoomNames();
var regionNames = GetRegionNames();
var regionNames = GetRegionNames(excludeDungeons: true);

var clearDungeon = new GrammarBuilder()
.Append("Hey tracker,")
.OneOf("clear", "please clear")
.Append(DungeonKey, dungeonNames);

var clearRoom = new GrammarBuilder()
.Append("Hey tracker,")
Expand All @@ -119,7 +130,7 @@ private GrammarBuilder GetClearAreaRule()
.OneOf("clear", "please clear")
.Append(RegionKey, regionNames);

return GrammarBuilder.Combine(clearRoom, clearRegion);
return GrammarBuilder.Combine(clearDungeon, clearRoom, clearRegion);
}
}
}
5 changes: 4 additions & 1 deletion src/Randomizer.SMZ3.Tracking/VoiceCommands/TrackerModule.cs
Original file line number Diff line number Diff line change
Expand Up @@ -452,12 +452,15 @@ protected virtual Choices GetRoomNames()
/// A new <see cref="Choices"/> object representing all possible region
/// names mapped to the primary region name.
/// </returns>
protected virtual Choices GetRegionNames()
protected virtual Choices GetRegionNames(bool excludeDungeons = false)
{
var regionNames = new Choices();

foreach (var region in Tracker.WorldInfo.Regions)
{
if (excludeDungeons && Tracker.WorldInfo.Dungeon(region.TypeName) != null)
continue;

foreach (var name in region.Name)
regionNames.Add(new SemanticResultValue(name.Text, region.TypeName));
}
Expand Down
28 changes: 22 additions & 6 deletions src/Randomizer.SMZ3.Tracking/tracker.json
Original file line number Diff line number Diff line change
Expand Up @@ -1352,23 +1352,39 @@
"You did that already."
],
"DungeonCleared": [
"Cleared all items from {0}",
"Marked {0} as all clear"
],
"DungeonAlreadyCleared": [
"But you already cleared {0}.",
"But there's nothing left in {0}."
],
"DungeonClearedWithInaccessibleItems": [
"Including some out of logic checks that need {2}, like {1}.",
"Including some inaccessible areas that require {2}, such as {1}.",
"Also cleared some checks that require {2}. You might want to double-check {1}.",
"You might want to double-check {1} because it requires {2}."
],
"DungeonClearedWithTooManyInaccessibleItems": [
"Are you sure you cleared {0}? You're missing way too many items to get to {1}.",
"I doubt you got {1} though. Not with that many missing items.",
"I doubt you got {1} though. Not without so many items."
],
"DungeonBossCleared": [
"Marked {1} as defeated.",
"Congratulations on beating off {1}.",
"RIP {1}",
[ "f", 0.05 ]
],
"DungeonAlreadyCleared": [
"But you already cleared {0}.",
"DungeonBossAlreadyCleared": [
"But you already defeated {1}.",
"But {1} is already dead."
],
"DungeonUncleared": [
"Marked {0} as incomplete.",
"DungeonBossUncleared": [
"Marked {1} as still alive.",
"Revived {1}."
],
"DungeonNotYetCleared": [
"But you haven't cleared {0} yet.",
"DungeonBossNotYetCleared": [
"But you never defeated {1} in the first place."
],
"DungeonRequirementMarked": [
Expand Down
Loading