diff --git a/TrackerCouncil.Smz3.sln b/TrackerCouncil.Smz3.sln index b9543006e..c07f3c346 100644 --- a/TrackerCouncil.Smz3.sln +++ b/TrackerCouncil.Smz3.sln @@ -42,6 +42,8 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TrackerCouncil.Smz3.Data.Sc EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "TrackerCouncil.Smz3.UI", "src\TrackerCouncil.Smz3.UI\TrackerCouncil.Smz3.UI.csproj", "{07BA0552-C03C-4996-AD9B-DEFFECAC0987}" EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TrackerCouncil.Smz3.LegacyLogic", "src\TrackerCouncil.Smz3.LegacyLogic\TrackerCouncil.Smz3.LegacyLogic.csproj", "{87623A81-7721-46E4-8CF6-F9C1705F4D7B}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -104,6 +106,10 @@ Global {07BA0552-C03C-4996-AD9B-DEFFECAC0987}.Debug|Any CPU.Build.0 = Debug|Any CPU {07BA0552-C03C-4996-AD9B-DEFFECAC0987}.Release|Any CPU.ActiveCfg = Release|Any CPU {07BA0552-C03C-4996-AD9B-DEFFECAC0987}.Release|Any CPU.Build.0 = Release|Any CPU + {87623A81-7721-46E4-8CF6-F9C1705F4D7B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {87623A81-7721-46E4-8CF6-F9C1705F4D7B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {87623A81-7721-46E4-8CF6-F9C1705F4D7B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {87623A81-7721-46E4-8CF6-F9C1705F4D7B}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE diff --git a/src/TrackerCouncil.Smz3.Data/Logic/Logic.cs b/src/TrackerCouncil.Smz3.Data/Logic/Logic.cs index 11dc3b3d2..05e99b0a3 100644 --- a/src/TrackerCouncil.Smz3.Data/Logic/Logic.cs +++ b/src/TrackerCouncil.Smz3.Data/Logic/Logic.cs @@ -175,7 +175,7 @@ public bool CheckAgahnim(Progression items, World world, bool requireRewards) public static IEnumerable GetMissingRequiredItems(Location location, Progression items, out IEnumerable allPossibleMissingItems) { - if (location.IsAvailable(items)) + if (location.Accessibility is Accessibility.Available or Accessibility.AvailableWithKeys) { allPossibleMissingItems = Enumerable.Empty(); return Enumerable.Empty(); diff --git a/src/TrackerCouncil.Smz3.Data/Options/Config.cs b/src/TrackerCouncil.Smz3.Data/Options/Config.cs index f7dd5e9d5..dbfbbc84f 100644 --- a/src/TrackerCouncil.Smz3.Data/Options/Config.cs +++ b/src/TrackerCouncil.Smz3.Data/Options/Config.cs @@ -173,6 +173,7 @@ public class Config public GameMode GameMode { get; set; } = GameMode.Normal; public KeysanityMode KeysanityMode { get; set; } = KeysanityMode.None; + public SMLogic LegacyMetroidLogic { get; set; } = SMLogic.Normal; public bool Race { get; set; } = false; public bool DisableSpoilerLog { get; set; } = false; public bool DisableTrackerSpoilers { get; set; } = false; diff --git a/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj b/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj index 239e94e08..7da69b660 100644 --- a/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj +++ b/src/TrackerCouncil.Smz3.Data/TrackerCouncil.Smz3.Data.csproj @@ -33,6 +33,7 @@ + diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Boss.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Boss.cs index 2df6444f8..1bbf08c5e 100644 --- a/src/TrackerCouncil.Smz3.Data/WorldData/Boss.cs +++ b/src/TrackerCouncil.Smz3.Data/WorldData/Boss.cs @@ -111,6 +111,30 @@ public void UpdateAccessibility(Progression actualProgression, Progression withK } } + public void UpdateLegacyAccessibility(bool isActuallyAccessible, bool isAccessibleWithKeys) + { + if (Defeated) + { + Accessibility = Accessibility.Cleared; + } + else if (Region == null) + { + Accessibility = Accessibility.Unknown; + } + else if (isActuallyAccessible) + { + Accessibility = Accessibility.Available; + } + else if (isAccessibleWithKeys) + { + Accessibility = Accessibility.AvailableWithKeys; + } + else + { + Accessibility = Accessibility.OutOfLogic; + } + } + public event EventHandler? UpdatedBossState; public event EventHandler? UpdatedAccessibility; diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Location.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Location.cs index 7c7c6109d..bf5251777 100644 --- a/src/TrackerCouncil.Smz3.Data/WorldData/Location.cs +++ b/src/TrackerCouncil.Smz3.Data/WorldData/Location.cs @@ -328,6 +328,22 @@ public Accessibility GetAccessibility(Progression actualProgression, Progression /// /// Returns the status of a location based on the given items /// + /// + /// + /// + /// + /// + public Accessibility GetLegacyAccessibility(bool isAccessible, bool isAccessibleWithKeys) + { + if (State.Cleared) return Accessibility.Cleared; + else if (isAccessible) return Accessibility.Available; + else if (isAccessibleWithKeys) return Accessibility.AvailableWithKeys; + else return Accessibility.OutOfLogic; + } + + /// + /// Updates 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 @@ -339,6 +355,20 @@ public void UpdateAccessibility(Progression actualProgression, Progression withK AccessibilityUpdated?.Invoke(this, EventArgs.Empty); } + /// + /// Updates the status of a location based on the given items + /// + /// + /// + /// The LocationStatus enum of the location + public void UpdateLegacyAccessibility(bool isAccessible, bool isAccessibleWithKeys) + { + var newValue = GetLegacyAccessibility(isAccessible, isAccessibleWithKeys); + if (newValue == Accessibility) return; + Accessibility = newValue; + AccessibilityUpdated?.Invoke(this, EventArgs.Empty); + } + /// /// Determines whether the specified item can be assigned to this /// location. diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Progression.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Progression.cs index f8178cd0d..518a46af3 100644 --- a/src/TrackerCouncil.Smz3.Data/WorldData/Progression.cs +++ b/src/TrackerCouncil.Smz3.Data/WorldData/Progression.cs @@ -1,5 +1,6 @@ using System.Collections.Generic; using System.Linq; +using Randomizer.SMZ3; using TrackerCouncil.Smz3.Shared; using TrackerCouncil.Smz3.Shared.Enums; @@ -194,6 +195,14 @@ public bool Remove(ItemType item) public IEnumerator GetEnumerator() => Items.GetEnumerator(); + public void InitLegacyProgression() + { + var itemIds = Items.Select(x => (int)x).ToList(); + LegacyProgression = new LegacyProgression(itemIds); + } + + public LegacyProgression? LegacyProgression { get; private set; } + public bool HasMarkedMedallion(ItemType? medallion) => (medallion != null && medallion != ItemType.Nothing && Contains(medallion.Value)) || (Bombos && Ether && Quake); diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/Reward.cs b/src/TrackerCouncil.Smz3.Data/WorldData/Reward.cs index 11278af78..9103ec196 100644 --- a/src/TrackerCouncil.Smz3.Data/WorldData/Reward.cs +++ b/src/TrackerCouncil.Smz3.Data/WorldData/Reward.cs @@ -89,6 +89,30 @@ public void UpdateAccessibility(Progression actualProgression, Progression withK } } + public void UpdateLegacyAccessibility(bool isActuallyAccessible, bool isAccessibleWithKeys) + { + if (HasReceivedReward) + { + Accessibility = Accessibility.Cleared; + } + else if (Region == null) + { + Accessibility = Accessibility.Unknown; + } + else if (isActuallyAccessible) + { + Accessibility = Accessibility.Available; + } + else if (isAccessibleWithKeys) + { + Accessibility = Accessibility.AvailableWithKeys; + } + else + { + Accessibility = Accessibility.OutOfLogic; + } + } + public bool HasCorrectlyMarkedReward => MarkedReward == Type; public event EventHandler? UpdatedRewardState; diff --git a/src/TrackerCouncil.Smz3.Data/WorldData/World.cs b/src/TrackerCouncil.Smz3.Data/WorldData/World.cs index 539480be5..45ad14fa8 100644 --- a/src/TrackerCouncil.Smz3.Data/WorldData/World.cs +++ b/src/TrackerCouncil.Smz3.Data/WorldData/World.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.Collections.Immutable; using System.Linq; +using Randomizer.SMZ3; using TrackerCouncil.Smz3.Shared; using TrackerCouncil.Smz3.Data.Logic; using TrackerCouncil.Smz3.Data.Options; @@ -101,6 +102,22 @@ public World(Config config, string player, int id, string guid, bool isLocalWorl RewardRegions = Regions.OfType().ToImmutableList(); BossRegions = Regions.OfType().ToImmutableList(); PrerequisiteRegions = Regions.OfType().ToImmutableList(); + + if (Config.RomGenerator != RomGenerator.Cas) + { + var legacyConfig = new LegacyConfig() + { + LegacyGameMode = (LegacyGameMode)Config.GameMode, + LegacySmLogic = (LegacySMLogic)Config.LegacyMetroidLogic, + LegacyKeyShuffle = Config.MetroidKeysanity || Config.ZeldaKeysanity + ? LegacyKeyShuffle.Keysanity + : LegacyKeyShuffle.None, + }; + + LegacyWorld = new LegacyWorld(legacyConfig, Player, Id, Guid); + + UpdateLegacyWorld(); + } } public Config Config { get; } @@ -170,6 +187,7 @@ public World(Config config, string player, int id, string guid, bool isLocalWorl public WreckedShip WreckedShip { get; } public WorldItemPools ItemPools { get; } public IEnumerable HintTiles { get; set; } = []; + public LegacyWorld? LegacyWorld { get; private set; } public IEnumerable ActiveHintTileLocations => HintTiles .Where(x => x.State?.HintState == HintState.Viewed && x.Locations?.Any() == true && x.WorldId == Id) @@ -305,6 +323,79 @@ private void SetBottles(Random rnd) } } + public void UpdateLegacyWorld() + { + if (LegacyWorld == null) + { + return; + } + + var worldState = new LegacyWorldState() + { + Medallions = + [ + GetLegacyMedallion(MiseryMire), + GetLegacyMedallion(TurtleRock) + ], + Rewards = + [ + GetLegacyRewardType(EasternPalace), + GetLegacyRewardType(DesertPalace), + GetLegacyRewardType(TowerOfHera), + GetLegacyRewardType(PalaceOfDarkness), + GetLegacyRewardType(SwampPalace), + GetLegacyRewardType(SkullWoods), + GetLegacyRewardType(ThievesTown), + GetLegacyRewardType(IcePalace), + GetLegacyRewardType(MiseryMire), + GetLegacyRewardType(TurtleRock), + GetLegacyRewardType(KraidsLair), + GetLegacyRewardType(WreckedShip), + GetLegacyRewardType(InnerMaridia), + GetLegacyRewardType(LowerNorfairEast), + ], + TowerCrystals = Config.GanonsTowerCrystalCount, + GanonCrystals = Config.GanonCrystalCount, + TourianBossTokens = Config.TourianBossCount + }; + + LegacyWorld.Setup(worldState); + } + + private LegacyRewardType GetLegacyRewardType(IHasReward region) + { + return region.MarkedReward switch + { + RewardType.Agahnim => LegacyRewardType.Agahnim, + RewardType.PendantGreen => LegacyRewardType.PendantGreen, + RewardType.PendantBlue => LegacyRewardType.PendantNonGreen, + RewardType.PendantRed => LegacyRewardType.PendantNonGreen, + RewardType.CrystalBlue => LegacyRewardType.CrystalBlue, + RewardType.CrystalRed => LegacyRewardType.CrystalRed, + RewardType.KraidToken => LegacyRewardType.BossTokenKraid, + RewardType.PhantoonToken => LegacyRewardType.BossTokenPhantoon, + RewardType.DraygonToken => LegacyRewardType.BossTokenDraygon, + RewardType.RidleyToken => LegacyRewardType.BossTokenRidley, + _ => LegacyRewardType.None + }; + } + + private LegacyWorldState.LegacyMedallion GetLegacyMedallion(IHasPrerequisite region) + { + var unobtainedMedallion = AllItems.FirstOrDefault(x => + x.IsLocalPlayerItem && x.Type.IsInCategory(ItemCategory.Medallion) && x.TrackingState == 0)?.Type; + + var requiredItemType = + region.MarkedItem ?? unobtainedMedallion ?? region.RequiredItem; + + return requiredItemType switch + { + ItemType.Bombos => LegacyWorldState.LegacyMedallion.Bombos, + ItemType.Ether => LegacyWorldState.LegacyMedallion.Ether, + _ => LegacyWorldState.LegacyMedallion.Bombos + }; + } + public int CountReceivedReward(Progression items, RewardType reward) { return CountReceivedRewards(items, [reward]); diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/LegacyConfig.cs b/src/TrackerCouncil.Smz3.LegacyLogic/LegacyConfig.cs new file mode 100644 index 000000000..1f5e40948 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/LegacyConfig.cs @@ -0,0 +1,151 @@ +using System.Collections.Generic; +using System.ComponentModel; + +namespace Randomizer.SMZ3 { + + [DefaultValue(Normal)] + public enum LegacyGameMode { + [Description("Single player")] + Normal, + [Description("Multiworld")] + Multiworld, + } + + [DefaultValue(Normal)] + public enum LegacyZ3Logic { + [Description("Normal")] + Normal, + [Description("No major glitches")] + Nmg, + [Description("Overworld glitches")] + Owg, + } + + [DefaultValue(Normal)] + public enum LegacySMLogic { + [Description("Normal")] + Normal, + [Description("Hard")] + Hard, + } + + [DefaultValue(Randomized)] + enum SwordLocation { + [Description("Randomized")] + Randomized, + [Description("Early")] + Early, + [Description("Uncle assured")] + Uncle, + } + + [DefaultValue(Randomized)] + enum MorphLocation { + [Description("Randomized")] + Randomized, + [Description("Early")] + Early, + [Description("Original location")] + Original, + } + + [DefaultValue(DefeatBoth)] + public enum LegacyGoal { + [Description("Defeat Ganon and Mother Brain")] + DefeatBoth, + [Description("Fast Ganon and Defeat Mother Brain")] + FastGanonDefeatMotherBrain, + [Description("All Dungeons and Defeat Mother Brain")] + AllDungeonsDefeatMotherBrain, + } + + [DefaultValue(None)] + public enum LegacyKeyShuffle { + [Description("None")] + None, + [Description("Keysanity")] + Keysanity + } + + [DefaultValue(SevenCrystals)] + public enum LegacyOpenTower { + [Description("Random")] + Random = -1, + [Description("No Crystals")] + NoCrystals = 0, + [Description("One Crystal")] + OneCrystal = 1, + [Description("Two Crystals")] + TwoCrystals = 2, + [Description("Three Crystals")] + ThreeCrystals = 3, + [Description("Four Crystals")] + FourCrystals = 4, + [Description("Five Crystals")] + FiveCrystals = 5, + [Description("Six Crystals")] + SixCrystals = 6, + [Description("Seven Crystals")] + SevenCrystals = 7, + } + + [DefaultValue(SevenCrystals)] + public enum LegacyGanonVulnerable { + [Description("Random")] + Random = -1, + [Description("No Crystals")] + NoCrystals = 0, + [Description("One Crystal")] + OneCrystal = 1, + [Description("Two Crystals")] + TwoCrystals = 2, + [Description("Three Crystals")] + ThreeCrystals = 3, + [Description("Four Crystals")] + FourCrystals = 4, + [Description("Five Crystals")] + FiveCrystals = 5, + [Description("Six Crystals")] + SixCrystals = 6, + [Description("Seven Crystals")] + SevenCrystals = 7, + } + + [DefaultValue(FourBosses)] + public enum LegacyOpenTourian { + [Description("Random")] + Random = -1, + [Description("No Bosses")] + NoBosses = 0, + [Description("One Boss")] + OneBoss = 1, + [Description("Two Bosses")] + TwoBosses = 2, + [Description("Three Bosses")] + ThreeBosses = 3, + [Description("Four Bosses")] + FourBosses = 4, + } + + public class LegacyConfig { + + public LegacyGameMode LegacyGameMode { get; set; } = LegacyGameMode.Normal; + public LegacyZ3Logic LegacyZ3Logic { get; set; } = LegacyZ3Logic.Normal; + public LegacySMLogic LegacySmLogic { get; set; } = LegacySMLogic.Normal; + internal SwordLocation SwordLocation { get; set; } = SwordLocation.Randomized; + internal MorphLocation MorphLocation { get; set; } = MorphLocation.Randomized; + public LegacyGoal LegacyGoal { get; set; } = LegacyGoal.DefeatBoth; + public LegacyKeyShuffle LegacyKeyShuffle { get; set; } = LegacyKeyShuffle.None; + public bool Race { get; set; } = false; + public LegacyOpenTower LegacyOpenTower { get; set; } = LegacyOpenTower.SevenCrystals; + public LegacyGanonVulnerable LegacyGanonVulnerable { get; set; } = LegacyGanonVulnerable.SevenCrystals; + public LegacyOpenTourian LegacyOpenTourian { get; set; } = LegacyOpenTourian.FourBosses; + public Dictionary InitialItems = new Dictionary(); + + public bool SingleWorld => LegacyGameMode == LegacyGameMode.Normal; + public bool MultiWorld => LegacyGameMode == LegacyGameMode.Multiworld; + public bool Keysanity => LegacyKeyShuffle != LegacyKeyShuffle.None; + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/LegacyItem.cs b/src/TrackerCouncil.Smz3.LegacyLogic/LegacyItem.cs new file mode 100644 index 000000000..ed5a26db9 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/LegacyItem.cs @@ -0,0 +1,1010 @@ +using System; +using System.Linq; +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyItemType; +using static Randomizer.SMZ3.LegacySMLogic; +using static Randomizer.SMZ3.LegacyRewardType; +using System.Text.RegularExpressions; +using System.ComponentModel; +using TrackerCouncil.Smz3.Shared; + +namespace Randomizer.SMZ3 { + + public enum LegacyItemType : byte { + [Description("Nothing")] + Nothing, + + /* This is used for the Random starting items */ + [Description("Random")] + Random, + + [Description("Hyrule Castle Map")] + MapHC = 0x7F, + [Description("Eastern Palace Map")] + MapEP = 0x7D, + [Description("Desert Palace Map")] + MapDP = 0x7C, + [Description("Tower of Hera Map")] + MapTH = 0x75, + [Description("Palace of Darkness Map")] + MapPD = 0x79, + [Description("Swamp Palace Map")] + MapSP = 0x7A, + [Description("Skull Woods Map")] + MapSW = 0x77, + [Description("Thieves Town Map")] + MapTT = 0x74, + [Description("Ice Palace Map")] + MapIP = 0x76, + [Description("Misery Mire Map")] + MapMM = 0x78, + [Description("Turtle Rock Map")] + MapTR = 0x73, + [Description("Ganons Tower Map")] + MapGT = 0x72, + + [Description("Eastern Palace Compass")] + CompassEP = 0x8D, + [Description("Desert Palace Compass")] + CompassDP = 0x8C, + [Description("Tower of Hera Compass")] + CompassTH = 0x85, + [Description("Palace of Darkness Compass")] + CompassPD = 0x89, + [Description("Swamp Palace Compass")] + CompassSP = 0x8A, + [Description("Skull Woods Compass")] + CompassSW = 0x87, + [Description("Thieves Town Compass")] + CompassTT = 0x84, + [Description("Ice Palace Compass")] + CompassIP = 0x86, + [Description("Misery Mire Compass")] + CompassMM = 0x88, + [Description("Turtle Rock Compass")] + CompassTR = 0x83, + [Description("Ganons Tower Compass")] + CompassGT = 0x82, + + [Description("Eastern Palace Big Key")] + BigKeyEP = 0x9D, + [Description("Desert Palace Big Key")] + BigKeyDP = 0x9C, + [Description("Tower of Hera Big Key")] + BigKeyTH = 0x95, + [Description("Palace of Darkness Big Key")] + BigKeyPD = 0x99, + [Description("Swamp Palace Big Key")] + BigKeySP = 0x9A, + [Description("Skull Woods Big Key")] + BigKeySW = 0x97, + [Description("Thieves Town Big Key")] + BigKeyTT = 0x94, + [Description("Ice Palace Big Key")] + BigKeyIP = 0x96, + [Description("Misery Mire Big Key")] + BigKeyMM = 0x98, + [Description("Turtle Rock Big Key")] + BigKeyTR = 0x93, + [Description("Ganons Tower Big Key")] + BigKeyGT = 0x92, + + [Description("Sewer Key")] + KeyHC = 0xA0, + [Description("Castle Tower Key")] + KeyCT = 0xA4, + [Description("Desert Palace Key")] + KeyDP = 0xA3, + [Description("Tower of Hera Key")] + KeyTH = 0xAA, + [Description("Palace of Darkness Key")] + KeyPD = 0xA6, + [Description("Swamp Palace Key")] + KeySP = 0xA5, + [Description("Skull Woods Key")] + KeySW = 0xA8, + [Description("Thieves Town Key")] + KeyTT = 0xAB, + [Description("Ice Palace Key")] + KeyIP = 0xA9, + [Description("Misery Mire Key")] + KeyMM = 0xA7, + [Description("Turtle Rock Key")] + KeyTR = 0xAC, + [Description("Ganons Tower Key")] + KeyGT = 0xAD, + + [Description("Small Key")] + Key = 0x24, + [Description("Compass")] + Compass = 0x25, + [Description("Big Key")] + BigKey = 0x32, + [Description("Map")] + Map = 0x33, + + + [Description("Progressive Mail")] + ProgressiveTunic = 0x60, + [Description("Progressive Shield")] + ProgressiveShield = 0x5F, + [Description("Progressive Sword")] + ProgressiveSword = 0x5E, + [Description("Bow")] + Bow = 0x0B, + [Description("Silver Arrows")] + SilverArrows = 0x58, + [Description("Blue Boomerang")] + BlueBoomerang = 0x0C, + [Description("Red Boomerang")] + RedBoomerang = 0x2A, + [Description("Hookshot")] + Hookshot = 0x0A, + [Description("Mushroom")] + Mushroom = 0x29, + [Description("Magic Powder")] + Powder = 0x0D, + [Description("Fire Rod")] + Firerod = 0x07, + [Description("Ice Rod")] + Icerod = 0x08, + [Description("Bombos")] + Bombos = 0x0f, + [Description("Ether")] + Ether = 0x10, + [Description("Quake")] + Quake = 0x11, + [Description("Lamp")] + Lamp = 0x12, + [Description("Hammer")] + Hammer = 0x09, + [Description("Shovel")] + Shovel = 0x13, + [Description("Flute")] + Flute = 0x14, + [Description("Bug Catching Net")] + Bugnet = 0x21, + [Description("Book of Mudora")] + Book = 0x1D, + [Description("Bottle")] + Bottle = 0x16, + [Description("Cane of Somaria")] + Somaria = 0x15, + [Description("Cane of Byrna")] + Byrna = 0x18, + [Description("Magic Cape")] + Cape = 0x19, + [Description("Magic Mirror")] + Mirror = 0x1A, + [Description("Pegasus Boots")] + Boots = 0x4B, + [Description("Progressive Glove")] + ProgressiveGlove = 0x61, + [Description("Zora's Flippers")] + Flippers = 0x1E, + [Description("Moon Pearl")] + MoonPearl = 0x1F, + [Description("Half Magic")] + HalfMagic = 0x4E, + [Description("Piece of Heart")] + HeartPiece = 0x17, + [Description("Heart Container")] + HeartContainer = 0x3E, + [Description("Sanctuary Heart Container")] + HeartContainerRefill = 0x3F, + [Description("Three Bombs")] + ThreeBombs = 0x28, + [Description("Single Arrow")] + Arrow = 0x43, + [Description("Ten Arrows")] + TenArrows = 0x44, + [Description("One Rupee")] + OneRupee = 0x34, + [Description("Five Rupees")] + FiveRupees = 0x35, + [Description("Twenty Rupees")] + TwentyRupees = 0x36, + [Description("Twenty Rupees")] + TwentyRupees2 = 0x47, + [Description("Fifty Rupees")] + FiftyRupees = 0x41, + [Description("One Hundred Rupees")] + OneHundredRupees = 0x40, + [Description("Three Hundred Rupees")] + ThreeHundredRupees = 0x46, + [Description("+5 Bomb Capacity")] + BombUpgrade5 = 0x51, + [Description("+10 Bomb Capacity")] + BombUpgrade10 = 0x52, + [Description("+5 Arrow Capacity")] + ArrowUpgrade5 = 0x53, + [Description("+10 Arrow Capacity")] + ArrowUpgrade10 = 0x54, + + [Description("Crateria Level 1 Keycard")] + CardCrateriaL1 = 0xD0, + [Description("Crateria Level 2 Keycard")] + CardCrateriaL2 = 0xD1, + [Description("Crateria Boss Keycard")] + CardCrateriaBoss = 0xD2, + [Description("Brinstar Level 1 Keycard")] + CardBrinstarL1 = 0xD3, + [Description("Brinstar Level 2 Keycard")] + CardBrinstarL2 = 0xD4, + [Description("Brinstar Boss Keycard")] + CardBrinstarBoss = 0xD5, + [Description("Norfair Level 1 Keycard")] + CardNorfairL1 = 0xD6, + [Description("Norfair Level 2 Keycard")] + CardNorfairL2 = 0xD7, + [Description("Norfair Boss Keycard")] + CardNorfairBoss = 0xD8, + [Description("Maridia Level 1 Keycard")] + CardMaridiaL1 = 0xD9, + [Description("Maridia Level 2 Keycard")] + CardMaridiaL2 = 0xDA, + [Description("Maridia Boss Keycard")] + CardMaridiaBoss = 0xDB, + [Description("Wrecked Ship Level 1 Keycard")] + CardWreckedShipL1 = 0xDC, + [Description("Wrecked Ship Boss Keycard")] + CardWreckedShipBoss = 0xDD, + [Description("Lower Norfair Level 1 Keycard")] + CardLowerNorfairL1 = 0xDE, + [Description("Lower Norfair Boss Keycard")] + CardLowerNorfairBoss = 0xDF, + + [Description("Brinstar Map")] + SmMapBrinstar = 0xCA, + [Description("Wrecked Ship Map")] + SmMapWreckedShip = 0xCB, + [Description("Maridia Map")] + SmMapMaridia = 0xCC, + [Description("Lower Norfair Map")] + SmMapLowerNorfair = 0xCD, + + [Description("Missile")] + Missile = 0xC2, + [Description("Super Missile")] + Super = 0xC3, + [Description("Power Bomb")] + PowerBomb = 0xC4, + [Description("Grappling Beam")] + Grapple = 0xB0, + [Description("X-Ray Scope")] + XRay = 0xB1, + [Description("Energy Tank")] + ETank = 0xC0, + [Description("Reserve Tank")] + ReserveTank = 0xC1, + [Description("Charge Beam")] + Charge = 0xBB, + [Description("Ice Beam")] + Ice = 0xBC, + [Description("Wave Beam")] + Wave = 0xBD, + [Description("Spazer")] + Spazer = 0xBE, + [Description("Plasma Beam")] + Plasma = 0xBF, + [Description("Varia Suit")] + Varia = 0xB2, + [Description("Gravity Suit")] + Gravity = 0xB6, + [Description("Morphing Ball")] + Morph = 0xB4, + [Description("Morph Bombs")] + Bombs = 0xB9, + [Description("Spring Ball")] + SpringBall = 0xB3, + [Description("Screw Attack")] + ScrewAttack = 0xB5, + [Description("Hi-Jump Boots")] + HiJump = 0xB7, + [Description("Space Jump")] + SpaceJump = 0xB8, + [Description("Speed Booster")] + SpeedBooster = 0xBA, + + [Description("Bottle with Red Potion")] + BottleWithRedPotion = 0x2B, + [Description("Bottle with Green Potion")] + BottleWithGreenPotion = 0x2C, + [Description("Bottle with Blue Potion")] + BottleWithBluePotion = 0x2D, + [Description("Bottle with Fairy")] + BottleWithFairy = 0x3D, + [Description("Bottle with Bee")] + BottleWithBee = 0x3C, + [Description("Bottle with Gold Bee")] + BottleWithGoldBee = 0x48, + [Description("Red Potion Refill")] + RedContent = 0x2E, + [Description("Green Potion Refill")] + GreenContent = 0x2F, + [Description("Blue Potion Refill")] + BlueContent = 0x30, + [Description("Bee Refill")] + BeeContent = 0x0E, + } + + class LegacyItem { + + public string Name { get; set; } + public LegacyItemType Type { get; set; } + public bool Progression { get; set; } + public LegacyWorld LegacyWorld { get; set; } + + static readonly Regex dungeon = new("^(BigKey|Key|Map|Compass)"); + static readonly Regex bigKey = new("^BigKey"); + static readonly Regex key = new("^Key"); + static readonly Regex map = new("^Map"); + static readonly Regex compass = new("^Compass"); + static readonly Regex keycard = new("^Card"); + static readonly Regex smMap = new("^SmMap"); + + public bool IsDungeonItem => dungeon.IsMatch(Type.ToString()); + public bool IsBigKey => bigKey.IsMatch(Type.ToString()); + public bool IsKey => key.IsMatch(Type.ToString()); + public bool IsMap => map.IsMatch(Type.ToString()); + public bool IsCompass => compass.IsMatch(Type.ToString()); + public bool IsKeycard => keycard.IsMatch(Type.ToString()); + public bool IsSmMap => smMap.IsMatch(Type.ToString()); + + public bool Is(LegacyItemType type, LegacyWorld legacyWorld) => Type == type && LegacyWorld == legacyWorld; + public bool IsNot(LegacyItemType type, LegacyWorld legacyWorld) => !Is(type, legacyWorld); + + public LegacyItem(LegacyItemType legacyItemType) { + Name = legacyItemType.GetDescription(); + Type = legacyItemType; + } + + public LegacyItem(LegacyItemType legacyItemType, LegacyWorld legacyWorld) : this(legacyItemType) { + LegacyWorld = legacyWorld; + } + + public static LegacyItem Nothing(LegacyWorld legacyWorld) { + return new LegacyItem(LegacyItemType.Nothing, legacyWorld); + } + + public static List CreateProgressionPool(LegacyWorld legacyWorld) { + var itemPool = new List { + new LegacyItem(ProgressiveShield), + new LegacyItem(ProgressiveShield), + new LegacyItem(ProgressiveShield), + new LegacyItem(ProgressiveSword), + new LegacyItem(ProgressiveSword), + new LegacyItem(Bow), + new LegacyItem(Hookshot), + new LegacyItem(Mushroom), + new LegacyItem(Powder), + new LegacyItem(Firerod), + new LegacyItem(Icerod), + new LegacyItem(Bombos), + new LegacyItem(Ether), + new LegacyItem(Quake), + new LegacyItem(Lamp), + new LegacyItem(Hammer), + new LegacyItem(Shovel), + new LegacyItem(Flute), + new LegacyItem(Bugnet), + new LegacyItem(Book), + new LegacyItem(Bottle), + new LegacyItem(Somaria), + new LegacyItem(Byrna), + new LegacyItem(Cape), + new LegacyItem(Mirror), + new LegacyItem(Boots), + new LegacyItem(ProgressiveGlove), + new LegacyItem(ProgressiveGlove), + new LegacyItem(Flippers), + new LegacyItem(MoonPearl), + new LegacyItem(HalfMagic), + + new LegacyItem(Grapple), + new LegacyItem(Charge), + new LegacyItem(Ice), + new LegacyItem(Wave), + new LegacyItem(Plasma), + new LegacyItem(Varia), + new LegacyItem(Gravity), + new LegacyItem(Morph), + new LegacyItem(Bombs), + new LegacyItem(SpringBall), + new LegacyItem(ScrewAttack), + new LegacyItem(HiJump), + new LegacyItem(SpaceJump), + new LegacyItem(SpeedBooster), + + new LegacyItem(Missile), + new LegacyItem(Super), + new LegacyItem(PowerBomb), + new LegacyItem(PowerBomb), + new LegacyItem(ETank), + new LegacyItem(ETank), + new LegacyItem(ETank), + new LegacyItem(ETank), + new LegacyItem(ETank), + + new LegacyItem(ReserveTank), + new LegacyItem(ReserveTank), + new LegacyItem(ReserveTank), + new LegacyItem(ReserveTank), + }; + + foreach (var item in itemPool) { + item.Progression = true; + item.LegacyWorld = legacyWorld; + } + + return itemPool; + } + + public static List CreateNicePool(LegacyWorld legacyWorld) { + var itemPool = new List { + new LegacyItem(ProgressiveTunic), + new LegacyItem(ProgressiveTunic), + new LegacyItem(ProgressiveSword), + new LegacyItem(ProgressiveSword), + new LegacyItem(SilverArrows), + new LegacyItem(BlueBoomerang), + new LegacyItem(RedBoomerang), + new LegacyItem(Bottle), + new LegacyItem(Bottle), + new LegacyItem(Bottle), + new LegacyItem(HeartContainerRefill), + + new LegacyItem(Spazer), + new LegacyItem(XRay), + }; + + itemPool.AddRange(Copies(10, () => new LegacyItem(HeartContainer, legacyWorld))); + + foreach (var item in itemPool) item.LegacyWorld = legacyWorld; + + return itemPool; + } + + public static List CreateJunkPool(LegacyWorld legacyWorld) { + var itemPool = new List { + new LegacyItem(Arrow), + new LegacyItem(OneHundredRupees) + }; + + itemPool.AddRange(Copies(24, () => new LegacyItem(HeartPiece))); + itemPool.AddRange(Copies(8, () => new LegacyItem(TenArrows))); + itemPool.AddRange(Copies(13, () => new LegacyItem(ThreeBombs))); + itemPool.AddRange(Copies(4, () => new LegacyItem(ArrowUpgrade5))); + itemPool.AddRange(Copies(4, () => new LegacyItem(BombUpgrade5))); + itemPool.AddRange(Copies(2, () => new LegacyItem(OneRupee))); + itemPool.AddRange(Copies(4, () => new LegacyItem(FiveRupees))); + itemPool.AddRange(Copies(legacyWorld.LegacyConfig.Keysanity ? 21 : 28, () => new LegacyItem(TwentyRupees))); + itemPool.AddRange(Copies(7, () => new LegacyItem(FiftyRupees))); + itemPool.AddRange(Copies(5, () => new LegacyItem(ThreeHundredRupees))); + + itemPool.AddRange(Copies(9, () => new LegacyItem(ETank))); + itemPool.AddRange(Copies(39, () => new LegacyItem(Missile))); + itemPool.AddRange(Copies(15, () => new LegacyItem(Super))); + itemPool.AddRange(Copies(8, () => new LegacyItem(PowerBomb))); + + foreach (var item in itemPool) item.LegacyWorld = legacyWorld; + + return itemPool; + } + + /* The order of the dungeon pool is significant */ + public static List CreateDungeonPool(LegacyWorld legacyWorld) { + var itemPool = new List(); + + itemPool.AddRange(new[] { + new LegacyItem(BigKeyEP), + new LegacyItem(BigKeyDP), + new LegacyItem(BigKeyTH), + new LegacyItem(BigKeyPD), + new LegacyItem(BigKeySP), + new LegacyItem(BigKeySW), + new LegacyItem(BigKeyTT), + new LegacyItem(BigKeyIP), + new LegacyItem(BigKeyMM), + new LegacyItem(BigKeyTR), + new LegacyItem(BigKeyGT), + }); + + itemPool.AddRange(Copies(1, () => new LegacyItem(KeyHC))); + itemPool.AddRange(Copies(2, () => new LegacyItem(KeyCT))); + itemPool.AddRange(Copies(1, () => new LegacyItem(KeyDP))); + itemPool.AddRange(Copies(1, () => new LegacyItem(KeyTH))); + itemPool.AddRange(Copies(6, () => new LegacyItem(KeyPD))); + itemPool.AddRange(Copies(1, () => new LegacyItem(KeySP))); + itemPool.AddRange(Copies(3, () => new LegacyItem(KeySW))); + itemPool.AddRange(Copies(1, () => new LegacyItem(KeyTT))); + itemPool.AddRange(Copies(2, () => new LegacyItem(KeyIP))); + itemPool.AddRange(Copies(3, () => new LegacyItem(KeyMM))); + itemPool.AddRange(Copies(4, () => new LegacyItem(KeyTR))); + itemPool.AddRange(Copies(4, () => new LegacyItem(KeyGT))); + + itemPool.AddRange(new[] { + new LegacyItem(MapEP), + new LegacyItem(MapDP), + new LegacyItem(MapTH), + new LegacyItem(MapPD), + new LegacyItem(MapSP), + new LegacyItem(MapSW), + new LegacyItem(MapTT), + new LegacyItem(MapIP), + new LegacyItem(MapMM), + new LegacyItem(MapTR), + }); + if (!legacyWorld.LegacyConfig.Keysanity) { + itemPool.AddRange(new[] { + new LegacyItem(MapHC), + new LegacyItem(MapGT), + new LegacyItem(CompassEP), + new LegacyItem(CompassDP), + new LegacyItem(CompassTH), + new LegacyItem(CompassPD), + new LegacyItem(CompassSP), + new LegacyItem(CompassSW), + new LegacyItem(CompassTT), + new LegacyItem(CompassIP), + new LegacyItem(CompassMM), + new LegacyItem(CompassTR), + new LegacyItem(CompassGT), + }); + } + + foreach (var item in itemPool) item.LegacyWorld = legacyWorld; + + return itemPool; + } + + public static List CreateKeycards(LegacyWorld legacyWorld) { + return new List { + new LegacyItem(CardCrateriaL1, legacyWorld), + new LegacyItem(CardCrateriaL2, legacyWorld), + new LegacyItem(CardCrateriaBoss, legacyWorld), + new LegacyItem(CardBrinstarL1, legacyWorld), + new LegacyItem(CardBrinstarL2, legacyWorld), + new LegacyItem(CardBrinstarBoss, legacyWorld), + new LegacyItem(CardNorfairL1, legacyWorld), + new LegacyItem(CardNorfairL2, legacyWorld), + new LegacyItem(CardNorfairBoss, legacyWorld), + new LegacyItem(CardMaridiaL1, legacyWorld), + new LegacyItem(CardMaridiaL2, legacyWorld), + new LegacyItem(CardMaridiaBoss, legacyWorld), + new LegacyItem(CardWreckedShipL1, legacyWorld), + new LegacyItem(CardWreckedShipBoss, legacyWorld), + new LegacyItem(CardLowerNorfairL1, legacyWorld), + new LegacyItem(CardLowerNorfairBoss, legacyWorld), + }; + } + + public static List CreateSmMaps(LegacyWorld legacyWorld) { + return new List { + new LegacyItem(SmMapBrinstar, legacyWorld), + new LegacyItem(SmMapWreckedShip, legacyWorld), + new LegacyItem(SmMapMaridia, legacyWorld), + new LegacyItem(SmMapLowerNorfair, legacyWorld), + }; + } + + static List Copies(int nr, Func template) { + return Enumerable.Range(1, nr).Select(i => template()).ToList(); + } + + } + + static class ItemsExtensions { + + public static LegacyItem Get(this IEnumerable items, LegacyItemType legacyItemType) { + var item = items.FirstOrDefault(i => i.Type == legacyItemType); + if (item == null) + throw new InvalidOperationException($"Could not find an item of type {legacyItemType}"); + return item; + } + + public static LegacyItem Get(this IEnumerable items, LegacyItemType legacyItemType, LegacyWorld legacyWorld) { + var item = items.FirstOrDefault(i => i.Is(legacyItemType, legacyWorld)); + if (item == null) + throw new InvalidOperationException($"Could not find an item of type {legacyItemType} in world {legacyWorld.Id}"); + return item; + } + + } + + public class LegacyProgression { + + public bool BigKeyEP { get; private set; } + public bool BigKeyDP { get; private set; } + public bool BigKeyTH { get; private set; } + public bool BigKeyPD { get; private set; } + public bool BigKeySP { get; private set; } + public bool BigKeySW { get; private set; } + public bool BigKeyTT { get; private set; } + public bool BigKeyIP { get; private set; } + public bool BigKeyMM { get; private set; } + public bool BigKeyTR { get; private set; } + public bool BigKeyGT { get; private set; } + public bool KeyHC { get; private set; } + public bool KeyDP { get; private set; } + public bool KeyTH { get; private set; } + public bool KeySP { get; private set; } + public bool KeyTT { get; private set; } + public int KeyCT { get; private set; } + public int KeyPD { get; private set; } + public int KeySW { get; private set; } + public int KeyIP { get; private set; } + public int KeyMM { get; private set; } + public int KeyTR { get; private set; } + public int KeyGT { get; private set; } + public bool CardCrateriaL1 { get; private set; } + public bool CardCrateriaL2 { get; private set; } + public bool CardCrateriaBoss { get; private set; } + public bool CardBrinstarL1 { get; private set; } + public bool CardBrinstarL2 { get; private set; } + public bool CardBrinstarBoss { get; private set; } + public bool CardNorfairL1 { get; private set; } + public bool CardNorfairL2 { get; private set; } + public bool CardNorfairBoss { get; private set; } + public bool CardMaridiaL1 { get; private set; } + public bool CardMaridiaL2 { get; private set; } + public bool CardMaridiaBoss { get; private set; } + public bool CardWreckedShipL1 { get; private set; } + public bool CardWreckedShipBoss { get; private set; } + public bool CardLowerNorfairL1 { get; private set; } + public bool CardLowerNorfairBoss { get; private set; } + public bool CanBlockLasers { get { return shield >= 3; } } + public bool Sword { get; private set; } + public bool MasterSword { get; private set; } + public bool Bow { get; private set; } + public bool Hookshot { get; private set; } + public bool Mushroom { get; private set; } + public bool Powder { get; private set; } + public bool Firerod { get; private set; } + public bool Icerod { get; private set; } + public bool Bombos { get; private set; } + public bool Ether { get; private set; } + public bool Quake { get; private set; } + public bool Lamp { get; private set; } + public bool Hammer { get; private set; } + public bool Shovel { get; private set; } + public bool Flute { get; private set; } + public bool Book { get; private set; } + public bool Bottle { get; private set; } + public bool Somaria { get; private set; } + public bool Byrna { get; private set; } + public bool Cape { get; private set; } + public bool Mirror { get; private set; } + public bool Boots { get; private set; } + public bool Glove { get; private set; } + public bool Mitt { get; private set; } + public bool Flippers { get; private set; } + public bool MoonPearl { get; private set; } + public bool HalfMagic { get; private set; } + public bool Grapple { get; private set; } + public bool Charge { get; private set; } + public bool Ice { get; private set; } + public bool Wave { get; private set; } + public bool Plasma { get; private set; } + public bool Varia { get; private set; } + public bool Gravity { get; private set; } + public bool Morph { get; private set; } + public bool Bombs { get; private set; } + public bool SpringBall { get; private set; } + public bool ScrewAttack { get; private set; } + public bool HiJump { get; private set; } + public bool SpaceJump { get; private set; } + public bool SpeedBooster { get; private set; } + public bool Missile { get; private set; } + public bool Super { get; private set; } + public bool PowerBomb { get; private set; } + public bool TwoPowerBombs { get; private set; } + public int ETank { get; private set; } + public int ReserveTank { get; private set; } + + int shield; + + public LegacyProgression(List itemTypeValues) + { + Add(itemTypeValues.Select(x => new LegacyItem((LegacyItemType)x))); + } + + internal LegacyProgression(IEnumerable items) { + Add(items); + } + + internal void Add(IEnumerable items) { + foreach (var item in items) { + bool done = item.Type switch { + LegacyItemType.BigKeyEP => BigKeyEP = true, + LegacyItemType.BigKeyDP => BigKeyDP = true, + LegacyItemType.BigKeyTH => BigKeyTH = true, + LegacyItemType.BigKeyPD => BigKeyPD = true, + LegacyItemType.BigKeySP => BigKeySP = true, + LegacyItemType.BigKeySW => BigKeySW = true, + LegacyItemType.BigKeyTT => BigKeyTT = true, + LegacyItemType.BigKeyIP => BigKeyIP = true, + LegacyItemType.BigKeyMM => BigKeyMM = true, + LegacyItemType.BigKeyTR => BigKeyTR = true, + LegacyItemType.BigKeyGT => BigKeyGT = true, + LegacyItemType.KeyHC => KeyHC = true, + LegacyItemType.KeyDP => KeyDP = true, + LegacyItemType.KeyTH => KeyTH = true, + LegacyItemType.KeySP => KeySP = true, + LegacyItemType.KeyTT => KeyTT = true, + LegacyItemType.CardCrateriaL1 => CardCrateriaL1 = true, + LegacyItemType.CardCrateriaL2 => CardCrateriaL2 = true, + LegacyItemType.CardCrateriaBoss => CardCrateriaBoss = true, + LegacyItemType.CardBrinstarL1 => CardBrinstarL1 = true, + LegacyItemType.CardBrinstarL2 => CardBrinstarL2 = true, + LegacyItemType.CardBrinstarBoss => CardBrinstarBoss = true, + LegacyItemType.CardNorfairL1 => CardNorfairL1 = true, + LegacyItemType.CardNorfairL2 => CardNorfairL2 = true, + LegacyItemType.CardNorfairBoss => CardNorfairBoss = true, + LegacyItemType.CardMaridiaL1 => CardMaridiaL1 = true, + LegacyItemType.CardMaridiaL2 => CardMaridiaL2 = true, + LegacyItemType.CardMaridiaBoss => CardMaridiaBoss = true, + LegacyItemType.CardWreckedShipL1 => CardWreckedShipL1 = true, + LegacyItemType.CardWreckedShipBoss => CardWreckedShipBoss = true, + LegacyItemType.CardLowerNorfairL1 => CardLowerNorfairL1 = true, + LegacyItemType.CardLowerNorfairBoss => CardLowerNorfairBoss = true, + LegacyItemType.Bow => Bow = true, + LegacyItemType.Hookshot => Hookshot = true, + LegacyItemType.Mushroom => Mushroom = true, + LegacyItemType.Powder => Powder = true, + LegacyItemType.Firerod => Firerod = true, + LegacyItemType.Icerod => Icerod = true, + LegacyItemType.Bombos => Bombos = true, + LegacyItemType.Ether => Ether = true, + LegacyItemType.Quake => Quake = true, + LegacyItemType.Lamp => Lamp = true, + LegacyItemType.Hammer => Hammer = true, + LegacyItemType.Shovel => Shovel = true, + LegacyItemType.Flute => Flute = true, + LegacyItemType.Book => Book = true, + LegacyItemType.Bottle => Bottle = true, + LegacyItemType.Somaria => Somaria = true, + LegacyItemType.Byrna => Byrna = true, + LegacyItemType.Cape => Cape = true, + LegacyItemType.Mirror => Mirror = true, + LegacyItemType.Boots => Boots = true, + LegacyItemType.Flippers => Flippers = true, + LegacyItemType.MoonPearl => MoonPearl = true, + LegacyItemType.HalfMagic => HalfMagic = true, + LegacyItemType.Grapple => Grapple = true, + LegacyItemType.Charge => Charge = true, + LegacyItemType.Ice => Ice = true, + LegacyItemType.Wave => Wave = true, + LegacyItemType.Plasma => Plasma = true, + LegacyItemType.Varia => Varia = true, + LegacyItemType.Gravity => Gravity = true, + LegacyItemType.Morph => Morph = true, + LegacyItemType.Bombs => Bombs = true, + LegacyItemType.SpringBall => SpringBall = true, + LegacyItemType.ScrewAttack => ScrewAttack = true, + LegacyItemType.HiJump => HiJump = true, + LegacyItemType.SpaceJump => SpaceJump = true, + LegacyItemType.SpeedBooster => SpeedBooster = true, + LegacyItemType.Missile => Missile = true, + LegacyItemType.Super => Super = true, + _ => false + }; + + if (done) + continue; + + switch (item.Type) { + case LegacyItemType.KeyCT: KeyCT += 1; break; + case LegacyItemType.KeyPD: KeyPD += 1; break; + case LegacyItemType.KeySW: KeySW += 1; break; + case LegacyItemType.KeyIP: KeyIP += 1; break; + case LegacyItemType.KeyMM: KeyMM += 1; break; + case LegacyItemType.KeyTR: KeyTR += 1; break; + case LegacyItemType.KeyGT: KeyGT += 1; break; + case LegacyItemType.ETank: ETank += 1; break; + case LegacyItemType.ReserveTank: ReserveTank += 1; break; + case ProgressiveShield: shield += 1; break; + case ProgressiveSword: + MasterSword = Sword; + Sword = true; + break; + case ProgressiveGlove: + Mitt = Glove; + Glove = true; + break; + case LegacyItemType.PowerBomb: + TwoPowerBombs = PowerBomb; + PowerBomb = true; + break; + } + } + } + } + + static class ProgressionExtensions { + + public static bool CanLiftLight(this LegacyProgression items) => items.Glove; + public static bool CanLiftHeavy(this LegacyProgression items) => items.Mitt; + + public static bool CanLightTorches(this LegacyProgression items) { + return items.Firerod || items.Lamp; + } + + public static bool CanMeltFreezors(this LegacyProgression items) { + return items.Firerod || items.Bombos && items.Sword; + } + + public static bool CanExtendMagic(this LegacyProgression items, int bars = 2) { + return (items.HalfMagic ? 2 : 1) * (items.Bottle ? 2 : 1) >= bars; + } + + public static bool CanKillManyEnemies(this LegacyProgression items) { + return items.Sword || items.Hammer || items.Bow || items.Firerod || + items.Somaria || items.Byrna && items.CanExtendMagic(); + } + + public static bool CanAccessDeathMountainPortal(this LegacyProgression items) { + return (items.CanDestroyBombWalls() || items.SpeedBooster) && items.Super && items.Morph; + } + + public static bool CanAccessDarkWorldPortal(this LegacyProgression items, LegacyConfig legacyConfig) { + return legacyConfig.LegacySmLogic switch { + Normal => + items.CardMaridiaL1 && items.CardMaridiaL2 && items.CanUsePowerBombs() && items.Super && items.Gravity && items.SpeedBooster, + _ => + items.CardMaridiaL1 && items.CardMaridiaL2 && items.CanUsePowerBombs() && items.Super && + (items.Charge || items.Super && items.Missile) && + (items.Gravity || items.HiJump && items.Ice && items.Grapple) && + (items.Ice || items.Gravity && items.SpeedBooster) + }; + } + + public static bool CanAccessMiseryMirePortal(this LegacyProgression items, LegacyConfig legacyConfig) { + return legacyConfig.LegacySmLogic switch { + Normal => + (items.CardNorfairL2 || items.SpeedBooster && items.Wave) && items.Varia && items.Super && + items.Gravity && items.SpaceJump && items.CanUsePowerBombs(), + _ => + (items.CardNorfairL2 || items.SpeedBooster) && items.Varia && items.Super && ( + items.CanFly() || items.HiJump || items.SpeedBooster || items.CanSpringBallJump() || items.Ice + ) && (items.Gravity || items.HiJump) && items.CanUsePowerBombs() + }; + } + + public static bool CanIbj(this LegacyProgression items) { + return items.Morph && items.Bombs; + } + + public static bool CanFly(this LegacyProgression items) { + return items.SpaceJump || items.CanIbj(); + } + + public static bool CanUsePowerBombs(this LegacyProgression items) { + return items.Morph && items.PowerBomb; + } + + public static bool CanPassBombPassages(this LegacyProgression items) { + return items.Morph && (items.Bombs || items.PowerBomb); + } + + public static bool CanDestroyBombWalls(this LegacyProgression items) { + return items.CanPassBombPassages() || items.ScrewAttack; + } + + public static bool CanSpringBallJump(this LegacyProgression items) { + return items.Morph && items.SpringBall; + } + + public static bool CanHellRun(this LegacyProgression items) { + return items.Varia || items.HasEnergyReserves(5); + } + + public static bool HasEnergyReserves(this LegacyProgression items, int amount) { + return (items.ETank + items.ReserveTank) >= amount; + } + + public static bool CanOpenRedDoors(this LegacyProgression items) { + return items.Missile || items.Super; + } + + public static bool CanAccessNorfairUpperPortal(this LegacyProgression items) { + return items.Flute || items.CanLiftLight() && items.Lamp; + } + + public static bool CanAccessNorfairLowerPortal(this LegacyProgression items) { + return items.Flute && items.CanLiftHeavy(); + } + + public static bool CanAccessMaridiaPortal(this LegacyProgression items, LegacyWorld legacyWorld) { + return legacyWorld.LegacyConfig.LegacySmLogic switch { + Normal => + items.MoonPearl && items.Flippers && + items.Gravity && items.Morph && + (legacyWorld.CanAcquire(items, Agahnim) || items.Hammer && items.CanLiftLight() || items.CanLiftHeavy()), + _ => + items.MoonPearl && items.Flippers && + (items.CanSpringBallJump() || items.HiJump || items.Gravity) && items.Morph && + (legacyWorld.CanAcquire(items, Agahnim) || items.Hammer && items.CanLiftLight() || items.CanLiftHeavy()) + }; + } + + } + + public record ItemAddress { + public int Address { get; init; } + public int Value { get; init; } = 0x01; + public bool Bitflag { get; init; } = false; + public bool Additive { get; init; } = false; + } + + static class ItemTypeExtensions { + public static ItemAddress[] ItemAddress(this LegacyItemType legacyItemType) { + return legacyItemType switch { + Bow => new[] { new ItemAddress { Address = 0x403000 }, new ItemAddress { Address = 0x40304E, Value = 0b1000000, Bitflag = true } }, + SilverArrows => new[] { new ItemAddress { Address = 0x40304E, Value = 0b01000000, Bitflag = true } }, + BlueBoomerang => new[] { new ItemAddress { Address = 0x403001 }, new ItemAddress { Address = 0x40304C, Value = 0b1000000, Bitflag = true } }, + RedBoomerang => new[] { new ItemAddress { Address = 0x403001, Value = 0x02 }, new ItemAddress { Address = 0x40304C, Value = 0b0100000, Bitflag = true } }, + Hookshot => new[] { new ItemAddress { Address = 0x403002 } }, + ThreeBombs => new[] { new ItemAddress { Address = 0x403003, Value = 0x03, Additive = true }, new ItemAddress { Address = 0x40304D, Value = 0b00000010, Bitflag = true } }, + Mushroom => new[] { new ItemAddress { Address = 0x403004, Value = 0x01 }, new ItemAddress { Address = 0x40304C, Value = 0b00101000, Bitflag = true } }, + Powder => new[] { new ItemAddress { Address = 0x403004, Value = 0x02 }, new ItemAddress { Address = 0x40304C, Value = 0b00010000, Bitflag = true } }, + Firerod => new[] { new ItemAddress { Address = 0x403005 } }, + Icerod => new[] { new ItemAddress { Address = 0x403006 } }, + Bombos => new[] { new ItemAddress { Address = 0x403007 } }, + Ether => new[] { new ItemAddress { Address = 0x403008 } }, + Quake => new[] { new ItemAddress { Address = 0x403009 } }, + Lamp => new[] { new ItemAddress { Address = 0x40300A } }, + Hammer => new[] { new ItemAddress { Address = 0x40300B } }, + Shovel => new[] { new ItemAddress { Address = 0x40300C }, new ItemAddress { Address = 0x40304C, Value = 0b00000100, Bitflag = true } }, + Flute => new[] { new ItemAddress { Address = 0x40300C, Value = 0x03 }, new ItemAddress { Address = 0x40304C, Value = 0b00000001, Bitflag = true } }, + Bugnet => new[] { new ItemAddress { Address = 0x40300D } }, + Book => new[] { new ItemAddress { Address = 0x40300E } }, + Somaria => new[] { new ItemAddress { Address = 0x403010 } }, + Byrna => new[] { new ItemAddress { Address = 0x403011 } }, + Cape => new[] { new ItemAddress { Address = 0x403012 } }, + Mirror => new[] { new ItemAddress { Address = 0x403013, Value = 0x02 } }, + ProgressiveGlove => new[] { new ItemAddress { Address = 0x403014, Value = 0x01, Additive = true } }, + Boots => new[] { new ItemAddress { Address = 0x403015 }, new ItemAddress { Address = 0x403039, Value = 0b00000100, Bitflag = true } }, + Flippers => new[] { new ItemAddress { Address = 0x403016 }, new ItemAddress { Address = 0x403039, Value = 0b00000010, Bitflag = true } }, + MoonPearl => new[] { new ItemAddress { Address = 0x403017 } }, + ProgressiveSword => new[] { new ItemAddress { Address = 0x400043, Value = 0x01, Additive = true } }, + ProgressiveShield => new[] { new ItemAddress { Address = 0x40301A, Value = 0x01, Additive = true } }, + ProgressiveTunic => new[] { new ItemAddress { Address = 0x40301B, Value = 0x01, Additive = true } }, + Bottle => new[] { new ItemAddress { Address = 0x40301C, Value = 0x02 }, new ItemAddress { Address = 0x40300F, Value = 0x01 } }, + OneRupee => new[] { new ItemAddress { Address = 0x403020, Value = 0x01, Additive = true } }, + FiveRupees => new[] { new ItemAddress { Address = 0x403020, Value = 0x05, Additive = true } }, + TwentyRupees => new[] { new ItemAddress { Address = 0x403020, Value = 0x14, Additive = true } }, + TwentyRupees2 => new[] { new ItemAddress { Address = 0x403020, Value = 0x14, Additive = true } }, + FiftyRupees => new[] { new ItemAddress { Address = 0x403020, Value = 0x32, Additive = true } }, + OneHundredRupees => new[] { new ItemAddress { Address = 0x403020, Value = 0x64, Additive = true } }, + ThreeHundredRupees => new[] { new ItemAddress { Address = 0x403020, Value = 0x12C, Additive = true } }, + + Missile => new[] { new ItemAddress { Address = 0xF26106, Value = 0x05, Additive = true } }, + Super => new[] { new ItemAddress { Address = 0xF26108, Value = 0x05, Additive = true } }, + PowerBomb => new[] { new ItemAddress { Address = 0xF2610A, Value = 0x05, Additive = true } }, + + Varia => new[] { new ItemAddress { Address = 0xF26100, Value = 0x1, Bitflag = true } }, + SpringBall => new[] { new ItemAddress { Address = 0xF26100, Value = 0x2, Bitflag = true } }, + Morph => new[] { new ItemAddress { Address = 0xF26100, Value = 0x4, Bitflag = true } }, + ScrewAttack => new[] { new ItemAddress { Address = 0xF26100, Value = 0x8, Bitflag = true } }, + Gravity => new[] { new ItemAddress { Address = 0xF26100, Value = 0x20, Bitflag = true } }, + HiJump => new[] { new ItemAddress { Address = 0xF26100, Value = 0x100, Bitflag = true } }, + SpaceJump => new[] { new ItemAddress { Address = 0xF26100, Value = 0x200, Bitflag = true } }, + Bombs => new[] { new ItemAddress { Address = 0xF26100, Value = 0x1000, Bitflag = true } }, + SpeedBooster => new[] { new ItemAddress { Address = 0xF26100, Value = 0x2000, Bitflag = true } }, + Grapple => new[] { new ItemAddress { Address = 0xF26100, Value = 0x4000, Bitflag = true } }, + XRay => new[] { new ItemAddress { Address = 0xF26100, Value = 0x8000, Bitflag = true } }, + + Wave => new[] { new ItemAddress { Address = 0xF26102, Value = 0x1, Bitflag = true } }, + Ice => new[] { new ItemAddress { Address = 0xF26102, Value = 0x2, Bitflag = true } }, + Spazer => new[] { new ItemAddress { Address = 0xF26102, Value = 0x4, Bitflag = true } }, + Plasma => new[] { new ItemAddress { Address = 0xF26102, Value = 0x8, Bitflag = true } }, + Charge => new[] { new ItemAddress { Address = 0xF26102, Value = 0x1000, Bitflag = true } }, + + ETank => new[] { new ItemAddress { Address = 0xF26104, Value = 0x64, Additive = true } }, + _ => null + }; + } + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/LegacyLocation.cs b/src/TrackerCouncil.Smz3.LegacyLogic/LegacyLocation.cs new file mode 100644 index 000000000..a52d28319 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/LegacyLocation.cs @@ -0,0 +1,129 @@ +using System; +using System.Collections.Generic; +using System.Linq; + +namespace Randomizer.SMZ3 { + + enum LocationType { + Regular, + HeraStandingKey, + Pedestal, + Ether, + Bombos, + NotInDungeon, + + Visible, + Chozo, + Hidden + } + + delegate bool Requirement(LegacyProgression items); + delegate bool Verification(LegacyItem legacyItem, LegacyProgression items); + + class LegacyLocation { + + public int Id { get; set; } + public string Name { get; set; } + public LocationType Type { get; set; } + public int Address { get; set; } + public LegacyItem LegacyItem { get; set; } + public LegacyRegion LegacyRegion { get; set; } + + public int Weight { + get { return weight ?? LegacyRegion.Weight; } + } + + readonly Requirement canAccess; + Verification alwaysAllow; + Verification allow; + int? weight; + + public bool ItemIs(LegacyItemType type, LegacyWorld legacyWorld) => LegacyItem?.Is(type, legacyWorld) ?? false; + public bool ItemIsNot(LegacyItemType type, LegacyWorld legacyWorld) => !ItemIs(type, legacyWorld); + + public LegacyLocation(LegacyRegion legacyRegion, int id, int address, LocationType type, string name) + : this(legacyRegion, id, address, type, name, items => true) { + } + + public LegacyLocation(LegacyRegion legacyRegion, int id, int address, LocationType type, string name, Requirement access) { + LegacyRegion = legacyRegion; + Id = id; + Name = name; + Type = type; + Address = address; + canAccess = access; + alwaysAllow = (item, items) => false; + allow = (item, items) => true; + } + + public LegacyLocation Weighted(int? weight) { + this.weight = weight; + return this; + } + + public LegacyLocation AlwaysAllow(Verification allow) { + alwaysAllow = allow; + return this; + } + + public LegacyLocation Allow(Verification allow) { + this.allow = allow; + return this; + } + + public bool Available(LegacyProgression items) { + return LegacyRegion.CanEnter(items) && canAccess(items); + } + + public bool CanFill(LegacyItem legacyItem, LegacyProgression items) { + var oldItem = LegacyItem; + LegacyItem = legacyItem; + bool fillable = alwaysAllow(legacyItem, items) || (LegacyRegion.CanFill(legacyItem, items) && allow(legacyItem, items) && Available(items)); + LegacyItem = oldItem; + return fillable; + } + + // For logic unit tests + internal bool CanAccess(LegacyProgression items) => canAccess(items); + + } + + static class LocationsExtensions { + + public static LegacyLocation Get(this IEnumerable locations, string name) { + var location = locations.FirstOrDefault(l => l.Name == name); + if (location == null) + throw new ArgumentException($"Could not find location name {name}", nameof(name)); + return location; + } + + public static IEnumerable Empty(this IEnumerable locations) { + return locations.Where(l => l.LegacyItem == null); + } + + public static IEnumerable Filled(this IEnumerable locations) { + return locations.Where(l => l.LegacyItem != null); + } + + public static IEnumerable AvailableWithinWorld(this IEnumerable locations, IEnumerable items) { + return locations.Select(x => x.LegacyRegion.LegacyWorld).Distinct().SelectMany(world => + locations.Where(l => l.LegacyRegion.LegacyWorld == world).Available(items.Where(i => i.LegacyWorld == world))); + } + + public static IEnumerable Available(this IEnumerable locations, IEnumerable items) { + var progression = new LegacyProgression(items); + return locations.Where(l => l.Available(progression)); + } + + public static IEnumerable CanFillWithinWorld(this IEnumerable locations, LegacyItem legacyItem, IEnumerable items) { + var itemWorldProgression = new LegacyProgression(items.Where(i => i.LegacyWorld == legacyItem.LegacyWorld).Append(legacyItem)); + var worldProgression = locations.Select(x => x.LegacyRegion.LegacyWorld).Distinct().ToDictionary(world => world.Id, world => new LegacyProgression(items.Where(i => i.LegacyWorld == world))); + + return locations.Where(l => + l.CanFill(legacyItem, worldProgression[l.LegacyRegion.LegacyWorld.Id]) && + legacyItem.LegacyWorld.Locations.Find(ll => ll.Id == l.Id).Available(itemWorldProgression)); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/LegacyRegion.cs b/src/TrackerCouncil.Smz3.LegacyLogic/LegacyRegion.cs new file mode 100644 index 000000000..cb8ec345f --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/LegacyRegion.cs @@ -0,0 +1,96 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Linq; +using static Randomizer.SMZ3.LegacyWorldState; + +namespace Randomizer.SMZ3 { + + [Flags] + public enum LegacyRewardType { + [Description("None")] + None, + [Description("Agahnim")] + Agahnim = 1 << 0, + [Description("Green Pendant")] + PendantGreen = 1 << 1, + [Description("Blue/Red Pendant")] + PendantNonGreen = 1 << 2, + [Description("Blue Crystal")] + CrystalBlue = 1 << 3, + [Description("Red Crystal")] + CrystalRed = 1 << 4, + [Description("Kraid Boss Token")] + BossTokenKraid = 1 << 5, + [Description("Phantoon Boss Token")] + BossTokenPhantoon = 1 << 6, + [Description("Draygon Boss Token")] + BossTokenDraygon = 1 << 7, + [Description("Ridley Boss Token")] + BossTokenRidley = 1 << 8, + + AnyPendant = PendantGreen | PendantNonGreen, + AnyCrystal = CrystalBlue | CrystalRed, + AnyBossToken = BossTokenKraid | BossTokenPhantoon + | BossTokenDraygon | BossTokenRidley, + } + + interface ILegacyReward { + LegacyRewardType LegacyReward { get; set; } + bool CanComplete(LegacyProgression items); + } + + interface ILegacyMedallionAccess { + LegacyMedallion LegacyMedallion { get; set; } + } + + abstract class LegacySMRegion : LegacyRegion { + public LegacySMLogic Logic => LegacyConfig.LegacySmLogic; + public LegacySMRegion(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { } + } + + abstract class LegacyZ3Region : LegacyRegion { + public LegacyZ3Region(LegacyWorld legacyWorld, LegacyConfig legacyConfig) + : base(legacyWorld, legacyConfig) { } + } + + abstract class LegacyRegion { + + public virtual string Name { get; } + public virtual string Area => Name; + + public List Locations { get; set; } + public LegacyWorld LegacyWorld { get; set; } + public int Weight { get; set; } = 0; + + protected LegacyConfig LegacyConfig { get; set; } + protected IList RegionItems { get; set; } = new List(); + + private Dictionary locationLookup { get; set; } + public LegacyLocation GetLocation(string name) => locationLookup[name]; + + public LegacyRegion(LegacyWorld legacyWorld, LegacyConfig legacyConfig) { + LegacyConfig = legacyConfig; + LegacyWorld = legacyWorld; + locationLookup = new Dictionary(); + } + + public void GenerateLocationLookup() { + locationLookup = Locations.ToDictionary(l => l.Name, l => l); + } + + public bool IsRegionItem(LegacyItem legacyItem) { + return RegionItems.Contains(legacyItem.Type); + } + + public virtual bool CanFill(LegacyItem legacyItem, LegacyProgression items) { + return LegacyConfig.Keysanity || !legacyItem.IsDungeonItem || IsRegionItem(legacyItem); + } + + public virtual bool CanEnter(LegacyProgression items) { + return true; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/LegacyWorld.cs b/src/TrackerCouncil.Smz3.LegacyLogic/LegacyWorld.cs new file mode 100644 index 000000000..84ca39ca1 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/LegacyWorld.cs @@ -0,0 +1,189 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using static Randomizer.SMZ3.LegacyRewardType; +using static Randomizer.SMZ3.LegacyWorldState; + +namespace Randomizer.SMZ3 { + + public class LegacyWorld { + + internal List Locations { get; set; } + internal List Regions { get; set; } + public LegacyConfig LegacyConfig { get; set; } + public string Player { get; set; } + public string Guid { get; set; } + public int Id { get; set; } + public LegacyWorldState LegacyWorldState { get; set; } + + public int TowerCrystals => LegacyWorldState?.TowerCrystals ?? 7; + public int GanonCrystals => LegacyWorldState?.GanonCrystals ?? 7; + public int TourianBossTokens => LegacyWorldState?.TourianBossTokens ?? 4; + + internal IEnumerable Items { + get { return Locations.Select(l => l.LegacyItem).Where(i => i != null); } + } + + public bool ForwardSearch { get; set; } = false; + + private Dictionary rewardLookup { get; set; } + private Dictionary locationLookup { get; set; } + private Dictionary regionLookup { get; set; } + + internal LegacyLocation GetLocation(string name) => locationLookup[name]; + internal LegacyRegion GetRegion(string name) => regionLookup[name]; + + public LegacyWorld(LegacyConfig legacyConfig, string player, int id, string guid) { + LegacyConfig = legacyConfig; + Player = player; + Id = id; + Guid = guid; + + Regions = new List { + new Regions.Zelda.LegacyCastleTower(this, LegacyConfig), + new Regions.Zelda.LegacyEasternPalace(this, LegacyConfig), + new Regions.Zelda.LegacyDesertPalace(this, LegacyConfig), + new Regions.Zelda.LegacyTowerOfHera(this, LegacyConfig), + new Regions.Zelda.LegacyPalaceOfDarkness(this, LegacyConfig), + new Regions.Zelda.LegacySwampPalace(this, LegacyConfig), + new Regions.Zelda.LegacySkullWoods(this, LegacyConfig), + new Regions.Zelda.LegacyThievesTown(this, LegacyConfig), + new Regions.Zelda.LegacyIcePalace(this, LegacyConfig), + new Regions.Zelda.LegacyMiseryMire(this, LegacyConfig), + new Regions.Zelda.LegacyTurtleRock(this, LegacyConfig), + new Regions.Zelda.LegacyGanonsTower(this, LegacyConfig), + new Regions.Zelda.LightWorld.DeathMountain.LegacyWest(this, LegacyConfig), + new Regions.Zelda.LightWorld.DeathMountain.LegacyEast(this, LegacyConfig), + new Regions.Zelda.LightWorld.LegacyNorthWest(this, LegacyConfig), + new Regions.Zelda.LightWorld.LegacyNorthEast(this, LegacyConfig), + new Regions.Zelda.LightWorld.LegacySouth(this, LegacyConfig), + new Regions.Zelda.LegacyHyruleCastle(this, LegacyConfig), + new Regions.Zelda.DarkWorld.DeathMountain.LegacyWest(this, LegacyConfig), + new Regions.Zelda.DarkWorld.DeathMountain.LegacyEast(this, LegacyConfig), + new Regions.Zelda.DarkWorld.LegacyNorthWest(this, LegacyConfig), + new Regions.Zelda.DarkWorld.LegacyNorthEast(this, LegacyConfig), + new Regions.Zelda.DarkWorld.LegacySouth(this, LegacyConfig), + new Regions.Zelda.DarkWorld.LegacyMire(this, LegacyConfig), + new Regions.SuperMetroid.Crateria.LegacyWest(this, LegacyConfig), + new Regions.SuperMetroid.Crateria.LegacyCentral(this, LegacyConfig), + new Regions.SuperMetroid.Crateria.LegacyEast(this, LegacyConfig), + new Regions.SuperMetroid.Brinstar.LegacyBlue(this, LegacyConfig), + new Regions.SuperMetroid.Brinstar.LegacyGreen(this, LegacyConfig), + new Regions.SuperMetroid.Brinstar.LegacyPink(this, LegacyConfig), + new Regions.SuperMetroid.Brinstar.LegacyRed(this, LegacyConfig), + new Regions.SuperMetroid.Brinstar.LegacyKraid(this, LegacyConfig), + new Regions.SuperMetroid.LegacyWreckedShip(this, LegacyConfig), + new Regions.SuperMetroid.Maridia.LegacyOuter(this, LegacyConfig), + new Regions.SuperMetroid.Maridia.LegacyInner(this, LegacyConfig), + new Regions.SuperMetroid.NorfairUpper.LegacyWest(this, LegacyConfig), + new Regions.SuperMetroid.NorfairUpper.LegacyEast(this, LegacyConfig), + new Regions.SuperMetroid.NorfairUpper.LegacyCrocomire(this, LegacyConfig), + new Regions.SuperMetroid.NorfairLower.LegacyWest(this, LegacyConfig), + new Regions.SuperMetroid.NorfairLower.LegacyEast(this, LegacyConfig), + }; + + Locations = Regions.SelectMany(x => x.Locations).ToList(); + + regionLookup = Regions.ToDictionary(r => r.Name, r => r); + locationLookup = Locations.ToDictionary(l => l.Name, l => l); + + foreach(var region in Regions) { + region.GenerateLocationLookup(); + } + } + + public bool CanEnter(string regionName, LegacyProgression items) { + var region = regionLookup[regionName]; + if (region == null) + throw new ArgumentException($"World.CanEnter: Invalid region name {regionName}", nameof(regionName)); + return region.CanEnter(items); + } + + public bool CanAcquire(LegacyProgression items, LegacyRewardType legacyReward) { + // For the purpose of logic unit tests, if no region has the reward then CanAcquire is satisfied + return Regions.OfType().FirstOrDefault(x => legacyReward == x.LegacyReward)?.CanComplete(items) ?? false; + } + + public bool CanAcquireAll(LegacyProgression items, LegacyRewardType legacyRewardsMask) + { + var count = (legacyRewardsMask & CrystalRed) == CrystalRed ? 2 : + (legacyRewardsMask & AnyCrystal) == CrystalBlue ? 7 : + (legacyRewardsMask & AnyPendant) == AnyPendant ? 3 : + (legacyRewardsMask & AnyBossToken) == AnyBossToken ? 4 : + 1; + return CanAcquireAtLeast(count, items, legacyRewardsMask); + } + + public bool CanAcquireAtLeast(int amount, LegacyProgression items, LegacyRewardType legacyRewardsMask) { + return rewardLookup[(int)legacyRewardsMask].Where(x => x.CanComplete(items)).Count() >= amount; + } + + public void Setup(LegacyWorldState state) { + LegacyWorldState = state; + SetRewards(state.Rewards); + SetMedallions(state.Medallions); + SetRewardLookup(); + } + + void SetRewards(IEnumerable rewards) { + var regions = Regions.OfType().Where(x => x.LegacyReward == None); + foreach (var (region, reward) in regions.Zip(rewards)) { + region.LegacyReward = reward; + } + } + + void SetMedallions(IEnumerable medallions) { + (GetRegion("Misery Mire") as ILegacyMedallionAccess).LegacyMedallion = medallions.First(); + (GetRegion("Turtle Rock") as ILegacyMedallionAccess).LegacyMedallion = medallions.Skip(1).First(); + } + + void SetRewardLookup() { + /* Generate a lookup of all possible regions for any given reward combination for faster lookup later */ + rewardLookup = new Dictionary(); + for (var i = 0; i < 512; i += 1) { + rewardLookup.Add(i, Regions.OfType().Where(x => (((int)x.LegacyReward) & i) != 0).ToArray()); + } + } + + private static Dictionary s_locationIdMappings = new() + { + { 256 + 196, 256 + 230 }, // GanonsTowerRandomizerRoomTopLeft + { 256 + 197, 256 + 231 }, // GanonsTowerRandomizerRoomTopRight + { 256 + 198, 256 + 232 }, // GanonsTowerRandomizerRoomBottomLeft + { 256 + 199, 256 + 233 }, // GanonsTowerRandomizerRoomBottomRight + { 256 + 200, 256 + 234 }, // GanonsTowerHopeRoomLeft + { 256 + 201, 256 + 235 }, // GanonsTowerHopeRoomRight + { 256 + 202, 256 + 236 }, // GanonsTowerTileRoom + }; + + public bool IsLocationAccessible(int id, LegacyProgression items) + { + if (s_locationIdMappings.TryGetValue(id, out var newId)) + { + id = newId; + } + + var legacyLocation = Locations.First(x => x.Id == id); + + return legacyLocation.Available(items); + } + + public bool CanCompleteRegion(string name, int? locationId, LegacyProgression items) + { + if (regionLookup.TryGetValue(name, out var regionByName) && regionByName is ILegacyReward rewardRegion) + { + return rewardRegion.CanComplete(items); + } + + var legacyLocation = Locations.FirstOrDefault(x => x.Id == locationId); + if (legacyLocation?.LegacyRegion is ILegacyReward locationRegion) + { + return locationRegion.CanComplete(items); + } + + return false; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/LegacyWorldState.cs b/src/TrackerCouncil.Smz3.LegacyLogic/LegacyWorldState.cs new file mode 100644 index 000000000..fc17f2863 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/LegacyWorldState.cs @@ -0,0 +1,26 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using static Randomizer.SMZ3.LegacyRewardType; + +namespace Randomizer.SMZ3 { + + public class LegacyWorldState { + + public enum LegacyMedallion { + Bombos, + Ether, + Quake, + } + + public IEnumerable Rewards { get; init; } + public IEnumerable Medallions { get; init; } + + public int TowerCrystals { get; init; } + public int GanonCrystals { get; init; } + public int TourianBossTokens { get; init; } + + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Brinstar/LegacyBlue.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Brinstar/LegacyBlue.cs new file mode 100644 index 000000000..e6d10bc64 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Brinstar/LegacyBlue.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacySMLogic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid.Brinstar { + + class LegacyBlue : LegacySMRegion { + + public override string Name => "Brinstar Blue"; + public override string Area => "Brinstar"; + + public LegacyBlue(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 26, 0x8F86EC, LocationType.Visible, "Morphing Ball"), + new LegacyLocation(this, 27, 0x8F874C, LocationType.Visible, "Power Bomb (blue Brinstar)", Logic switch { + _ => new Requirement(items => items.CanUsePowerBombs()) + }), + new LegacyLocation(this, 28, 0x8F8798, LocationType.Visible, "Missile (blue Brinstar middle)", Logic switch { + _ => new Requirement(items => items.CardBrinstarL1 && items.Morph) + }), + new LegacyLocation(this, 29, 0x8F879E, LocationType.Hidden, "Energy Tank, Brinstar Ceiling", Logic switch { + Normal => items => items.CardBrinstarL1 && (items.CanFly() || items.HiJump || items.SpeedBooster || items.Ice), + _ => new Requirement(items => items.CardBrinstarL1) + }), + new LegacyLocation(this, 34, 0x8F8802, LocationType.Chozo, "Missile (blue Brinstar bottom)", Logic switch { + _ => new Requirement(items => items.Morph) + }), + new LegacyLocation(this, 36, 0x8F8836, LocationType.Visible, "Missile (blue Brinstar top)", Logic switch { + _ => new Requirement(items => items.CardBrinstarL1 && items.CanUsePowerBombs()) + }), + new LegacyLocation(this, 37, 0x8F883C, LocationType.Hidden, "Missile (blue Brinstar behind missile)", Logic switch { + _ => new Requirement(items => items.CardBrinstarL1 && items.CanUsePowerBombs()) + }), + }; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Brinstar/LegacyGreen.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Brinstar/LegacyGreen.cs new file mode 100644 index 000000000..240870316 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Brinstar/LegacyGreen.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacySMLogic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid.Brinstar { + + class LegacyGreen : LegacySMRegion { + + public override string Name => "Brinstar Green"; + public override string Area => "Brinstar"; + + public LegacyGreen(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Weight = -6; + + Locations = new List { + new LegacyLocation(this, 13, 0x8F84AC, LocationType.Chozo, "Power Bomb (green Brinstar bottom)", Logic switch { + _ => new Requirement(items => items.CardBrinstarL2 && items.CanUsePowerBombs()) + }), + new LegacyLocation(this, 15, 0x8F8518, LocationType.Visible, "Missile (green Brinstar below super missile)", Logic switch { + _ => new Requirement(items => items.CanPassBombPassages() && items.CanOpenRedDoors()) + }), + new LegacyLocation(this, 16, 0x8F851E, LocationType.Visible, "Super Missile (green Brinstar top)", Logic switch { + Normal => items => items.CanOpenRedDoors() && items.SpeedBooster, + _ => new Requirement(items => items.CanOpenRedDoors() && (items.Morph || items.SpeedBooster)) + }), + new LegacyLocation(this, 17, 0x8F852C, LocationType.Chozo, "Reserve Tank, Brinstar", Logic switch { + Normal => items => items.CanOpenRedDoors() && items.SpeedBooster, + _ => new Requirement(items => items.CanOpenRedDoors() && (items.Morph || items.SpeedBooster)) + }), + new LegacyLocation(this, 18, 0x8F8532, LocationType.Hidden, "Missile (green Brinstar behind missile)", Logic switch { + Normal => items => items.SpeedBooster && items.CanPassBombPassages() && items.CanOpenRedDoors(), + _ => new Requirement(items => (items.CanPassBombPassages() || items.Morph && items.ScrewAttack) && items.CanOpenRedDoors()) + }), + new LegacyLocation(this, 19, 0x8F8538, LocationType.Visible, "Missile (green Brinstar behind reserve tank)", Logic switch { + Normal => items => items.SpeedBooster && items.CanOpenRedDoors() && items.Morph, + _ => new Requirement(items => items.CanOpenRedDoors() && items.Morph) + }), + new LegacyLocation(this, 30, 0x8F87C2, LocationType.Visible, "Energy Tank, Etecoons", Logic switch { + _ => new Requirement(items => items.CardBrinstarL2 && items.CanUsePowerBombs()) + }), + new LegacyLocation(this, 31, 0x8F87D0, LocationType.Visible, "Super Missile (green Brinstar bottom)", Logic switch { + _ => new Requirement(items => items.CardBrinstarL2 && items.CanUsePowerBombs() && items.Super) + }), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return items.CanDestroyBombWalls() || items.SpeedBooster; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Brinstar/LegacyKraid.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Brinstar/LegacyKraid.cs new file mode 100644 index 000000000..1887d3976 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Brinstar/LegacyKraid.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid.Brinstar { + + class LegacyKraid : LegacySMRegion, ILegacyReward { + + public override string Name => "Brinstar Kraid"; + public override string Area => "Brinstar"; + + public LegacyRewardType LegacyReward { get; set; } = LegacyRewardType.None; + + public LegacyKraid(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 43, 0x8F899C, LocationType.Hidden, "Energy Tank, Kraid", + items => items.CardBrinstarBoss), + new LegacyLocation(this, 48, 0x8F8ACA, LocationType.Chozo, "Varia Suit", + items => items.CardBrinstarBoss), + new LegacyLocation(this, 44, 0x8F89EC, LocationType.Hidden, "Missile (Kraid)", Logic switch { + _ => new Requirement(items => items.CanUsePowerBombs()) + }), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return (items.CanDestroyBombWalls() || items.SpeedBooster || items.CanAccessNorfairUpperPortal()) && + items.Super && items.CanPassBombPassages(); + } + + public bool CanComplete(LegacyProgression items) { + return GetLocation("Varia Suit").Available(items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Brinstar/LegacyPink.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Brinstar/LegacyPink.cs new file mode 100644 index 000000000..4074732f0 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Brinstar/LegacyPink.cs @@ -0,0 +1,60 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacySMLogic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid.Brinstar { + + class LegacyPink : LegacySMRegion { + + public override string Name => "Brinstar Pink"; + public override string Area => "Brinstar"; + + public LegacyPink(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Weight = -4; + + Locations = new List { + new LegacyLocation(this, 14, 0x8F84E4, LocationType.Chozo, "Super Missile (pink Brinstar)", Logic switch { + Normal => new Requirement(items => items.CardBrinstarBoss && items.CanPassBombPassages() && items.Super), + _ => new Requirement(items => (items.CardBrinstarBoss || items.CardBrinstarL2) && items.CanPassBombPassages() && items.Super) + }), + new LegacyLocation(this, 21, 0x8F8608, LocationType.Visible, "Missile (pink Brinstar top)"), + new LegacyLocation(this, 22, 0x8F860E, LocationType.Visible, "Missile (pink Brinstar bottom)"), + new LegacyLocation(this, 23, 0x8F8614, LocationType.Chozo, "Charge Beam", Logic switch { + _ => new Requirement(items => items.CanPassBombPassages()) + }), + new LegacyLocation(this, 24, 0x8F865C, LocationType.Visible, "Power Bomb (pink Brinstar)", Logic switch { + Normal => items => items.CanUsePowerBombs() && items.Super && items.HasEnergyReserves(1), + _ => new Requirement(items => items.CanUsePowerBombs() && items.Super) + }), + new LegacyLocation(this, 25, 0x8F8676, LocationType.Visible, "Missile (green Brinstar pipe)", Logic switch { + _ => new Requirement(items => items.Morph && + (items.PowerBomb || items.Super || items.CanAccessNorfairUpperPortal())) + }), + new LegacyLocation(this, 33, 0x8F87FA, LocationType.Visible, "Energy Tank, Waterway", Logic switch { + _ => new Requirement(items => items.CanUsePowerBombs() && items.CanOpenRedDoors() && items.SpeedBooster && + (items.HasEnergyReserves(1) || items.Gravity)) + }), + new LegacyLocation(this, 35, 0x8F8824, LocationType.Visible, "Energy Tank, Brinstar Gate", Logic switch { + Normal => items => items.CardBrinstarL2 && items.CanUsePowerBombs() && items.Wave && items.HasEnergyReserves(1), + _ => new Requirement(items => items.CardBrinstarL2 && items.CanUsePowerBombs() && (items.Wave || items.Super)) + }), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return Logic switch { + Normal => + items.CanOpenRedDoors() && (items.CanDestroyBombWalls() || items.SpeedBooster) || + items.CanUsePowerBombs() || + items.CanAccessNorfairUpperPortal() && items.Morph && items.Wave && + (items.Ice || items.HiJump || items.SpaceJump), + _ => + items.CanOpenRedDoors() && (items.CanDestroyBombWalls() || items.SpeedBooster) || + items.CanUsePowerBombs() || + items.CanAccessNorfairUpperPortal() && items.Morph && (items.Missile || items.Super || items.Wave /* Blue Gate */) && + (items.Ice || items.HiJump || items.CanSpringBallJump() || items.CanFly()) + }; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Brinstar/LegacyRed.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Brinstar/LegacyRed.cs new file mode 100644 index 000000000..12df3bf33 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Brinstar/LegacyRed.cs @@ -0,0 +1,49 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacySMLogic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid.Brinstar { + + class LegacyRed : LegacySMRegion { + + public override string Name => "Brinstar Red"; + public override string Area => "Brinstar"; + + public LegacyRed(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 38, 0x8F8876, LocationType.Chozo, "X-Ray Scope", Logic switch { + Normal => items => items.CanUsePowerBombs() && items.CanOpenRedDoors() && (items.Grapple || items.SpaceJump), + _ => new Requirement(items => items.CanUsePowerBombs() && items.CanOpenRedDoors() && ( + items.Grapple || items.SpaceJump || + (items.CanIbj() || items.HiJump && items.SpeedBooster || items.CanSpringBallJump()) && + (items.Varia && items.HasEnergyReserves(3) || items.HasEnergyReserves(5)))) + }), + new LegacyLocation(this, 39, 0x8F88CA, LocationType.Visible, "Power Bomb (red Brinstar sidehopper room)", Logic switch { + _ => new Requirement(items => items.CanUsePowerBombs() && items.Super) + }), + new LegacyLocation(this, 40, 0x8F890E, LocationType.Chozo, "Power Bomb (red Brinstar spike room)", Logic switch { + Normal => items => (items.CanUsePowerBombs() || items.Ice) && items.Super, + _ => new Requirement(items => items.Super) + }), + new LegacyLocation(this, 41, 0x8F8914, LocationType.Visible, "Missile (red Brinstar spike room)", Logic switch { + _ => new Requirement(items => items.CanUsePowerBombs() && items.Super) + }), + new LegacyLocation(this, 42, 0x8F896E, LocationType.Chozo, "Spazer", Logic switch { + _ => new Requirement(items => items.CanPassBombPassages() && items.Super) + }), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return Logic switch { + Normal => + (items.CanDestroyBombWalls() || items.SpeedBooster) && items.Super && items.Morph || + items.CanAccessNorfairUpperPortal() && (items.Ice || items.HiJump || items.SpaceJump), + _ => + (items.CanDestroyBombWalls() || items.SpeedBooster) && items.Super && items.Morph || + items.CanAccessNorfairUpperPortal() && (items.Ice || items.CanSpringBallJump() || items.HiJump || items.CanFly()) + }; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Crateria/LegacyCentral.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Crateria/LegacyCentral.cs new file mode 100644 index 000000000..634194a17 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Crateria/LegacyCentral.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacySMLogic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid.Crateria { + + class LegacyCentral : LegacySMRegion { + + public override string Name => "Crateria Central"; + public override string Area => "Crateria"; + + public LegacyCentral(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 0, 0x8F81CC, LocationType.Visible, "Power Bomb (Crateria surface)", Logic switch { + _ => new Requirement(items => (legacyConfig.Keysanity ? items.CardCrateriaL1 : items.CanUsePowerBombs()) && (items.SpeedBooster || items.CanFly())) + }), + new LegacyLocation(this, 12, 0x8F8486, LocationType.Visible, "Missile (Crateria middle)", Logic switch { + _ => new Requirement(items => items.CanPassBombPassages()) + }), + new LegacyLocation(this, 6, 0x8F83EE, LocationType.Visible, "Missile (Crateria bottom)", Logic switch { + _ => new Requirement(items => items.CanDestroyBombWalls()) + }), + new LegacyLocation(this, 11, 0x8F8478, LocationType.Visible, "Super Missile (Crateria)", Logic switch { + _ => new Requirement(items => items.CanUsePowerBombs() && items.HasEnergyReserves(2) && items.SpeedBooster) + }), + new LegacyLocation(this, 7, 0x8F8404, LocationType.Chozo, "Bombs", Logic switch { + Normal => items => (legacyConfig.Keysanity ? items.CardCrateriaBoss : items.CanOpenRedDoors()) && items.CanPassBombPassages(), + _ => new Requirement(items => (legacyConfig.Keysanity ? items.CardCrateriaBoss : items.CanOpenRedDoors()) && items.Morph) + }) + }; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Crateria/LegacyEast.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Crateria/LegacyEast.cs new file mode 100644 index 000000000..fd98118d9 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Crateria/LegacyEast.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacySMLogic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid.Crateria { + + class LegacyEast : LegacySMRegion { + + public override string Name => "Crateria East"; + public override string Area => "Crateria"; + + public LegacyEast(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 1, 0x8F81E8, LocationType.Visible, "Missile (outside Wrecked Ship bottom)", Logic switch { + Normal => items => items.Morph && ( + items.SpeedBooster || items.Grapple || items.SpaceJump || + items.Gravity && (items.CanIbj() || items.HiJump) || + LegacyWorld.CanEnter("Wrecked Ship", items) + ), + _ => new Requirement(items => items.Morph) + }), + new LegacyLocation(this, 2, 0x8F81EE, LocationType.Hidden, "Missile (outside Wrecked Ship top)", Logic switch { + _ => new Requirement(items => LegacyWorld.CanEnter("Wrecked Ship", items) && items.CardWreckedShipBoss && items.CanPassBombPassages()) + }), + new LegacyLocation(this, 3, 0x8F81F4, LocationType.Visible, "Missile (outside Wrecked Ship middle)", Logic switch { + _ => new Requirement(items => LegacyWorld.CanEnter("Wrecked Ship", items) && items.CardWreckedShipBoss && items.CanPassBombPassages()) + }), + new LegacyLocation(this, 4, 0x8F8248, LocationType.Visible, "Missile (Crateria moat)", Logic switch { + _ => new Requirement(items => true) + }), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return Logic switch { + Normal => + /* Ship -> Moat */ + (LegacyConfig.Keysanity ? items.CardCrateriaL2 : items.CanUsePowerBombs()) && items.Super || + /* UN Portal -> Red Tower -> Moat */ + (LegacyConfig.Keysanity ? items.CardCrateriaL2 : items.CanUsePowerBombs()) && items.CanAccessNorfairUpperPortal() && + (items.Ice || items.HiJump || items.SpaceJump) || + /* Through Maridia From Portal */ + items.CanAccessMaridiaPortal(LegacyWorld) && items.Gravity && items.Super && ( + /* Oasis -> Forgotten Highway */ + items.CardMaridiaL2 && items.CanDestroyBombWalls() || + /* Draygon -> Cactus Alley -> Forgotten Highway */ + LegacyWorld.GetLocation("Space Jump").Available(items) + ) || + /*Through Maridia from Pipe*/ + items.CanUsePowerBombs() && items.Super && items.Gravity, + _ => + /* Ship -> Moat */ + (LegacyConfig.Keysanity ? items.CardCrateriaL2 : items.CanUsePowerBombs()) && items.Super || + /* UN Portal -> Red Tower -> Moat */ + (LegacyConfig.Keysanity ? items.CardCrateriaL2 : items.CanUsePowerBombs()) && items.CanAccessNorfairUpperPortal() && + (items.Ice || items.HiJump || items.CanFly() || items.CanSpringBallJump()) || + /* Through Maridia From Portal */ + items.CanAccessMaridiaPortal(LegacyWorld) && ( + /* Oasis -> Forgotten Highway */ + items.CardMaridiaL2 && items.Super && ( + items.HiJump && items.CanPassBombPassages() || + items.Gravity && items.CanDestroyBombWalls() + ) || + /* Draygon -> Cactus Alley -> Forgotten Highway */ + items.Gravity && LegacyWorld.GetLocation("Space Jump").Available(items) + ) || + /* Through Maridia from Pipe */ + items.CanUsePowerBombs() && items.Super && ( + items.Gravity || + items.HiJump && (items.Ice || items.CanSpringBallJump()) && items.Grapple && items.CardMaridiaL1 + ), + }; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Crateria/LegacyWest.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Crateria/LegacyWest.cs new file mode 100644 index 000000000..5fd91e6f1 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Crateria/LegacyWest.cs @@ -0,0 +1,52 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacySMLogic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid.Crateria { + + class LegacyWest : LegacySMRegion { + + public override string Name => "Crateria West"; + public override string Area => "Crateria"; + + public LegacyWest(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 8, 0x8F8432, LocationType.Visible, "Energy Tank, Terminator"), + new LegacyLocation(this, 5, 0x8F8264, LocationType.Visible, "Energy Tank, Gauntlet", Logic switch { + Normal => items => CanEnterAndLeaveGauntlet(items) && items.HasEnergyReserves(1), + _ => new Requirement(items => CanEnterAndLeaveGauntlet(items)) + }), + new LegacyLocation(this, 9, 0x8F8464, LocationType.Visible, "Missile (Crateria gauntlet right)", Logic switch { + Normal => items => CanEnterAndLeaveGauntlet(items) && items.CanPassBombPassages() && items.HasEnergyReserves(2), + _ => new Requirement(items => CanEnterAndLeaveGauntlet(items) && items.CanPassBombPassages()) + }), + new LegacyLocation(this, 10, 0x8F846A, LocationType.Visible, "Missile (Crateria gauntlet left)", Logic switch { + Normal => items => CanEnterAndLeaveGauntlet(items) && items.CanPassBombPassages() && items.HasEnergyReserves(2), + _ => new Requirement(items => CanEnterAndLeaveGauntlet(items) && items.CanPassBombPassages()) + }) + }; + } + + public override bool CanEnter(LegacyProgression items) { + return items.CanDestroyBombWalls() || items.SpeedBooster; + } + + private bool CanEnterAndLeaveGauntlet(LegacyProgression items) { + return Logic switch { + Normal => + items.CardCrateriaL1 && items.Morph && (items.CanFly() || items.SpeedBooster) && ( + items.CanIbj() || + items.CanUsePowerBombs() && items.TwoPowerBombs || + items.ScrewAttack + ), + _ => + items.CardCrateriaL1 && ( + items.Morph && (items.Bombs || items.TwoPowerBombs) || + items.ScrewAttack || + items.SpeedBooster && items.CanUsePowerBombs() && items.HasEnergyReserves(2) + ) + }; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/LegacyWreckedShip.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/LegacyWreckedShip.cs new file mode 100644 index 000000000..53d2d3e2c --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/LegacyWreckedShip.cs @@ -0,0 +1,102 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacySMLogic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid { + + class LegacyWreckedShip : LegacySMRegion, ILegacyReward { + + public override string Name => "Wrecked Ship"; + public override string Area => "Wrecked Ship"; + + public LegacyRewardType LegacyReward { get; set; } = LegacyRewardType.None; + + public LegacyWreckedShip(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Weight = 4; + + Locations = new List { + new LegacyLocation(this, 128, 0x8FC265, LocationType.Visible, "Missile (Wrecked Ship middle)", Logic switch { + _ => new Requirement(items => items.CanPassBombPassages()) + }), + new LegacyLocation(this, 129, 0x8FC2E9, LocationType.Chozo, "Reserve Tank, Wrecked Ship", Logic switch { + Normal => items => CanUnlockShip(items) && items.CardWreckedShipL1 && items.CanUsePowerBombs() && items.SpeedBooster && + (items.Grapple || items.SpaceJump || items.Varia && items.HasEnergyReserves(2) || items.HasEnergyReserves(3)), + _ => new Requirement(items => CanUnlockShip(items) && items.CardWreckedShipL1 && items.CanUsePowerBombs() && items.SpeedBooster && + (items.Varia || items.HasEnergyReserves(2))) + }), + new LegacyLocation(this, 130, 0x8FC2EF, LocationType.Visible, "Missile (Gravity Suit)", Logic switch { + Normal => items => CanUnlockShip(items) && items.CardWreckedShipL1 && + (items.Grapple || items.SpaceJump || items.Varia && items.HasEnergyReserves(2) || items.HasEnergyReserves(3)), + _ => new Requirement(items => CanUnlockShip(items) && items.CardWreckedShipL1 && (items.Varia || items.HasEnergyReserves(1))) + }), + new LegacyLocation(this, 131, 0x8FC319, LocationType.Visible, "Missile (Wrecked Ship top)", + items => CanUnlockShip(items)), + new LegacyLocation(this, 132, 0x8FC337, LocationType.Visible, "Energy Tank, Wrecked Ship", Logic switch { + Normal => items => CanUnlockShip(items) && + (items.HiJump || items.SpaceJump || items.SpeedBooster || items.Gravity), + _ => new Requirement(items => CanUnlockShip(items) && ( + items.Morph && (items.Bombs || items.PowerBomb) /* "OnceBJ" */ || items.CanSpringBallJump() || + items.HiJump || items.SpaceJump || items.SpeedBooster || items.Gravity + )), + }), + new LegacyLocation(this, 133, 0x8FC357, LocationType.Visible, "Super Missile (Wrecked Ship left)", + items => CanUnlockShip(items)), + new LegacyLocation(this, 134, 0x8FC365, LocationType.Visible, "Right Super, Wrecked Ship", + items => CanUnlockShip(items)), + new LegacyLocation(this, 135, 0x8FC36D, LocationType.Chozo, "Gravity Suit", Logic switch { + Normal => items => CanUnlockShip(items) && items.CardWreckedShipL1 && + (items.Grapple || items.SpaceJump || items.Varia && items.HasEnergyReserves(2) || items.HasEnergyReserves(3)), + _ => new Requirement(items => CanUnlockShip(items) && items.CardWreckedShipL1 && (items.Varia || items.HasEnergyReserves(1))) + }) + }; + } + + bool CanUnlockShip(LegacyProgression items) { + return items.CardWreckedShipBoss && items.CanPassBombPassages(); + } + + public override bool CanEnter(LegacyProgression items) { + return Logic switch { + Normal => + items.Super && ( + /* Over the Moat */ + (LegacyConfig.Keysanity ? items.CardCrateriaL2 : items.CanUsePowerBombs()) && ( + items.SpeedBooster || items.Grapple || items.SpaceJump || + items.Gravity && (items.CanIbj() || items.HiJump) + ) || + /* Through Maridia -> Forgotten Highway */ + items.CanUsePowerBombs() && items.Gravity || + /* From Maridia portal -> Forgotten Highway */ + items.CanAccessMaridiaPortal(LegacyWorld) && items.Gravity && ( + items.CanDestroyBombWalls() && items.CardMaridiaL2 || + LegacyWorld.GetLocation("Space Jump").Available(items) + ) + ), + _ => + items.Super && ( + /* Over the Moat */ + (LegacyConfig.Keysanity ? items.CardCrateriaL2 : items.CanUsePowerBombs()) || + /* Through Maridia -> Forgotten Highway */ + items.CanUsePowerBombs() && ( + items.Gravity || + /* Climb Mt. Everest */ + items.HiJump && (items.Ice || items.CanSpringBallJump()) && items.Grapple && items.CardMaridiaL1 + ) || + /* From Maridia portal -> Forgotten Highway */ + items.CanAccessMaridiaPortal(LegacyWorld) && ( + items.HiJump && items.CanPassBombPassages() && items.CardMaridiaL2 || + items.Gravity && ( + items.CanDestroyBombWalls() && items.CardMaridiaL2 || + LegacyWorld.GetLocation("Space Jump").Available(items) + ) + ) + ), + }; + } + + public bool CanComplete(LegacyProgression items) { + return CanEnter(items) && CanUnlockShip(items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Maridia/LegacyInner.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Maridia/LegacyInner.cs new file mode 100644 index 000000000..a2fa0ff27 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Maridia/LegacyInner.cs @@ -0,0 +1,135 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacySMLogic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid.Maridia { + + class LegacyInner : LegacySMRegion, ILegacyReward { + + public override string Name => "Maridia Inner"; + public override string Area => "Maridia"; + + public LegacyRewardType LegacyReward { get; set; } = LegacyRewardType.None; + + public LegacyInner(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 140, 0x8FC4AF, LocationType.Visible, "Super Missile (yellow Maridia)", Logic switch { + _ => new Requirement(items => items.CardMaridiaL1 && items.CanPassBombPassages() && CanReachAqueduct(items) + && (items.Gravity || items.Ice || items.HiJump && items.SpringBall)) + }), + new LegacyLocation(this, 141, 0x8FC4B5, LocationType.Visible, "Missile (yellow Maridia super missile)", Logic switch { + _ => new Requirement(items => items.CardMaridiaL1 && items.CanPassBombPassages() && CanReachAqueduct(items) + && (items.Gravity || items.Ice || items.HiJump && items.SpringBall)) + }), + new LegacyLocation(this, 142, 0x8FC533, LocationType.Visible, "Missile (yellow Maridia false wall)", Logic switch { + _ => new Requirement(items => items.CardMaridiaL1 && items.CanPassBombPassages() && CanReachAqueduct(items) + && (items.Gravity || items.Ice || items.HiJump && items.SpringBall)) + }), + new LegacyLocation(this, 143, 0x8FC559, LocationType.Chozo, "Plasma Beam", Logic switch { + Normal => items => CanDefeatDraygon(items) && (items.ScrewAttack || items.Plasma) && (items.HiJump || items.CanFly()), + _ => new Requirement(items => CanDefeatDraygon(items) && + (items.Charge && items.HasEnergyReserves(3) || items.ScrewAttack || items.Plasma || items.SpeedBooster) && + (items.HiJump || items.CanSpringBallJump() || items.CanFly() || items.SpeedBooster)) + }), + new LegacyLocation(this, 144, 0x8FC5DD, LocationType.Visible, "Missile (left Maridia sand pit room)", Logic switch { + Normal => items => CanReachAqueduct(items) && items.Super && items.CanPassBombPassages(), + _ => new Requirement(items => CanReachAqueduct(items) && items.Super && + (items.Gravity || items.HiJump && (items.SpaceJump || items.CanSpringBallJump()))) + }), + new LegacyLocation(this, 145, 0x8FC5E3, LocationType.Chozo, "Reserve Tank, Maridia", Logic switch { + Normal => items => CanReachAqueduct(items) && items.Super && items.CanPassBombPassages(), + _ => new Requirement(items => CanReachAqueduct(items) && items.Super && + (items.Gravity || items.HiJump && (items.SpaceJump || items.CanSpringBallJump()))) + }), + new LegacyLocation(this, 146, 0x8FC5EB, LocationType.Visible, "Missile (right Maridia sand pit room)", Logic switch { + Normal => new Requirement(items => CanReachAqueduct(items) && items.Super), + _ => items => CanReachAqueduct(items) && items.Super && (items.HiJump || items.Gravity) + }), + new LegacyLocation(this, 147, 0x8FC5F1, LocationType.Visible, "Power Bomb (right Maridia sand pit room)", Logic switch { + Normal => new Requirement(items => CanReachAqueduct(items) && items.Super), + _ => items => CanReachAqueduct(items) && items.Super && (items.Gravity || items.HiJump && items.CanSpringBallJump()) + }), + new LegacyLocation(this, 148, 0x8FC603, LocationType.Visible, "Missile (pink Maridia)", Logic switch { + Normal => items => CanReachAqueduct(items) && items.SpeedBooster, + _ => new Requirement(items => CanReachAqueduct(items) && items.Gravity) + }), + new LegacyLocation(this, 149, 0x8FC609, LocationType.Visible, "Super Missile (pink Maridia)", Logic switch { + Normal => items => CanReachAqueduct(items) && items.SpeedBooster, + _ => new Requirement(items => CanReachAqueduct(items) && items.Gravity) + }), + new LegacyLocation(this, 150, 0x8FC6E5, LocationType.Chozo, "Spring Ball", Logic switch { + Normal => items => items.Super && items.Grapple && items.CanUsePowerBombs() && (items.SpaceJump || items.HiJump), + _ => new Requirement(items => items.Super && items.Grapple && items.CanUsePowerBombs() && ( + items.Gravity && (items.CanFly() || items.HiJump) || + items.Ice && items.HiJump && items.CanSpringBallJump() && items.SpaceJump) + ) + }), + new LegacyLocation(this, 151, 0x8FC74D, LocationType.Hidden, "Missile (Draygon)", Logic switch { + Normal => items => + items.CardMaridiaL1 && items.CardMaridiaL2 && CanDefeatBotwoon(items) || + items.CanAccessMaridiaPortal(LegacyWorld), + _ => new Requirement(items => ( + items.CardMaridiaL1 && items.CardMaridiaL2 && CanDefeatBotwoon(items) || + items.CanAccessMaridiaPortal(LegacyWorld) + ) && items.Gravity) + }), + new LegacyLocation(this, 152, 0x8FC755, LocationType.Visible, "Energy Tank, Botwoon", Logic switch { + _ => new Requirement(items => + items.CardMaridiaL1 && items.CardMaridiaL2 && CanDefeatBotwoon(items) || + items.CanAccessMaridiaPortal(LegacyWorld) && items.CardMaridiaL2) + }), + new LegacyLocation(this, 154, 0x8FC7A7, LocationType.Chozo, "Space Jump", Logic switch { + _ => new Requirement(items => CanDefeatDraygon(items)) + }) + }; + } + + bool CanReachAqueduct(LegacyProgression items) { + return Logic switch { + Normal => items.CardMaridiaL1 && (items.CanFly() || items.SpeedBooster || items.Grapple) + || items.CardMaridiaL2 && items.CanAccessMaridiaPortal(LegacyWorld), + _ => items.CardMaridiaL1 && (items.Gravity || items.HiJump && (items.Ice || items.CanSpringBallJump()) && items.Grapple) + || items.CardMaridiaL2 && items.CanAccessMaridiaPortal(LegacyWorld) + }; + } + + bool CanDefeatDraygon(LegacyProgression items) { + return Logic switch { + Normal => ( + items.CardMaridiaL1 && items.CardMaridiaL2 && CanDefeatBotwoon(items) || + items.CanAccessMaridiaPortal(LegacyWorld) + ) && items.CardMaridiaBoss && items.Gravity && (items.SpeedBooster && items.HiJump || items.CanFly()), + _ => ( + items.CardMaridiaL1 && items.CardMaridiaL2 && CanDefeatBotwoon(items) || + items.CanAccessMaridiaPortal(LegacyWorld) + ) && items.CardMaridiaBoss && items.Gravity + }; + } + + bool CanDefeatBotwoon(LegacyProgression items) { + return Logic switch { + Normal => items.SpeedBooster || items.CanAccessMaridiaPortal(LegacyWorld), + _ => items.Ice || items.SpeedBooster && items.Gravity || items.CanAccessMaridiaPortal(LegacyWorld) + }; + } + + public override bool CanEnter(LegacyProgression items) { + return Logic switch { + Normal => items.Gravity && ( + LegacyWorld.CanEnter("Norfair Upper West", items) && items.Super && items.CanUsePowerBombs() && + (items.CanFly() || items.SpeedBooster || items.Grapple) || + items.CanAccessMaridiaPortal(LegacyWorld) + ), + _ => + items.Super && LegacyWorld.CanEnter("Norfair Upper West", items) && items.CanUsePowerBombs() && + (items.Gravity || items.HiJump && (items.Ice || items.CanSpringBallJump()) && items.Grapple) || + items.CanAccessMaridiaPortal(LegacyWorld) + }; + } + + public bool CanComplete(LegacyProgression items) { + return GetLocation("Space Jump").Available(items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Maridia/LegacyOuter.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Maridia/LegacyOuter.cs new file mode 100644 index 000000000..f5ddca00c --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/Maridia/LegacyOuter.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacySMLogic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid.Maridia { + + class LegacyOuter : LegacySMRegion { + + public override string Name => "Maridia Outer"; + public override string Area => "Maridia"; + + public LegacyOuter(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 136, 0x8FC437, LocationType.Visible, "Missile (green Maridia shinespark)", Logic switch { + Normal => items => items.SpeedBooster, + _ => new Requirement(items => items.Gravity && items.SpeedBooster) + }), + new LegacyLocation(this, 137, 0x8FC43D, LocationType.Visible, "Super Missile (green Maridia)"), + new LegacyLocation(this, 138, 0x8FC47D, LocationType.Visible, "Energy Tank, Mama turtle", Logic switch { + Normal => items => items.CanOpenRedDoors() && (items.CanFly() || items.SpeedBooster || items.Grapple), + _ => new Requirement(items => items.CanOpenRedDoors() && ( + items.CanFly() || items.SpeedBooster || items.Grapple || + items.CanSpringBallJump() && (items.Gravity || items.HiJump) + )) + }), + new LegacyLocation(this, 139, 0x8FC483, LocationType.Hidden, "Missile (green Maridia tatori)", Logic switch { + _ => new Requirement(items => items.CanOpenRedDoors()) + }), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return Logic switch { + Normal => items.Gravity && ( + LegacyWorld.CanEnter("Norfair Upper West", items) && items.CanUsePowerBombs() || + items.CanAccessMaridiaPortal(LegacyWorld) && items.CardMaridiaL1 && items.CardMaridiaL2 && (items.CanPassBombPassages() || items.ScrewAttack) + ), + _ => + LegacyWorld.CanEnter("Norfair Upper West", items) && items.CanUsePowerBombs() && + (items.Gravity || items.HiJump && (items.CanSpringBallJump() || items.Ice)) || + items.CanAccessMaridiaPortal(LegacyWorld) && items.CardMaridiaL1 && items.CardMaridiaL2 && ( + items.CanPassBombPassages() || + items.Gravity && items.ScrewAttack || + items.Super && (items.Gravity || items.HiJump && (items.CanSpringBallJump() || items.Ice)) + ) + }; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/NorfairLower/LegacyEast.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/NorfairLower/LegacyEast.cs new file mode 100644 index 000000000..cf285ee6d --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/NorfairLower/LegacyEast.cs @@ -0,0 +1,82 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacySMLogic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid.NorfairLower { + + class LegacyEast : LegacySMRegion, ILegacyReward { + + public override string Name => "Norfair Lower East"; + public override string Area => "Norfair Lower"; + + public LegacyRewardType LegacyReward { get; set; } = LegacyRewardType.None; + + public LegacyEast(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 74, 0x8F8FCA, LocationType.Visible, "Missile (lower Norfair above fire flea room)", Logic switch { + _ => new Requirement(items => CanExit(items)) + }), + new LegacyLocation(this, 75, 0x8F8FD2, LocationType.Visible, "Power Bomb (lower Norfair above fire flea room)", Logic switch { + Normal => new Requirement(items => CanExit(items)), + _ => items => CanExit(items) && items.CanPassBombPassages() + }), + new LegacyLocation(this, 76, 0x8F90C0, LocationType.Visible, "Power Bomb (Power Bombs of shame)", Logic switch { + _ => new Requirement(items => CanExit(items) && items.CanUsePowerBombs()) + }), + new LegacyLocation(this, 77, 0x8F9100, LocationType.Visible, "Missile (lower Norfair near Wave Beam)", Logic switch { + Normal => new Requirement(items => CanExit(items)), + _ => items => CanExit(items) && items.Morph && items.CanDestroyBombWalls() + }), + new LegacyLocation(this, 78, 0x8F9108, LocationType.Hidden, "Energy Tank, Ridley", Logic switch { + _ => new Requirement(items => CanExit(items) && items.CardLowerNorfairBoss && items.CanUsePowerBombs() && items.Super) + }), + new LegacyLocation(this, 80, 0x8F9184, LocationType.Visible, "Energy Tank, Firefleas", Logic switch { + _ => new Requirement(items => CanExit(items)) + }) + }; + } + + bool CanExit(LegacyProgression items) { + return Logic switch { + Normal => /* Intended LN Escape */ + items.Morph && ( + items.CardNorfairL2 /* Bubble Mountain */ || + items.Gravity && items.Wave /* Volcano Room, Blue Gate */ && + (items.Grapple || items.SpaceJump) /* Spikey Acid Snakes -> Croc Escape (this shortcuts Frog Speedway) */ + ), + _ => /* Intended LN Escape */ + items.Morph && ( + items.CardNorfairL2 /* Bubble Mountain */ || + (items.Missile || items.Super || items.Wave /* Blue Gate */) && ( + items.SpeedBooster || items.CanFly() || items.Grapple || + items.HiJump && (items.CanSpringBallJump() || items.Ice) /* Frog Speedway / Croc Escape */ + ) + ) || + /* Reverse Amphitheater */ + items.HasEnergyReserves(5), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return Logic switch { + Normal => + items.Varia && items.CardLowerNorfairL1 && ( + LegacyWorld.CanEnter("Norfair Upper East", items) && items.CanUsePowerBombs() && items.SpaceJump && items.Gravity || + items.CanAccessNorfairLowerPortal() && items.CanDestroyBombWalls() && items.Super && items.CanUsePowerBombs() && items.CanFly() + ), + _ => + items.Varia && items.CardLowerNorfairL1 && ( + LegacyWorld.CanEnter("Norfair Upper East", items) && items.CanUsePowerBombs() && (items.HiJump || items.Gravity) || + items.CanAccessNorfairLowerPortal() && items.CanDestroyBombWalls() && items.Super && (items.CanFly() || items.CanSpringBallJump() || items.SpeedBooster) + ) && + (items.CanFly() || items.HiJump || items.CanSpringBallJump() || items.Ice && items.Charge) && + (items.CanPassBombPassages() || items.ScrewAttack && items.SpaceJump) + }; + } + + public bool CanComplete(LegacyProgression items) { + return GetLocation("Energy Tank, Ridley").Available(items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/NorfairLower/LegacyWest.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/NorfairLower/LegacyWest.cs new file mode 100644 index 000000000..0f5d58171 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/NorfairLower/LegacyWest.cs @@ -0,0 +1,80 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacySMLogic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid.NorfairLower { + + class LegacyWest : LegacySMRegion { + + public override string Name => "Norfair Lower West"; + public override string Area => "Norfair Lower"; + + public LegacyWest(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 70, 0x8F8E6E, LocationType.Visible, "Missile (Gold Torizo)", Logic switch { + Normal => items => items.CanUsePowerBombs() && items.SpaceJump && items.Super, + _ => new Requirement(items => items.CanUsePowerBombs() && items.SpaceJump && items.Varia && ( + items.HiJump || items.Gravity || + items.CanAccessNorfairLowerPortal() && (items.CanFly() || items.CanSpringBallJump() || items.SpeedBooster) && items.Super + )), + }), + new LegacyLocation(this, 71, 0x8F8E74, LocationType.Hidden, "Super Missile (Gold Torizo)", Logic switch { + Normal => items => items.CanDestroyBombWalls() && (items.Super || items.Charge) && + (items.CanAccessNorfairLowerPortal() || items.CanUsePowerBombs() && items.SpaceJump), + _ => new Requirement(items => items.CanDestroyBombWalls() && items.Varia && (items.Super || items.Charge)) + }), + new LegacyLocation(this, 79, 0x8F9110, LocationType.Chozo, "Screw Attack", Logic switch { + Normal => items => items.CanDestroyBombWalls() && (items.CanAccessNorfairLowerPortal() || items.CanUsePowerBombs() && items.SpaceJump), + _ => new Requirement(items => items.CanDestroyBombWalls() && (items.CanAccessNorfairLowerPortal() || items.Varia)) + }), + new LegacyLocation(this, 73, 0x8F8F30, LocationType.Visible, "Missile (Mickey Mouse room)", Logic switch { + Normal => items => items.Morph && items.Super && + /* Climb worst room (from LN East CanEnter) */ + items.CanFly() && items.CanUsePowerBombs() && ( + /* Exit to Upper Norfair */ + (items.CardLowerNorfairL1 || items.Gravity) /* Intended, or Reverse Lava Dive */ && items.CardNorfairL2 /* Bubble Mountain */ || + items.Gravity && items.Wave /* Volcano Room and Blue Gate */ && + (items.Grapple || items.SpaceJump) /* Spikey Acid Snakes -> Croc Escape (this shortcuts Frog Speedway) */ || + /* GT fight -> Portal */ + items.CanUsePowerBombs() && items.SpaceJump && (items.Super || items.Charge) + ), + _ => new Requirement(items => items.Varia && items.Morph && items.Super && + /* Climb worst room (from LN East CanEnter) */ + (items.CanFly() || items.HiJump || items.CanSpringBallJump() || items.Ice && items.Charge) && + (items.CanPassBombPassages() || items.ScrewAttack && items.SpaceJump) && ( + /* Exit to Upper Norfair */ + items.CardNorfairL2 || items.SpeedBooster || items.CanFly() || items.Grapple || + items.HiJump && (items.CanSpringBallJump() || items.Ice) || + /* Portal (with GGG) */ + items.CanUsePowerBombs() + )), + }), + }; + } + + // Todo: account for Croc Speedway once Norfair Upper East also do so, otherwise it would be inconsistent to do so here + public override bool CanEnter(LegacyProgression items) { + return Logic switch { + Normal => + items.Varia && ( + LegacyWorld.CanEnter("Norfair Upper East", items) && items.CanUsePowerBombs() && items.SpaceJump && items.Gravity && ( + /* Trivial case, Bubble Mountain access */ + items.CardNorfairL2 || + /* Frog Speedway -> UN Farming Room gate */ + items.SpeedBooster && items.Wave + ) || + items.CanAccessNorfairLowerPortal() && items.CanDestroyBombWalls() + ), + _ => + LegacyWorld.CanEnter("Norfair Upper East", items) && items.CanUsePowerBombs() && items.Varia && (items.HiJump || items.Gravity) && ( + /* Trivial case, Bubble Mountain access */ + items.CardNorfairL2 || + /* Frog Speedway -> UN Farming Room gate */ + items.SpeedBooster && (items.Missile || items.Super || items.Wave /* Blue Gate */) + ) || + items.CanAccessNorfairLowerPortal() && items.CanDestroyBombWalls() + }; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/NorfairUpper/LegacyCrocomire.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/NorfairUpper/LegacyCrocomire.cs new file mode 100644 index 000000000..6853489ec --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/NorfairUpper/LegacyCrocomire.cs @@ -0,0 +1,90 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacySMLogic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid.NorfairUpper { + + class LegacyCrocomire : LegacySMRegion { + + public override string Name => "Norfair Upper Crocomire"; + public override string Area => "Norfair Upper"; + + public LegacyCrocomire(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 52, 0x8F8BA4, LocationType.Visible, "Energy Tank, Crocomire", Logic switch { + Normal => items => CanAccessCrocomire(items) && (items.HasEnergyReserves(1) || items.SpaceJump || items.Grapple), + _ => new Requirement(items => CanAccessCrocomire(items)) + }), + new LegacyLocation(this, 54, 0x8F8BC0, LocationType.Visible, "Missile (above Crocomire)", Logic switch { + Normal => items => items.CanFly() || items.Grapple || items.HiJump && items.SpeedBooster, + _ => new Requirement(items => ( + items.CanFly() || items.Grapple || + items.HiJump && (items.SpeedBooster || items.CanSpringBallJump() || items.Varia && items.Ice) + ) && items.CanHellRun()), + }), + new LegacyLocation(this, 57, 0x8F8C04, LocationType.Visible, "Power Bomb (Crocomire)", Logic switch { + Normal => items => CanAccessCrocomire(items) && (items.CanFly() || items.HiJump || items.Grapple), + _ => new Requirement(items => CanAccessCrocomire(items)) + }), + new LegacyLocation(this, 58, 0x8F8C14, LocationType.Visible, "Missile (below Crocomire)", Logic switch { + _ => new Requirement(items => CanAccessCrocomire(items) && items.Morph) + }), + new LegacyLocation(this, 59, 0x8F8C2A, LocationType.Visible, "Missile (Grappling Beam)", Logic switch { + Normal => items => CanAccessCrocomire(items) && items.Morph && (items.CanFly() || items.SpeedBooster && items.CanUsePowerBombs()), + _ => new Requirement(items => CanAccessCrocomire(items) && (items.SpeedBooster || items.Morph && (items.CanFly() || items.Grapple))) + }), + new LegacyLocation(this, 60, 0x8F8C36, LocationType.Chozo, "Grappling Beam", Logic switch { + Normal => items => CanAccessCrocomire(items) && items.Morph && (items.CanFly() || items.SpeedBooster && items.CanUsePowerBombs()), + _ => new Requirement(items => CanAccessCrocomire(items) && ( + items.SpaceJump || items.Morph || items.Grapple || items.HiJump && items.SpeedBooster + )), + }), + }; + } + + bool CanAccessCrocomire(LegacyProgression items) { + return LegacyConfig.Keysanity ? items.CardNorfairBoss : items.Super; + } + + public override bool CanEnter(LegacyProgression items) { + return Logic switch { + Normal => ( + (items.CanDestroyBombWalls() || items.SpeedBooster) && items.Super && items.Morph || + items.CanAccessNorfairUpperPortal() + ) && items.Varia && ( + /* Ice Beam -> Croc Speedway */ + (LegacyConfig.Keysanity ? items.CardNorfairL1 : items.Super) && items.CanUsePowerBombs() && items.SpeedBooster || + /* Frog Speedway */ + items.SpeedBooster && items.Wave || + /* Cathedral -> through the floor or Vulcano */ + items.CanOpenRedDoors() && (LegacyConfig.Keysanity ? items.CardNorfairL2 : items.Super) && + (items.CanFly() || items.HiJump || items.SpeedBooster) && + (items.CanPassBombPassages() || items.Gravity && items.Morph) && items.Wave + ) || + /* Reverse Lava Dive */ + items.Varia && items.CanAccessNorfairLowerPortal() && items.ScrewAttack && items.SpaceJump && items.Super && + items.Gravity && items.Wave && (items.CardNorfairL2 || items.Morph), + _ => ( + (items.CanDestroyBombWalls() || items.SpeedBooster) && items.Super && items.Morph || + items.CanAccessNorfairUpperPortal() + ) && ( + /* Ice Beam -> Croc Speedway */ + (LegacyConfig.Keysanity ? items.CardNorfairL1 : items.Super) && items.CanUsePowerBombs() && + items.SpeedBooster && (items.HasEnergyReserves(3) || items.Varia) || + /* Frog Speedway */ + items.SpeedBooster && (items.HasEnergyReserves(2) || items.Varia) && + (items.Missile || items.Super || items.Wave /* Blue Gate */) || + /* Cathedral -> through the floor or Vulcano */ + items.CanHellRun() && items.CanOpenRedDoors() && (LegacyConfig.Keysanity ? items.CardNorfairL2 : items.Super) && + (items.CanFly() || items.HiJump || items.SpeedBooster || items.CanSpringBallJump() || items.Varia && items.Ice) && + (items.CanPassBombPassages() || items.Varia && items.Morph) && + (items.Missile || items.Super || items.Wave /* Blue Gate */) + ) || + /* Reverse Lava Dive */ + items.Varia && items.CanAccessNorfairLowerPortal() && items.ScrewAttack && items.SpaceJump && items.Super && + items.HasEnergyReserves(2) && (items.CardNorfairL2 || items.Morph) + }; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/NorfairUpper/LegacyEast.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/NorfairUpper/LegacyEast.cs new file mode 100644 index 000000000..170e70d00 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/NorfairUpper/LegacyEast.cs @@ -0,0 +1,112 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacySMLogic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid.NorfairUpper { + + class LegacyEast : LegacySMRegion { + + public override string Name => "Norfair Upper East"; + public override string Area => "Norfair Upper"; + + public LegacyEast(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 61, 0x8F8C3E, LocationType.Chozo, "Reserve Tank, Norfair", Logic switch { + Normal => items => items.CardNorfairL2 && items.Morph && ( + items.CanFly() || + items.Grapple && (items.SpeedBooster || items.CanPassBombPassages()) || + items.HiJump || items.Ice + ), + _ => new Requirement(items => items.CardNorfairL2 && items.Morph && items.Super) + }), + new LegacyLocation(this, 62, 0x8F8C44, LocationType.Hidden, "Missile (Norfair Reserve Tank)", Logic switch { + Normal => items => items.CardNorfairL2 && items.Morph && ( + items.CanFly() || + items.Grapple && (items.SpeedBooster || items.CanPassBombPassages()) || + items.HiJump || items.Ice + ), + _ => new Requirement(items => items.CardNorfairL2 && items.Morph && items.Super) + }), + new LegacyLocation(this, 63, 0x8F8C52, LocationType.Visible, "Missile (bubble Norfair green door)", Logic switch { + Normal => items => items.CardNorfairL2 && ( + items.CanFly() || + items.Grapple && items.Morph && (items.SpeedBooster || items.CanPassBombPassages()) || + items.HiJump || items.Ice + ), + _ => new Requirement(items => items.CardNorfairL2 && items.Super) + }), + new LegacyLocation(this, 64, 0x8F8C66, LocationType.Visible, "Missile (bubble Norfair)", Logic switch { + _ => new Requirement(items => items.CardNorfairL2) + }), + new LegacyLocation(this, 65, 0x8F8C74, LocationType.Hidden, "Missile (Speed Booster)", Logic switch { + Normal => items => items.CardNorfairL2 && ( + items.CanFly() || + items.Morph && (items.SpeedBooster || items.CanPassBombPassages()) || + items.HiJump || items.Ice + ), + _ => new Requirement(items => items.CardNorfairL2 && items.Super) + }), + new LegacyLocation(this, 66, 0x8F8C82, LocationType.Chozo, "Speed Booster", Logic switch { + Normal => items => items.CardNorfairL2 && ( + items.CanFly() || + items.Morph && (items.SpeedBooster || items.CanPassBombPassages()) || + items.HiJump || items.Ice + ), + _ => new Requirement(items => items.CardNorfairL2 && items.Super) + }), + new LegacyLocation(this, 67, 0x8F8CBC, LocationType.Visible, "Missile (Wave Beam)", Logic switch { + Normal => items => items.CardNorfairL2 && ( + items.CanFly() || + items.Morph && (items.SpeedBooster || items.CanPassBombPassages()) || + items.HiJump || items.Ice + ) || + items.SpeedBooster && items.Wave && items.Morph && items.Super, + _ => new Requirement(items => items.CardNorfairL2 || items.Varia) + }), + new LegacyLocation(this, 68, 0x8F8CCA, LocationType.Chozo, "Wave Beam", Logic switch { + Normal => items => items.Morph && ( + items.CardNorfairL2 && ( + items.CanFly() || + items.Morph && (items.SpeedBooster || items.CanPassBombPassages()) || + items.HiJump || items.Ice + ) || + items.SpeedBooster && items.Wave && items.Morph && items.Super + ), + _ => new Requirement(items => items.CanOpenRedDoors() && (items.CardNorfairL2 || items.Varia) && + (items.Morph || items.Grapple || items.HiJump && items.Varia || items.SpaceJump)) + }), + }; + } + + // Todo: Super is not actually needed for Frog Speedway, but changing this will affect locations + // Todo: Ice Beam -> Croc Speedway is not considered + public override bool CanEnter(LegacyProgression items) { + return Logic switch { + Normal => ( + (items.CanDestroyBombWalls() || items.SpeedBooster) && items.Super && items.Morph || + items.CanAccessNorfairUpperPortal() + ) && items.Varia && items.Super && ( + /* Cathedral */ + items.CanOpenRedDoors() && (LegacyConfig.Keysanity ? items.CardNorfairL2 : items.Super) && + (items.CanFly() || items.HiJump || items.SpeedBooster) || + /* Frog Speedway */ + items.SpeedBooster && (items.CardNorfairL2 || items.Wave /* Blue Gate */) && items.CanUsePowerBombs() + ), + _ => ( + (items.CanDestroyBombWalls() || items.SpeedBooster) && items.Super && items.Morph || + items.CanAccessNorfairUpperPortal() + ) && + items.CanHellRun() && ( + /* Cathedral */ + items.CanOpenRedDoors() && (LegacyConfig.Keysanity ? items.CardNorfairL2 : items.Super) && ( + items.CanFly() || items.HiJump || items.SpeedBooster || + items.CanSpringBallJump() || items.Varia && items.Ice + ) || + /* Frog Speedway */ + items.SpeedBooster && (items.CardNorfairL2 || items.Missile || items.Super || items.Wave /* Blue Gate */) && items.CanUsePowerBombs() + ), + }; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/NorfairUpper/LegacyWest.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/NorfairUpper/LegacyWest.cs new file mode 100644 index 000000000..f2d749b23 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/SuperMetroid/NorfairUpper/LegacyWest.cs @@ -0,0 +1,57 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacySMLogic; + +namespace Randomizer.SMZ3.Regions.SuperMetroid.NorfairUpper { + + class LegacyWest : LegacySMRegion { + + public override string Name => "Norfair Upper West"; + public override string Area => "Norfair Upper"; + + public LegacyWest(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 49, 0x8F8AE4, LocationType.Hidden, "Missile (lava room)", Logic switch { + Normal => items => items.Varia && ( + items.CanOpenRedDoors() && (items.CanFly() || items.HiJump || items.SpeedBooster) || + LegacyWorld.CanEnter("Norfair Upper East", items) && items.CardNorfairL2 + ) && items.Morph, + _ => new Requirement(items => items.CanHellRun() && ( + items.CanOpenRedDoors() && ( + items.CanFly() || items.HiJump || items.SpeedBooster || + items.CanSpringBallJump() || items.Varia && items.Ice + ) || + LegacyWorld.CanEnter("Norfair Upper East", items) && items.CardNorfairL2 + ) && items.Morph), + }), + new LegacyLocation(this, 50, 0x8F8B24, LocationType.Chozo, "Ice Beam", Logic switch { + Normal => items => (legacyConfig.Keysanity ? items.CardNorfairL1 : items.Super) && items.CanPassBombPassages() && items.Varia && items.SpeedBooster, + _ => new Requirement(items => (legacyConfig.Keysanity ? items.CardNorfairL1 : items.Super) && items.Morph && (items.Varia || items.HasEnergyReserves(3))) + }), + new LegacyLocation(this, 51, 0x8F8B46, LocationType.Hidden, "Missile (below Ice Beam)", Logic switch { + Normal => items => (legacyConfig.Keysanity ? items.CardNorfairL1 : items.Super) && items.CanUsePowerBombs() && items.Varia && items.SpeedBooster, + _ => new Requirement(items => + (legacyConfig.Keysanity ? items.CardNorfairL1 : items.Super) && items.CanUsePowerBombs() && (items.Varia || items.HasEnergyReserves(3)) || + (items.Missile || items.Super || items.Wave /* Blue Gate */) && items.Varia && items.SpeedBooster && + /* Access to Croc's room to get spark */ + (legacyConfig.Keysanity ? items.CardNorfairBoss : items.Super) && items.CardNorfairL1) + }), + new LegacyLocation(this, 53, 0x8F8BAC, LocationType.Chozo, "Hi-Jump Boots", Logic switch { + _ => new Requirement(items => items.CanOpenRedDoors() && items.CanPassBombPassages()) + }), + new LegacyLocation(this, 55, 0x8F8BE6, LocationType.Visible, "Missile (Hi-Jump Boots)", Logic switch { + _ => new Requirement(items => items.CanOpenRedDoors() && items.Morph) + }), + new LegacyLocation(this, 56, 0x8F8BEC, LocationType.Visible, "Energy Tank (Hi-Jump Boots)", Logic switch { + _ => new Requirement(items => items.CanOpenRedDoors()) + }), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return (items.CanDestroyBombWalls() || items.SpeedBooster) && items.Super && items.Morph || + items.CanAccessNorfairUpperPortal(); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/DeathMountain/LegacyEast.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/DeathMountain/LegacyEast.cs new file mode 100644 index 000000000..65cab9419 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/DeathMountain/LegacyEast.cs @@ -0,0 +1,33 @@ +using System.Collections.Generic; + +namespace Randomizer.SMZ3.Regions.Zelda.DarkWorld.DeathMountain { + + class LegacyEast : LegacyZ3Region { + + public override string Name => "Dark World Death Mountain East"; + public override string Area => "Dark World"; + + public LegacyEast(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 256+65, 0x1EB51, LocationType.Regular, "Hookshot Cave - Top Right", + items => items.MoonPearl && items.Hookshot), + new LegacyLocation(this, 256+66, 0x1EB54, LocationType.Regular, "Hookshot Cave - Top Left", + items => items.MoonPearl && items.Hookshot), + new LegacyLocation(this, 256+67, 0x1EB57, LocationType.Regular, "Hookshot Cave - Bottom Left", + items => items.MoonPearl && items.Hookshot), + new LegacyLocation(this, 256+68, 0x1EB5A, LocationType.Regular, "Hookshot Cave - Bottom Right", + items => items.MoonPearl && (items.Hookshot || items.Boots)), + new LegacyLocation(this, 256+69, 0x1EA7C, LocationType.Regular, "Superbunny Cave - Top", + items => items.MoonPearl), + new LegacyLocation(this, 256+70, 0x1EA7F, LocationType.Regular, "Superbunny Cave - Bottom", + items => items.MoonPearl), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return items.CanLiftHeavy() && LegacyWorld.CanEnter("Light World Death Mountain East", items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/DeathMountain/LegacyWest.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/DeathMountain/LegacyWest.cs new file mode 100644 index 000000000..6208d410f --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/DeathMountain/LegacyWest.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; + +namespace Randomizer.SMZ3.Regions.Zelda.DarkWorld.DeathMountain { + + class LegacyWest : LegacyZ3Region { + + public override string Name => "Dark World Death Mountain West"; + public override string Area => "Dark World"; + + public LegacyWest(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 256+64, 0x1EA8B, LocationType.Regular, "Spike Cave", + items => items.MoonPearl && items.Hammer && items.CanLiftLight() && + (items.CanExtendMagic() && items.Cape || items.Byrna) && + LegacyWorld.CanEnter("Light World Death Mountain West", items)), + }; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/LegacyMire.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/LegacyMire.cs new file mode 100644 index 000000000..1e80f4c91 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/LegacyMire.cs @@ -0,0 +1,25 @@ +using System.Collections.Generic; + +namespace Randomizer.SMZ3.Regions.Zelda.DarkWorld { + + class LegacyMire : LegacyZ3Region { + + public override string Name => "Dark World Mire"; + public override string Area => "Dark World"; + + public LegacyMire(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 256+89, 0x1EA73, LocationType.Regular, "Mire Shed - Left", + items => items.MoonPearl), + new LegacyLocation(this, 256+90, 0x1EA76, LocationType.Regular, "Mire Shed - Right", + items => items.MoonPearl), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return items.Flute && items.CanLiftHeavy() || items.CanAccessMiseryMirePortal(LegacyConfig); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/LegacyNorthEast.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/LegacyNorthEast.cs new file mode 100644 index 000000000..dce3de944 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/LegacyNorthEast.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyRewardType; + +namespace Randomizer.SMZ3.Regions.Zelda.DarkWorld { + + class LegacyNorthEast : LegacyZ3Region { + + public override string Name => "Dark World North East"; + public override string Area => "Dark World"; + + public LegacyNorthEast(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 256+78, 0x1DE185, LocationType.Regular, "Catfish", + items => items.MoonPearl && items.CanLiftLight()), + new LegacyLocation(this, 256+79, 0x308147, LocationType.Regular, "Pyramid"), + new LegacyLocation(this, 256+80, 0x1E980, LocationType.Regular, "Pyramid Fairy - Left", + items => LegacyWorld.CanAcquireAll(items, CrystalRed) && items.MoonPearl && LegacyWorld.CanEnter("Dark World South", items) && + (items.Hammer || items.Mirror && LegacyWorld.CanAcquire(items, Agahnim))), + new LegacyLocation(this, 256+81, 0x1E983, LocationType.Regular, "Pyramid Fairy - Right", + items => LegacyWorld.CanAcquireAll(items, CrystalRed) && items.MoonPearl && LegacyWorld.CanEnter("Dark World South", items) && + (items.Hammer || items.Mirror && LegacyWorld.CanAcquire(items, Agahnim))) + }; + } + + public override bool CanEnter(LegacyProgression items) { + return LegacyWorld.CanAcquire(items, Agahnim) || items.MoonPearl && ( + items.Hammer && items.CanLiftLight() || + items.CanLiftHeavy() && items.Flippers || + items.CanAccessDarkWorldPortal(LegacyConfig) && items.Flippers + ); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/LegacyNorthWest.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/LegacyNorthWest.cs new file mode 100644 index 000000000..a106982b5 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/LegacyNorthWest.cs @@ -0,0 +1,39 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyRewardType; + +namespace Randomizer.SMZ3.Regions.Zelda.DarkWorld { + + class LegacyNorthWest : LegacyZ3Region { + + public override string Name => "Dark World North West"; + public override string Area => "Dark World"; + + public LegacyNorthWest(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 256+71, 0x308146, LocationType.Regular, "Bumper Cave", + items => items.CanLiftLight() && items.Cape), + new LegacyLocation(this, 256+72, 0x1EDA8, LocationType.Regular, "Chest Game"), + new LegacyLocation(this, 256+73, 0x1E9EF, LocationType.Regular, "C-Shaped House"), + new LegacyLocation(this, 256+74, 0x1E9EC, LocationType.Regular, "Brewery"), + new LegacyLocation(this, 256+75, 0x308006, LocationType.Regular, "Hammer Pegs", + items => items.CanLiftHeavy() && items.Hammer), + new LegacyLocation(this, 256+76, 0x30802A, LocationType.Regular, "Blacksmith", + items => items.CanLiftHeavy()), + new LegacyLocation(this, 256+77, 0x6BD68, LocationType.Regular, "Purple Chest", + items => items.CanLiftHeavy()), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return items.MoonPearl && (( + LegacyWorld.CanAcquire(items, Agahnim) || + items.CanAccessDarkWorldPortal(LegacyConfig) && items.Flippers + ) && items.Hookshot && (items.Flippers || items.CanLiftLight() || items.Hammer) || + items.Hammer && items.CanLiftLight() || + items.CanLiftHeavy() + ); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/LegacySouth.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/LegacySouth.cs new file mode 100644 index 000000000..ad36b5d44 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/DarkWorld/LegacySouth.cs @@ -0,0 +1,35 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyRewardType; + +namespace Randomizer.SMZ3.Regions.Zelda.DarkWorld { + + class LegacySouth : LegacyZ3Region { + + public override string Name => "Dark World South"; + public override string Area => "Dark World"; + + public LegacySouth(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 256+82, 0x308148, LocationType.Regular, "Digging Game"), + new LegacyLocation(this, 256+83, 0x6B0C7, LocationType.Regular, "Stumpy"), + new LegacyLocation(this, 256+84, 0x1EB1E, LocationType.Regular, "Hype Cave - Top"), + new LegacyLocation(this, 256+85, 0x1EB21, LocationType.Regular, "Hype Cave - Middle Right"), + new LegacyLocation(this, 256+86, 0x1EB24, LocationType.Regular, "Hype Cave - Middle Left"), + new LegacyLocation(this, 256+87, 0x1EB27, LocationType.Regular, "Hype Cave - Bottom"), + new LegacyLocation(this, 256+88, 0x308011, LocationType.Regular, "Hype Cave - NPC"), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return items.MoonPearl && (( + LegacyWorld.CanAcquire(items, Agahnim) || + items.CanAccessDarkWorldPortal(LegacyConfig) && items.Flippers + ) && (items.Hammer || items.Hookshot && (items.Flippers || items.CanLiftLight())) || + items.Hammer && items.CanLiftLight() || + items.CanLiftHeavy() + ); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyCastleTower.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyCastleTower.cs new file mode 100644 index 000000000..d5844b763 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyCastleTower.cs @@ -0,0 +1,32 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyItemType; + +namespace Randomizer.SMZ3.Regions.Zelda { + + class LegacyCastleTower : LegacyZ3Region, ILegacyReward { + + public override string Name => "Castle Tower"; + + public LegacyRewardType LegacyReward { get; set; } = LegacyRewardType.Agahnim; + + public LegacyCastleTower(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + RegionItems = new[] { KeyCT }; + + Locations = new List { + new LegacyLocation(this, 256+101, 0x1EAB5, LocationType.Regular, "Castle Tower - Foyer"), + new LegacyLocation(this, 256+102, 0x1EAB2, LocationType.Regular, "Castle Tower - Dark Maze", + items => items.Lamp && items.KeyCT >= 1), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return items.CanKillManyEnemies() && (items.Cape || items.MasterSword); + } + + public bool CanComplete(LegacyProgression items) { + return CanEnter(items) && items.Lamp && items.KeyCT >= 2 && items.Sword; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyDesertPalace.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyDesertPalace.cs new file mode 100644 index 000000000..fa5e4f5a3 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyDesertPalace.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyItemType; + +namespace Randomizer.SMZ3.Regions.Zelda { + + class LegacyDesertPalace : LegacyZ3Region, ILegacyReward { + + public override string Name => "Desert Palace"; + + public LegacyRewardType LegacyReward { get; set; } = LegacyRewardType.None; + + public LegacyDesertPalace(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + RegionItems = new[] { KeyDP, BigKeyDP, MapDP, CompassDP }; + + Locations = new List { + new LegacyLocation(this, 256+109, 0x1E98F, LocationType.Regular, "Desert Palace - Big Chest", + items => items.BigKeyDP), + new LegacyLocation(this, 256+110, 0x308160, LocationType.Regular, "Desert Palace - Torch", + items => items.Boots), + new LegacyLocation(this, 256+111, 0x1E9B6, LocationType.Regular, "Desert Palace - Map Chest"), + new LegacyLocation(this, 256+112, 0x1E9C2, LocationType.Regular, "Desert Palace - Big Key Chest", + items => items.KeyDP), + new LegacyLocation(this, 256+113, 0x1E9CB, LocationType.Regular, "Desert Palace - Compass Chest", + items => items.KeyDP), + new LegacyLocation(this, 256+114, 0x308151, LocationType.Regular, "Desert Palace - Lanmolas", + items => ( + items.CanLiftLight() || + items.CanAccessMiseryMirePortal(LegacyConfig) && items.Mirror + ) && items.BigKeyDP && items.KeyDP && items.CanLightTorches() && CanBeatBoss(items)), + }; + } + + private bool CanBeatBoss(LegacyProgression items) { + return items.Sword || items.Hammer || items.Bow || + items.Firerod || items.Icerod || + items.Byrna || items.Somaria; + } + + public override bool CanEnter(LegacyProgression items) { + return items.Book || + items.Mirror && items.CanLiftHeavy() && items.Flute || + items.CanAccessMiseryMirePortal(LegacyConfig) && items.Mirror; + } + + public bool CanComplete(LegacyProgression items) { + return GetLocation("Desert Palace - Lanmolas").Available(items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyEasternPalace.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyEasternPalace.cs new file mode 100644 index 000000000..8e35a40a8 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyEasternPalace.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyItemType; + +namespace Randomizer.SMZ3.Regions.Zelda { + + class LegacyEasternPalace : LegacyZ3Region, ILegacyReward { + + public override string Name => "Eastern Palace"; + + public LegacyRewardType LegacyReward { get; set; } = LegacyRewardType.None; + + public LegacyEasternPalace(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + RegionItems = new[] { BigKeyEP, MapEP, CompassEP }; + + Locations = new List { + new LegacyLocation(this, 256+103, 0x1E9B3, LocationType.Regular, "Eastern Palace - Cannonball Chest"), + new LegacyLocation(this, 256+104, 0x1E9F5, LocationType.Regular, "Eastern Palace - Map Chest"), + new LegacyLocation(this, 256+105, 0x1E977, LocationType.Regular, "Eastern Palace - Compass Chest"), + new LegacyLocation(this, 256+106, 0x1E97D, LocationType.Regular, "Eastern Palace - Big Chest", + items => items.BigKeyEP), + new LegacyLocation(this, 256+107, 0x1E9B9, LocationType.Regular, "Eastern Palace - Big Key Chest", + items => items.Lamp), + new LegacyLocation(this, 256+108, 0x308150, LocationType.Regular, "Eastern Palace - Armos Knights", + items => items.BigKeyEP && items.Bow && items.Lamp), + }; + } + + public bool CanComplete(LegacyProgression items) { + return GetLocation("Eastern Palace - Armos Knights").Available(items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyGanonsTower.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyGanonsTower.cs new file mode 100644 index 000000000..7101509e7 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyGanonsTower.cs @@ -0,0 +1,167 @@ +using System.Collections.Generic; +using System.Linq; +using static Randomizer.SMZ3.LegacyItemType; +using static Randomizer.SMZ3.LegacyRewardType; + +namespace Randomizer.SMZ3.Regions.Zelda { + + class LegacyGanonsTower : LegacyZ3Region { + + public override string Name => "Ganon's Tower"; + + public LegacyGanonsTower(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + RegionItems = new[] { KeyGT, BigKeyGT, MapGT, CompassGT }; + + Locations = new List { + new LegacyLocation(this, 256+189, 0x308161, LocationType.Regular, "Ganon's Tower - Bob's Torch", + items => items.Boots), + new LegacyLocation(this, 256+190, 0x1EAB8, LocationType.Regular, "Ganon's Tower - DMs Room - Top Left", + items => items.Hammer && items.Hookshot), + new LegacyLocation(this, 256+191, 0x1EABB, LocationType.Regular, "Ganon's Tower - DMs Room - Top Right", + items => items.Hammer && items.Hookshot), + new LegacyLocation(this, 256+192, 0x1EABE, LocationType.Regular, "Ganon's Tower - DMs Room - Bottom Left", + items => items.Hammer && items.Hookshot), + new LegacyLocation(this, 256+193, 0x1EAC1, LocationType.Regular, "Ganon's Tower - DMs Room - Bottom Right", + items => items.Hammer && items.Hookshot), + new LegacyLocation(this, 256+194, 0x1EAD3, LocationType.Regular, "Ganon's Tower - Map Chest", + items => items.Hammer && (items.Hookshot || items.Boots) && items.KeyGT >= + (new[] { BigKeyGT, KeyGT }.Any(type => GetLocation("Ganon's Tower - Map Chest").ItemIs(type, LegacyWorld)) ? 3 : 4)) + .AlwaysAllow((item, items) => item.Is(KeyGT, LegacyWorld) && items.KeyGT >= 3), + new LegacyLocation(this, 256+195, 0x1EAD0, LocationType.Regular, "Ganon's Tower - Firesnake Room", + items => items.Hammer && items.Hookshot && items.KeyGT >= (new[] { + GetLocation("Ganon's Tower - Randomizer Room - Top Right"), + GetLocation("Ganon's Tower - Randomizer Room - Top Left"), + GetLocation("Ganon's Tower - Randomizer Room - Bottom Left"), + GetLocation("Ganon's Tower - Randomizer Room - Bottom Right") + }.Any(l => l.ItemIs(BigKeyGT, LegacyWorld)) || + GetLocation("Ganon's Tower - Firesnake Room").ItemIs(KeyGT, LegacyWorld) ? 2 : 3)), + new LegacyLocation(this, 256+230, 0x1EAC4, LocationType.Regular, "Ganon's Tower - Randomizer Room - Top Left", + items => LeftSide(items, new[] { + GetLocation("Ganon's Tower - Randomizer Room - Top Right"), + GetLocation("Ganon's Tower - Randomizer Room - Bottom Left"), + GetLocation("Ganon's Tower - Randomizer Room - Bottom Right") + })), + new LegacyLocation(this, 256+231, 0x1EAC7, LocationType.Regular, "Ganon's Tower - Randomizer Room - Top Right", + items => LeftSide(items, new[] { + GetLocation("Ganon's Tower - Randomizer Room - Top Left"), + GetLocation("Ganon's Tower - Randomizer Room - Bottom Left"), + GetLocation("Ganon's Tower - Randomizer Room - Bottom Right") + })), + new LegacyLocation(this, 256+232, 0x1EACA, LocationType.Regular, "Ganon's Tower - Randomizer Room - Bottom Left", + items => LeftSide(items, new[] { + GetLocation("Ganon's Tower - Randomizer Room - Top Right"), + GetLocation("Ganon's Tower - Randomizer Room - Top Left"), + GetLocation("Ganon's Tower - Randomizer Room - Bottom Right") + })), + new LegacyLocation(this, 256+233, 0x1EACD, LocationType.Regular, "Ganon's Tower - Randomizer Room - Bottom Right", + items => LeftSide(items, new[] { + GetLocation("Ganon's Tower - Randomizer Room - Top Right"), + GetLocation("Ganon's Tower - Randomizer Room - Top Left"), + GetLocation("Ganon's Tower - Randomizer Room - Bottom Left") + })), + new LegacyLocation(this, 256+234, 0x1EAD9, LocationType.Regular, "Ganon's Tower - Hope Room - Left"), + new LegacyLocation(this, 256+235, 0x1EADC, LocationType.Regular, "Ganon's Tower - Hope Room - Right"), + new LegacyLocation(this, 256+236, 0x1EAE2, LocationType.Regular, "Ganon's Tower - Tile Room", + items => items.Somaria), + new LegacyLocation(this, 256+203, 0x1EAE5, LocationType.Regular, "Ganon's Tower - Compass Room - Top Left", + items => RightSide(items, new[] { + GetLocation("Ganon's Tower - Compass Room - Top Right"), + GetLocation("Ganon's Tower - Compass Room - Bottom Left"), + GetLocation("Ganon's Tower - Compass Room - Bottom Right") + })), + new LegacyLocation(this, 256+204, 0x1EAE8, LocationType.Regular, "Ganon's Tower - Compass Room - Top Right", + items => RightSide(items, new[] { + GetLocation("Ganon's Tower - Compass Room - Top Left"), + GetLocation("Ganon's Tower - Compass Room - Bottom Left"), + GetLocation("Ganon's Tower - Compass Room - Bottom Right") + })), + new LegacyLocation(this, 256+205, 0x1EAEB, LocationType.Regular, "Ganon's Tower - Compass Room - Bottom Left", + items => RightSide(items, new[] { + GetLocation("Ganon's Tower - Compass Room - Top Right"), + GetLocation("Ganon's Tower - Compass Room - Top Left"), + GetLocation("Ganon's Tower - Compass Room - Bottom Right") + })), + new LegacyLocation(this, 256+206, 0x1EAEE, LocationType.Regular, "Ganon's Tower - Compass Room - Bottom Right", + items => RightSide(items, new[] { + GetLocation("Ganon's Tower - Compass Room - Top Right"), + GetLocation("Ganon's Tower - Compass Room - Top Left"), + GetLocation("Ganon's Tower - Compass Room - Bottom Left") + })), + new LegacyLocation(this, 256+207, 0x1EADF, LocationType.Regular, "Ganon's Tower - Bob's Chest", + items => items.KeyGT >= 3 && ( + items.Hammer && items.Hookshot || + items.Somaria && items.Firerod)), + new LegacyLocation(this, 256+208, 0x1EAD6, LocationType.Regular, "Ganon's Tower - Big Chest", + items => items.BigKeyGT && items.KeyGT >= 3 && ( + items.Hammer && items.Hookshot || + items.Somaria && items.Firerod)) + .Allow((item, items) => item.IsNot(BigKeyGT, LegacyWorld)), + new LegacyLocation(this, 256+209, 0x1EAF1, LocationType.Regular, "Ganon's Tower - Big Key Chest", BigKeyRoom), + new LegacyLocation(this, 256+210, 0x1EAF4, LocationType.Regular, "Ganon's Tower - Big Key Room - Left", BigKeyRoom), + new LegacyLocation(this, 256+211, 0x1EAF7, LocationType.Regular, "Ganon's Tower - Big Key Room - Right", BigKeyRoom), + new LegacyLocation(this, 256+212, 0x1EAFD, LocationType.Regular, "Ganon's Tower - Mini Helmasaur Room - Left", TowerAscend) + .Allow((item, items) => item.IsNot(BigKeyGT, LegacyWorld)), + new LegacyLocation(this, 256+213, 0x1EB00, LocationType.Regular, "Ganon's Tower - Mini Helmasaur Room - Right", TowerAscend) + .Allow((item, items) => item.IsNot(BigKeyGT, LegacyWorld)), + new LegacyLocation(this, 256+214, 0x1EB03, LocationType.Regular, "Ganon's Tower - Pre-Moldorm Chest", TowerAscend) + .Allow((item, items) => item.IsNot(BigKeyGT, LegacyWorld)), + new LegacyLocation(this, 256+215, 0x1EB06, LocationType.Regular, "Ganon's Tower - Moldorm Chest", + items => items.BigKeyGT && items.KeyGT >= 4 && + items.Bow && items.CanLightTorches() && + CanBeatMoldorm(items) && items.Hookshot) + .Allow((item, items) => new[] { KeyGT, BigKeyGT }.All(type => item.IsNot(type, LegacyWorld))), + }; + } + + bool LeftSide(LegacyProgression items, IList locations) { + return items.Hammer && items.Hookshot && items.KeyGT >= (locations.Any(l => l.ItemIs(BigKeyGT, LegacyWorld)) ? 3 : 4); + } + + bool RightSide(LegacyProgression items, IList locations) { + return items.Somaria && items.Firerod && items.KeyGT >= (locations.Any(l => l.ItemIs(BigKeyGT, LegacyWorld)) ? 3 : 4); + } + + bool BigKeyRoom(LegacyProgression items) { + return items.KeyGT >= 3 && ( + items.Hammer && items.Hookshot || + items.Firerod && items.Somaria + ) && CanBeatArmos(items); + } + + bool TowerAscend(LegacyProgression items) { + return items.BigKeyGT && items.KeyGT >= 3 && items.Bow && items.CanLightTorches(); + } + + bool CanBeatArmos(LegacyProgression items) { + return items.Sword || items.Hammer || items.Bow || + items.CanExtendMagic(2) && (items.Somaria || items.Byrna) || + items.CanExtendMagic(4) && (items.Firerod || items.Icerod); + } + + bool CanBeatMoldorm(LegacyProgression items) { + return items.Sword || items.Hammer; + } + + public override bool CanEnter(LegacyProgression items) { + return items.MoonPearl && LegacyWorld.CanEnter("Dark World Death Mountain East", items) && + LegacyWorld.CanAcquireAtLeast(LegacyWorld.TowerCrystals, items, AnyCrystal) && + LegacyWorld.CanAcquireAtLeast((LegacyWorld.TourianBossTokens * LegacyWorld.TowerCrystals) / 7, items, AnyBossToken); + } + + public override bool CanFill(LegacyItem legacyItem, LegacyProgression items) { + if (LegacyConfig.MultiWorld) { + if (legacyItem.LegacyWorld != LegacyWorld || legacyItem.Progression) { + return false; + } + + if (LegacyConfig.Keysanity && !((legacyItem.Type == BigKeyGT || legacyItem.Type == KeyGT) && legacyItem.LegacyWorld == LegacyWorld) && (legacyItem.IsKey || legacyItem.IsBigKey || legacyItem.IsKeycard)) { + return false; + } + } + + return base.CanFill(legacyItem, items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyHyruleCastle.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyHyruleCastle.cs new file mode 100644 index 000000000..53fb9dd5b --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyHyruleCastle.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyItemType; + +namespace Randomizer.SMZ3.Regions.Zelda { + + class LegacyHyruleCastle : LegacyZ3Region { + + public override string Name => "Hyrule Castle"; + + public LegacyHyruleCastle(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + RegionItems = new[] { KeyHC, MapHC }; + + var sphereOne = -10; + Locations = new List { + new LegacyLocation(this, 256+91, 0x1EA79, LocationType.Regular, "Sanctuary").Weighted(sphereOne), + new LegacyLocation(this, 256+92, 0x1EB5D, LocationType.Regular, "Sewers - Secret Room - Left", + items => items.CanLiftLight() || items.Lamp && items.KeyHC), + new LegacyLocation(this, 256+93, 0x1EB60, LocationType.Regular, "Sewers - Secret Room - Middle", + items => items.CanLiftLight() || items.Lamp && items.KeyHC), + new LegacyLocation(this, 256+94, 0x1EB63, LocationType.Regular, "Sewers - Secret Room - Right", + items => items.CanLiftLight() || items.Lamp && items.KeyHC), + new LegacyLocation(this, 256+95, 0x1E96E, LocationType.Regular, "Sewers - Dark Cross", + items => items.Lamp), + new LegacyLocation(this, 256+96, 0x1EB0C, LocationType.Regular, "Hyrule Castle - Map Chest").Weighted(sphereOne), + new LegacyLocation(this, 256+97, 0x1E974, LocationType.Regular, "Hyrule Castle - Boomerang Chest", + items => items.KeyHC).Weighted(sphereOne), + new LegacyLocation(this, 256+98, 0x1EB09, LocationType.Regular, "Hyrule Castle - Zelda's Cell", + items => items.KeyHC).Weighted(sphereOne), + new LegacyLocation(this, 256+99, 0x5DF45, LocationType.NotInDungeon, "Link's Uncle") + .Allow((item, items) => LegacyConfig.Keysanity || !item.IsDungeonItem).Weighted(sphereOne), + new LegacyLocation(this, 256+100, 0x1E971, LocationType.NotInDungeon, "Secret Passage") + .Allow((item, items) => LegacyConfig.Keysanity || !item.IsDungeonItem).Weighted(sphereOne), + }; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyIcePalace.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyIcePalace.cs new file mode 100644 index 000000000..54945e6aa --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyIcePalace.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using System.Linq; +using static Randomizer.SMZ3.LegacyItemType; + +namespace Randomizer.SMZ3.Regions.Zelda { + + class LegacyIcePalace : LegacyZ3Region, ILegacyReward { + + public override string Name => "Ice Palace"; + + public LegacyRewardType LegacyReward { get; set; } = LegacyRewardType.None; + + public LegacyIcePalace(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Weight = 4; + RegionItems = new[] { KeyIP, BigKeyIP, MapIP, CompassIP }; + + Locations = new List { + new LegacyLocation(this, 256+161, 0x1E9D4, LocationType.Regular, "Ice Palace - Compass Chest"), + new LegacyLocation(this, 256+162, 0x1E9E0, LocationType.Regular, "Ice Palace - Spike Room", + items => items.Hookshot || items.KeyIP >= 1 && CanNotWasteKeysBeforeAccessible(items, new[] { + GetLocation("Ice Palace - Map Chest"), + GetLocation("Ice Palace - Big Key Chest") + })), + new LegacyLocation(this, 256+163, 0x1E9DD, LocationType.Regular, "Ice Palace - Map Chest", + items => items.Hammer && items.CanLiftLight() && ( + items.Hookshot || items.KeyIP >= 1 && CanNotWasteKeysBeforeAccessible(items, new[] { + GetLocation("Ice Palace - Spike Room"), + GetLocation("Ice Palace - Big Key Chest") + }) + )), + new LegacyLocation(this, 256+164, 0x1E9A4, LocationType.Regular, "Ice Palace - Big Key Chest", + items => items.Hammer && items.CanLiftLight() && ( + items.Hookshot || items.KeyIP >= 1 && CanNotWasteKeysBeforeAccessible(items, new[] { + GetLocation("Ice Palace - Spike Room"), + GetLocation("Ice Palace - Map Chest") + }) + )), + new LegacyLocation(this, 256+165, 0x1E9E3, LocationType.Regular, "Ice Palace - Iced T Room"), + new LegacyLocation(this, 256+166, 0x1E995, LocationType.Regular, "Ice Palace - Freezor Chest"), + new LegacyLocation(this, 256+167, 0x1E9AA, LocationType.Regular, "Ice Palace - Big Chest", + items => items.BigKeyIP), + new LegacyLocation(this, 256+168, 0x308157, LocationType.Regular, "Ice Palace - Kholdstare", + items => items.BigKeyIP && items.Hammer && items.CanLiftLight() && + items.KeyIP >= (items.Somaria ? 1 : 2)), + }; + } + + bool CanNotWasteKeysBeforeAccessible(LegacyProgression items, IList locations) { + return LegacyWorld.ForwardSearch || !items.BigKeyIP || locations.Any(l => l.ItemIs(BigKeyIP, LegacyWorld)); + } + + public override bool CanEnter(LegacyProgression items) { + return items.MoonPearl && items.Flippers && items.CanLiftHeavy() && items.CanMeltFreezors(); + } + + public bool CanComplete(LegacyProgression items) { + return GetLocation("Ice Palace - Kholdstare").Available(items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyMiseryMire.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyMiseryMire.cs new file mode 100644 index 000000000..a2c8f8148 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyMiseryMire.cs @@ -0,0 +1,55 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyItemType; +using static Randomizer.SMZ3.LegacyWorldState; + +namespace Randomizer.SMZ3.Regions.Zelda { + + class LegacyMiseryMire : LegacyZ3Region, ILegacyReward, ILegacyMedallionAccess { + + public override string Name => "Misery Mire"; + + public LegacyRewardType LegacyReward { get; set; } = LegacyRewardType.None; + public LegacyMedallion LegacyMedallion { get; set; } + + public LegacyMiseryMire(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Weight = 2; + RegionItems = new[] { KeyMM, BigKeyMM, MapMM, CompassMM }; + + Locations = new List { + new LegacyLocation(this, 256+169, 0x1EA5E, LocationType.Regular, "Misery Mire - Main Lobby", + items => items.BigKeyMM || items.KeyMM >= 1), + new LegacyLocation(this, 256+170, 0x1EA6A, LocationType.Regular, "Misery Mire - Map Chest", + items => items.BigKeyMM || items.KeyMM >= 1), + new LegacyLocation(this, 256+171, 0x1EA61, LocationType.Regular, "Misery Mire - Bridge Chest"), + new LegacyLocation(this, 256+172, 0x1E9DA, LocationType.Regular, "Misery Mire - Spike Chest"), + new LegacyLocation(this, 256+173, 0x1EA64, LocationType.Regular, "Misery Mire - Compass Chest", + items => items.CanLightTorches() && + items.KeyMM >= (GetLocation("Misery Mire - Big Key Chest").ItemIs(BigKeyMM, LegacyWorld) ? 2 : 3)), + new LegacyLocation(this, 256+174, 0x1EA6D, LocationType.Regular, "Misery Mire - Big Key Chest", + items => items.CanLightTorches() && + items.KeyMM >= (GetLocation("Misery Mire - Compass Chest").ItemIs(BigKeyMM, LegacyWorld) ? 2 : 3)), + new LegacyLocation(this, 256+175, 0x1EA67, LocationType.Regular, "Misery Mire - Big Chest", + items => items.BigKeyMM), + new LegacyLocation(this, 256+176, 0x308158, LocationType.Regular, "Misery Mire - Vitreous", + items => items.BigKeyMM && items.Lamp && items.Somaria), + }; + } + + // Need "CanKillManyEnemies" if implementing swordless + public override bool CanEnter(LegacyProgression items) { + return LegacyMedallion switch { + LegacyMedallion.Bombos => items.Bombos, + LegacyMedallion.Ether => items.Ether, + _ => items.Quake, + } && items.Sword && + items.MoonPearl && (items.Boots || items.Hookshot) && + LegacyWorld.CanEnter("Dark World Mire", items); + } + + public bool CanComplete(LegacyProgression items) { + return GetLocation("Misery Mire - Vitreous").Available(items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyPalaceOfDarkness.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyPalaceOfDarkness.cs new file mode 100644 index 000000000..3a91e224b --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyPalaceOfDarkness.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyItemType; + +namespace Randomizer.SMZ3.Regions.Zelda { + + class LegacyPalaceOfDarkness : LegacyZ3Region, ILegacyReward { + + public override string Name => "Palace of Darkness"; + public override string Area => "Dark Palace"; + + public LegacyRewardType LegacyReward { get; set; } = LegacyRewardType.None; + + public LegacyPalaceOfDarkness(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + RegionItems = new[] { KeyPD, BigKeyPD, MapPD, CompassPD }; + + Locations = new List { + new LegacyLocation(this, 256+121, 0x1EA5B, LocationType.Regular, "Palace of Darkness - Shooter Room"), + new LegacyLocation(this, 256+122, 0x1EA37, LocationType.Regular, "Palace of Darkness - Big Key Chest", + items => items.KeyPD >= (GetLocation("Palace of Darkness - Big Key Chest").ItemIs(KeyPD, LegacyWorld) ? 1 : + (items.Hammer && items.Bow && items.Lamp) || legacyConfig.Keysanity ? 6 : 5)) + .AlwaysAllow((item, items) => item.Is(KeyPD, LegacyWorld) && items.KeyPD >= 5), + new LegacyLocation(this, 256+123, 0x1EA49, LocationType.Regular, "Palace of Darkness - Stalfos Basement", + items => items.KeyPD >= 1 || items.Bow && items.Hammer), + new LegacyLocation(this, 256+124, 0x1EA3D, LocationType.Regular, "Palace of Darkness - The Arena - Bridge", + items => items.KeyPD >= 1 || items.Bow && items.Hammer), + new LegacyLocation(this, 256+125, 0x1EA3A, LocationType.Regular, "Palace of Darkness - The Arena - Ledge", + items => items.Bow), + new LegacyLocation(this, 256+126, 0x1EA52, LocationType.Regular, "Palace of Darkness - Map Chest", + items => items.Bow), + new LegacyLocation(this, 256+127, 0x1EA43, LocationType.Regular, "Palace of Darkness - Compass Chest", + items => items.KeyPD >= ((items.Hammer && items.Bow && items.Lamp) || legacyConfig.Keysanity ? 4 : 3)), + new LegacyLocation(this, 256+128, 0x1EA46, LocationType.Regular, "Palace of Darkness - Harmless Hellway", + items => items.KeyPD >= (GetLocation("Palace of Darkness - Harmless Hellway").ItemIs(KeyPD, LegacyWorld) ? + (items.Hammer && items.Bow && items.Lamp) || legacyConfig.Keysanity ? 4 : 3 : + (items.Hammer && items.Bow && items.Lamp) || legacyConfig.Keysanity ? 6 : 5)) + .AlwaysAllow((item, items) => item.Is(KeyPD, LegacyWorld) && items.KeyPD >= 5), + new LegacyLocation(this, 256+129, 0x1EA4C, LocationType.Regular, "Palace of Darkness - Dark Basement - Left", + items => items.Lamp && items.KeyPD >= ((items.Hammer && items.Bow) || legacyConfig.Keysanity ? 4 : 3)), + new LegacyLocation(this, 256+130, 0x1EA4F, LocationType.Regular, "Palace of Darkness - Dark Basement - Right", + items => items.Lamp && items.KeyPD >= ((items.Hammer && items.Bow) || legacyConfig.Keysanity ? 4 : 3)), + new LegacyLocation(this, 256+131, 0x1EA55, LocationType.Regular, "Palace of Darkness - Dark Maze - Top", + items => items.Lamp && items.KeyPD >= ((items.Hammer && items.Bow) || legacyConfig.Keysanity ? 6 : 5)), + new LegacyLocation(this, 256+132, 0x1EA58, LocationType.Regular, "Palace of Darkness - Dark Maze - Bottom", + items => items.Lamp && items.KeyPD >= ((items.Hammer && items.Bow) || legacyConfig.Keysanity ? 6 : 5)), + new LegacyLocation(this, 256+133, 0x1EA40, LocationType.Regular, "Palace of Darkness - Big Chest", + items => items.BigKeyPD && items.Lamp && items.KeyPD >= ((items.Hammer && items.Bow) || legacyConfig.Keysanity ? 6 : 5)), + new LegacyLocation(this, 256+134, 0x308153, LocationType.Regular, "Palace of Darkness - Helmasaur King", + items => items.Lamp && items.Hammer && items.Bow && items.BigKeyPD && items.KeyPD >= 6), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return items.MoonPearl && LegacyWorld.CanEnter("Dark World North East", items); + } + + public bool CanComplete(LegacyProgression items) { + return GetLocation("Palace of Darkness - Helmasaur King").Available(items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacySkullWoods.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacySkullWoods.cs new file mode 100644 index 000000000..f66bb2246 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacySkullWoods.cs @@ -0,0 +1,42 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyItemType; + +namespace Randomizer.SMZ3.Regions.Zelda { + + class LegacySkullWoods : LegacyZ3Region, ILegacyReward { + + public override string Name => "Skull Woods"; + + public LegacyRewardType LegacyReward { get; set; } = LegacyRewardType.None; + + public LegacySkullWoods(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + RegionItems = new[] { KeySW, BigKeySW, MapSW, CompassSW }; + + Locations = new List { + new LegacyLocation(this, 256+145, 0x1E9A1, LocationType.Regular, "Skull Woods - Pot Prison"), + new LegacyLocation(this, 256+146, 0x1E992, LocationType.Regular, "Skull Woods - Compass Chest"), + new LegacyLocation(this, 256+147, 0x1E998, LocationType.Regular, "Skull Woods - Big Chest", + items => items.BigKeySW) + .AlwaysAllow((item, items) => item.Is(BigKeySW, LegacyWorld)), + new LegacyLocation(this, 256+148, 0x1E99B, LocationType.Regular, "Skull Woods - Map Chest"), + new LegacyLocation(this, 256+149, 0x1E9C8, LocationType.Regular, "Skull Woods - Pinball Room") + .Allow((item, items) => item.Is(KeySW, LegacyWorld)), + new LegacyLocation(this, 256+150, 0x1E99E, LocationType.Regular, "Skull Woods - Big Key Chest"), + new LegacyLocation(this, 256+151, 0x1E9FE, LocationType.Regular, "Skull Woods - Bridge Room", + items => items.Firerod), + new LegacyLocation(this, 256+152, 0x308155, LocationType.Regular, "Skull Woods - Mothula", + items => items.Firerod && items.Sword && items.KeySW >= 3), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return items.MoonPearl && LegacyWorld.CanEnter("Dark World North West", items); + } + + public bool CanComplete(LegacyProgression items) { + return GetLocation("Skull Woods - Mothula").Available(items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacySwampPalace.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacySwampPalace.cs new file mode 100644 index 000000000..8fef94d2b --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacySwampPalace.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyItemType; + +namespace Randomizer.SMZ3.Regions.Zelda { + + class LegacySwampPalace : LegacyZ3Region, ILegacyReward { + + public override string Name => "Swamp Palace"; + + public LegacyRewardType LegacyReward { get; set; } = LegacyRewardType.None; + + public LegacySwampPalace(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Weight = 3; + RegionItems = new[] { KeySP, BigKeySP, MapSP, CompassSP }; + + Locations = new List { + new LegacyLocation(this, 256+135, 0x1EA9D, LocationType.Regular, "Swamp Palace - Entrance") + .Allow((item, items) => LegacyConfig.Keysanity || item.Is(KeySP, LegacyWorld)), + new LegacyLocation(this, 256+136, 0x1E986, LocationType.Regular, "Swamp Palace - Map Chest", + items => items.KeySP), + new LegacyLocation(this, 256+137, 0x1E989, LocationType.Regular, "Swamp Palace - Big Chest", + items => items.BigKeySP && items.KeySP && items.Hammer) + .AlwaysAllow((item, items) => item.Is(BigKeySP, LegacyWorld)), + new LegacyLocation(this, 256+138, 0x1EAA0, LocationType.Regular, "Swamp Palace - Compass Chest", + items => items.KeySP && items.Hammer), + new LegacyLocation(this, 256+139, 0x1EAA3, LocationType.Regular, "Swamp Palace - West Chest", + items => items.KeySP && items.Hammer), + new LegacyLocation(this, 256+140, 0x1EAA6, LocationType.Regular, "Swamp Palace - Big Key Chest", + items => items.KeySP && items.Hammer), + new LegacyLocation(this, 256+141, 0x1EAA9, LocationType.Regular, "Swamp Palace - Flooded Room - Left", + items => items.KeySP && items.Hammer && items.Hookshot), + new LegacyLocation(this, 256+142, 0x1EAAC, LocationType.Regular, "Swamp Palace - Flooded Room - Right", + items => items.KeySP && items.Hammer && items.Hookshot), + new LegacyLocation(this, 256+143, 0x1EAAF, LocationType.Regular, "Swamp Palace - Waterfall Room", + items => items.KeySP && items.Hammer && items.Hookshot), + new LegacyLocation(this, 256+144, 0x308154, LocationType.Regular, "Swamp Palace - Arrghus", + items => items.KeySP && items.Hammer && items.Hookshot), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return items.MoonPearl && items.Mirror && items.Flippers && LegacyWorld.CanEnter("Dark World South", items); + } + + public bool CanComplete(LegacyProgression items) { + return GetLocation("Swamp Palace - Arrghus").Available(items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyThievesTown.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyThievesTown.cs new file mode 100644 index 000000000..55faa0182 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyThievesTown.cs @@ -0,0 +1,48 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyItemType; + +namespace Randomizer.SMZ3.Regions.Zelda { + + class LegacyThievesTown : LegacyZ3Region, ILegacyReward { + + public override string Name => "Thieves' Town"; + + public LegacyRewardType LegacyReward { get; set; } = LegacyRewardType.None; + + public LegacyThievesTown(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + RegionItems = new[] { KeyTT, BigKeyTT, MapTT, CompassTT }; + + Locations = new List { + new LegacyLocation(this, 256+153, 0x1EA01, LocationType.Regular, "Thieves' Town - Map Chest"), + new LegacyLocation(this, 256+154, 0x1EA0A, LocationType.Regular, "Thieves' Town - Ambush Chest"), + new LegacyLocation(this, 256+155, 0x1EA07, LocationType.Regular, "Thieves' Town - Compass Chest"), + new LegacyLocation(this, 256+156, 0x1EA04, LocationType.Regular, "Thieves' Town - Big Key Chest"), + new LegacyLocation(this, 256+157, 0x1EA0D, LocationType.Regular, "Thieves' Town - Attic", + items => items.BigKeyTT && items.KeyTT), + new LegacyLocation(this, 256+158, 0x1EA13, LocationType.Regular, "Thieves' Town - Blind's Cell", + items => items.BigKeyTT), + new LegacyLocation(this, 256+159, 0x1EA10, LocationType.Regular, "Thieves' Town - Big Chest", + items => items.BigKeyTT && items.Hammer && + (GetLocation("Thieves' Town - Big Chest").ItemIs(KeyTT, LegacyWorld) || items.KeyTT)) + .AlwaysAllow((item, items) => item.Is(KeyTT, LegacyWorld) && items.Hammer), + new LegacyLocation(this, 256+160, 0x308156, LocationType.Regular, "Thieves' Town - Blind", + items => items.BigKeyTT && items.KeyTT && CanBeatBoss(items)), + }; + } + + private bool CanBeatBoss(LegacyProgression items) { + return items.Sword || items.Hammer || + items.Somaria || items.Byrna; + } + + public override bool CanEnter(LegacyProgression items) { + return items.MoonPearl && LegacyWorld.CanEnter("Dark World North West", items); + } + + public bool CanComplete(LegacyProgression items) { + return GetLocation("Thieves' Town - Blind").Available(items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyTowerOfHera.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyTowerOfHera.cs new file mode 100644 index 000000000..8bddebe7d --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyTowerOfHera.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyItemType; + +namespace Randomizer.SMZ3.Regions.Zelda { + + class LegacyTowerOfHera : LegacyZ3Region, ILegacyReward { + + public override string Name => "Tower of Hera"; + + public LegacyRewardType LegacyReward { get; set; } = LegacyRewardType.None; + + public LegacyTowerOfHera(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + RegionItems = new[] { KeyTH, BigKeyTH, MapTH, CompassTH }; + + Locations = new List { + new LegacyLocation(this, 256+115, 0x308162, LocationType.HeraStandingKey, "Tower of Hera - Basement Cage"), + new LegacyLocation(this, 256+116, 0x1E9AD, LocationType.Regular, "Tower of Hera - Map Chest"), + new LegacyLocation(this, 256+117, 0x1E9E6, LocationType.Regular, "Tower of Hera - Big Key Chest", + items => items.KeyTH && items.CanLightTorches()) + .AlwaysAllow((item, items) => item.Is(KeyTH, LegacyWorld)), + new LegacyLocation(this, 256+118, 0x1E9FB, LocationType.Regular, "Tower of Hera - Compass Chest", + items => items.BigKeyTH), + new LegacyLocation(this, 256+119, 0x1E9F8, LocationType.Regular, "Tower of Hera - Big Chest", + items => items.BigKeyTH), + new LegacyLocation(this, 256+120, 0x308152, LocationType.Regular, "Tower of Hera - Moldorm", + items => items.BigKeyTH && CanBeatBoss(items)), + }; + } + + private bool CanBeatBoss(LegacyProgression items) { + return items.Sword || items.Hammer; + } + + public override bool CanEnter(LegacyProgression items) { + return (items.Mirror || items.Hookshot && items.Hammer) && LegacyWorld.CanEnter("Light World Death Mountain West", items); + } + + public bool CanComplete(LegacyProgression items) { + return GetLocation("Tower of Hera - Moldorm").Available(items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyTurtleRock.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyTurtleRock.cs new file mode 100644 index 000000000..97cc24fc0 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LegacyTurtleRock.cs @@ -0,0 +1,69 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyItemType; +using static Randomizer.SMZ3.LegacyWorldState; + +namespace Randomizer.SMZ3.Regions.Zelda { + + class LegacyTurtleRock : LegacyZ3Region, ILegacyReward, ILegacyMedallionAccess { + + public override string Name => "Turtle Rock"; + + public LegacyRewardType LegacyReward { get; set; } = LegacyRewardType.None; + public LegacyMedallion LegacyMedallion { get; set; } + + public LegacyTurtleRock(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Weight = 6; + RegionItems = new[] { KeyTR, BigKeyTR, MapTR, CompassTR }; + + Locations = new List { + new LegacyLocation(this, 256+177, 0x1EA22, LocationType.Regular, "Turtle Rock - Compass Chest"), + new LegacyLocation(this, 256+178, 0x1EA1C, LocationType.Regular, "Turtle Rock - Roller Room - Left", + items => items.Firerod), + new LegacyLocation(this, 256+179, 0x1EA1F, LocationType.Regular, "Turtle Rock - Roller Room - Right", + items => items.Firerod), + new LegacyLocation(this, 256+180, 0x1EA16, LocationType.Regular, "Turtle Rock - Chain Chomps", + items => items.KeyTR >= 1), + new LegacyLocation(this, 256+181, 0x1EA25, LocationType.Regular, "Turtle Rock - Big Key Chest", + items => items.KeyTR >= + (!LegacyConfig.Keysanity || GetLocation("Turtle Rock - Big Key Chest").ItemIs(BigKeyTR, LegacyWorld) ? 2 : + GetLocation("Turtle Rock - Big Key Chest").ItemIs(KeyTR, LegacyWorld) ? 3 : 4)) + .AlwaysAllow((item, items) => item.Is(KeyTR, LegacyWorld) && items.KeyTR >= 3), + new LegacyLocation(this, 256+182, 0x1EA19, LocationType.Regular, "Turtle Rock - Big Chest", + items => items.BigKeyTR && items.KeyTR >= 2) + .Allow((item, items) => item.IsNot(BigKeyTR, LegacyWorld)), + new LegacyLocation(this, 256+183, 0x1EA34, LocationType.Regular, "Turtle Rock - Crystaroller Room", + items => items.BigKeyTR && items.KeyTR >= 2), + new LegacyLocation(this, 256+184, 0x1EA28, LocationType.Regular, "Turtle Rock - Eye Bridge - Top Right", LaserBridge), + new LegacyLocation(this, 256+185, 0x1EA2B, LocationType.Regular, "Turtle Rock - Eye Bridge - Top Left", LaserBridge), + new LegacyLocation(this, 256+186, 0x1EA2E, LocationType.Regular, "Turtle Rock - Eye Bridge - Bottom Right", LaserBridge), + new LegacyLocation(this, 256+187, 0x1EA31, LocationType.Regular, "Turtle Rock - Eye Bridge - Bottom Left", LaserBridge), + new LegacyLocation(this, 256+188, 0x308159, LocationType.Regular, "Turtle Rock - Trinexx", + items => items.BigKeyTR && items.KeyTR >= 4 && items.Lamp && CanBeatBoss(items)), + }; + } + + bool LaserBridge(LegacyProgression items) { + return items.BigKeyTR && items.KeyTR >= 3 && items.Lamp && (items.Cape || items.Byrna || items.CanBlockLasers); + } + + bool CanBeatBoss(LegacyProgression items) { + return items.Firerod && items.Icerod; + } + + public override bool CanEnter(LegacyProgression items) { + return LegacyMedallion switch { + LegacyMedallion.Bombos => items.Bombos, + LegacyMedallion.Ether => items.Ether, + _ => items.Quake, + } && items.Sword && + items.MoonPearl && items.CanLiftHeavy() && items.Hammer && items.Somaria && + LegacyWorld.CanEnter("Light World Death Mountain East", items); + } + + public bool CanComplete(LegacyProgression items) { + return GetLocation("Turtle Rock - Trinexx").Available(items); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LightWorld/DeathMountain/LegacyEast.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LightWorld/DeathMountain/LegacyEast.cs new file mode 100644 index 000000000..dbc5ef455 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LightWorld/DeathMountain/LegacyEast.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; + +namespace Randomizer.SMZ3.Regions.Zelda.LightWorld.DeathMountain { + + class LegacyEast : LegacyZ3Region { + + public override string Name => "Light World Death Mountain East"; + public override string Area => "Death Mountain"; + + public LegacyEast(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 256+4, 0x308141, LocationType.Regular, "Floating Island", + items => items.Mirror && items.MoonPearl && items.CanLiftHeavy()), + new LegacyLocation(this, 256+5, 0x1E9BF, LocationType.Regular, "Spiral Cave"), + new LegacyLocation(this, 256+6, 0x1EB39, LocationType.Regular, "Paradox Cave Upper - Left"), + new LegacyLocation(this, 256+7, 0x1EB3C, LocationType.Regular, "Paradox Cave Upper - Right"), + new LegacyLocation(this, 256+8, 0x1EB2A, LocationType.Regular, "Paradox Cave Lower - Far Left"), + new LegacyLocation(this, 256+9, 0x1EB2D, LocationType.Regular, "Paradox Cave Lower - Left"), + new LegacyLocation(this, 256+10, 0x1EB36, LocationType.Regular, "Paradox Cave Lower - Middle"), + new LegacyLocation(this, 256+11, 0x1EB30, LocationType.Regular, "Paradox Cave Lower - Right"), + new LegacyLocation(this, 256+12, 0x1EB33, LocationType.Regular, "Paradox Cave Lower - Far Right"), + new LegacyLocation(this, 256+13, 0x1E9C5, LocationType.Regular, "Mimic Cave", + items => items.Mirror && items.KeyTR >= 2 && LegacyWorld.CanEnter("Turtle Rock", items)), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return LegacyWorld.CanEnter("Light World Death Mountain West", items) && ( + items.Hammer && items.Mirror || + items.Hookshot + ); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LightWorld/DeathMountain/LegacyWest.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LightWorld/DeathMountain/LegacyWest.cs new file mode 100644 index 000000000..41aed44a7 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LightWorld/DeathMountain/LegacyWest.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; + +namespace Randomizer.SMZ3.Regions.Zelda.LightWorld.DeathMountain { + + class LegacyWest : LegacyZ3Region { + + public override string Name => "Light World Death Mountain West"; + public override string Area => "Death Mountain"; + + public LegacyWest(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + Locations = new List { + new LegacyLocation(this, 256+0, 0x308016, LocationType.Ether, "Ether Tablet", + items => items.Book && items.MasterSword && (items.Mirror || items.Hammer && items.Hookshot)), + new LegacyLocation(this, 256+1, 0x308140, LocationType.Regular, "Spectacle Rock", + items => items.Mirror), + new LegacyLocation(this, 256+2, 0x308002, LocationType.Regular, "Spectacle Rock Cave"), + new LegacyLocation(this, 256+3, 0x1EE9FA, LocationType.Regular, "Old Man", + items => items.Lamp), + }; + } + + public override bool CanEnter(LegacyProgression items) { + return items.Flute || items.CanLiftLight() && items.Lamp || items.CanAccessDeathMountainPortal(); + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LightWorld/LegacyNorthEast.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LightWorld/LegacyNorthEast.cs new file mode 100644 index 000000000..2598b557b --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LightWorld/LegacyNorthEast.cs @@ -0,0 +1,34 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyRewardType; + +namespace Randomizer.SMZ3.Regions.Zelda.LightWorld { + + class LegacyNorthEast : LegacyZ3Region { + + public override string Name => "Light World North East"; + public override string Area => "Light World"; + + public LegacyNorthEast(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + var sphereOne = -10; + Locations = new List { + new LegacyLocation(this, 256+36, 0x1DE1C3, LocationType.Regular, "King Zora", + items => items.CanLiftLight() || items.Flippers), + new LegacyLocation(this, 256+37, 0x308149, LocationType.Regular, "Zora's Ledge", + items => items.Flippers), + new LegacyLocation(this, 256+254, 0x1E9B0, LocationType.Regular, "Waterfall Fairy - Left", + items => items.Flippers), + new LegacyLocation(this, 256+39, 0x1E9D1, LocationType.Regular, "Waterfall Fairy - Right", + items => items.Flippers), + new LegacyLocation(this, 256+40, 0x308014, LocationType.Regular, "Potion Shop", + items => items.Mushroom), + new LegacyLocation(this, 256+41, 0x1EA82, LocationType.Regular, "Sahasrahla's Hut - Left").Weighted(sphereOne), + new LegacyLocation(this, 256+42, 0x1EA85, LocationType.Regular, "Sahasrahla's Hut - Middle").Weighted(sphereOne), + new LegacyLocation(this, 256+43, 0x1EA88, LocationType.Regular, "Sahasrahla's Hut - Right").Weighted(sphereOne), + new LegacyLocation(this, 256+44, 0x5F1FC, LocationType.Regular, "Sahasrahla", + items => LegacyWorld.CanAcquire(items, PendantGreen)), + }; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LightWorld/LegacyNorthWest.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LightWorld/LegacyNorthWest.cs new file mode 100644 index 000000000..86aab6c29 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LightWorld/LegacyNorthWest.cs @@ -0,0 +1,50 @@ +using System.Collections.Generic; +using static Randomizer.SMZ3.LegacyRewardType; + +namespace Randomizer.SMZ3.Regions.Zelda.LightWorld { + + class LegacyNorthWest : LegacyZ3Region { + + public override string Name => "Light World North West"; + public override string Area => "Light World"; + + public LegacyNorthWest(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + var sphereOne = -14; + Locations = new List { + new LegacyLocation(this, 256+14, 0x589B0, LocationType.Pedestal, "Master Sword Pedestal", + items => LegacyWorld.CanAcquireAll(items, AnyPendant)), + new LegacyLocation(this, 256+15, 0x308013, LocationType.Regular, "Mushroom").Weighted(sphereOne), + new LegacyLocation(this, 256+16, 0x308000, LocationType.Regular, "Lost Woods Hideout").Weighted(sphereOne), + new LegacyLocation(this, 256+17, 0x308001, LocationType.Regular, "Lumberjack Tree", + items => LegacyWorld.CanAcquire(items, Agahnim) && items.Boots), + new LegacyLocation(this, 256+18, 0x1EB3F, LocationType.Regular, "Pegasus Rocks", + items => items.Boots), + new LegacyLocation(this, 256+19, 0x308004, LocationType.Regular, "Graveyard Ledge", + items => items.Mirror && items.MoonPearl && LegacyWorld.CanEnter("Dark World North West", items)), + new LegacyLocation(this, 256+20, 0x1E97A, LocationType.Regular, "King's Tomb", + items => items.Boots && ( + items.CanLiftHeavy() || + items.Mirror && items.MoonPearl && LegacyWorld.CanEnter("Dark World North West", items))), + new LegacyLocation(this, 256+21, 0x1EA8E, LocationType.Regular, "Kakariko Well - Top").Weighted(sphereOne), + new LegacyLocation(this, 256+22, 0x1EA91, LocationType.Regular, "Kakariko Well - Left").Weighted(sphereOne), + new LegacyLocation(this, 256+23, 0x1EA94, LocationType.Regular, "Kakariko Well - Middle").Weighted(sphereOne), + new LegacyLocation(this, 256+24, 0x1EA97, LocationType.Regular, "Kakariko Well - Right").Weighted(sphereOne), + new LegacyLocation(this, 256+25, 0x1EA9A, LocationType.Regular, "Kakariko Well - Bottom").Weighted(sphereOne), + new LegacyLocation(this, 256+26, 0x1EB0F, LocationType.Regular, "Blind's Hideout - Top").Weighted(sphereOne), + new LegacyLocation(this, 256+27, 0x1EB18, LocationType.Regular, "Blind's Hideout - Far Left").Weighted(sphereOne), + new LegacyLocation(this, 256+28, 0x1EB12, LocationType.Regular, "Blind's Hideout - Left").Weighted(sphereOne), + new LegacyLocation(this, 256+29, 0x1EB15, LocationType.Regular, "Blind's Hideout - Right").Weighted(sphereOne), + new LegacyLocation(this, 256+30, 0x1EB1B, LocationType.Regular, "Blind's Hideout - Far Right").Weighted(sphereOne), + new LegacyLocation(this, 256+31, 0x5EB18, LocationType.Regular, "Bottle Merchant").Weighted(sphereOne), + new LegacyLocation(this, 256+250, 0x1E9E9, LocationType.Regular, "Chicken House").Weighted(sphereOne), + new LegacyLocation(this, 256+33, 0x6B9CF, LocationType.Regular, "Sick Kid", + items => items.Bottle), + new LegacyLocation(this, 256+34, 0x1E9CE, LocationType.Regular, "Kakariko Tavern").Weighted(sphereOne), + new LegacyLocation(this, 256+35, 0x308015, LocationType.Regular, "Magic Bat", + items => items.Powder && (items.Hammer || items.MoonPearl && items.Mirror && items.CanLiftHeavy())), + }; + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LightWorld/LegacySouth.cs b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LightWorld/LegacySouth.cs new file mode 100644 index 000000000..f4e5bc7db --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/Regions/Zelda/LightWorld/LegacySouth.cs @@ -0,0 +1,51 @@ +using System.Collections.Generic; + +namespace Randomizer.SMZ3.Regions.Zelda.LightWorld { + + class LegacySouth : LegacyZ3Region { + + public override string Name => "Light World South"; + public override string Area => "Light World"; + + public LegacySouth(LegacyWorld legacyWorld, LegacyConfig legacyConfig) : base(legacyWorld, legacyConfig) { + var sphereOne = -10; + Locations = new List { + new LegacyLocation(this, 256+45, 0x308142, LocationType.Regular, "Maze Race").Weighted(sphereOne), + new LegacyLocation(this, 256+240, 0x308012, LocationType.Regular, "Library", + items => items.Boots), + new LegacyLocation(this, 256+241, 0x30814A, LocationType.Regular, "Flute Spot", + items => items.Shovel), + new LegacyLocation(this, 256+242, 0x308003, LocationType.Regular, "South of Grove", + items => items.Mirror && LegacyWorld.CanEnter("Dark World South", items)), + new LegacyLocation(this, 256+243, 0x1E9BC, LocationType.Regular, "Link's House").Weighted(sphereOne), + new LegacyLocation(this, 256+244, 0x1E9F2, LocationType.Regular, "Aginah's Cave").Weighted(sphereOne), + new LegacyLocation(this, 256+51, 0x1EB42, LocationType.Regular, "Mini Moldorm Cave - Far Left").Weighted(sphereOne), + new LegacyLocation(this, 256+52, 0x1EB45, LocationType.Regular, "Mini Moldorm Cave - Left").Weighted(sphereOne), + new LegacyLocation(this, 256+53, 0x308010, LocationType.Regular, "Mini Moldorm Cave - NPC").Weighted(sphereOne), + new LegacyLocation(this, 256+54, 0x1EB48, LocationType.Regular, "Mini Moldorm Cave - Right").Weighted(sphereOne), + new LegacyLocation(this, 256+251, 0x1EB4B, LocationType.Regular, "Mini Moldorm Cave - Far Right").Weighted(sphereOne), + new LegacyLocation(this, 256+252, 0x308143, LocationType.Regular, "Desert Ledge", + items => LegacyWorld.CanEnter("Desert Palace", items)), + new LegacyLocation(this, 256+253, 0x308005, LocationType.Regular, "Checkerboard Cave", + items => items.Mirror && ( + items.Flute && items.CanLiftHeavy() || + items.CanAccessMiseryMirePortal(LegacyConfig) + ) && items.CanLiftLight()), + new LegacyLocation(this, 256+58, 0x308017, LocationType.Bombos, "Bombos Tablet", + items => items.Book && items.MasterSword && items.Mirror && LegacyWorld.CanEnter("Dark World South", items)), + new LegacyLocation(this, 256+59, 0x1E98C, LocationType.Regular, "Floodgate Chest").Weighted(sphereOne), + new LegacyLocation(this, 256+60, 0x308145, LocationType.Regular, "Sunken Treasure").Weighted(sphereOne), + new LegacyLocation(this, 256+61, 0x308144, LocationType.Regular, "Lake Hylia Island", + items => items.Flippers && items.MoonPearl && items.Mirror && ( + LegacyWorld.CanEnter("Dark World South", items) || + LegacyWorld.CanEnter("Dark World North East", items))), + new LegacyLocation(this, 256+62, 0x6BE7D, LocationType.Regular, "Hobo", + items => items.Flippers), + new LegacyLocation(this, 256+63, 0x1EB4E, LocationType.Regular, "Ice Rod Cave").Weighted(sphereOne), + }; + + } + + } + +} diff --git a/src/TrackerCouncil.Smz3.LegacyLogic/TrackerCouncil.Smz3.LegacyLogic.csproj b/src/TrackerCouncil.Smz3.LegacyLogic/TrackerCouncil.Smz3.LegacyLogic.csproj new file mode 100644 index 000000000..40347bf23 --- /dev/null +++ b/src/TrackerCouncil.Smz3.LegacyLogic/TrackerCouncil.Smz3.LegacyLogic.csproj @@ -0,0 +1,13 @@ + + + + net8.0 + enable + disable + + + + + + + diff --git a/src/TrackerCouncil.Smz3.SeedGenerator/FileData/Patches/LocationsPatch.cs b/src/TrackerCouncil.Smz3.SeedGenerator/FileData/Patches/LocationsPatch.cs index c29d2956c..dbb7b541c 100644 --- a/src/TrackerCouncil.Smz3.SeedGenerator/FileData/Patches/LocationsPatch.cs +++ b/src/TrackerCouncil.Smz3.SeedGenerator/FileData/Patches/LocationsPatch.cs @@ -128,24 +128,35 @@ public override IEnumerable GetChanges(GetPatchesRequest data) return patches; } - public static List GetLocationsFromRom(byte[] rom, List playerNames, World exampleWorld, bool isMultiworldEnabled, List smz3ItemTypes) + public static List GetLocationsFromRom(byte[] rom, List playerNames, World exampleWorld, bool isMultiworldEnabled, List smz3ItemTypes, bool isCasRom) { var toReturn = new List(); foreach (var location in exampleWorld.Regions.OfType().SelectMany(x => x.Locations)) { - toReturn.Add(GetParsedLocationDetails(rom, location, true, playerNames, isMultiworldEnabled, smz3ItemTypes)); + toReturn.Add(GetParsedLocationDetails(rom, location, true, playerNames, isMultiworldEnabled, smz3ItemTypes, isCasRom)); } foreach (var location in exampleWorld.Regions.OfType().SelectMany(x => x.Locations)) { - toReturn.Add(GetParsedLocationDetails(rom, location, false, playerNames, isMultiworldEnabled, smz3ItemTypes)); + toReturn.Add(GetParsedLocationDetails(rom, location, false, playerNames, isMultiworldEnabled, smz3ItemTypes, isCasRom)); } return toReturn; } - private static ParsedRomLocationDetails GetParsedLocationDetails(byte[] rom, Location location, bool isSuperMetroidLocation, List playerNames, bool isMultiworldEnabled, List smz3ItemTypes) + private static Dictionary s_locationIdMappings = new() + { + { 256 + 230, 256 + 196 }, // GanonsTowerRandomizerRoomTopLeft + { 256 + 231, 256 + 197 }, // GanonsTowerRandomizerRoomTopRight + { 256 + 232, 256 + 198 }, // GanonsTowerRandomizerRoomBottomLeft + { 256 + 233, 256 + 199 }, // GanonsTowerRandomizerRoomBottomRight + { 256 + 234, 256 + 200 }, // GanonsTowerHopeRoomLeft + { 256 + 235, 256 + 201 }, // GanonsTowerHopeRoomRight + { 256 + 236, 256 + 202 }, // GanonsTowerTileRoom + }; + + private static ParsedRomLocationDetails GetParsedLocationDetails(byte[] rom, Location location, bool isSuperMetroidLocation, List playerNames, bool isMultiworldEnabled, List smz3ItemTypes, bool isCasRom) { var isLocal = false; ItemType itemType; @@ -153,9 +164,15 @@ private static ParsedRomLocationDetails GetParsedLocationDetails(byte[] rom, Loc var isProgression = false; var itemName = ""; + var id = (int)location.Id; + if (!isCasRom && s_locationIdMappings.TryGetValue(id, out var newId)) + { + id = newId; + } + if (isMultiworldEnabled) { - var address = 0x386000 + (int)location.Id * 8; + var address = 0x386000 + id * 8; var bytes = rom.Skip(address).Take(8).ToArray(); isLocal = BitConverter.ToInt16(bytes, 0) == 0; var itemNumber = BitConverter.ToInt16(bytes, 2); diff --git a/src/TrackerCouncil.Smz3.SeedGenerator/Generation/Smz3RomParser.cs b/src/TrackerCouncil.Smz3.SeedGenerator/Generation/Smz3RomParser.cs index 97feff44d..f19d8a445 100644 --- a/src/TrackerCouncil.Smz3.SeedGenerator/Generation/Smz3RomParser.cs +++ b/src/TrackerCouncil.Smz3.SeedGenerator/Generation/Smz3RomParser.cs @@ -51,8 +51,7 @@ public ParsedRomDetails ParseRomFile(string filePath) var itemTypes = configs.Items.Where(x => x.InternalItemType != ItemType.Nothing) .Select(x => (int)x.InternalItemType).ToList(); - var locations = - LocationsPatch.GetLocationsFromRom(rom, playerNames, exampleWorld, isMultiworldEnabled, itemTypes); + var prerequisites = MedallionPatch.GetPrerequisitesFromRom(rom, exampleWorld.PrerequisiteRegions); var bosses = exampleWorld.BossRegions.Select(x => new ParsedRomBossDetails() { @@ -111,6 +110,8 @@ public ParsedRomDetails ParseRomFile(string filePath) throw new InvalidOperationException("Could not determine rom generator from the rom details"); } + var locations = + LocationsPatch.GetLocationsFromRom(rom, playerNames, exampleWorld, isMultiworldEnabled, itemTypes, romGenerator == RomGenerator.Cas); var rewards = ZeldaRewardsPatch.GetRewardsFromRom(rom, exampleWorld.RewardRegions, romGenerator == RomGenerator.Cas); var startingItems = StartingEquipmentPatch.GetStartingItemList(rom, romGenerator == RomGenerator.Cas); @@ -156,6 +157,7 @@ public SeedData GenerateSeedData(RandomizerOptions options, ParsedRomDetails par config.PlayerName = parsedRomDetails.Players.First(x => x.IsLocalPlayer).PlayerName; config.ParsedRomDetails = parsedRomDetails; config.GameMode = parsedRomDetails.IsMultiworld ? GameMode.Multiworld : GameMode.Normal; + config.LegacyMetroidLogic = parsedRomDetails.IsHardLogic ? SMLogic.Hard : SMLogic.Normal; config.RomGenerator = parsedRomDetails.RomGenerator; config.KeysanityMode = parsedRomDetails.KeysanityMode; config.Seed = parsedRomDetails.Seed.ToString(); diff --git a/src/TrackerCouncil.Smz3.Shared/Enums/LocationId.cs b/src/TrackerCouncil.Smz3.Shared/Enums/LocationId.cs index ae7e730f9..23e0fd086 100644 --- a/src/TrackerCouncil.Smz3.Shared/Enums/LocationId.cs +++ b/src/TrackerCouncil.Smz3.Shared/Enums/LocationId.cs @@ -307,7 +307,7 @@ public enum LocationId GanonsTowerRandomizerRoomBottomRight = 256 + 199, // 233 in main GanonsTowerHopeRoomLeft = 256 + 200, // 234 in main GanonsTowerHopeRoomRight = 256 + 201, // 235 in main - GanonsTowerTileRoom = 256 + 202, + GanonsTowerTileRoom = 256 + 202, // 236 in main GanonsTowerCompassRoomTopLeft = 256 + 203, GanonsTowerCompassRoomTopRight = 256 + 204, GanonsTowerCompassRoomBottomLeft = 256 + 205, diff --git a/src/TrackerCouncil.Smz3.Tracking/Services/PlayerProgressionService.cs b/src/TrackerCouncil.Smz3.Tracking/Services/PlayerProgressionService.cs index 0d3c945e0..abd11cb73 100644 --- a/src/TrackerCouncil.Smz3.Tracking/Services/PlayerProgressionService.cs +++ b/src/TrackerCouncil.Smz3.Tracking/Services/PlayerProgressionService.cs @@ -74,6 +74,12 @@ public Progression GetProgression(bool assumeKeys) progression.Add(boss); } + if (_world.World.Config.RomGenerator != RomGenerator.Cas) + { + progression.InitLegacyProgression(); + _world.World.UpdateLegacyWorld(); + } + _progression[key] = progression; return progression; } diff --git a/src/TrackerCouncil.Smz3.Tracking/Services/WorldQueryService.cs b/src/TrackerCouncil.Smz3.Tracking/Services/WorldQueryService.cs index 29535fc27..df5001b4b 100644 --- a/src/TrackerCouncil.Smz3.Tracking/Services/WorldQueryService.cs +++ b/src/TrackerCouncil.Smz3.Tracking/Services/WorldQueryService.cs @@ -72,7 +72,9 @@ public IEnumerable AccessibleLocations(Progression progression) /// If keys and keycards should be assumed for the player /// public IEnumerable AccessibleLocations(bool assumeKeys) - => AccessibleLocations(Progression(assumeKeys)).ToImmutableList(); + => UnclearedLocations().Where(x => + x.Accessibility == Accessibility.Available || + (assumeKeys && x.Accessibility == Accessibility.AvailableWithKeys)).ToImmutableList(); /// /// Retrieves all uncleared locations for the current player's world, regardless of if diff --git a/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerBossService.cs b/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerBossService.cs index 9bc653dea..442057e2c 100644 --- a/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerBossService.cs +++ b/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerBossService.cs @@ -269,6 +269,15 @@ public void UpdateAccessibility(IHasBoss region, Progression? actualProgression actualProgression ??= playerProgressionService.GetProgression(false); withKeysProgression ??= playerProgressionService.GetProgression(true); - region.Boss.UpdateAccessibility(actualProgression, withKeysProgression); + if (region.World.Config.RomGenerator == RomGenerator.Cas) + { + region.Boss.UpdateAccessibility(actualProgression, withKeysProgression); + } + else + { + var actuallyAccessible = region.World.LegacyWorld!.CanCompleteRegion(region.Name, (int?)region.BossLocationId, actualProgression.LegacyProgression); + var withKeysAccessible = region.World.LegacyWorld!.CanCompleteRegion(region.Name, (int?)region.BossLocationId, withKeysProgression.LegacyProgression); + region.Boss.UpdateLegacyAccessibility(actuallyAccessible, withKeysAccessible); + } } } diff --git a/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerLocationService.cs b/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerLocationService.cs index 33f2a6955..a49afc039 100644 --- a/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerLocationService.cs +++ b/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerLocationService.cs @@ -76,7 +76,7 @@ public void Clear(Location location, float? confidence = null, bool autoTracked var isKeysanityForLocation = (location.Region is Z3Region && World.Config.ZeldaKeysanity) || (location.Region is SMRegion && World.Config.MetroidKeysanity); var items = playerProgressionService.GetProgression(!isKeysanityForLocation); - if (!location.IsAvailable(items) && (confidence >= Options.MinimumSassConfidence || autoTracked)) + if (location.Accessibility is not (Accessibility.Available or Accessibility.AvailableWithKeys) && (confidence >= Options.MinimumSassConfidence || autoTracked)) { var locationInfo = location.Metadata; var roomInfo = location.Room?.Metadata; @@ -293,7 +293,7 @@ public void ClearArea(IHasLocations area, bool trackItems, bool includeUnavailab { var locations = area.Locations .Where(x => x.Cleared == false) - .WhereUnless(includeUnavailable, x => x.IsAvailable(playerProgressionService.GetProgression(area))) + .WhereUnless(includeUnavailable, x => x.Accessibility is Accessibility.Available or Accessibility.AvailableWithKeys) .ToImmutableList(); playerProgressionService.ResetProgression(); @@ -380,7 +380,7 @@ public void ClearArea(IHasLocations area, bool trackItems, bool includeUnavailab else { var progression = playerProgressionService.GetProgression(area); - var someOutOfLogicLocation = locations.Where(x => !x.IsAvailable(progression)).Random(Random); + var someOutOfLogicLocation = locations.Where(x => x.Accessibility is not (Accessibility.Available or Accessibility.AvailableWithKeys)).Random(Random); if (someOutOfLogicLocation != null && confidence >= Options.MinimumSassConfidence) { var someOutOfLogicItem = someOutOfLogicLocation.Item; @@ -528,10 +528,40 @@ public void UpdateAccessibility(Location location, Progression? actualProgressio { actualProgression ??= playerProgressionService.GetProgression(false); withKeysProgression ??= playerProgressionService.GetProgression(true); + if (location.Region is HyruleCastle) { withKeysProgression = actualProgression; } - location.UpdateAccessibility(actualProgression, withKeysProgression); + + if (location.World.Config.RomGenerator == RomGenerator.Cas) + { + location.UpdateAccessibility(actualProgression, withKeysProgression); + } + else + { + try + { + var legacyActuallyAccessible = + location.World.LegacyWorld!.IsLocationAccessible((int)location.Id, + actualProgression.LegacyProgression); + var legacyAccessibleWithKeys = actualProgression == withKeysProgression + ? legacyActuallyAccessible + : location.World.LegacyWorld!.IsLocationAccessible((int)location.Id, + withKeysProgression.LegacyProgression); + location.UpdateLegacyAccessibility(legacyActuallyAccessible, legacyAccessibleWithKeys); + } + catch (Exception e) + { + var legacyActuallyAccessible = + location.World.LegacyWorld!.IsLocationAccessible((int)location.Id, + actualProgression.LegacyProgression); + var legacyAccessibleWithKeys = actualProgression == withKeysProgression + ? legacyActuallyAccessible + : location.World.LegacyWorld!.IsLocationAccessible((int)location.Id, + withKeysProgression.LegacyProgression); + location.UpdateLegacyAccessibility(legacyActuallyAccessible, legacyAccessibleWithKeys); + } + } } } diff --git a/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerRewardService.cs b/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerRewardService.cs index 636b68047..fad0db446 100644 --- a/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerRewardService.cs +++ b/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerRewardService.cs @@ -148,6 +148,18 @@ public void UpdateAccessibility(IHasReward region, Progression? actualProgressio actualProgression ??= playerProgressionService.GetProgression(false); withKeysProgression ??= playerProgressionService.GetProgression(true); - region.Reward.UpdateAccessibility(actualProgression, withKeysProgression); + + if (region.World.Config.RomGenerator == RomGenerator.Cas) + { + region.Reward.UpdateAccessibility(actualProgression, withKeysProgression); + } + else + { + var regionLLocationId = region.Region.Locations.First().Id; + var actuallyAccessible = region.World.LegacyWorld!.CanCompleteRegion(region.Name, (int?)regionLLocationId, actualProgression.LegacyProgression); + var withKeysAccessible = region.World.LegacyWorld!.CanCompleteRegion(region.Name, (int?)regionLLocationId, withKeysProgression.LegacyProgression); + region.Reward.UpdateLegacyAccessibility(actuallyAccessible, withKeysAccessible); + } + } } diff --git a/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerTreasureService.cs b/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerTreasureService.cs index d63cfe416..ff684fdc1 100644 --- a/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerTreasureService.cs +++ b/src/TrackerCouncil.Smz3.Tracking/TrackingServices/TrackerTreasureService.cs @@ -8,6 +8,7 @@ using TrackerCouncil.Smz3.Data.WorldData; using TrackerCouncil.Smz3.Data.WorldData.Regions; using TrackerCouncil.Smz3.Shared; +using TrackerCouncil.Smz3.Shared.Enums; using TrackerCouncil.Smz3.Tracking.Services; namespace TrackerCouncil.Smz3.Tracking.TrackingServices; @@ -174,7 +175,7 @@ public void ClearDungeon(IHasTreasure treasureRegion, float? confidence = null) Tracker.Say(x => x.DungeonCleared, args: [treasureRegion.Metadata.Name]); - var inaccessibleLocations = locations.Where(x => !x.IsAvailable(progress)).ToList(); + var inaccessibleLocations = locations.Where(x => x.Accessibility is not (Accessibility.Available or Accessibility.AvailableWithKeys)).ToList(); if (inaccessibleLocations.Count > 0 && confidence >= Options.MinimumSassConfidence) { var anyMissedLocation = inaccessibleLocations.Random(Random) ?? inaccessibleLocations.First(); diff --git a/tests/TrackerCouncil.Smz3.Tests/LogicTests/LocationLogicTests.cs b/tests/TrackerCouncil.Smz3.Tests/LogicTests/LocationLogicTests.cs index e76c191e6..20019727b 100644 --- a/tests/TrackerCouncil.Smz3.Tests/LogicTests/LocationLogicTests.cs +++ b/tests/TrackerCouncil.Smz3.Tests/LogicTests/LocationLogicTests.cs @@ -24,6 +24,7 @@ public LocationLogicTests() public void LocationWithoutLogicNeverHasMissingItems() { var emptyProgression = new Progression(); + World.HyruleCastle.LinksUncle.UpdateAccessibility(emptyProgression, emptyProgression); var missingItems = Logic.GetMissingRequiredItems(World.HyruleCastle.LinksUncle, emptyProgression, out _); missingItems.Should().BeEmpty(); } @@ -32,6 +33,7 @@ public void LocationWithoutLogicNeverHasMissingItems() public void LocationWithSatisfiedLogicHasNoMissingItems() { var progression = new Progression(new[] { new Item(ItemType.Boots, World) }, new List(), new List()); + World.LightWorldSouth.Library.UpdateAccessibility(progression, progression); var missingItems = Logic.GetMissingRequiredItems(World.LightWorldSouth.Library, progression, out _); missingItems.Should().BeEmpty(); } diff --git a/tests/TrackerCouncil.Smz3.Tests/LogicTests/LogicConfigTests.cs b/tests/TrackerCouncil.Smz3.Tests/LogicTests/LogicConfigTests.cs index 507cc8c60..555f3afe9 100644 --- a/tests/TrackerCouncil.Smz3.Tests/LogicTests/LogicConfigTests.cs +++ b/tests/TrackerCouncil.Smz3.Tests/LogicTests/LogicConfigTests.cs @@ -50,6 +50,7 @@ public void TestPreventScrewAttackSoftLock() config.LogicConfig.PreventScrewAttackSoftLock = false; World tempWorld = new World(config, "", 0, ""); var progression = new Progression(new[] { ItemType.ScrewAttack }, new List(), new List()); + tempWorld.FindLocation(LocationId.CrateriaTerminator).UpdateAccessibility(progression, progression); var missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaTerminator), progression, out _); missingItems.Should().BeEmpty(); tempWorld.FindLocation(LocationId.CrateriaTerminator).IsAvailable(progression).Should().BeTrue(); @@ -57,6 +58,7 @@ public void TestPreventScrewAttackSoftLock() config.LogicConfig.PreventScrewAttackSoftLock = true; tempWorld = new World(config, "", 0, ""); progression = new Progression(new[] { ItemType.ScrewAttack }, new List(), new List()); + tempWorld.FindLocation(LocationId.CrateriaTerminator).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaTerminator), progression, out _); missingItems.Should().HaveCount(1) .And.ContainEquivalentOf(new[] { ItemType.Morph }); @@ -71,6 +73,7 @@ public void TestRequireTwoPowerBombs() config.LogicConfig.PreventFivePowerBombSeed = false; World tempWorld = new World(config, "", 0, ""); var progression = new Progression(new[] { ItemType.Morph, ItemType.PowerBomb }, new List(), new List()); + tempWorld.FindLocation(LocationId.CrateriaTerminator).UpdateAccessibility(progression, progression); var missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaTerminator), progression, out _); missingItems.Should().BeEmpty(); tempWorld.FindLocation(LocationId.CrateriaTerminator).IsAvailable(progression).Should().BeTrue(); @@ -78,6 +81,7 @@ public void TestRequireTwoPowerBombs() config.LogicConfig.PreventFivePowerBombSeed = true; tempWorld = new World(config, "", 0, ""); progression = new Progression(new[] { ItemType.Morph, ItemType.PowerBomb }, new List(), new List()); + tempWorld.FindLocation(LocationId.CrateriaTerminator).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaTerminator), progression, out _); missingItems.Should().HaveCount(3) .And.ContainEquivalentOf(new[] { ItemType.Bombs }) @@ -86,6 +90,7 @@ public void TestRequireTwoPowerBombs() tempWorld.FindLocation(LocationId.CrateriaTerminator).IsAvailable(progression).Should().BeFalse(); progression = new Progression(new[] { ItemType.Morph, ItemType.PowerBomb, ItemType.PowerBomb }, new List(), new List()); + tempWorld.FindLocation(LocationId.CrateriaTerminator).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaTerminator), progression, out _); missingItems.Should().BeEmpty(); tempWorld.FindLocation(LocationId.CrateriaTerminator).IsAvailable(progression).Should().BeTrue(); @@ -99,6 +104,7 @@ public void TestFireRodDarkRooms() config.LogicConfig.FireRodDarkRooms = false; World tempWorld = new World(config, "", 0, ""); var progression = new Progression(); + tempWorld.FindLocation(LocationId.SewersDarkCross).UpdateAccessibility(progression, progression); var missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.SewersDarkCross), progression, out _); missingItems.Should().HaveCount(1) .And.ContainEquivalentOf(new[] { ItemType.Lamp }); @@ -107,6 +113,7 @@ public void TestFireRodDarkRooms() config.LogicConfig.FireRodDarkRooms = true; tempWorld = new World(config, "", 0, ""); progression = new Progression(); + tempWorld.FindLocation(LocationId.SewersDarkCross).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.SewersDarkCross), progression, out _); missingItems.Should().HaveCount(2) .And.ContainEquivalentOf(new[] { ItemType.Lamp }) @@ -114,6 +121,7 @@ public void TestFireRodDarkRooms() tempWorld.FindLocation(LocationId.SewersDarkCross).IsAvailable(progression).Should().BeFalse(); progression = new Progression(new[] { ItemType.Firerod }, new List(), new List()); + tempWorld.FindLocation(LocationId.SewersDarkCross).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.SewersDarkCross), progression, out _); missingItems.Should().BeEmpty(); tempWorld.FindLocation(LocationId.SewersDarkCross).IsAvailable(progression).Should().BeTrue(); @@ -127,6 +135,7 @@ public void TestInfiniteBombJump() config.LogicConfig.InfiniteBombJump = false; World tempWorld = new World(config, "", 0, ""); var progression = new Progression(new[] { ItemType.Morph, ItemType.PowerBomb, ItemType.Bombs }, new List(), new List()); + tempWorld.FindLocation(LocationId.CrateriaPowerBomb).UpdateAccessibility(progression, progression); var missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaPowerBomb), progression, out _); missingItems.Should().HaveCount(2) .And.ContainEquivalentOf(new[] { ItemType.SpaceJump }) @@ -136,6 +145,7 @@ public void TestInfiniteBombJump() config.LogicConfig.InfiniteBombJump = true; tempWorld = new World(config, "", 0, ""); progression = new Progression(new[] { ItemType.Morph, ItemType.PowerBomb, ItemType.Bombs }, new List(), new List()); + tempWorld.FindLocation(LocationId.CrateriaPowerBomb).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaPowerBomb), progression, out _); missingItems.Should().BeEmpty(); tempWorld.FindLocation(LocationId.CrateriaPowerBomb).IsAvailable(progression).Should().BeTrue(); @@ -149,6 +159,7 @@ public void TestParlorSpeedBooster() config.LogicConfig.ParlorSpeedBooster = false; World tempWorld = new World(config, "", 0, ""); var progression = new Progression(); + tempWorld.FindLocation(LocationId.CrateriaTerminator).UpdateAccessibility(progression, progression); var missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaTerminator), progression, out _); missingItems.Should().HaveCount(7) .And.NotContainEquivalentOf(new[] { ItemType.SpeedBooster }); @@ -157,12 +168,14 @@ public void TestParlorSpeedBooster() config.LogicConfig.ParlorSpeedBooster = true; tempWorld = new World(config, "", 0, ""); progression = new Progression(); + tempWorld.FindLocation(LocationId.CrateriaTerminator).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaTerminator), progression, out _); missingItems.Should().HaveCount(8) .And.ContainEquivalentOf(new[] { ItemType.SpeedBooster }); tempWorld.FindLocation(LocationId.CrateriaTerminator).IsAvailable(progression).Should().BeFalse(); progression = new Progression(new[] { ItemType.SpeedBooster }, new List(), new List()); + tempWorld.FindLocation(LocationId.CrateriaTerminator).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaTerminator), progression, out _); missingItems.Should().BeEmpty(); tempWorld.FindLocation(LocationId.CrateriaTerminator).IsAvailable(progression).Should().BeTrue(); @@ -176,6 +189,7 @@ public void TestMockBall() config.LogicConfig.MockBall = false; World tempWorld = new World(config, "", 0, ""); var progression = new Progression(new[] { ItemType.Morph, ItemType.Bombs , ItemType.Missile , ItemType.PowerBomb, ItemType.ScrewAttack }, new List(), new List()); + tempWorld.FindLocation(LocationId.GreenBrinstarEarlySupersTop).UpdateAccessibility(progression, progression); var missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.GreenBrinstarEarlySupersTop), progression, out _); missingItems.Should().HaveCount(1) .And.ContainEquivalentOf(new[] { ItemType.SpeedBooster }); @@ -183,11 +197,13 @@ public void TestMockBall() config.LogicConfig.MockBall = true; tempWorld = new World(config, "", 0, ""); + tempWorld.FindLocation(LocationId.GreenBrinstarEarlySupersTop).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.GreenBrinstarEarlySupersTop), progression, out _); missingItems.Should().BeEmpty(); tempWorld.FindLocation(LocationId.GreenBrinstarEarlySupersTop).IsAvailable(progression).Should().BeTrue(); progression = new Progression(new[] { ItemType.Bombs, ItemType.Missile, ItemType.PowerBomb, ItemType.ScrewAttack }, new List(), new List()); + tempWorld.FindLocation(LocationId.GreenBrinstarEarlySupersTop).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.GreenBrinstarEarlySupersTop), progression, out _); missingItems.Should().HaveCount(2) .And.ContainEquivalentOf(new[] { ItemType.SpeedBooster }) @@ -203,6 +219,7 @@ public void TestSwordOnlyDarkRoom() config.LogicConfig.SwordOnlyDarkRooms = false; World tempWorld = new World(config, "", 0, ""); var progression = new Progression(new[] { ItemType.ProgressiveSword }, new List(), new List()); + tempWorld.EasternPalace.BigKeyChest.UpdateAccessibility(progression, progression); var missingItems = Logic.GetMissingRequiredItems(tempWorld.EasternPalace.BigKeyChest, progression, out _); missingItems.Should().HaveCount(1) .And.ContainEquivalentOf(new[] { ItemType.Lamp }); @@ -210,11 +227,13 @@ public void TestSwordOnlyDarkRoom() config.LogicConfig.SwordOnlyDarkRooms = true; tempWorld = new World(config, "", 0, ""); + tempWorld.EasternPalace.BigKeyChest.UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.EasternPalace.BigKeyChest, progression, out _); missingItems.Should().BeEmpty(); tempWorld.EasternPalace.BigKeyChest.IsAvailable(progression).Should().BeTrue(); progression = new Progression(); + tempWorld.EasternPalace.BigKeyChest.UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.EasternPalace.BigKeyChest, progression, out _); missingItems.Should().HaveCount(3) .And.ContainEquivalentOf(new[] { ItemType.ProgressiveSword }) @@ -230,9 +249,12 @@ public void TestLightWorldSouthFakeFlippers() config.LogicConfig.LightWorldSouthFakeFlippers = false; var tempWorld = new World(config, "", 0, ""); var progression = new Progression(); + tempWorld.LightWorldSouth.UnderTheBridge.UpdateAccessibility(progression, progression); var missingItems = Logic.GetMissingRequiredItems(tempWorld.LightWorldSouth.UnderTheBridge, progression, out _); missingItems.Should().HaveCount(1) .And.ContainEquivalentOf(new[] { ItemType.Flippers }); + + tempWorld.LightWorldNorthEast.WaterfallFairy.Locations.First().UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.LightWorldNorthEast.WaterfallFairy.Locations.First(), progression, out _); missingItems.Should().HaveCount(1) .And.ContainEquivalentOf(new[] { ItemType.Flippers }); @@ -240,8 +262,10 @@ public void TestLightWorldSouthFakeFlippers() config.LogicConfig.LightWorldSouthFakeFlippers = true; tempWorld = new World(config, "", 0, ""); + tempWorld.LightWorldSouth.UnderTheBridge.UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.LightWorldSouth.UnderTheBridge, progression, out _); missingItems.Should().BeEmpty(); + tempWorld.LightWorldNorthEast.WaterfallFairy.Locations.First().UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.LightWorldNorthEast.WaterfallFairy.Locations.First(), progression, out _); missingItems.Should().HaveCount(2) .And.ContainEquivalentOf(new[] { ItemType.Flippers }) @@ -249,6 +273,7 @@ public void TestLightWorldSouthFakeFlippers() tempWorld.LightWorldNorthEast.WaterfallFairy.Locations.First().IsAvailable(progression).Should().BeFalse(); progression = new Progression(new[] { ItemType.MoonPearl }, new List(), new List()); + tempWorld.LightWorldNorthEast.WaterfallFairy.Locations.First().UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.LightWorldNorthEast.WaterfallFairy.Locations.First(), progression, out _); missingItems.Should().BeEmpty(); tempWorld.LightWorldNorthEast.WaterfallFairy.Locations.First().IsAvailable(progression).Should().BeTrue(); @@ -281,6 +306,7 @@ public void LeftSandPitRequiresSpringBallIfConfigured() var tempWorld = new World(config, "", 0, ""); var progression = new Progression(new[] { ItemType.CardMaridiaL1, ItemType.Morph, ItemType.Super, ItemType.PowerBomb, ItemType.Gravity, ItemType.SpaceJump }, new List(), new List()); + tempWorld.FindLocation(LocationId.InnerMaridiaWestSandHoleLeft).UpdateAccessibility(progression, progression); var missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.InnerMaridiaWestSandHoleLeft), progression, out _); missingItems.Should().HaveCount(1) .And.ContainEquivalentOf(new[] { ItemType.SpringBall }); @@ -288,6 +314,7 @@ public void LeftSandPitRequiresSpringBallIfConfigured() tempWorld.FindLocation(LocationId.InnerMaridiaWestSandHoleRight).IsAvailable(progression).Should().BeFalse(); progression = new Progression(new[] { ItemType.CardMaridiaL1, ItemType.Morph, ItemType.Super, ItemType.PowerBomb, ItemType.Gravity, ItemType.SpaceJump, ItemType.SpringBall, ItemType.HiJump }, new List(), new List()); + tempWorld.FindLocation(LocationId.InnerMaridiaWestSandHoleLeft).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.InnerMaridiaWestSandHoleLeft), progression, out _); missingItems.Should().BeEmpty(); tempWorld.FindLocation(LocationId.InnerMaridiaWestSandHoleLeft).IsAvailable(progression).Should().BeTrue(); @@ -302,18 +329,21 @@ public void TestLaunchPadRequiresIceBeam() config.LogicConfig.LaunchPadRequiresIceBeam = false; var tempWorld = new World(config, "", 0, ""); var progression = new Progression(new[] { ItemType.ETank, ItemType.ETank, ItemType.Morph, ItemType.SpeedBooster, ItemType.PowerBomb, ItemType.PowerBomb}, new List(), new List()); + tempWorld.FindLocation(LocationId.CrateriaSuper).UpdateAccessibility(progression, progression); var missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaSuper), progression, out _); missingItems.Should().BeEmpty(); tempWorld.FindLocation(LocationId.CrateriaSuper).IsAvailable(progression).Should().BeTrue(); config.LogicConfig.LaunchPadRequiresIceBeam = true; tempWorld = new World(config, "", 0, ""); + tempWorld.FindLocation(LocationId.CrateriaSuper).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaSuper), progression, out _); missingItems.Should().HaveCount(1) .And.ContainEquivalentOf(new[] { ItemType.Ice }); tempWorld.FindLocation(LocationId.CrateriaSuper).IsAvailable(progression).Should().BeFalse(); progression = new Progression(new[] { ItemType.ETank, ItemType.ETank, ItemType.Morph, ItemType.SpeedBooster, ItemType.PowerBomb, ItemType.PowerBomb, ItemType.Ice }, new List(), new List()); + tempWorld.FindLocation(LocationId.CrateriaSuper).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaSuper), progression, out _); missingItems.Should().BeEmpty(); tempWorld.FindLocation(LocationId.CrateriaSuper).IsAvailable(progression).Should().BeTrue(); @@ -327,18 +357,21 @@ public void TestWaterwayNeedsGravitySuit() config.LogicConfig.WaterwayNeedsGravitySuit = false; var tempWorld = new World(config, "", 0, ""); var progression = new Progression(new[] { ItemType.ETank, ItemType.ETank, ItemType.Morph, ItemType.SpeedBooster, ItemType.PowerBomb, ItemType.PowerBomb, ItemType.Missile }, new List(), new List()); + tempWorld.FindLocation(LocationId.PinkBrinstarWaterwayEnergyTank).UpdateAccessibility(progression, progression); var missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.PinkBrinstarWaterwayEnergyTank), progression, out _); missingItems.Should().BeEmpty(); tempWorld.FindLocation(LocationId.PinkBrinstarWaterwayEnergyTank).IsAvailable(progression).Should().BeTrue(); config.LogicConfig.WaterwayNeedsGravitySuit = true; tempWorld = new World(config, "", 0, ""); + tempWorld.FindLocation(LocationId.PinkBrinstarWaterwayEnergyTank).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.PinkBrinstarWaterwayEnergyTank), progression, out _); missingItems.Should().HaveCount(1) .And.ContainEquivalentOf(new[] { ItemType.Gravity }); tempWorld.FindLocation(LocationId.PinkBrinstarWaterwayEnergyTank).IsAvailable(progression).Should().BeFalse(); progression = new Progression(new[] { ItemType.ETank, ItemType.ETank, ItemType.Morph, ItemType.SpeedBooster, ItemType.PowerBomb, ItemType.PowerBomb, ItemType.Missile, ItemType.Gravity }, new List(), new List()); + tempWorld.FindLocation(LocationId.CrateriaSuper).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaSuper), progression, out _); missingItems.Should().BeEmpty(); tempWorld.FindLocation(LocationId.PinkBrinstarWaterwayEnergyTank).IsAvailable(progression).Should().BeTrue(); @@ -352,12 +385,14 @@ public void TestEasyEastCrateriaSkyItem() config.LogicConfig.EasyEastCrateriaSkyItem = false; var tempWorld = new World(config, "", 0, ""); var progression = new Progression(new[] { ItemType.ETank, ItemType.ETank, ItemType.Morph, ItemType.PowerBomb, ItemType.PowerBomb, ItemType.Super, ItemType.Gravity, ItemType.Grapple }, new List(), new List() { BossType.Phantoon }); + tempWorld.FindLocation(LocationId.CrateriaWestOceanSky).UpdateAccessibility(progression, progression); var missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaWestOceanSky), progression, out _); missingItems.Should().BeEmpty(); tempWorld.FindLocation(LocationId.CrateriaWestOceanSky).IsAvailable(progression).Should().BeTrue(); config.LogicConfig.EasyEastCrateriaSkyItem = true; tempWorld = new World(config, "", 0, ""); + tempWorld.FindLocation(LocationId.CrateriaWestOceanSky).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaWestOceanSky), progression, out _); missingItems.Should().HaveCount(2) .And.ContainEquivalentOf(new[] { ItemType.SpaceJump }) @@ -366,6 +401,7 @@ public void TestEasyEastCrateriaSkyItem() tempWorld.FindLocation(LocationId.CrateriaWestOceanSky).IsAvailable(progression).Should().BeFalse(); progression = new Progression(new[] { ItemType.ETank, ItemType.ETank, ItemType.Morph, ItemType.PowerBomb, ItemType.PowerBomb, ItemType.Super, ItemType.Gravity, ItemType.Grapple, ItemType.SpaceJump }, new List(), new List() { BossType.Phantoon }); + tempWorld.FindLocation(LocationId.CrateriaWestOceanSky).UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.CrateriaWestOceanSky), progression, out _); missingItems.Should().BeEmpty(); tempWorld.FindLocation(LocationId.CrateriaWestOceanSky).IsAvailable(progression).Should().BeTrue(); @@ -393,28 +429,33 @@ public void TestKholdstareNeedsCaneOfSomaria() config.LogicConfig.KholdstareNeedsCaneOfSomaria = false; var tempWorld = new World(config, "", 0, ""); var progression = new Progression(items, Array.Empty(), Array.Empty()); + tempWorld.IcePalace.KholdstareReward.UpdateAccessibility(progression, progression); var missingItems = Logic.GetMissingRequiredItems(tempWorld.IcePalace.KholdstareReward, progression, out _); missingItems.Should().HaveCount(2) .And.ContainEquivalentOf(new[] { ItemType.KeyIP }) .And.ContainEquivalentOf(new[] { ItemType.Somaria }); progression = new Progression(items.Append(ItemType.KeyIP), Array.Empty(), Array.Empty()); + tempWorld.IcePalace.KholdstareReward.UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.IcePalace.KholdstareReward, progression, out _); missingItems.Should().BeEmpty(); config.LogicConfig.KholdstareNeedsCaneOfSomaria = true; tempWorld = new World(config, "", 0, ""); progression = new Progression(items, Array.Empty(), Array.Empty()); + tempWorld.IcePalace.KholdstareReward.UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.IcePalace.KholdstareReward, progression, out _); missingItems.Should().HaveCount(1) .And.ContainEquivalentOf(new[] { ItemType.Somaria }); progression = new Progression(items.Append(ItemType.KeyIP), Array.Empty(), Array.Empty()); + tempWorld.IcePalace.KholdstareReward.UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.IcePalace.KholdstareReward, progression, out _); missingItems.Should().HaveCount(1) .And.ContainEquivalentOf(new[] { ItemType.Somaria }); progression = new Progression(items.Append(ItemType.Somaria), Array.Empty(), Array.Empty()); + tempWorld.IcePalace.KholdstareReward.UpdateAccessibility(progression, progression); missingItems = Logic.GetMissingRequiredItems(tempWorld.IcePalace.KholdstareReward, progression, out _); missingItems.Should().BeEmpty(); } @@ -426,6 +467,8 @@ public void TestEasyBlueBrinstarTop() var tempWorld = new World(config, "", 0, ""); var progression = new Progression(new[] { ItemType.Morph, ItemType.PowerBomb, ItemType.CardBrinstarL1 }, new List(), new List()); + tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileVisible).UpdateAccessibility(progression, progression); + tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileHidden).UpdateAccessibility(progression, progression); Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileVisible), progression, out _).Should().BeEmpty(); Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileHidden), progression, out _).Should().BeEmpty(); tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileVisible).IsAvailable(progression).Should().BeTrue(); @@ -433,6 +476,8 @@ public void TestEasyBlueBrinstarTop() config.LogicConfig.EasyBlueBrinstarTop = true; tempWorld = new World(config, "", 0, ""); + tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileVisible).UpdateAccessibility(progression, progression); + tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileHidden).UpdateAccessibility(progression, progression); Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileVisible), progression, out _).Should().HaveCount(2) .And.ContainEquivalentOf(new[] { ItemType.SpaceJump }) .And.ContainEquivalentOf(new[] { ItemType.Gravity }); @@ -443,12 +488,16 @@ public void TestEasyBlueBrinstarTop() tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileHidden).IsAvailable(progression).Should().BeFalse(); progression = new Progression(new[] { ItemType.Morph, ItemType.PowerBomb, ItemType.Gravity, ItemType.CardBrinstarL1 }, new List(), new List()); + tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileVisible).UpdateAccessibility(progression, progression); + tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileHidden).UpdateAccessibility(progression, progression); Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileVisible), progression, out _).Should().BeEmpty(); Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileHidden), progression, out _).Should().BeEmpty(); tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileVisible).IsAvailable(progression).Should().BeTrue(); tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileHidden).IsAvailable(progression).Should().BeTrue(); progression = new Progression(new[] { ItemType.Morph, ItemType.PowerBomb, ItemType.SpaceJump, ItemType.CardBrinstarL1 }, new List(), new List()); + tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileVisible).UpdateAccessibility(progression, progression); + tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileHidden).UpdateAccessibility(progression, progression); Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileVisible), progression, out _).Should().BeEmpty(); Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileHidden), progression, out _).Should().BeEmpty(); tempWorld.FindLocation(LocationId.BlueBrinstarDoubleMissileVisible).IsAvailable(progression).Should().BeTrue(); @@ -462,11 +511,13 @@ public void TestZoraNeedsRupeeItems() var tempWorld = new World(config, "", 0, ""); var progression = new Progression(new[] { ItemType.Flippers, ItemType.ThreeHundredRupees}, new List(), new List()); + tempWorld.FindLocation(LocationId.KingZora).UpdateAccessibility(progression, progression); Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.KingZora), progression, out _).Should().BeEmpty(); tempWorld.FindLocation(LocationId.KingZora).IsAvailable(progression).Should().BeTrue(); config.LogicConfig.ZoraNeedsRupeeItems = true; tempWorld = new World(config, "", 0, ""); + tempWorld.FindLocation(LocationId.KingZora).UpdateAccessibility(progression, progression); var items = Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.KingZora), progression, out _); Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.KingZora), progression, out _).Should() @@ -475,6 +526,7 @@ public void TestZoraNeedsRupeeItems() tempWorld.FindLocation(LocationId.KingZora).IsAvailable(progression).Should().BeFalse(); progression = new Progression(new[] { ItemType.Flippers, ItemType.ThreeHundredRupees, ItemType.ThreeHundredRupees }, new List(), new List()); + tempWorld.FindLocation(LocationId.KingZora).UpdateAccessibility(progression, progression); Logic.GetMissingRequiredItems(tempWorld.FindLocation(LocationId.KingZora), progression, out _).Should().BeEmpty(); tempWorld.FindLocation(LocationId.KingZora).IsAvailable(progression).Should().BeTrue(); } diff --git a/tests/TrackerCouncil.Smz3.Tests/LogicTests/RandomizerTests.cs b/tests/TrackerCouncil.Smz3.Tests/LogicTests/RandomizerTests.cs index 25e5d8dc4..0604ed687 100644 --- a/tests/TrackerCouncil.Smz3.Tests/LogicTests/RandomizerTests.cs +++ b/tests/TrackerCouncil.Smz3.Tests/LogicTests/RandomizerTests.cs @@ -110,6 +110,9 @@ public void EarlyItemConfig() var seedData = randomizer.GenerateSeed(config, null, default); var world = seedData.WorldGenerationData.LocalWorld.World; var progression = new Progression(); + world.Locations.First(x => x.Item.Type == ItemType.Firerod).UpdateAccessibility(progression, progression); + world.Locations.First(x => x.Item.Type == ItemType.Icerod).UpdateAccessibility(progression, progression); + world.Locations.First(x => x.Item.Type == ItemType.HiJump).UpdateAccessibility(progression, progression); Logic.GetMissingRequiredItems(world.Locations.First(x => x.Item.Type == ItemType.Firerod), progression, out _).Should().BeEmpty(); Logic.GetMissingRequiredItems(world.Locations.First(x => x.Item.Type == ItemType.Icerod), progression, out _).Should().BeEmpty(); Logic.GetMissingRequiredItems(world.Locations.First(x => x.Item.Type == ItemType.HiJump), progression, out _).Should().BeEmpty();