diff --git a/.gitignore b/.gitignore
index 45e8f02a5..5ce039cab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -347,4 +347,7 @@ healthchecksdb
/asar/
setup/Output/
patch-config*
-**/.DS_Store
\ No newline at end of file
+**/.DS_Store
+**/*.db
+**/*.db-shm
+**/*.db-wal
diff --git a/TrackerCouncil.Smz3.sln b/TrackerCouncil.Smz3.sln
index b98cee2dd..b9543006e 100644
--- a/TrackerCouncil.Smz3.sln
+++ b/TrackerCouncil.Smz3.sln
@@ -11,11 +11,6 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TrackerCouncil.Smz3.Shared"
EndProject
Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TrackerCouncil.Smz3.Tools", "src\TrackerCouncil.Smz3.Tools\TrackerCouncil.Smz3.Tools.csproj", "{B594A95C-7B87-447A-8A80-EBF6991EB04C}"
EndProject
-Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TrackerCouncil.Smz3.UI.Legacy", "src\TrackerCouncil.Smz3.UI.Legacy\TrackerCouncil.Smz3.UI.Legacy.csproj", "{CCEDE012-894B-48F6-811E-B2324E74C604}"
- ProjectSection(ProjectDependencies) = postProject
- {A9465041-3416-4C60-A160-1454A045B90F} = {A9465041-3416-4C60-A160-1454A045B90F}
- EndProjectSection
-EndProject
Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{F11C9119-7B5E-46E4-9A58-E157B80C7E9F}"
ProjectSection(SolutionItems) = preProject
.editorconfig = .editorconfig
@@ -69,10 +64,6 @@ Global
{B594A95C-7B87-447A-8A80-EBF6991EB04C}.Debug|Any CPU.Build.0 = Debug|Any CPU
{B594A95C-7B87-447A-8A80-EBF6991EB04C}.Release|Any CPU.ActiveCfg = Release|Any CPU
{B594A95C-7B87-447A-8A80-EBF6991EB04C}.Release|Any CPU.Build.0 = Release|Any CPU
- {CCEDE012-894B-48F6-811E-B2324E74C604}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
- {CCEDE012-894B-48F6-811E-B2324E74C604}.Debug|Any CPU.Build.0 = Debug|Any CPU
- {CCEDE012-894B-48F6-811E-B2324E74C604}.Release|Any CPU.ActiveCfg = Release|Any CPU
- {CCEDE012-894B-48F6-811E-B2324E74C604}.Release|Any CPU.Build.0 = Release|Any CPU
{286CCBD5-FB69-4120-A0DB-FBDAD5C43072}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{286CCBD5-FB69-4120-A0DB-FBDAD5C43072}.Debug|Any CPU.Build.0 = Debug|Any CPU
{286CCBD5-FB69-4120-A0DB-FBDAD5C43072}.Release|Any CPU.ActiveCfg = Release|Any CPU
diff --git a/src/TrackerCouncil.Smz3.Abstractions/IItemService.cs b/src/TrackerCouncil.Smz3.Abstractions/IItemService.cs
deleted file mode 100644
index ad716c186..000000000
--- a/src/TrackerCouncil.Smz3.Abstractions/IItemService.cs
+++ /dev/null
@@ -1,168 +0,0 @@
-using TrackerCouncil.Smz3.Shared;
-using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
-using TrackerCouncil.Smz3.Data.WorldData;
-using TrackerCouncil.Smz3.Data.WorldData.Regions;
-using TrackerCouncil.Smz3.Shared.Enums;
-
-namespace TrackerCouncil.Smz3.Abstractions;
-
-///
-/// Defines methods for managing items and their tracking state.
-///
-public interface IItemService
-{
- ///
- /// Enumerates all items that can be tracked for all players.
- ///
- /// A collection of items.
- IEnumerable- AllItems();
-
- ///
- /// Enumerates all items that can be tracked for the local player.
- ///
- /// A collection of items.
- IEnumerable
- LocalPlayersItems();
-
- ///
- /// Enumarates all currently tracked items for the local player.
- ///
- ///
- /// A collection of items that have been tracked at least once.
- ///
- IEnumerable
- TrackedItems();
-
- ///
- /// Finds the item with the specified name for the local player.
- ///
- ///
- /// The name of the item or item stage to find.
- ///
- ///
- /// An representing the item with the specified
- /// name, or if there is no item that has the
- /// specified name.
- ///
- Item? FirstOrDefault(string name);
-
- ///
- /// Finds an item with the specified item type for the local player.
- ///
- /// The type of item to find.
- ///
- /// An representing the item. If there are
- /// multiple configured items with the same type, this method returns
- /// one at random. If there no configured items with the specified type,
- /// this method returns .
- ///
- Item? FirstOrDefault(ItemType itemType);
-
- ///
- /// Returns a random name for the specified item including article, e.g.
- /// "an E-Tank" or "the Book of Mudora".
- ///
- /// The type of item whose name to get.
- ///
- /// The name of the type of item, including "a", "an" or "the" if
- /// applicable.
- ///
- string GetName(ItemType itemType);
-
- ///
- /// Indicates whether an item of the specified type has been tracked
- /// for the local player.
- ///
- /// The type of item to check.
- ///
- /// if an item with the specified type has been
- /// tracked at least once; otherwise, .
- ///
- bool IsTracked(ItemType itemType);
-
- ///
- /// Finds an reward with the specified item type.
- ///
- /// The type of reward to find.
- ///
- /// An representing the reward. If there are
- /// multiple configured rewards with the same type, this method returns
- /// one at random. If there no configured rewards with the specified type,
- /// this method returns .
- ///
- Reward? FirstOrDefault(RewardType rewardType);
-
- ///
- /// Returns a random name for the specified item including article, e.g.
- /// "a blue crystal" or "the green pendant".
- ///
- /// The reward of item whose name to get.
- ///
- /// The name of the reward of item, including "a", "an" or "the" if
- /// applicable.
- ///
- string GetName(RewardType rewardType);
-
- ///
- /// Enumerates all rewards that can be tracked for the local player.
- ///
- /// A collection of rewards.
-
- IEnumerable AllRewards();
-
- ///
- /// Enumerates all rewards that can be tracked for the local player.
- ///
- /// A collection of rewards.
-
- IEnumerable LocalPlayersRewards();
-
- ///
- /// Enumarates all currently tracked rewards for the local player.
- ///
- ///
- /// A collection of reward that have been tracked.
- ///
- IEnumerable TrackedRewards();
-
- ///
- /// Enumerates all bosses that can be tracked for all players.
- ///
- /// A collection of bosses.
-
- IEnumerable AllBosses();
-
- ///
- /// Enumerates all bosses that can be tracked for the local player.
- ///
- /// A collection of bosses.
-
- IEnumerable LocalPlayersBosses();
-
- ///
- /// Enumarates all currently tracked bosses for the local player.
- ///
- ///
- /// A collection of bosses that have been tracked.
- ///
- IEnumerable TrackedBosses();
-
- ///
- /// Retrieves the progression containing all of the tracked items, rewards, and bosses
- /// for determining in logic locations
- ///
- /// If it should be assumed that the player has all keys and keycards
- ///
- Progression GetProgression(bool assumeKeys);
-
- ///
- /// Retrieves the progression containing all of the tracked items, rewards, and bosses
- /// for determining in logic locations
- ///
- /// The area being looked at to see if keys/keycards should be assumed or not
- ///
- Progression GetProgression(IHasLocations area);
-
- ///
- /// Clears cached progression
- ///
- void ResetProgression();
-}
diff --git a/src/TrackerCouncil.Smz3.Abstractions/IPlayerProgressionService.cs b/src/TrackerCouncil.Smz3.Abstractions/IPlayerProgressionService.cs
new file mode 100644
index 000000000..2105572a5
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.Abstractions/IPlayerProgressionService.cs
@@ -0,0 +1,75 @@
+using TrackerCouncil.Smz3.Data.WorldData;
+using TrackerCouncil.Smz3.Data.WorldData.Regions;
+using TrackerCouncil.Smz3.Shared.Enums;
+
+namespace TrackerCouncil.Smz3.Abstractions;
+
+///
+/// Defines methods for retrieving the progression and individual tracking statuses
+///
+public interface IPlayerProgressionService
+{
+ ///
+ /// Enumarates all currently tracked items for the local player.
+ ///
+ ///
+ /// A collection of items that have been tracked at least once.
+ ///
+ IEnumerable
- TrackedItems();
+
+ ///
+ /// Indicates whether an item of the specified type has been tracked
+ /// for the local player.
+ ///
+ /// The type of item to check.
+ ///
+ /// if an item with the specified type has been
+ /// tracked at least once; otherwise, .
+ ///
+ bool IsTracked(ItemType itemType);
+
+ ///
+ /// Enumarates all currently tracked rewards for the local player.
+ ///
+ ///
+ /// A collection of reward that have been tracked.
+ ///
+ IEnumerable TrackedRewards();
+
+ ///
+ /// Enumarates all currently tracked bosses for the local player.
+ ///
+ ///
+ /// A collection of bosses that have been tracked.
+ ///
+ IEnumerable TrackedBosses();
+
+ ///
+ /// Retrieves the progression containing all of the tracked items, rewards, and bosses
+ /// for determining in logic locations
+ ///
+ /// If it should be assumed that the player has all keys and keycards
+ ///
+ Progression GetProgression(bool assumeKeys);
+
+ ///
+ /// Retrieves the progression containing all of the tracked items, rewards, and bosses
+ /// for determining in logic locations
+ ///
+ /// The area being looked at to see if keys/keycards should be assumed or not
+ ///
+ Progression GetProgression(IHasLocations area);
+
+ ///
+ /// Retrieves the progression containing all of the tracked items, rewards, and bosses
+ /// for determining in logic locations
+ ///
+ /// The location being looked at to see if keys/keycards should be assumed or not
+ ///
+ Progression GetProgression(Location location);
+
+ ///
+ /// Clears cached progression
+ ///
+ void ResetProgression();
+}
diff --git a/src/TrackerCouncil.Smz3.Abstractions/ITrackerBossService.cs b/src/TrackerCouncil.Smz3.Abstractions/ITrackerBossService.cs
new file mode 100644
index 000000000..0e4ed9ee7
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.Abstractions/ITrackerBossService.cs
@@ -0,0 +1,58 @@
+using TrackerCouncil.Smz3.Data.Tracking;
+using TrackerCouncil.Smz3.Data.WorldData;
+using TrackerCouncil.Smz3.Data.WorldData.Regions;
+
+namespace TrackerCouncil.Smz3.Abstractions;
+
+public interface ITrackerBossService
+{
+ public event EventHandler? BossUpdated;
+
+ ///
+ /// Marks a dungeon as cleared and, if possible, tracks the boss reward.
+ ///
+ /// The dungeon that was cleared.
+ /// The speech recognition confidence.
+ /// If this was cleared by the auto tracker
+ ///
+ /// if the command implies the boss was killed;
+ /// if the boss was simply "tracked".
+ ///
+ public void MarkBossAsDefeated(IHasBoss region, float? confidence = null, bool autoTracked = false, bool admittedGuilt = false);
+
+ ///
+ /// Marks a boss as defeated.
+ ///
+ /// The boss that was defeated.
+ ///
+ /// if the command implies the boss was killed;
+ /// if the boss was simply "tracked".
+ ///
+ /// The speech recognition confidence.
+ /// If this was tracked by the auto tracker
+ public void MarkBossAsDefeated(Boss boss, bool admittedGuilt = true, float? confidence = null,
+ bool autoTracked = false);
+
+ ///
+ /// Un-marks a boss as defeated.
+ ///
+ /// The boss that should be 'revived'.
+ /// The speech recognition confidence.
+ public void MarkBossAsNotDefeated(Boss boss, float? confidence = null);
+
+ ///
+ /// Un-marks a dungeon as cleared and, if possible, untracks the boss
+ /// reward.
+ ///
+ /// The dungeon that should be un-cleared.
+ /// The speech recognition confidence.
+ public void MarkBossAsNotDefeated(IHasBoss region, float? confidence = null);
+
+ public void UpdateAccessibility(Progression? actualProgression = null, Progression? withKeysProgression = null);
+
+ public void UpdateAccessibility(Boss boss, Progression? actualProgression = null, Progression? withKeysProgression = null);
+
+ public void UpdateAccessibility(IHasBoss region, Progression? actualProgression = null, Progression? withKeysProgression = null);
+
+
+}
diff --git a/src/TrackerCouncil.Smz3.Abstractions/ITrackerGameStateService.cs b/src/TrackerCouncil.Smz3.Abstractions/ITrackerGameStateService.cs
new file mode 100644
index 000000000..b80350a4d
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.Abstractions/ITrackerGameStateService.cs
@@ -0,0 +1,111 @@
+using MSURandomizerLibrary.Configs;
+using TrackerCouncil.Smz3.Data.Tracking;
+using TrackerCouncil.Smz3.Data.WorldData;
+using TrackerCouncil.Smz3.Data.WorldData.Regions;
+using TrackerCouncil.Smz3.Shared.Models;
+
+namespace TrackerCouncil.Smz3.Abstractions;
+
+public interface ITrackerGameStateService
+{
+ ///
+ /// Gets if the local player has beaten the game or not
+ ///
+ public bool HasBeatenGame { get; }
+
+ ///
+ /// The last viewed hint tile or set of locations
+ ///
+ public ViewedObject? LastViewedObject { get; }
+
+ ///
+ /// The region the player is currently in according to the Auto Tracker
+ ///
+ public Region? CurrentRegion { get; }
+
+ ///
+ /// The map to display for the player
+ ///
+ public string CurrentMap { get; }
+
+ ///
+ /// The current track number being played
+ ///
+ public int CurrentTrackNumber { get; }
+
+ ///
+ /// Occurs when the map has been updated
+ ///
+ public event EventHandler? MapUpdated;
+
+ ///
+ /// Occurs when the map has been updated
+ ///
+ public event EventHandler? BeatGame;
+
+ ///
+ /// Occurs when the map has died
+ ///
+ public event EventHandler? PlayerDied;
+
+ ///
+ /// Occurs when a hint tile is viewed that is for a region, dungeon, or group of locations
+ ///
+ public event EventHandler? HintTileUpdated;
+
+ ///
+ /// Occurs when the current played track number is updated
+ ///
+ public event EventHandler? TrackNumberUpdated;
+
+ ///
+ /// Occurs when the current track has changed
+ ///
+ public event EventHandler? TrackChanged;
+
+ ///
+ /// Updates the region that the player is in
+ ///
+ /// The region the player is in
+ /// Set to true to update the map for the player to match the region
+ /// If the time should be reset if this is the first region update
+ public void UpdateRegion(Region region, bool updateMap = false, bool resetTime = false);
+
+ ///
+ /// Updates the map to display for the user
+ ///
+ /// The name of the map
+ public void UpdateMap(string map);
+
+ ///
+ /// Called when the game is beaten by entering triforce room
+ /// or entering the ship after beating both bosses
+ ///
+ /// If this was triggered by the auto tracker
+ public void GameBeaten(bool autoTracked);
+
+ ///
+ /// Called when the player has died
+ ///
+ public void TrackDeath(bool autoTracked);
+
+ ///
+ /// Updates the current track number being played
+ ///
+ /// The number of the track
+ public void UpdateTrackNumber(int number);
+
+ ///
+ /// Updates the current track being played
+ ///
+ /// The current MSU pack
+ /// The current track
+ /// Formatted output text matching the requested style
+ public void UpdateTrack(Msu msu, Track track, string outputText);
+
+ public void UpdateHintTile(PlayerHintTile hintTile);
+
+ public void UpdateLastMarkedLocations(List locations);
+
+ public void ClearLastViewedObject(float confidence);
+}
diff --git a/src/TrackerCouncil.Smz3.Abstractions/ITrackerItemService.cs b/src/TrackerCouncil.Smz3.Abstractions/ITrackerItemService.cs
new file mode 100644
index 000000000..35d1c754f
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.Abstractions/ITrackerItemService.cs
@@ -0,0 +1,81 @@
+using TrackerCouncil.Smz3.Data.Tracking;
+using TrackerCouncil.Smz3.Data.WorldData;
+using TrackerCouncil.Smz3.Data.WorldData.Regions;
+
+namespace TrackerCouncil.Smz3.Abstractions;
+
+public interface ITrackerItemService
+{
+ public event EventHandler? ItemTracked;
+
+ ///
+ /// Tracks the specifies item.
+ ///
+ /// The item data to track.
+ ///
+ /// The text that was tracked, when triggered by voice command.
+ ///
+ /// The speech recognition confidence.
+ ///
+ /// to attempt to clear a location for the
+ /// tracked item; if that is done by the caller.
+ ///
+ /// If this was tracked by the auto tracker
+ /// The location an item was tracked from
+ /// If the item was gifted to the player by tracker or another player
+ /// If tracker should not say anything
+ ///
+ /// if the item was actually tracked; if the item could not be tracked, e.g. when
+ /// tracking Bow twice.
+ ///
+ bool TrackItem(Item item, string? trackedAs = null, float? confidence = null, bool tryClear = true,
+ bool autoTracked = false, Location? location = null, bool giftedItem = false, bool silent = false);
+
+ ///
+ /// Tracks the specifies item and clears it from the specified dungeon.
+ ///
+ /// The item data to track.
+ ///
+ /// The text that was tracked, when triggered by voice command.
+ ///
+ /// The dungeon the item was tracked in.
+ /// The speech recognition confidence.
+ void TrackItemFrom(Item item, IHasTreasure hasTreasure, string? trackedAs = null, float? confidence = null);
+
+ ///
+ /// Tracks the specified item and clears it from the specified room.
+ ///
+ /// The item data to track.
+ ///
+ /// The text that was tracked, when triggered by voice command.
+ ///
+ /// The area the item was found in.
+ /// The speech recognition confidence.
+ void TrackItemFrom(Item item, IHasLocations area, string? trackedAs = null, float? confidence = null);
+
+ ///
+ /// Sets the item count for the specified item.
+ ///
+ /// The item to track.
+ ///
+ /// The amount of the item that is in the player's inventory now.
+ ///
+ /// The speech recognition confidence.
+ void TrackItemAmount(Item item, int count, float confidence);
+
+ ///
+ /// Tracks multiple items at the same time
+ ///
+ /// The items to track
+ /// If the items were tracked via auto tracker
+ /// If the items were gifted to the player
+ void TrackItems(List
- items, bool autoTracked, bool giftedItem);
+
+ ///
+ /// Removes an item from the tracker.
+ ///
+ /// The item to untrack.
+ /// The speech recognition confidence.
+ void UntrackItem(Item item, float? confidence = null);
+}
diff --git a/src/TrackerCouncil.Smz3.Abstractions/ITrackerLocationService.cs b/src/TrackerCouncil.Smz3.Abstractions/ITrackerLocationService.cs
new file mode 100644
index 000000000..116f033cc
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.Abstractions/ITrackerLocationService.cs
@@ -0,0 +1,88 @@
+using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
+using TrackerCouncil.Smz3.Data.Tracking;
+using TrackerCouncil.Smz3.Data.WorldData;
+using TrackerCouncil.Smz3.Data.WorldData.Regions;
+using TrackerCouncil.Smz3.Shared.Enums;
+
+namespace TrackerCouncil.Smz3.Abstractions;
+
+public interface ITrackerLocationService
+{
+ public event EventHandler LocationCleared;
+
+ public event EventHandler LocationMarked;
+
+ ///
+ /// Clears an item from the specified location.
+ ///
+ /// The location to clear.
+ /// The speech recognition confidence.
+ /// If this was tracked by the auto tracker
+ void Clear(Location location, float? confidence = null, bool autoTracked = false, bool stateResponse = true, bool allowLocationComments = false, bool updateTreasureCount = true);
+
+ ///
+ /// Unclears an item from the specified location.
+ ///
+ /// The location to clear.
+ /// The speech recognition confidence.
+ /// If this was tracked by the auto tracker
+ void Unclear(Location location, bool updateTreasureCount = true);
+
+ ///
+ /// Clears an item from the specified locations.
+ ///
+ /// The locations to clear.
+ /// The speech recognition confidence
+ void Clear(List locations, float? confidence = null);
+
+ ///
+ /// Marks an item at the specified location.
+ ///
+ /// The location to mark.
+ ///
+ /// The item that is found at .
+ ///
+ /// The speech recognition confidence.
+ /// If the marked location was auto tracked
+ public void MarkLocation(Location location, Item item, float? confidence = null, bool autoTracked = false);
+
+ ///
+ /// Marks an item at the specified location.
+ ///
+ /// The location to mark.
+ ///
+ /// The item that is found at .
+ ///
+ /// The speech recognition confidence.
+ /// If the marked location was auto tracked
+ /// The metadata of the item
+ public void MarkLocation(Location location, ItemType item, float? confidence = null, bool autoTracked = false,
+ ItemData? metadata = null);
+
+ ///
+ /// Clears every item in the specified area, optionally tracking the
+ /// cleared items.
+ ///
+ /// The area whose items to clear.
+ ///
+ /// true to track any items found; false to only clear the
+ /// affected locations.
+ ///
+ ///
+ /// true to include every item in , even
+ /// those that are not in logic. false to only include chests
+ /// available with current items.
+ ///
+ /// The speech recognition confidence.
+ ///
+ /// Set to true to ignore keys when clearing the location.
+ ///
+ public void ClearArea(IHasLocations area, bool trackItems, bool includeUnavailable = false,
+ float? confidence = null, bool assumeKeys = false);
+
+ public void UpdateAccessibility(bool unclearedOnly = true, Progression? actualProgression = null, Progression? withKeysProgression = null);
+
+ public void UpdateAccessibility(IEnumerable locations, Progression? actualProgression = null, Progression? withKeysProgression = null);
+
+ public void UpdateAccessibility(Location location, Progression? actualProgression = null, Progression? withKeysProgression = null);
+}
diff --git a/src/TrackerCouncil.Smz3.Abstractions/ITrackerModeService.cs b/src/TrackerCouncil.Smz3.Abstractions/ITrackerModeService.cs
new file mode 100644
index 000000000..83eb542bb
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.Abstractions/ITrackerModeService.cs
@@ -0,0 +1,89 @@
+using TrackerCouncil.Smz3.Data.Tracking;
+
+namespace TrackerCouncil.Smz3.Abstractions;
+
+public interface ITrackerModeService
+{
+ ///
+ /// Indicates whether Tracker is in Go Mode.
+ ///
+ public bool GoMode { get; protected set; }
+
+ ///
+ /// Indicates whether Tracker is in Peg World mode.
+ ///
+ public bool PegWorldMode { get; protected set; }
+
+ ///
+ /// Indicates whether Tracker is in Shaktool mode.
+ ///
+ public bool ShaktoolMode { get; protected set; }
+
+ ///
+ /// The number of pegs that have been pegged for Peg World mode
+ ///
+ public int PegsPegged { get; protected set; }
+
+ //
+ /// Occurs when Peg World mode has been toggled on.
+ ///
+ public event EventHandler? ToggledPegWorldModeOn;
+
+ ///
+ /// Occurs when going to Shaktool
+ ///
+ public event EventHandler? ToggledShaktoolMode;
+
+ ///
+ /// Occurs when a Peg World peg has been pegged.
+ ///
+ public event EventHandler? PegPegged;
+
+ ///
+ /// Occurs when Go mode has been turned on.
+ ///
+ public event EventHandler? GoModeToggledOn;
+
+ ///
+ /// Occurs when Go mode has been turned off.
+ ///
+ public event EventHandler? GoModeToggledOff;
+
+ ///
+ /// Toggles Go Mode on.
+ ///
+ /// The speech recognition confidence.
+ public void ToggleGoMode(float? confidence = null);
+
+ ///
+ /// Pegs a Peg World peg.
+ ///
+ /// The speech recognition confidence.
+ public void Peg(float? confidence = null);
+
+ public void SetPegs(int count);
+
+ ///
+ /// Starts Peg World mode.
+ ///
+ /// The speech recognition confidence.
+ public void StartPegWorldMode(float? confidence = null);
+
+ ///
+ /// Turns Peg World mode off.
+ ///
+ /// The speech recognition confidence.
+ public void StopPegWorldMode(float? confidence = null);
+
+ ///
+ /// Starts Peg World mode.
+ ///
+ /// The speech recognition confidence.
+ public void StartShaktoolMode(float? confidence = null);
+
+ ///
+ /// Turns Peg World mode off.
+ ///
+ /// The speech recognition confidence.
+ public void StopShaktoolMode(float? confidence = null);
+}
diff --git a/src/TrackerCouncil.Smz3.Abstractions/ITrackerPrerequisiteService.cs b/src/TrackerCouncil.Smz3.Abstractions/ITrackerPrerequisiteService.cs
new file mode 100644
index 000000000..8807ad064
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.Abstractions/ITrackerPrerequisiteService.cs
@@ -0,0 +1,17 @@
+using TrackerCouncil.Smz3.Data.WorldData.Regions;
+using TrackerCouncil.Smz3.Shared.Enums;
+
+namespace TrackerCouncil.Smz3.Abstractions;
+
+public interface ITrackerPrerequisiteService
+{
+ ///
+ /// Sets the dungeon's medallion requirement to the specified item.
+ ///
+ /// The dungeon to mark.
+ /// The medallion that is required.
+ /// The speech recognition confidence.
+ /// If the marked dungeon requirement was autotracked
+ public void SetDungeonRequirement(IHasPrerequisite region, ItemType? medallion = null, float? confidence = null,
+ bool autoTracked = false);
+}
diff --git a/src/TrackerCouncil.Smz3.Abstractions/ITrackerRewardService.cs b/src/TrackerCouncil.Smz3.Abstractions/ITrackerRewardService.cs
new file mode 100644
index 000000000..165c575bc
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.Abstractions/ITrackerRewardService.cs
@@ -0,0 +1,44 @@
+using TrackerCouncil.Smz3.Data.WorldData;
+using TrackerCouncil.Smz3.Data.WorldData.Regions;
+using TrackerCouncil.Smz3.Shared.Enums;
+
+namespace TrackerCouncil.Smz3.Abstractions;
+
+public interface ITrackerRewardService
+{
+ ///
+ /// Sets the dungeon's reward to the specific pendant or crystal.
+ ///
+ /// The dungeon to mark.
+ ///
+ /// The type of pendant or crystal, or null to cycle through the
+ /// possible rewards.
+ ///
+ /// The speech recognition confidence.
+ /// If this was called by the auto tracker
+ public void SetAreaReward(IHasReward rewardRegion, RewardType? reward = null, float? confidence = null,
+ bool autoTracked = false);
+
+ ///
+ /// Gives the area's reward to the player
+ ///
+ /// The region to give the reward for
+ /// If this is from auto tracking
+ /// If the response should be stated
+ public void GiveAreaReward(IHasReward rewardRegion, bool isAutoTracked, bool stateResponse);
+
+ public void RemoveAreaReward(IHasReward rewardRegion, bool stateResponse);
+
+ ///
+ /// Sets the reward of all unmarked dungeons.
+ ///
+ /// The reward to set.
+ /// The speech recognition confidence.
+ public void SetUnmarkedRewards(RewardType reward, float? confidence = null);
+
+ public void UpdateAccessibility(Progression? actualProgression = null, Progression? withKeysProgression = null);
+
+ public void UpdateAccessibility(Reward reward, Progression? actualProgression = null, Progression? withKeysProgression = null);
+
+ public void UpdateAccessibility(IHasReward region, Progression? actualProgression = null, Progression? withKeysProgression = null);
+}
diff --git a/src/TrackerCouncil.Smz3.Abstractions/ITrackerTreasureService.cs b/src/TrackerCouncil.Smz3.Abstractions/ITrackerTreasureService.cs
new file mode 100644
index 000000000..c28f22acf
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.Abstractions/ITrackerTreasureService.cs
@@ -0,0 +1,43 @@
+using TrackerCouncil.Smz3.Data.WorldData;
+using TrackerCouncil.Smz3.Data.WorldData.Regions;
+
+namespace TrackerCouncil.Smz3.Abstractions;
+
+public interface ITrackerTreasureService
+{
+ ///
+ /// Removes one or more items from the available treasure in the specified region.
+ ///
+ /// The dungeon.
+ /// The number of treasures to track.
+ /// The speech recognition confidence.
+ /// If this was called by the auto tracker
+ /// If tracker should state the treasure amount
+ ///
+ /// true if treasure was tracked; false if there is no
+ /// treasure left to track.
+ ///
+ ///
+ /// This method adds to the undo history if the return value is
+ /// true.
+ ///
+ ///
+ /// is less than 1.
+ ///
+ bool TrackDungeonTreasure(IHasTreasure region, float? confidence = null, int amount = 1,
+ bool autoTracked = false, bool stateResponse = true);
+
+ public bool UntrackDungeonTreasure(IHasTreasure region, int amount = 1);
+
+ Action? TryTrackDungeonTreasure(Location location, float? confidence, bool autoTracked = false,
+ bool stateResponse = true);
+
+ public Action? TryUntrackDungeonTreasure(Location location);
+
+ ///
+ /// Marks all locations and treasure within a dungeon as cleared.
+ ///
+ /// The dungeon to clear.
+ /// The speech recognition confidence.
+ public void ClearDungeon(IHasTreasure treasureRegion, float? confidence = null);
+}
diff --git a/src/TrackerCouncil.Smz3.Abstractions/TrackerBase.cs b/src/TrackerCouncil.Smz3.Abstractions/TrackerBase.cs
index 723ca45c9..c6b273b14 100644
--- a/src/TrackerCouncil.Smz3.Abstractions/TrackerBase.cs
+++ b/src/TrackerCouncil.Smz3.Abstractions/TrackerBase.cs
@@ -1,12 +1,9 @@
-using MSURandomizerLibrary.Configs;
using TrackerCouncil.Smz3.Data.Configuration;
using TrackerCouncil.Smz3.Data.Configuration.ConfigFiles;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
using TrackerCouncil.Smz3.Data.Options;
using TrackerCouncil.Smz3.Data.Tracking;
using TrackerCouncil.Smz3.Data.WorldData;
-using TrackerCouncil.Smz3.Data.WorldData.Regions;
-using TrackerCouncil.Smz3.Shared.Enums;
using TrackerCouncil.Smz3.Shared.Models;
namespace TrackerCouncil.Smz3.Abstractions;
@@ -19,56 +16,6 @@ public abstract class TrackerBase
///
public event EventHandler? SpeechRecognized;
- ///
- /// Occurs when one or more items have been tracked.
- ///
- public event EventHandler? ItemTracked;
-
- ///
- /// Occurs when a location has been cleared.
- ///
- public event EventHandler? LocationCleared;
-
- ///
- /// Occurs when Peg World mode has been toggled on.
- ///
- public event EventHandler? ToggledPegWorldModeOn;
-
- ///
- /// Occurs when going to Shaktool
- ///
- public event EventHandler? ToggledShaktoolMode;
-
- ///
- /// Occurs when a Peg World peg has been pegged.
- ///
- public event EventHandler? PegPegged;
-
- ///
- /// Occurs when the properties of a dungeon have changed.
- ///
- public event EventHandler? DungeonUpdated;
-
- ///
- /// Occurs when the properties of a boss have changed.
- ///
- public event EventHandler? BossUpdated;
-
- ///
- /// Occurs when the marked locations have changed
- ///
- public event EventHandler? MarkedLocationsUpdated;
-
- ///
- /// Occurs when Go mode has been turned on.
- ///
- public event EventHandler? GoModeToggledOn;
-
- ///
- /// Occurs when Go mode has been turned off.
- ///
- public event EventHandler? GoModeToggledOff;
-
///
/// Occurs when the last action was undone.
///
@@ -80,70 +27,36 @@ public abstract class TrackerBase
public event EventHandler? StateLoaded;
///
- /// Occurs when the map has been updated
+ /// Occurs when the voice recognition has been enabled or disabled
///
- public event EventHandler? MapUpdated;
+ public event EventHandler? VoiceRecognitionEnabledChanged;
///
- /// Occurs when the map has been updated
+ /// Gets a reference to the .
///
- public event EventHandler? BeatGame;
+ public IPlayerProgressionService PlayerProgressionService { get; protected init; } = null!;
- ///
- /// Occurs when the map has died
- ///
- public event EventHandler? PlayerDied;
+ public ITrackerTreasureService TreasureTracker { get; protected set; } = null!;
- ///
- /// Occurs when the current played track number is updated
- ///
- public event EventHandler? TrackNumberUpdated;
+ public ITrackerItemService ItemTracker { get; protected set; } = null!;
- ///
- /// Occurs when the current track has changed
- ///
- public event EventHandler? TrackChanged;
+ public ITrackerLocationService LocationTracker { get; protected set; } = null!;
- ///
- /// Occurs when a hint tile is viewed that is for a region, dungeon, or group of locations
- ///
- public event EventHandler? HintTileUpdated;
+ public ITrackerRewardService RewardTracker { get; protected set; } = null!;
- ///
- /// Occurs when the voice recognition has been enabled or disabled
- ///
- public event EventHandler? VoiceRecognitionEnabledChanged;
+ public ITrackerBossService BossTracker { get; protected set; } = null!;
- ///
- /// Gets a reference to the .
- ///
- public IItemService ItemService { get; protected init; } = null!;
+ public ITrackerPrerequisiteService PrerequisiteTracker { get; protected set; } = null!;
- ///
- /// The number of pegs that have been pegged for Peg World mode
- ///
- public int PegsPegged { get; protected set; }
+ public ITrackerModeService ModeTracker { get; protected set; } = null!;
+
+ public ITrackerGameStateService GameStateTracker { get; protected set; } = null!;
///
/// Gets the world for the currently tracked playthrough.
///
public World World { get; protected init; } = null!;
- ///
- /// Indicates whether Tracker is in Go Mode.
- ///
- public bool GoMode { get; protected set; }
-
- ///
- /// Indicates whether Tracker is in Peg World mode.
- ///
- public bool PegWorldMode { get; protected set; }
-
- ///
- /// Indicates whether Tracker is in Shaktool mode.
- ///
- public bool ShaktoolMode { get; protected set; }
-
///
/// If the speech recognition engine was fully initialized
///
@@ -195,21 +108,6 @@ public abstract class TrackerBase
///
public string? RomPath { get; protected set; }
- ///
- /// The region the player is currently in according to the Auto Tracker
- ///
- public Region? CurrentRegion { get; protected set; }
-
- ///
- /// The map to display for the player
- ///
- public string CurrentMap { get; protected set; } = "";
-
- ///
- /// The current track number being played
- ///
- public int CurrentTrackNumber { get; protected set; }
-
///
/// Gets a string describing tracker's mood.
///
@@ -238,7 +136,7 @@ public abstract class TrackerBase
///
/// Module that houses the history
///
- protected IHistoryService History { get; init; } = null!;
+ public IHistoryService History { get; init; } = null!;
///
/// Gets or sets a value indicating whether Tracker may give hints when
@@ -252,16 +150,6 @@ public abstract class TrackerBase
///
public bool SpoilersEnabled { get; set; }
- ///
- /// Gets if the local player has beaten the game or not
- ///
- public bool HasBeatenGame { get; protected set; }
-
- ///
- /// The last viewed hint tile or set of locations
- ///
- public ViewedObject? LastViewedObject { get; set; }
-
///
/// Attempts to replace a user name with a pronunciation-corrected
/// version of it.
@@ -294,68 +182,14 @@ public abstract class TrackerBase
///
public abstract Task SaveAsync();
+ public abstract void MarkAsDirty(bool isDirty = true);
+
///
/// Undoes the last operation.
///
/// The speech recognition confidence.
public abstract void Undo(float confidence);
- ///
- /// Toggles Go Mode on.
- ///
- /// The speech recognition confidence.
- public abstract void ToggleGoMode(float? confidence = null);
-
- ///
- /// Removes one or more items from the available treasure in the
- /// specified dungeon.
- ///
- /// The dungeon.
- /// The number of treasures to track.
- /// The speech recognition confidence.
- /// If this was called by the auto tracker
- /// If tracker should state the treasure ammount
- ///
- /// true if treasure was tracked; false if there is no
- /// treasure left to track.
- ///
- ///
- /// This method adds to the undo history if the return value is
- /// true.
- ///
- ///
- /// is less than 1.
- ///
- public abstract bool TrackDungeonTreasure(IDungeon dungeon, float? confidence = null, int amount = 1, bool autoTracked = false, bool stateResponse = true);
-
- ///
- /// Sets the dungeon's reward to the specific pendant or crystal.
- ///
- /// The dungeon to mark.
- ///
- /// The type of pendant or crystal, or null to cycle through the
- /// possible rewards.
- ///
- /// The speech recognition confidence.
- /// If this was called by the auto tracker
- public abstract void SetDungeonReward(IDungeon dungeon, RewardType? reward = null, float? confidence = null, bool autoTracked = false);
-
- ///
- /// Sets the reward of all unmarked dungeons.
- ///
- /// The reward to set.
- /// The speech recognition confidence.
- public abstract void SetUnmarkedDungeonReward(RewardType reward, float? confidence = null);
-
- ///
- /// Sets the dungeon's medallion requirement to the specified item.
- ///
- /// The dungeon to mark.
- /// The medallion that is required.
- /// The speech recognition confidence.
- /// If the marked dungeon requirement was auto tracked
- public abstract void SetDungeonRequirement(IDungeon dungeon, ItemType? medallion = null, float? confidence = null, bool autoTracked = false);
-
///
/// Starts voice recognition.
///
@@ -476,274 +310,6 @@ public abstract bool Say(
///
public abstract void Error();
- ///
- /// Tracks the specifies item.
- ///
- /// The item data to track.
- ///
- /// The text that was tracked, when triggered by voice command.
- ///
- /// The speech recognition confidence.
- ///
- /// to attempt to clear a location for the
- /// tracked item; if that is done by the caller.
- ///
- /// If this was tracked by the auto tracker
- /// The location an item was tracked from
- /// If the item was gifted to the player by tracker or another player
- /// If tracker should not say anything
- ///
- /// if the item was actually tracked; if the item could not be tracked, e.g. when
- /// tracking Bow twice.
- ///
- public abstract bool TrackItem(Item item, string? trackedAs = null, float? confidence = null, bool tryClear = true, bool autoTracked = false, Location? location = null, bool giftedItem = false, bool silent = false);
-
- ///
- /// Tracks multiple items at the same time
- ///
- /// The items to track
- /// If the items were tracked via auto tracker
- /// If the items were gifted to the player
- public abstract void TrackItems(List
- items, bool autoTracked, bool giftedItem);
-
- ///
- /// Removes an item from the tracker.
- ///
- /// The item to untrack.
- /// The speech recognition confidence.
- public abstract void UntrackItem(Item item, float? confidence = null);
-
- ///
- /// Tracks the specifies item and clears it from the specified dungeon.
- ///
- /// The item data to track.
- ///
- /// The text that was tracked, when triggered by voice command.
- ///
- /// The dungeon the item was tracked in.
- /// The speech recognition confidence.
- public abstract void TrackItem(Item item, IDungeon dungeon, string? trackedAs = null, float? confidence = null);
-
- ///
- /// Tracks the specified item and clears it from the specified room.
- ///
- /// The item data to track.
- ///
- /// The text that was tracked, when triggered by voice command.
- ///
- /// The area the item was found in.
- /// The speech recognition confidence.
- public abstract void TrackItem(Item item, IHasLocations area, string? trackedAs = null, float? confidence = null);
-
- ///
- /// Sets the item count for the specified item.
- ///
- /// The item to track.
- ///
- /// The amount of the item that is in the player's inventory now.
- ///
- /// The speech recognition confidence.
- public abstract void TrackItemAmount(Item item, int count, float confidence);
-
- ///
- /// Clears every item in the specified area, optionally tracking the
- /// cleared items.
- ///
- /// The area whose items to clear.
- ///
- /// true to track any items found; false to only clear the
- /// affected locations.
- ///
- ///
- /// true to include every item in , even
- /// those that are not in logic. false to only include chests
- /// available with current items.
- ///
- /// The speech recognition confidence.
- ///
- /// Set to true to ignore keys when clearing the location.
- ///
- public abstract void ClearArea(IHasLocations area, bool trackItems, bool includeUnavailable = false, float? confidence = null, bool assumeKeys = false);
-
- ///
- /// Marks all locations and treasure within a dungeon as cleared.
- ///
- /// The dungeon to clear.
- /// The speech recognition confidence.
- public abstract void ClearDungeon(IDungeon dungeon, float? confidence = null);
-
- ///
- /// Clears an item from the specified location.
- ///
- /// The location to clear.
- /// The speech recognition confidence.
- /// If this was tracked by the auto tracker
- public abstract void Clear(Location location, float? confidence = null, bool autoTracked = false);
-
- ///
- /// Clears an item from the specified locations.
- ///
- /// The locations to clear.
- /// The speech recognition confidence.
- public abstract void Clear(List locations, float? confidence = null);
-
- ///
- /// Marks a dungeon as cleared and, if possible, tracks the boss reward.
- ///
- /// The dungeon that was cleared.
- /// The speech recognition confidence.
- /// If this was cleared by the auto tracker
- public abstract void MarkDungeonAsCleared(IDungeon dungeon, float? confidence = null, bool autoTracked = false);
-
- ///
- /// Marks a boss as defeated.
- ///
- /// The boss that was defeated.
- ///
- /// if the command implies the boss was killed;
- /// if the boss was simply "tracked".
- ///
- /// The speech recognition confidence.
- /// If this was tracked by the auto tracker
- public abstract void MarkBossAsDefeated(Boss boss, bool admittedGuilt = true, float? confidence = null, bool autoTracked = false);
-
- ///
- /// Un-marks a boss as defeated.
- ///
- /// The boss that should be 'revived'.
- /// The speech recognition confidence.
- public abstract void MarkBossAsNotDefeated(Boss boss, float? confidence = null);
-
- ///
- /// Un-marks a dungeon as cleared and, if possible, untracks the boss
- /// reward.
- ///
- /// The dungeon that should be un-cleared.
- /// The speech recognition confidence.
- public abstract void MarkDungeonAsIncomplete(IDungeon dungeon, float? confidence = null);
-
- ///
- /// Marks an item at the specified location.
- ///
- /// The location to mark.
- ///
- /// The item that is found at .
- ///
- /// The speech recognition confidence.
- /// If the marked location was auto tracked
- public abstract void MarkLocation(Location location, Item item, float? confidence = null, bool autoTracked = false);
-
- ///
- /// Marks an item at the specified location.
- ///
- /// The location to mark.
- /// The item that is found at .
- /// The speech recognition confidence.
- /// If the marked location was auto tracked
- /// The metadata of the item
- public abstract void MarkLocation(Location location, ItemType item, float? confidence = null,
- bool autoTracked = false, ItemData? metadata = null);
-
- ///
- /// Pegs a Peg World peg, incrementing the count by one.
- ///
- /// The speech recognition confidence.
- public abstract void Peg(float? confidence = null);
-
- ///
- /// Sets the Peg World peg count to the given value.
- ///
- /// The new count of hammered pegs.
- public abstract void SetPegs(int count);
-
- ///
- /// Starts Peg World mode.
- ///
- /// The speech recognition confidence.
- public abstract void StartPegWorldMode(float? confidence = null);
-
- ///
- /// Turns Peg World mode off.
- ///
- /// The speech recognition confidence.
- public abstract void StopPegWorldMode(float? confidence = null);
-
- ///
- /// Starts Peg World mode.
- ///
- /// The speech recognition confidence.
- public abstract void StartShaktoolMode(float? confidence = null);
-
- ///
- /// Turns Peg World mode off.
- ///
- /// The speech recognition confidence.
- public abstract void StopShaktoolMode(float? confidence = null);
-
- ///
- /// Updates the region that the player is in
- ///
- /// The region the player is in
- /// Set to true to update the map for the player to match the region
- /// If the time should be reset if this is the first region update
- public abstract void UpdateRegion(Region region, bool updateMap = false, bool resetTime = false);
-
- ///
- /// Updates the map to display for the user
- ///
- /// The name of the map
- public abstract void UpdateMap(string map);
-
- ///
- /// Called when the game is beaten by entering triforce room
- /// or entering the ship after beating both bosses
- ///
- /// If this was triggered by the auto tracker
- public abstract void GameBeaten(bool autoTracked);
-
- ///
- /// Called when the player has died
- ///
- public abstract void TrackDeath(bool autoTracked);
-
- ///
- /// Updates the current track number being played
- ///
- /// The number of the track
- public abstract void UpdateTrackNumber(int number);
-
- ///
- /// Updates the current track being played
- ///
- /// The current MSU pack
- /// The current track
- /// Formatted output text matching the requested style
- public abstract void UpdateTrack(Msu msu, Track track, string outputText);
-
- ///
- /// Marks a hint tile as viewed or cleared
- ///
- /// Details about the hint for the player
- public abstract void UpdateHintTile(PlayerHintTile playerHintTile);
-
- ///
- /// Updates the most recently marked locations to be able to clear later
- ///
- /// List of locations that were just marked
- public abstract void UpdateLastMarkedLocations(List locations);
-
- ///
- /// Clears the most recently marked locations
- ///
- /// Voice recognition confidence
- public abstract void ClearLastViewedObject(float confidence);
-
- ///
- /// Reports how many Hyper Beam shots were needed to defeat Mother Brain
- ///
- public abstract void CountHyperBeamShots(int count);
-
///
/// Resets the idle timers when tracker will comment on nothing happening
///
@@ -757,15 +323,9 @@ public abstract void MarkLocation(Location location, ItemType item, float? confi
///
public abstract void AddUndo(Action undo);
- ///
- /// Determines whether or not the specified reward is worth getting.
- ///
- /// The dungeon reward.
- ///
- /// if the reward leads to something good;
- /// otherwise, .
- ///
- public abstract bool IsWorth(RewardType reward);
+ public abstract (Action Action, DateTime UndoTime) PopUndo();
+
+ public abstract void UpdateAllAccessibility(bool forceRefreshAll, params Item[] items);
///
/// Formats a string so that it will be pronounced correctly by the
@@ -776,17 +336,6 @@ public abstract void MarkLocation(Location location, ItemType item, float? confi
public static string CorrectPronunciation(string name)
=> name.Replace("Samus", "Sammus");
- ///
- /// Determines whether or not the specified item is worth getting.
- ///
- /// The item whose worth to consider.
- ///
- /// is the item is worth getting or leads to
- /// another item that is worth getting; otherwise, .
- ///
- public abstract bool IsWorth(Item item);
-
///
/// Invokes the SpeechRecognized event
///
@@ -796,96 +345,6 @@ protected virtual void OnSpeechRecognized(TrackerEventArgs args)
SpeechRecognized?.Invoke(this, args);
}
- ///
- /// Invokes the ItemTracked event
- ///
- ///
- protected virtual void OnItemTracked(ItemTrackedEventArgs args)
- {
- ItemTracked?.Invoke(this, args);
- }
-
- ///
- /// Invokes the LocationCleared event
- ///
- ///
- protected virtual void OnLocationCleared(LocationClearedEventArgs args)
- {
- LocationCleared?.Invoke(this, args);
- }
-
- ///
- /// Invokes the ToggledPegWorldModeOn event
- ///
- ///
- protected virtual void OnToggledPegWorldModeOn(TrackerEventArgs args)
- {
- ToggledPegWorldModeOn?.Invoke(this, args);
- }
-
- ///
- /// Invokes the ToggledShaktoolMode event
- ///
- ///
- protected virtual void OnToggledShaktoolMode(TrackerEventArgs args)
- {
- ToggledShaktoolMode?.Invoke(this, args);
- }
-
- ///
- /// Invokes the PegPegged event
- ///
- ///
- protected virtual void OnPegPegged(TrackerEventArgs args)
- {
- PegPegged?.Invoke(this, args);
- }
-
- ///
- /// Invokes the DungeonUpdated event
- ///
- ///
- protected virtual void OnDungeonUpdated(DungeonTrackedEventArgs args)
- {
- DungeonUpdated?.Invoke(this, args);
- }
-
- ///
- /// Invokes the BossUpdated event
- ///
- ///
- protected virtual void OnBossUpdated(BossTrackedEventArgs args)
- {
- BossUpdated?.Invoke(this, args);
- }
-
- ///
- /// Invokes the MarkedLocationsUpdated event
- ///
- ///
- protected virtual void OnMarkedLocationsUpdated(TrackerEventArgs args)
- {
- MarkedLocationsUpdated?.Invoke(this, args);
- }
-
- ///
- /// Invokes the GoModeToggledOn event
- ///
- ///
- protected virtual void OnGoModeToggledOn(TrackerEventArgs args)
- {
- GoModeToggledOn?.Invoke(this, args);
- }
-
- ///
- /// Invokes the GoModeToggledOff event
- ///
- ///
- protected virtual void OnGoModeToggledOff(TrackerEventArgs args)
- {
- GoModeToggledOff?.Invoke(this, args);
- }
-
///
/// Invokes the ActionUndone event
///
@@ -903,59 +362,6 @@ protected virtual void OnStateLoaded()
StateLoaded?.Invoke(this, EventArgs.Empty);
}
- ///
- /// Invokes the MapUpdated event
- ///
- protected virtual void OnMapUpdated()
- {
- MapUpdated?.Invoke(this, EventArgs.Empty);
- }
-
- ///
- /// Invokes the BeatGame event
- ///
- ///
- protected virtual void OnBeatGame(TrackerEventArgs args)
- {
- BeatGame?.Invoke(this, args);
- }
-
- ///
- /// Invokes the PlayerDied event
- ///
- ///
- protected virtual void OnPlayerDied(TrackerEventArgs args)
- {
- PlayerDied?.Invoke(this, args);
- }
-
- ///
- /// Invokes the TrackNumberUpdated event
- ///
- ///
- protected virtual void OnTrackNumberUpdated(TrackNumberEventArgs args)
- {
- TrackNumberUpdated?.Invoke(this, args);
- }
-
- ///
- /// Invokes the TrackChanged event
- ///
- ///
- protected virtual void OnTrackChanged(TrackChangedEventArgs args)
- {
- TrackChanged?.Invoke(this, args);
- }
-
- ///
- /// Invokes the HintTileUpdated event
- ///
- ///
- protected virtual void OnHintTileUpdated(HintTileUpdatedEventArgs args)
- {
- HintTileUpdated?.Invoke(this, args);
- }
-
///
/// Invokes the VoiceRecognitionEnabledChanged event
///
diff --git a/src/TrackerCouncil.Smz3.Data.SchemaGenerator/Program.cs b/src/TrackerCouncil.Smz3.Data.SchemaGenerator/Program.cs
index 9b2db1df3..b6fd3bc34 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.Boss }));
+ templateBossConfig.AddRange(bossConfig.Select(boss => new BossInfo(boss.Boss)));
var exampleBossConfig = BossConfig.Example();
WriteTemplate(templatePath, "bosses", templateBossConfig, exampleBossConfig);
diff --git a/src/TrackerCouncil.Smz3.Data/Configuration/ConfigFiles/BossConfig.cs b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigFiles/BossConfig.cs
index e5c7b3ba0..2851185f6 100644
--- a/src/TrackerCouncil.Smz3.Data/Configuration/ConfigFiles/BossConfig.cs
+++ b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigFiles/BossConfig.cs
@@ -25,84 +25,29 @@ public BossConfig() : base()
///
public static BossConfig Default()
{
- return new BossConfig
- {
- new()
- {
- Boss = "Spore Spawn",
- MemoryAddress = 1,
- MemoryFlag = 0x2,
- },
- new()
- {
- Boss = "Botwoon",
- MemoryAddress = 4,
- MemoryFlag = 0x2,
- },
- new()
- {
- Boss = "Kraid",
- Type = BossType.Kraid,
- MemoryAddress = 1,
- MemoryFlag = 0x1,
- },
- new()
- {
- Boss = "Crocomire",
- MemoryAddress = 2,
- MemoryFlag = 0x2,
- },
- new()
- {
- Boss = "Phantoon",
- Type = BossType.Phantoon,
- MemoryAddress = 3,
- MemoryFlag = 0x1,
- },
- new()
- {
- Boss = "Shaktool",
- },
- new()
- {
- Boss = "Draygon",
- Type = BossType.Draygon,
- MemoryAddress = 4,
- MemoryFlag = 0x1,
- },
- new()
- {
- Boss = "Ridley",
- Type = BossType.Ridley,
- MemoryAddress = 2,
- MemoryFlag = 0x1,
- },
- new()
- {
- Boss = "Mother Brain",
- },
- new()
- {
- Boss = "Bomb Torizo",
- MemoryAddress = 0,
- MemoryFlag = 0x4,
- },
- new()
- {
- Boss = "Golden Torizo",
- MemoryAddress = 2,
- MemoryFlag = 0x4,
- },
- };
+ return
+ [
+ new BossInfo("Spore Spawn") { MemoryAddress = 1, MemoryFlag = 0x2, },
+ new BossInfo("Botwoon") { MemoryAddress = 4, MemoryFlag = 0x2, },
+ new BossInfo("Kraid") { Type = BossType.Kraid, MemoryAddress = 1, MemoryFlag = 0x1, },
+ new BossInfo("Crocomire") { MemoryAddress = 2, MemoryFlag = 0x2, },
+ new BossInfo("Phantoon") { Type = BossType.Phantoon, MemoryAddress = 3, MemoryFlag = 0x1, },
+ new BossInfo("Shaktool"),
+ new BossInfo("Draygon") { Type = BossType.Draygon, MemoryAddress = 4, MemoryFlag = 0x1, },
+ new BossInfo("Ridley") { Type = BossType.Ridley, MemoryAddress = 2, MemoryFlag = 0x1, },
+ new BossInfo("Mother Brain") { Type = BossType.MotherBrain },
+ new BossInfo("Bomb Torizo") { MemoryAddress = 0, MemoryFlag = 0x4, },
+ new BossInfo("Golden Torizo") { MemoryAddress = 2, MemoryFlag = 0x4, }
+
+ ];
}
public static object Example()
{
return new BossConfig
{
- new()
+ new BossInfo("Bomb Torizo")
{
- Boss = "Bomb Torizo",
Name = new("Bomb Torizo", "Bomb Chozo", new Possibility("Bozo", 0.1)),
WhenTracked = new SchrodingersString("Message when clearing the boss", new Possibility("Another message when clearing the boss", 0.1)),
WhenDefeated = new SchrodingersString("Message when defeating the boss", new Possibility("Another message when defeating the boss", 0.1)),
diff --git a/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/BossInfo.cs b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/BossInfo.cs
index f16d1d731..02788021a 100644
--- a/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/BossInfo.cs
+++ b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/BossInfo.cs
@@ -13,10 +13,10 @@ namespace TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
///
public class BossInfo : IMergeable
{
- ///
- /// Constructor
- ///
- public BossInfo() { }
+ public BossInfo()
+ {
+ Name = [];
+ }
///
/// Initializes a new instance of the class.
@@ -33,6 +33,7 @@ public BossInfo(SchrodingersString name)
/// The name of the boss.
public BossInfo(string name)
{
+ Boss = name;
Name = new SchrodingersString(name);
}
@@ -45,7 +46,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
diff --git a/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/SchrodingersStringExtensions.cs b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/SchrodingersStringExtensions.cs
index 440540e9c..b5a566a78 100644
--- a/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/SchrodingersStringExtensions.cs
+++ b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/SchrodingersStringExtensions.cs
@@ -18,11 +18,7 @@ public static class SchrodingersStringExtensions
///
public static SchrodingersString GetName(this IHasLocations area)
{
- if (area is IDungeon dungeon)
- {
- return dungeon.DungeonMetadata.Name ?? new SchrodingersString(dungeon.DungeonName);
- }
- else if (area is Region region)
+ if (area is Region region)
{
return region.Metadata.Name ?? new SchrodingersString(region.Name);
}
diff --git a/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/TrackerMapLocation.cs b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/TrackerMapLocation.cs
index 3edc51fbf..0651d7708 100644
--- a/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/TrackerMapLocation.cs
+++ b/src/TrackerCouncil.Smz3.Data/Configuration/ConfigTypes/TrackerMapLocation.cs
@@ -88,9 +88,9 @@ public string GetName(World world)
if (room?.Metadata != null)
return room.Metadata.Name?[0] ?? Name;
- var dungeon = world.Dungeons.SingleOrDefault(x => x.DungeonName.Equals(Name, StringComparison.OrdinalIgnoreCase));
- if (dungeon?.DungeonMetadata != null)
- return dungeon.DungeonMetadata.Name?[0] ?? Name;
+ var dungeon = world.Regions.SingleOrDefault(x => x.Name.Equals(Name, StringComparison.OrdinalIgnoreCase));
+ if (dungeon?.Metadata != null)
+ return dungeon.Metadata.Name?[0] ?? Name;
var location = world.Locations.SingleOrDefault(x => x.Name.Equals(Name, StringComparison.OrdinalIgnoreCase));
if (location?.Metadata != null)
diff --git a/src/TrackerCouncil.Smz3.Data/GeneratedData/WorldGenerationData.cs b/src/TrackerCouncil.Smz3.Data/GeneratedData/WorldGenerationData.cs
index a75e78827..09739f8af 100644
--- a/src/TrackerCouncil.Smz3.Data/GeneratedData/WorldGenerationData.cs
+++ b/src/TrackerCouncil.Smz3.Data/GeneratedData/WorldGenerationData.cs
@@ -4,6 +4,8 @@
using System.Text.Json;
using TrackerCouncil.Smz3.Data.Options;
using TrackerCouncil.Smz3.Data.WorldData;
+using TrackerCouncil.Smz3.Data.WorldData.Regions;
+using TrackerCouncil.Smz3.Shared.Enums;
using TrackerCouncil.Smz3.Shared.Multiplayer;
namespace TrackerCouncil.Smz3.Data.GeneratedData;
@@ -31,12 +33,11 @@ public WorldGenerationData(World world, Dictionary? patches = null)
public MultiplayerPlayerGenerationData GetPlayerGenerationData()
{
- var locationItems = World.Locations
- .Select(x => new PlayerGenerationLocationData(x.Id, x.Item.World.Id, x.Item.Type)).ToList();
- var dungeonData = World.Dungeons
- .Select(x => new PlayerGenerationDungeonData(x.DungeonName, x.DungeonRewardType, x.Medallion)).ToList();
- return new MultiplayerPlayerGenerationData(World.Guid, World.Id, locationItems, dungeonData, World.HintTiles.ToList());
+ var locations = World.Locations.Select(x => new PlayerGenerationLocationData(x.Id, x.Item.World.Id, x.Item.Type)).ToList();
+ var bosses = World.BossRegions.ToDictionary(x => x.GetType().Name, x => x.BossType);
+ var rewards = World.RewardRegions.ToDictionary(x => x.GetType().Name, x => x.RewardType);
+ var prerequisites = World.PrerequisiteRegions.ToDictionary(x => x.GetType().Name, x => x.RequiredItem);
+ return new MultiplayerPlayerGenerationData(World.Guid, World.Id, locations, bosses, rewards, prerequisites, World.HintTiles.ToList());
}
-
}
diff --git a/src/TrackerCouncil.Smz3.Data/Interfaces/IRomGenerationService.cs b/src/TrackerCouncil.Smz3.Data/Interfaces/IRomGenerationService.cs
index 5ca69404a..51afa2e95 100644
--- a/src/TrackerCouncil.Smz3.Data/Interfaces/IRomGenerationService.cs
+++ b/src/TrackerCouncil.Smz3.Data/Interfaces/IRomGenerationService.cs
@@ -15,4 +15,6 @@ public interface IRomGenerationService
public Task GeneratePreSeededRomAsync(RandomizerOptions options, SeedData seed,
MultiplayerGameDetails multiplayerGameDetails);
+
+ public void ApplyCasPatches(byte[] rom, PatchOptions options);
}
diff --git a/src/TrackerCouncil.Smz3.Data/Options/PlandoConfig.cs b/src/TrackerCouncil.Smz3.Data/Options/PlandoConfig.cs
index cf0d9f31f..07f830fc9 100644
--- a/src/TrackerCouncil.Smz3.Data/Options/PlandoConfig.cs
+++ b/src/TrackerCouncil.Smz3.Data/Options/PlandoConfig.cs
@@ -39,8 +39,8 @@ public PlandoConfig(World world)
.ToDictionary(x => x.ToString(), x => x.Item.Type);
Rewards = world.Regions.Where(x => x is IHasReward)
.ToDictionary(x => x.ToString(), x => ((IHasReward)x).RewardType);
- Medallions = world.Regions.Where(x => x is INeedsMedallion)
- .ToDictionary(x => x.ToString(), x => ((INeedsMedallion)x).Medallion);
+ Medallions = world.Regions.Where(x => x is IHasPrerequisite)
+ .ToDictionary(x => x.ToString(), x => ((IHasPrerequisite)x).RequiredItem);
Logic = world.Config.LogicConfig.Clone();
StartingInventory = world.Config.ItemOptions;
var prizes = DropPrizes.GetPool(world.Config.CasPatches.ZeldaDrops);
diff --git a/src/TrackerCouncil.Smz3.Data/Options/RandomizerOptions.cs b/src/TrackerCouncil.Smz3.Data/Options/RandomizerOptions.cs
index e1908eada..e042d02bf 100644
--- a/src/TrackerCouncil.Smz3.Data/Options/RandomizerOptions.cs
+++ b/src/TrackerCouncil.Smz3.Data/Options/RandomizerOptions.cs
@@ -36,10 +36,10 @@ public RandomizerOptions(GeneralOptions generalOptions,
PatchOptions patchOptions,
LogicConfig logicConfig)
{
- GeneralOptions = generalOptions ?? new();
- SeedOptions = seedOptions ?? new();
- PatchOptions = patchOptions ?? new();
- LogicConfig = logicConfig ?? new();
+ GeneralOptions = generalOptions;
+ SeedOptions = seedOptions;
+ PatchOptions = patchOptions;
+ LogicConfig = logicConfig;
}
public event PropertyChangedEventHandler? PropertyChanged;
@@ -59,12 +59,13 @@ public RandomizerOptions(GeneralOptions generalOptions,
[JsonIgnore, YamlIgnore]
public string? FilePath { get; set; }
+ public string? ApplicationVersion { get; set; }
+
public bool IsAdvancedMode { get; set; }
public double WindowWidth { get; set; } = 500d;
public double WindowHeight { get; set; } = 600d;
-
public string MultiplayerUrl { get; set; } = "";
[YamlIgnore]
diff --git a/src/TrackerCouncil.Smz3.Data/RandomizerVersion.cs b/src/TrackerCouncil.Smz3.Data/RandomizerVersion.cs
index b92af6715..c44c8614d 100644
--- a/src/TrackerCouncil.Smz3.Data/RandomizerVersion.cs
+++ b/src/TrackerCouncil.Smz3.Data/RandomizerVersion.cs
@@ -11,4 +11,8 @@ public class RandomizerVersion
public static string VersionString => Version.ToString();
public static int MajorVersion => Version.Major;
+
+ public static Version PreReplaceDungeonStateVersion => new Version(6, 0);
+
+ public static bool IsVersionPreReplaceDungeonState(int version) => version <= PreReplaceDungeonStateVersion.Major;
}
diff --git a/src/TrackerCouncil.Smz3.Data/Services/GameDbService.cs b/src/TrackerCouncil.Smz3.Data/Services/GameDbService.cs
index 12964d954..a4f70e6ad 100644
--- a/src/TrackerCouncil.Smz3.Data/Services/GameDbService.cs
+++ b/src/TrackerCouncil.Smz3.Data/Services/GameDbService.cs
@@ -93,11 +93,18 @@ public bool DeleteGeneratedRom(GeneratedRom rom, out string error)
{
context.Entry(rom.TrackerState).Collection(x => x.ItemStates).Load();
context.Entry(rom.TrackerState).Collection(x => x.LocationStates).Load();
+ context.Entry(rom.TrackerState).Collection(x => x.BossStates).Load();
+ context.Entry(rom.TrackerState).Collection(x => x.RewardStates).Load();
+ context.Entry(rom.TrackerState).Collection(x => x.PrerequisiteStates).Load();
+ context.Entry(rom.TrackerState).Collection(x => x.TreasureStates).Load();
+ context.Entry(rom.TrackerState).Collection(x => x.Hints).Load();
+ context.Entry(rom.TrackerState).Collection(x => x.History).Load();
+
+#pragma warning disable CS0618 // Type or member is obsolete
context.Entry(rom.TrackerState).Collection(x => x.RegionStates).Load();
context.Entry(rom.TrackerState).Collection(x => x.DungeonStates).Load();
context.Entry(rom.TrackerState).Collection(x => x.MarkedLocations).Load();
- context.Entry(rom.TrackerState).Collection(x => x.BossStates).Load();
- context.Entry(rom.TrackerState).Collection(x => x.History).Load();
+#pragma warning restore CS0618 // Type or member is obsolete
context.TrackerStates.Remove(rom.TrackerState);
}
diff --git a/src/TrackerCouncil.Smz3.Data/Services/IMetadataService.cs b/src/TrackerCouncil.Smz3.Data/Services/IMetadataService.cs
index 5828b6490..979797ac5 100644
--- a/src/TrackerCouncil.Smz3.Data/Services/IMetadataService.cs
+++ b/src/TrackerCouncil.Smz3.Data/Services/IMetadataService.cs
@@ -141,13 +141,13 @@ public interface IMetadataService
///
/// Returns extra information for the specified dungeon.
///
- ///
+ ///
/// The dungeon to get extra information for.
///
///
/// A new for the specified dungeon region.
///
- public DungeonInfo Dungeon(IDungeon dungeon);
+ public DungeonInfo Dungeon(IHasTreasure hasTreasure);
///
/// Returns extra information for the specified room.
@@ -245,4 +245,28 @@ public interface IMetadataService
/// The type of the reward
///
public RewardInfo? Reward(RewardType type);
+
+ ///
+ /// Returns a random name for the specified item including article, e.g.
+ /// "an E-Tank" or "the Book of Mudora".
+ ///
+ /// The type of item whose name to get.
+ ///
+ /// The name of the type of item, including "a", "an" or "the" if
+ /// applicable.
+ ///
+ string GetName(ItemType itemType);
+
+
+ ///
+ /// Returns a random name for the specified item including article, e.g.
+ /// "a blue crystal" or "the green pendant".
+ ///
+ /// The reward of item whose name to get.
+ ///
+ /// The name of the reward of item, including "a", "an" or "the" if
+ /// applicable.
+ ///
+ string GetName(RewardType rewardType);
+
}
diff --git a/src/TrackerCouncil.Smz3.Data/Services/ITrackerStateService.cs b/src/TrackerCouncil.Smz3.Data/Services/ITrackerStateService.cs
index 9541c427c..616fa751f 100644
--- a/src/TrackerCouncil.Smz3.Data/Services/ITrackerStateService.cs
+++ b/src/TrackerCouncil.Smz3.Data/Services/ITrackerStateService.cs
@@ -9,7 +9,7 @@ public interface ITrackerStateService
{
public Task CreateStateAsync(IEnumerable world, GeneratedRom generatedRom);
- public TrackerState CreateTrackerState(IEnumerable worlds);
+ public TrackerState CreateTrackerState(List worlds);
public Task SaveStateAsync(IEnumerable worlds, GeneratedRom generatedRom, double secondsElapsed);
diff --git a/src/TrackerCouncil.Smz3.Data/Services/MetadataService.cs b/src/TrackerCouncil.Smz3.Data/Services/MetadataService.cs
index e6c5cc720..137bfe47d 100644
--- a/src/TrackerCouncil.Smz3.Data/Services/MetadataService.cs
+++ b/src/TrackerCouncil.Smz3.Data/Services/MetadataService.cs
@@ -171,14 +171,14 @@ public DungeonInfo Dungeon() where TRegion : Region
///
/// Returns extra information for the specified dungeon.
///
- ///
+ ///
/// The dungeon to get extra information for.
///
///
/// A new for the specified dungeon region.
///
- public DungeonInfo Dungeon(IDungeon dungeon)
- => Dungeon(dungeon.GetType());
+ public DungeonInfo Dungeon(IHasTreasure hasTreasure)
+ => Dungeon(hasTreasure.GetType());
///
/// Returns extra information for the specified room.
@@ -287,4 +287,9 @@ public LocationInfo Location(Location location)
///
public RewardInfo? Reward(RewardType type)
=> Rewards.FirstOrDefault(x => x.RewardType == type);
+
+ public string GetName(ItemType itemType) => Item(itemType)?.NameWithArticle ?? itemType.GetDescription();
+
+ public string GetName(RewardType rewardType) => Reward(rewardType)?.NameWithArticle ?? rewardType.GetDescription();
+
}
diff --git a/src/TrackerCouncil.Smz3.Data/Services/TrackerStateService.cs b/src/TrackerCouncil.Smz3.Data/Services/TrackerStateService.cs
index 98e1f7627..58024d55b 100644
--- a/src/TrackerCouncil.Smz3.Data/Services/TrackerStateService.cs
+++ b/src/TrackerCouncil.Smz3.Data/Services/TrackerStateService.cs
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
+using System.Collections.Immutable;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.Extensions.Logging;
@@ -8,7 +9,6 @@
using TrackerCouncil.Smz3.Data.Options;
using TrackerCouncil.Smz3.Data.WorldData;
using TrackerCouncil.Smz3.Data.WorldData.Regions;
-using TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
using TrackerCouncil.Smz3.Shared.Enums;
using TrackerCouncil.Smz3.Shared.Models;
@@ -29,23 +29,22 @@ public TrackerStateService(RandomizerContext dbContext, ILogger worlds, GeneratedRom generatedRom)
{
- var state = CreateTrackerState(worlds);
+ var worldList = worlds.ToList();
- foreach (var world in worlds)
+ var state = CreateTrackerState(worldList);
+
+ foreach (var world in worldList)
{
world.State = state;
}
generatedRom.TrackerState = state;
await _randomizerContext.SaveChangesAsync();
-
}
- public TrackerState CreateTrackerState(IEnumerable worlds)
+ public TrackerState CreateTrackerState(List worlds)
{
- var worldList = worlds.ToList();
-
- var locationStates = worldList
+ var locationStates = worlds
.SelectMany(x => x.Locations)
.Select(x => new TrackerLocationState
{
@@ -60,7 +59,7 @@ public TrackerState CreateTrackerState(IEnumerable worlds)
var addedItems = new List<(World, ItemType)>();
var itemStates = new List();
- foreach (var item in worldList.SelectMany(x => x.AllItems))
+ foreach (var item in worlds.SelectMany(x => x.AllItems))
{
if (addedItems.Contains((item.World, item.Type))) continue;
itemStates.Add(new TrackerItemState
@@ -72,31 +71,52 @@ public TrackerState CreateTrackerState(IEnumerable worlds)
addedItems.Add((item.World, item.Type));
}
- var dungeonStates = worldList
- .SelectMany(x => x.Dungeons)
- .Select(x => new TrackerDungeonState
+ var rewardStates = worlds
+ .SelectMany(x => x.RewardRegions)
+ .Select(x => new TrackerRewardState
{
- Name = x.GetType().Name,
- RemainingTreasure = x.GetTreasureCount(),
- Reward = x is IHasReward rewardRegion ? rewardRegion.RewardType : null,
- RequiredMedallion = x is INeedsMedallion medallionRegion ? medallionRegion.Medallion : null,
- MarkedReward = x is CastleTower ? RewardType.Agahnim : null,
- WorldId = ((Region)x).World.Id
+ RegionName = x.GetType().Name,
+ RewardType = x.RewardType,
+ MarkedReward = x.MarkedReward,
+ WorldId = x.World.Id
})
.ToList();
- var bossStates = worldList
+ var treasureStates = worlds
+ .SelectMany(x => x.TreasureRegions)
+ .Select(region => new TrackerTreasureState
+ {
+ RegionName = region.GetType().Name,
+ RemainingTreasure = region.GetTreasureCount(),
+ TotalTreasure = region.GetTreasureCount(),
+ WorldId = ((Region)region).World.Id
+ })
+ .ToList();
+
+ var bossStates = worlds
.SelectMany(x => x.AllBosses)
- .Select(boss => new TrackerBossState()
+ .Select(boss => new TrackerBossState
{
BossName = boss.Name,
+ RegionName = boss.Region?.GetType().Name ?? string.Empty,
Type = boss.Type,
WorldId = boss.World.Id,
})
.ToList();
+ var prereqStates = worlds
+ .SelectMany(x => x.PrerequisiteRegions)
+ .Select(region => new TrackerPrerequisiteState
+ {
+ RequiredItem = region.RequiredItem,
+ RegionName = region.GetType().Name,
+ WorldId = region.World.Id
+ })
+ .ToList();
+
+
var hintStates = new List();
- foreach (var hint in worldList.SelectMany(x => x.HintTiles))
+ foreach (var hint in worlds.SelectMany(x => x.HintTiles))
{
var hintState = new TrackerHintState()
{
@@ -115,7 +135,7 @@ public TrackerState CreateTrackerState(IEnumerable worlds)
}
// Add starting equipment, including items that may not be in the world anymore
- foreach (var world in worldList)
+ foreach (var world in worlds)
{
var startingInventory = ItemSettingOptions.GetStartingItemTypes(world.Config).ToList();
foreach (var itemType in startingInventory.Distinct())
@@ -140,7 +160,7 @@ public TrackerState CreateTrackerState(IEnumerable worlds)
}
// Add items from metadata that may be missing
- foreach (var world in worldList)
+ foreach (var world in worlds)
{
foreach (var itemMetadata in _configs.Items.Where(m => !itemStates.Any(s => m.Is(s) && s.WorldId == world.Id)))
{
@@ -159,9 +179,11 @@ public TrackerState CreateTrackerState(IEnumerable worlds)
{
LocationStates = locationStates,
ItemStates = itemStates,
- DungeonStates = dungeonStates,
+ TreasureStates = treasureStates,
BossStates = bossStates,
- LocalWorldId = worldList.First(x => x.IsLocalWorld).Id,
+ RewardStates = rewardStates,
+ PrerequisiteStates = prereqStates,
+ LocalWorldId = worlds.First(x => x.IsLocalWorld).Id,
Hints = hintStates,
StartDateTime = DateTimeOffset.Now,
UpdatedDateTime = DateTimeOffset.Now
@@ -191,7 +213,7 @@ public TrackerState CreateTrackerState(IEnumerable worlds)
MedallionType = hint.MedallionType,
HintTileCode = hint.HintTileCode,
State = hint
- });
+ }).ToImmutableList();
world.State = trackerState;
}
@@ -201,6 +223,7 @@ public TrackerState CreateTrackerState(IEnumerable worlds)
public async Task SaveStateAsync(IEnumerable worlds, GeneratedRom generatedRom, double secondsElapsed)
{
+ var worldList = worlds.ToList();
var trackerState = generatedRom.TrackerState;
if (trackerState == null)
@@ -208,43 +231,42 @@ public async Task SaveStateAsync(IEnumerable worlds, GeneratedRom generat
return;
}
- foreach (var world in worlds)
+ foreach (var world in worldList)
{
world.State = trackerState;
}
- SaveLocationStates(worlds, trackerState);
- SaveItemStates(worlds, trackerState);
- SaveBossStates(worlds, trackerState);
+ SaveLocationStates(worldList, trackerState);
+ SaveItemStates(worldList, trackerState);
+ SaveBossStates(worldList, trackerState);
trackerState.UpdatedDateTime = DateTimeOffset.Now;
trackerState.SecondsElapsed = secondsElapsed;
-
await _randomizerContext.SaveChangesAsync();
}
- private void SaveLocationStates(IEnumerable worlds, TrackerState trackerState)
+ private void SaveLocationStates(List worlds, TrackerState trackerState)
{
var totalLocations = worlds.SelectMany(x => x.Locations).Count();
- var clearedLocations = worlds.SelectMany(x => x.Locations).Count(x => x.State.Cleared == true);
+ var clearedLocations = worlds.SelectMany(x => x.Locations).Count(x => x.State.Cleared);
var percCleared = (int)Math.Floor((double)clearedLocations / totalLocations * 100);
trackerState.PercentageCleared = percCleared;
}
- private void SaveItemStates(IEnumerable worlds, TrackerState trackerState)
+ private void SaveItemStates(List worlds, TrackerState trackerState)
{
// Add any new item states
var itemStates = worlds
.SelectMany(x => x.AllItems)
.Select(x => x.State).Distinct()
- .Where(x => x != null && !trackerState.ItemStates.Contains(x))
+ .Where(x => !trackerState.ItemStates.Contains(x))
.NonNull()
.ToList();
itemStates.ForEach(x => trackerState.ItemStates.Add(x) );
}
- private void SaveBossStates(IEnumerable worlds, TrackerState trackerState)
+ private void SaveBossStates(List worlds, TrackerState trackerState)
{
// Add any new item states
var bossStates = worlds
diff --git a/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj b/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj
index 0c44b97ab..4fe79a71e 100644
--- a/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj
+++ b/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj
@@ -27,7 +27,7 @@
-
+
diff --git a/src/TrackerCouncil.Smz3.Data/Tracking/AutoTrackerMetroidState.cs b/src/TrackerCouncil.Smz3.Data/Tracking/AutoTrackerMetroidState.cs
index d948f49c3..0016b6f8a 100644
--- a/src/TrackerCouncil.Smz3.Data/Tracking/AutoTrackerMetroidState.cs
+++ b/src/TrackerCouncil.Smz3.Data/Tracking/AutoTrackerMetroidState.cs
@@ -105,8 +105,9 @@ public bool IsSamusInArea(int minX, int maxX, int minY, int maxY)
/// Checks to make sure that the state is valid and fully loaded. There's a period upon first booting up that
/// all of these are 0s, but some of the memory in the location data can be screwy.
///
- public bool IsValid => CurrentRoom != 0 || CurrentRegion != 0 || CurrentRoomInRegion != 0 || Energy != 0 ||
- SamusX != 0 || SamusY != 0;
+ public bool IsValid => Energy is > 0 and < 1999 && ReserveTanks is >= 0 and < 600 && (CurrentRoom != 0 ||
+ CurrentRegion != 0 || CurrentRoomInRegion != 0 || Energy != 0 ||
+ SamusX != 0 || SamusY != 0);
///
/// Prints debug data for the state
diff --git a/src/TrackerCouncil.Smz3.Data/Tracking/AutoTrackerZeldaState.cs b/src/TrackerCouncil.Smz3.Data/Tracking/AutoTrackerZeldaState.cs
index ceff60d77..26e611aa0 100644
--- a/src/TrackerCouncil.Smz3.Data/Tracking/AutoTrackerZeldaState.cs
+++ b/src/TrackerCouncil.Smz3.Data/Tracking/AutoTrackerZeldaState.cs
@@ -104,6 +104,8 @@ public bool IsWithinRegion(int topLeftX, int topLeftY, int bottomRightX, int bot
///
public int? ReadUInt16(int address) => _data.ReadUInt16(address);
+ public bool IsValid => State is >= 0x01 and <= 0x1b && LinkState is >= 0x00 and <= 0x1E;
+
///
/// Get debug string
///
diff --git a/src/TrackerCouncil.Smz3.Data/Tracking/DungeonTrackedEventArgs.cs b/src/TrackerCouncil.Smz3.Data/Tracking/DungeonTrackedEventArgs.cs
index f90ec4cc1..e0e6a3110 100644
--- a/src/TrackerCouncil.Smz3.Data/Tracking/DungeonTrackedEventArgs.cs
+++ b/src/TrackerCouncil.Smz3.Data/Tracking/DungeonTrackedEventArgs.cs
@@ -14,7 +14,7 @@ public class DungeonTrackedEventArgs : TrackerEventArgs
/// The dungeon that was tracked.
/// The speech recognition confidence.
/// If the location was automatically tracked
- public DungeonTrackedEventArgs(IDungeon? dungeon, float? confidence, bool autoTracked)
+ public DungeonTrackedEventArgs(IHasTreasure? dungeon, float? confidence, bool autoTracked)
: base(confidence, autoTracked)
{
Dungeon = dungeon;
@@ -23,5 +23,5 @@ public DungeonTrackedEventArgs(IDungeon? dungeon, float? confidence, bool autoTr
///
/// Gets the boss that was tracked.
///
- public IDungeon? Dungeon { get; }
+ public IHasTreasure? Dungeon { get; }
}
diff --git a/src/TrackerCouncil.Smz3.Data/ViewModels/GenerationWindowItemsViewModel.cs b/src/TrackerCouncil.Smz3.Data/ViewModels/GenerationWindowItemsViewModel.cs
index 039d6df64..46746466b 100644
--- a/src/TrackerCouncil.Smz3.Data/ViewModels/GenerationWindowItemsViewModel.cs
+++ b/src/TrackerCouncil.Smz3.Data/ViewModels/GenerationWindowItemsViewModel.cs
@@ -100,7 +100,7 @@ public void Init(RandomizerOptions options, LocationConfig locations)
LocationOptions.Add(new GenerationWindowLocationViewModel()
{
LocationId = location.Id,
- LocationName = locationDetails.Name?.FirstOrDefault() ?? location.Id.ToString(),
+ LocationName = location.Name,
Options = LocationItemOptions,
Region = location.Region,
SelectedOption = options.SeedOptions.LocationItems.TryGetValue(location.Id, out var locationSetting)
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Boss.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Boss.cs
index 1c1e180d8..5ca43f814 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Boss.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Boss.cs
@@ -1,4 +1,5 @@
-using System.Linq;
+using System;
+using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
using TrackerCouncil.Smz3.Data.Services;
@@ -13,21 +14,22 @@ namespace TrackerCouncil.Smz3.Data.WorldData;
///
public class Boss
{
+ private Accessibility _accessibility;
+
///
/// Constructor
///
/// The type of boss
/// The world this boss belongs to
- /// The name of the boss
/// The metadata object with additional boss info
- /// The tracking state of the boss
- public Boss(BossType type, World world, string name, BossInfo metadata, TrackerBossState state)
+ ///
+ public Boss(BossType type, World world, BossInfo metadata, TrackerBossState? bossState = null)
{
Type = type;
World = world;
- Name = name;
+ Name = metadata.Boss;
Metadata = metadata;
- State = state;
+ State = bossState ?? new TrackerBossState();
}
///
@@ -35,17 +37,15 @@ public Boss(BossType type, World world, string name, BossInfo metadata, TrackerB
///
/// The type of boss
/// The world this boss belongs to
- /// The region where this boss is locatedd
/// The metadata service for looking up additional boss info
- /// The tracking state for this run
- public Boss(BossType type, World world, IHasBoss region, IMetadataService? metadata, TrackerState? trackerState)
+ ///
+ public Boss(BossType type, World world, IMetadataService? metadata, TrackerBossState? bossState = null)
{
Type = type;
World = world;
- Region = region;
Name = type.GetDescription();
Metadata = metadata?.Boss(type) ?? new BossInfo(Name);
- State = trackerState?.BossStates.First(x => x.WorldId == world.Id && x.BossName == Name) ?? new TrackerBossState();
+ State = bossState ?? new TrackerBossState();
}
public string Name { get; set; }
@@ -60,6 +60,61 @@ public Boss(BossType type, World world, IHasBoss region, IMetadataService? metad
public IHasBoss? Region { get; set; }
+ public bool Defeated
+ {
+ get => State.Defeated;
+ set
+ {
+ State.Defeated = value;
+ UpdatedBossState?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ public bool AutoTracked
+ {
+ get => State.AutoTracked;
+ set => State.AutoTracked = value;
+ }
+
+ public Accessibility Accessibility
+ {
+ get => _accessibility;
+ set
+ {
+ if (_accessibility == value) return;
+ _accessibility = value;
+ UpdatedAccessibility?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ public void UpdateAccessibility(Progression actualProgression, Progression withKeysProgression)
+ {
+ if (Defeated)
+ {
+ Accessibility = Accessibility.Cleared;
+ }
+ else if (Region == null)
+ {
+ Accessibility = Accessibility.Unknown;
+ }
+ else if (Region.CanBeatBoss(actualProgression))
+ {
+ Accessibility = Accessibility.Available;
+ }
+ else if (Region.CanBeatBoss(withKeysProgression))
+ {
+ Accessibility = Accessibility.AvailableWithKeys;
+ }
+ else
+ {
+ Accessibility = Accessibility.OutOfLogic;
+ }
+ }
+
+ public event EventHandler? UpdatedBossState;
+
+ public event EventHandler? UpdatedAccessibility;
+
///
/// Determines if an item matches the type or name
///
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Item.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Item.cs
index a25299590..d82d5374a 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Item.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Item.cs
@@ -84,6 +84,21 @@ public Item(ItemType itemType, World world, string name, ItemData metadata, Trac
///
public TrackerItemState State { get; set; }
+ public int TrackingState
+ {
+ get => State.TrackingState;
+ set
+ {
+ if (value == State.TrackingState)
+ {
+ return;
+ }
+
+ State.TrackingState = value;
+ UpdatedItemState?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
///
/// Indicates whether the item is a dungeon-specific item.
///
@@ -118,15 +133,15 @@ public Item(ItemType itemType, World world, string name, ItemData metadata, Trac
///
public bool IsKeycard => Type.IsInCategory(ItemCategory.Keycard);
+ public bool IsTreasure => !IsDungeonItem;
+
///
/// Gets the number of actual items as displayed or mentioned by
/// tracker, or 0 if the item does not have copies.
///
- public int Counter => State == null || Metadata == null
- ? 0
- : Metadata.Multiple && !Metadata.HasStages
- ? State.TrackingState * (Metadata.CounterMultiplier ?? 1)
- : 0;
+ public int Counter => Metadata is { Multiple: true, HasStages: false }
+ ? TrackingState * (Metadata.CounterMultiplier ?? 1)
+ : 0;
///
/// Tracks the item.
@@ -144,11 +159,11 @@ public bool Track()
if (State == null)
throw new InvalidOperationException($"State not loaded for item '{Name}'");
- if (State.TrackingState == 0 // Item hasn't been tracked yet (any case)
- || (Metadata?.HasStages == false && Metadata?.Multiple == true) // State.Multiple items always track
- || (Metadata?.HasStages == true && State.TrackingState < Metadata?.MaxStage)) // Hasn't reached max. stage yet
+ if (TrackingState == 0 // Item hasn't been tracked yet (any case)
+ || Metadata is { HasStages: false, Multiple: true } // State.Multiple items always track
+ || (Metadata.HasStages && TrackingState < Metadata.MaxStage)) // Hasn't reached max. stage yet
{
- State.TrackingState++;
+ TrackingState++;
return true;
}
@@ -167,10 +182,10 @@ public bool Untrack()
if (State == null)
throw new InvalidOperationException($"State not loaded for item '{Name}'");
- if (State.TrackingState == 0)
+ if (TrackingState == 0)
return false;
- State.TrackingState--;
+ TrackingState--;
return true;
}
@@ -190,15 +205,15 @@ public bool Track(int stage)
if (State == null)
throw new InvalidOperationException($"State not loaded for item '{Name}'");
- if (Metadata?.HasStages == false)
+ if (Metadata.HasStages == false)
throw new ArgumentException($"The item '{Name}' does not have Multiple stages.");
- if (stage > Metadata?.MaxStage)
+ if (stage > Metadata.MaxStage)
throw new ArgumentOutOfRangeException($"Cannot advance item '{Name}' to stage {stage} as the highest state is {Metadata.MaxStage}.");
- if (State?.TrackingState < stage)
+ if (TrackingState < stage)
{
- State.TrackingState = stage;
+ TrackingState = stage;
return true;
}
@@ -221,7 +236,7 @@ public bool TryGetTrackingResponse([NotNullWhen(true)] out SchrodingersString? r
{
if (Metadata == null || State == null)
throw new InvalidOperationException($"State or metadata not loaded item '{Name}'");
- return Metadata.TryGetTrackingResponse(State.TrackingState, out response);
+ return Metadata.TryGetTrackingResponse(TrackingState, out response);
}
///
@@ -262,6 +277,15 @@ public bool IsNot(ItemType type, World world)
public bool Is(ItemType type, string name)
=> (Type != ItemType.Nothing && Type == type) || (Type == ItemType.Nothing && Name == name);
+ ///
+ /// Determines if an item matches name
+ ///
+ /// The name to compare against if the item type is set to Nothing
+ /// if the item matches the given name otherwise, .
+ public bool Is(string name)
+ => Name == name || Metadata.Name?.Contains(name, StringComparison.OrdinalIgnoreCase) == true ||
+ Metadata.GetStage(name) != null;
+
///
/// Updates an item in the middle of world generation
///
@@ -272,6 +296,8 @@ public void UpdateItemType(ItemType type)
Name = type.GetDescription();
}
+ public event EventHandler? UpdatedItemState;
+
///
/// Returns a string that represents the item.
///
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Location.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Location.cs
index fe19d1208..16d3cc06f 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Location.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Location.cs
@@ -1,9 +1,11 @@
-using System.Linq;
+using System;
+using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
using TrackerCouncil.Smz3.Data.Logic;
using TrackerCouncil.Smz3.Data.Services;
using TrackerCouncil.Smz3.Data.WorldData.Regions;
+using TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
using TrackerCouncil.Smz3.Shared.Enums;
using TrackerCouncil.Smz3.Shared.Models;
@@ -104,6 +106,64 @@ public Location(Region region, LocationId id, int romAddress, LocationType type,
///
public TrackerLocationState State { get; set; }
+ public ItemType ItemType => Item.Type;
+
+ public bool Cleared
+ {
+ get => State.Cleared;
+ set
+ {
+ if (State.Cleared == value) return;
+ State.Cleared = value;
+ ClearedUpdated?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ public bool Autotracked
+ {
+ get => State.Autotracked;
+ set => State.Autotracked = value;
+ }
+
+ public ItemType? MarkedItem
+ {
+ get => State.MarkedItem;
+ set
+ {
+ if (State.MarkedItem == value) return;
+ State.MarkedItem = value;
+
+ if (value != null)
+ {
+ State.MarkedUsefulness =
+ Item.Type.IsPossibleProgression(World.Config.ZeldaKeysanity, World.Config.MetroidKeysanity)
+ ? LocationUsefulness.NiceToHave
+ : LocationUsefulness.Useless;
+ }
+ else
+ {
+ State.MarkedUsefulness = null;
+ }
+
+ MarkedItemUpdated?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ public LocationUsefulness? MarkedUsefulness
+ {
+ get => State.MarkedUsefulness;
+ set
+ {
+ if (State.MarkedUsefulness == value) return;
+ State.MarkedUsefulness = value;
+ MarkedItemUpdated?.Invoke(this, EventArgs.Empty);
+ }
+ }
+
+ public bool HasMarkedItem => State.HasMarkedItem;
+
+ public bool HasMarkedCorrectItem => State.HasMarkedCorrectItem;
+
///
/// Gets the type of location.
///
@@ -235,17 +295,48 @@ public bool IsAvailable(Progression items, bool applyTrackerLogic = false)
/// name="items"/>; otherwise, .
public bool IsRelevant(Progression items) => Region.CanEnter(items, false) && _relevanceRequirement(items) && _trackerLogic(items);
+ public Accessibility Accessibility { get; private set; }
+
+ public Accessibility GetKeysanityAdjustedAccessibility()
+ {
+ return Region.GetKeysanityAdjustedAccessibility(Accessibility);
+ }
+
+ public void SetAccessibility(Accessibility newValue)
+ {
+ if (Accessibility == newValue) return;
+ Accessibility = newValue;
+ AccessibilityUpdated?.Invoke(this, EventArgs.Empty);
+ }
+
+ ///
+ /// Returns the status of a location based on the given items
+ ///
+ /// The available items
+ /// The available items plus additional keys
+ /// The LocationStatus enum of the location
+ public Accessibility GetAccessibility(Progression actualProgression, Progression withKeysProgression)
+ {
+ if (State.Cleared) return Accessibility.Cleared;
+ else if (IsAvailable(actualProgression) && _trackerLogic(actualProgression)) return Accessibility.Available;
+ else if (IsAvailable(withKeysProgression) && _trackerLogic(withKeysProgression)) return Accessibility.AvailableWithKeys;
+ else if (IsRelevant(actualProgression) && _trackerLogic(actualProgression)) return Accessibility.Relevant;
+ else if (IsRelevant(withKeysProgression) && _trackerLogic(withKeysProgression)) return Accessibility.RelevantWithKeys;
+ else return Accessibility.OutOfLogic;
+ }
+
///
/// Returns the status of a location based on the given items
///
- /// The available items
+ /// The available items
+ /// The available items plus additional keys
/// The LocationStatus enum of the location
- public LocationStatus GetStatus(Progression items)
+ public void UpdateAccessibility(Progression actualProgression, Progression withKeysProgression)
{
- if (State.Cleared) return LocationStatus.Cleared;
- else if (IsAvailable(items) && _trackerLogic(items)) return LocationStatus.Available;
- else if (IsRelevant(items) && _trackerLogic(items)) return LocationStatus.Relevant;
- else return LocationStatus.OutOfLogic;
+ var newValue = GetAccessibility(actualProgression, withKeysProgression);
+ if (newValue == Accessibility) return;
+ Accessibility = newValue;
+ AccessibilityUpdated?.Invoke(this, EventArgs.Empty);
}
///
@@ -290,7 +381,6 @@ public bool IsPotentiallyImportant(KeysanityMode? keysanity = null)
|| (Item.IsKeycard && keysanity is KeysanityMode.Both or KeysanityMode.SuperMetroid);
}
-
///
/// Returns a string that represents the location.
///
@@ -301,4 +391,10 @@ public override string ToString()
? $"{Room} - {Name}"
: $"{Region} - {Name}";
}
+
+ public IHasTreasure? GetTreasureRegion() => Region as IHasTreasure;
+
+ public event EventHandler? ClearedUpdated;
+ public event EventHandler? MarkedItemUpdated;
+ public event EventHandler? AccessibilityUpdated;
}
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IDungeon.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IDungeon.cs
deleted file mode 100644
index 0cb4c663c..000000000
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IDungeon.cs
+++ /dev/null
@@ -1,125 +0,0 @@
-using System.Linq;
-using TrackerCouncil.Smz3.Shared;
-using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
-using TrackerCouncil.Smz3.Shared.Enums;
-using TrackerCouncil.Smz3.Shared.Models;
-
-namespace TrackerCouncil.Smz3.Data.WorldData.Regions;
-
-///
-/// Defines a region that offers a reward for completing it, e.g. a Zelda
-/// dungeon or a Super Metroid boss.
-///
-public interface IDungeon
-{
- ///
- /// Gets or sets the reward for completing the region, e.g. pendant or
- /// crystal.
- ///
- DungeonInfo DungeonMetadata { get; set; }
-
- ///
- /// The current tracking state of the dungeon
- ///
- TrackerDungeonState DungeonState { get; set; }
-
- ///
- /// Calculates the number of treasures in the dungeon
- ///
- ///
- public int GetTreasureCount()
- {
- var region = (Region)this;
- return region.Locations.Count(x => x.Item.Type != ItemType.Nothing && (!x.Item.IsDungeonItem || region.World.Config.ZeldaKeysanity) && x.Type != LocationType.NotInDungeon);
- }
-
- ///
- /// Retrieves the base name of the dungeon
- ///
- public string DungeonName => ((Region)this).Name;
-
- ///
- /// The reward object for the dungeon, if any
- ///
- public Reward? DungeonReward => HasReward ? ((IHasReward)this).Reward : null;
-
- ///
- /// The type of reward in the dungeon, if any
- ///
- public RewardType? DungeonRewardType => DungeonReward?.Type;
-
- ///
- /// If the dungeon has a pendant in it or not
- ///
- public bool IsPendantDungeon => DungeonRewardType is RewardType.PendantGreen or RewardType.PendantRed or RewardType.PendantBlue;
-
- ///
- /// If the dungeon has a crystal in it or not
- ///
- public bool IsCrystalDungeon => DungeonRewardType is RewardType.CrystalBlue or RewardType.CrystalRed;
-
- ///
- /// The MSU song index for the dungeon
- ///
- public int SongIndex { get; init; }
-
- public string Abbreviation { get; }
-
- public LocationId? BossLocationId { get; }
-
- ///
- /// The reward marked by the player
- ///
- public RewardType MarkedReward
- {
- get
- {
- return DungeonState.MarkedReward ?? RewardType.None;
- }
- set
- {
- if (DungeonState != null)
- {
- DungeonState.MarkedReward = value;
- }
- }
- }
-
- ///
- /// If this dungeon has a reward in it
- ///
- public bool HasReward => this is IHasReward;
-
- ///
- /// If this dungeon needs a medallion
- ///
- public bool NeedsMedallion => this is INeedsMedallion;
-
- ///
- /// The medallion required to entered the dungeon, if any
- ///
- public ItemType Medallion => NeedsMedallion ? ((INeedsMedallion)this).Medallion : ItemType.Nothing;
-
- ///
- /// The required medallion marked by the player
- ///
- public ItemType MarkedMedallion
- {
- get
- {
- return DungeonState.MarkedMedallion ?? ItemType.Nothing;
- }
- set
- {
- if (DungeonState != null)
- {
- DungeonState.MarkedMedallion = value;
- }
- }
- }
-
- ///
- /// The overworld region that this dungeon is within
- ///
- public Region ParentRegion { get; }
-}
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasBoss.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasBoss.cs
index 773601c5f..a5c14b6bf 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasBoss.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasBoss.cs
@@ -1,4 +1,9 @@
-using TrackerCouncil.Smz3.Shared.Enums;
+using System;
+using System.Linq;
+using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
+using TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
+using TrackerCouncil.Smz3.Shared.Enums;
+using TrackerCouncil.Smz3.Shared.Models;
namespace TrackerCouncil.Smz3.Data.WorldData.Regions;
@@ -7,15 +12,70 @@ namespace TrackerCouncil.Smz3.Data.WorldData.Regions;
///
public interface IHasBoss
{
- ///
- /// Gets or sets the SM golden boss for the region
- ///
- Boss Boss { get; set; }
+ string Name { get; }
- ///
- /// The boss type
- ///
- BossType BossType => Boss?.Type ?? BossType.None;
+ RegionInfo Metadata { get; set; }
+
+ World World { get; }
+
+ Boss Boss { get; protected set; }
+
+ BossInfo BossMetadata => Boss.Metadata;
+
+ BossType BossType => Boss.Type;
+
+ BossType DefaultBossType { get; }
+
+ TrackerBossState BossState => Boss.State;
+
+ LocationId? BossLocationId { get; }
+
+ Region Region => (Region)this;
+
+ public bool BossDefeated
+ {
+ get => Boss.Defeated;
+ set => Boss.Defeated = value;
+ }
+
+ public Accessibility BossAccessibility
+ {
+ get => Boss.Accessibility;
+ set => Boss.Accessibility = value;
+ }
+
+ public Accessibility GetKeysanityAdjustedBossAccessibility()
+ {
+ return Region.GetKeysanityAdjustedAccessibility(BossAccessibility);
+ }
+
+ public void SetBossType(BossType bossType)
+ {
+ Boss = World.Bosses.First(x => x.Type == bossType && x.Region == null);
+ Boss.Region = this;
+ }
+
+ public void ApplyState(TrackerState? state)
+ {
+ if (state == null)
+ {
+ SetBossType(DefaultBossType);
+ Boss.State = new TrackerBossState
+ {
+ WorldId = World.Id,
+ RegionName = GetType().Name,
+ Type = DefaultBossType,
+ BossName = Boss.Name
+ };
+ }
+ else
+ {
+ var bossState = state.BossStates.First(x =>
+ x.WorldId == World.Id && x.RegionName == GetType().Name);
+ SetBossType(bossState.Type);
+ Boss.State = bossState;
+ }
+ }
///
/// Determines whether the boss for the region can be defeated.
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasLocations.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasLocations.cs
index 4f46dbcc8..9fa9366fa 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasLocations.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasLocations.cs
@@ -21,4 +21,25 @@ public interface IHasLocations
/// Gets all locations in the area.
///
IEnumerable Locations { get; }
+
+ public IHasTreasure? GetTreasureRegion()
+ {
+ return Region as IHasTreasure;
+ }
+
+ public Region? Region => this switch
+ {
+ Room room => room.Region,
+ Region region => region,
+ _ => null
+ };
+
+ public bool IsKeysanityForArea
+ {
+ get
+ {
+ if (Region is Z3Region && Region.Config.ZeldaKeysanity) return true;
+ return Region is SMRegion && Region.Config.MetroidKeysanity;
+ }
+ }
}
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasPrerequisite.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasPrerequisite.cs
new file mode 100644
index 000000000..269fcb4c7
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasPrerequisite.cs
@@ -0,0 +1,83 @@
+using System;
+using System.Linq;
+using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
+using TrackerCouncil.Smz3.Shared.Enums;
+using TrackerCouncil.Smz3.Shared.Models;
+
+namespace TrackerCouncil.Smz3.Data.WorldData.Regions;
+
+///
+/// Defines a region that requires an item (Bombos, Ether, Quake) to be
+/// accessible.
+///
+public interface IHasPrerequisite
+{
+ public string Name { get; }
+
+ public RegionInfo Metadata { get; set; }
+
+ public World World { get; }
+
+ ///
+ /// The required item type to enter this region
+ ///
+ ItemType RequiredItem
+ {
+ get => PrerequisiteState.RequiredItem;
+ set => PrerequisiteState.RequiredItem = value;
+ }
+
+ ///
+ /// The item marked by the player
+ ///
+ ItemType? MarkedItem
+ {
+ get => PrerequisiteState.MarkedItem;
+ set
+ {
+ if (PrerequisiteState.MarkedItem == value)
+ {
+ return;
+ }
+
+ PrerequisiteState.MarkedItem = value;
+ OnUpdatedPrerequisite();
+ }
+ }
+
+ ///
+ /// Gets the default medallion for the dungeon
+ ///
+ ItemType DefaultRequiredItem { get; }
+
+ public TrackerPrerequisiteState PrerequisiteState { get; set; }
+
+ public bool HasMarkedCorrectly => RequiredItem == MarkedItem;
+
+ public void ApplyState(TrackerState? state)
+ {
+ var region = (Region)this;
+
+ if (state == null)
+ {
+ PrerequisiteState = new TrackerPrerequisiteState
+ {
+ WorldId = region.World.Id, RegionName = GetType().Name, RequiredItem = DefaultRequiredItem
+ };
+ RequiredItem = DefaultRequiredItem;
+ }
+ else
+ {
+ PrerequisiteState = state.PrerequisiteStates.FirstOrDefault(x =>
+ x.WorldId == region.World.Id && x.RegionName == GetType().Name) ?? new TrackerPrerequisiteState
+ {
+ WorldId = region.World.Id, RegionName = GetType().Name, RequiredItem = DefaultRequiredItem
+ };
+ RequiredItem = PrerequisiteState?.RequiredItem ?? DefaultRequiredItem;
+ }
+ }
+
+ event EventHandler? UpdatedPrerequisite;
+
+ void OnUpdatedPrerequisite();
+}
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasReward.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasReward.cs
index 7d67aa1c7..398cd87b6 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasReward.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasReward.cs
@@ -1,5 +1,8 @@
-using TrackerCouncil.Smz3.Shared;
+using System;
+using System.Linq;
+using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
using TrackerCouncil.Smz3.Shared.Enums;
+using TrackerCouncil.Smz3.Shared.Models;
namespace TrackerCouncil.Smz3.Data.WorldData.Regions;
@@ -9,15 +12,64 @@ namespace TrackerCouncil.Smz3.Data.WorldData.Regions;
///
public interface IHasReward
{
+ string Name { get; }
+
+ RegionInfo Metadata { get; set; }
+
+ World World { get; }
+
///
/// Gets or sets the reward for completing the region, e.g. pendant or
/// crystal.
///
RewardType RewardType => Reward.Type;
- Reward Reward { get; set; }
+ Reward Reward { get; protected set; }
+
+ RewardInfo RewardMetadata => Reward.Metadata;
+
+ RewardType DefaultRewardType { get; }
+
+ TrackerRewardState RewardState { get; set; }
+
+ public bool IsShuffledReward { get; }
+
+ Region Region => (Region)this;
+
+ public void SetReward(Reward reward)
+ {
+ Reward = reward;
+ Reward.Region = this;
+ RewardState.RewardType = reward.Type;
+ }
+
+ public void SetRewardType(RewardType rewardType)
+ {
+ var region = (Region)this;
+ Reward = region.World.Rewards.First(x => x.Type == rewardType && x.Region == null);
+ Reward.Region = this;
+ RewardState.RewardType = rewardType;
+ }
+
+ public RewardType MarkedReward
+ {
+ get => Reward.MarkedReward ?? RewardType.None;
+ set => Reward.MarkedReward = value;
+ }
+
+ public Accessibility RewardAccessibility
+ {
+ get => Reward.Accessibility;
+ set => Reward.Accessibility = value;
+ }
- RewardType DefaultRewardType { get; }
+ public bool HasCorrectlyMarkedReward => Reward.HasCorrectlyMarkedReward;
+
+ public bool HasReceivedReward
+ {
+ get => Reward.HasReceivedReward;
+ set => Reward.HasReceivedReward = value;
+ }
///
/// Determines whether the reward for the region can be obtained.
@@ -27,5 +79,42 @@ public interface IHasReward
/// if the region can be completed; otherwise,
/// .
///
- bool CanComplete(Progression items);
+ bool CanRetrieveReward(Progression items);
+
+ ///
+ /// Determines if the user can see what the reward is
+ ///
+ /// The items currently available
+ ///
+ /// if the reward can be seen; otherwise,
+ /// .
+ ///
+ bool CanSeeReward(Progression items);
+
+ public Accessibility GetKeysanityAdjustedBossAccessibility()
+ {
+ return Region.GetKeysanityAdjustedAccessibility(RewardAccessibility);
+ }
+
+ public bool HasReward(params RewardType[] types) => types.Contains(RewardType);
+
+ public void ApplyState(TrackerState? state)
+ {
+ var region = (Region)this;
+
+ if (state == null)
+ {
+ RewardState = new TrackerRewardState
+ {
+ WorldId = region.World.Id, RegionName = GetType().Name
+ };
+ SetRewardType(DefaultRewardType);
+ }
+ else
+ {
+ RewardState = state.RewardStates.First(x =>
+ x.WorldId == region.World.Id && x.RegionName == GetType().Name);
+ SetRewardType(RewardState.RewardType);
+ }
+ }
}
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasTreasure.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasTreasure.cs
new file mode 100644
index 000000000..88818b12f
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/IHasTreasure.cs
@@ -0,0 +1,82 @@
+using System;
+using System.Linq;
+using TrackerCouncil.Smz3.Shared;
+using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
+using TrackerCouncil.Smz3.Shared.Enums;
+using TrackerCouncil.Smz3.Shared.Models;
+
+namespace TrackerCouncil.Smz3.Data.WorldData.Regions;
+
+///
+/// Defines a region that has treasure (tray-sure) in it
+///
+public interface IHasTreasure
+{
+ public string Name { get; }
+
+ public RegionInfo Metadata { get; set; }
+
+ public World World { get; }
+
+ ///
+ /// The current tracking state of the treasures
+ ///
+ TrackerTreasureState TreasureState { get; set; }
+
+ ///
+ /// Gets the total treasure of this region
+ ///
+ public int TotalTreasure => TreasureState.TotalTreasure;
+
+ public bool HasManuallyClearedTreasure
+ {
+ get => TreasureState.HasManuallyClearedTreasure;
+ set => TreasureState.HasManuallyClearedTreasure = value;
+ }
+
+ ///
+ /// Gets or sets the amount of remaining treasure in this region
+ ///
+ public int RemainingTreasure
+ {
+ get => TreasureState.RemainingTreasure;
+ set
+ {
+ TreasureState.RemainingTreasure = value;
+ OnUpdatedTreasure();
+ }
+ }
+
+ ///
+ /// Calculates the number of treasures in the dungeon
+ ///
+ ///
+ public int GetTreasureCount()
+ {
+ var region = (Region)this;
+ return region.Locations.Count(x => x.Item.Type != ItemType.Nothing && (!x.Item.IsDungeonItem || region.World.Config.ZeldaKeysanity) && x.Type != LocationType.NotInDungeon);
+ }
+
+ public void ApplyState(TrackerState? state)
+ {
+ var region = (Region)this;
+
+ if (state == null)
+ {
+ var totalTreasure = GetTreasureCount();
+ TreasureState = new TrackerTreasureState()
+ {
+ WorldId = region.World.Id, RegionName = GetType().Name, TotalTreasure = totalTreasure, RemainingTreasure = totalTreasure
+ };
+ }
+ else
+ {
+ TreasureState = state.TreasureStates.First(x =>
+ x.WorldId == region.World.Id && x.RegionName == GetType().Name);
+ }
+ }
+
+ event EventHandler? UpdatedTreasure;
+
+ void OnUpdatedTreasure();
+}
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/INeedsMedallion.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/INeedsMedallion.cs
deleted file mode 100644
index f90646300..000000000
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/INeedsMedallion.cs
+++ /dev/null
@@ -1,21 +0,0 @@
-using TrackerCouncil.Smz3.Shared;
-using TrackerCouncil.Smz3.Shared.Enums;
-
-namespace TrackerCouncil.Smz3.Data.WorldData.Regions;
-
-///
-/// Defines a region that requires a medallion (Bombos, Ether, Quake) to be
-/// accessible.
-///
-public interface INeedsMedallion
-{
- ///
- /// Gets or sets the type of medallion required to access the region.
- ///
- ItemType Medallion { get; set; }
-
- ///
- /// Gets the default medallion for the dungeon
- ///
- ItemType DefaultMedallion { get; }
-}
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Region.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Region.cs
index 2b232a5e0..af8fd1988 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Region.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Region.cs
@@ -1,4 +1,5 @@
using System.Collections.Generic;
+using System.ComponentModel;
using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
@@ -181,18 +182,42 @@ public IEnumerable GetStandaloneLocations()
protected IEnumerable GetRooms()
=> GetType().GetPropertyValues(this);
- public bool CheckDungeonMedallion(Progression items, IDungeon dungeon)
+ public Accessibility GetKeysanityAdjustedAccessibility(Accessibility accessibility)
+ {
+ if (Config.KeysanityForRegion(this))
+ {
+ return accessibility;
+ }
+ else if (accessibility == Accessibility.AvailableWithKeys)
+ {
+ return Accessibility.Available;
+ }
+ else if (accessibility == Accessibility.RelevantWithKeys)
+ {
+ return Accessibility.Relevant;
+ }
+
+ return accessibility;
+ }
+
+ ///
+ /// Returns if the region matches the LocationFilter
+ ///
+ /// The filter to apply
+ /// True if the region matches, false otherwise
+ public bool MatchesFilter(RegionFilter filter) => filter switch
+ {
+ RegionFilter.None => true,
+ RegionFilter.ZeldaOnly => this is Z3Region,
+ RegionFilter.MetroidOnly => this is SMRegion,
+ _ => throw new InvalidEnumArgumentException(nameof(filter), (int)filter, typeof(RegionFilter)),
+ };
+
+ /*public bool CheckDungeonMedallion(Progression items, IDungeon dungeon)
{
if (!dungeon.NeedsMedallion) return true;
var medallionItem = dungeon.MarkedMedallion;
return (medallionItem != ItemType.Nothing && items.Contains(medallionItem)) ||
(items.Bombos && items.Ether && items.Quake);
- }
-
- public int CountReward(Progression items, RewardType reward)
- {
- return World.Dungeons
- .Where(x => x is IHasReward rewardRegion && x.MarkedReward == reward)
- .Count(x => x.DungeonState.Cleared);
- }
+ }*/
}
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/Brinstar/KraidsLair.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/Brinstar/KraidsLair.cs
index 47f11c347..b6ca005b5 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/Brinstar/KraidsLair.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/Brinstar/KraidsLair.cs
@@ -8,7 +8,7 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.SuperMetroid.Brinstar;
-public class KraidsLair : SMRegion, IHasBoss
+public class KraidsLair : SMRegion, IHasBoss, IHasReward
{
public KraidsLair(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
@@ -16,21 +16,32 @@ public KraidsLair(World world, Config config, IMetadataService? metadata, Tracke
WarehouseKihunter = new WarehouseKihunterRoom(this, metadata, trackerState);
VariaSuit = new VariaSuitRoom(this, metadata, trackerState);
MemoryRegionId = 1;
- Boss = new Boss(Shared.Enums.BossType.Kraid, World, this, metadata, trackerState);
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Kraid's Lair");
MapName = "Brinstar";
+
+ ((IHasReward)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Kraid's Lair";
public override string Area => "Brinstar";
- public override List AlsoKnownAs { get; } = new List()
- {
- "Warehouse"
- };
+ public override List AlsoKnownAs { get; } = ["Warehouse"];
+
+ public Boss Boss { get; set; } = null!;
+
+ public BossType DefaultBossType => BossType.Kraid;
+
+ public LocationId? BossLocationId => LocationId.KraidsLairVariaSuit;
- public Boss Boss{ get; set; }
+ public Reward Reward { get; set; } = null!;
+
+ public RewardType DefaultRewardType => RewardType.KraidToken;
+
+ public TrackerRewardState RewardState { get; set; } = null!;
+
+ public bool IsShuffledReward => false;
public WarehouseEnergyTankRoom WarehouseEnergyTank { get; }
@@ -50,6 +61,10 @@ public bool CanBeatBoss(Progression items)
return CanEnter(items, true) && items.CardBrinstarBoss;
}
+ public bool CanRetrieveReward(Progression items) => CanBeatBoss(items);
+
+ public bool CanSeeReward(Progression items) => true;
+
public class WarehouseEnergyTankRoom : Room
{
public WarehouseEnergyTankRoom(KraidsLair region, IMetadataService? metadata, TrackerState? trackerState)
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/Maridia/InnerMaridia.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/Maridia/InnerMaridia.cs
index c574637a2..27363434c 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/Maridia/InnerMaridia.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/Maridia/InnerMaridia.cs
@@ -9,7 +9,7 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.SuperMetroid.Maridia;
-public class InnerMaridia : SMRegion, IHasBoss
+public class InnerMaridia : SMRegion, IHasBoss, IHasReward
{
public InnerMaridia(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
@@ -24,16 +24,30 @@ public InnerMaridia(World world, Config config, IMetadataService? metadata, Trac
Botwoons = new BotwoonsRoom(this, metadata, trackerState);
SpaceJump = new SpaceJumpRoom(this, metadata, trackerState);
MemoryRegionId = 4;
- Boss = new Boss(Shared.Enums.BossType.Draygon, world, this, metadata, trackerState);
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Inner Maridia");
MapName = "Maridia";
+
+ ((IHasReward)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Inner Maridia";
public override string Area => "Maridia";
- public Boss Boss{ get; set; }
+ public Boss Boss { get; set; } = null!;
+
+ public BossType DefaultBossType => BossType.Draygon;
+
+ public LocationId? BossLocationId => LocationId.InnerMaridiaSpaceJump;
+
+ public Reward Reward { get; set; } = null!;
+
+ public RewardType DefaultRewardType => RewardType.DraygonToken;
+
+ public TrackerRewardState RewardState { get; set; } = null!;
+
+ public bool IsShuffledReward => false;
public WateringHoleRoom WateringHole { get; }
@@ -63,6 +77,10 @@ public override bool CanEnter(Progression items, bool requireRewards)
public bool CanBeatBoss(Progression items)
=> CanEnter(items, true) && CanDefeatDraygon(items, true);
+ public bool CanRetrieveReward(Progression items) => CanBeatBoss(items);
+
+ public bool CanSeeReward(Progression items) => true;
+
private static bool CanReachAqueduct(Progression items, ILogic logic, bool requireRewards)
=> (items.CardMaridiaL1 && CanPassMountDeath(items, logic))
|| (items.CardMaridiaL2 && logic.CanAccessMaridiaPortal(items, requireRewards));
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/Norfair/LowerNorfairEast.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/Norfair/LowerNorfairEast.cs
index 6aa145811..fdc478df4 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/Norfair/LowerNorfairEast.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/Norfair/LowerNorfairEast.cs
@@ -8,7 +8,7 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.SuperMetroid.Norfair;
-public class LowerNorfairEast : SMRegion, IHasBoss
+public class LowerNorfairEast : SMRegion, IHasBoss, IHasReward
{
public LowerNorfairEast(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
@@ -19,16 +19,30 @@ public LowerNorfairEast(World world, Config config, IMetadataService? metadata,
RidleyTank = new RidleyTankRoom(this, metadata, trackerState);
Fireflea = new FirefleaRoom(this, metadata, trackerState);
MemoryRegionId = 2;
- Boss = new Boss(Shared.Enums.BossType.Ridley, world, this, metadata, trackerState);
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Lower Norfair East");
MapName = "Norfair";
+
+ ((IHasReward)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Lower Norfair, East";
public override string Area => "Lower Norfair";
- public Boss Boss { get; set; }
+ public Boss Boss { get; set; } = null!;
+
+ public BossType DefaultBossType => BossType.Ridley;
+
+ public LocationId? BossLocationId => LocationId.LowerNorfairRidleyTank;
+
+ public Reward Reward { get; set; } = null!;
+
+ public RewardType DefaultRewardType => RewardType.RidleyToken;
+
+ public TrackerRewardState RewardState { get; set; } = null!;
+
+ public bool IsShuffledReward => false;
public SpringBallMazeRoom SpringBallMaze { get; }
@@ -58,6 +72,10 @@ public bool CanBeatBoss(Progression items)
return CanEnter(items, true) && CanExit(items) && items.CardLowerNorfairBoss && Logic.CanUsePowerBombs(items) && items.Super;
}
+ public bool CanRetrieveReward(Progression items) => CanBeatBoss(items);
+
+ public bool CanSeeReward(Progression items) => true;
+
private bool CanExit(Progression items)
{
return items.CardNorfairL2 /*Bubble Mountain*/ ||
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/WreckedShip.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/WreckedShip.cs
index 1cb14033e..97458b4b8 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/WreckedShip.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/SuperMetroid/WreckedShip.cs
@@ -8,7 +8,7 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.SuperMetroid;
-public class WreckedShip : SMRegion, IHasBoss
+public class WreckedShip : SMRegion, IHasBoss, IHasReward
{
public WreckedShip(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
@@ -20,17 +20,30 @@ public WreckedShip(World world, Config config, IMetadataService? metadata, Track
EastSuper = new EastSuperRoom(this, metadata, trackerState);
GravitySuit = new GravitySuitRoom(this, metadata, trackerState);
MemoryRegionId = 3;
- Boss = new Boss(Shared.Enums.BossType.Phantoon, world, this, metadata, trackerState);
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Wrecked Ship");
MapName = "Wrecked Ship";
+ ((IHasReward)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Wrecked Ship";
public override string Area => "Wrecked Ship";
- public Boss Boss { get; set; }
+ public Boss Boss { get; set; } = null!;
+
+ public BossType DefaultBossType => BossType.Phantoon;
+
+ public LocationId? BossLocationId => LocationId.WreckedShipWestSuper;
+
+ public Reward Reward { get; set; } = null!;
+
+ public RewardType DefaultRewardType => RewardType.PhantoonToken;
+
+ public TrackerRewardState RewardState { get; set; } = null!;
+
+ public bool IsShuffledReward => false;
public MainShaftRoom MainShaft { get; }
@@ -92,6 +105,10 @@ public bool CanUnlockShip(Progression items)
return items.CardWreckedShipBoss && Logic.CanPassBombPassages(items);
}
+ public bool CanRetrieveReward(Progression items) => CanEnter(items, true) && CanUnlockShip(items);
+
+ public bool CanSeeReward(Progression items) => true;
+
public class MainShaftRoom : Room
{
public MainShaftRoom(WreckedShip region, IMetadataService? metadata, TrackerState? trackerState)
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/CastleTower.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/CastleTower.cs
index a553685f8..e6b05201e 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/CastleTower.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/CastleTower.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
using TrackerCouncil.Smz3.Data.Options;
@@ -8,57 +9,65 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
-public class CastleTower : Z3Region, IHasReward, IDungeon
+public class CastleTower : Z3Region, IHasReward, IHasTreasure, IHasBoss
{
public CastleTower(World world, Config config, IMetadataService? metadata, TrackerState? trackerState)
: base(world, config, metadata, trackerState)
{
- RegionItems = new[] { ItemType.KeyCT };
+ RegionItems = [ItemType.KeyCT];
Foyer = new FoyerRoom(this, metadata, trackerState);
DarkMaze = new DarkMazeRoom(this, metadata, trackerState);
StartingRooms = new List() { 224 };
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Castle Tower");
- DungeonMetadata = metadata?.Dungeon(GetType()) ?? new DungeonInfo("Castle Tower", "Agahnim");
- DungeonState = trackerState?.DungeonStates.First(x => x.WorldId == world.Id && x.Name == GetType().Name) ?? new TrackerDungeonState();
- Reward = new Reward(RewardType.Agahnim, world, this, metadata, DungeonState);
MapName = "Light World";
+
+ ((IHasReward)this).ApplyState(trackerState);
+ ((IHasTreasure)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Castle Tower";
public RewardType DefaultRewardType => RewardType.Agahnim;
- public override List AlsoKnownAs { get; }
- = new List() { "Agahnim's Tower", "Hyrule Castle Tower" };
+ public BossType DefaultBossType => BossType.Agahnim;
+
+ public bool IsShuffledReward => false;
+
+ public override List AlsoKnownAs { get; } = ["Agahnim's Tower", "Hyrule Castle Tower"];
- public int SongIndex { get; init; } = 2;
- public string Abbreviation => "AT";
public LocationId? BossLocationId => null;
- public Reward Reward { get; set; }
+ public Reward Reward { get; set; } = null!;
public FoyerRoom Foyer { get; }
public DarkMazeRoom DarkMaze { get; }
- public DungeonInfo DungeonMetadata { get; set; }
+ public TrackerRewardState RewardState { get; set; } = null!;
- public TrackerDungeonState DungeonState { get; set; }
+ public TrackerTreasureState TreasureState { get; set; } = null!;
+ public event EventHandler? UpdatedTreasure;
+ public void OnUpdatedTreasure() => UpdatedTreasure?.Invoke(this, EventArgs.Empty);
- public Region ParentRegion => World.LightWorldNorthEast;
+ public Boss Boss { get; set; } = null!;
public override bool CanEnter(Progression items, bool requireRewards)
{
return Logic.CanKillManyEnemies(items) && (items.Cape || items.MasterSword);
}
- public bool CanComplete(Progression items)
+ public bool CanBeatBoss(Progression items) => CanRetrieveReward(items);
+
+ public bool CanRetrieveReward(Progression items)
{
return CanEnter(items, true) && items.Lamp && items.KeyCT >= 2 && items.Sword;
}
+ public bool CanSeeReward(Progression items) => true;
+
public class FoyerRoom : Room
{
public FoyerRoom(Region region, IMetadataService? metadata, TrackerState? trackerState)
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/DarkWorld/DarkWorldNorthEast.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/DarkWorld/DarkWorldNorthEast.cs
index c1ae6dea5..58f11b912 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/DarkWorld/DarkWorldNorthEast.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/DarkWorld/DarkWorldNorthEast.cs
@@ -76,7 +76,7 @@ public PyramidFairyChamber(Region region, IMetadataService? metadata, TrackerSta
relevanceRequirement: items => CanAccessPyramidFairy(items, requireRewards: false),
memoryAddress: 0x116,
memoryFlag: 0x4,
- trackerLogic: items => region.CountReward(items, RewardType.CrystalRed) == 2,
+ trackerLogic: items => World.CountReceivedReward(items, RewardType.CrystalRed) == 2,
metadata: metadata,
trackerState: trackerState),
new Location(this, LocationId.PyramidFairyRight, 0x1E983, LocationType.Regular,
@@ -86,7 +86,7 @@ public PyramidFairyChamber(Region region, IMetadataService? metadata, TrackerSta
relevanceRequirement: items => CanAccessPyramidFairy(items, requireRewards: false),
memoryAddress: 0x116,
memoryFlag: 0x5,
- trackerLogic: items => region.CountReward(items, RewardType.CrystalRed) == 2,
+ trackerLogic: items => World.CountReceivedReward(items, RewardType.CrystalRed) == 2,
metadata: metadata,
trackerState: trackerState)
};
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/DesertPalace.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/DesertPalace.cs
index ab7fe6e43..f96e8985d 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/DesertPalace.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/DesertPalace.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
@@ -9,18 +10,11 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
-public class DesertPalace : Z3Region, IHasReward, IDungeon
+public class DesertPalace : Z3Region, IHasReward, IHasTreasure, IHasBoss
{
- public static readonly int[] MusicAddresses = new[] {
- 0x02D59B,
- 0x02D59C,
- 0x02D59D,
- 0x02D59E
- };
-
public DesertPalace(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
- RegionItems = new[] { ItemType.KeyDP, ItemType.BigKeyDP, ItemType.MapDP, ItemType.CompassDP };
+ RegionItems = [ItemType.KeyDP, ItemType.BigKeyDP, ItemType.MapDP, ItemType.CompassDP];
BigChest = new Location(this, LocationId.DesertPalaceBigChest, 0x1E98F, LocationType.Regular,
name: "Big Chest",
@@ -72,7 +66,7 @@ public DesertPalace(World world, Config config, IMetadataService? metadata, Trac
access: items => (
Logic.CanLiftLight(items) ||
(Logic.CanAccessMiseryMirePortal(items) && items.Mirror)
- ) && items.BigKeyDP && items.KeyDP && Logic.CanLightTorches(items) && CanBeatBoss(items),
+ ) && items.BigKeyDP && items.KeyDP && Logic.CanLightTorches(items) && CanDefeatLanmolas(items),
memoryAddress: 0x33,
memoryFlag: 0xB,
metadata: metadata,
@@ -81,32 +75,37 @@ public DesertPalace(World world, Config config, IMetadataService? metadata, Trac
MemoryAddress = 0x33;
MemoryFlag = 0xB;
StartingRooms = new List() { 99, 131, 132, 133 };
- Reward = new Reward(RewardType.None, world, this);
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Desert Palace");
- DungeonMetadata = metadata?.Dungeon(GetType()) ?? new DungeonInfo("Desert Palace", "Lanmolas");
- DungeonState = trackerState?.DungeonStates.First(x => x.WorldId == world.Id && x.Name == GetType().Name) ?? new TrackerDungeonState();
- Reward = new Reward(DungeonState.Reward ?? RewardType.None, world, this, metadata, DungeonState);
MapName = "Light World";
+
+ ((IHasReward)this).ApplyState(trackerState);
+ ((IHasTreasure)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Desert Palace";
public RewardType DefaultRewardType => RewardType.PendantBlue;
- public override List AlsoKnownAs { get; }
- = new List() { "Dessert Palace" };
+ public BossType DefaultBossType => BossType.Lanmolas;
+
+ public bool IsShuffledReward => true;
+
+ public override List AlsoKnownAs { get; } = ["Dessert Palace"];
- public int SongIndex { get; init; } = 1;
- public string Abbreviation => "DP";
public LocationId? BossLocationId => LocationId.DesertPalaceLanmolas;
- public Reward Reward { get; set; }
+ public Reward Reward { get; set; } = null!;
+
+ public TrackerRewardState RewardState { get; set; } = null!;
- public DungeonInfo DungeonMetadata { get; set; }
+ public TrackerTreasureState TreasureState { get; set; } = null!;
- public TrackerDungeonState DungeonState { get; set; }
+ public event EventHandler? UpdatedTreasure;
- public Region ParentRegion => World.LightWorldSouth;
+ public void OnUpdatedTreasure() => UpdatedTreasure?.Invoke(this, EventArgs.Empty);
+
+ public Boss Boss { get; set; } = null!;
public Location BigChest { get; }
@@ -127,12 +126,13 @@ public override bool CanEnter(Progression items, bool requireRewards)
Logic.CanAccessMiseryMirePortal(items) && items.Mirror;
}
- public bool CanComplete(Progression items)
- {
- return LanmolasReward.IsAvailable(items);
- }
+ public bool CanBeatBoss(Progression items) => LanmolasReward.IsAvailable(items);
+
+ public bool CanRetrieveReward(Progression items) => LanmolasReward.IsAvailable(items);
+
+ public bool CanSeeReward(Progression items) => !World.Config.ZeldaKeysanity || items.Contains(ItemType.MapDP);
- private bool CanBeatBoss(Progression items)
+ private bool CanDefeatLanmolas(Progression items)
{
return items.Sword || items.Hammer || items.Bow ||
items.FireRod || items.IceRod ||
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/EasternPalace.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/EasternPalace.cs
index 9e68dad52..b1736a51e 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/EasternPalace.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/EasternPalace.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
@@ -9,15 +10,11 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
-public class EasternPalace : Z3Region, IHasReward, IDungeon
+public class EasternPalace : Z3Region, IHasReward, IHasTreasure, IHasBoss
{
- public static readonly int[] MusicAddresses = new[] {
- 0x02D59A
- };
-
public EasternPalace(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
- RegionItems = new[] { ItemType.BigKeyEP, ItemType.MapEP, ItemType.CompassEP };
+ RegionItems = [ItemType.BigKeyEP, ItemType.MapEP, ItemType.CompassEP];
CannonballChest = new Location(this, LocationId.EasternPalaceCannonballChest, 0x1E9B3, LocationType.Regular,
name: "Cannonball Chest",
@@ -74,29 +71,34 @@ public EasternPalace(World world, Config config, IMetadataService? metadata, Tra
MemoryFlag = 0xB;
StartingRooms = new List { 201 };
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Eastern Palace");
- DungeonMetadata = metadata?.Dungeon(GetType()) ?? new DungeonInfo("Eastern Palace", "Armos Knights");
- DungeonState = trackerState?.DungeonStates.First(x => x.WorldId == world.Id && x.Name == GetType().Name) ?? new TrackerDungeonState();
- Reward = new Reward(DungeonState.Reward ?? RewardType.None, world, this, metadata, DungeonState);
MapName = "Light World";
+
+ ((IHasReward)this).ApplyState(trackerState);
+ ((IHasTreasure)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Eastern Palace";
public RewardType DefaultRewardType => RewardType.PendantGreen;
- public Reward Reward { get; set; }
+ public BossType DefaultBossType => BossType.ArmosKnights;
- public DungeonInfo DungeonMetadata { get; set; }
+ public bool IsShuffledReward => true;
- public TrackerDungeonState DungeonState { get; set; }
+ public Reward Reward { get; set; } = null!;
- public int SongIndex { get; init; } = 0;
+ public TrackerTreasureState TreasureState { get; set; } = null!;
- public string Abbreviation => "EP";
+ public event EventHandler? UpdatedTreasure;
- public LocationId? BossLocationId => LocationId.EasternPalaceArmosKnights;
+ public void OnUpdatedTreasure() => UpdatedTreasure?.Invoke(this, EventArgs.Empty);
- public Region ParentRegion => World.LightWorldNorthEast;
+ public TrackerRewardState RewardState { get; set; } = null!;
+
+ public Boss Boss { get; set; } = null!;
+
+ public LocationId? BossLocationId => LocationId.EasternPalaceArmosKnights;
public Location CannonballChest { get; }
@@ -110,6 +112,11 @@ public EasternPalace(World world, Config config, IMetadataService? metadata, Tra
public Location ArmosKnightsRewards { get; }
- public bool CanComplete(Progression items)
+ public bool CanRetrieveReward(Progression items)
+ => ArmosKnightsRewards.IsAvailable(items);
+
+ public bool CanSeeReward(Progression items) => !World.Config.ZeldaKeysanity || items.Contains(ItemType.MapEP);
+
+ public bool CanBeatBoss(Progression items)
=> ArmosKnightsRewards.IsAvailable(items);
}
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/GanonsTower.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/GanonsTower.cs
index 80b847676..8552bf26a 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/GanonsTower.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/GanonsTower.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
@@ -9,15 +10,11 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
-public class GanonsTower : Z3Region, IDungeon
+public class GanonsTower : Z3Region, IHasTreasure, IHasBoss
{
- public static readonly int[] MusicAddresses = new[] {
- 0x02D5C9
- };
-
public GanonsTower(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
- RegionItems = new[] { ItemType.KeyGT, ItemType.BigKeyGT, ItemType.MapGT, ItemType.CompassGT };
+ RegionItems = [ItemType.KeyGT, ItemType.BigKeyGT, ItemType.MapGT, ItemType.CompassGT];
BobsTorch = new Location(this, LocationId.GanonsTowerBobsTorch, 0x308161, LocationType.Regular,
name: "Bob's Torch",
@@ -116,20 +113,25 @@ public GanonsTower(World world, Config config, IMetadataService? metadata, Track
MiniHelmasaurRoom = new MiniHelmasaurRoomRoom(this, metadata, trackerState);
StartingRooms = new List { 0xC };
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Ganon's Tower");
- DungeonMetadata = metadata?.Dungeon(GetType()) ?? new DungeonInfo("Ganon's Tower", "Ganon");
- DungeonState = trackerState?.DungeonStates.First(x => x.WorldId == world.Id && x.Name == GetType().Name) ?? new TrackerDungeonState();
MapName = "Dark World";
+
+ ((IHasTreasure)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Ganon's Tower";
- public int SongIndex { get; init; } = 11;
- public string Abbreviation => "GT";
public LocationId? BossLocationId => null;
- public DungeonInfo DungeonMetadata { get; set; }
+ public BossType DefaultBossType => BossType.Ganon;
+
+ public TrackerTreasureState TreasureState { get; set; } = null!;
+
+ public event EventHandler? UpdatedTreasure;
- public TrackerDungeonState DungeonState { get; set; }
+ public void OnUpdatedTreasure() => UpdatedTreasure?.Invoke(this, EventArgs.Empty);
+
+ public Boss Boss { get; set; } = null!;
public Location BobsTorch { get; }
@@ -159,8 +161,6 @@ public GanonsTower(World world, Config config, IMetadataService? metadata, Track
public MiniHelmasaurRoomRoom MiniHelmasaurRoom { get; }
- public Region ParentRegion => World.DarkWorldDeathMountainWest;
-
public override bool CanEnter(Progression items, bool requireRewards)
{
var smBosses = new[] { BossType.Kraid, BossType.Phantoon, BossType.Draygon, BossType.Ridley };
@@ -191,6 +191,9 @@ public override bool CanFill(Item item, Progression items)
return base.CanFill(item, items);
}
+ public bool CanBeatBoss(Progression items) => MoldormChest.IsAvailable(items) && items.MasterSword &&
+ items.Contains(ItemType.SilverArrows);
+
private bool TowerAscend(Progression items)
{
return items.BigKeyGT && items.KeyGT >= 3 && items.Bow && Logic.CanLightTorches(items);
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/HyruleCastle.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/HyruleCastle.cs
index ac0e49beb..15dc98748 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/HyruleCastle.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/HyruleCastle.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
@@ -9,13 +10,13 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
-public class HyruleCastle : Z3Region, IDungeon
+public class HyruleCastle : Z3Region, IHasTreasure, IHasBoss
{
public const int SphereOne = -10;
public HyruleCastle(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
- RegionItems = new[] { ItemType.KeyHC, ItemType.MapHC };
+ RegionItems = [ItemType.KeyHC, ItemType.MapHC];
Sanctuary = new Location(this, LocationId.Sanctuary, 0x1EA79, LocationType.Regular,
name: "Sanctuary",
@@ -80,20 +81,27 @@ public HyruleCastle(World world, Config config, IMetadataService? metadata, Trac
StartingRooms = new List { 96, 97, 98 };
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Hyrule Castle");
- DungeonMetadata = metadata?.Dungeon(GetType()) ?? new DungeonInfo("Hyrule Castle", "Ball and Chain Soldier");
- DungeonState = trackerState?.DungeonStates.First(x => x.WorldId == world.Id && x.Name == GetType().Name) ?? new TrackerDungeonState();
MapName = "Light World";
+
+ ((IHasTreasure)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Hyrule Castle";
- public int SongIndex { get; init; } = 0;
- public string Abbreviation => "HC";
+ public BossType DefaultBossType => BossType.CastleGuard;
+
public LocationId? BossLocationId => null;
- public DungeonInfo DungeonMetadata { get; set; }
+ public TrackerTreasureState TreasureState { get; set; } = null!;
+
+ public event EventHandler? UpdatedTreasure;
- public TrackerDungeonState DungeonState { get; set; }
+ public void OnUpdatedTreasure() => UpdatedTreasure?.Invoke(this, EventArgs.Empty);
+
+ public Boss Boss { get; set; } = null!;
+
+ public bool CanBeatBoss(Progression items) => ZeldasCell.IsAvailable(items);
public Location Sanctuary { get; }
@@ -109,8 +117,6 @@ public HyruleCastle(World world, Config config, IMetadataService? metadata, Trac
public BackOfEscapeRoom BackOfEscape { get; }
- public Region ParentRegion => World.LightWorldNorthEast;
-
public class BackOfEscapeRoom : Room
{
public BackOfEscapeRoom(Region region, IMetadataService? metadata, TrackerState? trackerState)
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/IcePalace.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/IcePalace.cs
index 3aa042dfd..be8378536 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/IcePalace.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/IcePalace.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
@@ -9,14 +10,11 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
-public class IcePalace : Z3Region, IHasReward, IDungeon
+public class IcePalace : Z3Region, IHasReward, IHasTreasure, IHasBoss
{
- public static readonly int[] MusicAddresses = new[] {
- 0x02D5BF
- };
public IcePalace(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
- RegionItems = new[] { ItemType.KeyIP, ItemType.BigKeyIP, ItemType.MapIP, ItemType.CompassIP };
+ RegionItems = [ItemType.KeyIP, ItemType.BigKeyIP, ItemType.MapIP, ItemType.CompassIP];
CompassChest = new Location(this, LocationId.IcePalaceCompassChest, 0x1E9D4, LocationType.Regular,
name: "Compass Chest",
@@ -99,27 +97,34 @@ public IcePalace(World world, Config config, IMetadataService? metadata, Tracker
MemoryFlag = 0xB;
StartingRooms = new List { 14 };
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Ice Palace");
- DungeonMetadata = metadata?.Dungeon(GetType()) ?? new DungeonInfo("Ice Palace", "Kholdstare");
- DungeonState = trackerState?.DungeonStates.First(x => x.WorldId == world.Id && x.Name == GetType().Name) ?? new TrackerDungeonState();
- Reward = new Reward(DungeonState.Reward ?? RewardType.None, world, this, metadata, DungeonState);
MapName = "Dark World";
+
+ ((IHasReward)this).ApplyState(trackerState);
+ ((IHasTreasure)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Ice Palace";
public RewardType DefaultRewardType => RewardType.CrystalRed;
- public int SongIndex { get; init; } = 7;
- public string Abbreviation => "IP";
+ public BossType DefaultBossType => BossType.Kholdstare;
+
+ public bool IsShuffledReward => true;
+
public LocationId? BossLocationId => LocationId.IcePalaceKholdstare;
- public Reward Reward { get; set; }
+ public Reward Reward { get; set; } = null!;
+
+ public TrackerTreasureState TreasureState { get; set; } = null!;
+
+ public event EventHandler? UpdatedTreasure;
- public DungeonInfo DungeonMetadata { get; set; }
+ public void OnUpdatedTreasure() => UpdatedTreasure?.Invoke(this, EventArgs.Empty);
- public TrackerDungeonState DungeonState { get; set; }
+ public TrackerRewardState RewardState { get; set; } = null!;
- public Region ParentRegion => World.DarkWorldSouth;
+ public Boss Boss { get; set; } = null!;
public Location CompassChest { get; }
@@ -142,10 +147,11 @@ public override bool CanEnter(Progression items, bool requireRewards)
return items.MoonPearl && items.Flippers && Logic.CanLiftHeavy(items) && Logic.CanMeltFreezors(items);
}
- public bool CanComplete(Progression items)
- {
- return KholdstareReward.IsAvailable(items);
- }
+ public bool CanBeatBoss(Progression items) => KholdstareReward.IsAvailable(items);
+
+ public bool CanRetrieveReward(Progression items) => KholdstareReward.IsAvailable(items);
+
+ public bool CanSeeReward(Progression items) => !World.Config.ZeldaKeysanity || items.Contains(ItemType.MapIP);
private bool CanNotWasteKeysBeforeAccessible(Progression items, params Location[] locations)
{
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/LightWorld/DeathMountain/LightWorldDeathMountainEast.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/LightWorld/DeathMountain/LightWorldDeathMountainEast.cs
index fd42356b1..345ae1e57 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/LightWorld/DeathMountain/LightWorldDeathMountainEast.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/LightWorld/DeathMountain/LightWorldDeathMountainEast.cs
@@ -36,7 +36,7 @@ public LightWorldDeathMountainEast(World world, Config config, IMetadataService?
access: items => items.Mirror && items.KeyTR >= 2 && World.TurtleRock.CanEnter(items, true),
memoryAddress: 0x10C,
memoryFlag: 0x4,
- trackerLogic: items => items.HasMarkedMedallion(World.TurtleRock.DungeonState.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(World.TurtleRock.PrerequisiteState.MarkedItem),
metadata: metadata,
trackerState: trackerState);
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/LightWorld/LightWorldNorthEast.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/LightWorld/LightWorldNorthEast.cs
index 77713d4e5..5ab761198 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/LightWorld/LightWorldNorthEast.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/LightWorld/LightWorldNorthEast.cs
@@ -84,7 +84,7 @@ public SahasrahlasHideoutRoom(Region region, IMetadataService? metadata, Tracker
memoryAddress: 0x190,
memoryFlag: 0x10,
memoryType: LocationMemoryType.ZeldaMisc,
- trackerLogic: items => region.CountReward(items, RewardType.PendantGreen) == 1,
+ trackerLogic: items => World.CountReceivedReward(items, RewardType.PendantGreen) == 1,
metadata: metadata,
trackerState: trackerState)
};
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/MiseryMire.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/MiseryMire.cs
index 7e2c2d43d..191cb7171 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/MiseryMire.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/MiseryMire.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
@@ -9,14 +10,11 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
-public class MiseryMire : Z3Region, IHasReward, INeedsMedallion, IDungeon
+public class MiseryMire : Z3Region, IHasReward, IHasPrerequisite, IHasTreasure, IHasBoss
{
- public static readonly int[] MusicAddresses = new[] {
- 0x02D5B9
- };
public MiseryMire(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
- RegionItems = new[] { ItemType.KeyMM, ItemType.BigKeyMM, ItemType.MapMM, ItemType.CompassMM };
+ RegionItems = [ItemType.KeyMM, ItemType.BigKeyMM, ItemType.MapMM, ItemType.CompassMM];
MainLobby = new Location(this, LocationId.MiseryMireMainLobby, 0x1EA5E, LocationType.Regular,
name: "Main Lobby",
@@ -24,7 +22,7 @@ public MiseryMire(World world, Config config, IMetadataService? metadata, Tracke
access: items => items.BigKeyMM || items.KeyMM >= 1,
memoryAddress: 0xC2,
memoryFlag: 0x4,
- trackerLogic: items => items.HasMarkedMedallion(DungeonState!.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(PrerequisiteState.MarkedItem),
metadata: metadata,
trackerState: trackerState);
@@ -34,7 +32,7 @@ public MiseryMire(World world, Config config, IMetadataService? metadata, Tracke
access: items => items.BigKeyMM || items.KeyMM >= 1,
memoryAddress: 0xC3,
memoryFlag: 0x5,
- trackerLogic: items => items.HasMarkedMedallion(DungeonState!.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(PrerequisiteState.MarkedItem),
metadata: metadata,
trackerState: trackerState);
@@ -43,7 +41,7 @@ public MiseryMire(World world, Config config, IMetadataService? metadata, Tracke
vanillaItem: ItemType.KeyMM,
memoryAddress: 0xA2,
memoryFlag: 0x4,
- trackerLogic: items => items.HasMarkedMedallion(DungeonState!.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(PrerequisiteState.MarkedItem),
metadata: metadata,
trackerState: trackerState);
@@ -52,7 +50,7 @@ public MiseryMire(World world, Config config, IMetadataService? metadata, Tracke
vanillaItem: ItemType.KeyMM,
memoryAddress: 0xB3,
memoryFlag: 0x4,
- trackerLogic: items => items.HasMarkedMedallion(DungeonState!.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(PrerequisiteState.MarkedItem),
metadata: metadata,
trackerState: trackerState);
@@ -64,7 +62,7 @@ public MiseryMire(World world, Config config, IMetadataService? metadata, Tracke
&& items.KeyMM >= (BigKeyChest.ItemIs(ItemType.BigKeyMM, World) ? 2 : 3),
memoryAddress: 0xC1,
memoryFlag: 0x4,
- trackerLogic: items => items.HasMarkedMedallion(DungeonState!.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(PrerequisiteState.MarkedItem),
metadata: metadata,
trackerState: trackerState);
@@ -75,7 +73,7 @@ public MiseryMire(World world, Config config, IMetadataService? metadata, Tracke
&& items.KeyMM >= (CompassChest.ItemIs(ItemType.BigKeyMM, World) ? 2 : 3),
memoryAddress: 0xD1,
memoryFlag: 0x4,
- trackerLogic: items => items.HasMarkedMedallion(DungeonState!.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(PrerequisiteState.MarkedItem),
metadata: metadata,
trackerState: trackerState);
@@ -85,7 +83,7 @@ public MiseryMire(World world, Config config, IMetadataService? metadata, Tracke
access: items => items.BigKeyMM,
memoryAddress: 0xC3,
memoryFlag: 0x4,
- trackerLogic: items => items.HasMarkedMedallion(DungeonState!.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(PrerequisiteState.MarkedItem),
metadata: metadata,
trackerState: trackerState);
@@ -95,41 +93,51 @@ public MiseryMire(World world, Config config, IMetadataService? metadata, Tracke
access: items => items.BigKeyMM && Logic.CanPassSwordOnlyDarkRooms(items) && items.Somaria,
memoryAddress: 0x90,
memoryFlag: 0xB,
- trackerLogic: items => items.HasMarkedMedallion(DungeonState!.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(PrerequisiteState.MarkedItem),
metadata: metadata,
trackerState: trackerState);
MemoryAddress = 0x90;
MemoryFlag = 0xB;
StartingRooms = new List { 152 };
-
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Misery Mire");
- DungeonMetadata = metadata?.Dungeon(GetType()) ?? new DungeonInfo("Misery Mire", "Vitreous");
- DungeonState = trackerState?.DungeonStates.First(x => x.WorldId == world.Id && x.Name == GetType().Name) ?? new TrackerDungeonState();
- Reward = new Reward(DungeonState.Reward ?? RewardType.None, world, this, metadata, DungeonState);
- Medallion = DungeonState.RequiredMedallion ?? ItemType.Nothing;
MapName = "Dark World";
+
+ ((IHasReward)this).ApplyState(trackerState);
+ ((IHasPrerequisite)this).ApplyState(trackerState);
+ ((IHasTreasure)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Misery Mire";
public RewardType DefaultRewardType => RewardType.CrystalRed;
- public ItemType DefaultMedallion => ItemType.Ether;
+ public BossType DefaultBossType => BossType.Vitreous;
+
+ public bool IsShuffledReward => true;
+
+ public ItemType DefaultRequiredItem => ItemType.Ether;
- public int SongIndex { get; init; } = 5;
- public string Abbreviation => "MM";
public LocationId? BossLocationId => LocationId.MiseryMireVitreous;
- public Reward Reward { get; set; }
+ public Reward Reward { get; set; } = null!;
+
+ public TrackerRewardState RewardState { get; set; } = null!;
+
+ public TrackerTreasureState TreasureState { get; set; } = null!;
- public DungeonInfo DungeonMetadata { get; set; }
+ public event EventHandler? UpdatedTreasure;
- public TrackerDungeonState DungeonState { get; set; }
+ public void OnUpdatedTreasure() => UpdatedTreasure?.Invoke(this, EventArgs.Empty);
- public Region ParentRegion => World.DarkWorldMire;
+ public TrackerPrerequisiteState PrerequisiteState { get; set; } = null!;
- public ItemType Medallion { get; set; }
+ public event EventHandler? UpdatedPrerequisite;
+
+ public void OnUpdatedPrerequisite() => UpdatedPrerequisite?.Invoke(this, EventArgs.Empty);
+
+ public Boss Boss { get; set; } = null!;
public Location MainLobby { get; }
@@ -150,12 +158,13 @@ public MiseryMire(World world, Config config, IMetadataService? metadata, Tracke
// Need "CanKillManyEnemies" if implementing swordless
public override bool CanEnter(Progression items, bool requireRewards)
{
- return items.Contains(Medallion) && items.Sword && items.MoonPearl &&
+ return items.Contains(PrerequisiteState.RequiredItem) && items is { Sword: true, MoonPearl: true } &&
(items.Boots || items.Hookshot) && World.DarkWorldMire.CanEnter(items, requireRewards);
}
- public bool CanComplete(Progression items)
- {
- return VitreousReward.IsAvailable(items);
- }
+ public bool CanBeatBoss(Progression items) => VitreousReward.IsAvailable(items);
+
+ public bool CanRetrieveReward(Progression items) => VitreousReward.IsAvailable(items);
+
+ public bool CanSeeReward(Progression items) => !World.Config.ZeldaKeysanity || items.Contains(ItemType.MapMM);
}
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/PalaceOfDarkness.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/PalaceOfDarkness.cs
index d0efef2b3..fc97ee264 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/PalaceOfDarkness.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/PalaceOfDarkness.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
@@ -9,15 +10,11 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
-public class PalaceOfDarkness : Z3Region, IHasReward, IDungeon
+public class PalaceOfDarkness : Z3Region, IHasReward, IHasTreasure, IHasBoss
{
- public static readonly int[] MusicAddresses = new[] {
- 0x02D5B8
- };
-
public PalaceOfDarkness(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
- RegionItems = new[] { ItemType.KeyPD, ItemType.BigKeyPD, ItemType.MapPD, ItemType.CompassPD };
+ RegionItems = [ItemType.KeyPD, ItemType.BigKeyPD, ItemType.MapPD, ItemType.CompassPD];
ShooterRoom = new Location(this, LocationId.PalaceOfDarknessShooterRoom, 0x1EA5B, LocationType.Regular,
name: "Shooter Room",
@@ -120,29 +117,36 @@ public PalaceOfDarkness(World world, Config config, IMetadataService? metadata,
MemoryFlag = 0xB;
StartingRooms = new List { 74 };
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Palace of Darkness");
- DungeonMetadata = metadata?.Dungeon(GetType()) ?? new DungeonInfo("Palace of Darkness", "Helmasaur King");
- DungeonState = trackerState?.DungeonStates.First(x => x.WorldId == world.Id && x.Name == GetType().Name) ?? new TrackerDungeonState();
- Reward = new Reward(DungeonState.Reward ?? RewardType.None, world, this, metadata, DungeonState);
MapName = "Dark World";
+
+ ((IHasReward)this).ApplyState(trackerState);
+ ((IHasTreasure)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Palace of Darkness";
public RewardType DefaultRewardType => RewardType.CrystalBlue;
+ public BossType DefaultBossType => BossType.HelmasaurKing;
+
+ public bool IsShuffledReward => true;
+
public override string Area => "Dark Palace";
- public int SongIndex { get; init; } = 4;
- public string Abbreviation => "PD";
public LocationId? BossLocationId => LocationId.PalaceOfDarknessHelmasaurKing;
- public Reward Reward { get; set; }
+ public Reward Reward { get; set; } = null!;
+
+ public TrackerTreasureState TreasureState { get; set; } = null!;
- public DungeonInfo DungeonMetadata { get; set; }
+ public event EventHandler? UpdatedTreasure;
- public TrackerDungeonState DungeonState { get; set; }
+ public void OnUpdatedTreasure() => UpdatedTreasure?.Invoke(this, EventArgs.Empty);
- public Region ParentRegion => World.DarkWorldNorthEast;
+ public TrackerRewardState RewardState { get; set; } = null!;
+
+ public Boss Boss { get; set; } = null!;
public Location ShooterRoom { get; }
@@ -173,10 +177,11 @@ public override bool CanEnter(Progression items, bool requireRewards)
return items.MoonPearl && World.DarkWorldNorthEast.CanEnter(items, requireRewards);
}
- public bool CanComplete(Progression items)
- {
- return HelmasaurKingReward.IsAvailable(items);
- }
+ public bool CanBeatBoss(Progression items) => HelmasaurKingReward.IsAvailable(items);
+
+ public bool CanRetrieveReward(Progression items) => HelmasaurKingReward.IsAvailable(items);
+
+ public bool CanSeeReward(Progression items) => !World.Config.ZeldaKeysanity || items.Contains(ItemType.MapPD);
public class DarkMazeRoom : Room
{
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/SkullWoods.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/SkullWoods.cs
index 3e3ef9ee8..c873f47ba 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/SkullWoods.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/SkullWoods.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
@@ -9,22 +10,11 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
-public class SkullWoods : Z3Region, IHasReward, IDungeon
+public class SkullWoods : Z3Region, IHasReward, IHasTreasure, IHasBoss
{
- public static readonly int[] MusicAddresses = new[] {
- 0x02D5BA,
- 0x02D5BB,
- 0x02D5BC,
- 0x02D5BD,
- 0x02D608,
- 0x02D609,
- 0x02D60A,
- 0x02D60B
- };
-
public SkullWoods(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
- RegionItems = new[] { ItemType.KeySW, ItemType.BigKeySW, ItemType.MapSW, ItemType.CompassSW };
+ RegionItems = [ItemType.KeySW, ItemType.BigKeySW, ItemType.MapSW, ItemType.CompassSW];
PotPrison = new Location(this, LocationId.SkullWoodsPotPrison, 0x1E9A1, LocationType.Regular,
name: "Pot Prison",
@@ -99,30 +89,36 @@ public SkullWoods(World world, Config config, IMetadataService? metadata, Tracke
MemoryFlag = 0xB;
StartingRooms = new List { 86, 87, 88, 89, 103, 104 };
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Skull Woods");
- DungeonMetadata = metadata?.Dungeon(GetType()) ?? new DungeonInfo("Skull Woods", "Mothula");
- DungeonState = trackerState?.DungeonStates.First(x => x.WorldId == world.Id && x.Name == GetType().Name) ?? new TrackerDungeonState();
- Reward = new Reward(DungeonState.Reward ?? RewardType.None, world, this, metadata, DungeonState);
MapName = "Dark World";
+
+ ((IHasReward)this).ApplyState(trackerState);
+ ((IHasTreasure)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Skull Woods";
public RewardType DefaultRewardType => RewardType.CrystalBlue;
- public override List AlsoKnownAs { get; }
- = new List() { "Skill Woods" };
+ public BossType DefaultBossType => BossType.Mothula;
+
+ public bool IsShuffledReward => true;
+
+ public override List AlsoKnownAs { get; } = ["Skill Woods"];
- public int SongIndex { get; init; } = 6;
- public string Abbreviation => "SW";
public LocationId? BossLocationId => LocationId.SkullWoodsMothula;
- public Reward Reward { get; set; }
+ public Reward Reward { get; set; } = null!;
+
+ public TrackerTreasureState TreasureState { get; set; } = null!;
- public DungeonInfo DungeonMetadata { get; set; }
+ public event EventHandler? UpdatedTreasure;
- public TrackerDungeonState DungeonState { get; set; }
+ public void OnUpdatedTreasure() => UpdatedTreasure?.Invoke(this, EventArgs.Empty);
- public Region ParentRegion => World.DarkWorldNorthWest;
+ public TrackerRewardState RewardState { get; set; } = null!;
+
+ public Boss Boss { get; set; } = null!;
public Location PotPrison { get; }
@@ -145,8 +141,9 @@ public override bool CanEnter(Progression items, bool requireRewards)
return items.MoonPearl && World.DarkWorldNorthWest.CanEnter(items, requireRewards);
}
- public bool CanComplete(Progression items)
- {
- return MothulaReward.IsAvailable(items);
- }
+ public bool CanBeatBoss(Progression items) => MothulaReward.IsAvailable(items);
+
+ public bool CanRetrieveReward(Progression items) => MothulaReward.IsAvailable(items);
+
+ public bool CanSeeReward(Progression items) => !World.Config.ZeldaKeysanity || items.Contains(ItemType.MapSW);
}
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/SwampPalace.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/SwampPalace.cs
index 253abd435..51e1f6437 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/SwampPalace.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/SwampPalace.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
@@ -9,15 +10,11 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
-public class SwampPalace : Z3Region, IHasReward, IDungeon
+public class SwampPalace : Z3Region, IHasReward, IHasTreasure, IHasBoss
{
- public static readonly int[] MusicAddresses = new[] {
- 0x02D5B7
- };
-
public SwampPalace(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
- RegionItems = new[] { ItemType.KeySP, ItemType.BigKeySP, ItemType.MapSP, ItemType.CompassSP };
+ RegionItems = [ItemType.KeySP, ItemType.BigKeySP, ItemType.MapSP, ItemType.CompassSP];
Entrance = new Location(this, LocationId.SwampPalaceEntrance, 0x1EA9D, LocationType.Regular,
name: "Entrance",
@@ -98,27 +95,34 @@ public SwampPalace(World world, Config config, IMetadataService? metadata, Track
MemoryFlag = 0xB;
StartingRooms = new List { 40 };
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Swamp Palace");
- DungeonMetadata = metadata?.Dungeon(GetType()) ?? new DungeonInfo("Swamp Palace", "Arrghus");
- DungeonState = trackerState?.DungeonStates.First(x => x.WorldId == world.Id && x.Name == GetType().Name) ?? new TrackerDungeonState();
- Reward = new Reward(DungeonState.Reward ?? RewardType.None, world, this, metadata, DungeonState);
MapName = "Dark World";
+
+ ((IHasReward)this).ApplyState(trackerState);
+ ((IHasTreasure)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Swamp Palace";
public RewardType DefaultRewardType => RewardType.CrystalBlue;
- public int SongIndex { get; init; } = 3;
- public string Abbreviation => "SP";
+ public BossType DefaultBossType => BossType.Arrghus;
+
+ public bool IsShuffledReward => true;
+
public LocationId? BossLocationId => LocationId.SwampPalaceArrghus;
- public Reward Reward { get; set; }
+ public Reward Reward { get; set; } = null!;
+
+ public TrackerTreasureState TreasureState { get; set; } = null!;
- public DungeonInfo DungeonMetadata { get; set; }
+ public event EventHandler? UpdatedTreasure;
- public TrackerDungeonState DungeonState { get; set; }
+ public void OnUpdatedTreasure() => UpdatedTreasure?.Invoke(this, EventArgs.Empty);
- public Region ParentRegion => World.DarkWorldSouth;
+ public TrackerRewardState RewardState { get; set; } = null!;
+
+ public Boss Boss { get; set; } = null!;
public Location Entrance { get; }
@@ -143,10 +147,11 @@ public override bool CanEnter(Progression items, bool requireRewards)
return items.MoonPearl && items.Mirror && items.Flippers && World.DarkWorldSouth.CanEnter(items, requireRewards);
}
- public bool CanComplete(Progression items)
- {
- return ArrghusReward.IsAvailable(items);
- }
+ public bool CanBeatBoss(Progression items) => ArrghusReward.IsAvailable(items);
+
+ public bool CanRetrieveReward(Progression items) => ArrghusReward.IsAvailable(items);
+
+ public bool CanSeeReward(Progression items) => !World.Config.ZeldaKeysanity || items.Contains(ItemType.MapSP);
public class FloodedRoomRoom : Room
{
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/ThievesTown.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/ThievesTown.cs
index b67726a81..dec6aca80 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/ThievesTown.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/ThievesTown.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
@@ -9,15 +10,11 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
-public class ThievesTown : Z3Region, IHasReward, IDungeon
+public class ThievesTown : Z3Region, IHasReward, IHasTreasure, IHasBoss
{
- public static readonly int[] MusicAddresses = new[] {
- 0x02D5C6
- };
-
public ThievesTown(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
- RegionItems = new[] { ItemType.KeyTT, ItemType.BigKeyTT, ItemType.MapTT, ItemType.CompassTT };
+ RegionItems = [ItemType.KeyTT, ItemType.BigKeyTT, ItemType.MapTT, ItemType.CompassTT];
MapChest = new Location(this, LocationId.ThievesTownMapChest, 0x1EA01, LocationType.Regular,
name: "Map Chest",
@@ -82,7 +79,7 @@ public ThievesTown(World world, Config config, IMetadataService? metadata, Track
BlindReward = new Location(this, LocationId.ThievesTownBlind, 0x308156, LocationType.Regular,
name: "Blind",
vanillaItem: ItemType.HeartContainer,
- access: items => items.BigKeyTT && items.KeyTT && CanBeatBoss(items),
+ access: items => items.BigKeyTT && items.KeyTT && CanBeatBlind(items),
memoryAddress: 0xAC,
memoryFlag: 0xB,
metadata: metadata,
@@ -92,27 +89,34 @@ public ThievesTown(World world, Config config, IMetadataService? metadata, Track
MemoryFlag = 0xB;
StartingRooms = new List { 219 };
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Thieves' Town");
- DungeonMetadata = metadata?.Dungeon(GetType()) ?? new DungeonInfo("Thieves' Town", "Blind");
- DungeonState = trackerState?.DungeonStates.First(x => x.WorldId == world.Id && x.Name == GetType().Name) ?? new TrackerDungeonState();
- Reward = new Reward(DungeonState.Reward ?? RewardType.None, world, this, metadata, DungeonState);
MapName = "Dark World";
+
+ ((IHasReward)this).ApplyState(trackerState);
+ ((IHasTreasure)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Thieves' Town";
public RewardType DefaultRewardType => RewardType.CrystalBlue;
- public int SongIndex { get; init; } = 9;
- public string Abbreviation => "TT";
+ public BossType DefaultBossType => BossType.Blind;
+
+ public bool IsShuffledReward => true;
+
public LocationId? BossLocationId => LocationId.ThievesTownBlind;
- public Reward Reward { get; set; }
+ public Reward Reward { get; set; } = null!;
+
+ public TrackerTreasureState TreasureState { get; set; } = null!;
- public DungeonInfo DungeonMetadata { get; set; }
+ public event EventHandler? UpdatedTreasure;
- public TrackerDungeonState DungeonState { get; set; }
+ public void OnUpdatedTreasure() => UpdatedTreasure?.Invoke(this, EventArgs.Empty);
- public Region ParentRegion => World.DarkWorldNorthWest;
+ public TrackerRewardState RewardState { get; set; } = null!;
+
+ public Boss Boss { get; set; } = null!;
public Location MapChest { get; }
@@ -135,12 +139,13 @@ public override bool CanEnter(Progression items, bool requireRewards)
return items.MoonPearl && World.DarkWorldNorthWest.CanEnter(items, requireRewards);
}
- public bool CanComplete(Progression items)
- {
- return BlindReward.IsAvailable(items);
- }
+ public bool CanBeatBoss(Progression items) =>BlindReward.IsAvailable(items);
+
+ public bool CanRetrieveReward(Progression items) =>BlindReward.IsAvailable(items);
+
+ public bool CanSeeReward(Progression items) => !World.Config.ZeldaKeysanity || items.Contains(ItemType.MapTT);
- private bool CanBeatBoss(Progression items)
+ private bool CanBeatBlind(Progression items)
{
return items.Sword || items.Hammer ||
items.Somaria || items.Byrna;
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/TowerOfHera.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/TowerOfHera.cs
index ecaf65d8c..19c558b3e 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/TowerOfHera.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/TowerOfHera.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
@@ -9,17 +10,11 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
-public class TowerOfHera : Z3Region, IHasReward, IDungeon
+public class TowerOfHera : Z3Region, IHasReward, IHasTreasure, IHasBoss
{
- public static readonly int[] MusicAddresses = new[] {
- 0x02D5C5,
- 0x02907A,
- 0x028B8C
- };
-
public TowerOfHera(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
- RegionItems = new[] { ItemType.KeyTH, ItemType.BigKeyTH, ItemType.MapTH, ItemType.CompassTH };
+ RegionItems = [ItemType.KeyTH, ItemType.BigKeyTH, ItemType.MapTH, ItemType.CompassTH];
BasementCage = new Location(this, LocationId.TowerOfHeraBasementCage, 0x308162, LocationType.HeraStandingKey,
name: "Basement Cage",
@@ -68,7 +63,7 @@ public TowerOfHera(World world, Config config, IMetadataService? metadata, Track
MoldormReward = new Location(this, LocationId.TowerOfHeraMoldorm, 0x308152, LocationType.Regular,
name: "Moldorm",
vanillaItem: ItemType.HeartContainer,
- access: items => items.BigKeyTH && CanBeatBoss(items),
+ access: items => items.BigKeyTH && CanBeatMoldorm(items),
memoryAddress: 0x7,
memoryFlag: 0xB,
metadata: metadata,
@@ -78,27 +73,34 @@ public TowerOfHera(World world, Config config, IMetadataService? metadata, Track
MemoryFlag = 0xB;
StartingRooms = new List { 119 };
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Tower of Hera");
- DungeonMetadata = metadata?.Dungeon(GetType()) ?? new DungeonInfo("Tower of Hera", "Moldorm");
- DungeonState = trackerState?.DungeonStates.First(x => x.WorldId == world.Id && x.Name == GetType().Name) ?? new TrackerDungeonState();
- Reward = new Reward(DungeonState.Reward ?? RewardType.None, world, this, metadata, DungeonState);
MapName = "Light World";
+
+ ((IHasReward)this).ApplyState(trackerState);
+ ((IHasTreasure)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Tower of Hera";
public RewardType DefaultRewardType => RewardType.PendantRed;
- public int SongIndex { get; init; } = 8;
- public string Abbreviation => "TH";
+ public BossType DefaultBossType => BossType.Moldorm;
+
+ public bool IsShuffledReward => true;
+
public LocationId? BossLocationId => LocationId.TowerOfHeraMoldorm;
- public Reward Reward { get; set; }
+ public Reward Reward { get; set; } = null!;
+
+ public TrackerTreasureState TreasureState { get; set; } = null!;
- public DungeonInfo DungeonMetadata { get; set; }
+ public event EventHandler? UpdatedTreasure;
- public TrackerDungeonState DungeonState { get; set; }
+ public void OnUpdatedTreasure() => UpdatedTreasure?.Invoke(this, EventArgs.Empty);
- public Region ParentRegion => World.LightWorldNorthWest;
+ public TrackerRewardState RewardState { get; set; } = null!;
+
+ public Boss Boss { get; set; } = null!;
public Location BasementCage { get; }
@@ -118,12 +120,13 @@ public override bool CanEnter(Progression items, bool requireRewards)
&& World.LightWorldDeathMountainWest.CanEnter(items, requireRewards);
}
- public bool CanComplete(Progression items)
- {
- return MoldormReward.IsAvailable(items);
- }
+ public bool CanBeatBoss(Progression items) => MoldormReward.IsAvailable(items);
+
+ public bool CanRetrieveReward(Progression items) => MoldormReward.IsAvailable(items);
+
+ public bool CanSeeReward(Progression items) => !World.Config.ZeldaKeysanity || items.Contains(ItemType.MapTH);
- private bool CanBeatBoss(Progression items)
+ private bool CanBeatMoldorm(Progression items)
{
return items.Sword || items.Hammer;
}
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/TurtleRock.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/TurtleRock.cs
index 1d1fdcd00..0c21132e7 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/TurtleRock.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Regions/Zelda/TurtleRock.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
@@ -9,24 +10,18 @@
namespace TrackerCouncil.Smz3.Data.WorldData.Regions.Zelda;
-public class TurtleRock : Z3Region, IHasReward, INeedsMedallion, IDungeon
+public class TurtleRock : Z3Region, IHasReward, IHasPrerequisite, IHasTreasure, IHasBoss
{
- public static readonly int[] MusicAddresses = new[] {
- 0x02D5C7,
- 0x02D5A7,
- 0x02D5AA,
- 0x02D5AB
- };
public TurtleRock(World world, Config config, IMetadataService? metadata, TrackerState? trackerState) : base(world, config, metadata, trackerState)
{
- RegionItems = new[] { ItemType.KeyTR, ItemType.BigKeyTR, ItemType.MapTR, ItemType.CompassTR };
+ RegionItems = [ItemType.KeyTR, ItemType.BigKeyTR, ItemType.MapTR, ItemType.CompassTR];
CompassChest = new Location(this, LocationId.TurtleRockCompassChest, 0x1EA22, LocationType.Regular,
name: "Compass Chest",
vanillaItem: ItemType.CompassTR,
memoryAddress: 0xD6,
memoryFlag: 0x4,
- trackerLogic: items => items.HasMarkedMedallion(DungeonState!.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(PrerequisiteState?.MarkedItem),
metadata: metadata,
trackerState: trackerState);
@@ -36,7 +31,7 @@ public TurtleRock(World world, Config config, IMetadataService? metadata, Tracke
access: items => items.KeyTR >= 1,
memoryAddress: 0xB6,
memoryFlag: 0x4,
- trackerLogic: items => items.HasMarkedMedallion(DungeonState!.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(PrerequisiteState?.MarkedItem),
metadata: metadata,
trackerState: trackerState);
@@ -48,7 +43,7 @@ public TurtleRock(World world, Config config, IMetadataService? metadata, Tracke
BigKeyChest.ItemIs(ItemType.KeyTR, World) ? 3 : 4),
memoryAddress: 0x14,
memoryFlag: 0x4,
- trackerLogic: items => items.HasMarkedMedallion(DungeonState!.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(PrerequisiteState?.MarkedItem),
metadata: metadata,
trackerState: trackerState)
.AlwaysAllow((item, items) => item.Is(ItemType.KeyTR, World) && items.KeyTR >= 3);
@@ -59,7 +54,7 @@ public TurtleRock(World world, Config config, IMetadataService? metadata, Tracke
access: items => items.BigKeyTR && items.KeyTR >= 2,
memoryAddress: 0x24,
memoryFlag: 0x4,
- trackerLogic: items => items.HasMarkedMedallion(DungeonState!.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(PrerequisiteState?.MarkedItem),
metadata: metadata,
trackerState: trackerState)
.Allow((item, items) => item.IsNot(ItemType.BigKeyTR, World));
@@ -70,17 +65,17 @@ public TurtleRock(World world, Config config, IMetadataService? metadata, Tracke
access: items => items.BigKeyTR && items.KeyTR >= 2,
memoryAddress: 0x4,
memoryFlag: 0x4,
- trackerLogic: items => items.HasMarkedMedallion(DungeonState!.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(PrerequisiteState?.MarkedItem),
metadata: metadata,
trackerState: trackerState);
TrinexxReward = new Location(this, LocationId.TurtleRockTrinexx, 0x308159, LocationType.Regular,
name: "Trinexx",
vanillaItem: ItemType.HeartContainer,
- access: items => items.BigKeyTR && items.KeyTR >= 4 && Logic.CanPassSwordOnlyDarkRooms(items) && CanBeatBoss(items),
+ access: items => items.BigKeyTR && items.KeyTR >= 4 && Logic.CanPassSwordOnlyDarkRooms(items) && CanBeatTrinexx(items),
memoryAddress: 0xA4,
memoryFlag: 0xB,
- trackerLogic: items => items.HasMarkedMedallion(DungeonState!.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(PrerequisiteState?.MarkedItem),
metadata: metadata,
trackerState: trackerState);
@@ -91,32 +86,43 @@ public TurtleRock(World world, Config config, IMetadataService? metadata, Tracke
MemoryFlag = 0xB;
StartingRooms = new List { 35, 36, 213, 214 };
Metadata = metadata?.Region(GetType()) ?? new RegionInfo("Turtle Rock");
- DungeonMetadata = metadata?.Dungeon(GetType()) ?? new DungeonInfo("Turtle Rock", "Trinexx");
- DungeonState = trackerState?.DungeonStates.First(x => x.WorldId == world.Id && x.Name == GetType().Name) ?? new TrackerDungeonState();
- Reward = new Reward(DungeonState.Reward ?? RewardType.None, world, this, metadata, DungeonState);
- Medallion = DungeonState.RequiredMedallion ?? ItemType.Nothing;
MapName = "Dark World";
+
+ ((IHasReward)this).ApplyState(trackerState);
+ ((IHasPrerequisite)this).ApplyState(trackerState);
+ ((IHasTreasure)this).ApplyState(trackerState);
+ ((IHasBoss)this).ApplyState(trackerState);
}
public override string Name => "Turtle Rock";
public RewardType DefaultRewardType => RewardType.CrystalBlue;
- public ItemType DefaultMedallion => ItemType.Quake;
+ public bool IsShuffledReward => true;
+
+ public ItemType DefaultRequiredItem => ItemType.Quake;
+
+ public BossType DefaultBossType => BossType.Trinexx;
- public int SongIndex { get; init; } = 10;
- public string Abbreviation => "TR";
public LocationId? BossLocationId => LocationId.TurtleRockTrinexx;
- public Reward Reward { get; set; }
+ public Reward Reward { get; set; } = null!;
+
+ public TrackerTreasureState TreasureState { get; set; } = null!;
+
+ public event EventHandler? UpdatedTreasure;
+
+ public void OnUpdatedTreasure() => UpdatedTreasure?.Invoke(this, EventArgs.Empty);
- public DungeonInfo DungeonMetadata { get; set; }
+ public TrackerRewardState RewardState { get; set; } = null!;
- public TrackerDungeonState DungeonState { get; set; }
+ public TrackerPrerequisiteState PrerequisiteState { get; set; } = null!;
- public Region ParentRegion => World.DarkWorldDeathMountainEast;
+ public event EventHandler? UpdatedPrerequisite;
- public ItemType Medallion { get; set; }
+ public void OnUpdatedPrerequisite() => UpdatedPrerequisite?.Invoke(this, EventArgs.Empty);
+
+ public Boss Boss { get; set; } = null!;
public Location CompassChest { get; }
@@ -136,19 +142,20 @@ public TurtleRock(World world, Config config, IMetadataService? metadata, Tracke
public override bool CanEnter(Progression items, bool requireRewards)
{
- return items.Contains(Medallion) && items.Sword && items.MoonPearl &&
+ return items.Contains(PrerequisiteState.RequiredItem) && items.Sword && items.MoonPearl &&
Logic.CanLiftHeavy(items) && items.Hammer && items.Somaria &&
World.LightWorldDeathMountainEast.CanEnter(items, requireRewards);
}
- public bool CanComplete(Progression items)
- {
- return TrinexxReward.IsAvailable(items);
- }
+ public bool CanBeatBoss(Progression items) => TrinexxReward.IsAvailable(items);
+
+ public bool CanRetrieveReward(Progression items) => TrinexxReward.IsAvailable(items);
+
+ public bool CanSeeReward(Progression items) => !World.Config.ZeldaKeysanity || items.Contains(ItemType.MapTR);
- private bool CanBeatBoss(Progression items)
+ private bool CanBeatTrinexx(Progression items)
{
- return items.FireRod && items.IceRod;
+ return items is { FireRod: true, IceRod: true };
}
public class RollerRoomRoom : Room
@@ -164,7 +171,7 @@ public RollerRoomRoom(Region region, IMetadataService? metadata, TrackerState? t
access: items => items.FireRod,
memoryAddress: 0xB7,
memoryFlag: 0x4,
- trackerLogic: items => items.HasMarkedMedallion(World.TurtleRock.DungeonState.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(World.TurtleRock.PrerequisiteState.MarkedItem),
metadata: metadata,
trackerState: trackerState),
new Location(this, LocationId.TurtleRockRollerRoomRight, 0x1EA1F, LocationType.Regular,
@@ -173,7 +180,7 @@ public RollerRoomRoom(Region region, IMetadataService? metadata, TrackerState? t
access: items => items.FireRod,
memoryAddress: 0xB7,
memoryFlag: 0x5,
- trackerLogic: items => items.HasMarkedMedallion(World.TurtleRock.DungeonState.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(World.TurtleRock.PrerequisiteState.MarkedItem),
metadata: metadata,
trackerState: trackerState)
};
@@ -193,7 +200,7 @@ public LaserBridgeRoom(Region region, IMetadataService? metadata, TrackerState?
access: CanAccess,
memoryAddress: 0xD5,
memoryFlag: 0x4,
- trackerLogic: items => items.HasMarkedMedallion(World.TurtleRock.DungeonState.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(World.TurtleRock.PrerequisiteState.MarkedItem),
metadata: metadata,
trackerState: trackerState),
new Location(this, LocationId.TurtleRockEyeBridgeTopLeft, 0x1EA2B, LocationType.Regular,
@@ -202,7 +209,7 @@ public LaserBridgeRoom(Region region, IMetadataService? metadata, TrackerState?
access: CanAccess,
memoryAddress: 0xD5,
memoryFlag: 0x5,
- trackerLogic: items => items.HasMarkedMedallion(World.TurtleRock.DungeonState.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(World.TurtleRock.PrerequisiteState.MarkedItem),
metadata: metadata,
trackerState: trackerState),
new Location(this, LocationId.TurtleRockEyeBridgeBottomRight, 0x1EA2E, LocationType.Regular,
@@ -211,7 +218,7 @@ public LaserBridgeRoom(Region region, IMetadataService? metadata, TrackerState?
access: CanAccess,
memoryAddress: 0xD5,
memoryFlag: 0x6,
- trackerLogic: items => items.HasMarkedMedallion(World.TurtleRock.DungeonState.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(World.TurtleRock.PrerequisiteState.MarkedItem),
metadata: metadata,
trackerState: trackerState),
new Location(this, LocationId.TurtleRockEyeBridgeBottomLeft, 0x1EA31, LocationType.Regular,
@@ -220,7 +227,7 @@ public LaserBridgeRoom(Region region, IMetadataService? metadata, TrackerState?
access: CanAccess,
memoryAddress: 0xD5,
memoryFlag: 0x7,
- trackerLogic: items => items.HasMarkedMedallion(World.TurtleRock.DungeonState.MarkedMedallion),
+ trackerLogic: items => items.HasMarkedMedallion(World.TurtleRock.PrerequisiteState.MarkedItem),
metadata: metadata,
trackerState: trackerState)
};
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Reward.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Reward.cs
index 6b0d0f4e6..11278af78 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/Reward.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/Reward.cs
@@ -1,4 +1,5 @@
-using System.Collections.Generic;
+using System;
+using System.Collections.Generic;
using System.Linq;
using TrackerCouncil.Smz3.Shared;
using TrackerCouncil.Smz3.Data.Configuration.ConfigTypes;
@@ -14,49 +15,84 @@ namespace TrackerCouncil.Smz3.Data.WorldData;
///
public class Reward
{
- public Reward(RewardType type, World world, IHasReward region, IMetadataService? metadata = null, TrackerDungeonState? dungeonState = null)
+ private Accessibility _accessibility;
+
+ public Reward(RewardType type, World world, IMetadataService? metadata)
{
Type = type;
World = world;
- Region = region;
Metadata = metadata?.Reward(type) ?? new RewardInfo(type);
- State = dungeonState ?? new TrackerDungeonState();
}
- public RewardType Type { get; set; }
+ public RewardType Type { get; }
+
+ public World World { get; }
- public World World { get; set; }
+ public RewardInfo Metadata { get; }
- public IHasReward Region { get; set; }
+ public IHasReward? Region { get; set; }
- public RewardInfo Metadata { get; set; }
+ public TrackerRewardState State => Region?.RewardState ?? new TrackerRewardState();
- public TrackerDungeonState State { get; set; }
+ public bool HasReceivedReward
+ {
+ get => State.HasReceivedReward;
+ set
+ {
+ State.HasReceivedReward = value;
+ UpdatedRewardState?.Invoke(this, EventArgs.Empty);
+ }
+ }
- public static ICollection CreatePool(World world)
+ public RewardType? MarkedReward
{
- var regions = world.Regions.OfType().ToList();
-
- return new List()
- {
- CreatePlayerReward(RewardType.PendantGreen, world, regions),
- CreatePlayerReward(RewardType.PendantRed, world, regions),
- CreatePlayerReward(RewardType.PendantBlue, world, regions),
- CreatePlayerReward(RewardType.CrystalBlue, world, regions),
- CreatePlayerReward(RewardType.CrystalBlue, world, regions),
- CreatePlayerReward(RewardType.CrystalBlue, world, regions),
- CreatePlayerReward(RewardType.CrystalBlue, world, regions),
- CreatePlayerReward(RewardType.CrystalBlue, world, regions),
- CreatePlayerReward(RewardType.CrystalRed, world, regions),
- CreatePlayerReward(RewardType.CrystalRed, world, regions),
- CreatePlayerReward(RewardType.Agahnim, world, regions),
- };
+ get => State.MarkedReward;
+ set
+ {
+ State.MarkedReward = value;
+ UpdatedRewardState?.Invoke(this, EventArgs.Empty);
+ }
}
- private static Reward CreatePlayerReward(RewardType reward, World world, ICollection regions)
+ public Accessibility Accessibility
{
- var region = regions.First(x => x.RewardType == reward);
- regions.Remove(region);
- return new(reward, world, region);
+ get => _accessibility;
+ set
+ {
+ if (_accessibility == value) return;
+ _accessibility = value;
+ UpdatedAccessibility?.Invoke(this, EventArgs.Empty);
+ }
}
+
+ public void UpdateAccessibility(Progression actualProgression, Progression withKeysProgression)
+ {
+ if (HasReceivedReward)
+ {
+ Accessibility = Accessibility.Cleared;
+ }
+ else if (Region == null)
+ {
+ Accessibility = Accessibility.Unknown;
+ }
+ else if (Region.CanRetrieveReward(actualProgression))
+ {
+ Accessibility = Accessibility.Available;
+ }
+ else if (Region.CanRetrieveReward(withKeysProgression))
+ {
+ Accessibility = Accessibility.AvailableWithKeys;
+ }
+ else
+ {
+ Accessibility = Accessibility.OutOfLogic;
+ }
+ }
+
+ public bool HasCorrectlyMarkedReward => MarkedReward == Type;
+
+ public event EventHandler? UpdatedRewardState;
+
+ public event EventHandler? UpdatedAccessibility;
+
}
diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/World.cs b/src/TrackerCouncil.Smz3.Data/WorldData/World.cs
index 754710647..539480be5 100644
--- a/src/TrackerCouncil.Smz3.Data/WorldData/World.cs
+++ b/src/TrackerCouncil.Smz3.Data/WorldData/World.cs
@@ -34,6 +34,9 @@ public World(Config config, string player, int id, string guid, bool isLocalWorl
Logic = new Smz3.Data.Logic.Logic(this);
+ CreateBosses(metadata, trackerState);
+ CreateRewards(metadata);
+
CastleTower = new(this, Config, metadata, trackerState);
EasternPalace = new(this, Config, metadata, trackerState);
DesertPalace = new(this, Config, metadata, trackerState);
@@ -90,9 +93,14 @@ public World(Config config, string player, int id, string guid, bool isLocalWorl
WreckedShip
};
Locations = Regions.SelectMany(x => x.Locations).ToImmutableList();
+ LocationMap = Locations.ToImmutableDictionary(x => x.Id, x => x);
Rooms = Regions.SelectMany(x => x.Rooms).ToImmutableList();
State = trackerState ?? new TrackerState();
ItemPools = new WorldItemPools(this);
+ TreasureRegions = Regions.OfType().ToImmutableList();
+ RewardRegions = Regions.OfType().ToImmutableList();
+ BossRegions = Regions.OfType().ToImmutableList();
+ PrerequisiteRegions = Regions.OfType().ToImmutableList();
}
public Config Config { get; }
@@ -101,18 +109,25 @@ public World(Config config, string player, int id, string guid, bool isLocalWorl
public int Id { get; }
public bool HasCompleted { get; set; }
public bool IsLocalWorld { get; set; }
+ public IEnumerable RewardRegions { get; set; }
+ public IEnumerable TreasureRegions { get; set; }
+ public IEnumerable BossRegions { get; set; }
+ public IEnumerable PrerequisiteRegions { get; set; }
public IEnumerable Regions { get; }
public IEnumerable Rooms { get; }
public IEnumerable Locations { get; }
+ public IDictionary LocationMap { get; }
+ public List Rewards = [];
+ public List Bosses = [];
public IEnumerable
- LocationItems => Locations.Select(l => l.Item);
- public List
- TrackerItems { get; } = new List
- ();
- public IEnumerable
- AllItems => TrackerItems.Concat(LocationItems);
+ public List
- CustomItems { get; } = [];
+ public IEnumerable
- AllItems => CustomItems.Concat(LocationItems);
public ILogic Logic { get; }
- public IEnumerable Rewards => Regions.OfType().Select(x => x.Reward);
- public List TrackerBosses { get; } = new List();
- public IEnumerable GoldenBosses => Regions.OfType().Select(x => x.Boss);
- public IEnumerable AllBosses => GoldenBosses.Concat(TrackerBosses);
- public IEnumerable Dungeons => Regions.OfType();
+ public List CustomBosses { get; } = [];
+
+ public IEnumerable GoldenBosses => Bosses.Where(x =>
+ x.Type is BossType.Kraid or BossType.Phantoon or BossType.Draygon or BossType.Ridley);
+ public IEnumerable AllBosses => Bosses.Concat(CustomBosses);
public CastleTower CastleTower { get; }
public EasternPalace EasternPalace { get; }
public DesertPalace DesertPalace { get; }
@@ -154,7 +169,7 @@ public World(Config config, string player, int id, string guid, bool isLocalWorl
public LowerNorfairEast LowerNorfairEast { get; }
public WreckedShip WreckedShip { get; }
public WorldItemPools ItemPools { get; }
- public IEnumerable HintTiles { get; set; } = new List();
+ public IEnumerable HintTiles { get; set; } = [];
public IEnumerable ActiveHintTileLocations => HintTiles
.Where(x => x.State?.HintState == HintState.Viewed && x.Locations?.Any() == true && x.WorldId == Id)
@@ -168,6 +183,35 @@ public World(Config config, string player, int id, string guid, bool isLocalWorl
?? Locations.FirstOrDefault(x => x.ToString().Equals(name, comparisonType));
}
+ public void CreateRewards(IMetadataService? metadata)
+ {
+ RewardType[] rewardTypes = [ RewardType.PendantGreen, RewardType.PendantRed, RewardType.PendantBlue, RewardType.CrystalRed, RewardType.CrystalRed,
+ RewardType.CrystalBlue, RewardType.CrystalBlue, RewardType.CrystalBlue, RewardType.CrystalBlue, RewardType.CrystalBlue, RewardType.Agahnim,
+ RewardType.KraidToken, RewardType.PhantoonToken, RewardType.DraygonToken, RewardType.RidleyToken ];
+
+ foreach (var rewardType in rewardTypes)
+ {
+ Rewards.Add(new Reward(rewardType, this, metadata));
+ }
+ }
+
+ public void CreateBosses(IMetadataService? metadata, TrackerState? trackerState)
+ {
+ BossType[] bossTypes =
+ [
+ BossType.Kraid, BossType.Phantoon, BossType.Draygon, BossType.Ridley, BossType.MotherBrain,
+ BossType.CastleGuard, BossType.ArmosKnights, BossType.Lanmolas, BossType.Moldorm, BossType.HelmasaurKing,
+ BossType.Arrghus, BossType.Blind, BossType.Mothula, BossType.Kholdstare, BossType.Vitreous,
+ BossType.Trinexx, BossType.Agahnim, BossType.Ganon
+ ];
+
+ foreach (var bossType in bossTypes)
+ {
+ var state = trackerState?.BossStates.FirstOrDefault(x => x.Type == bossType && x.WorldId == Id);
+ Bosses.Add(new Boss(bossType, this, metadata, state));
+ }
+ }
+
///
/// Returns the Location object matching the given ID.
/// We can be confident this won't throw an exception because we have a
@@ -181,22 +225,32 @@ public Location FindLocation(LocationId id)
public bool CanAquire(Progression items, RewardType reward)
{
var dungeonWithReward = Regions.OfType().FirstOrDefault(x => reward == x.RewardType);
- return dungeonWithReward != null && dungeonWithReward.CanComplete(items);
+ return dungeonWithReward != null && dungeonWithReward.CanRetrieveReward(items);
}
public bool CanAquireAll(Progression items, params RewardType[] rewards)
{
- return Regions.OfType().Where(x => rewards.Contains(x.RewardType)).All(x => x.CanComplete(items));
+ return Regions.OfType().Where(x => rewards.Contains(x.RewardType)).All(x => x.CanRetrieveReward(items));
}
public bool CanDefeatAll(Progression items, params BossType[] bosses)
{
- return Regions.OfType().Where(x => bosses.Contains(x.BossType)).All(x => x.CanBeatBoss(items));
+ return BossRegions.Where(x => bosses.Contains(x.BossType)).All(x => x.CanBeatBoss(items));
}
public int CanDefeatBossCount(Progression items, params BossType[] bosses)
{
- return Regions.OfType().Where(x => bosses.Contains(x.BossType)).Count(x => x.CanBeatBoss(items));
+ return BossRegions.Where(x => bosses.Contains(x.BossType)).Count(x => x.CanBeatBoss(items));
+ }
+
+ public bool HasDefeated(params BossType[] bosses)
+ {
+ return Bosses.Where(x => bosses.Contains(x.Type)).All(x => x.Defeated);
+ }
+
+ public Boss GetBossOfType(BossType type)
+ {
+ return Bosses.First(x => x.Type == type);
}
public void Setup(Random rnd)
@@ -210,9 +264,9 @@ public void Setup(Random rnd)
private void SetMedallions(Random rnd)
{
- foreach (var region in Regions.OfType())
+ foreach (var region in Regions.OfType())
{
- region.Medallion = rnd.Next(3) switch
+ region.RequiredItem = rnd.Next(3) switch
{
0 => ItemType.Bombos,
1 => ItemType.Ether,
@@ -223,13 +277,11 @@ private void SetMedallions(Random rnd)
private void SetRewards(Random rnd)
{
- var rewards = new[] {
- RewardType.PendantGreen, RewardType.PendantRed, RewardType.PendantBlue, RewardType.CrystalRed, RewardType.CrystalRed,
- RewardType.CrystalBlue, RewardType.CrystalBlue, RewardType.CrystalBlue, RewardType.CrystalBlue, RewardType.CrystalBlue }.Shuffle(rnd);
- foreach (var region in Regions.OfType().Where(x => x.RewardType == RewardType.None))
+ var rewards = Rewards.Where(x => x.Type.IsInCategory(RewardCategory.Zelda) && !x.Type.IsInCategory(RewardCategory.NonRandomized)).Shuffle(rnd);
+ foreach (var region in RewardRegions.Where(x => x.IsShuffledReward))
{
- region.Reward = new Reward(rewards.First(), this, region);
- rewards.Remove(region.RewardType);
+ region.SetReward(rewards.First());
+ rewards.Remove(region.Reward);
}
}
@@ -252,4 +304,16 @@ private void SetBottles(Random rnd)
bottleItem.UpdateItemType(newType);
}
}
+
+ public int CountReceivedReward(Progression items, RewardType reward)
+ {
+ return CountReceivedRewards(items, [reward]);
+ }
+
+ public int CountReceivedRewards(Progression items, IList rewards)
+ {
+ return RewardRegions
+ .Where(x => rewards.Contains(x.MarkedReward))
+ .Count(x => x.HasReceivedReward);
+ }
}
diff --git a/src/TrackerCouncil.Smz3.Multiplayer.Client/EventHandlers/PlayerSyncReceivedEventHandler.cs b/src/TrackerCouncil.Smz3.Multiplayer.Client/EventHandlers/PlayerSyncReceivedEventHandler.cs
index 3494de5dd..37feac7ce 100644
--- a/src/TrackerCouncil.Smz3.Multiplayer.Client/EventHandlers/PlayerSyncReceivedEventHandler.cs
+++ b/src/TrackerCouncil.Smz3.Multiplayer.Client/EventHandlers/PlayerSyncReceivedEventHandler.cs
@@ -14,7 +14,6 @@ public class PlayerSyncReceivedEventHandlerArgs
public ICollection UpdatedLocationStates { get; init; } = null!;
public ICollection<(TrackerItemState State, int TrackingValue)> UpdatedItemStates { get; init; } = null!;
public ICollection UpdatedBossStates { get; init; } = null!;
- public ICollection UpdatedDungeonStates { get; init; } = null!;
public ICollection? ItemsToGive { get; init; }
public bool IsLocalPlayer { get; init; }
public bool DidForfeit { get; init; }
diff --git a/src/TrackerCouncil.Smz3.Multiplayer.Client/EventHandlers/PlayerTrackedDungeonEventHandler.cs b/src/TrackerCouncil.Smz3.Multiplayer.Client/EventHandlers/PlayerTrackedDungeonEventHandler.cs
deleted file mode 100644
index b0bb5ab69..000000000
--- a/src/TrackerCouncil.Smz3.Multiplayer.Client/EventHandlers/PlayerTrackedDungeonEventHandler.cs
+++ /dev/null
@@ -1,14 +0,0 @@
-using TrackerCouncil.Smz3.Shared.Models;
-
-namespace TrackerCouncil.Smz3.Multiplayer.Client.EventHandlers;
-
-public delegate void PlayerTrackedDungeonEventHandler(PlayerTrackedDungeonEventHandlerArgs args);
-
-public class PlayerTrackedDungeonEventHandlerArgs
-{
- public int PlayerId { get; init; }
- public string PlayerName { get; init; } = "";
- public string PhoneticName { get; init; } = "";
- public TrackerDungeonState DungeonState { get; init; } = null!;
- public bool IsLocalPlayer { get; init; }
-}
diff --git a/src/TrackerCouncil.Smz3.Multiplayer.Client/EventHandlers/TrackDungeonEventHandler.cs b/src/TrackerCouncil.Smz3.Multiplayer.Client/EventHandlers/TrackDungeonEventHandler.cs
deleted file mode 100644
index 75c95badc..000000000
--- a/src/TrackerCouncil.Smz3.Multiplayer.Client/EventHandlers/TrackDungeonEventHandler.cs
+++ /dev/null
@@ -1,5 +0,0 @@
-using TrackerCouncil.Smz3.Shared.Multiplayer;
-
-namespace TrackerCouncil.Smz3.Multiplayer.Client.EventHandlers;
-
-public delegate void TrackDungeonEventHandler(MultiplayerPlayerState playerState, string dungeonName);
diff --git a/src/TrackerCouncil.Smz3.Multiplayer.Client/GameServices/MultiplayerGameTypeService.cs b/src/TrackerCouncil.Smz3.Multiplayer.Client/GameServices/MultiplayerGameTypeService.cs
index 2916fde0e..650b0391d 100644
--- a/src/TrackerCouncil.Smz3.Multiplayer.Client/GameServices/MultiplayerGameTypeService.cs
+++ b/src/TrackerCouncil.Smz3.Multiplayer.Client/GameServices/MultiplayerGameTypeService.cs
@@ -82,9 +82,8 @@ public MultiplayerWorldState GetPlayerDefaultState(World world, IEnumerable x.Id, _ => false);
var items = playerItemTypes.ToDictionary(x => x, _ => 0);
- var bosses = world.GoldenBosses.Select(x => x.Type).ToDictionary(x => x, _ => false);
- var dungeons = world.Dungeons.ToDictionary(x => x.GetType().Name, _ => false);
- var state = new MultiplayerWorldState(locations, items, bosses, dungeons);
+ var bosses = world.Bosses.Select(x => x.Type).ToDictionary(x => x, _ => false);
+ var state = new MultiplayerWorldState(locations, items, bosses);
return state;
}
@@ -181,14 +180,28 @@ public MultiplayerWorldState GetPlayerDefaultState(World world, IEnumerableA unique hash for all of the location items and dungeon rewards
public string GetValidationHash(IEnumerable worlds)
{
+ var worldList = worlds.ToList();
var itemHashCode = string.Join(",",
- worlds.SelectMany(x => x.Locations).OrderBy(x => x.World.Id).ThenBy(x => x.Id)
+ worldList.SelectMany(x => x.Locations)
+ .OrderBy(x => x.World.Id)
+ .ThenBy(x => x.Id)
.Select(x => x.Item.Type.ToString()));
var rewardHashCode = string.Join(",",
- worlds.SelectMany(x => x.Regions).OrderBy(x => x.World.Id)
+ worldList.SelectMany(x => x.RewardRegions)
+ .OrderBy(x => x.World.Id)
.ThenBy(x => x.Name)
- .OfType().Select(x => x.RewardType.ToString()));
- return $"{NonCryptographicHash.Fnv1a(itemHashCode)}{NonCryptographicHash.Fnv1a(rewardHashCode)}";
+ .Select(x => x.RewardType.ToString()));
+ var bossHashCode = string.Join(",",
+ worldList.SelectMany(x => x.BossRegions)
+ .OrderBy(x => x.World.Id)
+ .ThenBy(x => x.Name)
+ .Select(x => x.BossType.ToString()));
+ var prereqHashCode = string.Join(",",
+ worldList.SelectMany(x => x.PrerequisiteRegions)
+ .OrderBy(x => x.World.Id)
+ .ThenBy(x => x.Name)
+ .Select(x => x.RequiredItem.ToString()));
+ return $"{NonCryptographicHash.Fnv1a(itemHashCode)}{NonCryptographicHash.Fnv1a(rewardHashCode)}{NonCryptographicHash.Fnv1a(bossHashCode)}{NonCryptographicHash.Fnv1a(prereqHashCode)}";
}
///
@@ -206,16 +219,7 @@ public async Task TrackLocation(Location location)
/// The item that has been tracked
public async Task TrackItem(Item item)
{
- await Client.TrackItem(item.Type, item.State.TrackingState, item.World.Guid);
- }
-
- ///
- /// Notifies the server that a dungeon has been tracked by the local player
- ///
- /// The dungeon that has been tracked
- public async Task TrackDungeon(IDungeon dungeon)
- {
- await Client.TrackDungeon(dungeon.DungeonState.Name, (dungeon as Region)!.World.Guid);
+ await Client.TrackItem(item.Type, item.TrackingState, item.World.Guid);
}
///
@@ -334,29 +338,6 @@ public async Task TrackDeath()
};
}
- ///
- /// Creates arguments to send to Tracker when a player tracks a dungeon
- ///
- /// The player that tracked the dungeon
- /// The name of the dungeon that was tracked
- /// If it was the local player tracking the item
- public PlayerTrackedDungeonEventHandlerArgs? PlayerTrackedDungeon(MultiplayerPlayerState player, string dungeonName, bool isLocalPlayer)
- {
- if (TrackerState == null || isLocalPlayer) return null;
- var dungeonState =
- TrackerState.DungeonStates.FirstOrDefault(x => x.WorldId == player.WorldId && x.Name == dungeonName);
- if (dungeonState == null || dungeonState.AutoTracked) return null;
-
- return new PlayerTrackedDungeonEventHandlerArgs()
- {
- PlayerId = player.WorldId!.Value,
- PlayerName = player.PlayerName,
- PhoneticName = player.PhoneticName,
- IsLocalPlayer = isLocalPlayer,
- DungeonState = dungeonState,
- };
- }
-
///
/// Creates arguments for when receiving a player state from the server by determining what locations, items, etc.
/// mismatch with the tracker state
@@ -368,7 +349,7 @@ public async Task TrackDeath()
public PlayerSyncReceivedEventHandlerArgs? PlayerSyncReceived(MultiplayerPlayerState player,
MultiplayerPlayerState? previousState, bool isLocalPlayer)
{
- if (TrackerState == null || isLocalPlayer || player.Locations == null || player.Items == null || player.Bosses == null || player.Dungeons == null) return null;
+ if (TrackerState == null || isLocalPlayer || player.Locations == null || player.Items == null || player.Bosses == null) return null;
var didEndGame = player.HasForfeited || player.HasCompleted;
@@ -390,11 +371,6 @@ public async Task TrackDeath()
var updatedBossStates = TrackerState.BossStates.Where(x =>
x.WorldId == player.WorldId && x.Type != BossType.None && !x.AutoTracked && defeatedBosses.Contains(x.Type)).ToList();
- // Gather dungeons that have been cleared
- var clearedDungeons = player.Dungeons.Where(x => x.Tracked).Select(x => x.Dungeon).ToList();
- var updatedDungeonStates = TrackerState.DungeonStates.Where(x =>
- x.WorldId == player.WorldId && !x.AutoTracked && clearedDungeons.Contains(x.Name)).ToList();
-
return new PlayerSyncReceivedEventHandlerArgs()
{
PlayerId = player.WorldId,
@@ -404,7 +380,6 @@ public async Task TrackDeath()
UpdatedLocationStates = updatedLocationStates,
UpdatedItemStates = updatedItemStates,
UpdatedBossStates = updatedBossStates,
- UpdatedDungeonStates = updatedDungeonStates,
ItemsToGive = itemsToGive,
DidForfeit = player.HasForfeited && previousState?.HasForfeited != true,
DidComplete = player.HasCompleted && previousState?.HasCompleted != true
@@ -433,11 +408,7 @@ public MultiplayerWorldState GetPlayerWorldState(MultiplayerPlayerState state, T
.Where(x => x.WorldId == state.WorldId && x.Type != BossType.None)
.ToDictionary(x => x.Type, x => x.AutoTracked);
- var dungeons = trackerState.DungeonStates
- .Where(x => x.WorldId == state.WorldId)
- .ToDictionary(x => x.Name, x => x.AutoTracked);
-
- return new MultiplayerWorldState(locations, items, bosses, dungeons);
+ return new MultiplayerWorldState(locations, items, bosses);
}
///
diff --git a/src/TrackerCouncil.Smz3.Multiplayer.Client/GameServices/MultiworldGameService.cs b/src/TrackerCouncil.Smz3.Multiplayer.Client/GameServices/MultiworldGameService.cs
index de9590abf..02019dd86 100644
--- a/src/TrackerCouncil.Smz3.Multiplayer.Client/GameServices/MultiworldGameService.cs
+++ b/src/TrackerCouncil.Smz3.Multiplayer.Client/GameServices/MultiworldGameService.cs
@@ -1,4 +1,5 @@
-using Microsoft.Extensions.Logging;
+using System.Text.Json;
+using Microsoft.Extensions.Logging;
using TrackerCouncil.Smz3.Data.GeneratedData;
using TrackerCouncil.Smz3.Data.Options;
using TrackerCouncil.Smz3.Data.Services;
@@ -11,15 +12,13 @@ namespace TrackerCouncil.Smz3.Multiplayer.Client.GameServices;
///
/// Service for handling multiworld games
///
-public class MultiworldGameService : MultiplayerGameTypeService
+public class MultiworldGameService(
+ Smz3Randomizer randomizer,
+ Smz3MultiplayerRomGenerator multiplayerRomGenerator,
+ MultiplayerClientService client,
+ ILogger logger)
+ : MultiplayerGameTypeService(randomizer, multiplayerRomGenerator, client, logger)
{
- private ITrackerStateService _trackerStateService;
-
- public MultiworldGameService(Smz3Randomizer randomizer, Smz3MultiplayerRomGenerator multiplayerRomGenerator, MultiplayerClientService client, ITrackerStateService trackerStateService, ILogger logger) : base(randomizer, multiplayerRomGenerator, client, logger)
- {
- _trackerStateService = trackerStateService;
- }
-
///
/// Generates a multiworld seed
///
@@ -44,6 +43,8 @@ public MultiworldGameService(Smz3Randomizer randomizer, Smz3MultiplayerRomGenera
generationConfigs.Add(config);
}
+ Logger.LogDebug("{Json}", JsonSerializer.Serialize(generationConfigs));
+
return GenerateSeedInternal(generationConfigs, seed, out error);
}
@@ -72,6 +73,8 @@ public MultiworldGameService(Smz3Randomizer randomizer, Smz3MultiplayerRomGenera
generationConfigs.Add(config);
}
+ Logger.LogDebug("{Json}", JsonSerializer.Serialize(generationConfigs));
+
return RegenerateSeedInternal(generationConfigs, seed, out error);
}
diff --git a/src/TrackerCouncil.Smz3.Multiplayer.Client/MultiplayerClientService.cs b/src/TrackerCouncil.Smz3.Multiplayer.Client/MultiplayerClientService.cs
index f03c9f390..52ee39258 100644
--- a/src/TrackerCouncil.Smz3.Multiplayer.Client/MultiplayerClientService.cs
+++ b/src/TrackerCouncil.Smz3.Multiplayer.Client/MultiplayerClientService.cs
@@ -40,7 +40,6 @@ public MultiplayerClientService(ILogger logger, Random
public event GameStartedEventHandler? GameStarted;
public event TrackLocationEventHandler? LocationTracked;
public event TrackItemEventHandler? ItemTracked;
- public event TrackDungeonEventHandler? DungeonTracked;
public event TrackBossEventHandler? BossTracked;
public event TrackDeathEventHandler? DeathTracked;
@@ -122,8 +121,6 @@ public async Task Connect(string url)
_connection.On("TrackItem", OnTrackItem);
- _connection.On("TrackDungeon", OnTrackDungeon);
-
_connection.On("TrackBoss", OnTrackBoss);
_connection.On("TrackDeath", OnTrackDeath);
@@ -393,17 +390,6 @@ public async Task TrackItem(ItemType itemType, int trackedValue, string? playerG
await MakeRequest("TrackItem", new TrackItemRequest(playerGuid ?? CurrentPlayerGuid!, itemType, trackedValue));
}
- ///
- /// Notifies the server that a dungeon has been cleared
- ///
- /// The name of the dungeon that has been cleared
- /// The player whose dungeon was cleared. Set to null to use the local player.
- public async Task TrackDungeon(string dungeonName, string? playerGuid = null)
- {
- playerGuid ??= CurrentPlayerGuid;
- await MakeRequest("TrackDungeon", new TrackDungeonRequest(playerGuid ?? CurrentPlayerGuid!, dungeonName));
- }
-
///
/// Notifies the server that a boss has been defeated
///
@@ -618,18 +604,6 @@ private void OnTrackDeath(TrackDeathResponse response)
DeathTracked?.Invoke(player);
}
- ///
- /// On retrieving a player clearing a dungeon
- ///
- ///
- private void OnTrackDungeon(TrackDungeonResponse response)
- {
- var player = Players!.First(x => x.Guid == response.PlayerGuid);
- player.TrackDungeon(response.DungeonName);
- _logger.LogInformation("{Player} tracked dungeon {DungeonName}", player.PlayerName, response.DungeonName);
- DungeonTracked?.Invoke(player, response.DungeonName);
- }
-
///
/// On retrieving a player tracking an item
///
diff --git a/src/TrackerCouncil.Smz3.Multiplayer.Client/MultiplayerGameService.cs b/src/TrackerCouncil.Smz3.Multiplayer.Client/MultiplayerGameService.cs
index d1ef23a39..9ad8cd207 100644
--- a/src/TrackerCouncil.Smz3.Multiplayer.Client/MultiplayerGameService.cs
+++ b/src/TrackerCouncil.Smz3.Multiplayer.Client/MultiplayerGameService.cs
@@ -31,7 +31,6 @@ public MultiplayerGameService(MultiplayerClientService clientService, IServicePr
_client.LocationTracked += ClientOnLocationTracked;
_client.ItemTracked += ClientOnItemTracked;
_client.BossTracked += ClientOnBossTracked;
- _client.DungeonTracked += ClientOnDungeonTracked;
_client.DeathTracked += ClientOnDeathTracked;
_client.PlayerUpdated += ClientOnPlayerUpdated;
_client.PlayerStateRequested += ClientOnPlayerStateRequested;
@@ -56,8 +55,6 @@ private async void ClientOnConnectionClosed(string error, Exception? exception)
public PlayerTrackedBossEventHandler? PlayerTrackedBoss;
- public PlayerTrackedDungeonEventHandler? PlayerTrackedDungeon;
-
public PlayerTrackedDeathEventHandler? PlayerTrackedDeath;
public PlayerSyncReceivedEventHandler? PlayerSyncReceived;
@@ -158,15 +155,6 @@ public async Task TrackItem(Item item)
await _currentGameService.TrackItem(item);
}
- ///
- /// Sends a dungeon tracked event to the server
- ///
- /// The dungeon that was tracked
- public async Task TrackDungeon(IDungeon dungeon)
- {
- await _currentGameService.TrackDungeon(dungeon);
- }
-
///
/// Sends a boss tracked event to the server
///
@@ -226,7 +214,6 @@ public void Dispose()
_client.LocationTracked -= ClientOnLocationTracked;
_client.ItemTracked -= ClientOnItemTracked;
_client.BossTracked -= ClientOnBossTracked;
- _client.DungeonTracked -= ClientOnDungeonTracked;
_client.PlayerUpdated -= ClientOnPlayerUpdated;
_client.PlayerStateRequested -= ClientOnPlayerStateRequested;
_client.PlayerForfeited -= ClientOnPlayerForfeited;
@@ -237,13 +224,6 @@ public void Dispose()
}
#region Multiplayer Client Events
- private void ClientOnDungeonTracked(MultiplayerPlayerState playerState, string dungeonName)
- {
- var args = _currentGameService.PlayerTrackedDungeon(playerState, dungeonName,
- playerState.Guid == _client.CurrentPlayerGuid);
- if (args != null) PlayerTrackedDungeon?.Invoke(args);
- }
-
private void ClientOnBossTracked(MultiplayerPlayerState playerState, BossType bossType)
{
var args = _currentGameService.PlayerTrackedBoss(playerState, bossType,
diff --git a/src/TrackerCouncil.Smz3.Multiplayer.Server/Migrations/20241003001723_ReplaceDungeonState.Designer.cs b/src/TrackerCouncil.Smz3.Multiplayer.Server/Migrations/20241003001723_ReplaceDungeonState.Designer.cs
new file mode 100644
index 000000000..a7dc0c77e
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.Multiplayer.Server/Migrations/20241003001723_ReplaceDungeonState.Designer.cs
@@ -0,0 +1,299 @@
+//
+using System;
+using Microsoft.EntityFrameworkCore;
+using Microsoft.EntityFrameworkCore.Infrastructure;
+using Microsoft.EntityFrameworkCore.Migrations;
+using Microsoft.EntityFrameworkCore.Storage.ValueConversion;
+using TrackerCouncil.Smz3.Multiplayer.Server;
+
+#nullable disable
+
+namespace TrackerCouncil.Smz3.Multiplayer.Server.Migrations
+{
+ [DbContext(typeof(MultiplayerDbContext))]
+ [Migration("20241003001723_ReplaceDungeonState")]
+ partial class ReplaceDungeonState
+ {
+ ///
+ protected override void BuildTargetModel(ModelBuilder modelBuilder)
+ {
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "8.0.7");
+
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerBossState", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("Boss")
+ .HasColumnType("INTEGER");
+
+ b.Property("GameId")
+ .HasColumnType("INTEGER");
+
+ b.Property("PlayerId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Tracked")
+ .HasColumnType("INTEGER");
+
+ b.Property("TrackedTime")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("GameId");
+
+ b.HasIndex("PlayerId");
+
+ b.ToTable("MultiplayerBossStates");
+ });
+
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerGameState", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("CreatedDate")
+ .HasColumnType("TEXT");
+
+ b.Property("DeathLink")
+ .HasColumnType("INTEGER");
+
+ b.Property("Guid")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("LastMessage")
+ .HasColumnType("TEXT");
+
+ b.Property("SaveToDatabase")
+ .HasColumnType("INTEGER");
+
+ b.Property("Seed")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("SendItemsOnComplete")
+ .HasColumnType("INTEGER");
+
+ b.Property("Status")
+ .HasColumnType("INTEGER");
+
+ b.Property("Type")
+ .HasColumnType("INTEGER");
+
+ b.Property("Url")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("ValidationHash")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Version")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.ToTable("MultiplayerGameStates");
+ });
+
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerItemState", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("GameId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Item")
+ .HasColumnType("INTEGER");
+
+ b.Property("PlayerId")
+ .HasColumnType("INTEGER");
+
+ b.Property("TrackedTime")
+ .HasColumnType("TEXT");
+
+ b.Property("TrackingValue")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("GameId");
+
+ b.HasIndex("PlayerId");
+
+ b.ToTable("MultiplayerItemStates");
+ });
+
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerLocationState", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("GameId")
+ .HasColumnType("INTEGER");
+
+ b.Property("LocationId")
+ .HasColumnType("INTEGER");
+
+ b.Property("PlayerId")
+ .HasColumnType("INTEGER");
+
+ b.Property("Tracked")
+ .HasColumnType("INTEGER");
+
+ b.Property("TrackedTime")
+ .HasColumnType("TEXT");
+
+ b.HasKey("Id");
+
+ b.HasIndex("GameId");
+
+ b.HasIndex("PlayerId");
+
+ b.ToTable("MultiplayerLocationStates");
+ });
+
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerPlayerState", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
+
+ b.Property("AdditionalData")
+ .HasColumnType("TEXT");
+
+ b.Property("Config")
+ .HasColumnType("TEXT");
+
+ b.Property("GameId")
+ .HasColumnType("INTEGER");
+
+ b.Property("GenerationData")
+ .HasColumnType("TEXT");
+
+ b.Property("Guid")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("IsAdmin")
+ .HasColumnType("INTEGER");
+
+ b.Property("Key")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("PhoneticName")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("PlayerName")
+ .IsRequired()
+ .HasColumnType("TEXT");
+
+ b.Property("Status")
+ .HasColumnType("INTEGER");
+
+ b.Property("WorldId")
+ .HasColumnType("INTEGER");
+
+ b.HasKey("Id");
+
+ b.HasIndex("GameId");
+
+ b.ToTable("MultiplayerPlayerStates");
+ });
+
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerBossState", b =>
+ {
+ b.HasOne("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerGameState", "Game")
+ .WithMany()
+ .HasForeignKey("GameId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerPlayerState", "Player")
+ .WithMany("Bosses")
+ .HasForeignKey("PlayerId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Game");
+
+ b.Navigation("Player");
+ });
+
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerItemState", b =>
+ {
+ b.HasOne("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerGameState", "Game")
+ .WithMany()
+ .HasForeignKey("GameId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerPlayerState", "Player")
+ .WithMany("Items")
+ .HasForeignKey("PlayerId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Game");
+
+ b.Navigation("Player");
+ });
+
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerLocationState", b =>
+ {
+ b.HasOne("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerGameState", "Game")
+ .WithMany()
+ .HasForeignKey("GameId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.HasOne("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerPlayerState", "Player")
+ .WithMany("Locations")
+ .HasForeignKey("PlayerId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Game");
+
+ b.Navigation("Player");
+ });
+
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerPlayerState", b =>
+ {
+ b.HasOne("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerGameState", "Game")
+ .WithMany("Players")
+ .HasForeignKey("GameId")
+ .OnDelete(DeleteBehavior.Cascade)
+ .IsRequired();
+
+ b.Navigation("Game");
+ });
+
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerGameState", b =>
+ {
+ b.Navigation("Players");
+ });
+
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerPlayerState", b =>
+ {
+ b.Navigation("Bosses");
+
+ b.Navigation("Items");
+
+ b.Navigation("Locations");
+ });
+#pragma warning restore 612, 618
+ }
+ }
+}
diff --git a/src/TrackerCouncil.Smz3.Multiplayer.Server/Migrations/20241003001723_ReplaceDungeonState.cs b/src/TrackerCouncil.Smz3.Multiplayer.Server/Migrations/20241003001723_ReplaceDungeonState.cs
new file mode 100644
index 000000000..965766adf
--- /dev/null
+++ b/src/TrackerCouncil.Smz3.Multiplayer.Server/Migrations/20241003001723_ReplaceDungeonState.cs
@@ -0,0 +1,61 @@
+using System;
+using Microsoft.EntityFrameworkCore.Migrations;
+
+#nullable disable
+
+namespace TrackerCouncil.Smz3.Multiplayer.Server.Migrations
+{
+ ///
+ public partial class ReplaceDungeonState : Migration
+ {
+ ///
+ protected override void Up(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.DropTable(
+ name: "MultiplayerDungeonStates");
+ }
+
+ ///
+ protected override void Down(MigrationBuilder migrationBuilder)
+ {
+ migrationBuilder.CreateTable(
+ name: "MultiplayerDungeonStates",
+ columns: table => new
+ {
+ Id = table.Column(type: "INTEGER", nullable: false)
+ .Annotation("Sqlite:Autoincrement", true),
+ GameId = table.Column(type: "INTEGER", nullable: false),
+ PlayerId = table.Column(type: "INTEGER", nullable: false),
+ Dungeon = table.Column(type: "TEXT", nullable: false),
+ Tracked = table.Column(type: "INTEGER", nullable: false),
+ TrackedTime = table.Column(type: "TEXT", nullable: true)
+ },
+ constraints: table =>
+ {
+ table.PrimaryKey("PK_MultiplayerDungeonStates", x => x.Id);
+ table.ForeignKey(
+ name: "FK_MultiplayerDungeonStates_MultiplayerGameStates_GameId",
+ column: x => x.GameId,
+ principalTable: "MultiplayerGameStates",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ table.ForeignKey(
+ name: "FK_MultiplayerDungeonStates_MultiplayerPlayerStates_PlayerId",
+ column: x => x.PlayerId,
+ principalTable: "MultiplayerPlayerStates",
+ principalColumn: "Id",
+ onDelete: ReferentialAction.Cascade);
+ });
+
+ migrationBuilder.CreateIndex(
+ name: "IX_MultiplayerDungeonStates_GameId",
+ table: "MultiplayerDungeonStates",
+ column: "GameId");
+
+ migrationBuilder.CreateIndex(
+ name: "IX_MultiplayerDungeonStates_PlayerId",
+ table: "MultiplayerDungeonStates",
+ column: "PlayerId");
+ }
+ }
+}
diff --git a/src/TrackerCouncil.Smz3.Multiplayer.Server/Migrations/MultiplayerDbContextModelSnapshot.cs b/src/TrackerCouncil.Smz3.Multiplayer.Server/Migrations/MultiplayerDbContextModelSnapshot.cs
index 94af58315..5209ac516 100644
--- a/src/TrackerCouncil.Smz3.Multiplayer.Server/Migrations/MultiplayerDbContextModelSnapshot.cs
+++ b/src/TrackerCouncil.Smz3.Multiplayer.Server/Migrations/MultiplayerDbContextModelSnapshot.cs
@@ -7,341 +7,290 @@
#nullable disable
-namespace TrackerCouncil.Smz3.Multiplayer.Server.Migrations;
-
-[DbContext(typeof(MultiplayerDbContext))]
-partial class MultiplayerDbContextModelSnapshot : ModelSnapshot
+namespace TrackerCouncil.Smz3.Multiplayer.Server.Migrations
{
- protected override void BuildModel(ModelBuilder modelBuilder)
+ [DbContext(typeof(MultiplayerDbContext))]
+ partial class MultiplayerDbContextModelSnapshot : ModelSnapshot
{
-#pragma warning disable 612, 618
- modelBuilder.HasAnnotation("ProductVersion", "6.0.11");
-
- modelBuilder.Entity("Randomizer.Shared.Multiplayer.MultiplayerBossState", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property("Boss")
- .HasColumnType("INTEGER");
-
- b.Property("GameId")
- .HasColumnType("INTEGER");
-
- b.Property("PlayerId")
- .HasColumnType("INTEGER");
-
- b.Property("Tracked")
- .HasColumnType("INTEGER");
-
- b.Property("TrackedTime")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
-
- b.HasIndex("GameId");
-
- b.HasIndex("PlayerId");
-
- b.ToTable("MultiplayerBossStates");
- });
-
- modelBuilder.Entity("Randomizer.Shared.Multiplayer.MultiplayerDungeonState", b =>
+ protected override void BuildModel(ModelBuilder modelBuilder)
{
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
-
- b.Property("Dungeon")
- .IsRequired()
- .HasColumnType("TEXT");
-
- b.Property("GameId")
- .HasColumnType("INTEGER");
-
- b.Property("PlayerId")
- .HasColumnType("INTEGER");
-
- b.Property("Tracked")
- .HasColumnType("INTEGER");
-
- b.Property("TrackedTime")
- .HasColumnType("TEXT");
-
- b.HasKey("Id");
+#pragma warning disable 612, 618
+ modelBuilder.HasAnnotation("ProductVersion", "8.0.7");
- b.HasIndex("GameId");
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerBossState", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
- b.HasIndex("PlayerId");
+ b.Property("Boss")
+ .HasColumnType("INTEGER");
- b.ToTable("MultiplayerDungeonStates");
- });
+ b.Property("GameId")
+ .HasColumnType("INTEGER");
- modelBuilder.Entity("Randomizer.Shared.Multiplayer.MultiplayerGameState", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
+ b.Property("PlayerId")
+ .HasColumnType("INTEGER");
- b.Property("CreatedDate")
- .HasColumnType("TEXT");
+ b.Property("Tracked")
+ .HasColumnType("INTEGER");
- b.Property("DeathLink")
- .HasColumnType("INTEGER");
+ b.Property("TrackedTime")
+ .HasColumnType("TEXT");
- b.Property("Guid")
- .IsRequired()
- .HasColumnType("TEXT");
+ b.HasKey("Id");
- b.Property("LastMessage")
- .HasColumnType("TEXT");
+ b.HasIndex("GameId");
- b.Property("SaveToDatabase")
- .HasColumnType("INTEGER");
+ b.HasIndex("PlayerId");
- b.Property("Seed")
- .IsRequired()
- .HasColumnType("TEXT");
+ b.ToTable("MultiplayerBossStates");
+ });
- b.Property("SendItemsOnComplete")
- .HasColumnType("INTEGER");
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerGameState", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
- b.Property("Status")
- .HasColumnType("INTEGER");
+ b.Property("CreatedDate")
+ .HasColumnType("TEXT");
- b.Property("Type")
- .HasColumnType("INTEGER");
+ b.Property("DeathLink")
+ .HasColumnType("INTEGER");
- b.Property("Url")
- .IsRequired()
- .HasColumnType("TEXT");
+ b.Property("Guid")
+ .IsRequired()
+ .HasColumnType("TEXT");
- b.Property("ValidationHash")
- .IsRequired()
- .HasColumnType("TEXT");
+ b.Property("LastMessage")
+ .HasColumnType("TEXT");
- b.Property("Version")
- .IsRequired()
- .HasColumnType("TEXT");
+ b.Property("SaveToDatabase")
+ .HasColumnType("INTEGER");
- b.HasKey("Id");
+ b.Property("Seed")
+ .IsRequired()
+ .HasColumnType("TEXT");
- b.ToTable("MultiplayerGameStates");
- });
+ b.Property("SendItemsOnComplete")
+ .HasColumnType("INTEGER");
- modelBuilder.Entity("Randomizer.Shared.Multiplayer.MultiplayerItemState", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
+ b.Property("Status")
+ .HasColumnType("INTEGER");
- b.Property("GameId")
- .HasColumnType("INTEGER");
+ b.Property("Type")
+ .HasColumnType("INTEGER");
- b.Property("Item")
- .HasColumnType("INTEGER");
+ b.Property("Url")
+ .IsRequired()
+ .HasColumnType("TEXT");
- b.Property("PlayerId")
- .HasColumnType("INTEGER");
+ b.Property("ValidationHash")
+ .IsRequired()
+ .HasColumnType("TEXT");
- b.Property("TrackedTime")
- .HasColumnType("TEXT");
+ b.Property("Version")
+ .IsRequired()
+ .HasColumnType("TEXT");
- b.Property("TrackingValue")
- .HasColumnType("INTEGER");
+ b.HasKey("Id");
- b.HasKey("Id");
+ b.ToTable("MultiplayerGameStates");
+ });
- b.HasIndex("GameId");
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerItemState", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
- b.HasIndex("PlayerId");
+ b.Property("GameId")
+ .HasColumnType("INTEGER");
- b.ToTable("MultiplayerItemStates");
- });
+ b.Property("Item")
+ .HasColumnType("INTEGER");
- modelBuilder.Entity("Randomizer.Shared.Multiplayer.MultiplayerLocationState", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
+ b.Property("PlayerId")
+ .HasColumnType("INTEGER");
- b.Property("GameId")
- .HasColumnType("INTEGER");
+ b.Property("TrackedTime")
+ .HasColumnType("TEXT");
- b.Property("LocationId")
- .HasColumnType("INTEGER");
+ b.Property("TrackingValue")
+ .HasColumnType("INTEGER");
- b.Property("PlayerId")
- .HasColumnType("INTEGER");
+ b.HasKey("Id");
- b.Property("Tracked")
- .HasColumnType("INTEGER");
+ b.HasIndex("GameId");
- b.Property("TrackedTime")
- .HasColumnType("TEXT");
+ b.HasIndex("PlayerId");
- b.HasKey("Id");
+ b.ToTable("MultiplayerItemStates");
+ });
- b.HasIndex("GameId");
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerLocationState", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
- b.HasIndex("PlayerId");
+ b.Property("GameId")
+ .HasColumnType("INTEGER");
- b.ToTable("MultiplayerLocationStates");
- });
+ b.Property("LocationId")
+ .HasColumnType("INTEGER");
- modelBuilder.Entity("Randomizer.Shared.Multiplayer.MultiplayerPlayerState", b =>
- {
- b.Property("Id")
- .ValueGeneratedOnAdd()
- .HasColumnType("INTEGER");
+ b.Property("PlayerId")
+ .HasColumnType("INTEGER");
- b.Property("AdditionalData")
- .HasColumnType("TEXT");
+ b.Property("Tracked")
+ .HasColumnType("INTEGER");
- b.Property("Config")
- .HasColumnType("TEXT");
+ b.Property("TrackedTime")
+ .HasColumnType("TEXT");
- b.Property("GameId")
- .HasColumnType("INTEGER");
+ b.HasKey("Id");
- b.Property("GenerationData")
- .HasColumnType("TEXT");
+ b.HasIndex("GameId");
- b.Property("Guid")
- .IsRequired()
- .HasColumnType("TEXT");
+ b.HasIndex("PlayerId");
- b.Property("IsAdmin")
- .HasColumnType("INTEGER");
+ b.ToTable("MultiplayerLocationStates");
+ });
- b.Property("Key")
- .IsRequired()
- .HasColumnType("TEXT");
+ modelBuilder.Entity("TrackerCouncil.Smz3.Shared.Multiplayer.MultiplayerPlayerState", b =>
+ {
+ b.Property("Id")
+ .ValueGeneratedOnAdd()
+ .HasColumnType("INTEGER");
- b.Property("PhoneticName")
- .IsRequired()
- .HasColumnType("TEXT");
+ b.Property("AdditionalData")
+ .HasColumnType("TEXT");
- b.Property("PlayerName")
- .IsRequired()
- .HasColumnType("TEXT");
+ b.Property("Config")
+ .HasColumnType("TEXT");
- b.Property("Status")
- .HasColumnType("INTEGER");
+ b.Property("GameId")
+ .HasColumnType("INTEGER");
- b.Property