From 4e6ee8a8c78bfb92101d425aaa72c6837995b5a6 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Mon, 10 Jun 2019 22:07:47 -0600 Subject: [PATCH 01/41] Write out initial structure for the randomizer --- App.config | 6 ++ Core/Constants.cs | 170 +++++++++++++++++++++++++++++++ Core/ROM.cs | 70 +++++++++++++ Minish Cap Randomizer.sln | 25 +++++ MinishRandomizer.csproj | 92 +++++++++++++++++ Program.cs | 22 ++++ Properties/AssemblyInfo.cs | 36 +++++++ Properties/Resources.Designer.cs | 71 +++++++++++++ Properties/Resources.resx | 117 +++++++++++++++++++++ Properties/Settings.Designer.cs | 30 ++++++ Properties/Settings.settings | 7 ++ Randomizer/Location.cs | 50 +++++++++ Randomizer/Shuffler.cs | 93 +++++++++++++++++ Resources/default.locdata | 0 UI/MainWindow.Designer.cs | 122 ++++++++++++++++++++++ UI/MainWindow.cs | 73 +++++++++++++ UI/MainWindow.resx | 126 +++++++++++++++++++++++ Utilities/ExtensionMethods.cs | 36 +++++++ Utilities/Reader.cs | 132 ++++++++++++++++++++++++ Utilities/StringUtil.cs | 44 ++++++++ Utilities/Writer.cs | 111 ++++++++++++++++++++ 21 files changed, 1433 insertions(+) create mode 100644 App.config create mode 100644 Core/Constants.cs create mode 100644 Core/ROM.cs create mode 100644 Minish Cap Randomizer.sln create mode 100644 MinishRandomizer.csproj create mode 100644 Program.cs create mode 100644 Properties/AssemblyInfo.cs create mode 100644 Properties/Resources.Designer.cs create mode 100644 Properties/Resources.resx create mode 100644 Properties/Settings.Designer.cs create mode 100644 Properties/Settings.settings create mode 100644 Randomizer/Location.cs create mode 100644 Randomizer/Shuffler.cs create mode 100644 Resources/default.locdata create mode 100644 UI/MainWindow.Designer.cs create mode 100644 UI/MainWindow.cs create mode 100644 UI/MainWindow.resx create mode 100644 Utilities/ExtensionMethods.cs create mode 100644 Utilities/Reader.cs create mode 100644 Utilities/StringUtil.cs create mode 100644 Utilities/Writer.cs diff --git a/App.config b/App.config new file mode 100644 index 00000000..8e156463 --- /dev/null +++ b/App.config @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Core/Constants.cs b/Core/Constants.cs new file mode 100644 index 00000000..cdfc4bc7 --- /dev/null +++ b/Core/Constants.cs @@ -0,0 +1,170 @@ +using System; +using System.IO; +using System.Reflection; + +namespace MinishRandomizer.Core +{ + public enum RegionVersion + { + EU, + JP, + US, + None + } + + public struct HeaderData + { + public int MapHeaderBase; + // Name on this is gonna have to change sometime + public int AreaMetadataBase; + + //added for now, change names to whatever you want + public int tileOffset; + public int paletteSetTableLoc; + public int chunk0TableLoc; + public int area1Chunk0TableLoc; + public int chunk1TableLoc; + public int chunk2TableLoc; + public int swapBase; + public int paletteChangeBase; + public int area1SwapBase; + public int globalTileSetTableLoc; + public int gfxSourceBase; + public int globalMetaTileSetTableLoc; + public int globalTileDataTableLoc; + + public HeaderData( int map, int area, int tileOffset, int paletteSetTableLoc, int c0TableLoc, int a1C0TableLoc, int c1TableLoc, int c2TableLoc, int swapBase, int paletteChangeBase, int area1SwapBase, int globalTileSetTableLoc, int gfxSourceBase, int globalMetaTileSetTableLoc, int globalTileDataTableLoc ) + { + this.MapHeaderBase = map; + this.AreaMetadataBase = area; + this.tileOffset = tileOffset; + this.paletteSetTableLoc = paletteSetTableLoc; + this.chunk0TableLoc = c0TableLoc; //looks like each next chunkTable is a 0x16 further (eu and us), not adding because of possible jp + this.area1Chunk0TableLoc = a1C0TableLoc; + this.chunk1TableLoc = c1TableLoc; //c0+ 0x16 + this.chunk2TableLoc = c2TableLoc; //c0+ 0x32 + this.swapBase = swapBase; + this.paletteChangeBase = paletteChangeBase; + this.area1SwapBase = area1SwapBase; //above -0x140? + this.globalTileSetTableLoc = globalTileSetTableLoc; + this.gfxSourceBase = gfxSourceBase; + this.globalMetaTileSetTableLoc = globalMetaTileSetTableLoc; + this.globalTileDataTableLoc = globalTileDataTableLoc; + } + } + + public enum TileEntityType + { + None = 0x00, + TestA = 0x01, + Chest = 0x02, + BigChest = 0x03, + TestB = 0x04, + TestC = 0x05, + } + + public enum KinstoneType + { + UnTyped, + + YellowTornadoProng = 0x65, + YellowTornadoSpike = 0x66, + YellowTornadoChaotic = 0x67, + //68 and 69 are repeats of above + + YellowTotemProng = 0x6A, + YellowTotemWave = 0x6B, + YellowTotemZigZag = 0x6C, + + YellowCrown = 0x6D, + + RedSpike = 0x6E, + RedCrack = 0x6F, + RedProng = 0x70, + + BlueL = 0x71, + BlueS = 0x72, + + GreenSpike = 0x73, + GreenSquare = 0x74, + GreenSplit = 0x75, + } + + public enum ItemType + { + Untyped, + Bow = 0x09, + + Boomerang = 0x0C, + + LanternOff = 0x0F, + + GustJar = 0x11, + PacciCane = 0x12, + MoleMitts = 0x13, + RocsCape = 0x14, + PegasusBoots = 0x15, + FireRod = 0x16, + Ocarina = 0x17, + Bottle1 = 0x1C, + Bottle2 = 0x1D, + Bottle3 = 0x1E, + Bottle4 = 0x1F, + BottleEmpty = 0x20, + BottleButter = 0x21, + BottleMilk = 0x22, + BottleHalfMilk = 0x23, + BottleRedPotion = 0x24, + BottleBluePotion = 0x25, + BottleWater = 0x26, + BottleMineralWater = 0x27, + BottleFairy = 0x28, + BottlePicolyteRed = 0x29, + BottlePicolyteOrange = 0x2A, + BottlePicolyteYellow = 0x2B, + BottlePiclolyteGreen = 0x2C, + BottlePicolyteBlue = 0x2D, + BottlePicolyteWhite = 0x2E, + BottleCharmNayru = 0x2F, + BottleCharmFarore = 0x30, + BottleCharmDin = 0x31, + + ShellsX = 0x3F, + + PowerBracelets = 0x45, + Flippers = 0X46, + + DungeonMap = 0x50, + Compass = 0x51, + BigKey = 0x52, + SmallKey = 0x53, + Rupee1 = 0x54, + Rupee5 = 0x55, + Rupee20 = 0x56, + Rupee50 = 0x57, + Rupee100 = 0x58, + Rupee200 = 0x59, + + KinstoneX = 0x5C, + + PieceOfHeart = 0x63, + Wallet = 0x64, + + } + + public class Header + { + // Will fill out when relevant, only need EU for now + private readonly HeaderData[] headerTable = new HeaderData[] + { // MAP , ENTITY?, TILEOFFSET PALETTESET CHUNK0 CHUNK0AREA1 CHUNK1 CHUNK2 SWAP PALETTECHANGE AREA1SWAP TILESET GFXSOURCE METATILE TILEDATA + new HeaderData(0x11D95C, 0x0D4828, 0x5A23D0, 0xFED88, 0x107AEC, 0x1077AC, 0x107B02, 0x107B18, 0x107B5C, 0x107940, 0x107800, 0x101BC8, 0x323FEC, 0x1027F8, 0x1070E4), + new HeaderData(0, 0,0,0,0,0,0,0,0,0,0,0,0,0,0), + new HeaderData(0x11E214, 0x0D50FC, 0x5A2E80, 0xFF850, 0x108398, 0x108050, 0x1083AE, 0x1083C4, 0x108408, 0x1081E4, 0x1080A4, 0x10246C, 0x324AE4, 0x10309C, 0x107988) + }; + + public HeaderData GetHeaderAddresses( RegionVersion region ) + { + return headerTable[(int)region]; + } + } +} diff --git a/Core/ROM.cs b/Core/ROM.cs new file mode 100644 index 00000000..7ce16a92 --- /dev/null +++ b/Core/ROM.cs @@ -0,0 +1,70 @@ +using System; +using System.Diagnostics; +using MinishRandomizer.Utilities; +using System.IO; + +namespace MinishRandomizer.Core +{ + public class ROM + { + public static ROM Instance { get; private set; } + public readonly string path; + + public readonly byte[] romData; + public readonly Reader reader; + + public RegionVersion version { get; private set; } = RegionVersion.None; + public HeaderData headers { get; private set; } + + + public ROM(string filePath) + { + Instance = this; + path = filePath; + byte[] smallData = File.ReadAllBytes(filePath); + if (smallData.Length >= 0x01000000) + { + romData = smallData; + } + else + { + romData = new byte[0x1000000]; + smallData.CopyTo(romData, 0); + } + + Stream stream = Stream.Synchronized(new MemoryStream(romData)); + reader = new Reader(stream); + Debug.WriteLine("Read " + stream.Length + " bytes."); + + SetupRom(); + } + + private void SetupRom() + { + // Determine game region and if valid ROM + byte[] regionBytes = reader.ReadBytes(4, 0xAC); + string region = System.Text.Encoding.UTF8.GetString(regionBytes); + Debug.WriteLine("Region detected: "+region); + + if (region == "BZMP") + { + version = RegionVersion.EU; + } + + if (region == "BZMJ") + { + version = RegionVersion.JP; + } + + if (region == "BZME") + { + version = RegionVersion.US; + } + + if (version != RegionVersion.None) + { + headers = new Header().GetHeaderAddresses(version); + } + } + } +} diff --git a/Minish Cap Randomizer.sln b/Minish Cap Randomizer.sln new file mode 100644 index 00000000..bff0a759 --- /dev/null +++ b/Minish Cap Randomizer.sln @@ -0,0 +1,25 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio Version 16 +VisualStudioVersion = 16.0.29001.49 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MinishRandomizer", "MinishRandomizer.csproj", "{704DAD64-9C28-458B-8980-A945C1159FFA}" +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {704DAD64-9C28-458B-8980-A945C1159FFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {704DAD64-9C28-458B-8980-A945C1159FFA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {704DAD64-9C28-458B-8980-A945C1159FFA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {704DAD64-9C28-458B-8980-A945C1159FFA}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {3200685A-8233-4883-B294-A2CBC408902C} + EndGlobalSection +EndGlobal diff --git a/MinishRandomizer.csproj b/MinishRandomizer.csproj new file mode 100644 index 00000000..2587e826 --- /dev/null +++ b/MinishRandomizer.csproj @@ -0,0 +1,92 @@ + + + + + Debug + AnyCPU + {704DAD64-9C28-458B-8980-A945C1159FFA} + WinExe + Minish_Cap_Randomizer + Minish Cap Randomizer + v4.5 + 512 + true + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + Form + + + MainWindow.cs + + + + + + + + + + + MainWindow.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + + \ No newline at end of file diff --git a/Program.cs b/Program.cs new file mode 100644 index 00000000..c03987d7 --- /dev/null +++ b/Program.cs @@ -0,0 +1,22 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading.Tasks; +using System.Windows.Forms; + +namespace MinishRandomizer +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainWindow()); + } + } +} diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..fb3e713c --- /dev/null +++ b/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("Minish Cap Randomizer")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Minish Cap Randomizer")] +[assembly: AssemblyCopyright("Copyright © 2019")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("704dad64-9c28-458b-8980-a945c1159ffa")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs new file mode 100644 index 00000000..725a808b --- /dev/null +++ b/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Minish_Cap_Randomizer.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Minish_Cap_Randomizer.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/Properties/Resources.resx b/Properties/Resources.resx new file mode 100644 index 00000000..af7dbebb --- /dev/null +++ b/Properties/Resources.resx @@ -0,0 +1,117 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs new file mode 100644 index 00000000..2120df4d --- /dev/null +++ b/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Minish_Cap_Randomizer.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/Properties/Settings.settings b/Properties/Settings.settings new file mode 100644 index 00000000..39645652 --- /dev/null +++ b/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs new file mode 100644 index 00000000..756e9c1e --- /dev/null +++ b/Randomizer/Location.cs @@ -0,0 +1,50 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MinishRandomizer.Core; +using MinishRandomizer.Utilities; + +namespace MinishRandomizer.Randomizer +{ + public class Location + { + public enum Region + { + Overworld, + Deepwood, + Flames, + WindFort, + Droplets, + WindTemple + } + + public byte Type; + public Region SubRegion; + public bool Large; + public Item DefaultContents { get; private set; } + public Item Contents; + public int Address { get; private set; } + + public Location(byte type, Region region, int address, bool large, Item contents) + { + Type = type; + DefaultContents = contents; + Contents = contents; + Address = address; + Large = large; + } + + public void WriteLocation(Writer r) + { + r.SetPosition(Address); + r.WriteByte((byte)Contents.Type); + if (Large) + { + r.WriteByte(Contents.SubValue); + } + } + } +} diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs new file mode 100644 index 00000000..3bbc8dd3 --- /dev/null +++ b/Randomizer/Shuffler.cs @@ -0,0 +1,93 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using MinishRandomizer.Core; +using MinishRandomizer.Utilities; + +namespace MinishRandomizer.Randomizer +{ + public readonly struct Item + { + public readonly ItemType Type; + public readonly KinstoneType Kinstone; + public readonly byte SubValue; + + public Item(ItemType type, byte subValue) + { + Type = type; + SubValue = subValue; + if (type == ItemType.KinstoneX) + { + Kinstone = (KinstoneType)subValue; + } + else + { + Kinstone = KinstoneType.UnTyped; + } + } + } + + public class Shuffler + { + + List Locations; + List Items; + + public Shuffler() + { + + } + + public void LoadLocations(string locationFile) + { + byte[] locationData; + + if (locationFile == null) + { + locationData = File.ReadAllBytes(""); + } + else + { + locationData = File.ReadAllBytes(locationFile); + } + + Locations = new List(); + Items = new List(); + + using (MemoryStream ms = new MemoryStream(locationData)) + { + Reader r = new Reader(ms); + + byte type = r.ReadByte(); + while (type != 0) + { + Location.Region region = (Location.Region)r.ReadByte(); + int addr = r.ReadAddr(); + bool large = r.ReadByte() == 1; + ItemType itemType = (ItemType)r.ReadByte(); + byte subValue = r.ReadByte(); + Item item = new Item(itemType, subValue); + + Locations.Add(new Location(type, region, addr, large, item)); + + Items.Add(item); + + type = r.ReadByte(); + } + } + } + + public void LoadLogic(string logicFile) + { + + } + + public void RandomizeLocations() + { + + } + } +} diff --git a/Resources/default.locdata b/Resources/default.locdata new file mode 100644 index 00000000..e69de29b diff --git a/UI/MainWindow.Designer.cs b/UI/MainWindow.Designer.cs new file mode 100644 index 00000000..40c6a221 --- /dev/null +++ b/UI/MainWindow.Designer.cs @@ -0,0 +1,122 @@ +namespace MinishRandomizer +{ + partial class MainWindow + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.statusStrip1 = new System.Windows.Forms.StatusStrip(); + this.menuStrip1 = new System.Windows.Forms.MenuStrip(); + this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.randomize = new System.Windows.Forms.Button(); + this.statusText = new System.Windows.Forms.ToolStripStatusLabel(); + this.statusStrip1.SuspendLayout(); + this.menuStrip1.SuspendLayout(); + this.SuspendLayout(); + // + // statusStrip1 + // + this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.statusText}); + this.statusStrip1.Location = new System.Drawing.Point(0, 200); + this.statusStrip1.Name = "statusStrip1"; + this.statusStrip1.Size = new System.Drawing.Size(345, 22); + this.statusStrip1.TabIndex = 0; + this.statusStrip1.Text = "statusStrip"; + // + // menuStrip1 + // + this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.fileToolStripMenuItem}); + this.menuStrip1.Location = new System.Drawing.Point(0, 0); + this.menuStrip1.Name = "menuStrip1"; + this.menuStrip1.Size = new System.Drawing.Size(345, 24); + this.menuStrip1.TabIndex = 1; + this.menuStrip1.Text = "menuStrip1"; + // + // fileToolStripMenuItem + // + this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.openToolStripMenuItem}); + this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; + this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); + this.fileToolStripMenuItem.Text = "File"; + // + // openToolStripMenuItem + // + this.openToolStripMenuItem.Name = "openToolStripMenuItem"; + this.openToolStripMenuItem.Size = new System.Drawing.Size(180, 22); + this.openToolStripMenuItem.Text = "Open"; + this.openToolStripMenuItem.Click += new System.EventHandler(this.OpenToolStripMenuItem_Click); + // + // randomize + // + this.randomize.Location = new System.Drawing.Point(258, 27); + this.randomize.Name = "randomize"; + this.randomize.Size = new System.Drawing.Size(75, 23); + this.randomize.TabIndex = 2; + this.randomize.Text = "Randomize"; + this.randomize.UseVisualStyleBackColor = true; + this.randomize.Click += new System.EventHandler(this.Randomize_Click); + // + // statusText + // + this.statusText.Name = "statusText"; + this.statusText.Size = new System.Drawing.Size(59, 17); + this.statusText.Text = ""; + // + // MainWindow + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(345, 222); + this.Controls.Add(this.randomize); + this.Controls.Add(this.statusStrip1); + this.Controls.Add(this.menuStrip1); + this.MainMenuStrip = this.menuStrip1; + this.Name = "MainWindow"; + this.Text = "Minish Cap Randomizer"; + this.statusStrip1.ResumeLayout(false); + this.statusStrip1.PerformLayout(); + this.menuStrip1.ResumeLayout(false); + this.menuStrip1.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.StatusStrip statusStrip1; + private System.Windows.Forms.MenuStrip menuStrip1; + private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem openToolStripMenuItem; + private System.Windows.Forms.Button randomize; + private System.Windows.Forms.ToolStripStatusLabel statusText; + } +} + diff --git a/UI/MainWindow.cs b/UI/MainWindow.cs new file mode 100644 index 00000000..ae65e0d2 --- /dev/null +++ b/UI/MainWindow.cs @@ -0,0 +1,73 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows.Forms; +using MinishRandomizer.Core; +using MinishRandomizer.Randomizer; + +namespace MinishRandomizer +{ + public partial class MainWindow : Form + { + private ROM ROM_; + private string locationPath; + + public MainWindow() + { + InitializeComponent(); + } + + private void OpenToolStripMenuItem_Click(object sender, EventArgs e) + { + LoadRom(); + } + + private void Randomize_Click(object sender, EventArgs e) + { + if (ROM_ == null) + { + return; + } + + Shuffler shuffler = new Shuffler(); + shuffler.LoadLocations(locationPath); + + } + + private void LoadRom() + { + OpenFileDialog ofd = new OpenFileDialog + { + Filter = "GBA ROMs|*.gba|All Files|*.*", + Title = "Select TMC ROM" + }; + + if (ofd.ShowDialog() != DialogResult.OK) + { + return; + } + + try + { + ROM_ = new ROM(ofd.FileName); + } + catch (Exception e) + { + Console.WriteLine(e); + throw; + } + + if (ROM.Instance.version.Equals(RegionVersion.None)) + { + MessageBox.Show("Invalid TMC ROM. Please Open a valid ROM.", "Incorrect ROM", MessageBoxButtons.OK); + statusText.Text = "Unable to determine ROM."; + return; + } + } + } +} diff --git a/UI/MainWindow.resx b/UI/MainWindow.resx new file mode 100644 index 00000000..6beaffa3 --- /dev/null +++ b/UI/MainWindow.resx @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 133, 17 + + \ No newline at end of file diff --git a/Utilities/ExtensionMethods.cs b/Utilities/ExtensionMethods.cs new file mode 100644 index 00000000..a2a0b49e --- /dev/null +++ b/Utilities/ExtensionMethods.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MinishRandomizer.Utilities +{ + public static class ExtensionMethods + { + public static string Hex( this uint num ) + { + return Convert.ToString( num, 16 ).ToUpper(); + } + + public static string Hex( this int num ) + { + return Convert.ToString( num, 16 ).ToUpper(); + } + + public static string Hex( this long num ) + { + return Convert.ToString( num, 16 ).ToUpper(); + } + + public static string Hex( this ushort num ) + { + return Convert.ToString( num, 16 ).ToUpper(); + } + + public static string Hex( this byte num ) + { + return Convert.ToString( num, 16 ).ToUpper(); + } + } +} diff --git a/Utilities/Reader.cs b/Utilities/Reader.cs new file mode 100644 index 00000000..d944dd8c --- /dev/null +++ b/Utilities/Reader.cs @@ -0,0 +1,132 @@ +using System.IO; + +namespace MinishRandomizer.Utilities +{ + public class Reader + { + private readonly Stream stream_; + + private readonly BinaryReader reader; + + public Reader(Stream stream) + { + stream_ = stream; + reader = new BinaryReader(stream); + } + + public long Position + { + get { return stream_.Position; } + } + + public void SetPosition(long pos) + { + stream_.Position = pos; + } + + public byte PeekByte() + { + long tempPos = stream_.Position; + byte b = ReadByte(); + stream_.Position = tempPos; + return b; + } + + public byte PeekByte(long pos) + { + stream_.Position = pos; + return PeekByte(); + } + + public byte[] PeekBytes(int num) + { + long tempPos = stream_.Position; + byte[] b = ReadBytes(num); + stream_.Position = tempPos; + return b; + } + + public byte[] PeekBytes(int num, long pos) + { + stream_.Position = pos; + return PeekBytes(num); + } + + public byte ReadByte() + { + return reader.ReadByte(); + } + + public byte ReadByte(long pos) + { + stream_.Position = pos; + return ReadByte(); + } + + public byte[] ReadBytes(int num) + { + return reader.ReadBytes(num); + } + + public byte[] ReadBytes(int num, long pos) + { + stream_.Position = pos; + return ReadBytes(num); + } + + public ushort ReadUInt16() + { + return reader.ReadUInt16(); + } + + public ushort ReadUInt16(long pos) + { + stream_.Position = pos; + return reader.ReadUInt16(); + } + + public short ReadInt16() + { + return reader.ReadInt16(); + } + + public short ReadInt16(long pos) + { + stream_.Position = pos; + return reader.ReadInt16(); + } + + public uint ReadUInt32() + { + return reader.ReadUInt32(); + } + + public uint ReadUInt32(long pos) + { + stream_.Position = pos; + return reader.ReadUInt32(); + } + + public int ReadInt() + { + return reader.ReadInt32(); + } + + public int ReadInt(long pos) + { + stream_.Position = pos; + return reader.ReadInt32(); ; + } + + public int ReadAddr() + { + return ReadInt() & 0xFFFFFF; + } + + public int ReadAddr(long pos) + { + stream_.Position = pos; + return ReadAddr(); + } + } +} diff --git a/Utilities/StringUtil.cs b/Utilities/StringUtil.cs new file mode 100644 index 00000000..68a4d2d0 --- /dev/null +++ b/Utilities/StringUtil.cs @@ -0,0 +1,44 @@ +using System; +using System.Collections.Generic; +using System.Linq; +namespace MinishRandomizer.Utilities +{ + class StringUtil + { + // Convert values to hex strings + public static string AsStringHex(int val, int spacing) + { + return val.ToString("X").PadLeft(spacing, '0'); + } + + public static string AsStringHex2(int val) + { + return AsStringHex(val, 2); + } + + public static string AsStringHex4(int val) + { + return AsStringHex(val, 4); + } + + public static string AsStringGBAAddress(int val) + { + return AsStringHex(val, 6); + } + + public static string AsStringHex8(int val) + { + return AsStringHex(val, 8); + } + + public static string AsStringHex16(int val) + { + return AsStringHex(val, 16); + } + + public static string AsStringHex32(int val) + { + return AsStringHex(val, 32); + } + } +} diff --git a/Utilities/Writer.cs b/Utilities/Writer.cs new file mode 100644 index 00000000..9e8a6b7e --- /dev/null +++ b/Utilities/Writer.cs @@ -0,0 +1,111 @@ +using System.IO; + +namespace MinishRandomizer.Utilities +{ + public class Writer + { + private readonly Stream stream_; + + private readonly BinaryWriter writer_; + + public Writer(Stream stream) + { + stream_ = stream; + writer_ = new BinaryWriter(stream); + } + + ~Writer() + { + Flush(); + } + + public void Flush() + { + writer_.Flush(); + } + + public long Position => stream_.Position; + + public void SetPosition(long pos) + { + stream_.Position = pos; + } + + public void WriteByte(byte byteToWrite) + { + writer_.Write(byteToWrite); + } + + public void WriteByte(byte byteToWrite, long pos) + { + stream_.Position = pos; + WriteByte(byteToWrite); + } + + public void WriteBytes(byte[] bytesToWrite) + { + writer_.Write(bytesToWrite); + } + + public void WriteBytes(byte[] bytesToWrite, long pos) + { + stream_.Position = pos; + WriteBytes(bytesToWrite); + } + + public void WriteUInt16(ushort uint16) + { + writer_.Write(uint16); + } + + public void WriteUInt16(ushort uint16, long pos) + { + stream_.Position = pos; + WriteUInt16(uint16); + } + + public void WriteInt16(short int16) + { + writer_.Write(int16); + } + + public void WriteInt16(short int16, long pos) + { + stream_.Position = pos; + WriteInt16(int16); + } + + public void WriteUInt32(uint uint32) + { + writer_.Write(uint32); + } + + public void WriteUInt32(uint uint32, long pos) + { + stream_.Position = pos; + WriteUInt32(uint32); + } + + public void WriteInt(int int32) + { + writer_.Write(int32); + } + + public void WriteInt(int int32, long pos) + { + stream_.Position = pos; + WriteInt(int32); + } + + public void WriteAddr(int addr) + { + writer_.Write(addr | 0x08000000); + } + + public void WriteAddr(int addr, long pos) + { + stream_.Position = pos; + WriteAddr(addr); + } + } +} From a7a5b1f35cc24ce3037933844eee941829556e14 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Tue, 11 Jun 2019 20:25:59 -0600 Subject: [PATCH 02/41] Allow item randomization, take steps toward adding logic --- Core/Constants.cs | 18 ++- MinishRandomizer.csproj | 8 +- ...Cap Randomizer.sln => MinishRandomizer.sln | 0 Properties/AssemblyInfo.cs | 4 +- Properties/Resources.Designer.cs | 46 +++--- Properties/Settings.Designer.cs | 22 ++- Randomizer/Dependency.cs | 142 ++++++++++++++++ Randomizer/Location.cs | 152 ++++++++++++++++-- Randomizer/Shuffler.cs | 103 ++++++++---- Resources/default.locdata | 0 Resources/default.logic | 2 + UI/MainWindow.Designer.cs | 144 +++++++++++++++-- UI/MainWindow.cs | 54 ++++++- 13 files changed, 581 insertions(+), 114 deletions(-) rename Minish Cap Randomizer.sln => MinishRandomizer.sln (100%) create mode 100644 Randomizer/Dependency.cs delete mode 100644 Resources/default.locdata create mode 100644 Resources/default.logic diff --git a/Core/Constants.cs b/Core/Constants.cs index cdfc4bc7..f63255c3 100644 --- a/Core/Constants.cs +++ b/Core/Constants.cs @@ -93,12 +93,22 @@ public enum KinstoneType public enum ItemType { Untyped, + SmithSword = 0x01, + GreenSword = 0x02, + RedSword = 0x03, + BlueSword = 0x04, +// UnusedSword = 0x05, + FourSword = 0x06, + Bombs = 0x07, + RemoteBombs = 0x08, Bow = 0x09, - - Boomerang = 0x0C, - + LightArrow = 0x0A, + Boomerang = 0x0B, + MagicBoomerang = 0x0C, + Shield = 0x0D, + MirrorShield = 0x0E, LanternOff = 0x0F, - + GustJar = 0x11, PacciCane = 0x12, MoleMitts = 0x13, diff --git a/MinishRandomizer.csproj b/MinishRandomizer.csproj index 2587e826..517ea6c9 100644 --- a/MinishRandomizer.csproj +++ b/MinishRandomizer.csproj @@ -6,8 +6,8 @@ AnyCPU {704DAD64-9C28-458B-8980-A945C1159FFA} WinExe - Minish_Cap_Randomizer - Minish Cap Randomizer + MinishRandomizer + MinishRandomizer v4.5 512 true @@ -47,6 +47,7 @@ + Form @@ -72,6 +73,7 @@ True Resources.resx + True SettingsSingleFileGenerator @@ -82,7 +84,7 @@ Settings.settings True - + diff --git a/Minish Cap Randomizer.sln b/MinishRandomizer.sln similarity index 100% rename from Minish Cap Randomizer.sln rename to MinishRandomizer.sln diff --git a/Properties/AssemblyInfo.cs b/Properties/AssemblyInfo.cs index fb3e713c..31bc6e87 100644 --- a/Properties/AssemblyInfo.cs +++ b/Properties/AssemblyInfo.cs @@ -5,11 +5,11 @@ // General Information about an assembly is controlled through the following // set of attributes. Change these attribute values to modify the information // associated with an assembly. -[assembly: AssemblyTitle("Minish Cap Randomizer")] +[assembly: AssemblyTitle("MinishRandomizer")] [assembly: AssemblyDescription("")] [assembly: AssemblyConfiguration("")] [assembly: AssemblyCompany("")] -[assembly: AssemblyProduct("Minish Cap Randomizer")] +[assembly: AssemblyProduct("MinishRandomizer")] [assembly: AssemblyCopyright("Copyright © 2019")] [assembly: AssemblyTrademark("")] [assembly: AssemblyCulture("")] diff --git a/Properties/Resources.Designer.cs b/Properties/Resources.Designer.cs index 725a808b..2da8682b 100644 --- a/Properties/Resources.Designer.cs +++ b/Properties/Resources.Designer.cs @@ -8,10 +8,10 @@ // //------------------------------------------------------------------------------ -namespace Minish_Cap_Randomizer.Properties -{ - - +namespace MinishRandomizer.Properties { + using System; + + /// /// A strongly-typed resource class, for looking up localized strings, etc. /// @@ -19,51 +19,43 @@ namespace Minish_Cap_Randomizer.Properties // class via a tool like ResGen or Visual Studio. // To add or remove a member, edit your .ResX file then rerun ResGen // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "16.0.0.0")] [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources - { - + internal class Resources { + private static global::System.Resources.ResourceManager resourceMan; - + private static global::System.Globalization.CultureInfo resourceCulture; - + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() - { + internal Resources() { } - + /// /// Returns the cached ResourceManager instance used by this class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager - { - get - { - if ((resourceMan == null)) - { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Minish_Cap_Randomizer.Properties.Resources", typeof(Resources).Assembly); + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("MinishRandomizer.Properties.Resources", typeof(Resources).Assembly); resourceMan = temp; } return resourceMan; } } - + /// /// Overrides the current thread's CurrentUICulture property for all /// resource lookups using this strongly typed resource class. /// [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture - { - get - { + internal static global::System.Globalization.CultureInfo Culture { + get { return resourceCulture; } - set - { + set { resourceCulture = value; } } diff --git a/Properties/Settings.Designer.cs b/Properties/Settings.Designer.cs index 2120df4d..daf57327 100644 --- a/Properties/Settings.Designer.cs +++ b/Properties/Settings.Designer.cs @@ -8,21 +8,17 @@ // //------------------------------------------------------------------------------ -namespace Minish_Cap_Randomizer.Properties -{ - - +namespace MinishRandomizer.Properties { + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase - { - + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "16.1.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default - { - get - { + + public static Settings Default { + get { return defaultInstance; } } diff --git a/Randomizer/Dependency.cs b/Randomizer/Dependency.cs new file mode 100644 index 00000000..b31e4d47 --- /dev/null +++ b/Randomizer/Dependency.cs @@ -0,0 +1,142 @@ +using MinishRandomizer.Core; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Text.RegularExpressions; +using System.Threading.Tasks; +using System.Globalization; + +namespace MinishRandomizer.Randomizer +{ + public class Dependency + { + /// + /// Get a list of the dependencies contained within a given logic string. Compound dependencies call this recursively + /// + /// The logic string to parse + /// The list of dependencies contained in the logic + public static List GetDependencies(string logic) + { + List dependencies = new List(); + + // Match: comma, capture and match anything between + string regexPattern = @"(?:,|^)\(([&|](?:[^()]|(?

\()|(?<-p>\)))+)\)(?:,|$)|,"; + string[] subLogic = Regex.Split(logic, regexPattern); + foreach (string sequence in subLogic) + { + if (string.IsNullOrEmpty(sequence)) + { + continue; + } + + switch(sequence[0]) + { + // If the first character of the string is & or |, it's a compound dependency + case '&': + AndDependency andDependency = new AndDependency(sequence.Substring(1)); + dependencies.Add(andDependency); + break; + case '|': + OrDependency orDependency = new OrDependency(sequence.Substring(1)); + dependencies.Add(orDependency); + break; + default: + + break; + } + } + + return dependencies; + } + + private Item RequiredItem; + + public Dependency() + { + + } + + public Dependency(string dependencyText) + { + string[] dependencyParts = dependencyText.Split('.'); + switch(dependencyParts[0].ToLower()) + { + case "locations": + + break; + case "items": + if (Enum.TryParse(dependencyParts[1], out ItemType type)) + { + byte subType = 0; + if (dependencyParts.Length >= 2) + { + if (!byte.TryParse(dependencyParts[2], NumberStyles.HexNumber, null, out subType)) + { + if (Enum.TryParse(dependencyParts[2], out KinstoneType subKinstoneType)) + { + subType = (byte)subKinstoneType; + } + } + } + RequiredItem = new Item(type, subType); + } + else + { + RequiredItem = new Item(ItemType.Untyped, 0); + } + break; + } + } + + public virtual bool DependencyFulfilled(List unplacedItems) + { + return false; + } + + } + + public class AndDependency : Dependency + { + public List AndList; + + public AndDependency(string dependencyText) + { + AndList = GetDependencies(dependencyText); + } + + override public bool DependencyFulfilled(List unplacedItems) + { + foreach (Dependency dependency in AndList) + { + if (dependency.DependencyFulfilled(unplacedItems) == false) + { + return false; + } + } + return true; + } + } + + public class OrDependency : Dependency + { + public List OrList; + + public OrDependency(string dependencyText) + { + OrList = GetDependencies(dependencyText); + } + + override public bool DependencyFulfilled(List unplacedItems) + { + foreach (Dependency dependency in OrList) + { + if (dependency.DependencyFulfilled(unplacedItems) == true) + { + return true; + } + } + return false; + } + } +} diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs index 756e9c1e..1cddc61f 100644 --- a/Randomizer/Location.cs +++ b/Randomizer/Location.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.IO; using System.Linq; using System.Text; @@ -11,30 +12,118 @@ namespace MinishRandomizer.Randomizer { public class Location { - public enum Region + public static Location GetLocation(string locationText) { - Overworld, - Deepwood, - Flames, - WindFort, - Droplets, - WindTemple + // Location format: Type;Name;Address;Large;Logic + string[] locationParts = locationText.Split(';'); + + if (locationParts.Length < 4) + { + return new Location(LocationType.Untyped, "INVALID LOCATION", 0, false, null); + } + + string name = locationParts[0]; + + string locationType = locationParts[1]; + if (!Enum.TryParse(locationType, out LocationType type) || type == LocationType.Untyped) + { + return new Location(LocationType.Untyped, "INVALID LOCATION", 0, false, null); + } + + int address = GetAddressFromString(locationParts[2]); + + string largeText = locationParts[3]; + if (!bool.TryParse(largeText, out bool large)) + { + large = false; + } + + string logic = ""; + if (locationParts.Length >= 5) + { + logic = locationParts[4]; + } + + List dependencies = Dependency.GetDependencies(logic); + + return new Location(type, name, address, large, dependencies); } - public byte Type; - public Region SubRegion; - public bool Large; - public Item DefaultContents { get; private set; } - public Item Contents; - public int Address { get; private set; } + public static int GetAddressFromString(string addressString) + { + // Either direct address or area-room-chest + if (int.TryParse(addressString, NumberStyles.HexNumber, null, out int address)) + { + return address; + } + + string[] chestDetails = addressString.Split('-'); + if (chestDetails.Length != 3) + { + return 0; + } + + if (!int.TryParse(chestDetails[0], NumberStyles.HexNumber, null, out int area)) + { + return 0; + } + + if (!int.TryParse(chestDetails[1], NumberStyles.HexNumber, null, out int room)) + { + return 0; + } + + if (!int.TryParse(chestDetails[2], NumberStyles.HexNumber, null, out int chest)) + { + return 0; + } + + int areaTableAddr = ROM.Instance.reader.ReadAddr(ROM.Instance.headers.AreaMetadataBase + (area << 2)); + int roomTableAddr = ROM.Instance.reader.ReadAddr(areaTableAddr + (room << 2)); + int chestTableAddr = ROM.Instance.reader.ReadAddr(roomTableAddr + 0x0C); - public Location(byte type, Region region, int address, bool large, Item contents) + return chestTableAddr + chest * 8 + 0x02; + } + + public enum LocationType + { + Untyped, + Normal, + Minor, + DungeonItem, + NPCItem, + KinstoneItem, + HeartPieceItem, + HelperItem + } + + public List Dependencies; + public LocationType Type; + public string Name; + public bool Filled; + public Item Contents { get; private set; } + private bool Large; + private Item DefaultContents; + private int Address; + + public Location(LocationType type, string name, int address, bool large, List dependencies) { Type = type; - DefaultContents = contents; - Contents = contents; + Name = name; + Address = address; + Large = large; + + Dependencies = dependencies; + + if (address != 0) + { + DefaultContents = GetItemContents(); + Contents = DefaultContents; + } + + Filled = false; } public void WriteLocation(Writer r) @@ -46,5 +135,36 @@ public void WriteLocation(Writer r) r.WriteByte(Contents.SubValue); } } + + public Item GetItemContents() + { + ItemType type = (ItemType)ROM.Instance.reader.ReadByte(Address); + byte subType = 0; + if (Large) + { + subType = ROM.Instance.reader.ReadByte(); + } + + return new Item(type, subType); + } + + public bool IsAccessible(List unplacedItems) + { + foreach (Dependency dependency in Dependencies) + { + if (!dependency.DependencyFulfilled(unplacedItems)) + { + return false; + } + } + + return true; + } + + public void Fill(Item contents) + { + Contents = contents; + Filled = true; + } } } diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index 3bbc8dd3..6f021a50 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -2,6 +2,7 @@ using System.Collections.Generic; using System.IO; using System.Linq; +using System.Reflection; using System.Text; using System.Threading.Tasks; using MinishRandomizer.Core; @@ -28,66 +29,98 @@ public Item(ItemType type, byte subValue) Kinstone = KinstoneType.UnTyped; } } + + public override bool Equals(object obj) + { + if (obj == null || GetType() != obj.GetType()) + { + return false; + } + Item asItem = (Item)obj; + return asItem.Type == Type && asItem.SubValue == SubValue; + } + + public override int GetHashCode() + { + return base.GetHashCode(); + } } public class Shuffler { - - List Locations; - List Items; - public Shuffler() + private Random RNG; + private List Locations; + private List Items; + private string OutputDirectory; + + public Shuffler(string outputDirectory) { + RNG = new Random(); + Locations = new List(); + Items = new List(); + OutputDirectory = outputDirectory; + } + public void SetSeed(int seed) + { + RNG = new Random(seed); } public void LoadLocations(string locationFile) { - byte[] locationData; + string[] locationStrings; if (locationFile == null) { - locationData = File.ReadAllBytes(""); + Assembly assembly = Assembly.GetExecutingAssembly(); + using (Stream stream = assembly.GetManifestResourceStream("MinishRandomizer.Resources.default.logic")) + using (StreamReader reader = new StreamReader(stream)) + { + string allLocations = reader.ReadToEnd(); + locationStrings = allLocations.Split('\n'); + } } else { - locationData = File.ReadAllBytes(locationFile); + locationStrings = File.ReadAllLines(locationFile); } - - Locations = new List(); - Items = new List(); - - using (MemoryStream ms = new MemoryStream(locationData)) + + foreach (string locationString in locationStrings) { - Reader r = new Reader(ms); - - byte type = r.ReadByte(); - while (type != 0) - { - Location.Region region = (Location.Region)r.ReadByte(); - int addr = r.ReadAddr(); - bool large = r.ReadByte() == 1; - ItemType itemType = (ItemType)r.ReadByte(); - byte subValue = r.ReadByte(); - Item item = new Item(itemType, subValue); - - Locations.Add(new Location(type, region, addr, large, item)); - - Items.Add(item); - - type = r.ReadByte(); - } + Location newLocation = Location.GetLocation(locationString); + Locations.Add(newLocation); + Items.Add(newLocation.Contents); } } - public void LoadLogic(string logicFile) + public void RandomizeLocations() { + List unfilledLocations = Locations.Where(location => !location.Filled).ToList(); + while (unfilledLocations.Count > 0) + { + Location location = unfilledLocations[RNG.Next(unfilledLocations.Count)]; + Item item = Items[RNG.Next(Items.Count)]; + List unplacedItems = Items.Where(listItem => !ReferenceEquals(listItem, item)).ToList(); + if (location.IsAccessible(unplacedItems)) + { + Console.WriteLine($"Loc: {location.Name} Item: {item.Type.ToString()}"); + Items.Remove(item); + location.Fill(item); + unfilledLocations.Remove(location); + } + } - } + using (MemoryStream ms = new MemoryStream(ROM.Instance.romData)) + { + Writer writer = new Writer(ms); + foreach (Location location in Locations) + { + location.WriteLocation(writer); + } + } - public void RandomizeLocations() - { - + File.WriteAllBytes(OutputDirectory + "/mcrando.gba", ROM.Instance.romData); } } } diff --git a/Resources/default.locdata b/Resources/default.locdata deleted file mode 100644 index e69de29b..00000000 diff --git a/Resources/default.logic b/Resources/default.logic new file mode 100644 index 00000000..4bcacd0b --- /dev/null +++ b/Resources/default.logic @@ -0,0 +1,2 @@ +Smith's House;Minor;22-11-00;true +Shield Location;Normal;0000FA6A;true \ No newline at end of file diff --git a/UI/MainWindow.Designer.cs b/UI/MainWindow.Designer.cs index 40c6a221..25d2e099 100644 --- a/UI/MainWindow.Designer.cs +++ b/UI/MainWindow.Designer.cs @@ -29,32 +29,49 @@ protected override void Dispose(bool disposing) private void InitializeComponent() { this.statusStrip1 = new System.Windows.Forms.StatusStrip(); + this.statusText = new System.Windows.Forms.ToolStripStatusLabel(); this.menuStrip1 = new System.Windows.Forms.MenuStrip(); this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.randomize = new System.Windows.Forms.Button(); - this.statusText = new System.Windows.Forms.ToolStripStatusLabel(); + this.logicTab = new System.Windows.Forms.TabPage(); + this.customLogicCheckBox = new System.Windows.Forms.CheckBox(); + this.customLocationPath = new System.Windows.Forms.TextBox(); + this.browseLogicButton = new System.Windows.Forms.Button(); + this.browseLocationButton = new System.Windows.Forms.Button(); + this.customLogicPath = new System.Windows.Forms.TextBox(); + this.customLocationCheckBox = new System.Windows.Forms.CheckBox(); + this.mainTabs = new System.Windows.Forms.TabControl(); + this.locationsTab = new System.Windows.Forms.TabPage(); this.statusStrip1.SuspendLayout(); this.menuStrip1.SuspendLayout(); + this.logicTab.SuspendLayout(); + this.mainTabs.SuspendLayout(); + this.locationsTab.SuspendLayout(); this.SuspendLayout(); // // statusStrip1 // this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.statusText}); - this.statusStrip1.Location = new System.Drawing.Point(0, 200); + this.statusStrip1.Location = new System.Drawing.Point(0, 294); this.statusStrip1.Name = "statusStrip1"; - this.statusStrip1.Size = new System.Drawing.Size(345, 22); + this.statusStrip1.Size = new System.Drawing.Size(390, 22); this.statusStrip1.TabIndex = 0; this.statusStrip1.Text = "statusStrip"; // + // statusText + // + this.statusText.Name = "statusText"; + this.statusText.Size = new System.Drawing.Size(0, 17); + // // menuStrip1 // this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { this.fileToolStripMenuItem}); this.menuStrip1.Location = new System.Drawing.Point(0, 0); this.menuStrip1.Name = "menuStrip1"; - this.menuStrip1.Size = new System.Drawing.Size(345, 24); + this.menuStrip1.Size = new System.Drawing.Size(390, 24); this.menuStrip1.TabIndex = 1; this.menuStrip1.Text = "menuStrip1"; // @@ -69,13 +86,13 @@ private void InitializeComponent() // openToolStripMenuItem // this.openToolStripMenuItem.Name = "openToolStripMenuItem"; - this.openToolStripMenuItem.Size = new System.Drawing.Size(180, 22); + this.openToolStripMenuItem.Size = new System.Drawing.Size(103, 22); this.openToolStripMenuItem.Text = "Open"; this.openToolStripMenuItem.Click += new System.EventHandler(this.OpenToolStripMenuItem_Click); // // randomize // - this.randomize.Location = new System.Drawing.Point(258, 27); + this.randomize.Location = new System.Drawing.Point(19, 268); this.randomize.Name = "randomize"; this.randomize.Size = new System.Drawing.Size(75, 23); this.randomize.TabIndex = 2; @@ -83,17 +100,108 @@ private void InitializeComponent() this.randomize.UseVisualStyleBackColor = true; this.randomize.Click += new System.EventHandler(this.Randomize_Click); // - // statusText + // logicTab // - this.statusText.Name = "statusText"; - this.statusText.Size = new System.Drawing.Size(59, 17); - this.statusText.Text = ""; + this.logicTab.Controls.Add(this.customLogicPath); + this.logicTab.Controls.Add(this.browseLogicButton); + this.logicTab.Controls.Add(this.customLogicCheckBox); + this.logicTab.Location = new System.Drawing.Point(4, 22); + this.logicTab.Name = "logicTab"; + this.logicTab.Padding = new System.Windows.Forms.Padding(3); + this.logicTab.Size = new System.Drawing.Size(361, 209); + this.logicTab.TabIndex = 0; + this.logicTab.Text = "Logic"; + this.logicTab.UseVisualStyleBackColor = true; + // + // customLogicCheckBox + // + this.customLogicCheckBox.AutoSize = true; + this.customLogicCheckBox.Location = new System.Drawing.Point(5, 152); + this.customLogicCheckBox.Name = "customLogicCheckBox"; + this.customLogicCheckBox.Size = new System.Drawing.Size(112, 17); + this.customLogicCheckBox.TabIndex = 5; + this.customLogicCheckBox.Text = "Use Custom Logic"; + this.customLogicCheckBox.UseVisualStyleBackColor = true; + this.customLogicCheckBox.CheckedChanged += new System.EventHandler(this.CustomLogicCheckBox_CheckedChanged); + // + // customLocationPath + // + this.customLocationPath.Enabled = false; + this.customLocationPath.Location = new System.Drawing.Point(113, 175); + this.customLocationPath.Name = "customLocationPath"; + this.customLocationPath.Size = new System.Drawing.Size(206, 20); + this.customLocationPath.TabIndex = 3; + // + // browseLogicButton + // + this.browseLogicButton.Enabled = false; + this.browseLogicButton.Location = new System.Drawing.Point(32, 175); + this.browseLogicButton.Name = "browseLogicButton"; + this.browseLogicButton.Size = new System.Drawing.Size(75, 23); + this.browseLogicButton.TabIndex = 7; + this.browseLogicButton.Text = "Browse..."; + this.browseLogicButton.UseVisualStyleBackColor = true; + this.browseLogicButton.Click += new System.EventHandler(this.BrowseLogicButton_Click); + // + // browseLocationButton + // + this.browseLocationButton.Enabled = false; + this.browseLocationButton.Location = new System.Drawing.Point(32, 175); + this.browseLocationButton.Name = "browseLocationButton"; + this.browseLocationButton.Size = new System.Drawing.Size(75, 23); + this.browseLocationButton.TabIndex = 4; + this.browseLocationButton.Text = "Browse..."; + this.browseLocationButton.UseVisualStyleBackColor = true; + this.browseLocationButton.Click += new System.EventHandler(this.BrowseLocationButton_Click); + // + // customLogicPath + // + this.customLogicPath.Enabled = false; + this.customLogicPath.Location = new System.Drawing.Point(113, 175); + this.customLogicPath.Name = "customLogicPath"; + this.customLogicPath.Size = new System.Drawing.Size(206, 20); + this.customLogicPath.TabIndex = 8; + // + // customLocationCheckBox + // + this.customLocationCheckBox.AutoSize = true; + this.customLocationCheckBox.Location = new System.Drawing.Point(5, 152); + this.customLocationCheckBox.Name = "customLocationCheckBox"; + this.customLocationCheckBox.Size = new System.Drawing.Size(146, 17); + this.customLocationCheckBox.TabIndex = 6; + this.customLocationCheckBox.Text = "Use Custom Location List"; + this.customLocationCheckBox.UseVisualStyleBackColor = true; + this.customLocationCheckBox.CheckedChanged += new System.EventHandler(this.CustomLocationCheckBox_CheckedChanged); + // + // mainTabs + // + this.mainTabs.Controls.Add(this.logicTab); + this.mainTabs.Controls.Add(this.locationsTab); + this.mainTabs.Location = new System.Drawing.Point(9, 27); + this.mainTabs.Name = "mainTabs"; + this.mainTabs.SelectedIndex = 0; + this.mainTabs.Size = new System.Drawing.Size(369, 235); + this.mainTabs.TabIndex = 9; + // + // locationsTab + // + this.locationsTab.Controls.Add(this.customLocationCheckBox); + this.locationsTab.Controls.Add(this.customLocationPath); + this.locationsTab.Controls.Add(this.browseLocationButton); + this.locationsTab.Location = new System.Drawing.Point(4, 22); + this.locationsTab.Name = "locationsTab"; + this.locationsTab.Padding = new System.Windows.Forms.Padding(3); + this.locationsTab.Size = new System.Drawing.Size(361, 209); + this.locationsTab.TabIndex = 1; + this.locationsTab.Text = "Locations"; + this.locationsTab.UseVisualStyleBackColor = true; // // MainWindow // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(345, 222); + this.ClientSize = new System.Drawing.Size(390, 316); + this.Controls.Add(this.mainTabs); this.Controls.Add(this.randomize); this.Controls.Add(this.statusStrip1); this.Controls.Add(this.menuStrip1); @@ -104,6 +212,11 @@ private void InitializeComponent() this.statusStrip1.PerformLayout(); this.menuStrip1.ResumeLayout(false); this.menuStrip1.PerformLayout(); + this.logicTab.ResumeLayout(false); + this.logicTab.PerformLayout(); + this.mainTabs.ResumeLayout(false); + this.locationsTab.ResumeLayout(false); + this.locationsTab.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); @@ -117,6 +230,15 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem openToolStripMenuItem; private System.Windows.Forms.Button randomize; private System.Windows.Forms.ToolStripStatusLabel statusText; + private System.Windows.Forms.TabPage logicTab; + private System.Windows.Forms.CheckBox customLocationCheckBox; + private System.Windows.Forms.TextBox customLogicPath; + private System.Windows.Forms.TextBox customLocationPath; + private System.Windows.Forms.Button browseLocationButton; + private System.Windows.Forms.Button browseLogicButton; + private System.Windows.Forms.CheckBox customLogicCheckBox; + private System.Windows.Forms.TabControl mainTabs; + private System.Windows.Forms.TabPage locationsTab; } } diff --git a/UI/MainWindow.cs b/UI/MainWindow.cs index ae65e0d2..af62c6ae 100644 --- a/UI/MainWindow.cs +++ b/UI/MainWindow.cs @@ -3,6 +3,7 @@ using System.ComponentModel; using System.Data; using System.Drawing; +using System.IO; using System.Linq; using System.Text; using System.Threading.Tasks; @@ -15,7 +16,6 @@ namespace MinishRandomizer public partial class MainWindow : Form { private ROM ROM_; - private string locationPath; public MainWindow() { @@ -34,9 +34,19 @@ private void Randomize_Click(object sender, EventArgs e) return; } - Shuffler shuffler = new Shuffler(); - shuffler.LoadLocations(locationPath); + Shuffler shuffler = new Shuffler(Path.GetDirectoryName(ROM.Instance.path)); + + if (customLocationCheckBox.Checked) + { + shuffler.LoadLocations(customLocationPath.Text); + } + else + { + shuffler.LoadLocations(null); + } + + shuffler.RandomizeLocations(); } private void LoadRom() @@ -69,5 +79,43 @@ private void LoadRom() return; } } + + private void CustomLocationCheckBox_CheckedChanged(object sender, EventArgs e) + { + + } + + private void CustomLogicCheckBox_CheckedChanged(object sender, EventArgs e) + { + + } + + private void BrowseLocationButton_Click(object sender, EventArgs e) + { + OpenFileDialog ofd = new OpenFileDialog + { + Filter = "Location Data|*.location|All Files|*.*", + Title = "Select Custom Locations" + }; + + if (ofd.ShowDialog() != DialogResult.OK) + { + return; + } + } + + private void BrowseLogicButton_Click(object sender, EventArgs e) + { + OpenFileDialog ofd = new OpenFileDialog + { + Filter = "Logic Data|*.logic|All Files|*.*", + Title = "Select Custom Logic" + }; + + if (ofd.ShowDialog() != DialogResult.OK) + { + return; + } + } } } From 571ba70ec5f28a826baafbd55c9827b8e23986e5 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Thu, 13 Jun 2019 14:30:45 -0600 Subject: [PATCH 03/41] Allow randomization of items with a buggy shuffle that's likely to fail --- Randomizer/Dependency.cs | 111 ++++++++++++++++++++++------------ Randomizer/Location.cs | 53 ++++++++++++++-- Randomizer/Shuffler.cs | 51 ++++++++++++---- Resources/default.logic | 12 +++- Resources/icon.ico | Bin 0 -> 1078 bytes UI/MainWindow.Designer.cs | 91 ++++++---------------------- UI/MainWindow.cs | 28 ++------- UI/MainWindow.resx | 23 +++++++ Utilities/ExtensionMethods.cs | 15 ++++- 9 files changed, 233 insertions(+), 151 deletions(-) create mode 100644 Resources/icon.ico diff --git a/Randomizer/Dependency.cs b/Randomizer/Dependency.cs index b31e4d47..adb9e378 100644 --- a/Randomizer/Dependency.cs +++ b/Randomizer/Dependency.cs @@ -20,6 +20,8 @@ public static List GetDependencies(string logic) { List dependencies = new List(); + logic = logic.Replace(" ", ""); + // Match: comma, capture and match anything between string regexPattern = @"(?:,|^)\(([&|](?:[^()]|(?

\()|(?<-p>\)))+)\)(?:,|$)|,"; string[] subLogic = Regex.Split(logic, regexPattern); @@ -30,6 +32,7 @@ public static List GetDependencies(string logic) continue; } + switch(sequence[0]) { // If the first character of the string is & or |, it's a compound dependency @@ -42,7 +45,33 @@ public static List GetDependencies(string logic) dependencies.Add(orDependency); break; default: - + string[] dependencyParts = sequence.Split('.'); + switch (dependencyParts[0]) + { + case "Locations": + LocationDependency locationDependency = new LocationDependency(dependencyParts[1]); + dependencies.Add(locationDependency); + break; + case "Items": + if (Enum.TryParse(dependencyParts[1], out ItemType type)) + { + byte subType = 0; + if (dependencyParts.Length >= 3) + { + if (!byte.TryParse(dependencyParts[2], NumberStyles.HexNumber, null, out subType)) + { + if (Enum.TryParse(dependencyParts[2], out KinstoneType subKinstoneType)) + { + subType = (byte)subKinstoneType; + } + } + } + + ItemDependency itemDependency = new ItemDependency(new Item(type, subType)); + dependencies.Add(itemDependency); + } + break; + } break; } } @@ -50,50 +79,58 @@ public static List GetDependencies(string logic) return dependencies; } - private Item RequiredItem; - - public Dependency() + public virtual bool DependencyFulfilled(List availableItems, List locations) { + return false; + } + + } + public class ItemDependency : Dependency + { + private Item RequiredItem; + public ItemDependency(Item item) + { + RequiredItem = item; } - public Dependency(string dependencyText) + public override bool DependencyFulfilled(List availableItems, List locations) { - string[] dependencyParts = dependencyText.Split('.'); - switch(dependencyParts[0].ToLower()) + foreach (Item item in availableItems) { - case "locations": - - break; - case "items": - if (Enum.TryParse(dependencyParts[1], out ItemType type)) - { - byte subType = 0; - if (dependencyParts.Length >= 2) - { - if (!byte.TryParse(dependencyParts[2], NumberStyles.HexNumber, null, out subType)) - { - if (Enum.TryParse(dependencyParts[2], out KinstoneType subKinstoneType)) - { - subType = (byte)subKinstoneType; - } - } - } - RequiredItem = new Item(type, subType); - } - else - { - RequiredItem = new Item(ItemType.Untyped, 0); - } - break; + Console.WriteLine($"Has: {item.Type.ToString()} Needs: {RequiredItem.Type.ToString()}"); + if (item.Type == RequiredItem.Type && item.SubValue == RequiredItem.SubValue) + { + return true; + } } + + Console.WriteLine($"Inccessible because: {RequiredItem.Type.ToString()} is not available!"); + return false; } + } - public virtual bool DependencyFulfilled(List unplacedItems) + public class LocationDependency : Dependency + { + private string RequiredLocationName; + public LocationDependency(string locationName) { - return false; + RequiredLocationName = locationName; } + public override bool DependencyFulfilled(List availableItems, List locations) + { + foreach (Location location in locations) + { + if (location.Name == RequiredLocationName) + { + Console.WriteLine($"Sub-Evaluating: {RequiredLocationName}"); + return location.CanPlace(new Item(), availableItems, locations); + } + } + Console.WriteLine($"Could not find location: {RequiredLocationName}"); + return false; + } } public class AndDependency : Dependency @@ -105,11 +142,11 @@ public AndDependency(string dependencyText) AndList = GetDependencies(dependencyText); } - override public bool DependencyFulfilled(List unplacedItems) + public override bool DependencyFulfilled(List availableItems, List locations) { foreach (Dependency dependency in AndList) { - if (dependency.DependencyFulfilled(unplacedItems) == false) + if (dependency.DependencyFulfilled(availableItems, locations) == false) { return false; } @@ -127,11 +164,11 @@ public OrDependency(string dependencyText) OrList = GetDependencies(dependencyText); } - override public bool DependencyFulfilled(List unplacedItems) + public override bool DependencyFulfilled(List availableItems, List locations) { foreach (Dependency dependency in OrList) { - if (dependency.DependencyFulfilled(unplacedItems) == true) + if (dependency.DependencyFulfilled(availableItems, locations) == true) { return true; } diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs index 1cddc61f..5b04fdd2 100644 --- a/Randomizer/Location.cs +++ b/Randomizer/Location.cs @@ -46,7 +46,34 @@ public static Location GetLocation(string locationText) List dependencies = Dependency.GetDependencies(logic); - return new Location(type, name, address, large, dependencies); + Location location = new Location(type, name, address, large, dependencies); + + if (locationParts.Length >= 6) + { + string[] subParts = locationParts[5].Split('.'); + + if (subParts[0] == "Items") + { + if (Enum.TryParse(subParts[1], out ItemType replacementType)) + { + byte subType = 0; + if (subParts.Length >= 3) + { + if (!byte.TryParse(subParts[2], NumberStyles.HexNumber, null, out subType)) + { + if (Enum.TryParse(subParts[2], out KinstoneType subKinstoneType)) + { + subType = (byte)subKinstoneType; + } + } + } + + location.SetItem(new Item(replacementType, subType)); + } + } + } + + return location; } public static int GetAddressFromString(string addressString) @@ -94,7 +121,7 @@ public enum LocationType NPCItem, KinstoneItem, HeartPieceItem, - HelperItem + Helper } public List Dependencies; @@ -128,6 +155,11 @@ public Location(LocationType type, string name, int address, bool large, List unplacedItems) + public bool CanPlace(Item itemToPlace, List availableItems, List locations) { + Console.WriteLine($"Evaluating: {Name}"); + if (!Large && itemToPlace.SubValue != 0) + { + Console.WriteLine($"Can't place because {Name} is small!"); + return false; + } foreach (Dependency dependency in Dependencies) { - if (!dependency.DependencyFulfilled(unplacedItems)) + if (!dependency.DependencyFulfilled(availableItems, locations)) { return false; } @@ -163,8 +201,13 @@ public bool IsAccessible(List unplacedItems) public void Fill(Item contents) { - Contents = contents; + SetItem(contents); Filled = true; } + + public void SetItem(Item contents) + { + Contents = contents; + } } } diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index 6f021a50..b81f5ad2 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -69,6 +69,9 @@ public void SetSeed(int seed) public void LoadLocations(string locationFile) { + Locations.Clear(); + Items.Clear(); + string[] locationStrings; if (locationFile == null) @@ -78,7 +81,7 @@ public void LoadLocations(string locationFile) using (StreamReader reader = new StreamReader(stream)) { string allLocations = reader.ReadToEnd(); - locationStrings = allLocations.Split('\n'); + locationStrings = allLocations.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); } } else @@ -88,27 +91,51 @@ public void LoadLocations(string locationFile) foreach (string locationString in locationStrings) { + if (locationString == "" || locationString[0] == '#') + { + continue; + } + Location newLocation = Location.GetLocation(locationString); Locations.Add(newLocation); - Items.Add(newLocation.Contents); + if (newLocation.Type != Location.LocationType.Helper) + { + Items.Add(newLocation.Contents); + } } } public void RandomizeLocations() { - List unfilledLocations = Locations.Where(location => !location.Filled).ToList(); - while (unfilledLocations.Count > 0) + List unplacedItems = Items.ToList(); + List unfilledLocations = Locations.Where(location => !location.Filled && location.Type != Location.LocationType.Helper).ToList(); + unfilledLocations.Shuffle(RNG); + unplacedItems.Shuffle(RNG); + + int itemIndex; + while (unplacedItems.Count > 0) { - Location location = unfilledLocations[RNG.Next(unfilledLocations.Count)]; - Item item = Items[RNG.Next(Items.Count)]; - List unplacedItems = Items.Where(listItem => !ReferenceEquals(listItem, item)).ToList(); - if (location.IsAccessible(unplacedItems)) + // TODO: Make this fill not bad + itemIndex = RNG.Next(unplacedItems.Count); + Item item = unplacedItems[itemIndex]; + Console.WriteLine($"Placing: {item.Type.ToString()}"); + unplacedItems.RemoveAt(itemIndex); + List availableLocations = unfilledLocations.Where(location => location.CanPlace(item, unplacedItems, unfilledLocations)).ToList(); + + if (availableLocations.Count <= 0) { - Console.WriteLine($"Loc: {location.Name} Item: {item.Type.ToString()}"); - Items.Remove(item); - location.Fill(item); - unfilledLocations.Remove(location); + Console.WriteLine($"Could not place {item.Type.ToString()}!"); + return; } + + availableLocations[0].Fill(item); + Console.WriteLine($"Placed {item.Type.ToString()} at {availableLocations[0].Name} with {unplacedItems.Count} items remaining\n"); + unfilledLocations.Remove(availableLocations[0]); + } + + if (unfilledLocations.Count != 0) + { + Console.WriteLine("Not all locations filled!"); } using (MemoryStream ms = new MemoryStream(ROM.Instance.romData)) diff --git a/Resources/default.logic b/Resources/default.logic index 4bcacd0b..811f9014 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -1,2 +1,10 @@ -Smith's House;Minor;22-11-00;true -Shield Location;Normal;0000FA6A;true \ No newline at end of file +# Default logic for the randomizer + +SmithHouse;Minor;22-11-00;true + +# Deepwood locations +# Need gust jar to leave room +DeepwoodWiggler;Normal;48-00-01;true;Items.GustJar +DeepwoodButtonShells1;Minor;48-01-01;true;Items.GustJar +DeepwoodButtonShells2;Minor;48-01-02;true;Items.GustJar +DeepwoodPreCompassShells;Minor;48-02-01;true;Items.GustJar \ No newline at end of file diff --git a/Resources/icon.ico b/Resources/icon.ico new file mode 100644 index 0000000000000000000000000000000000000000..3858b6c8f8fa670bc1f680d205b1b7221ce59421 GIT binary patch literal 1078 zcmcIjyKciU3_QmHnyHaGb*pOxMMk6Y9=p|t^ppBY`4o>GGiC_j9wk3y8qHP#2kOWp zDOxg+VS}PDv|IdafHwffM1IVG#~e7)R}c*$y1Q%lh28TvOm$|$^{({qV5ve2UOMl6 zB2e7<(%K}fxbs$p7raa>vx?@vbh4)UD{g%nhF@_odv58i=5&C9p#x95lu6Rlvc zF#3Zo%?p8*TH-im)|R zCbqFL{%*HHcgUVZj=UMJur30h`h?;72jE6r_rT>PqT{}!ujBt7Ap0aPTFH^zU)p(g AW&i*H literal 0 HcmV?d00001 diff --git a/UI/MainWindow.Designer.cs b/UI/MainWindow.Designer.cs index 25d2e099..3fc1a7ac 100644 --- a/UI/MainWindow.Designer.cs +++ b/UI/MainWindow.Designer.cs @@ -28,6 +28,7 @@ protected override void Dispose(bool disposing) /// private void InitializeComponent() { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(MainWindow)); this.statusStrip1 = new System.Windows.Forms.StatusStrip(); this.statusText = new System.Windows.Forms.ToolStripStatusLabel(); this.menuStrip1 = new System.Windows.Forms.MenuStrip(); @@ -35,19 +36,14 @@ private void InitializeComponent() this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.randomize = new System.Windows.Forms.Button(); this.logicTab = new System.Windows.Forms.TabPage(); - this.customLogicCheckBox = new System.Windows.Forms.CheckBox(); - this.customLocationPath = new System.Windows.Forms.TextBox(); - this.browseLogicButton = new System.Windows.Forms.Button(); - this.browseLocationButton = new System.Windows.Forms.Button(); this.customLogicPath = new System.Windows.Forms.TextBox(); - this.customLocationCheckBox = new System.Windows.Forms.CheckBox(); + this.browseLogicButton = new System.Windows.Forms.Button(); + this.customLogicCheckBox = new System.Windows.Forms.CheckBox(); this.mainTabs = new System.Windows.Forms.TabControl(); - this.locationsTab = new System.Windows.Forms.TabPage(); this.statusStrip1.SuspendLayout(); this.menuStrip1.SuspendLayout(); this.logicTab.SuspendLayout(); this.mainTabs.SuspendLayout(); - this.locationsTab.SuspendLayout(); this.SuspendLayout(); // // statusStrip1 @@ -113,24 +109,13 @@ private void InitializeComponent() this.logicTab.Text = "Logic"; this.logicTab.UseVisualStyleBackColor = true; // - // customLogicCheckBox - // - this.customLogicCheckBox.AutoSize = true; - this.customLogicCheckBox.Location = new System.Drawing.Point(5, 152); - this.customLogicCheckBox.Name = "customLogicCheckBox"; - this.customLogicCheckBox.Size = new System.Drawing.Size(112, 17); - this.customLogicCheckBox.TabIndex = 5; - this.customLogicCheckBox.Text = "Use Custom Logic"; - this.customLogicCheckBox.UseVisualStyleBackColor = true; - this.customLogicCheckBox.CheckedChanged += new System.EventHandler(this.CustomLogicCheckBox_CheckedChanged); - // - // customLocationPath + // customLogicPath // - this.customLocationPath.Enabled = false; - this.customLocationPath.Location = new System.Drawing.Point(113, 175); - this.customLocationPath.Name = "customLocationPath"; - this.customLocationPath.Size = new System.Drawing.Size(206, 20); - this.customLocationPath.TabIndex = 3; + this.customLogicPath.Enabled = false; + this.customLogicPath.Location = new System.Drawing.Point(113, 175); + this.customLogicPath.Name = "customLogicPath"; + this.customLogicPath.Size = new System.Drawing.Size(206, 20); + this.customLogicPath.TabIndex = 8; // // browseLogicButton // @@ -143,59 +128,26 @@ private void InitializeComponent() this.browseLogicButton.UseVisualStyleBackColor = true; this.browseLogicButton.Click += new System.EventHandler(this.BrowseLogicButton_Click); // - // browseLocationButton - // - this.browseLocationButton.Enabled = false; - this.browseLocationButton.Location = new System.Drawing.Point(32, 175); - this.browseLocationButton.Name = "browseLocationButton"; - this.browseLocationButton.Size = new System.Drawing.Size(75, 23); - this.browseLocationButton.TabIndex = 4; - this.browseLocationButton.Text = "Browse..."; - this.browseLocationButton.UseVisualStyleBackColor = true; - this.browseLocationButton.Click += new System.EventHandler(this.BrowseLocationButton_Click); - // - // customLogicPath - // - this.customLogicPath.Enabled = false; - this.customLogicPath.Location = new System.Drawing.Point(113, 175); - this.customLogicPath.Name = "customLogicPath"; - this.customLogicPath.Size = new System.Drawing.Size(206, 20); - this.customLogicPath.TabIndex = 8; - // - // customLocationCheckBox + // customLogicCheckBox // - this.customLocationCheckBox.AutoSize = true; - this.customLocationCheckBox.Location = new System.Drawing.Point(5, 152); - this.customLocationCheckBox.Name = "customLocationCheckBox"; - this.customLocationCheckBox.Size = new System.Drawing.Size(146, 17); - this.customLocationCheckBox.TabIndex = 6; - this.customLocationCheckBox.Text = "Use Custom Location List"; - this.customLocationCheckBox.UseVisualStyleBackColor = true; - this.customLocationCheckBox.CheckedChanged += new System.EventHandler(this.CustomLocationCheckBox_CheckedChanged); + this.customLogicCheckBox.AutoSize = true; + this.customLogicCheckBox.Location = new System.Drawing.Point(5, 152); + this.customLogicCheckBox.Name = "customLogicCheckBox"; + this.customLogicCheckBox.Size = new System.Drawing.Size(112, 17); + this.customLogicCheckBox.TabIndex = 5; + this.customLogicCheckBox.Text = "Use Custom Logic"; + this.customLogicCheckBox.UseVisualStyleBackColor = true; + this.customLogicCheckBox.CheckedChanged += new System.EventHandler(this.CustomLogicCheckBox_CheckedChanged); // // mainTabs // this.mainTabs.Controls.Add(this.logicTab); - this.mainTabs.Controls.Add(this.locationsTab); this.mainTabs.Location = new System.Drawing.Point(9, 27); this.mainTabs.Name = "mainTabs"; this.mainTabs.SelectedIndex = 0; this.mainTabs.Size = new System.Drawing.Size(369, 235); this.mainTabs.TabIndex = 9; // - // locationsTab - // - this.locationsTab.Controls.Add(this.customLocationCheckBox); - this.locationsTab.Controls.Add(this.customLocationPath); - this.locationsTab.Controls.Add(this.browseLocationButton); - this.locationsTab.Location = new System.Drawing.Point(4, 22); - this.locationsTab.Name = "locationsTab"; - this.locationsTab.Padding = new System.Windows.Forms.Padding(3); - this.locationsTab.Size = new System.Drawing.Size(361, 209); - this.locationsTab.TabIndex = 1; - this.locationsTab.Text = "Locations"; - this.locationsTab.UseVisualStyleBackColor = true; - // // MainWindow // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -205,6 +157,7 @@ private void InitializeComponent() this.Controls.Add(this.randomize); this.Controls.Add(this.statusStrip1); this.Controls.Add(this.menuStrip1); + this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); this.MainMenuStrip = this.menuStrip1; this.Name = "MainWindow"; this.Text = "Minish Cap Randomizer"; @@ -215,8 +168,6 @@ private void InitializeComponent() this.logicTab.ResumeLayout(false); this.logicTab.PerformLayout(); this.mainTabs.ResumeLayout(false); - this.locationsTab.ResumeLayout(false); - this.locationsTab.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); @@ -231,14 +182,10 @@ private void InitializeComponent() private System.Windows.Forms.Button randomize; private System.Windows.Forms.ToolStripStatusLabel statusText; private System.Windows.Forms.TabPage logicTab; - private System.Windows.Forms.CheckBox customLocationCheckBox; private System.Windows.Forms.TextBox customLogicPath; - private System.Windows.Forms.TextBox customLocationPath; - private System.Windows.Forms.Button browseLocationButton; private System.Windows.Forms.Button browseLogicButton; private System.Windows.Forms.CheckBox customLogicCheckBox; private System.Windows.Forms.TabControl mainTabs; - private System.Windows.Forms.TabPage locationsTab; } } diff --git a/UI/MainWindow.cs b/UI/MainWindow.cs index af62c6ae..7e472c50 100644 --- a/UI/MainWindow.cs +++ b/UI/MainWindow.cs @@ -37,9 +37,9 @@ private void Randomize_Click(object sender, EventArgs e) Shuffler shuffler = new Shuffler(Path.GetDirectoryName(ROM.Instance.path)); - if (customLocationCheckBox.Checked) + if (customLogicCheckBox.Checked) { - shuffler.LoadLocations(customLocationPath.Text); + shuffler.LoadLocations(customLogicPath.Text); } else { @@ -80,28 +80,10 @@ private void LoadRom() } } - private void CustomLocationCheckBox_CheckedChanged(object sender, EventArgs e) - { - - } - private void CustomLogicCheckBox_CheckedChanged(object sender, EventArgs e) { - - } - - private void BrowseLocationButton_Click(object sender, EventArgs e) - { - OpenFileDialog ofd = new OpenFileDialog - { - Filter = "Location Data|*.location|All Files|*.*", - Title = "Select Custom Locations" - }; - - if (ofd.ShowDialog() != DialogResult.OK) - { - return; - } + browseLogicButton.Enabled = customLogicCheckBox.Checked; + customLogicPath.Enabled = customLogicCheckBox.Checked; } private void BrowseLogicButton_Click(object sender, EventArgs e) @@ -116,6 +98,8 @@ private void BrowseLogicButton_Click(object sender, EventArgs e) { return; } + + customLogicPath.Text = ofd.FileName; } } } diff --git a/UI/MainWindow.resx b/UI/MainWindow.resx index 6beaffa3..0ba25bb6 100644 --- a/UI/MainWindow.resx +++ b/UI/MainWindow.resx @@ -123,4 +123,27 @@ 133, 17 + + + + AAABAAIAICAQAAEABADoAgAAJgAAABAQEAABAAQAKAEAAA4DAAAoAAAAIAAAAEAAAAABAAQAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIYAAIGJAAEDDAAABA+AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA + AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAiAAAAAAAAAAAAAAAAAAAAIgAAAAAAAAAAAAAA + ACIzREREAAAAAAAAAAAAAAAiM0RERAAAAAAAAAAAAAAzRCIREQAAAAAAAAAAAAAAM0QiEREAAAAAAAAA + AAAAM0QREREAAAAAAAAAAAAAADNEERERAAAAAAAAAAAAACJEAAAAAAAAAAAAAAAAAAAiRAAAAAAAAAAA + AAAAAAAARCIzMzMzMwAAAAAAAAAAAEQiMzMzMzMAAAAAAAAAAABEEUQzMzMAAAAAAAAAAAAARBFEMzMz + AAAAAAAAAAAAAEQRADMzABEREQAAAAAAAABEEQAzMwAREREAAAAAAAAARBERADMAABERAAAAAAAAAEQR + EQAzAAAREQAAAAAAAABEABERMzMzAAAAAAAAAAAARAARETMzMwAAAAAAAAAAADMiEREARDMAAAAAAAAA + AAAzIhERAEQzAAAAAAAAAAAAIkQAEREiAAAAAAAAAAAAACJEABERIgAAAAAAAAAAAAAAM0QAEREAAAAA + AAAAAAAAADNEABERAAAAAAAAAAAAAAAAM0QiAAAAAAAAAAAAAAAAADNEIgAAAAAAAAAAAAAAAAAAIjNE + REQAAAAAAAAAAAAAACIzREREAAAAAAAAAAAAAAAAAAAiAAAAAAAAAAAAAAAAAAAAIgAAAAAA//8AP/// + AD//8AA///AAP//AAP//wAD//wAD//8AA///AAP//wAD//wAAP/8AAD//AAAP/wAAD/8AAAP/AAAD/wA + AA/8AAAP/AAAP/wAAD/8AAD//AAA//8AA///AAP//wAD//8AA///wAD//8AA///wAD//8AA///8AP/// + AD8oAAAAEAAAACAAAAABAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIYAAIGJAAEDDAAABA + +AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAAjRE + AAAAAAA0IRAAAAAAA0ERAAAAAAAkAAAAAAAAAEIzMzAAAAAAQUMzAAAAAABBAzAREAAAAEEQMAEQAAAA + QBEzMAAAAAAyEQQwAAAAACQBEgAAAAAAA0ARAAAAAAAANCAAAAAAAAACNEQAAAAAAAAAIAAA/wcAAPwH + AAD4DwAA8B8AAPAfAADgDwAA4AcAAOADAADgAwAA4AcAAOAPAADwHwAA8B8AAPgPAAD8BwAA/wcAAA== + + \ No newline at end of file diff --git a/Utilities/ExtensionMethods.cs b/Utilities/ExtensionMethods.cs index a2a0b49e..5d1185b9 100644 --- a/Utilities/ExtensionMethods.cs +++ b/Utilities/ExtensionMethods.cs @@ -32,5 +32,18 @@ public static string Hex( this byte num ) { return Convert.ToString( num, 16 ).ToUpper(); } - } + + public static void Shuffle(this IList list, Random rng) + { + int n = list.Count; + while (n > 1) + { + n--; + int k = rng.Next(n + 1); + T value = list[k]; + list[k] = list[n]; + list[n] = value; + } + } + } } From 579ec6d7a7b16d5fc3ce197f6ac1fa07390f63e8 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Thu, 13 Jun 2019 18:37:15 -0600 Subject: [PATCH 04/41] Improve filler, logic; enumerate all valid items --- Core/Constants.cs | 56 ++++++++++++++++++++++++++--- Randomizer/Location.cs | 52 ++++++++++----------------- Randomizer/Shuffler.cs | 79 ++++++++++++++++++++++++++++------------- Resources/default.logic | 28 +++++++++++---- 4 files changed, 146 insertions(+), 69 deletions(-) diff --git a/Core/Constants.cs b/Core/Constants.cs index f63255c3..78f26977 100644 --- a/Core/Constants.cs +++ b/Core/Constants.cs @@ -139,11 +139,35 @@ public enum ItemType BottleCharmFarore = 0x30, BottleCharmDin = 0x31, - ShellsX = 0x3F, + SmithSwordQuest = 0x34, + BrokenPicoriBlade = 0x35, + DogFoodBottle = 0x36, + LonLonKey = 0x37, + WakeUpMushroom = 0x38, + HyruleanBestiary = 0x39, + PicoriLegend = 0x3A, + MaskHistory = 0x3B, + GraveyardKey = 0x3C, + TingleTrophy = 0x3D, + CarlovMedal = 0x3E, + ShellsX = 0x3F, + EarthElement = 0x40, + FireElement = 0x41, + WaterElement = 0x42, + WindElement = 0x43, + GripRing = 0x44, PowerBracelets = 0x45, - Flippers = 0X46, - + Flippers = 0x46, + HyruleMap = 0x47, + SpinAttack = 0x48, + RollAttack = 0x49, + DashAttack = 0x4A, + RockBreaker = 0x4B, + SwordBeam = 0x4C, + GreatSpin = 0x4D, + DownThrust = 0x4E, + PerilBeam = 0x4F, DungeonMap = 0x50, Compass = 0x51, BigKey = 0x52, @@ -155,11 +179,33 @@ public enum ItemType Rupee100 = 0x58, Rupee200 = 0x59, + JabberNut = 0x5B, KinstoneX = 0x5C, - + Bombs5 = 0x5D, + Arrows5 = 0x5E, + SmallHeart = 0x5F, + Fairy = 0x60, + Shells30 = 0x61, + HeartContainer = 0x62, PieceOfHeart = 0x63, Wallet = 0x64, - + BigBomb = 0x65, + LargeQuiver = 0x66, + KinstoneBag = 0x67, + Brioche = 0x68, + Croissant = 0x69, + PieSlice = 0x6A, + CakeSlice = 0x6B, + Bombs10 = 0x6C, + Bombs30 = 0x6D, + Arrows10 = 0x6E, + Arrows30 = 0x6F, + ArrowButterfly = 0x70, + DigButterfly = 0x71, + SwimButterfly = 0x72, + FastSplin = 0x73, + FastSplit = 0x74, + LongSpin = 0x75 } public class Header diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs index 5b04fdd2..6b5ebd3f 100644 --- a/Randomizer/Location.cs +++ b/Randomizer/Location.cs @@ -17,9 +17,9 @@ public static Location GetLocation(string locationText) // Location format: Type;Name;Address;Large;Logic string[] locationParts = locationText.Split(';'); - if (locationParts.Length < 4) + if (locationParts.Length < 3) { - return new Location(LocationType.Untyped, "INVALID LOCATION", 0, false, null); + return new Location(LocationType.Untyped, "INVALID LOCATION", 0, null); } string name = locationParts[0]; @@ -27,30 +27,24 @@ public static Location GetLocation(string locationText) string locationType = locationParts[1]; if (!Enum.TryParse(locationType, out LocationType type) || type == LocationType.Untyped) { - return new Location(LocationType.Untyped, "INVALID LOCATION", 0, false, null); + return new Location(LocationType.Untyped, "INVALID LOCATION", 0, null); } int address = GetAddressFromString(locationParts[2]); - string largeText = locationParts[3]; - if (!bool.TryParse(largeText, out bool large)) - { - large = false; - } - string logic = ""; - if (locationParts.Length >= 5) + if (locationParts.Length >= 4) { - logic = locationParts[4]; + logic = locationParts[3]; } List dependencies = Dependency.GetDependencies(logic); - Location location = new Location(type, name, address, large, dependencies); + Location location = new Location(type, name, address, dependencies); - if (locationParts.Length >= 6) + if (locationParts.Length >= 5) { - string[] subParts = locationParts[5].Split('.'); + string[] subParts = locationParts[4].Split('.'); if (subParts[0] == "Items") { @@ -129,19 +123,16 @@ public enum LocationType public string Name; public bool Filled; public Item Contents { get; private set; } - private bool Large; private Item DefaultContents; private int Address; - public Location(LocationType type, string name, int address, bool large, List dependencies) + public Location(LocationType type, string name, int address, List dependencies) { Type = type; Name = name; Address = address; - Large = large; - Dependencies = dependencies; if (address != 0) @@ -155,27 +146,27 @@ public Location(LocationType type, string name, int address, bool large, List availableItems, List locations) { Console.WriteLine($"Evaluating: {Name}"); - if (!Large && itemToPlace.SubValue != 0) - { - Console.WriteLine($"Can't place because {Name} is small!"); - return false; - } foreach (Dependency dependency in Dependencies) { if (!dependency.DependencyFulfilled(availableItems, locations)) diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index b81f5ad2..bbf2c05b 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -51,14 +51,16 @@ public class Shuffler private Random RNG; private List Locations; - private List Items; + private List MajorItems; + private List MinorItems; private string OutputDirectory; public Shuffler(string outputDirectory) { RNG = new Random(); Locations = new List(); - Items = new List(); + MajorItems = new List(); + MinorItems = new List(); OutputDirectory = outputDirectory; } @@ -70,7 +72,8 @@ public void SetSeed(int seed) public void LoadLocations(string locationFile) { Locations.Clear(); - Items.Clear(); + MajorItems.Clear(); + MinorItems.Clear(); string[] locationStrings; @@ -98,41 +101,35 @@ public void LoadLocations(string locationFile) Location newLocation = Location.GetLocation(locationString); Locations.Add(newLocation); - if (newLocation.Type != Location.LocationType.Helper) + switch(newLocation.Type) { - Items.Add(newLocation.Contents); + case Location.LocationType.Untyped: + case Location.LocationType.Helper: + break; + case Location.LocationType.Minor: + MinorItems.Add(newLocation.Contents); + break; + case Location.LocationType.Normal: + default: + Console.WriteLine($"Hey! {newLocation.Contents.Type.ToString()}"); + MajorItems.Add(newLocation.Contents); + break; } } } public void RandomizeLocations() { - List unplacedItems = Items.ToList(); + List unplacedItems = MajorItems.ToList(); List unfilledLocations = Locations.Where(location => !location.Filled && location.Type != Location.LocationType.Helper).ToList(); unfilledLocations.Shuffle(RNG); unplacedItems.Shuffle(RNG); - int itemIndex; - while (unplacedItems.Count > 0) - { - // TODO: Make this fill not bad - itemIndex = RNG.Next(unplacedItems.Count); - Item item = unplacedItems[itemIndex]; - Console.WriteLine($"Placing: {item.Type.ToString()}"); - unplacedItems.RemoveAt(itemIndex); - List availableLocations = unfilledLocations.Where(location => location.CanPlace(item, unplacedItems, unfilledLocations)).ToList(); + FillLocations(MajorItems.ToList(), unfilledLocations); - if (availableLocations.Count <= 0) - { - Console.WriteLine($"Could not place {item.Type.ToString()}!"); - return; - } + unfilledLocations.Shuffle(RNG); + FastFillLocations(MinorItems.ToList(), unfilledLocations); - availableLocations[0].Fill(item); - Console.WriteLine($"Placed {item.Type.ToString()} at {availableLocations[0].Name} with {unplacedItems.Count} items remaining\n"); - unfilledLocations.Remove(availableLocations[0]); - } - if (unfilledLocations.Count != 0) { Console.WriteLine("Not all locations filled!"); @@ -149,5 +146,37 @@ public void RandomizeLocations() File.WriteAllBytes(OutputDirectory + "/mcrando.gba", ROM.Instance.romData); } + + private void FillLocations(List items, List locations) + { + int itemIndex; + for (int i = items.Count - 1; i >= 0; i--) + { + itemIndex = RNG.Next(items.Count); + Item item = items[itemIndex]; + Console.WriteLine($"Placing: {item.Type.ToString()}"); + items.RemoveAt(itemIndex); + List availableLocations = locations.Where(location => location.CanPlace(item, items, locations)).ToList(); + + if (availableLocations.Count <= 0) + { + Console.WriteLine($"Could not place {item.Type.ToString()}!"); + return; + } + + availableLocations[0].Fill(item); + Console.WriteLine($"Placed {item.Type.ToString()} at {availableLocations[0].Name} with {items.Count} items remaining\n"); + locations.Remove(availableLocations[0]); + } + } + + private void FastFillLocations(List items, List locations) + { + foreach (Item item in items) + { + locations[0].Fill(item); + locations.RemoveAt(0); + } + } } } diff --git a/Resources/default.logic b/Resources/default.logic index 811f9014..7516a489 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -1,10 +1,26 @@ -# Default logic for the randomizer +# Default logic for the randomizer - Doesn't allow glitches, but allows unintuitive tricks -SmithHouse;Minor;22-11-00;true +# Location format: +# Name;Type;Address[;Logic][;Item Override] +# Name can be anything unique, hopefully without spaces +# Type should be one of Location.LocationType +# Address can be a hexadecimal address or of the form area-room-chest +# Logic should be a comma separated series of conditions, which can take the forms: +# (&a,b,c...) and (|a,b,c...) are an and/or of the contents +# Items.(itemName)[.subValue] requires the given item for the location +# Locations.(locationName) requires the location to be accessible first + +SmithHouse;Minor;22-11-00 # Deepwood locations # Need gust jar to leave room -DeepwoodWiggler;Normal;48-00-01;true;Items.GustJar -DeepwoodButtonShells1;Minor;48-01-01;true;Items.GustJar -DeepwoodButtonShells2;Minor;48-01-02;true;Items.GustJar -DeepwoodPreCompassShells;Minor;48-02-01;true;Items.GustJar \ No newline at end of file +DeepwoodWiggler;Normal;48-00-01;Items.GustJar +DeepwoodButtonShells1;Minor;48-01-01;Items.GustJar +DeepwoodButtonShells2;Minor;48-01-02;Items.GustJar +DeepwoodPreCompassShells;Minor;48-02-01;Items.GustJar +DeepwoodBarrelShells;Minor;48-06-01;Items.GustJar +DeepwoodUpstairsChest;Minor;48-17-01; + +# Cave of Flames locations +CoFAccess;Helper;0; +CoFBigChest \ No newline at end of file From d6d9f1126c37df5298c5b790cfe4b9ea4184208d Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Fri, 14 Jun 2019 12:40:48 -0600 Subject: [PATCH 05/41] Update logic; still fails to generate seed --- Core/Constants.cs | 2 +- Randomizer/Location.cs | 12 ++++++++++++ Randomizer/Shuffler.cs | 15 ++++++++------- Resources/default.logic | 36 +++++++++++++++++++++++++++--------- 4 files changed, 48 insertions(+), 17 deletions(-) diff --git a/Core/Constants.cs b/Core/Constants.cs index 78f26977..7405496f 100644 --- a/Core/Constants.cs +++ b/Core/Constants.cs @@ -189,7 +189,7 @@ public enum ItemType HeartContainer = 0x62, PieceOfHeart = 0x63, Wallet = 0x64, - BigBomb = 0x65, + BombBag = 0x65, LargeQuiver = 0x66, KinstoneBag = 0x67, Brioche = 0x68, diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs index 6b5ebd3f..7116d3de 100644 --- a/Randomizer/Location.cs +++ b/Randomizer/Location.cs @@ -173,6 +173,18 @@ public Item GetItemContents() public bool CanPlace(Item itemToPlace, List availableItems, List locations) { + switch (Type) + { + case LocationType.Helper: + case LocationType.Untyped: + return false; + } + + if (Address == 0) + { + return false; + } + Console.WriteLine($"Evaluating: {Name}"); foreach (Dependency dependency in Dependencies) { diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index bbf2c05b..9df83d40 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -121,7 +121,7 @@ public void LoadLocations(string locationFile) public void RandomizeLocations() { List unplacedItems = MajorItems.ToList(); - List unfilledLocations = Locations.Where(location => !location.Filled && location.Type != Location.LocationType.Helper).ToList(); + List unfilledLocations = Locations.Where(location => !location.Filled).ToList(); unfilledLocations.Shuffle(RNG); unplacedItems.Shuffle(RNG); @@ -149,14 +149,13 @@ public void RandomizeLocations() private void FillLocations(List items, List locations) { - int itemIndex; for (int i = items.Count - 1; i >= 0; i--) { - itemIndex = RNG.Next(items.Count); + int itemIndex = RNG.Next(items.Count); Item item = items[itemIndex]; Console.WriteLine($"Placing: {item.Type.ToString()}"); items.RemoveAt(itemIndex); - List availableLocations = locations.Where(location => location.CanPlace(item, items, locations)).ToList(); + List availableLocations = locations.Where(location => location.CanPlace(item, items, Locations)).ToList(); if (availableLocations.Count <= 0) { @@ -164,9 +163,11 @@ private void FillLocations(List items, List locations) return; } - availableLocations[0].Fill(item); - Console.WriteLine($"Placed {item.Type.ToString()} at {availableLocations[0].Name} with {items.Count} items remaining\n"); - locations.Remove(availableLocations[0]); + int locationIndex = RNG.Next(availableLocations.Count); + + availableLocations[locationIndex].Fill(item); + Console.WriteLine($"Placed {item.Type.ToString()} at {availableLocations[locationIndex].Name} with {items.Count} items remaining\n"); + locations.Remove(availableLocations[locationIndex]); } } diff --git a/Resources/default.logic b/Resources/default.logic index 7516a489..fd526341 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -10,17 +10,35 @@ # Items.(itemName)[.subValue] requires the given item for the location # Locations.(locationName) requires the location to be accessible first +# Item Macro Helpers +HasSword;Helper;0; (|Items.SmithSword, Items.GreenSword, Items.BlueSword, Items.FourSword) +HasBottle;Helper;0; (|Items.Bottle1, Items.Bottle2, Items.Bottle3, Items.Bottle4) + +# Misc Locations SmithHouse;Minor;22-11-00 +SmithSword;Normal;009CC8;;Items.SmithSword +BrokenPicori;Normal;008F8A;;Items.Shield +SouthKeeseCave;Minor;32-13-00; Items.BombBag +BelariBombs;Normal;00A00C; (|Items.BombBag, Locations.CompleteDeepwood) + +AccessCrenel;Helper;0; Items.BombBag, Items.GustJar, Locations.HasBottle + + +SaveEzlo;Helper;0; +#JabberNut;Normal;;Locations.SaveEzlo + +BottleDeku;Normal;0CC0C0; Items.Shield, Items.BombBag, Locations.HasSword, Items.SpinAttack # Deepwood locations -# Need gust jar to leave room -DeepwoodWiggler;Normal;48-00-01;Items.GustJar -DeepwoodButtonShells1;Minor;48-01-01;Items.GustJar -DeepwoodButtonShells2;Minor;48-01-02;Items.GustJar -DeepwoodPreCompassShells;Minor;48-02-01;Items.GustJar -DeepwoodBarrelShells;Minor;48-06-01;Items.GustJar -DeepwoodUpstairsChest;Minor;48-17-01; +DeepwoodAccess;Helper;0; +# Need gust jar or lantern to leave room +DeepwoodWiggler;Normal;48-00-01; Locations.DeepwoodAccess, Locations.HasSword, (|Items.GustJar, Items.LanternOff) +DeepwoodButtonShells1;Minor;48-01-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) +DeepwoodButtonShells2;Minor;48-01-02; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) +DeepwoodPreCompassShells;Minor;48-02-01; Locations.DeepwoodAccess,(|Items.GustJar, Items.BombBag) +DeepwoodBarrelShells;Minor;48-06-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) +DeepwoodUpstairsChest;Minor;48-17-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.LanternOff) +CompleteDeepwood;Helper;0; Items.GustJar, Locations.HasSword # Cave of Flames locations -CoFAccess;Helper;0; -CoFBigChest \ No newline at end of file +CoFAccess;Helper;0; Locations.AccessCrenel From 7fbe2e3eedd310b77ababbf1fd42a9e706a23afb Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Sat, 15 Jun 2019 20:48:24 -0600 Subject: [PATCH 06/41] Fix helper bug, add Jabber Nut and new chests to logic --- Randomizer/Dependency.cs | 6 +++--- Randomizer/Location.cs | 36 ++++++++++++++++++++++++++---------- Randomizer/Shuffler.cs | 18 ++++++++++++++++++ Resources/default.logic | 22 +++++++++++----------- 4 files changed, 58 insertions(+), 24 deletions(-) diff --git a/Randomizer/Dependency.cs b/Randomizer/Dependency.cs index adb9e378..fb5f2c5f 100644 --- a/Randomizer/Dependency.cs +++ b/Randomizer/Dependency.cs @@ -49,6 +49,7 @@ public static List GetDependencies(string logic) switch (dependencyParts[0]) { case "Locations": + case "Helpers": LocationDependency locationDependency = new LocationDependency(dependencyParts[1]); dependencies.Add(locationDependency); break; @@ -98,9 +99,9 @@ public override bool DependencyFulfilled(List availableItems, List availableItems, List de Filled = false; } - public void WriteLocation(Writer r) + public void WriteLocation(Writer w) { + if (Address == 0) + { + return; + } + switch (Type) { case LocationType.Helper: case LocationType.Untyped: return; + case LocationType.JabberNonsense: + w.WriteByte((byte)Contents.Type, Address); + w.WriteByte(Contents.SubValue, Address + 2); + break; + case LocationType.Normal: + case LocationType.Minor: + default: + + w.SetPosition(Address); + w.WriteByte((byte)Contents.Type); + w.WriteByte(Contents.SubValue); + break; } - - if (Address == 0) - { - return; - } - - r.SetPosition(Address); - r.WriteByte((byte)Contents.Type); - r.WriteByte(Contents.SubValue); } public Item GetItemContents() @@ -185,15 +194,22 @@ public bool CanPlace(Item itemToPlace, List availableItems, List return false; } + return IsAccessible(availableItems, locations); + } + + public bool IsAccessible(List availableItems, List locations) + { Console.WriteLine($"Evaluating: {Name}"); foreach (Dependency dependency in Dependencies) { if (!dependency.DependencyFulfilled(availableItems, locations)) { + Console.WriteLine("Unavailable :("); return false; } } + Console.WriteLine("Available!"); return true; } diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index 9df83d40..ad817259 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Diagnostics; using System.IO; using System.Linq; using System.Reflection; @@ -110,6 +111,7 @@ public void LoadLocations(string locationFile) MinorItems.Add(newLocation.Contents); break; case Location.LocationType.Normal: + case Location.LocationType.JabberNonsense: default: Console.WriteLine($"Hey! {newLocation.Contents.Type.ToString()}"); MajorItems.Add(newLocation.Contents); @@ -120,6 +122,9 @@ public void LoadLocations(string locationFile) public void RandomizeLocations() { + //File.WriteAllBytes(OutputDirectory + "/mcrando.gba", ROM.Instance.romData); // Write file for patching. Bad, fix later + //ApplyPatch(OutputDirectory + "/mcrando.gba"); + List unplacedItems = MajorItems.ToList(); List unfilledLocations = Locations.Where(location => !location.Filled).ToList(); unfilledLocations.Shuffle(RNG); @@ -179,5 +184,18 @@ private void FastFillLocations(List items, List locations) locations.RemoveAt(0); } } + + /*private void ApplyPatch(string outputPath) + { + string directory = Directory.GetCurrentDirectory(); + + using (Process process = new Process()) + { + process.StartInfo.FileName = "ups.exe"; + process.StartInfo.Arguments = $"patch -b {ROM.Instance.path} -p {directory + "/randopatch.ups"} -o {outputPath}"; + process.Start(); + process.WaitForExit(); + } + }*/ } } diff --git a/Resources/default.logic b/Resources/default.logic index fd526341..fdf20fde 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -16,29 +16,29 @@ HasBottle;Helper;0; (|Items.Bottle1, Items.Bottle2, Items.Bottle3, Items.Bottle4 # Misc Locations SmithHouse;Minor;22-11-00 -SmithSword;Normal;009CC8;;Items.SmithSword -BrokenPicori;Normal;008F8A;;Items.Shield +Intro1;Normal;22-11-01 +Intro2;Normal;22-11-02 +Intro3;Normal;22-11-03 SouthKeeseCave;Minor;32-13-00; Items.BombBag -BelariBombs;Normal;00A00C; (|Items.BombBag, Locations.CompleteDeepwood) +BelariBombs;Normal;00A00C -AccessCrenel;Helper;0; Items.BombBag, Items.GustJar, Locations.HasBottle +AccessCrenel;Helper;0; Items.BombBag, Items.GustJar, Helpers.HasBottle +JabberNut;JabberNonsense;09498C -SaveEzlo;Helper;0; -#JabberNut;Normal;;Locations.SaveEzlo - -BottleDeku;Normal;0CC0C0; Items.Shield, Items.BombBag, Locations.HasSword, Items.SpinAttack +BottleDeku;Normal;0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword +#, Items.SpinAttack # Deepwood locations -DeepwoodAccess;Helper;0; +DeepwoodAccess;Helper;0; Items.JabberNut # Need gust jar or lantern to leave room -DeepwoodWiggler;Normal;48-00-01; Locations.DeepwoodAccess, Locations.HasSword, (|Items.GustJar, Items.LanternOff) +DeepwoodWiggler;Normal;48-00-01; Locations.DeepwoodAccess, Helpers.HasSword, (|Items.GustJar, Items.LanternOff) DeepwoodButtonShells1;Minor;48-01-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) DeepwoodButtonShells2;Minor;48-01-02; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) DeepwoodPreCompassShells;Minor;48-02-01; Locations.DeepwoodAccess,(|Items.GustJar, Items.BombBag) DeepwoodBarrelShells;Minor;48-06-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) DeepwoodUpstairsChest;Minor;48-17-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.LanternOff) -CompleteDeepwood;Helper;0; Items.GustJar, Locations.HasSword +CompleteDeepwood;Helper;0; Locations.DeepwoodAccess, Items.GustJar, Locations.HasSword # Cave of Flames locations CoFAccess;Helper;0; Locations.AccessCrenel From 7b22194730a68e1bc305ca4c76981696ad5b2f48 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Mon, 17 Jun 2019 22:38:20 -0600 Subject: [PATCH 07/41] Add logic for Mount Crenel locations --- Randomizer/Dependency.cs | 2 -- Randomizer/Location.cs | 28 +++++++++++++++++--- Randomizer/Shuffler.cs | 37 ++++++++++++++++++++++----- Resources/default.logic | 55 ++++++++++++++++++++++------------------ 4 files changed, 87 insertions(+), 35 deletions(-) diff --git a/Randomizer/Dependency.cs b/Randomizer/Dependency.cs index fb5f2c5f..a7331409 100644 --- a/Randomizer/Dependency.cs +++ b/Randomizer/Dependency.cs @@ -20,8 +20,6 @@ public static List GetDependencies(string logic) { List dependencies = new List(); - logic = logic.Replace(" ", ""); - // Match: comma, capture and match anything between string regexPattern = @"(?:,|^)\(([&|](?:[^()]|(?

\()|(?<-p>\)))+)\)(?:,|$)|,"; string[] subLogic = Regex.Split(logic, regexPattern); diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs index b19085a2..4a2e8f13 100644 --- a/Randomizer/Location.cs +++ b/Randomizer/Location.cs @@ -19,6 +19,7 @@ public static Location GetLocation(string locationText) if (locationParts.Length < 3) { + Console.WriteLine("Too short"); return new Location(LocationType.Untyped, "INVALID LOCATION", 0, null); } @@ -27,6 +28,7 @@ public static Location GetLocation(string locationText) string locationType = locationParts[1]; if (!Enum.TryParse(locationType, out LocationType type) || type == LocationType.Untyped) { + Console.WriteLine("Invalid type"); return new Location(LocationType.Untyped, "INVALID LOCATION", 0, null); } @@ -73,6 +75,11 @@ public static Location GetLocation(string locationText) public static int GetAddressFromString(string addressString) { // Either direct address or area-room-chest + if (addressString == "") + { + return 0; + } + if (int.TryParse(addressString, NumberStyles.HexNumber, null, out int address)) { return address; @@ -116,7 +123,8 @@ public enum LocationType KinstoneItem, HeartPieceItem, JabberNonsense, - Helper + Helper, + Starting } public List Dependencies; @@ -174,8 +182,22 @@ public void WriteLocation(Writer w) public Item GetItemContents() { - ItemType type = (ItemType)ROM.Instance.reader.ReadByte(Address); - byte subType = ROM.Instance.reader.ReadByte(); + ItemType type = ItemType.Untyped; + byte subType = 0; + + switch (Type) + { + case LocationType.JabberNonsense: + type = (ItemType)ROM.Instance.reader.ReadByte(Address); + subType = ROM.Instance.reader.ReadByte(Address + 2); + break; + default: + type = (ItemType)ROM.Instance.reader.ReadByte(Address); + subType = ROM.Instance.reader.ReadByte(); + break; + } + + return new Item(type, subType); } diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index ad817259..ae69bf3f 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -52,6 +52,7 @@ public class Shuffler private Random RNG; private List Locations; + private List StartingLocations; private List MajorItems; private List MinorItems; private string OutputDirectory; @@ -93,25 +94,40 @@ public void LoadLocations(string locationFile) locationStrings = File.ReadAllLines(locationFile); } - foreach (string locationString in locationStrings) + foreach (string locationLine in locationStrings) { - if (locationString == "" || locationString[0] == '#') + string locationString = locationLine.Split('#')[0].Replace(" ", ""); + locationString = locationString.Replace(" ", ""); + if (locationString == "") { continue; } Location newLocation = Location.GetLocation(locationString); - Locations.Add(newLocation); + + switch (newLocation.Type) + { + case Location.LocationType.Starting: + StartingLocations.Add(newLocation); + break; + default: + Locations.Add(newLocation); + break; + } + switch(newLocation.Type) { case Location.LocationType.Untyped: case Location.LocationType.Helper: + Console.WriteLine($"Helper or untyped {newLocation.Name}"); break; case Location.LocationType.Minor: + Console.WriteLine(newLocation.Contents.Type.ToString()); MinorItems.Add(newLocation.Contents); break; case Location.LocationType.Normal: case Location.LocationType.JabberNonsense: + case Location.LocationType.Starting: default: Console.WriteLine($"Hey! {newLocation.Contents.Type.ToString()}"); MajorItems.Add(newLocation.Contents); @@ -122,14 +138,19 @@ public void LoadLocations(string locationFile) public void RandomizeLocations() { - //File.WriteAllBytes(OutputDirectory + "/mcrando.gba", ROM.Instance.romData); // Write file for patching. Bad, fix later - //ApplyPatch(OutputDirectory + "/mcrando.gba"); + List allItems = MajorItems.Concat(MinorItems).ToList(); + List unplacedItems = MajorItems.ToList(); - List unfilledLocations = Locations.Where(location => !location.Filled).ToList(); + List unfilledLocations = Locations.Where(location => !location.Filled && location.Type != Location.LocationType.Helper && location.Type != Location.LocationType.Untyped).ToList(); unfilledLocations.Shuffle(RNG); unplacedItems.Shuffle(RNG); + foreach (Item item in MajorItems) + { + Console.WriteLine($"Item: {item.Type.ToString()} Sub: {item.SubValue}"); + } + FillLocations(MajorItems.ToList(), unfilledLocations); unfilledLocations.Shuffle(RNG); @@ -159,6 +180,10 @@ private void FillLocations(List items, List locations) int itemIndex = RNG.Next(items.Count); Item item = items[itemIndex]; Console.WriteLine($"Placing: {item.Type.ToString()}"); + if (item.Type == ItemType.KinstoneX || item.Type == ItemType.ShellsX || item.Type == ItemType.Rupee20) + { + Console.WriteLine("Inconcievable!"); + } items.RemoveAt(itemIndex); List availableLocations = locations.Where(location => location.CanPlace(item, items, Locations)).ToList(); diff --git a/Resources/default.logic b/Resources/default.logic index fdf20fde..c08b4aba 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -9,36 +9,43 @@ # (&a,b,c...) and (|a,b,c...) are an and/or of the contents # Items.(itemName)[.subValue] requires the given item for the location # Locations.(locationName) requires the location to be accessible first +# Helpers.(helperName) is equivalent for Locations.(locationName), only provided for readability +# Item Override should be in the form of Items.(itemName)[.subValue] + +# Beginning Items - Only item override is set to introduce them to the pool +#Intro1; Starting;;; Items.Shield +#Intro2; Starting;;; Items.SmithSword # Item Macro Helpers -HasSword;Helper;0; (|Items.SmithSword, Items.GreenSword, Items.BlueSword, Items.FourSword) -HasBottle;Helper;0; (|Items.Bottle1, Items.Bottle2, Items.Bottle3, Items.Bottle4) +HasSword; Helper;; (|Items.SmithSword, Items.GreenSword, Items.ReadSword, Items.BlueSword, Items.FourSword) +HasBottle; Helper;; (|Items.Bottle1.FF, Items.Bottle2.FF, Items.Bottle3.FF, Items.Bottle4.FF) # Misc Locations -SmithHouse;Minor;22-11-00 -Intro1;Normal;22-11-01 +SmithHouse; Minor; 22-11-00 +Intro1;Normal;22-11-01 # Will remove these later Intro2;Normal;22-11-02 -Intro3;Normal;22-11-03 -SouthKeeseCave;Minor;32-13-00; Items.BombBag -BelariBombs;Normal;00A00C - -AccessCrenel;Helper;0; Items.BombBag, Items.GustJar, Helpers.HasBottle - -JabberNut;JabberNonsense;09498C - -BottleDeku;Normal;0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword -#, Items.SpinAttack +SouthKeeseCave; Minor; 32-13-00; Items.BombBag +BelariBombs; Normal; 00A00C +JabberNut; JabberNonsense; 09498C +BottleScrub; Normal; 0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword #, Items.SpinAttack + +# Mount Crenel Locations +AccessCrenel;Helper;; Helpers.HasBottle, Items.BombBag +CrenelLowerScrub; Minor; 0CC09C; Helpers.HasBottle, Items.Shield # Technically a Crenel item, but doesn't need bombs +CrenelMinishHole; Minor; 35-00-00; Locations.AccessCrenel +CrenelCaveDownstairs; Minor; 26-07-00; Locations.AccessCrenel +CrenelGripScrub; Normal; 0CC0A8; Locations.AccessCrenel, Items.Shield +CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GustJar, Items.GripRing.FF)) # Deepwood locations -DeepwoodAccess;Helper;0; Items.JabberNut -# Need gust jar or lantern to leave room -DeepwoodWiggler;Normal;48-00-01; Locations.DeepwoodAccess, Helpers.HasSword, (|Items.GustJar, Items.LanternOff) -DeepwoodButtonShells1;Minor;48-01-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) -DeepwoodButtonShells2;Minor;48-01-02; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) -DeepwoodPreCompassShells;Minor;48-02-01; Locations.DeepwoodAccess,(|Items.GustJar, Items.BombBag) -DeepwoodBarrelShells;Minor;48-06-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) -DeepwoodUpstairsChest;Minor;48-17-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.LanternOff) -CompleteDeepwood;Helper;0; Locations.DeepwoodAccess, Items.GustJar, Locations.HasSword +DeepwoodAccess;Helper;; Items.JabberNut +DeepwoodWiggler; Normal; 48-00-01; Locations.DeepwoodAccess, Helpers.HasSword, (|Items.GustJar, Items.LanternOff) # Need gust jar or lantern to leave room +DeepwoodButtonShells1; Minor; 48-01-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) +DeepwoodButtonShells2; Minor; 48-01-02; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) +DeepwoodPreCompassShells; Minor; 48-02-01; Locations.DeepwoodAccess,(|Items.GustJar, Items.BombBag) +DeepwoodBarrelShells; Minor; 48-06-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) +DeepwoodUpstairsChest; Minor; 48-17-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.LanternOff) +CompleteDeepwood; Helper;; Locations.DeepwoodAccess, Items.GustJar, Locations.HasSword # Cave of Flames locations -CoFAccess;Helper;0; Locations.AccessCrenel +CoFAccess;Helper;; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GustJar, Items.GripRing.FF)) From e5a0315b4acb6d22929e344ff29364ab636a1f04 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Wed, 19 Jun 2019 17:37:31 -0600 Subject: [PATCH 08/41] Add capability to apply UPS patches --- Core/ROM.cs | 4 +- MinishRandomizer.csproj | 1 + Utilities/PatchUtil.cs | 153 ++++++++++++++++++++++++++++++++++++++++ 3 files changed, 156 insertions(+), 2 deletions(-) create mode 100644 Utilities/PatchUtil.cs diff --git a/Core/ROM.cs b/Core/ROM.cs index 7ce16a92..7d8f937f 100644 --- a/Core/ROM.cs +++ b/Core/ROM.cs @@ -22,13 +22,13 @@ public ROM(string filePath) Instance = this; path = filePath; byte[] smallData = File.ReadAllBytes(filePath); - if (smallData.Length >= 0x01000000) + if (smallData.Length >= 0x02000000) { romData = smallData; } else { - romData = new byte[0x1000000]; + romData = new byte[0x2000000]; smallData.CopyTo(romData, 0); } diff --git a/MinishRandomizer.csproj b/MinishRandomizer.csproj index 517ea6c9..95009418 100644 --- a/MinishRandomizer.csproj +++ b/MinishRandomizer.csproj @@ -59,6 +59,7 @@ + diff --git a/Utilities/PatchUtil.cs b/Utilities/PatchUtil.cs new file mode 100644 index 00000000..f370f625 --- /dev/null +++ b/Utilities/PatchUtil.cs @@ -0,0 +1,153 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using System.Threading.Tasks; + +namespace MinishRandomizer.Utilities +{ + static class PatchUtil { + ///

+ /// Apply a UPS patch to a byte array, in place + /// + /// The data to patch + /// The patch to apply + public static void ApplyUPS(byte[] inputData, byte[] patchData, bool checkCRC = true) + { + using (MemoryStream inputStream = new MemoryStream(inputData)) + { + Reader dataReader = new Reader(inputStream); + Writer dataWriter = new Writer(inputStream); + + using (MemoryStream patchStream = new MemoryStream(patchData)) + { + Reader patchReader = new Reader(patchStream); + + patchReader.SetPosition(patchData.Length - 12); + uint inputCRC = patchReader.ReadUInt32(); + uint outputCRC = patchReader.ReadUInt32(); + uint patchCRC = patchReader.ReadUInt32(); + + patchReader.SetPosition(0); + + // Check for magic number + if (!Enumerable.SequenceEqual(patchReader.ReadBytes(4), new byte[] { 0x55, 0x50, 0x53, 0x31 })) + { + Console.WriteLine("Invalid patch: Lacks magic number!"); + return; + } + + long inputSize = ReadVWI(patchReader); + long outputSize = ReadVWI(patchReader); + + if (outputSize > inputData.Length) + { + Console.WriteLine("Problem: Can't change ROM size while patching in this implementation."); + return; + } + + if (checkCRC && crc32(inputData, inputSize) != inputCRC) + { + Console.WriteLine("Input ROM has an invalid CRC!"); + return; + } + + if (checkCRC && crc32(patchData, patchData.Length - 4) != patchCRC) + { + Console.WriteLine("Patch has an invalid CRC!"); + return; + } + + dataReader.SetPosition(0); + dataWriter.SetPosition(0); + + while (patchReader.Position < patchData.Length - 12) + { + long skippedBytes = ReadVWI(patchReader); + dataReader.SetPosition(dataReader.Position + skippedBytes); + + byte nextPatch = patchReader.ReadByte(); + while (nextPatch != 0) + { + byte nextInput = dataReader.PeekByte(); + + dataWriter.WriteByte((byte)(nextInput ^ nextPatch)); + + nextPatch = patchReader.ReadByte(); + } + + dataReader.ReadByte(); // Advance the read/write head one byte + } + + if (crc32(inputData, outputSize) != outputCRC) + { + Console.WriteLine("Output ROM has an invalid CRC! ...but the array was already patched!"); + return; + } + } + } + } + + /// + /// Read a variable-width integer in UPS and BPS format, adapted from https://www.romhacking.net/patch/crc32 + /// + /// The reader to read the integer from + /// The value of the integer, restricted to being a 64-bit signed integer + private static long ReadVWI(Reader r) + { + byte nextByte; + long output = 0; + int shift = 1; + do + { + nextByte = r.ReadByte(); + + output += (nextByte & 0x7F) * shift; + + shift <<= 7; + output += shift; + } + while ((nextByte & 0x80) == 0); + + output -= shift; + + return output; + } + + private static void WriteVWI(Writer w) + { + + } + + /// + /// Find the crc32 of a byte array, adapted from https://www.romhacking.net/patch/crc32 + /// + /// The byte array to checksum + /// The size of the array to checksum, negative values equal the size of data + private static uint crc32(byte[] data, long length) + { + uint[] crcTable = new uint[256]; + + for (int i = 0; i < 256; i++) + { + uint c = (uint)i; + + for (int j = 0; j < 8; j++) + { + c = ((c & 1) >= 1 ? (0xedb88320 ^ (c >> 1)) : (c >> 1)); + } + crcTable[i] = c; + } + + uint outputCRC = 0xFFFFFFFF; + for (int i = 0; i < length; i++) + { + //Console.WriteLine(StringUtil.AsStringHex8((int)outputCRC)); + outputCRC = (outputCRC >> 8) ^ crcTable[(outputCRC ^ data[i]) & 0xFF]; + } + + return outputCRC ^ 0xFFFFFFFF; + } + } +} From 95f1498bcda83edf6bca46259563a29ac6e054c7 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Thu, 20 Jun 2019 19:26:10 -0600 Subject: [PATCH 09/41] Add default UPS patch to open the world, update logic --- MinishRandomizer.csproj | 1 + Randomizer/Location.cs | 10 ++++- Randomizer/Shuffler.cs | 24 +++++++++++- Resources/default.logic | 29 ++++++++------- Resources/randoPatch.ups | Bin 0 -> 471 bytes UI/MainWindow.Designer.cs | 75 +++++++++++++++++++++++++++++--------- UI/MainWindow.cs | 31 ++++++++++++++++ 7 files changed, 136 insertions(+), 34 deletions(-) create mode 100644 Resources/randoPatch.ups diff --git a/MinishRandomizer.csproj b/MinishRandomizer.csproj index 95009418..524c2d5e 100644 --- a/MinishRandomizer.csproj +++ b/MinishRandomizer.csproj @@ -86,6 +86,7 @@ True +
diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs index 4a2e8f13..99840649 100644 --- a/Randomizer/Location.cs +++ b/Randomizer/Location.cs @@ -124,7 +124,7 @@ public enum LocationType HeartPieceItem, JabberNonsense, Helper, - Starting + StartingItem } public List Dependencies; @@ -169,6 +169,14 @@ public void WriteLocation(Writer w) w.WriteByte((byte)Contents.Type, Address); w.WriteByte(Contents.SubValue, Address + 2); break; + case LocationType.StartingItem: + // Nonfunctional in new patches + break; + w.SetPosition(0xEF3348 + ((byte)Contents.Type >> 2)); // Get items index in the starting item table + byte initialByte = ROM.Instance.reader.ReadByte(0xEF3348 + ((byte)Contents.Type >> 2)); + initialByte |= (byte)(1 << ((byte)Contents.Type & 3) * 2); + w.WriteByte(initialByte); + break; case LocationType.Normal: case LocationType.Minor: default: diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index ae69bf3f..97841eff 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -107,7 +107,7 @@ public void LoadLocations(string locationFile) switch (newLocation.Type) { - case Location.LocationType.Starting: + case Location.LocationType.StartingItem: StartingLocations.Add(newLocation); break; default: @@ -127,7 +127,7 @@ public void LoadLocations(string locationFile) break; case Location.LocationType.Normal: case Location.LocationType.JabberNonsense: - case Location.LocationType.Starting: + case Location.LocationType.StartingItem: default: Console.WriteLine($"Hey! {newLocation.Contents.Type.ToString()}"); MajorItems.Add(newLocation.Contents); @@ -136,6 +136,26 @@ public void LoadLocations(string locationFile) } } + public void PatchRom(string locationFile) + { + byte[] patchContents; + if (locationFile == null) + { + Assembly assembly = Assembly.GetExecutingAssembly(); + using (Stream stream = assembly.GetManifestResourceStream("MinishRandomizer.Resources.randoPatch.ups")) + { + patchContents = new byte[stream.Length]; + stream.Read(patchContents, 0, (int)stream.Length); + } + } + else + { + patchContents = File.ReadAllBytes(locationFile); + } + + PatchUtil.ApplyUPS(ROM.Instance.romData, patchContents); + } + public void RandomizeLocations() { List allItems = MajorItems.Concat(MinorItems).ToList(); diff --git a/Resources/default.logic b/Resources/default.logic index c08b4aba..94c8445b 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -12,9 +12,8 @@ # Helpers.(helperName) is equivalent for Locations.(locationName), only provided for readability # Item Override should be in the form of Items.(itemName)[.subValue] -# Beginning Items - Only item override is set to introduce them to the pool -#Intro1; Starting;;; Items.Shield -#Intro2; Starting;;; Items.SmithSword + +# Deku (shop?) items are currently buggy and commented out. As a side effect, bottles are gone :( # Item Macro Helpers HasSword; Helper;; (|Items.SmithSword, Items.GreenSword, Items.ReadSword, Items.BlueSword, Items.FourSword) @@ -22,20 +21,23 @@ HasBottle; Helper;; (|Items.Bottle1.FF, Items.Bottle2.FF, Items.Bottle3.FF, Item # Misc Locations SmithHouse; Minor; 22-11-00 -Intro1;Normal;22-11-01 # Will remove these later -Intro2;Normal;22-11-02 +IntroItem1; Normal; F252B;; Items.SmithSword +IntroItem2; Normal; F253B;; Items.Shield SouthKeeseCave; Minor; 32-13-00; Items.BombBag BelariBombs; Normal; 00A00C JabberNut; JabberNonsense; 09498C -BottleScrub; Normal; 0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword #, Items.SpinAttack +#BottleScrub; Normal; 0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword #, Items.SpinAttack # Mount Crenel Locations -AccessCrenel;Helper;; Helpers.HasBottle, Items.BombBag -CrenelLowerScrub; Minor; 0CC09C; Helpers.HasBottle, Items.Shield # Technically a Crenel item, but doesn't need bombs -CrenelMinishHole; Minor; 35-00-00; Locations.AccessCrenel -CrenelCaveDownstairs; Minor; 26-07-00; Locations.AccessCrenel -CrenelGripScrub; Normal; 0CC0A8; Locations.AccessCrenel, Items.Shield -CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GustJar, Items.GripRing.FF)) +AccessCrenel;Helper;; Helpers.HasBottle, (|Items.BombBag, Items.GripRing.FF) +#CrenelLowerScrub; Minor; 0CC09C; Helpers.HasBottle, Items.Shield # Technically a Crenel item, but doesn't need bombs +CrenelVineHole; Minor; 35-00-00; Locations.AccessCrenel, (|Items.GustJar, Items.BombBag) +CrenelMinishHouse; Minor; 27-03-00; Locations.AccessCrenel, (|Items.GustJar, Items.BombBag) +CrenelCaveDownstairs; Minor; 26-07-00; Locations.AccessCrenel, Items.BombBag +CrenelHeartCaveLeft; Minor; 26-08-00; Locations.AccessCrenel, Items.BombBag +CrenelHeartCaveRight; Minor; 26-08-01; Locations.AccessCrenel, Items.BombBag +#CrenelGripScrub; Normal; 0CC0A8; Locations.AccessCrenel, Items.Shield, Items.BombBag +CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing.FF, (|Items.GustJar, Items.LightArrow))) # Deepwood locations DeepwoodAccess;Helper;; Items.JabberNut @@ -48,4 +50,5 @@ DeepwoodUpstairsChest; Minor; 48-17-01; Locations.DeepwoodAccess, (|Items.GustJa CompleteDeepwood; Helper;; Locations.DeepwoodAccess, Items.GustJar, Locations.HasSword # Cave of Flames locations -CoFAccess;Helper;; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GustJar, Items.GripRing.FF)) +CoFAccess;Helper;; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing.FF, (|Items.GustJar, Items.LightArrow))) + diff --git a/Resources/randoPatch.ups b/Resources/randoPatch.ups new file mode 100644 index 0000000000000000000000000000000000000000..de92ac5701f8066734ee6ca0193d722112311d49 GIT binary patch literal 471 zcmWFy2sUJ>uWJKgkAz8Yw;j2~ApA}DFDL84@F+J1m)8;ufleJ0vIUN(vtHL=P;{8% z%fisW!qDx@0iqbC>s#->`u%_T|KtDv{l@}U{aUpORrvURFn{ZR2#qQUVAVo;KY6)>ko(*4HjZ%(VsazV*39Jzx1!SpYQ*7 z;NPi#CpjcOpXQhNvn6pSLxb~wcKe;_rv<*3SpWFG|BS$Q^Q-^=f&yVXkbU;b|G$rb d>`6d&^5y@3xzW7IFsUf{g^68~afs%hT>#>!fL{Or literal 0 HcmV?d00001 diff --git a/UI/MainWindow.Designer.cs b/UI/MainWindow.Designer.cs index 3fc1a7ac..6060a762 100644 --- a/UI/MainWindow.Designer.cs +++ b/UI/MainWindow.Designer.cs @@ -35,14 +35,17 @@ private void InitializeComponent() this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.randomize = new System.Windows.Forms.Button(); - this.logicTab = new System.Windows.Forms.TabPage(); + this.generalTab = new System.Windows.Forms.TabPage(); this.customLogicPath = new System.Windows.Forms.TextBox(); this.browseLogicButton = new System.Windows.Forms.Button(); this.customLogicCheckBox = new System.Windows.Forms.CheckBox(); this.mainTabs = new System.Windows.Forms.TabControl(); + this.browsePatchButton = new System.Windows.Forms.Button(); + this.customPatchPath = new System.Windows.Forms.TextBox(); + this.customPatchCheckBox = new System.Windows.Forms.CheckBox(); this.statusStrip1.SuspendLayout(); this.menuStrip1.SuspendLayout(); - this.logicTab.SuspendLayout(); + this.generalTab.SuspendLayout(); this.mainTabs.SuspendLayout(); this.SuspendLayout(); // @@ -96,18 +99,21 @@ private void InitializeComponent() this.randomize.UseVisualStyleBackColor = true; this.randomize.Click += new System.EventHandler(this.Randomize_Click); // - // logicTab - // - this.logicTab.Controls.Add(this.customLogicPath); - this.logicTab.Controls.Add(this.browseLogicButton); - this.logicTab.Controls.Add(this.customLogicCheckBox); - this.logicTab.Location = new System.Drawing.Point(4, 22); - this.logicTab.Name = "logicTab"; - this.logicTab.Padding = new System.Windows.Forms.Padding(3); - this.logicTab.Size = new System.Drawing.Size(361, 209); - this.logicTab.TabIndex = 0; - this.logicTab.Text = "Logic"; - this.logicTab.UseVisualStyleBackColor = true; + // generalTab + // + this.generalTab.Controls.Add(this.customPatchCheckBox); + this.generalTab.Controls.Add(this.customPatchPath); + this.generalTab.Controls.Add(this.browsePatchButton); + this.generalTab.Controls.Add(this.customLogicPath); + this.generalTab.Controls.Add(this.browseLogicButton); + this.generalTab.Controls.Add(this.customLogicCheckBox); + this.generalTab.Location = new System.Drawing.Point(4, 22); + this.generalTab.Name = "generalTab"; + this.generalTab.Padding = new System.Windows.Forms.Padding(3); + this.generalTab.Size = new System.Drawing.Size(361, 209); + this.generalTab.TabIndex = 0; + this.generalTab.Text = "General"; + this.generalTab.UseVisualStyleBackColor = true; // // customLogicPath // @@ -141,13 +147,43 @@ private void InitializeComponent() // // mainTabs // - this.mainTabs.Controls.Add(this.logicTab); + this.mainTabs.Controls.Add(this.generalTab); this.mainTabs.Location = new System.Drawing.Point(9, 27); this.mainTabs.Name = "mainTabs"; this.mainTabs.SelectedIndex = 0; this.mainTabs.Size = new System.Drawing.Size(369, 235); this.mainTabs.TabIndex = 9; // + // browsePatchButton + // + this.browsePatchButton.Enabled = false; + this.browsePatchButton.Location = new System.Drawing.Point(32, 123); + this.browsePatchButton.Name = "browsePatchButton"; + this.browsePatchButton.Size = new System.Drawing.Size(75, 23); + this.browsePatchButton.TabIndex = 9; + this.browsePatchButton.Text = "Browse..."; + this.browsePatchButton.UseVisualStyleBackColor = true; + this.browsePatchButton.Click += new System.EventHandler(this.BrowsePatchButton_Click); + // + // customPatchPath + // + this.customPatchPath.Enabled = false; + this.customPatchPath.Location = new System.Drawing.Point(113, 123); + this.customPatchPath.Name = "customPatchPath"; + this.customPatchPath.Size = new System.Drawing.Size(206, 20); + this.customPatchPath.TabIndex = 10; + // + // customPatchCheckBox + // + this.customPatchCheckBox.AutoSize = true; + this.customPatchCheckBox.Location = new System.Drawing.Point(5, 100); + this.customPatchCheckBox.Name = "customPatchCheckBox"; + this.customPatchCheckBox.Size = new System.Drawing.Size(114, 17); + this.customPatchCheckBox.TabIndex = 11; + this.customPatchCheckBox.Text = "Use Custom Patch"; + this.customPatchCheckBox.UseVisualStyleBackColor = true; + this.customPatchCheckBox.CheckedChanged += new System.EventHandler(this.CustomPatchCheckBox_CheckedChanged); + // // MainWindow // this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); @@ -165,8 +201,8 @@ private void InitializeComponent() this.statusStrip1.PerformLayout(); this.menuStrip1.ResumeLayout(false); this.menuStrip1.PerformLayout(); - this.logicTab.ResumeLayout(false); - this.logicTab.PerformLayout(); + this.generalTab.ResumeLayout(false); + this.generalTab.PerformLayout(); this.mainTabs.ResumeLayout(false); this.ResumeLayout(false); this.PerformLayout(); @@ -181,11 +217,14 @@ private void InitializeComponent() private System.Windows.Forms.ToolStripMenuItem openToolStripMenuItem; private System.Windows.Forms.Button randomize; private System.Windows.Forms.ToolStripStatusLabel statusText; - private System.Windows.Forms.TabPage logicTab; + private System.Windows.Forms.TabPage generalTab; private System.Windows.Forms.TextBox customLogicPath; private System.Windows.Forms.Button browseLogicButton; private System.Windows.Forms.CheckBox customLogicCheckBox; private System.Windows.Forms.TabControl mainTabs; + private System.Windows.Forms.CheckBox customPatchCheckBox; + private System.Windows.Forms.TextBox customPatchPath; + private System.Windows.Forms.Button browsePatchButton; } } diff --git a/UI/MainWindow.cs b/UI/MainWindow.cs index 7e472c50..c9437133 100644 --- a/UI/MainWindow.cs +++ b/UI/MainWindow.cs @@ -46,6 +46,15 @@ private void Randomize_Click(object sender, EventArgs e) shuffler.LoadLocations(null); } + if (customPatchCheckBox.Checked) + { + shuffler.PatchRom(customPatchPath.Text); + } + else + { + shuffler.PatchRom(null); + } + shuffler.RandomizeLocations(); } @@ -86,6 +95,12 @@ private void CustomLogicCheckBox_CheckedChanged(object sender, EventArgs e) customLogicPath.Enabled = customLogicCheckBox.Checked; } + private void CustomPatchCheckBox_CheckedChanged(object sender, EventArgs e) + { + browsePatchButton.Enabled = customPatchCheckBox.Checked; + customPatchPath.Enabled = customPatchCheckBox.Checked; + } + private void BrowseLogicButton_Click(object sender, EventArgs e) { OpenFileDialog ofd = new OpenFileDialog @@ -101,5 +116,21 @@ private void BrowseLogicButton_Click(object sender, EventArgs e) customLogicPath.Text = ofd.FileName; } + + private void BrowsePatchButton_Click(object sender, EventArgs e) + { + OpenFileDialog ofd = new OpenFileDialog + { + Filter = "UPS Patch|*.ups|All Files|*.*", + Title = "Select Custom Patch" + }; + + if (ofd.ShowDialog() != DialogResult.OK) + { + return; + } + + customPatchPath.Text = ofd.FileName; + } } } From 8baf5b04642950b9da3f51bca846ef904423db35 Mon Sep 17 00:00:00 2001 From: LeonarthCG Date: Fri, 21 Jun 2019 17:52:20 +0200 Subject: [PATCH 10/41] Update randoPatch.ups --- Resources/randoPatch.ups | Bin 471 -> 679 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/randoPatch.ups b/Resources/randoPatch.ups index de92ac5701f8066734ee6ca0193d722112311d49..b0e4b5ccbaf2efd87d6a6f4c723bb5122670dc0e 100644 GIT binary patch delta 419 zcmcc4yqvW@G$7cJp}wvSgjK929iPWgH1D57`j;bHzc8q`zTI}@7K89N-M?(C2g9S> z7+hXUFa+DRoS)86H_L;eI<09!w!m>`rt2CEsq5@Eez?ORFE#t~e}>q$!~YqC-&|1U z=U|X-5MXHVWoTq!Xg|S#V4q~*Y2BmF&&I)!AyVHuIgL@LzWa>8_Y&(L-?f3X`PKh_ zzyJULUm3_gd*%P%e?Ybpki7@WRs^zl0@=TS;xa(?KB%}fkez(_|6gvPI5JoT1glrg zne*olL*w07zyB})fBgTy{XoH2tM>o@e;kPaPb?3bc%x_H1A%(4<@Y~sN!7+hXUFa$bvOvn~Ep3Zt* zgF(?@jxP&C0}Dg9F9(QXl&){R`|9`q<^PZW|2MgjQAA+XuT`u5BZ0}9jFF6gfKs!V z7@8-x>bRz#7WiIb{p0)oGXmeuum1o09jJIakbU;b|G$rb>`6d&^5y@3xseP3F&QQm PCBHDSOEM18{Id%HQD12g From eb6872f84365f91380c5888361daf6343a7ee865 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Mon, 24 Jun 2019 21:23:07 -0600 Subject: [PATCH 11/41] Add Deku Scrubs back to item pool, add beginning of dungeon items --- Randomizer/Dependency.cs | 2 -- Randomizer/Location.cs | 56 ++++++++++++++++++++++++++++++---------- Randomizer/Shuffler.cs | 9 ++++--- Resources/default.logic | 48 +++++++++++++++++----------------- UI/MainWindow.cs | 40 ++++++++++++++++------------ 5 files changed, 96 insertions(+), 59 deletions(-) diff --git a/Randomizer/Dependency.cs b/Randomizer/Dependency.cs index a7331409..4c260684 100644 --- a/Randomizer/Dependency.cs +++ b/Randomizer/Dependency.cs @@ -99,12 +99,10 @@ public override bool DependencyFulfilled(List availableItems, List= 2) + { + dungeon = names[1]; + } string locationType = locationParts[1]; if (!Enum.TryParse(locationType, out LocationType type) || type == LocationType.Untyped) { Console.WriteLine("Invalid type"); - return new Location(LocationType.Untyped, "INVALID LOCATION", 0, null); + return new Location(LocationType.Untyped, "INVALID LOCATION", "", 0, null); } int address = GetAddressFromString(locationParts[2]); @@ -42,7 +48,7 @@ public static Location GetLocation(string locationText) List dependencies = Dependency.GetDependencies(logic); - Location location = new Location(type, name, address, dependencies); + Location location = new Location(type, name, dungeon, address, dependencies); if (locationParts.Length >= 5) { @@ -116,7 +122,7 @@ public static int GetAddressFromString(string addressString) public enum LocationType { Untyped, - Normal, + Major, Minor, DungeonItem, NPCItem, @@ -124,21 +130,24 @@ public enum LocationType HeartPieceItem, JabberNonsense, Helper, - StartingItem + StartingItem, + PurchaseItem } public List Dependencies; public LocationType Type; public string Name; + public string Dungeon; public bool Filled; public Item Contents { get; private set; } private Item DefaultContents; private int Address; - public Location(LocationType type, string name, int address, List dependencies) + public Location(LocationType type, string name, string dungeon, int address, List dependencies) { Type = type; Name = name; + Dungeon = dungeon; Address = address; @@ -169,6 +178,11 @@ public void WriteLocation(Writer w) w.WriteByte((byte)Contents.Type, Address); w.WriteByte(Contents.SubValue, Address + 2); break; + case LocationType.PurchaseItem: + w.SetPosition(Address); + w.WriteByte((byte)Contents.Type); + w.WriteByte(0xFF); + break; case LocationType.StartingItem: // Nonfunctional in new patches break; @@ -177,7 +191,7 @@ public void WriteLocation(Writer w) initialByte |= (byte)(1 << ((byte)Contents.Type & 3) * 2); w.WriteByte(initialByte); break; - case LocationType.Normal: + case LocationType.Major: case LocationType.Minor: default: @@ -190,8 +204,8 @@ public void WriteLocation(Writer w) public Item GetItemContents() { - ItemType type = ItemType.Untyped; - byte subType = 0; + ItemType type; + byte subType; switch (Type) { @@ -199,6 +213,10 @@ public Item GetItemContents() type = (ItemType)ROM.Instance.reader.ReadByte(Address); subType = ROM.Instance.reader.ReadByte(Address + 2); break; + case LocationType.PurchaseItem: + type = (ItemType)ROM.Instance.reader.ReadByte(Address); + subType = 0x00; + break; default: type = (ItemType)ROM.Instance.reader.ReadByte(Address); subType = ROM.Instance.reader.ReadByte(); @@ -207,7 +225,7 @@ public Item GetItemContents() - return new Item(type, subType); + return new Item(type, subType, Dungeon); } public bool CanPlace(Item itemToPlace, List availableItems, List locations) @@ -219,6 +237,19 @@ public bool CanPlace(Item itemToPlace, List availableItems, List return false; } + switch (itemToPlace.Type) + { + case ItemType.SmallKey: + case ItemType.BigKey: + case ItemType.DungeonMap: + case ItemType.Compass: + if (itemToPlace.Dungeon != Dungeon) + { + return false; + } + break; + } + if (Address == 0) { return false; @@ -229,17 +260,14 @@ public bool CanPlace(Item itemToPlace, List availableItems, List public bool IsAccessible(List availableItems, List locations) { - Console.WriteLine($"Evaluating: {Name}"); foreach (Dependency dependency in Dependencies) { if (!dependency.DependencyFulfilled(availableItems, locations)) { - Console.WriteLine("Unavailable :("); return false; } } - Console.WriteLine("Available!"); return true; } diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index 97841eff..25a8ca12 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -16,8 +16,9 @@ namespace MinishRandomizer.Randomizer public readonly ItemType Type; public readonly KinstoneType Kinstone; public readonly byte SubValue; + public readonly string Dungeon; - public Item(ItemType type, byte subValue) + public Item(ItemType type, byte subValue, string dungeon = "") { Type = type; SubValue = subValue; @@ -29,6 +30,8 @@ public Item(ItemType type, byte subValue) { Kinstone = KinstoneType.UnTyped; } + + Dungeon = dungeon; } public override bool Equals(object obj) @@ -125,7 +128,7 @@ public void LoadLocations(string locationFile) Console.WriteLine(newLocation.Contents.Type.ToString()); MinorItems.Add(newLocation.Contents); break; - case Location.LocationType.Normal: + case Location.LocationType.Major: case Location.LocationType.JabberNonsense: case Location.LocationType.StartingItem: default: @@ -209,8 +212,8 @@ private void FillLocations(List items, List locations) if (availableLocations.Count <= 0) { - Console.WriteLine($"Could not place {item.Type.ToString()}!"); return; + //throw new Exception($"Could not place {item.Type.ToString()}!"); } int locationIndex = RNG.Next(availableLocations.Count); diff --git a/Resources/default.logic b/Resources/default.logic index 94c8445b..8be089ef 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -12,43 +12,45 @@ # Helpers.(helperName) is equivalent for Locations.(locationName), only provided for readability # Item Override should be in the form of Items.(itemName)[.subValue] - -# Deku (shop?) items are currently buggy and commented out. As a side effect, bottles are gone :( - # Item Macro Helpers HasSword; Helper;; (|Items.SmithSword, Items.GreenSword, Items.ReadSword, Items.BlueSword, Items.FourSword) -HasBottle; Helper;; (|Items.Bottle1.FF, Items.Bottle2.FF, Items.Bottle3.FF, Items.Bottle4.FF) +HasBottle; Helper;; (|Items.Bottle1, Items.Bottle2, Items.Bottle3, Items.Bottle4) -# Misc Locations +# Central Hyrule Locations SmithHouse; Minor; 22-11-00 -IntroItem1; Normal; F252B;; Items.SmithSword -IntroItem2; Normal; F253B;; Items.Shield +IntroItem1; Major; F252B;; Items.SmithSword +IntroItem2; Major; F253B;; Items.Shield SouthKeeseCave; Minor; 32-13-00; Items.BombBag -BelariBombs; Normal; 00A00C -JabberNut; JabberNonsense; 09498C -#BottleScrub; Normal; 0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword #, Items.SpinAttack +HyruleWellRight; Minor; 41-00-04 + +BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword #, Items.SpinAttack + +# Minish Woods Locations +AccessMinishWoods; Helper;; (|Helpers.HasSword, Items.BombBag) +JabberNut; JabberNonsense; 09498C; Locations.AccessMinishWoods +BelariBombs; Major; 00A00C; Locations.AccessMinishWoods # Mount Crenel Locations -AccessCrenel;Helper;; Helpers.HasBottle, (|Items.BombBag, Items.GripRing.FF) -#CrenelLowerScrub; Minor; 0CC09C; Helpers.HasBottle, Items.Shield # Technically a Crenel item, but doesn't need bombs +AccessCrenel; Helper;; Helpers.HasBottle, (|Items.BombBag, Items.GripRing) +CrenelLowerScrub; PurchaseItem; 0CC09C; Helpers.HasBottle, Items.Shield # Technically a Crenel item, but doesn't need bombs CrenelVineHole; Minor; 35-00-00; Locations.AccessCrenel, (|Items.GustJar, Items.BombBag) CrenelMinishHouse; Minor; 27-03-00; Locations.AccessCrenel, (|Items.GustJar, Items.BombBag) CrenelCaveDownstairs; Minor; 26-07-00; Locations.AccessCrenel, Items.BombBag CrenelHeartCaveLeft; Minor; 26-08-00; Locations.AccessCrenel, Items.BombBag CrenelHeartCaveRight; Minor; 26-08-01; Locations.AccessCrenel, Items.BombBag -#CrenelGripScrub; Normal; 0CC0A8; Locations.AccessCrenel, Items.Shield, Items.BombBag -CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing.FF, (|Items.GustJar, Items.LightArrow))) +CrenelGripScrub; PurchaseItem; 0CC0A8; Locations.AccessCrenel, Items.Shield, Items.BombBag +CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing, (|Items.GustJar, Items.LightArrow))) # Deepwood locations -DeepwoodAccess;Helper;; Items.JabberNut -DeepwoodWiggler; Normal; 48-00-01; Locations.DeepwoodAccess, Helpers.HasSword, (|Items.GustJar, Items.LanternOff) # Need gust jar or lantern to leave room -DeepwoodButtonShells1; Minor; 48-01-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) -DeepwoodButtonShells2; Minor; 48-01-02; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) -DeepwoodPreCompassShells; Minor; 48-02-01; Locations.DeepwoodAccess,(|Items.GustJar, Items.BombBag) -DeepwoodBarrelShells; Minor; 48-06-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) -DeepwoodUpstairsChest; Minor; 48-17-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.LanternOff) -CompleteDeepwood; Helper;; Locations.DeepwoodAccess, Items.GustJar, Locations.HasSword +DeepwoodAccess:Deepwood; Helper;; Locations.AccessMinishWoods, Items.JabberNut +DeepwoodWiggler:Deepwood; Major; 48-00-01; Locations.DeepwoodAccess, Helpers.HasSword, (|Items.GustJar, Items.LanternOff) # Need gust jar or lantern to leave room +DeepwoodButtonShells1:Deepwood; Minor; 48-01-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) +DeepwoodButtonShells2:Deepwood; Minor; 48-01-02; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) +DeepwoodPreCompassShells:Deepwood; Minor; 48-02-01; Locations.DeepwoodAccess,(|Items.GustJar, Items.BombBag) +DeepwoodBarrelShells:Deepwood; Minor; 48-06-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) +DeepwoodUpstairsChest:Deepwood; Minor; 48-17-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.LanternOff) +CompleteDeepwood:Deepwood; Helper;; Locations.DeepwoodAccess, Items.GustJar, Locations.HasSword # Cave of Flames locations -CoFAccess;Helper;; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing.FF, (|Items.GustJar, Items.LightArrow))) +CoFAccess:FlameCave; Helper;; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing, (|Items.GustJar, Items.LightArrow))) diff --git a/UI/MainWindow.cs b/UI/MainWindow.cs index c9437133..83b0d670 100644 --- a/UI/MainWindow.cs +++ b/UI/MainWindow.cs @@ -16,6 +16,7 @@ namespace MinishRandomizer public partial class MainWindow : Form { private ROM ROM_; + private Shuffler shuffler; public MainWindow() { @@ -35,24 +36,9 @@ private void Randomize_Click(object sender, EventArgs e) } - Shuffler shuffler = new Shuffler(Path.GetDirectoryName(ROM.Instance.path)); - - if (customLogicCheckBox.Checked) - { - shuffler.LoadLocations(customLogicPath.Text); - } - else - { - shuffler.LoadLocations(null); - } - - if (customPatchCheckBox.Checked) + if (shuffler == null) { - shuffler.PatchRom(customPatchPath.Text); - } - else - { - shuffler.PatchRom(null); + return; } shuffler.RandomizeLocations(); @@ -87,6 +73,26 @@ private void LoadRom() statusText.Text = "Unable to determine ROM."; return; } + + shuffler = new Shuffler(Path.GetDirectoryName(ROM.Instance.path)); + + if (customLogicCheckBox.Checked) + { + shuffler.LoadLocations(customLogicPath.Text); + } + else + { + shuffler.LoadLocations(null); + } + + if (customPatchCheckBox.Checked) + { + shuffler.PatchRom(customPatchPath.Text); + } + else + { + shuffler.PatchRom(null); + } } private void CustomLogicCheckBox_CheckedChanged(object sender, EventArgs e) From 2de30c50cb7422ac72448c7fd136e5fd3fdf1894 Mon Sep 17 00:00:00 2001 From: LeonarthCG Date: Mon, 1 Jul 2019 16:55:54 +0200 Subject: [PATCH 12/41] Update randoPatch.ups --- Resources/randoPatch.ups | Bin 679 -> 1439 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/randoPatch.ups b/Resources/randoPatch.ups index b0e4b5ccbaf2efd87d6a6f4c723bb5122670dc0e..dabf450ed24e028106755648226d9716c9b6cbeb 100644 GIT binary patch literal 1439 zcmd6l{ZCtE6vxj{C<#jIGNdN5#%Nq)MsIM*7^15bol?Ry!%eVGVlWclVlr9y!3Y=7 z-n|$xF0mOHochBOmL)6}y1H)FxX^(w8?#HIVZ)Iaeb6@BvS0%9DIIO>TA6BDg%9lf+fd$5 z@9Qe}#R+tN%AFhcv>R?;hv&ZbjlzLZ`gGSDQxgf;^2y5N#T)Rzv8^!!g@d=M<&xr` zUVdd4JhQBad++IwQG;#D=syfk)eKyAK=1JL@Zp;SpTDE?H5LVo@bGCJ-DE4?--A%c z?>rN`2@P!@FL_SA$%-EWU=kDsJ&_$KapQ@r1BBNJjN%d<^5wnQq zM8}%+zXh!o_~v-{-*I0L1_;-2o^s@pMlU$*JsCCy)UY|E!-??4-o6lNmYGD@T*@A! z#C^X=;xjt3uz+ZrMftmx47Rf_-#Kd38V`pYvB{(ZuVuN9&ZoKf?;1T(lcrTa;plcm zg(WWrkfR}G=*q-vzNDn-ku<4fc8uk3uSRuxO2b#TuJZ)9&c_S5p@2IWD>Zr9yWbS> zxkr&4y^3;(>4zCB^(%$<6)@YbEv;1whlJSmjCWMfhElUx)2l+#k0z~!I+xBO-h4?S zsij&014%CBM1R^OI{)fP1po1(pPI7l8O6+QZ49#;XOe_YCzazMT2F>n)pKKDmHR3b z&vSxHxN@DsfXJm>=#UJAd1^?xfY;~RhZeK@R9%{sJ&`8Etor(m5@}_o*((v-w2owA zOX@c|Sbvq39-qkXeK_c(1O~G%D$roWLx*T+h#jMiwJK`Ox^O->44D z7+hXUFa+DRoS)86H_L;eI<09!w!m>`rt2CEsq5@Eez?ORFE#t~e}>q$!~YqC-&|1U z=U|X-5MXHVWoTq!Xg|S#V4q~*Y2BmF&&I)!AyVHu*^$wtUi*x|ck`?N|9=1f|GzSj zefG-#zyE-2B_Mkbl&uJ4?*y`c0mWs2?0ryiX&^iK^8dfwKyhTS3J6xOnltCmABM)e zuYUhu{{Q&@fBS)guU75<|Nl4;1Hsn+AbPS1lknu Date: Mon, 1 Jul 2019 13:31:34 -0600 Subject: [PATCH 13/41] Add dungeon items to logic; somewhat buggy :( --- Randomizer/Dependency.cs | 50 ++++++++++++++++++++------ Randomizer/Location.cs | 78 ++++++++++++++++++++++++++++++---------- Randomizer/Shuffler.cs | 72 ++++++++++++++++++++++++++++--------- Resources/default.logic | 22 +++++++----- 4 files changed, 168 insertions(+), 54 deletions(-) diff --git a/Randomizer/Dependency.cs b/Randomizer/Dependency.cs index 4c260684..cf695a6a 100644 --- a/Randomizer/Dependency.cs +++ b/Randomizer/Dependency.cs @@ -1,11 +1,8 @@ using MinishRandomizer.Core; using System; using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Text.RegularExpressions; -using System.Threading.Tasks; using System.Globalization; +using System.Text.RegularExpressions; namespace MinishRandomizer.Randomizer { @@ -30,10 +27,10 @@ public static List GetDependencies(string logic) continue; } - switch(sequence[0]) { // If the first character of the string is & or |, it's a compound dependency + // These are handled recursively, with the first character chopped off case '&': AndDependency andDependency = new AndDependency(sequence.Substring(1)); dependencies.Add(andDependency); @@ -43,7 +40,33 @@ public static List GetDependencies(string logic) dependencies.Add(orDependency); break; default: - string[] dependencyParts = sequence.Split('.'); + string[] splitSequence = sequence.Split(':'); + string requirement = splitSequence[0]; + Console.WriteLine($"It's {requirement}"); + string dungeon = ""; + int count = 1; + + if (splitSequence.Length >= 2) + { + dungeon = splitSequence[1]; + if (splitSequence.Length >= 3) + { + if (!int.TryParse(splitSequence[2], out count)) + { + count = 1; + } + } + } + + string[] dependencyParts = requirement.Split('.'); + + Console.WriteLine($"It's also {dependencyParts[1]}"); + + if (dependencyParts.Length < 2) + { + break; + } + switch (dependencyParts[0]) { case "Locations": @@ -66,7 +89,7 @@ public static List GetDependencies(string logic) } } - ItemDependency itemDependency = new ItemDependency(new Item(type, subType)); + ItemDependency itemDependency = new ItemDependency(new Item(type, subType, dungeon), count); dependencies.Add(itemDependency); } break; @@ -88,18 +111,25 @@ public virtual bool DependencyFulfilled(List availableItems, List availableItems, List locations) { + int counter = 0; foreach (Item item in availableItems) { - if (item.Type == RequiredItem.Type && item.SubValue == RequiredItem.SubValue) + if (item.Type == RequiredItem.Type && item.SubValue == RequiredItem.SubValue && (RequiredItem.Dungeon == "" || RequiredItem.Dungeon == item.Dungeon)) { - return true; + counter++; + if (counter >= Count) + { + return true; + } } } diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs index c037eee7..410ba229 100644 --- a/Randomizer/Location.cs +++ b/Randomizer/Location.cs @@ -25,11 +25,6 @@ public static Location GetLocation(string locationText) string[] names = locationParts[0].Split(':'); string name = names[0]; - string dungeon = ""; - if (names.Length >= 2) - { - dungeon = names[1]; - } string locationType = locationParts[1]; if (!Enum.TryParse(locationType, out LocationType type) || type == LocationType.Untyped) @@ -48,6 +43,12 @@ public static Location GetLocation(string locationText) List dependencies = Dependency.GetDependencies(logic); + string dungeon = ""; + if (names.Length >= 2) + { + dungeon = names[1]; + } + Location location = new Location(type, name, dungeon, address, dependencies); if (locationParts.Length >= 5) @@ -78,6 +79,18 @@ public static Location GetLocation(string locationText) return location; } + public static List GetItems(List locations) + { + List items = new List(); + + foreach (Location location in locations) + { + items.Add(location.Contents); + } + + return items; + } + public static int GetAddressFromString(string addressString) { // Either direct address or area-room-chest @@ -140,6 +153,7 @@ public enum LocationType public string Dungeon; public bool Filled; public Item Contents { get; private set; } + private bool? AvailableCache; private Item DefaultContents; private int Address; @@ -160,6 +174,7 @@ public Location(LocationType type, string name, string dungeon, int address, Lis } Filled = false; + AvailableCache = null; } public void WriteLocation(Writer w) @@ -180,17 +195,25 @@ public void WriteLocation(Writer w) break; case LocationType.PurchaseItem: w.SetPosition(Address); - w.WriteByte((byte)Contents.Type); + if (Contents.Type == ItemType.KinstoneX || Contents.Type == ItemType.ShellsX) + { + w.WriteByte((byte)ItemType.Shells30); + } + else + { + w.WriteByte((byte)Contents.Type); + } + w.WriteByte(0xFF); break; case LocationType.StartingItem: // Nonfunctional in new patches break; - w.SetPosition(0xEF3348 + ((byte)Contents.Type >> 2)); // Get items index in the starting item table + /*w.SetPosition(0xEF3348 + ((byte)Contents.Type >> 2)); // Get items index in the starting item table byte initialByte = ROM.Instance.reader.ReadByte(0xEF3348 + ((byte)Contents.Type >> 2)); initialByte |= (byte)(1 << ((byte)Contents.Type & 3) * 2); w.WriteByte(initialByte); - break; + break;*/ case LocationType.Major: case LocationType.Minor: default: @@ -224,8 +247,14 @@ public Item GetItemContents() } - - return new Item(type, subType, Dungeon); + if (Type == LocationType.DungeonItem) + { + return new Item(type, subType, Dungeon); + } + else + { + return new Item(type, subType); + } } public bool CanPlace(Item itemToPlace, List availableItems, List locations) @@ -235,21 +264,22 @@ public bool CanPlace(Item itemToPlace, List availableItems, List case LocationType.Helper: case LocationType.Untyped: return false; - } - - switch (itemToPlace.Type) - { - case ItemType.SmallKey: - case ItemType.BigKey: - case ItemType.DungeonMap: - case ItemType.Compass: - if (itemToPlace.Dungeon != Dungeon) + case LocationType.PurchaseItem: + if (itemToPlace.Type == ItemType.KinstoneX || itemToPlace.Type == ItemType.ShellsX) { return false; } break; } + if (itemToPlace.Dungeon != "") + { + if (itemToPlace.Dungeon != Dungeon) + { + return false; + } + } + if (Address == 0) { return false; @@ -260,6 +290,11 @@ public bool CanPlace(Item itemToPlace, List availableItems, List public bool IsAccessible(List availableItems, List locations) { + if (AvailableCache != null) + { + return (bool)AvailableCache; + } + foreach (Dependency dependency in Dependencies) { if (!dependency.DependencyFulfilled(availableItems, locations)) @@ -271,6 +306,11 @@ public bool IsAccessible(List availableItems, List locations) return true; } + public void InvalidateCache() + { + AvailableCache = null; + } + public void Fill(Item contents) { SetItem(contents); diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index 25a8ca12..6d8594a6 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -55,7 +55,8 @@ public class Shuffler private Random RNG; private List Locations; - private List StartingLocations; + //private List StartingLocations; + private List DungeonItems; private List MajorItems; private List MinorItems; private string OutputDirectory; @@ -64,6 +65,7 @@ public Shuffler(string outputDirectory) { RNG = new Random(); Locations = new List(); + DungeonItems = new List(); MajorItems = new List(); MinorItems = new List(); OutputDirectory = outputDirectory; @@ -77,6 +79,7 @@ public void SetSeed(int seed) public void LoadLocations(string locationFile) { Locations.Clear(); + DungeonItems.Clear(); MajorItems.Clear(); MinorItems.Clear(); @@ -111,7 +114,7 @@ public void LoadLocations(string locationFile) switch (newLocation.Type) { case Location.LocationType.StartingItem: - StartingLocations.Add(newLocation); + //StartingLocations.Add(newLocation); break; default: Locations.Add(newLocation); @@ -128,6 +131,10 @@ public void LoadLocations(string locationFile) Console.WriteLine(newLocation.Contents.Type.ToString()); MinorItems.Add(newLocation.Contents); break; + case Location.LocationType.DungeonItem: + Console.WriteLine($"{newLocation.Dungeon}: {newLocation.Contents.Type.ToString()}"); + DungeonItems.Add(newLocation.Contents); + break; case Location.LocationType.Major: case Location.LocationType.JabberNonsense: case Location.LocationType.StartingItem: @@ -161,20 +168,20 @@ public void PatchRom(string locationFile) public void RandomizeLocations() { - List allItems = MajorItems.Concat(MinorItems).ToList(); - - List unplacedItems = MajorItems.ToList(); + List dungeonSpecificItems = DungeonItems.ToList(); + List unfilledLocations = Locations.Where(location => !location.Filled && location.Type != Location.LocationType.Helper && location.Type != Location.LocationType.Untyped).ToList(); unfilledLocations.Shuffle(RNG); unplacedItems.Shuffle(RNG); - foreach (Item item in MajorItems) - { - Console.WriteLine($"Item: {item.Type.ToString()} Sub: {item.SubValue}"); - } + // Fill dungeon items first so there is room for them all + List dungeonLocations = FillLocations(dungeonSpecificItems, unfilledLocations, unplacedItems); - FillLocations(MajorItems.ToList(), unfilledLocations); + // Fill non-dungeon items, taking into account the previously placed dungeon items + // This seems fairly crude; other randomizers don't seem to deal with this problem? + unfilledLocations.Shuffle(RNG); + FillLocations(unplacedItems, unfilledLocations, previousLocations: dungeonLocations); unfilledLocations.Shuffle(RNG); FastFillLocations(MinorItems.ToList(), unfilledLocations); @@ -196,23 +203,46 @@ public void RandomizeLocations() File.WriteAllBytes(OutputDirectory + "/mcrando.gba", ROM.Instance.romData); } - private void FillLocations(List items, List locations) + // Based off of the RandomAssumed ALttPR filler + private List FillLocations(List items, List locations, List assumedItems = null, List previousLocations = null) { + List filledLocations = new List(); + + assumedItems = assumedItems ?? new List(); + + previousLocations = previousLocations ?? new List(); + + List usableItems = new List(items.Count + previousLocations.Count + assumedItems.Count); + usableItems.AddRange(items); + usableItems.AddRange(assumedItems); + + previousLocations = previousLocations.Where(location => location.IsAccessible(usableItems, Locations)).ToList(); + + usableItems.AddRange(Location.GetItems(previousLocations)); + for (int i = items.Count - 1; i >= 0; i--) { int itemIndex = RNG.Next(items.Count); Item item = items[itemIndex]; Console.WriteLine($"Placing: {item.Type.ToString()}"); - if (item.Type == ItemType.KinstoneX || item.Type == ItemType.ShellsX || item.Type == ItemType.Rupee20) - { - Console.WriteLine("Inconcievable!"); - } + items.RemoveAt(itemIndex); - List availableLocations = locations.Where(location => location.CanPlace(item, items, Locations)).ToList(); + + // Set up the list of usable items: items unfilled, items assumed, and items from available locations + usableItems.Clear(); + usableItems.AddRange(items); + usableItems.AddRange(assumedItems); + + previousLocations = previousLocations.Where(location => location.IsAccessible(usableItems, Locations)).ToList(); + + usableItems.AddRange(Location.GetItems(previousLocations)); + + List availableLocations = locations.Where(location => location.CanPlace(item, usableItems, Locations)).ToList(); if (availableLocations.Count <= 0) { - return; + Console.WriteLine($"Could not place {item.Type.ToString()}!"); + return null; //throw new Exception($"Could not place {item.Type.ToString()}!"); } @@ -220,8 +250,16 @@ private void FillLocations(List items, List locations) availableLocations[locationIndex].Fill(item); Console.WriteLine($"Placed {item.Type.ToString()} at {availableLocations[locationIndex].Name} with {items.Count} items remaining\n"); + locations.Remove(availableLocations[locationIndex]); + + filledLocations.Add(availableLocations[locationIndex]); + + // Location caches are no longer valid because available items have changed + Locations.ForEach(location => location.InvalidateCache()); } + + return filledLocations; } private void FastFillLocations(List items, List locations) diff --git a/Resources/default.logic b/Resources/default.logic index 8be089ef..c5b82cf0 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -23,7 +23,7 @@ IntroItem2; Major; F253B;; Items.Shield SouthKeeseCave; Minor; 32-13-00; Items.BombBag HyruleWellRight; Minor; 41-00-04 -BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword #, Items.SpinAttack +BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword#, Items.SpinAttack # Minish Woods Locations AccessMinishWoods; Helper;; (|Helpers.HasSword, Items.BombBag) @@ -31,7 +31,7 @@ JabberNut; JabberNonsense; 09498C; Locations.AccessMinishWoods BelariBombs; Major; 00A00C; Locations.AccessMinishWoods # Mount Crenel Locations -AccessCrenel; Helper;; Helpers.HasBottle, (|Items.BombBag, Items.GripRing) +AccessCrenel; Helper;; Helpers.HasBottle, (|Items.BombBag, Items.GripRing), Helpers.HasSword#, Items.SpinAttack CrenelLowerScrub; PurchaseItem; 0CC09C; Helpers.HasBottle, Items.Shield # Technically a Crenel item, but doesn't need bombs CrenelVineHole; Minor; 35-00-00; Locations.AccessCrenel, (|Items.GustJar, Items.BombBag) CrenelMinishHouse; Minor; 27-03-00; Locations.AccessCrenel, (|Items.GustJar, Items.BombBag) @@ -43,13 +43,19 @@ CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.PacciCane, (& # Deepwood locations DeepwoodAccess:Deepwood; Helper;; Locations.AccessMinishWoods, Items.JabberNut -DeepwoodWiggler:Deepwood; Major; 48-00-01; Locations.DeepwoodAccess, Helpers.HasSword, (|Items.GustJar, Items.LanternOff) # Need gust jar or lantern to leave room -DeepwoodButtonShells1:Deepwood; Minor; 48-01-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) -DeepwoodButtonShells2:Deepwood; Minor; 48-01-02; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) -DeepwoodPreCompassShells:Deepwood; Minor; 48-02-01; Locations.DeepwoodAccess,(|Items.GustJar, Items.BombBag) -DeepwoodBarrelShells:Deepwood; Minor; 48-06-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag) +DeepwoodWiggler:Deepwood; Major; 48-00-01; Locations.DeepwoodAccess, Helpers.HasSword, (|Items.GustJar, Items.LanternOff), Items.SmallKey:Deepwood:3 +DeepwoodPreWiggler1:Deepwood; Minor; 48-01-01; Locations.DeepwoodAccess, (|(&Items.GustJar, Items.SmallKey:Deepwood), (&Items.BombBag, Items.SmallKey:Deepwood:2)) +DeepwoodPreWiggler2:Deepwood; Minor; 48-01-02; Locations.DeepwoodAccess, (|(&Items.GustJar, Items.SmallKey:Deepwood), (&Items.BombBag, Items.SmallKey:Deepwood:2)) +DeepwoodPreCompass:Deepwood; Minor; 48-02-01; Locations.DeepwoodAccess, (|(&Items.GustJar, Items.SmallKey:Deepwood), (&Items.BombBag, Items.SmallKey:Deepwood:2)) +DeepwoodStatueRoom:Deepwood; DungeonItem; 48-04-01; Locations.DeepwoodAccess, Items.SmallKey:Deepwood +DeepwoodWestWing:Deepwood; DungeonItem; 48-05-01; Locations.DeepwoodAccess, Items.SmallKey:Deepwood +DeepwoodPreBarrel:Deepwood; Minor; 48-06-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag), Items.SmallKey:Deepwood +DeepwoodSlugTorches:Deepwood; DungeonItem; 48-10-01; Locations.DeepwoodAccess +DeepwoodBasementNorth:Deepwood; DungeonItem; 48-11-01; Locations.DeepwoodAccess, (|Items.GustJar, (&Items.Flippers, Helpers.HasSword)), Items.SmallKey:Deepwood:3 +DeepwoodBasementSwitch:Deepwood; DungeonItem; 48-12-01; Locations.DeepwoodAccess, (|(&Items.GustJar, Items.SmallKey:Deepwood), (&Items.SmallKey:Deepwood:2, (|Items.Flippers, Items.RocsCape))) +DeepwoodBasementEast:Deepwood; DungeonItem; 48-12-02; Locations.DeepwoodAccess, (|(&Items.GustJar, Items.SmallKey:Deepwood), Items.SmallKey:Deepwood:2) DeepwoodUpstairsChest:Deepwood; Minor; 48-17-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.LanternOff) -CompleteDeepwood:Deepwood; Helper;; Locations.DeepwoodAccess, Items.GustJar, Locations.HasSword +CompleteDeepwood:Deepwood; Helper;; Locations.DeepwoodAccess, Items.GustJar, Helpers.HasSword, Items.BigKey:Deepwood # Cave of Flames locations CoFAccess:FlameCave; Helper;; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing, (|Items.GustJar, Items.LightArrow))) From c03097508a024da1645cfb660abc0174a1f22ac4 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Mon, 1 Jul 2019 15:12:27 -0600 Subject: [PATCH 14/41] Update readme, add Cave of Flames to logic --- README.md | 14 +++++++++++++- Randomizer/Location.cs | 26 +++++++++++++++----------- Randomizer/Shuffler.cs | 14 +++----------- Resources/default.logic | 18 ++++++++++++++++-- 4 files changed, 47 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 73284918..1879d431 100644 --- a/README.md +++ b/README.md @@ -1 +1,13 @@ -# rando \ No newline at end of file +# Minish Randomizer + +[![Discord](https://discordapp.com/api/guilds/342341497024151553/embed.png?style=shield)](https://discord.gg/ndFuWbV) +[![Twitter Follow](https://img.shields.io/badge/follow-%40minishmaker-blue.svg?style=flat&logo=twitter)](https://twitter.com/minishmaker) + +This program aims to randomize the item locations in such a way that all items are be available. This works sometimes, but currently is rather bugged. + +It's currently in early development; it likely will silently fail randomization and be broken until you reload the ROM. +When it's finished to the point that it can randomize the entire game without failing often, it'll be set up on a website similar to other popular randomizers. + +Logic only goes up to the Cave of Flames currently; in the future it'll be extended to cover the whole game. + +Please join our [Discord server](https://discord.gg/ndFuWbV) to discuss the project! diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs index 410ba229..75a48c24 100644 --- a/Randomizer/Location.cs +++ b/Randomizer/Location.cs @@ -143,8 +143,9 @@ public enum LocationType HeartPieceItem, JabberNonsense, Helper, - StartingItem, - PurchaseItem + //StartingItem, + PurchaseItem, + ScrollItem } public List Dependencies; @@ -206,18 +207,20 @@ public void WriteLocation(Writer w) w.WriteByte(0xFF); break; - case LocationType.StartingItem: - // Nonfunctional in new patches + case LocationType.ScrollItem: + w.SetPosition(Address); + if (Contents.Type == ItemType.KinstoneX || Contents.Type == ItemType.ShellsX) + { + w.WriteByte((byte)ItemType.Shells30); + } + else + { + w.WriteByte((byte)((byte)Contents.Type & 0x7F)); + } break; - /*w.SetPosition(0xEF3348 + ((byte)Contents.Type >> 2)); // Get items index in the starting item table - byte initialByte = ROM.Instance.reader.ReadByte(0xEF3348 + ((byte)Contents.Type >> 2)); - initialByte |= (byte)(1 << ((byte)Contents.Type & 3) * 2); - w.WriteByte(initialByte); - break;*/ case LocationType.Major: case LocationType.Minor: default: - w.SetPosition(Address); w.WriteByte((byte)Contents.Type); w.WriteByte(Contents.SubValue); @@ -265,7 +268,8 @@ public bool CanPlace(Item itemToPlace, List availableItems, List case LocationType.Untyped: return false; case LocationType.PurchaseItem: - if (itemToPlace.Type == ItemType.KinstoneX || itemToPlace.Type == ItemType.ShellsX) + case LocationType.ScrollItem: + if (itemToPlace.SubValue != 0) { return false; } diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index 6d8594a6..2e063fb6 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -110,16 +110,7 @@ public void LoadLocations(string locationFile) } Location newLocation = Location.GetLocation(locationString); - - switch (newLocation.Type) - { - case Location.LocationType.StartingItem: - //StartingLocations.Add(newLocation); - break; - default: - Locations.Add(newLocation); - break; - } + Locations.Add(newLocation); switch(newLocation.Type) { @@ -137,7 +128,8 @@ public void LoadLocations(string locationFile) break; case Location.LocationType.Major: case Location.LocationType.JabberNonsense: - case Location.LocationType.StartingItem: + case Location.LocationType.PurchaseItem: + case Location.LocationType.ScrollItem: default: Console.WriteLine($"Hey! {newLocation.Contents.Type.ToString()}"); MajorItems.Add(newLocation.Contents); diff --git a/Resources/default.logic b/Resources/default.logic index c5b82cf0..2ebed8fd 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -22,6 +22,7 @@ IntroItem1; Major; F252B;; Items.SmithSword IntroItem2; Major; F253B;; Items.Shield SouthKeeseCave; Minor; 32-13-00; Items.BombBag HyruleWellRight; Minor; 41-00-04 +SwiftbladeFirst; ScrollItem; 110D7B; BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword#, Items.SpinAttack @@ -58,5 +59,18 @@ DeepwoodUpstairsChest:Deepwood; Minor; 48-17-01; Locations.DeepwoodAccess, (|Ite CompleteDeepwood:Deepwood; Helper;; Locations.DeepwoodAccess, Items.GustJar, Helpers.HasSword, Items.BigKey:Deepwood # Cave of Flames locations -CoFAccess:FlameCave; Helper;; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing, (|Items.GustJar, Items.LightArrow))) - +CoFAccess:FlameCave; Helper;; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing, (|Items.GustJar, Items.LightArrow))), (|Items.BombBag, Items.Shield, Items.PacciCane) +CoFFlippedCart:FlameCave; DungeonItem; 50-01-01; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave +CoFChuPit:FlameCave; Major; 50-01-02; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave +CoFSouthwest1:FlameCave; DungeonItem; 50-08-00; Locations.CoFAccess +CoFSouthwest2:FlameCave; Minor; 50-08-01; Locations.CoFAccess +CoFSoutheast1:FlameCave; Minor; 50-09-00; Locations.CoFAccess +CoFSoutheast2:FlameCave; DungeonItem; 50-09-01; Locations.CoFAccess +CoFBasement1:FlameCave; Minor; 50-10-00; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave:2 +CoFBasement2:FlameCave; Minor; 50-10-01; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave:2 +CoFBlades:FlameCave; Minor; 50-14-00; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave:2 +CoFSpinies:FlameCave; DungeonItem; 50-15-00; Locations.CoFAccess +CoFBasementLava1:FlameCave; Minor; 50-17-00; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave:2 +CoFBasementLava2:FlameCave; Minor; 50-17-01; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave:2 +CoFBasementLavaBig:FlameCave; DungeonItem; 50-17-02; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave:2 +CompleteCoF:FlameCave; Helper;; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave:2, Items.BigKey:FlameCave \ No newline at end of file From 299635c49cb2a5410f996500b3b0d011e95ef67e Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Mon, 1 Jul 2019 19:20:02 -0600 Subject: [PATCH 15/41] Fix shuffling, logic bugs, remove messy debug prints --- README.md | 2 +- Randomizer/Dependency.cs | 8 ++------ Randomizer/Location.cs | 18 ++++++++++++++---- Randomizer/Shuffler.cs | 28 +++++++++++++++++++++++----- Resources/default.logic | 24 ++++++++++++++++-------- UI/MainWindow.cs | 9 ++++++++- 6 files changed, 64 insertions(+), 25 deletions(-) diff --git a/README.md b/README.md index 1879d431..d76afe82 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ This program aims to randomize the item locations in such a way that all items are be available. This works sometimes, but currently is rather bugged. -It's currently in early development; it likely will silently fail randomization and be broken until you reload the ROM. +It's currently in early development; it likely will fail randomization and be broken until you reload the ROM. When it's finished to the point that it can randomize the entire game without failing often, it'll be set up on a website similar to other popular randomizers. Logic only goes up to the Cave of Flames currently; in the future it'll be extended to cover the whole game. diff --git a/Randomizer/Dependency.cs b/Randomizer/Dependency.cs index cf695a6a..bd916cb0 100644 --- a/Randomizer/Dependency.cs +++ b/Randomizer/Dependency.cs @@ -42,7 +42,6 @@ public static List GetDependencies(string logic) default: string[] splitSequence = sequence.Split(':'); string requirement = splitSequence[0]; - Console.WriteLine($"It's {requirement}"); string dungeon = ""; int count = 1; @@ -60,8 +59,6 @@ public static List GetDependencies(string logic) string[] dependencyParts = requirement.Split('.'); - Console.WriteLine($"It's also {dependencyParts[1]}"); - if (dependencyParts.Length < 2) { break; @@ -123,6 +120,7 @@ public override bool DependencyFulfilled(List availableItems, List availableItems, List availableItems, List unplacedItems = MajorItems.ToList(); List dungeonSpecificItems = DungeonItems.ToList(); @@ -180,7 +186,7 @@ public void RandomizeLocations() if (unfilledLocations.Count != 0) { - Console.WriteLine("Not all locations filled!"); + throw new ShuffleException($"There are {unfilledLocations.Count} unfilled locations!"); } using (MemoryStream ms = new MemoryStream(ROM.Instance.romData)) @@ -195,6 +201,16 @@ public void RandomizeLocations() File.WriteAllBytes(OutputDirectory + "/mcrando.gba", ROM.Instance.romData); } + private void ResetLocations() + { + foreach (Location location in Locations) + { + location.SetDefaultContents(); + location.InvalidateCache(); + location.Filled = false; + } + } + // Based off of the RandomAssumed ALttPR filler private List FillLocations(List items, List locations, List assumedItems = null, List previousLocations = null) { @@ -217,6 +233,10 @@ private List FillLocations(List items, List locations, int itemIndex = RNG.Next(items.Count); Item item = items[itemIndex]; Console.WriteLine($"Placing: {item.Type.ToString()}"); + if (item.Dungeon != "") + { + Console.WriteLine($"Dungeon: {item.Dungeon}"); + } items.RemoveAt(itemIndex); @@ -233,9 +253,7 @@ private List FillLocations(List items, List locations, if (availableLocations.Count <= 0) { - Console.WriteLine($"Could not place {item.Type.ToString()}!"); - return null; - //throw new Exception($"Could not place {item.Type.ToString()}!"); + throw new ShuffleException($"Could not place {item.Type}"); } int locationIndex = RNG.Next(availableLocations.Count); diff --git a/Resources/default.logic b/Resources/default.logic index 2ebed8fd..351f13ac 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -22,18 +22,18 @@ IntroItem1; Major; F252B;; Items.SmithSword IntroItem2; Major; F253B;; Items.Shield SouthKeeseCave; Minor; 32-13-00; Items.BombBag HyruleWellRight; Minor; 41-00-04 -SwiftbladeFirst; ScrollItem; 110D7B; +SwiftbladeFirst; ScrollItem; 110D7B; Helpers.HasSword -BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword#, Items.SpinAttack +BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword, Items.SpinAttack # Minish Woods Locations AccessMinishWoods; Helper;; (|Helpers.HasSword, Items.BombBag) -JabberNut; JabberNonsense; 09498C; Locations.AccessMinishWoods +JabberNut; Split; 09498C; Locations.AccessMinishWoods BelariBombs; Major; 00A00C; Locations.AccessMinishWoods # Mount Crenel Locations -AccessCrenel; Helper;; Helpers.HasBottle, (|Items.BombBag, Items.GripRing), Helpers.HasSword#, Items.SpinAttack -CrenelLowerScrub; PurchaseItem; 0CC09C; Helpers.HasBottle, Items.Shield # Technically a Crenel item, but doesn't need bombs +AccessCrenel; Helper;; Helpers.HasBottle, (|Items.BombBag, Items.GripRing), Helpers.HasSword, Items.SpinAttack +#CrenelLowerScrub; PurchaseItem; 0CC09C; Helpers.HasBottle, Items.Shield # Technically a Crenel item, but doesn't need bombs CrenelVineHole; Minor; 35-00-00; Locations.AccessCrenel, (|Items.GustJar, Items.BombBag) CrenelMinishHouse; Minor; 27-03-00; Locations.AccessCrenel, (|Items.GustJar, Items.BombBag) CrenelCaveDownstairs; Minor; 26-07-00; Locations.AccessCrenel, Items.BombBag @@ -41,6 +41,10 @@ CrenelHeartCaveLeft; Minor; 26-08-00; Locations.AccessCrenel, Items.BombBag CrenelHeartCaveRight; Minor; 26-08-01; Locations.AccessCrenel, Items.BombBag CrenelGripScrub; PurchaseItem; 0CC0A8; Locations.AccessCrenel, Items.Shield, Items.BombBag CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing, (|Items.GustJar, Items.LightArrow))) +Melari; Major; 00D26E; Locations.CompleteCoF + +# Castor Wilds Locations +AccessWilds; Helper;; # Deepwood locations DeepwoodAccess:Deepwood; Helper;; Locations.AccessMinishWoods, Items.JabberNut @@ -52,8 +56,8 @@ DeepwoodStatueRoom:Deepwood; DungeonItem; 48-04-01; Locations.DeepwoodAccess, It DeepwoodWestWing:Deepwood; DungeonItem; 48-05-01; Locations.DeepwoodAccess, Items.SmallKey:Deepwood DeepwoodPreBarrel:Deepwood; Minor; 48-06-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.BombBag), Items.SmallKey:Deepwood DeepwoodSlugTorches:Deepwood; DungeonItem; 48-10-01; Locations.DeepwoodAccess -DeepwoodBasementNorth:Deepwood; DungeonItem; 48-11-01; Locations.DeepwoodAccess, (|Items.GustJar, (&Items.Flippers, Helpers.HasSword)), Items.SmallKey:Deepwood:3 -DeepwoodBasementSwitch:Deepwood; DungeonItem; 48-12-01; Locations.DeepwoodAccess, (|(&Items.GustJar, Items.SmallKey:Deepwood), (&Items.SmallKey:Deepwood:2, (|Items.Flippers, Items.RocsCape))) +DeepwoodBasementNorth:Deepwood; DungeonItem; 48-11-01; Locations.DeepwoodAccess, Items.GustJar, Items.SmallKey:Deepwood:3 +DeepwoodBasementSwitch:Deepwood; DungeonItem; 48-12-01; Locations.DeepwoodAccess, (|(&Items.GustJar, Items.SmallKey:Deepwood), (&Items.SmallKey:Deepwood:2, Items.RocsCape)) DeepwoodBasementEast:Deepwood; DungeonItem; 48-12-02; Locations.DeepwoodAccess, (|(&Items.GustJar, Items.SmallKey:Deepwood), Items.SmallKey:Deepwood:2) DeepwoodUpstairsChest:Deepwood; Minor; 48-17-01; Locations.DeepwoodAccess, (|Items.GustJar, Items.LanternOff) CompleteDeepwood:Deepwood; Helper;; Locations.DeepwoodAccess, Items.GustJar, Helpers.HasSword, Items.BigKey:Deepwood @@ -73,4 +77,8 @@ CoFSpinies:FlameCave; DungeonItem; 50-15-00; Locations.CoFAccess CoFBasementLava1:FlameCave; Minor; 50-17-00; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave:2 CoFBasementLava2:FlameCave; Minor; 50-17-01; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave:2 CoFBasementLavaBig:FlameCave; DungeonItem; 50-17-02; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave:2 -CompleteCoF:FlameCave; Helper;; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave:2, Items.BigKey:FlameCave \ No newline at end of file +CompleteCoF:FlameCave; Helper;; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave:2, Items.BigKey:FlameCave + +# Fortress of Winds Locations +#FortressAccess:Fortress; Helper;; Locations.AccessWilds +#FortressPrize:Prizes; Major; 09C9E6 \ No newline at end of file diff --git a/UI/MainWindow.cs b/UI/MainWindow.cs index 83b0d670..d495f135 100644 --- a/UI/MainWindow.cs +++ b/UI/MainWindow.cs @@ -41,7 +41,14 @@ private void Randomize_Click(object sender, EventArgs e) return; } - shuffler.RandomizeLocations(); + try + { + shuffler.RandomizeLocations(); + } + catch (ShuffleException error) + { + MessageBox.Show(error.Message); + } } private void LoadRom() From 2047c805cef4e95b733259f6ce4cac76030c0bff Mon Sep 17 00:00:00 2001 From: LeonarthCG Date: Wed, 3 Jul 2019 17:29:53 +0200 Subject: [PATCH 16/41] fixed graveyard dampe item spot ($96B4) looks wonky but works, like melari's --- Resources/randoPatch.ups | Bin 1439 -> 1678 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/randoPatch.ups b/Resources/randoPatch.ups index dabf450ed24e028106755648226d9716c9b6cbeb..487eccb8fc493af4df7f2b1d8ae0490501adb1af 100644 GIT binary patch delta 448 zcmbQw-Nzdj8W3#AP+!*u!dg0wHVk5_Egkb1{29AG{F8~6^n0Mk;J!%UpGEr1{%%P;wFRp{NwW&ist=uNZ&Kj z&pe{^r|bm==A6a}*#gI%nXYRvD6bXxClbvXTmGNHvcYcShdT^fv;PU|@7$Z#z`$?Q z@_f&NU5@haZZLRTG(6lBwnI|j^$mvPV-1;JP7_Mr|D}Kb{!g&RpIe^q__-ayw%;js`TyV5mOs7=Isg9tyZqC4_R!zo&pG}6 z{@-ib{Vx#9u7A?McmErd{YC%q{U2a9$n3w@|LFbN4s#;6)sOEWv%;-^h<#_j^#N=c S!=$3*7h*dDt|)|BEd~IEb@JZ; delta 193 zcmV;y06zbY4WA26RZvqg0Dpdl0DpdlLQ;l^o&Zk)itzp%ThUG6E&xBP5dK4F?Z!>< z0CR?bY5)#_{t;&m$Mg39V~j7mu)J9x=-B{ApU0j6a-RM`XJnCHHv@ZSf{~pYD?wO< z=e(f0`5x}r082Q4;k;J3bsy{40At30X-YyUbna{bXI6kgQ_w#Zk?k!4)bW!X0p|g~ vlTiW|3h)2_|Nq$0l5%6{_E?)ZU$-cmp2t~D From 0d4ad6841baa2fdc60208ee0367969a3b8cfdae8 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Wed, 3 Jul 2019 15:56:32 -0600 Subject: [PATCH 17/41] Fix shuffler - seed generation should have a much higher success rate --- Randomizer/Location.cs | 13 +++++++++++ Randomizer/Shuffler.cs | 50 ++++++++++++++++++------------------------ 2 files changed, 34 insertions(+), 29 deletions(-) diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs index 75e2792d..f4e95c3d 100644 --- a/Randomizer/Location.cs +++ b/Randomizer/Location.cs @@ -91,6 +91,19 @@ public static List GetItems(List locations) return items; } + /*public static List GetAvailableItems(List locations, List preItems) + { + List items = new List(); + + List unsearchedLocations = locations.ToList(); + + do + { + + } + while (); + }*/ + public static int GetAddressFromString(string addressString) { // Either direct address or area-room-chest diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index ea6f404d..27945365 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -179,7 +179,7 @@ public void RandomizeLocations() // Fill non-dungeon items, taking into account the previously placed dungeon items // This seems fairly crude; other randomizers don't seem to deal with this problem? unfilledLocations.Shuffle(RNG); - FillLocations(unplacedItems, unfilledLocations, previousLocations: dungeonLocations); + FillLocations(unplacedItems, unfilledLocations); unfilledLocations.Shuffle(RNG); FastFillLocations(MinorItems.ToList(), unfilledLocations); @@ -212,22 +212,12 @@ private void ResetLocations() } // Based off of the RandomAssumed ALttPR filler - private List FillLocations(List items, List locations, List assumedItems = null, List previousLocations = null) + private List FillLocations(List items, List locations, List assumedItems = null) { List filledLocations = new List(); assumedItems = assumedItems ?? new List(); - previousLocations = previousLocations ?? new List(); - - List usableItems = new List(items.Count + previousLocations.Count + assumedItems.Count); - usableItems.AddRange(items); - usableItems.AddRange(assumedItems); - - previousLocations = previousLocations.Where(location => location.IsAccessible(usableItems, Locations)).ToList(); - - usableItems.AddRange(Location.GetItems(previousLocations)); - for (int i = items.Count - 1; i >= 0; i--) { int itemIndex = RNG.Next(items.Count); @@ -240,16 +230,9 @@ private List FillLocations(List items, List locations, items.RemoveAt(itemIndex); - // Set up the list of usable items: items unfilled, items assumed, and items from available locations - usableItems.Clear(); - usableItems.AddRange(items); - usableItems.AddRange(assumedItems); - - previousLocations = previousLocations.Where(location => location.IsAccessible(usableItems, Locations)).ToList(); - - usableItems.AddRange(Location.GetItems(previousLocations)); + List availableItems = GetAvailableItems(items.Concat(assumedItems).ToList()); - List availableLocations = locations.Where(location => location.CanPlace(item, usableItems, Locations)).ToList(); + List availableLocations = locations.Where(location => location.CanPlace(item, availableItems, Locations)).ToList(); if (availableLocations.Count <= 0) { @@ -281,17 +264,26 @@ private void FastFillLocations(List items, List locations) } } - /*private void ApplyPatch(string outputPath) + // Gets all the available items with a given item set, looping until there are no more items left to get + private List GetAvailableItems(List preAvailableItems) { - string directory = Directory.GetCurrentDirectory(); + List availableItems = preAvailableItems.ToList(); - using (Process process = new Process()) + List filledLocations = Locations.Where(location => location.Filled && location.Type != Location.LocationType.Helper && location.Type != Location.LocationType.Untyped).ToList(); + + int previousSize; + do { - process.StartInfo.FileName = "ups.exe"; - process.StartInfo.Arguments = $"patch -b {ROM.Instance.path} -p {directory + "/randopatch.ups"} -o {outputPath}"; - process.Start(); - process.WaitForExit(); + List accessibleLocations = filledLocations.Where(location => location.IsAccessible(availableItems, Locations)).ToList(); + previousSize = accessibleLocations.Count; + + filledLocations.RemoveAll(location => accessibleLocations.Contains(location)); + + availableItems.AddRange(Location.GetItems(accessibleLocations)); } - }*/ + while (previousSize > 0); + + return availableItems; + } } } From 53ca4675967b54d1cf0daff9fb1b9b67c1accef4 Mon Sep 17 00:00:00 2001 From: LeonarthCG Date: Thu, 4 Jul 2019 01:32:45 +0200 Subject: [PATCH 18/41] small fixes fixed simulation heart piece, mushroom buying, eenie fusion, castle garden guards also made the player start with the bag and the first fusion completed (and counted for in the fusion counter) --- Resources/randoPatch.ups | Bin 1678 -> 1850 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/randoPatch.ups b/Resources/randoPatch.ups index 487eccb8fc493af4df7f2b1d8ae0490501adb1af..3d789abbc389bbf400622f6fe0f1748ef19a88f3 100644 GIT binary patch delta 545 zcmeC<-NjcQ8W3#AP+!*u!c6Lo4h(87HVk5_Egkb1{29AG{F8|e^n0Mk;J!%UpGA7& ziRdp3`RxM#oYU7H_x;FF+}4oAz}NUsF#X)|&)*r6d-Qj2*drXD-YYQJPMz2%XH^?p{+}UZ&Obr@#C>TE z4E#1N&-X0YwNw7x4F+$EhKGB?c1#p_eS;zSSVN|l(~FXKISlDx4UWMV?59qAtIIfL zvNU65z3>@<@8(zk|NZ{||9@^E`|OqffBym5TtN07D4P?=-U(#?0*bQ$+54d43=Kd| z^5y@3xq%YQKz0R?4bsd6WN(2gVg#~RK-oYkIJo=j_y6VpC%R#*Eu1 z>o6T)VrZEBhRIIx^eKVw4<7vgAAJ$%xJ!S*^y0Ta|3?Dt_;KjxfA7iG%>Ingle?Ms zGtQpu&5|kht?A#j|N8%({yi>l{eS)cmghTmdHno7c@s+{)8P}7d0AC7er^AI@7I>Z wo&VYYU;m@`d;4z)+X-Zcyw<<(+SwnzvxolvzS)p9l#ye{;-@-i_I`a00HrfGw*UYD delta 388 zcmdnR*T-8Q8W3#AP+!*u!dg0wHVk5_Egkb1{29AG{F8~6^n0Mk;J!%UpGEr1{%%P;wFRp{NwW&ist=uNZ)g8 z>lX&~*07+hXUFic#>&T&lRN+*M@VbjFpa*nLA z<^LHh8|*fIxWk||`=6lx&b?_34E#1N&-X0Y Date: Thu, 4 Jul 2019 16:50:23 +0200 Subject: [PATCH 19/41] fixed wonky melari/dampe and limited splits in some rooms melari and dampe no longer make you do the item get animation twice graveyard, crypt and temple of droplets now limit your fusions to 3, 3 and 2 respectively, so that they don't become impossible with better swords --- Resources/randoPatch.ups | Bin 1850 -> 2066 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/Resources/randoPatch.ups b/Resources/randoPatch.ups index 3d789abbc389bbf400622f6fe0f1748ef19a88f3..8b6358d3adea3d13a09e46bab57f2305ec183a71 100644 GIT binary patch delta 441 zcmdnRH%Y)hG$7cJp}wvSgqhSE9T@DiS~})2_%n8W_$L$n)9-;EgZm zOxHCSj91RwBfGyq>z4yVdeg**QYy6q?`|-7TQoe}6SgB%@bwLbS)NgsDd7KRKSmWs#)F%SnSz-TKW%wB{r8#wSAXc&?q$>axg~MufA(Kn z-|zn`{|C(e_xR8DKU<#fFrD`A`+w&D{}~$p|F=^6@jdvY!1rF=|KFQ|xE_ehfw&Zi z^MN=z^Z)m7APxp%|H=DV;-i0ff86rCZCTszm%vc7}$l|MW}u{??Ak6>e|DXN)|Nl`s_~W|_5Z9j&`2Owx-|4^q f&Sz-&H(8psgz5kB%@bJ{GIA_P*x#*fY!D9s=ced> delta 255 zcmVLh@JpX0gCYc99vON;4T11 zq7eQ#XJN=&^Z;*%5dK4Ft;bFA0CR?bY5)#_{t;)+$Mg39V~j7mu)GJcQ@Gpt`vq?$`iJIDp~2R=AN6>(~He#(-%`Lg;kv zYyf9gfI(BxKb4a)0WAWQ@Uvh6Bmn{cvxNdv0+T=mSd;bzaRS50vuOsQ0SLIG Date: Fri, 5 Jul 2019 20:21:23 -0600 Subject: [PATCH 20/41] Add most Castor Wilds locations --- Resources/default.logic | 29 +++++++++++++++++++++++------ 1 file changed, 23 insertions(+), 6 deletions(-) diff --git a/Resources/default.logic b/Resources/default.logic index 351f13ac..0e9f552f 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -13,9 +13,14 @@ # Item Override should be in the form of Items.(itemName)[.subValue] # Item Macro Helpers -HasSword; Helper;; (|Items.SmithSword, Items.GreenSword, Items.ReadSword, Items.BlueSword, Items.FourSword) +HasSword; Helper;; (|Items.SmithSword, Items.GreenSword, Items.RedSword, Items.BlueSword, Items.FourSword) +CanSplit2; Helper;; (|Items.RedSword, Items.BlueSword, Items.FourSword) +CanSplit3; Helper;; (|Items.BlueSword, Items.FourSword) + HasBottle; Helper;; (|Items.Bottle1, Items.Bottle2, Items.Bottle3, Items.Bottle4) +HasBow; Helper;; (|Items.Bow, Items.LightArrow) + # Central Hyrule Locations SmithHouse; Minor; 22-11-00 IntroItem1; Major; F252B;; Items.SmithSword @@ -23,16 +28,17 @@ IntroItem2; Major; F253B;; Items.Shield SouthKeeseCave; Minor; 32-13-00; Items.BombBag HyruleWellRight; Minor; 41-00-04 SwiftbladeFirst; ScrollItem; 110D7B; Helpers.HasSword +CafeLady; Minor; 00EDDA;; Items.KinstoneX.RedSpike -BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, Helpers.HasSword, Items.SpinAttack +BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, (|(&Helpers.HasSword, Items.SpinAttack), Items.Flippers) # Minish Woods Locations -AccessMinishWoods; Helper;; (|Helpers.HasSword, Items.BombBag) +AccessMinishWoods; Helper;; (|Helpers.HasSword, Items.BombBag, Items.LightArrow) JabberNut; Split; 09498C; Locations.AccessMinishWoods BelariBombs; Major; 00A00C; Locations.AccessMinishWoods # Mount Crenel Locations -AccessCrenel; Helper;; Helpers.HasBottle, (|Items.BombBag, Items.GripRing), Helpers.HasSword, Items.SpinAttack +AccessCrenel; Helper;; Helpers.HasBottle, (|Items.BombBag, Items.GripRing), (|(&Helpers.HasSword, Items.SpinAttack), Items.Flippers) #CrenelLowerScrub; PurchaseItem; 0CC09C; Helpers.HasBottle, Items.Shield # Technically a Crenel item, but doesn't need bombs CrenelVineHole; Minor; 35-00-00; Locations.AccessCrenel, (|Items.GustJar, Items.BombBag) CrenelMinishHouse; Minor; 27-03-00; Locations.AccessCrenel, (|Items.GustJar, Items.BombBag) @@ -44,7 +50,18 @@ CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.PacciCane, (& Melari; Major; 00D26E; Locations.CompleteCoF # Castor Wilds Locations -AccessWilds; Helper;; +AccessWilds; Helper;; Helpers.CanSplit2, (|Items.SpinAttack, Items.Flippers), (|Items.PegasusBoots, Items.RocsCape) +WildsSouthCave; Major; 2A-00-00; Locations.AccessWilds, (|Items.Flippers, Items.RocsCape, Helpers.HasBow); Items.KinstoneX.YellowTotemProng +WildsDarknutCave; Major; 2B-00-00; Locations.AccessWilds, Helpers.HasSword; Items.KinstoneX.YellowTotemProng +WildsDekuCaveRight; Major; 2A-01-00; Locations.AccessWilds, Helpers.HasBow; Items.KinstoneX.YellowTotemProng +WildsMulldozerHole; Major; 27-06-00; Locations.AccessWilds, (|Items.Flippers, Items.GustJar) +WildsDiggingCave1; Minor; 17-00-00; Locations.AccessWilds, Items.MoleMitts +WildsDiggingCave2; Minor; 17-00-00; Locations.AccessWilds, Items.MoleMitts + +# Royal Valley Locations +AccessValley; Helper;; (|(&Helpers.HasSword, Items.SpinAttack), Items.Flippers), Items.PacciCane, Items.LanternOff +DampeKey; Major; 0096B4; Locations.AccessValley +KingGift; Major; 00DA5A; Locations.AccessValley, Items.GraveyardKey, Helpers.CanSplit3, Items.PegasusBoots # Deepwood locations DeepwoodAccess:Deepwood; Helper;; Locations.AccessMinishWoods, Items.JabberNut @@ -80,5 +97,5 @@ CoFBasementLavaBig:FlameCave; DungeonItem; 50-17-02; Locations.CoFAccess, Items. CompleteCoF:FlameCave; Helper;; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave:2, Items.BigKey:FlameCave # Fortress of Winds Locations -#FortressAccess:Fortress; Helper;; Locations.AccessWilds +FortressAccess:Fortress; Helper;; Locations.AccessWilds, Items.KinstoneX.YellowTotemProng::3 #FortressPrize:Prizes; Major; 09C9E6 \ No newline at end of file From 97120c97702dce6c4db64d4de85461a188ed346f Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Sat, 6 Jul 2019 19:28:26 -0600 Subject: [PATCH 21/41] Add Fortress of Winds to logic --- Resources/default.logic | 34 ++++++++++++++++++++++++++++++---- 1 file changed, 30 insertions(+), 4 deletions(-) diff --git a/Resources/default.logic b/Resources/default.logic index 0e9f552f..68ae0996 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -36,6 +36,12 @@ BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, (|(&Helpers.HasS AccessMinishWoods; Helper;; (|Helpers.HasSword, Items.BombBag, Items.LightArrow) JabberNut; Split; 09498C; Locations.AccessMinishWoods BelariBombs; Major; 00A00C; Locations.AccessMinishWoods +MinishMiddleFlipperHole; Minor; 35-09-00; Locations.AccessMinishWoods, Items.Flippers +MinishRightFlipperHole; Minor; 35-09-01; Locations.AccessMinishWoods, Items.Flippers +MinishLeftFlipperHole; Minor; 35-09-02; Locations.AccessMinishWoods, Items.Flippers +MinishWitchDiggingCaveRight; Minor; 0C-00-00; Locations.AccessMinishWoods, Items.MoleMitts, (|Items.Flippers, Items.RocsCape, (&Items.LonLonKey, Items.PacciCane)) +MinishLikeLikeDiggingCaveLeft; Minor; 0C-00-01; Locations.AccessMinishWoods, Items.MoleMitts +MinishLikeLikeDiggingCaveRight; Minor; 0C-00-02; Locations.AccessMinishWoods, Items.MoleMitts # Mount Crenel Locations AccessCrenel; Helper;; Helpers.HasBottle, (|Items.BombBag, Items.GripRing), (|(&Helpers.HasSword, Items.SpinAttack), Items.Flippers) @@ -57,14 +63,19 @@ WildsDekuCaveRight; Major; 2A-01-00; Locations.AccessWilds, Helpers.HasBow; Item WildsMulldozerHole; Major; 27-06-00; Locations.AccessWilds, (|Items.Flippers, Items.GustJar) WildsDiggingCave1; Minor; 17-00-00; Locations.AccessWilds, Items.MoleMitts WildsDiggingCave2; Minor; 17-00-00; Locations.AccessWilds, Items.MoleMitts +WildsTopChest; Minor; 04-00-00; Locations.AccessWilds, Helpers.HasBow +RuinsBombCave; Minor; 2A-02-00; Locations.AccessWilds, Items.KinstoneX.YellowTotemProng::3, Items.BombBag +RuinsMinishCave; Minor; 27-07-00; Locations.AccessWilds, Items.KinstoneX.YellowTotemProng::3, Helpers.HasSword +RuinsArmosKillLeft; Minor; 05-05-00; Locations.AccessWilds, Items.KinstoneX.YellowTotemProng::3, Helpers.HasSword +RuinsArmosKillRight; Minor; 05-05-01; Locations.AccessWilds, Items.KinstoneX.YellowTotemProng::3, Helpers.HasSword # Royal Valley Locations AccessValley; Helper;; (|(&Helpers.HasSword, Items.SpinAttack), Items.Flippers), Items.PacciCane, Items.LanternOff DampeKey; Major; 0096B4; Locations.AccessValley -KingGift; Major; 00DA5A; Locations.AccessValley, Items.GraveyardKey, Helpers.CanSplit3, Items.PegasusBoots +KingGift:Prizes; Major; 00DA5A; Locations.AccessValley, Items.GraveyardKey, Helpers.CanSplit3, Items.PegasusBoots; Items.KinstoneX.YellowCrown # Deepwood locations -DeepwoodAccess:Deepwood; Helper;; Locations.AccessMinishWoods, Items.JabberNut +DeepwoodAccess:Deepwood; Helper;; Locations.AccessMinishWoods, (|Items.JabberNut, Items.Flippers) DeepwoodWiggler:Deepwood; Major; 48-00-01; Locations.DeepwoodAccess, Helpers.HasSword, (|Items.GustJar, Items.LanternOff), Items.SmallKey:Deepwood:3 DeepwoodPreWiggler1:Deepwood; Minor; 48-01-01; Locations.DeepwoodAccess, (|(&Items.GustJar, Items.SmallKey:Deepwood), (&Items.BombBag, Items.SmallKey:Deepwood:2)) DeepwoodPreWiggler2:Deepwood; Minor; 48-01-02; Locations.DeepwoodAccess, (|(&Items.GustJar, Items.SmallKey:Deepwood), (&Items.BombBag, Items.SmallKey:Deepwood:2)) @@ -97,5 +108,20 @@ CoFBasementLavaBig:FlameCave; DungeonItem; 50-17-02; Locations.CoFAccess, Items. CompleteCoF:FlameCave; Helper;; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave:2, Items.BigKey:FlameCave # Fortress of Winds Locations -FortressAccess:Fortress; Helper;; Locations.AccessWilds, Items.KinstoneX.YellowTotemProng::3 -#FortressPrize:Prizes; Major; 09C9E6 \ No newline at end of file +AccessFortress:Fortress; Helper;; Locations.AccessWilds, Items.KinstoneX.YellowTotemProng::3 +FortressEntrance:Fortress; Minor; 18-00-00; Locations.AccessFortress, Items.MoleMitts +FortressOutsideF2Left:Fortress; Minor; 18-01-00; Locations.AccessFortress, Items.MoleMitts +FortressOutsideF2Middle:Fortress; Minor; 18-01-01; Locations.AccessFortress, Items.MoleMitts +FortressOutsideF2Right:Fortress; Minor; 18-01-02; Locations.AccessFortress, Items.MoleMitts, Helpers.HasBow, Helpers.CanSplit2 +FortressOutsideF3Left:Fortress; Minor; 18-02-00; Locations.AccessFortress, Items.MoleMitts +FortressOutsideF3Right:Fortress; Minor; 18-02-00; Locations.AccessFortress, Items.MoleMitts +FortressOutsideBigChest:Fortress; Major; 18-03-00; Locations.AccessFortress, Items.BombBag, Helpers.HasBow, Helpers.CanSplit2 +FortressOutsideSmallChest:Fortress; Minor; 18-03-01; Locations.FortressOutsideBigChest, Items.MoleMitts +FortressEyegoreKill:Fortress; DungeonItem; 58-00-00; Locations.AccessFortress, Helpers.HasBow, Helpers.CanSplit2 +FortressPedestal:Fortress; DungeonItem; 58-19-00; Locations.AccessFortress +FortressSkullFall:Fortress; DungeonItem; 58-1B-00; Locations.AccessFortress, Items.MoleMitts, Helpers.CanSplit2, Helpers.HasBow +FortressSkullRoomLeft:Fortress; Minor; 58-1D-00; Locations.AccessFortress +FortressSkullRoomRight:Fortress; Minor; 58-1D-01; Locations.AccessFortress +FortressWizrobes:Fortress; Minor; 58-23-00; Locations.AccessFortress, Items.MoleMitts +CompleteFortress:Fortress; Helper;; Locations.AccessFortress, Items.MoleMitts, Helpers.CanSplit2, Items.Bow, Items.BigKey:Fortress +FortressPrize:Prizes; Major; 09C9E6; Locations.CompleteFortress \ No newline at end of file From 404b1a5bd9e2689c9cb5db62b7bbc8ff3b697d12 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Mon, 8 Jul 2019 20:34:22 -0600 Subject: [PATCH 22/41] Add Lake Hylia and Temple of Droplets to logic --- Resources/default.logic | 62 +++++++++++++++++++++++++++++++++-------- 1 file changed, 50 insertions(+), 12 deletions(-) diff --git a/Resources/default.logic b/Resources/default.logic index 68ae0996..250a2dcd 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -14,49 +14,54 @@ # Item Macro Helpers HasSword; Helper;; (|Items.SmithSword, Items.GreenSword, Items.RedSword, Items.BlueSword, Items.FourSword) -CanSplit2; Helper;; (|Items.RedSword, Items.BlueSword, Items.FourSword) -CanSplit3; Helper;; (|Items.BlueSword, Items.FourSword) +CanSplit2; Helper;; (|Items.SmithSword::3, Items.RedSword, Items.BlueSword, Items.FourSword) +CanSplit3; Helper;; (|Items.SmithSword::4, Items.BlueSword, Items.FourSword) +CanSplit4; Helper;; (|Items.SmithSword::5, Items.FourSword) HasBottle; Helper;; (|Items.Bottle1, Items.Bottle2, Items.Bottle3, Items.Bottle4) HasBow; Helper;; (|Items.Bow, Items.LightArrow) +HasLightBow; Helper;; (|Items.Bow::2, Items.LightArrow) # Central Hyrule Locations SmithHouse; Minor; 22-11-00 IntroItem1; Major; F252B;; Items.SmithSword IntroItem2; Major; F253B;; Items.Shield -SouthKeeseCave; Minor; 32-13-00; Items.BombBag HyruleWellRight; Minor; 41-00-04 SwiftbladeFirst; ScrollItem; 110D7B; Helpers.HasSword CafeLady; Minor; 00EDDA;; Items.KinstoneX.RedSpike -BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, (|(&Helpers.HasSword, Items.SpinAttack), Items.Flippers) +AccessEastField; Helper;; (|Helpers.HasSword, Items.BombBag, Helpers.HasLightBow, Items.Ocarina) +SouthKeeseCave; Minor; 32-13-00; Locations.AccessEastField, Items.BombBag + +AccessWestField; Helper;; (|(&Helpers.HasSword, Items.SpinAttack), Items.Flippers, (&Items.BombBag, Helpers.CanSplit3)) +BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, Locations.AccessWestField # Minish Woods Locations -AccessMinishWoods; Helper;; (|Helpers.HasSword, Items.BombBag, Items.LightArrow) +AccessMinishWoods; Helper;; Locations.AccessEastField JabberNut; Split; 09498C; Locations.AccessMinishWoods BelariBombs; Major; 00A00C; Locations.AccessMinishWoods MinishMiddleFlipperHole; Minor; 35-09-00; Locations.AccessMinishWoods, Items.Flippers MinishRightFlipperHole; Minor; 35-09-01; Locations.AccessMinishWoods, Items.Flippers MinishLeftFlipperHole; Minor; 35-09-02; Locations.AccessMinishWoods, Items.Flippers -MinishWitchDiggingCaveRight; Minor; 0C-00-00; Locations.AccessMinishWoods, Items.MoleMitts, (|Items.Flippers, Items.RocsCape, (&Items.LonLonKey, Items.PacciCane)) MinishLikeLikeDiggingCaveLeft; Minor; 0C-00-01; Locations.AccessMinishWoods, Items.MoleMitts MinishLikeLikeDiggingCaveRight; Minor; 0C-00-02; Locations.AccessMinishWoods, Items.MoleMitts +MinishNorthHole; Minor; 35-05-00; Locations.AccessSouthHylia, Items.Flippers, Items.PegasusBoots # Mount Crenel Locations -AccessCrenel; Helper;; Helpers.HasBottle, (|Items.BombBag, Items.GripRing), (|(&Helpers.HasSword, Items.SpinAttack), Items.Flippers) -#CrenelLowerScrub; PurchaseItem; 0CC09C; Helpers.HasBottle, Items.Shield # Technically a Crenel item, but doesn't need bombs +AccessCrenel; Helper;; Locations.AccessWestField, Helpers.HasBottle, (|Items.BombBag, Items.GripRing) +#CrenelLowerScrub; PurchaseItem; 0CC09C; Locations.AccessWestField, Helpers.HasBottle CrenelVineHole; Minor; 35-00-00; Locations.AccessCrenel, (|Items.GustJar, Items.BombBag) CrenelMinishHouse; Minor; 27-03-00; Locations.AccessCrenel, (|Items.GustJar, Items.BombBag) CrenelCaveDownstairs; Minor; 26-07-00; Locations.AccessCrenel, Items.BombBag CrenelHeartCaveLeft; Minor; 26-08-00; Locations.AccessCrenel, Items.BombBag CrenelHeartCaveRight; Minor; 26-08-01; Locations.AccessCrenel, Items.BombBag CrenelGripScrub; PurchaseItem; 0CC0A8; Locations.AccessCrenel, Items.Shield, Items.BombBag -CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing, (|Items.GustJar, Items.LightArrow))) +CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing, (|Items.GustJar, Helpers.HasLightBow))) Melari; Major; 00D26E; Locations.CompleteCoF # Castor Wilds Locations -AccessWilds; Helper;; Helpers.CanSplit2, (|Items.SpinAttack, Items.Flippers), (|Items.PegasusBoots, Items.RocsCape) +AccessWilds; Helper;; Locations.AccessWestField, Helpers.CanSplit2, (|Items.PegasusBoots, Items.RocsCape) WildsSouthCave; Major; 2A-00-00; Locations.AccessWilds, (|Items.Flippers, Items.RocsCape, Helpers.HasBow); Items.KinstoneX.YellowTotemProng WildsDarknutCave; Major; 2B-00-00; Locations.AccessWilds, Helpers.HasSword; Items.KinstoneX.YellowTotemProng WildsDekuCaveRight; Major; 2A-01-00; Locations.AccessWilds, Helpers.HasBow; Items.KinstoneX.YellowTotemProng @@ -69,6 +74,17 @@ RuinsMinishCave; Minor; 27-07-00; Locations.AccessWilds, Items.KinstoneX.YellowT RuinsArmosKillLeft; Minor; 05-05-00; Locations.AccessWilds, Items.KinstoneX.YellowTotemProng::3, Helpers.HasSword RuinsArmosKillRight; Minor; 05-05-01; Locations.AccessWilds, Items.KinstoneX.YellowTotemProng::3, Helpers.HasSword +# Lake Hylia Locations +AccessHylia; Helper;; Locations.AccessEastField, (|Items.Ocarina, Items.LonLonKey, (&Items.MoleMitts, (|Items.RocsCape, Items.Flippers))) +#StockwellDog; Major; ; Locations.AccessHylia, Items.DogFoodBottle +HyliaNorthMinishHole; Minor; 35-07-00; Locations.AccessHylia, Items.Flippers, Items.PegasusBoots + +AccessSouthHylia; Helper;; Locations.AccessHylia, (|Items.RocsCape, Items.Flippers, (&Items.MoleMitts, Items.PacciCane, (|Items.Ocarina, Items.LonLonKey))) +WitchDiggingCaveRight; Minor; 0C-00-00; (|Locations.AccessMinishWoods, Items.MoleMitts, (|Items.Flippers, Items.RocsCape, (&Items.LonLonKey, Items.PacciCane)) + +AccessTreasureCave; Helper;; Locations.AccessHylia, Items.RocsCape + + # Royal Valley Locations AccessValley; Helper;; (|(&Helpers.HasSword, Items.SpinAttack), Items.Flippers), Items.PacciCane, Items.LanternOff DampeKey; Major; 0096B4; Locations.AccessValley @@ -91,7 +107,7 @@ DeepwoodUpstairsChest:Deepwood; Minor; 48-17-01; Locations.DeepwoodAccess, (|Ite CompleteDeepwood:Deepwood; Helper;; Locations.DeepwoodAccess, Items.GustJar, Helpers.HasSword, Items.BigKey:Deepwood # Cave of Flames locations -CoFAccess:FlameCave; Helper;; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing, (|Items.GustJar, Items.LightArrow))), (|Items.BombBag, Items.Shield, Items.PacciCane) +CoFAccess:FlameCave; Helper;; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing, (|Items.GustJar, Helpers.HasLightBow))), (|Items.BombBag, Items.Shield, Items.PacciCane) CoFFlippedCart:FlameCave; DungeonItem; 50-01-01; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave CoFChuPit:FlameCave; Major; 50-01-02; Locations.CoFAccess, Items.PacciCane, Items.SmallKey:FlameCave CoFSouthwest1:FlameCave; DungeonItem; 50-08-00; Locations.CoFAccess @@ -124,4 +140,26 @@ FortressSkullRoomLeft:Fortress; Minor; 58-1D-00; Locations.AccessFortress FortressSkullRoomRight:Fortress; Minor; 58-1D-01; Locations.AccessFortress FortressWizrobes:Fortress; Minor; 58-23-00; Locations.AccessFortress, Items.MoleMitts CompleteFortress:Fortress; Helper;; Locations.AccessFortress, Items.MoleMitts, Helpers.CanSplit2, Items.Bow, Items.BigKey:Fortress -FortressPrize:Prizes; Major; 09C9E6; Locations.CompleteFortress \ No newline at end of file +FortressPrize:Prizes; Major; 09C9E6; Locations.CompleteFortress + +# Temple of Droplets Locations +AccessDroplets:Droplets; Helper;; Locations.AccessHylia, (|Items.Flippers, Items.RocsCape) +#DropletsFirstIceblock:Droplets; ; DungeonItem; Locations.AccessDroplets +#DropletsSecondIceblock:Droplets; ; DungeonItem; Locations.AccessDroplets, Helpers.DropletsOpenDoors +DropletsEastLever:Droplets; Helper;; Locations.AccessDroplets, Items.BigKey:Droplets, Helpers.DropletsOpenDoors, (|Items.GustJar, (&Items.LanternOff, Items.RocsCape)), Helpers.CanSplit2 +DropletsWestLever:Droplets; Helper;; Locations.AccessDroplets, Items.BigKey:Droplets, Helpers.DropletsOpenDoors, Items.LanternOff, Helpers.CanSplit2 +DropletsOpenDoors:Droplets; Helper;; Items.SmallKey:Droplets:2, Items.GripRing, Items.Flippers, Items.BombBag, Items.LanternOff, Helpers.HasSword +DropletsEastFirst:Droplets; Minor; 60-09-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) +DropletsIceMaze:Droplets; Minor; 60-0A-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) +DropletsOverhang:Droplets; DungeonItem; 60-0D-00; Locations.AccessDroplets, Items.BigKey:Droplets +DropletsBluChu:Droplets; Major; 60-10-01; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever), Helpers.DropletsOpenDoors +DropletsBasement:Droplets; DungeonItem; 60-11-01; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) +DropletsFrozenIcePlain:Droplets; Minor; 60-28-00; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors +DropletsFreeIcePlain:Droplets; Minor; 60-28-01; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors +DropletsDarkMazeRight:Droplets; Minor; 60-2B-01; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff +DropletsDarkMazeLeft:Droplets; Minor; 60-2B-02; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff +DropletsDarkMazeMiddle:Droplets; Minor; 60-2B-03; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff +DropletsPostTwinFrozen:Droplets; Minor; 60-2D-00; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors +DropletsPreviewFrozen:Droplets; Minor; 60-32-00; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff +DropletsIceWiggler:Droplets; DungeonItem; 60-32-01; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors +CompleteDroplets:Droplets; Helper;; Locations.DropletsEastLever, Locations.DropletsWestLever \ No newline at end of file From b988f8a5ff9744d4c1400ea525b59899a48c103a Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Sat, 13 Jul 2019 19:04:45 -0600 Subject: [PATCH 23/41] Add palace of winds to logic, fix caching --- Core/Constants.cs | 2 +- Randomizer/Location.cs | 29 +++++++++++++-- Randomizer/Shuffler.cs | 3 +- Resources/default.logic | 80 +++++++++++++++++++++++++++++++++++++---- 4 files changed, 102 insertions(+), 12 deletions(-) diff --git a/Core/Constants.cs b/Core/Constants.cs index 7405496f..9e6fa9c7 100644 --- a/Core/Constants.cs +++ b/Core/Constants.cs @@ -203,7 +203,7 @@ public enum ItemType ArrowButterfly = 0x70, DigButterfly = 0x71, SwimButterfly = 0x72, - FastSplin = 0x73, + FastSpin = 0x73, FastSplit = 0x74, LongSpin = 0x75 } diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs index f4e95c3d..edad54bb 100644 --- a/Randomizer/Location.cs +++ b/Randomizer/Location.cs @@ -158,7 +158,8 @@ public enum LocationType Helper, //StartingItem, PurchaseItem, - ScrollItem + ScrollItem, + Half } public List Dependencies; @@ -231,6 +232,17 @@ public void WriteLocation(Writer w) w.WriteByte((byte)((byte)Contents.Type & 0x7F)); } break; + case LocationType.Half: + w.SetPosition(Address); + if (Contents.Type == ItemType.KinstoneX || Contents.Type == ItemType.ShellsX) + { + w.WriteByte((byte)ItemType.Shells30); + } + else + { + w.WriteByte((byte)Contents.Type); + } + break; case LocationType.Major: case LocationType.Minor: default: @@ -286,6 +298,7 @@ public bool CanPlace(Item itemToPlace, List availableItems, List return false; case LocationType.PurchaseItem: case LocationType.ScrollItem: + case LocationType.Half: if (itemToPlace.SubValue != 0) { return false; @@ -309,9 +322,9 @@ public bool CanPlace(Item itemToPlace, List availableItems, List return IsAccessible(availableItems, locations); } - public bool IsAccessible(List availableItems, List locations) + public bool IsAccessible(List availableItems, List locations, bool cache = true) { - if (AvailableCache != null) + if (AvailableCache != null && cache == true) { return (bool)AvailableCache; } @@ -320,10 +333,20 @@ public bool IsAccessible(List availableItems, List locations) { if (!dependency.DependencyFulfilled(availableItems, locations)) { + if (cache) + { + AvailableCache = false; + } + return false; } } + if (cache) + { + AvailableCache = true; + } + return true; } diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index 27945365..d794dff3 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -274,7 +274,8 @@ private List GetAvailableItems(List preAvailableItems) int previousSize; do { - List accessibleLocations = filledLocations.Where(location => location.IsAccessible(availableItems, Locations)).ToList(); + // Doesn't touch the cache to prevent incorrect caching + List accessibleLocations = filledLocations.Where(location => location.IsAccessible(availableItems, Locations, false)).ToList(); previousSize = accessibleLocations.Count; filledLocations.RemoveAll(location => accessibleLocations.Contains(location)); diff --git a/Resources/default.logic b/Resources/default.logic index 250a2dcd..84ca1e86 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -14,15 +14,17 @@ # Item Macro Helpers HasSword; Helper;; (|Items.SmithSword, Items.GreenSword, Items.RedSword, Items.BlueSword, Items.FourSword) -CanSplit2; Helper;; (|Items.SmithSword::3, Items.RedSword, Items.BlueSword, Items.FourSword) -CanSplit3; Helper;; (|Items.SmithSword::4, Items.BlueSword, Items.FourSword) -CanSplit4; Helper;; (|Items.SmithSword::5, Items.FourSword) +CanSplit2; Helper;; (|Items.SmithSword::3, Items.RedSword, Items.BlueSword, Items.FourSword), Items.SpinAttack +CanSplit3; Helper;; (|Items.SmithSword::4, Items.BlueSword, Items.FourSword), Items.SpinAttack +CanSplit4; Helper;; (|Items.SmithSword::5, Items.FourSword), Items.SpinAttack HasBottle; Helper;; (|Items.Bottle1, Items.Bottle2, Items.Bottle3, Items.Bottle4) HasBow; Helper;; (|Items.Bow, Items.LightArrow) HasLightBow; Helper;; (|Items.Bow::2, Items.LightArrow) +HasBoomerang; Helper;; (|Items.Boomerang, Items.MagicBoomerang) + # Central Hyrule Locations SmithHouse; Minor; 22-11-00 IntroItem1; Major; F252B;; Items.SmithSword @@ -33,6 +35,7 @@ CafeLady; Minor; 00EDDA;; Items.KinstoneX.RedSpike AccessEastField; Helper;; (|Helpers.HasSword, Items.BombBag, Helpers.HasLightBow, Items.Ocarina) SouthKeeseCave; Minor; 32-13-00; Locations.AccessEastField, Items.BombBag +AboveHPHole; Minor; 27-00-00; Locations.AccessEastField, (|Items.Ocarina, Items.LonLonKey), (|Items.PacciCane, Items.RocsCape) AccessWestField; Helper;; (|(&Helpers.HasSword, Items.SpinAttack), Items.Flippers, (&Items.BombBag, Helpers.CanSplit3)) BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, Locations.AccessWestField @@ -83,13 +86,55 @@ AccessSouthHylia; Helper;; Locations.AccessHylia, (|Items.RocsCape, Items.Flippe WitchDiggingCaveRight; Minor; 0C-00-00; (|Locations.AccessMinishWoods, Items.MoleMitts, (|Items.Flippers, Items.RocsCape, (&Items.LonLonKey, Items.PacciCane)) AccessTreasureCave; Helper;; Locations.AccessHylia, Items.RocsCape - +TreasureCave0; Minor; 19-01-00; Locations.AccessTreasureCave +TreasureCave1; Minor; 19-01-01; Locations.AccessTreasureCave +TreasureCave2; Minor; 19-01-02; Locations.AccessTreasureCave +TreasureCave3; Minor; 19-01-03; Locations.AccessTreasureCave +TreasureCave4; Minor; 19-01-04; Locations.AccessTreasureCave +TreasureCave5; Minor; 19-01-05; Locations.AccessTreasureCave +TreasureCave6; Minor; 19-01-06; Locations.AccessTreasureCave +TreasureCave7; Minor; 19-01-07; Locations.AccessTreasureCave # Royal Valley Locations AccessValley; Helper;; (|(&Helpers.HasSword, Items.SpinAttack), Items.Flippers), Items.PacciCane, Items.LanternOff DampeKey; Major; 0096B4; Locations.AccessValley KingGift:Prizes; Major; 00DA5A; Locations.AccessValley, Items.GraveyardKey, Helpers.CanSplit3, Items.PegasusBoots; Items.KinstoneX.YellowCrown +# Veil Falls Locations +AccessFallsNorth; Helper;; Items.BombBag, Items.KinstoneX.YellowCrown, Items.LanternOff +FallsBehindWall; Minor; 33-05-00; Locations.AccessFallsNorth, Items.BombBag +FallsCliff; Minor; 0A-00-00; Locations.AccessFallsNorth, Items.BombBag, Helpers.CanSplit3 +FallsTopCaveBomb; Minor; 33-02-00; Locations.AccessFallsNorth, Items.GripRing, Items.BombBag +FallsTopCaveFree; Minor; 33-00-00; Locations.AccessFallsNorth, Items.GripRing + + +AccessFallsSouth; Helper;; Locations.AccessEastField, Items.Pacci +FallsLowerCaveLeft; Minor; 16-00-01; Locations.AccessEastField, Items.Pacci + +# Cloud Tops Locations +AccessClouds; Helper;; Locations.AccessFallsNorth, Items.GripRing +CloudsFreeChest; Major; 08-01-00; Locations.AccessClouds; Items.KinstoneX.YellowTornadoProng +CloudsNorthKill; Major; ; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts); Items.KinstoneX.YellowTornadoProng +CloudsSouthKill; Major; ; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts); Items.KinstoneX.YellowTornadoProng +CloudsSouthMiddle; Major; 08-01-01; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts); Items.KinstoneX.YellowTornadoProng +CloudsWestBottom; Major; 08-01-02; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts); Items.KinstoneX.YellowTornadoProng +CloudsWestLeft; Minor; 08-01-03; Locations.AccessClouds, Items.MoleMitts +CloudsWestRight; Minor; 08-01-04; Locations.AccessClouds, Items.MoleMitts +CloudsSouthLeft; Minor; 08-01-05; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts) +CloudsSouthRight; Minor; 08-01-06; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts) + +AccessUpperClouds; Helper;; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts), Items.KinstoneX.YellowTornadoProng::5 +TowerBottomRight; Minor; 30-00-00; Locations.AccessUpperClouds +TowerBottomLeft; Minor; 30-00-01; Locations.AccessUpperClouds +TowerF2; Minor; 30-01-00; Locations.AccessUpperClouds +GregalOne; Half; 014C5A; Locations.AccessUpperClouds, Items.GustJar +GregalTwo; Half; 014CBC; Locations.AccessUpperClouds, Items.GustJar +TowerRightBed; Minor; 30-02-00; Locations.AccessUpperClouds +TowerMiddleBed; Minor; 30-02-01; Locations.AccessUpperClouds +TowerLeftBed; Minor; 30-02-02; Locations.AccessUpperClouds +TowerTopLeft; Minor; 30-03-00; Locations.AccessUpperClouds +TowerTopRight; Minor; 30-03-01; Locations.AccessUpperClouds + # Deepwood locations DeepwoodAccess:Deepwood; Helper;; Locations.AccessMinishWoods, (|Items.JabberNut, Items.Flippers) DeepwoodWiggler:Deepwood; Major; 48-00-01; Locations.DeepwoodAccess, Helpers.HasSword, (|Items.GustJar, Items.LanternOff), Items.SmallKey:Deepwood:3 @@ -148,11 +193,11 @@ AccessDroplets:Droplets; Helper;; Locations.AccessHylia, (|Items.Flippers, Items #DropletsSecondIceblock:Droplets; ; DungeonItem; Locations.AccessDroplets, Helpers.DropletsOpenDoors DropletsEastLever:Droplets; Helper;; Locations.AccessDroplets, Items.BigKey:Droplets, Helpers.DropletsOpenDoors, (|Items.GustJar, (&Items.LanternOff, Items.RocsCape)), Helpers.CanSplit2 DropletsWestLever:Droplets; Helper;; Locations.AccessDroplets, Items.BigKey:Droplets, Helpers.DropletsOpenDoors, Items.LanternOff, Helpers.CanSplit2 -DropletsOpenDoors:Droplets; Helper;; Items.SmallKey:Droplets:2, Items.GripRing, Items.Flippers, Items.BombBag, Items.LanternOff, Helpers.HasSword +DropletsOpenDoors:Droplets; Helper;; Items.SmallKey:Droplets:2, Items.GripRing, Items.Flippers, (|Items.GustJar, Items.RocsCape), Items.BombBag, Items.LanternOff, Helpers.HasSword DropletsEastFirst:Droplets; Minor; 60-09-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) DropletsIceMaze:Droplets; Minor; 60-0A-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) DropletsOverhang:Droplets; DungeonItem; 60-0D-00; Locations.AccessDroplets, Items.BigKey:Droplets -DropletsBluChu:Droplets; Major; 60-10-01; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever), Helpers.DropletsOpenDoors +DropletsBluChu:Droplets; Major; 60-10-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever), Helpers.DropletsOpenDoors DropletsBasement:Droplets; DungeonItem; 60-11-01; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) DropletsFrozenIcePlain:Droplets; Minor; 60-28-00; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors DropletsFreeIcePlain:Droplets; Minor; 60-28-01; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors @@ -162,4 +207,25 @@ DropletsDarkMazeMiddle:Droplets; Minor; 60-2B-03; Locations.AccessDroplets, Item DropletsPostTwinFrozen:Droplets; Minor; 60-2D-00; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors DropletsPreviewFrozen:Droplets; Minor; 60-32-00; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff DropletsIceWiggler:Droplets; DungeonItem; 60-32-01; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors -CompleteDroplets:Droplets; Helper;; Locations.DropletsEastLever, Locations.DropletsWestLever \ No newline at end of file +CompleteDroplets:Droplets; Helper;; Locations.DropletsEastLever, Locations.DropletsWestLever, Items.LanternOff, Items.HasSword + +# Palace of Winds Locations +AccessPalace:Palace; Helper;; Locations.AccessUpperClouds, Helpers.CanSplit3, (|Items.RocsCape, Items.BombBag, Items.GustJar, Helpers.HasBoomerang, Helpers.HasBow) +PalacePreBigDoor:Palace; DungeonItem; 70-01-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.SmallKey:Palace:2 +PalacePreBoss:Palace; DungeonItem; 70-03-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace, Items.SmallKey:Palace:3 +PalaceDetour:Palace; Minor; 70-04-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace, Items.SmallKey:Palace:4 +PalaceFanLoop:Palace; DungeonItem; 70-07-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane +PalaceWideGap:Palace; Minor; 70-0F-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane +PalaceBlockMaze:Palace; Minor; 70-10-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace, Items.SmallKey:Palace:4 +PalaceSwitchHit:Palace; Minor; 70-15-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace, Items.SmallKey:Palace:2 +PalaceFirerobeTrio:Palace; DungeonItem; 70-1C-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace, Items.SmallKey:Palace:2 +PalaceTwinWizrobes:Palace; Minor; 70-29-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace, Items.SmallKey:Palace:2 +PalaceManyRollers:Palace; DungeonItem; 70-2B-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace +PalaceWizrobeKill:Palace; Major; 70-2C-00; Locations.AccessPalace, (|Items.RocsCape, Items.BombBag, Helpers.HasBoomerang) +PalaceFirstGrate:Palace; Major; 70-2D-00; Locations.AccessPalace, Items.RocsCape +PalaceDarkBig:Palace; DungeonItem; 70-32-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace +PalaceDarkSmall:Palace; DungeonItem; 70-32-01; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace +CompletePalace:Palace; Helper;; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace, Items.SmallKey:Palace:4 + +# Skipping ahead to DHC so the game can be considered beatable... +BeatVaati; Helper;; Locations.CompleteDeepwood, Locations.CompleteCoF, Locations.CompleteDroplets, Locations.CompletePalace, Helpers.CanSplit4, Items.BombBag, Helpers.HasBow, Items.RocsCape, Items.LanternOff, Items.GustJar, Items.PacciCane \ No newline at end of file From 98c230a610988d11817726aba5ed9bd251c169c4 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Sat, 13 Jul 2019 21:29:04 -0600 Subject: [PATCH 24/41] Make randomization testable --- Randomizer/Dependency.cs | 4 +++ Randomizer/Location.cs | 5 ++- Randomizer/Shuffler.cs | 1 + Resources/default.logic | 76 ++++++++++++++++++++++++---------------- 4 files changed, 55 insertions(+), 31 deletions(-) diff --git a/Randomizer/Dependency.cs b/Randomizer/Dependency.cs index bd916cb0..926bb9f0 100644 --- a/Randomizer/Dependency.cs +++ b/Randomizer/Dependency.cs @@ -130,6 +130,9 @@ public override bool DependencyFulfilled(List availableItems, List availableItems, List availableItems, List return IsAccessible(availableItems, locations); } - public bool IsAccessible(List availableItems, List locations, bool cache = true) + public bool IsAccessible(List availableItems, List locations, bool cache = false) { if (AvailableCache != null && cache == true) { @@ -336,6 +337,7 @@ public bool IsAccessible(List availableItems, List locations, bo if (cache) { AvailableCache = false; + Console.WriteLine($"Can't reach {Name}"); } return false; @@ -344,6 +346,7 @@ public bool IsAccessible(List availableItems, List locations, bo if (cache) { + Console.WriteLine($"Can reach {Name}"); AvailableCache = true; } diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index d794dff3..ada6c0ba 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -236,6 +236,7 @@ private List FillLocations(List items, List locations, if (availableLocations.Count <= 0) { + availableItems.ForEach(itm => Console.WriteLine($"{itm.Type} sub {itm.SubValue}")); throw new ShuffleException($"Could not place {item.Type}"); } diff --git a/Resources/default.logic b/Resources/default.logic index 84ca1e86..bec3f528 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -30,12 +30,27 @@ SmithHouse; Minor; 22-11-00 IntroItem1; Major; F252B;; Items.SmithSword IntroItem2; Major; F253B;; Items.Shield HyruleWellRight; Minor; 41-00-04 -SwiftbladeFirst; ScrollItem; 110D7B; Helpers.HasSword -CafeLady; Minor; 00EDDA;; Items.KinstoneX.RedSpike +HyruleWellLeft; Major; 40-00-01; Items.MoleMitts; Items.GreenSword # Temporary for Testing +#SwiftbladeFirst; ScrollItem; 110D7B; Helpers.HasSword +#CafeLady; Minor; 00EDDA;; Items.KinstoneX.RedSpike +HearthLedge; Major; 02-00-06; Items.LanternOff; Items.BlueSword # Temporary for Testing +SchoolTop; Major; 02-00-07; Items.PacciCane; Items.FourSword # Temporary for Testing +SchoolGardenLeft; Minor; 11-02-00; Items.PacciCane, Helpers.CanSplit4 +SchoolGardenMiddle; Minor; 11-02-01; Items.PacciCane, Helpers.CanSplit4 +SchoolGardenRight; Minor; 11-02-02; Items.PacciCane, Helpers.CanSplit4 +TownDiggingTop; Major; 0F-00-00; Items.MoleMitts; Items.RedSword # Temporary for Testing +TownDiggingRight; Major; 0F-00-01; Items.MoleMitts; Items.SpinAttack #Temporary for Testing +TownDiggingLeft; Minor; 0F-00-02; Items.MoleMitts + + +FlipsCaveBig; Major; 62-10-00; Items.Flippers, Items.Ocarina, Items.PacciCane, Helpers.HasSword +FlipsCaveSmall; Minor; 62-12-00; Items.Flippers, Items.Ocarina, Items.PacciCane, Items.LanternOff + AccessEastField; Helper;; (|Helpers.HasSword, Items.BombBag, Helpers.HasLightBow, Items.Ocarina) SouthKeeseCave; Minor; 32-13-00; Locations.AccessEastField, Items.BombBag AboveHPHole; Minor; 27-00-00; Locations.AccessEastField, (|Items.Ocarina, Items.LonLonKey), (|Items.PacciCane, Items.RocsCape) +LonLonCave; Minor; 32-0C-00; (|Items.Ocarina, Items.LonLonKey) Helpers.CanSplit2 AccessWestField; Helper;; (|(&Helpers.HasSword, Items.SpinAttack), Items.Flippers, (&Items.BombBag, Helpers.CanSplit3)) BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, Locations.AccessWestField @@ -43,8 +58,8 @@ BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, Locations.Access # Minish Woods Locations AccessMinishWoods; Helper;; Locations.AccessEastField JabberNut; Split; 09498C; Locations.AccessMinishWoods -BelariBombs; Major; 00A00C; Locations.AccessMinishWoods -MinishMiddleFlipperHole; Minor; 35-09-00; Locations.AccessMinishWoods, Items.Flippers +#BelariBombs; Major; 00A00C; Locations.AccessMinishWoods +MinishMiddleFlipperHole; Major; 35-09-00; Locations.AccessMinishWoods, Items.Flippers; Items.BombBag #Temp MinishRightFlipperHole; Minor; 35-09-01; Locations.AccessMinishWoods, Items.Flippers MinishLeftFlipperHole; Minor; 35-09-02; Locations.AccessMinishWoods, Items.Flippers MinishLikeLikeDiggingCaveLeft; Minor; 0C-00-01; Locations.AccessMinishWoods, Items.MoleMitts @@ -60,6 +75,8 @@ CrenelCaveDownstairs; Minor; 26-07-00; Locations.AccessCrenel, Items.BombBag CrenelHeartCaveLeft; Minor; 26-08-00; Locations.AccessCrenel, Items.BombBag CrenelHeartCaveRight; Minor; 26-08-01; Locations.AccessCrenel, Items.BombBag CrenelGripScrub; PurchaseItem; 0CC0A8; Locations.AccessCrenel, Items.Shield, Items.BombBag +GraybladeLeft; ScrollItem; 25-00-00; Locations.AccessCrenel, Helpers.CanSplit2 +GraybladeRight; ScrollItem; 25-00-01; Locations.AccessCrenel, Helpers.CanSplit2 CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing, (|Items.GustJar, Helpers.HasLightBow))) Melari; Major; 00D26E; Locations.CompleteCoF @@ -80,7 +97,7 @@ RuinsArmosKillRight; Minor; 05-05-01; Locations.AccessWilds, Items.KinstoneX.Yel # Lake Hylia Locations AccessHylia; Helper;; Locations.AccessEastField, (|Items.Ocarina, Items.LonLonKey, (&Items.MoleMitts, (|Items.RocsCape, Items.Flippers))) #StockwellDog; Major; ; Locations.AccessHylia, Items.DogFoodBottle -HyliaNorthMinishHole; Minor; 35-07-00; Locations.AccessHylia, Items.Flippers, Items.PegasusBoots +HyliaNorthMinishHole; Major; 35-07-00; Locations.AccessHylia, Items.Flippers, Items.PegasusBoots; Items.PegasusBoots #Temp AccessSouthHylia; Helper;; Locations.AccessHylia, (|Items.RocsCape, Items.Flippers, (&Items.MoleMitts, Items.PacciCane, (|Items.Ocarina, Items.LonLonKey))) WitchDiggingCaveRight; Minor; 0C-00-00; (|Locations.AccessMinishWoods, Items.MoleMitts, (|Items.Flippers, Items.RocsCape, (&Items.LonLonKey, Items.PacciCane)) @@ -96,26 +113,25 @@ TreasureCave6; Minor; 19-01-06; Locations.AccessTreasureCave TreasureCave7; Minor; 19-01-07; Locations.AccessTreasureCave # Royal Valley Locations -AccessValley; Helper;; (|(&Helpers.HasSword, Items.SpinAttack), Items.Flippers), Items.PacciCane, Items.LanternOff -DampeKey; Major; 0096B4; Locations.AccessValley -KingGift:Prizes; Major; 00DA5A; Locations.AccessValley, Items.GraveyardKey, Helpers.CanSplit3, Items.PegasusBoots; Items.KinstoneX.YellowCrown +AccessValley; Helper;; Helpers.CanSplit3, Items.BombBag, Items.LanternOff +DampeKey; Major; 0096B6; Locations.AccessValley +#KingGift:Prizes; Major; 00DA5A; Locations.AccessValley, Items.GraveyardKey, Helpers.CanSplit3, Items.PegasusBoots; Items.KinstoneX.YellowCrown # Veil Falls Locations -AccessFallsNorth; Helper;; Items.BombBag, Items.KinstoneX.YellowCrown, Items.LanternOff +AccessFallsNorth; Helper;; Items.BombBag, Items.LanternOff, Locations.AccessValley, Items.GraveyardKey, Helpers.CanSplit3, Items.PegasusBoots#, Items.KinstoneX.YellowCrown Temp removed FallsBehindWall; Minor; 33-05-00; Locations.AccessFallsNorth, Items.BombBag FallsCliff; Minor; 0A-00-00; Locations.AccessFallsNorth, Items.BombBag, Helpers.CanSplit3 FallsTopCaveBomb; Minor; 33-02-00; Locations.AccessFallsNorth, Items.GripRing, Items.BombBag FallsTopCaveFree; Minor; 33-00-00; Locations.AccessFallsNorth, Items.GripRing - AccessFallsSouth; Helper;; Locations.AccessEastField, Items.Pacci FallsLowerCaveLeft; Minor; 16-00-01; Locations.AccessEastField, Items.Pacci # Cloud Tops Locations AccessClouds; Helper;; Locations.AccessFallsNorth, Items.GripRing CloudsFreeChest; Major; 08-01-00; Locations.AccessClouds; Items.KinstoneX.YellowTornadoProng -CloudsNorthKill; Major; ; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts); Items.KinstoneX.YellowTornadoProng -CloudsSouthKill; Major; ; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts); Items.KinstoneX.YellowTornadoProng +#CloudsNorthKill; Major; ; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts); Items.KinstoneX.YellowTornadoProng +#CloudsSouthKill; Major; ; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts); Items.KinstoneX.YellowTornadoProng CloudsSouthMiddle; Major; 08-01-01; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts); Items.KinstoneX.YellowTornadoProng CloudsWestBottom; Major; 08-01-02; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts); Items.KinstoneX.YellowTornadoProng CloudsWestLeft; Minor; 08-01-03; Locations.AccessClouds, Items.MoleMitts @@ -123,7 +139,7 @@ CloudsWestRight; Minor; 08-01-04; Locations.AccessClouds, Items.MoleMitts CloudsSouthLeft; Minor; 08-01-05; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts) CloudsSouthRight; Minor; 08-01-06; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts) -AccessUpperClouds; Helper;; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts), Items.KinstoneX.YellowTornadoProng::5 +AccessUpperClouds; Helper;; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts), Items.KinstoneX.YellowTornadoProng::3 TowerBottomRight; Minor; 30-00-00; Locations.AccessUpperClouds TowerBottomLeft; Minor; 30-00-01; Locations.AccessUpperClouds TowerF2; Minor; 30-01-00; Locations.AccessUpperClouds @@ -185,28 +201,28 @@ FortressSkullRoomLeft:Fortress; Minor; 58-1D-00; Locations.AccessFortress FortressSkullRoomRight:Fortress; Minor; 58-1D-01; Locations.AccessFortress FortressWizrobes:Fortress; Minor; 58-23-00; Locations.AccessFortress, Items.MoleMitts CompleteFortress:Fortress; Helper;; Locations.AccessFortress, Items.MoleMitts, Helpers.CanSplit2, Items.Bow, Items.BigKey:Fortress -FortressPrize:Prizes; Major; 09C9E6; Locations.CompleteFortress +FortressPrize:Prizes; Half; 09C9E6; Locations.CompleteFortress # Temple of Droplets Locations AccessDroplets:Droplets; Helper;; Locations.AccessHylia, (|Items.Flippers, Items.RocsCape) #DropletsFirstIceblock:Droplets; ; DungeonItem; Locations.AccessDroplets #DropletsSecondIceblock:Droplets; ; DungeonItem; Locations.AccessDroplets, Helpers.DropletsOpenDoors -DropletsEastLever:Droplets; Helper;; Locations.AccessDroplets, Items.BigKey:Droplets, Helpers.DropletsOpenDoors, (|Items.GustJar, (&Items.LanternOff, Items.RocsCape)), Helpers.CanSplit2 -DropletsWestLever:Droplets; Helper;; Locations.AccessDroplets, Items.BigKey:Droplets, Helpers.DropletsOpenDoors, Items.LanternOff, Helpers.CanSplit2 -DropletsOpenDoors:Droplets; Helper;; Items.SmallKey:Droplets:2, Items.GripRing, Items.Flippers, (|Items.GustJar, Items.RocsCape), Items.BombBag, Items.LanternOff, Helpers.HasSword -DropletsEastFirst:Droplets; Minor; 60-09-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) -DropletsIceMaze:Droplets; Minor; 60-0A-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) -DropletsOverhang:Droplets; DungeonItem; 60-0D-00; Locations.AccessDroplets, Items.BigKey:Droplets -DropletsBluChu:Droplets; Major; 60-10-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever), Helpers.DropletsOpenDoors -DropletsBasement:Droplets; DungeonItem; 60-11-01; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) -DropletsFrozenIcePlain:Droplets; Minor; 60-28-00; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors -DropletsFreeIcePlain:Droplets; Minor; 60-28-01; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors -DropletsDarkMazeRight:Droplets; Minor; 60-2B-01; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff -DropletsDarkMazeLeft:Droplets; Minor; 60-2B-02; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff -DropletsDarkMazeMiddle:Droplets; Minor; 60-2B-03; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff -DropletsPostTwinFrozen:Droplets; Minor; 60-2D-00; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors -DropletsPreviewFrozen:Droplets; Minor; 60-32-00; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff -DropletsIceWiggler:Droplets; DungeonItem; 60-32-01; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors +#DropletsEastLever:Droplets; Helper;; Locations.AccessDroplets, Items.BigKey:Droplets, Helpers.DropletsOpenDoors, (|Items.GustJar, (&Items.LanternOff, Items.RocsCape)), Helpers.CanSplit2 +#DropletsWestLever:Droplets; Helper;; Locations.AccessDroplets, Items.BigKey:Droplets, Helpers.DropletsOpenDoors, Items.LanternOff, Helpers.CanSplit2 +#DropletsOpenDoors:Droplets; Helper;; Items.SmallKey:Droplets:2, Items.GripRing, Items.Flippers, (|Items.GustJar, Items.RocsCape), Items.BombBag, Items.LanternOff, Helpers.HasSword +#DropletsEastFirst:Droplets; Minor; 60-09-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) +#DropletsIceMaze:Droplets; Minor; 60-0A-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) +#DropletsOverhang:Droplets; DungeonItem; 60-0D-00; Locations.AccessDroplets, Items.BigKey:Droplets +DropletsBluChu:Droplets; Major; 60-10-01; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever), Helpers.DropletsOpenDoors +#DropletsBasement:Droplets; DungeonItem; 60-11-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) +#DropletsFrozenIcePlain:Droplets; Minor; 60-28-00; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors +#DropletsFreeIcePlain:Droplets; Minor; 60-28-01; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors +#DropletsDarkMazeRight:Droplets; Minor; 60-2B-01; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff +#DropletsDarkMazeLeft:Droplets; Minor; 60-2B-02; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff +#DropletsDarkMazeMiddle:Droplets; Minor; 60-2B-03; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff +#DropletsPostTwinFrozen:Droplets; Minor; 60-2D-00; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors +#DropletsPreviewFrozen:Droplets; Minor; 60-32-00; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff +#DropletsIceWiggler:Droplets; DungeonItem; 60-32-01; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors CompleteDroplets:Droplets; Helper;; Locations.DropletsEastLever, Locations.DropletsWestLever, Items.LanternOff, Items.HasSword # Palace of Winds Locations From c4ab5a1bbe281c97c8e5a8dc74e548d1a90f9a4e Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Mon, 22 Jul 2019 11:06:39 -0600 Subject: [PATCH 25/41] Add ToD to shuffle, fix locations for better accuracy --- Randomizer/Dependency.cs | 1 - Randomizer/Location.cs | 33 ++++++++++----------- Randomizer/Shuffler.cs | 16 +++++++++-- Resources/default.logic | 62 +++++++++++++++++++++------------------- 4 files changed, 64 insertions(+), 48 deletions(-) diff --git a/Randomizer/Dependency.cs b/Randomizer/Dependency.cs index 926bb9f0..3dacf6b0 100644 --- a/Randomizer/Dependency.cs +++ b/Randomizer/Dependency.cs @@ -120,7 +120,6 @@ public override bool DependencyFulfilled(List availableItems, List= 5) { - string[] subParts = locationParts[4].Split('.'); + string[] itemParts = locationParts[4].Split(':'); + string[] subParts = itemParts[0].Split('.'); if (subParts[0] == "Items") { @@ -71,7 +72,19 @@ public static Location GetLocation(string locationText) } } - location.SetItem(new Item(replacementType, subType)); + string itemDungeon = ""; + + if (type == LocationType.Unshuffled) + { + itemDungeon = dungeon; + } + + if (itemParts.Length >= 2) + { + itemDungeon = itemParts[1]; + } + + location.SetItem(new Item(replacementType, subType, itemDungeon)); } } } @@ -91,19 +104,6 @@ public static List GetItems(List locations) return items; } - /*public static List GetAvailableItems(List locations, List preItems) - { - List items = new List(); - - List unsearchedLocations = locations.ToList(); - - do - { - - } - while (); - }*/ - public static int GetAddressFromString(string addressString) { // Either direct address or area-room-chest @@ -159,7 +159,8 @@ public enum LocationType //StartingItem, PurchaseItem, ScrollItem, - Half + Half, + Unshuffled } public List Dependencies; diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index ada6c0ba..9a017144 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -122,6 +122,11 @@ public void LoadLocations(string locationFile) case Location.LocationType.Helper: Console.WriteLine($"Helper or untyped {newLocation.Name}"); break; + // Unshuffled locations are filled by default + case Location.LocationType.Unshuffled: + Console.WriteLine($"Unshuffled {newLocation.Name}"); + newLocation.Fill(newLocation.Contents); + break; case Location.LocationType.Minor: Console.WriteLine(newLocation.Contents.Type.ToString()); MinorItems.Add(newLocation.Contents); @@ -207,7 +212,11 @@ private void ResetLocations() { location.SetDefaultContents(); location.InvalidateCache(); - location.Filled = false; + + if (location.Type != Location.LocationType.Unshuffled) + { + location.Filled = false; + } } } @@ -270,7 +279,10 @@ private List GetAvailableItems(List preAvailableItems) { List availableItems = preAvailableItems.ToList(); - List filledLocations = Locations.Where(location => location.Filled && location.Type != Location.LocationType.Helper && location.Type != Location.LocationType.Untyped).ToList(); + List filledLocations = Locations.Where(location => + { + return location.Filled && location.Type != Location.LocationType.Helper && location.Type != Location.LocationType.Untyped; + }).ToList(); int previousSize; do diff --git a/Resources/default.logic b/Resources/default.logic index bec3f528..7b79634c 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -43,17 +43,17 @@ TownDiggingRight; Major; 0F-00-01; Items.MoleMitts; Items.SpinAttack #Temporary TownDiggingLeft; Minor; 0F-00-02; Items.MoleMitts -FlipsCaveBig; Major; 62-10-00; Items.Flippers, Items.Ocarina, Items.PacciCane, Helpers.HasSword -FlipsCaveSmall; Minor; 62-12-00; Items.Flippers, Items.Ocarina, Items.PacciCane, Items.LanternOff +#FlipsCaveBig; Major; 62-10-00; Items.Flippers, Items.Ocarina, Items.PacciCane, Helpers.HasSword +#FlipsCaveSmall; Minor; 62-12-00; Items.Flippers, Items.Ocarina, Items.PacciCane, Items.LanternOff AccessEastField; Helper;; (|Helpers.HasSword, Items.BombBag, Helpers.HasLightBow, Items.Ocarina) SouthKeeseCave; Minor; 32-13-00; Locations.AccessEastField, Items.BombBag AboveHPHole; Minor; 27-00-00; Locations.AccessEastField, (|Items.Ocarina, Items.LonLonKey), (|Items.PacciCane, Items.RocsCape) -LonLonCave; Minor; 32-0C-00; (|Items.Ocarina, Items.LonLonKey) Helpers.CanSplit2 +LonLonCave; Major; 32-0C-00; (|Items.Ocarina, Items.LonLonKey), Helpers.CanSplit2; Items.Bottle1 #Temp AccessWestField; Helper;; (|(&Helpers.HasSword, Items.SpinAttack), Items.Flippers, (&Items.BombBag, Helpers.CanSplit3)) -BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, Locations.AccessWestField +#BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, Locations.AccessWestField # Minish Woods Locations AccessMinishWoods; Helper;; Locations.AccessEastField @@ -73,8 +73,8 @@ CrenelVineHole; Minor; 35-00-00; Locations.AccessCrenel, (|Items.GustJar, Items. CrenelMinishHouse; Minor; 27-03-00; Locations.AccessCrenel, (|Items.GustJar, Items.BombBag) CrenelCaveDownstairs; Minor; 26-07-00; Locations.AccessCrenel, Items.BombBag CrenelHeartCaveLeft; Minor; 26-08-00; Locations.AccessCrenel, Items.BombBag -CrenelHeartCaveRight; Minor; 26-08-01; Locations.AccessCrenel, Items.BombBag -CrenelGripScrub; PurchaseItem; 0CC0A8; Locations.AccessCrenel, Items.Shield, Items.BombBag +CrenelHeartCaveRight; Major; 26-08-01; Locations.AccessCrenel, Items.BombBag; Items.GripRing # Temp +#CrenelGripScrub; PurchaseItem; 0CC0A8; Locations.AccessCrenel, Items.Shield, Items.BombBag GraybladeLeft; ScrollItem; 25-00-00; Locations.AccessCrenel, Helpers.CanSplit2 GraybladeRight; ScrollItem; 25-00-01; Locations.AccessCrenel, Helpers.CanSplit2 CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing, (|Items.GustJar, Helpers.HasLightBow))) @@ -134,16 +134,16 @@ CloudsFreeChest; Major; 08-01-00; Locations.AccessClouds; Items.KinstoneX.Yellow #CloudsSouthKill; Major; ; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts); Items.KinstoneX.YellowTornadoProng CloudsSouthMiddle; Major; 08-01-01; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts); Items.KinstoneX.YellowTornadoProng CloudsWestBottom; Major; 08-01-02; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts); Items.KinstoneX.YellowTornadoProng -CloudsWestLeft; Minor; 08-01-03; Locations.AccessClouds, Items.MoleMitts -CloudsWestRight; Minor; 08-01-04; Locations.AccessClouds, Items.MoleMitts +CloudsWestLeft; Major; 08-01-03; Locations.AccessClouds, Items.MoleMitts; Items.KinstoneX.YellowTornadoProng # Temp +CloudsWestRight; Major; 08-01-04; Locations.AccessClouds, Items.MoleMitts; Items.KinstoneX.YellowTornadoProng # Temp CloudsSouthLeft; Minor; 08-01-05; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts) CloudsSouthRight; Minor; 08-01-06; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts) -AccessUpperClouds; Helper;; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts), Items.KinstoneX.YellowTornadoProng::3 -TowerBottomRight; Minor; 30-00-00; Locations.AccessUpperClouds -TowerBottomLeft; Minor; 30-00-01; Locations.AccessUpperClouds +AccessUpperClouds; Helper;; Locations.AccessClouds, (|Items.RocsCape, Items.MoleMitts), Items.KinstoneX.YellowTornadoProng::5 +TowerBottomRight; Major; 30-00-00; Locations.AccessUpperClouds; Items.Flippers # Temp +TowerBottomLeft; Major; 30-00-01; Locations.AccessUpperClouds; Items.PowerBracelets # Temp TowerF2; Minor; 30-01-00; Locations.AccessUpperClouds -GregalOne; Half; 014C5A; Locations.AccessUpperClouds, Items.GustJar +#GregalOne; Half; 014C5A; Locations.AccessUpperClouds, Items.GustJar GregalTwo; Half; 014CBC; Locations.AccessUpperClouds, Items.GustJar TowerRightBed; Minor; 30-02-00; Locations.AccessUpperClouds TowerMiddleBed; Minor; 30-02-01; Locations.AccessUpperClouds @@ -205,24 +205,28 @@ FortressPrize:Prizes; Half; 09C9E6; Locations.CompleteFortress # Temple of Droplets Locations AccessDroplets:Droplets; Helper;; Locations.AccessHylia, (|Items.Flippers, Items.RocsCape) -#DropletsFirstIceblock:Droplets; ; DungeonItem; Locations.AccessDroplets -#DropletsSecondIceblock:Droplets; ; DungeonItem; Locations.AccessDroplets, Helpers.DropletsOpenDoors -#DropletsEastLever:Droplets; Helper;; Locations.AccessDroplets, Items.BigKey:Droplets, Helpers.DropletsOpenDoors, (|Items.GustJar, (&Items.LanternOff, Items.RocsCape)), Helpers.CanSplit2 -#DropletsWestLever:Droplets; Helper;; Locations.AccessDroplets, Items.BigKey:Droplets, Helpers.DropletsOpenDoors, Items.LanternOff, Helpers.CanSplit2 -#DropletsOpenDoors:Droplets; Helper;; Items.SmallKey:Droplets:2, Items.GripRing, Items.Flippers, (|Items.GustJar, Items.RocsCape), Items.BombBag, Items.LanternOff, Helpers.HasSword -#DropletsEastFirst:Droplets; Minor; 60-09-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) -#DropletsIceMaze:Droplets; Minor; 60-0A-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) -#DropletsOverhang:Droplets; DungeonItem; 60-0D-00; Locations.AccessDroplets, Items.BigKey:Droplets +# Temporary modification +DropletsFirstIceblockConst:Droplets; Unshuffled; ; Locations.AccessDroplets; Items.SmallKey:Droplets +DropletsSecondIceblockConst:Droplets; Unshuffled; ; Locations.AccessDroplets; Items.BigKey:Droplets +# Eventual replacement +#DropletsFirstIceblock:Droplets; DungeonItem; ; Locations.AccessDroplets; Items.SmallKey:Droplets +#DropletsSecondIceblock:Droplets; DungeonItem; ; Locations.AccessDroplets, Helpers.DropletsOpenDoors; Items.BigKey:Droplets +DropletsEastLever:Droplets; Helper;; Locations.AccessDroplets, Items.BigKey:Droplets, Helpers.DropletsOpenDoors, (|Items.GustJar, (&Items.LanternOff, Items.RocsCape)), Helpers.CanSplit2 +DropletsWestLever:Droplets; Helper;; Locations.AccessDroplets, Items.BigKey:Droplets, Helpers.DropletsOpenDoors, Items.LanternOff, Helpers.CanSplit2 +DropletsOpenDoors:Droplets; Helper;; Items.SmallKey:Droplets:2, Items.GripRing, Items.Flippers, (|Items.GustJar, Items.RocsCape), Items.BombBag, Items.LanternOff, Helpers.HasSword +DropletsEastFirst:Droplets; Minor; 60-09-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) +DropletsIceMaze:Droplets; Minor; 60-0A-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) +DropletsOverhang:Droplets; DungeonItem; 60-0D-00; Locations.AccessDroplets, Items.BigKey:Droplets DropletsBluChu:Droplets; Major; 60-10-01; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever), Helpers.DropletsOpenDoors -#DropletsBasement:Droplets; DungeonItem; 60-11-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) -#DropletsFrozenIcePlain:Droplets; Minor; 60-28-00; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors -#DropletsFreeIcePlain:Droplets; Minor; 60-28-01; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors -#DropletsDarkMazeRight:Droplets; Minor; 60-2B-01; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff -#DropletsDarkMazeLeft:Droplets; Minor; 60-2B-02; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff -#DropletsDarkMazeMiddle:Droplets; Minor; 60-2B-03; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff -#DropletsPostTwinFrozen:Droplets; Minor; 60-2D-00; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors -#DropletsPreviewFrozen:Droplets; Minor; 60-32-00; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff -#DropletsIceWiggler:Droplets; DungeonItem; 60-32-01; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors +DropletsBasement:Droplets; DungeonItem; 60-11-00; Locations.AccessDroplets, Items.BigKey:Droplets, (|Items.LanternOff, Locations.DropletsEastLever) +DropletsFrozenIcePlain:Droplets; Minor; 60-28-00; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors +DropletsFreeIcePlain:Droplets; Minor; 60-28-01; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors +DropletsDarkMazeRight:Droplets; Minor; 60-2B-01; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff +DropletsDarkMazeLeft:Droplets; Minor; 60-2B-02; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff +DropletsDarkMazeMiddle:Droplets; Minor; 60-2B-03; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff +DropletsPostTwinFrozen:Droplets; Minor; 60-2D-00; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors +DropletsPreviewFrozen:Droplets; Minor; 60-32-00; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff +DropletsIceWiggler:Droplets; DungeonItem; 60-32-01; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors CompleteDroplets:Droplets; Helper;; Locations.DropletsEastLever, Locations.DropletsWestLever, Items.LanternOff, Items.HasSword # Palace of Winds Locations From 515c29425023463695c44023379319482210e745 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Sat, 27 Jul 2019 23:29:22 -0600 Subject: [PATCH 26/41] Fix logic bugs, fix logic parser --- Randomizer/Dependency.cs | 60 ++++++++++++++++++++++++++++++++++++++-- Randomizer/Shuffler.cs | 27 ++++++++++++++---- Resources/default.logic | 53 ++++++++++++++++++----------------- UI/MainWindow.cs | 33 +++++++++++++--------- 4 files changed, 125 insertions(+), 48 deletions(-) diff --git a/Randomizer/Dependency.cs b/Randomizer/Dependency.cs index 3dacf6b0..f153ab25 100644 --- a/Randomizer/Dependency.cs +++ b/Randomizer/Dependency.cs @@ -17,9 +17,9 @@ public static List GetDependencies(string logic) { List dependencies = new List(); - // Match: comma, capture and match anything between - string regexPattern = @"(?:,|^)\(([&|](?:[^()]|(?

\()|(?<-p>\)))+)\)(?:,|$)|,"; - string[] subLogic = Regex.Split(logic, regexPattern); + + List subLogic = SplitDependencies(logic); + foreach (string sequence in subLogic) { if (string.IsNullOrEmpty(sequence)) @@ -89,6 +89,10 @@ public static List GetDependencies(string logic) ItemDependency itemDependency = new ItemDependency(new Item(type, subType, dungeon), count); dependencies.Add(itemDependency); } + else + { + throw new ShuffleException($"Item {dependencyParts[1]} could not be found!"); + } break; } break; @@ -98,6 +102,56 @@ public static List GetDependencies(string logic) return dependencies; } + public static List SplitDependencies(string logic) + { + List subLogic = new List(); + + int parenCount = 0; + string subsection = ""; + + foreach (char character in logic) + { + switch (character) + { + case ',': + if (parenCount == 0) + { + // Not within parentheses, should start a new logic string + subLogic.Add(subsection); + subsection = ""; + } + else + { + // Comma is within parentheses, so it will be parsed as a compound dependency + subsection += ','; + } + break; + case '(': + // Open parenthesis, so everything until it's closed should be one block + parenCount++; + break; + case ')': + // Close parenthesis, so reduce the number of open blocks by 1 + parenCount--; + break; + default: + // Not a special case, so it should be added to the current logic subsection + subsection += character; + break; + } + } + + // Add final section to logic + subLogic.Add(subsection); + + if (parenCount > 0) + { + throw new ShuffleException($"Parentheses could not be parsed correctly! Make sure none of them are mismatched."); + } + + return subLogic; + } + public virtual bool DependencyFulfilled(List availableItems, List locations) { return false; diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index 9a017144..44e6d33c 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -186,6 +186,14 @@ public void RandomizeLocations() unfilledLocations.Shuffle(RNG); FillLocations(unplacedItems, unfilledLocations); + // Before shuffling Minor locations in, create a playthrough log + List finalMajorItems = GetAvailableItems(new List(), true); + + if (!new LocationDependency("BeatVaati").DependencyFulfilled(finalMajorItems, Locations)) + { + throw new ShuffleException($"Randomization succeded, but could not beat Vaati!"); + } + unfilledLocations.Shuffle(RNG); FastFillLocations(MinorItems.ToList(), unfilledLocations); @@ -275,16 +283,14 @@ private void FastFillLocations(List items, List locations) } // Gets all the available items with a given item set, looping until there are no more items left to get - private List GetAvailableItems(List preAvailableItems) + private List GetAvailableItems(List preAvailableItems, bool printPlaythrough = false) { List availableItems = preAvailableItems.ToList(); - List filledLocations = Locations.Where(location => - { - return location.Filled && location.Type != Location.LocationType.Helper && location.Type != Location.LocationType.Untyped; - }).ToList(); + List filledLocations = Locations.Where(location => location.Filled && location.Type != Location.LocationType.Helper && location.Type != Location.LocationType.Untyped).ToList(); int previousSize; + int sphereCount = 1; do { // Doesn't touch the cache to prevent incorrect caching @@ -293,7 +299,16 @@ private List GetAvailableItems(List preAvailableItems) filledLocations.RemoveAll(location => accessibleLocations.Contains(location)); - availableItems.AddRange(Location.GetItems(accessibleLocations)); + if (printPlaythrough) + { + accessibleLocations.ForEach(location => Console.WriteLine($"Sphere {sphereCount}: {location.Contents.Type} sub {location.Contents.SubValue} at {location.Name}\n")); + } + + List newItems = Location.GetItems(accessibleLocations); + + availableItems.AddRange(newItems); + + sphereCount++; } while (previousSize > 0); diff --git a/Resources/default.logic b/Resources/default.logic index 7b79634c..5d4a25b9 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -50,9 +50,10 @@ TownDiggingLeft; Minor; 0F-00-02; Items.MoleMitts AccessEastField; Helper;; (|Helpers.HasSword, Items.BombBag, Helpers.HasLightBow, Items.Ocarina) SouthKeeseCave; Minor; 32-13-00; Locations.AccessEastField, Items.BombBag AboveHPHole; Minor; 27-00-00; Locations.AccessEastField, (|Items.Ocarina, Items.LonLonKey), (|Items.PacciCane, Items.RocsCape) +LonLonPot; Half; 0F2C9B; Locations.AccessEastField LonLonCave; Major; 32-0C-00; (|Items.Ocarina, Items.LonLonKey), Helpers.CanSplit2; Items.Bottle1 #Temp -AccessWestField; Helper;; (|(&Helpers.HasSword, Items.SpinAttack), Items.Flippers, (&Items.BombBag, Helpers.CanSplit3)) +AccessWestField; Helper;; (|Items.RocsCape, (&Helpers.HasSword, Items.SpinAttack), Items.Flippers, (&Items.BombBag, Helpers.CanSplit3)) #BottleScrub; PurchaseItem; 0CC0C0; Items.Shield, Items.BombBag, Locations.AccessWestField # Minish Woods Locations @@ -71,13 +72,13 @@ AccessCrenel; Helper;; Locations.AccessWestField, Helpers.HasBottle, (|Items.Bom #CrenelLowerScrub; PurchaseItem; 0CC09C; Locations.AccessWestField, Helpers.HasBottle CrenelVineHole; Minor; 35-00-00; Locations.AccessCrenel, (|Items.GustJar, Items.BombBag) CrenelMinishHouse; Minor; 27-03-00; Locations.AccessCrenel, (|Items.GustJar, Items.BombBag) -CrenelCaveDownstairs; Minor; 26-07-00; Locations.AccessCrenel, Items.BombBag +CrenelCaveDownstairs; Minor; 26-07-00; Locations.AccessCrenel, Items.BombBag, (|Items.GripRing, Helpers.HasBottle) CrenelHeartCaveLeft; Minor; 26-08-00; Locations.AccessCrenel, Items.BombBag CrenelHeartCaveRight; Major; 26-08-01; Locations.AccessCrenel, Items.BombBag; Items.GripRing # Temp #CrenelGripScrub; PurchaseItem; 0CC0A8; Locations.AccessCrenel, Items.Shield, Items.BombBag -GraybladeLeft; ScrollItem; 25-00-00; Locations.AccessCrenel, Helpers.CanSplit2 -GraybladeRight; ScrollItem; 25-00-01; Locations.AccessCrenel, Helpers.CanSplit2 -CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.PacciCane, (&Items.GripRing, (|Items.GustJar, Helpers.HasLightBow))) +GraybladeLeft; Minor; 25-00-00; Locations.AccessCrenel, Helpers.CanSplit2 +GraybladeRight; Minor; 25-00-01; Locations.AccessCrenel, Helpers.CanSplit2 +CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.GripRing, Helpers.HasBottle), (|Items.PacciCane, (&Items.GripRing, (|Items.GustJar, Helpers.HasLightBow))) Melari; Major; 00D26E; Locations.CompleteCoF # Castor Wilds Locations @@ -100,9 +101,9 @@ AccessHylia; Helper;; Locations.AccessEastField, (|Items.Ocarina, Items.LonLonKe HyliaNorthMinishHole; Major; 35-07-00; Locations.AccessHylia, Items.Flippers, Items.PegasusBoots; Items.PegasusBoots #Temp AccessSouthHylia; Helper;; Locations.AccessHylia, (|Items.RocsCape, Items.Flippers, (&Items.MoleMitts, Items.PacciCane, (|Items.Ocarina, Items.LonLonKey))) -WitchDiggingCaveRight; Minor; 0C-00-00; (|Locations.AccessMinishWoods, Items.MoleMitts, (|Items.Flippers, Items.RocsCape, (&Items.LonLonKey, Items.PacciCane)) +WitchDiggingCaveRight; Minor; 0C-00-00; Locations.AccessMinishWoods, Items.MoleMitts, (|Items.Flippers, Items.RocsCape, (&Items.LonLonKey, Items.PacciCane)) -AccessTreasureCave; Helper;; Locations.AccessHylia, Items.RocsCape +AccessTreasureCave; Helper;; Locations.AccessHylia, Items.MoleMitts, Items.RocsCape TreasureCave0; Minor; 19-01-00; Locations.AccessTreasureCave TreasureCave1; Minor; 19-01-01; Locations.AccessTreasureCave TreasureCave2; Minor; 19-01-02; Locations.AccessTreasureCave @@ -118,14 +119,14 @@ DampeKey; Major; 0096B6; Locations.AccessValley #KingGift:Prizes; Major; 00DA5A; Locations.AccessValley, Items.GraveyardKey, Helpers.CanSplit3, Items.PegasusBoots; Items.KinstoneX.YellowCrown # Veil Falls Locations -AccessFallsNorth; Helper;; Items.BombBag, Items.LanternOff, Locations.AccessValley, Items.GraveyardKey, Helpers.CanSplit3, Items.PegasusBoots#, Items.KinstoneX.YellowCrown Temp removed -FallsBehindWall; Minor; 33-05-00; Locations.AccessFallsNorth, Items.BombBag +AccessFallsNorth; Helper;; Items.BombBag, Items.KinstoneX.YellowCrown +FallsBehindWall; Major; 33-05-00; Locations.AccessFallsNorth, Items.BombBag; Items.KinstoneX.YellowCrown ; Temp FallsCliff; Minor; 0A-00-00; Locations.AccessFallsNorth, Items.BombBag, Helpers.CanSplit3 FallsTopCaveBomb; Minor; 33-02-00; Locations.AccessFallsNorth, Items.GripRing, Items.BombBag FallsTopCaveFree; Minor; 33-00-00; Locations.AccessFallsNorth, Items.GripRing -AccessFallsSouth; Helper;; Locations.AccessEastField, Items.Pacci -FallsLowerCaveLeft; Minor; 16-00-01; Locations.AccessEastField, Items.Pacci +AccessFallsSouth; Helper;; Locations.AccessEastField, Items.PacciCane +FallsLowerCaveLeft; Minor; 16-00-01; Locations.AccessEastField, Items.PacciCane, Items.MoleMitts # Cloud Tops Locations AccessClouds; Helper;; Locations.AccessFallsNorth, Items.GripRing @@ -186,7 +187,7 @@ CompleteCoF:FlameCave; Helper;; Locations.CoFAccess, Items.PacciCane, Items.Smal # Fortress of Winds Locations AccessFortress:Fortress; Helper;; Locations.AccessWilds, Items.KinstoneX.YellowTotemProng::3 -FortressEntrance:Fortress; Minor; 18-00-00; Locations.AccessFortress, Items.MoleMitts +FortressEntrance:Fortress; Major; 18-00-00; Locations.AccessFortress, Items.MoleMitts; Items.Ocarina # Temp FortressOutsideF2Left:Fortress; Minor; 18-01-00; Locations.AccessFortress, Items.MoleMitts FortressOutsideF2Middle:Fortress; Minor; 18-01-01; Locations.AccessFortress, Items.MoleMitts FortressOutsideF2Right:Fortress; Minor; 18-01-02; Locations.AccessFortress, Items.MoleMitts, Helpers.HasBow, Helpers.CanSplit2 @@ -201,7 +202,7 @@ FortressSkullRoomLeft:Fortress; Minor; 58-1D-00; Locations.AccessFortress FortressSkullRoomRight:Fortress; Minor; 58-1D-01; Locations.AccessFortress FortressWizrobes:Fortress; Minor; 58-23-00; Locations.AccessFortress, Items.MoleMitts CompleteFortress:Fortress; Helper;; Locations.AccessFortress, Items.MoleMitts, Helpers.CanSplit2, Items.Bow, Items.BigKey:Fortress -FortressPrize:Prizes; Half; 09C9E6; Locations.CompleteFortress +#FortressPrize:Prizes; Half; 09C9E6; Locations.CompleteFortress # Temple of Droplets Locations AccessDroplets:Droplets; Helper;; Locations.AccessHylia, (|Items.Flippers, Items.RocsCape) @@ -227,25 +228,25 @@ DropletsDarkMazeMiddle:Droplets; Minor; 60-2B-03; Locations.AccessDroplets, Item DropletsPostTwinFrozen:Droplets; Minor; 60-2D-00; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors DropletsPreviewFrozen:Droplets; Minor; 60-32-00; Locations.AccessDroplets, Items.BigKey:Droplets, Items.LanternOff DropletsIceWiggler:Droplets; DungeonItem; 60-32-01; Locations.AccessDroplets, Items.GustJar, Items.BigKey:Droplets, Helpers.DropletsOpenDoors -CompleteDroplets:Droplets; Helper;; Locations.DropletsEastLever, Locations.DropletsWestLever, Items.LanternOff, Items.HasSword +CompleteDroplets:Droplets; Helper;; Locations.DropletsEastLever, Locations.DropletsWestLever, Items.LanternOff, Helpers.HasSword # Palace of Winds Locations AccessPalace:Palace; Helper;; Locations.AccessUpperClouds, Helpers.CanSplit3, (|Items.RocsCape, Items.BombBag, Items.GustJar, Helpers.HasBoomerang, Helpers.HasBow) -PalacePreBigDoor:Palace; DungeonItem; 70-01-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.SmallKey:Palace:2 -PalacePreBoss:Palace; DungeonItem; 70-03-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace, Items.SmallKey:Palace:3 -PalaceDetour:Palace; Minor; 70-04-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace, Items.SmallKey:Palace:4 -PalaceFanLoop:Palace; DungeonItem; 70-07-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane +PalacePreBigDoor:Palace; DungeonItem; 70-01-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.PowerBracelets, Items.SmallKey:Palace:2 +PalacePreBoss:Palace; DungeonItem; 70-03-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.PowerBracelets, Items.BigKey:Palace, Items.SmallKey:Palace:3 +PalaceDetour:Palace; Minor; 70-04-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.PowerBracelets, Items.BigKey:Palace, Items.SmallKey:Palace:4 +PalaceFanLoop:Palace; DungeonItem; 70-07-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.PowerBracelets PalaceWideGap:Palace; Minor; 70-0F-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane -PalaceBlockMaze:Palace; Minor; 70-10-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace, Items.SmallKey:Palace:4 -PalaceSwitchHit:Palace; Minor; 70-15-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace, Items.SmallKey:Palace:2 -PalaceFirerobeTrio:Palace; DungeonItem; 70-1C-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace, Items.SmallKey:Palace:2 -PalaceTwinWizrobes:Palace; Minor; 70-29-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace, Items.SmallKey:Palace:2 -PalaceManyRollers:Palace; DungeonItem; 70-2B-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace +PalaceBlockMaze:Palace; Minor; 70-10-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.PowerBracelets, Items.BigKey:Palace, Items.SmallKey:Palace:4 +PalaceSwitchHit:Palace; Minor; 70-15-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.PowerBracelets, Items.BigKey:Palace, Items.SmallKey:Palace:2 +PalaceFirerobeTrio:Palace; DungeonItem; 70-1C-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.PowerBracelets, Items.BigKey:Palace, Items.SmallKey:Palace:2 +PalaceTwinWizrobes:Palace; Minor; 70-29-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.PowerBracelets, Items.BigKey:Palace, Items.SmallKey:Palace:2 +PalaceManyRollers:Palace; DungeonItem; 70-2B-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.PowerBracelets, Items.BigKey:Palace PalaceWizrobeKill:Palace; Major; 70-2C-00; Locations.AccessPalace, (|Items.RocsCape, Items.BombBag, Helpers.HasBoomerang) PalaceFirstGrate:Palace; Major; 70-2D-00; Locations.AccessPalace, Items.RocsCape -PalaceDarkBig:Palace; DungeonItem; 70-32-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace -PalaceDarkSmall:Palace; DungeonItem; 70-32-01; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace -CompletePalace:Palace; Helper;; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.BigKey:Palace, Items.SmallKey:Palace:4 +PalaceDarkBig:Palace; DungeonItem; 70-32-00; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.PowerBracelets, Items.BigKey:Palace +PalaceDarkSmall:Palace; DungeonItem; 70-32-01; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.PowerBracelets, Items.BigKey:Palace +CompletePalace:Palace; Helper;; Locations.AccessPalace, Items.RocsCape, Items.PacciCane, Items.LanternOff, Items.PowerBracelets, Items.BigKey:Palace, Items.SmallKey:Palace:4 # Skipping ahead to DHC so the game can be considered beatable... BeatVaati; Helper;; Locations.CompleteDeepwood, Locations.CompleteCoF, Locations.CompleteDroplets, Locations.CompletePalace, Helpers.CanSplit4, Items.BombBag, Helpers.HasBow, Items.RocsCape, Items.LanternOff, Items.GustJar, Items.PacciCane \ No newline at end of file diff --git a/UI/MainWindow.cs b/UI/MainWindow.cs index d495f135..172d47db 100644 --- a/UI/MainWindow.cs +++ b/UI/MainWindow.cs @@ -83,22 +83,29 @@ private void LoadRom() shuffler = new Shuffler(Path.GetDirectoryName(ROM.Instance.path)); - if (customLogicCheckBox.Checked) - { - shuffler.LoadLocations(customLogicPath.Text); - } - else - { - shuffler.LoadLocations(null); - } - - if (customPatchCheckBox.Checked) + try { - shuffler.PatchRom(customPatchPath.Text); + if (customLogicCheckBox.Checked) + { + shuffler.LoadLocations(customLogicPath.Text); + } + else + { + shuffler.LoadLocations(null); + } + + if (customPatchCheckBox.Checked) + { + shuffler.PatchRom(customPatchPath.Text); + } + else + { + shuffler.PatchRom(null); + } } - else + catch (ShuffleException error) { - shuffler.PatchRom(null); + MessageBox.Show(error.Message); } } From fdfd21f3d7cc69424881bca8879b65d64a918e91 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Mon, 29 Jul 2019 05:48:59 -0600 Subject: [PATCH 27/41] Fix nested logic parsing, print subtype --- Randomizer/Dependency.cs | 12 ++++++++++++ Randomizer/Shuffler.cs | 2 +- Resources/default.logic | 2 +- 3 files changed, 14 insertions(+), 2 deletions(-) diff --git a/Randomizer/Dependency.cs b/Randomizer/Dependency.cs index f153ab25..91c30191 100644 --- a/Randomizer/Dependency.cs +++ b/Randomizer/Dependency.cs @@ -127,12 +127,24 @@ public static List SplitDependencies(string logic) } break; case '(': + // Nested parentheses should be in the subsection + if (parenCount > 0) + { + subsection += '('; + } + // Open parenthesis, so everything until it's closed should be one block parenCount++; break; case ')': // Close parenthesis, so reduce the number of open blocks by 1 parenCount--; + + // Nested parentheses should be in the subsection + if (parenCount > 0) + { + subsection += ')'; + } break; default: // Not a special case, so it should be added to the current logic subsection diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index 44e6d33c..df2c81e5 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -260,7 +260,7 @@ private List FillLocations(List items, List locations, int locationIndex = RNG.Next(availableLocations.Count); availableLocations[locationIndex].Fill(item); - Console.WriteLine($"Placed {item.Type.ToString()} at {availableLocations[locationIndex].Name} with {items.Count} items remaining\n"); + Console.WriteLine($"Placed {item.Type.ToString()} subtype {StringUtil.AsStringHex2(item.SubValue)} at {availableLocations[locationIndex].Name} with {items.Count} items remaining\n"); locations.Remove(availableLocations[locationIndex]); diff --git a/Resources/default.logic b/Resources/default.logic index 5d4a25b9..4c17f9d5 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -79,7 +79,7 @@ CrenelHeartCaveRight; Major; 26-08-01; Locations.AccessCrenel, Items.BombBag; It GraybladeLeft; Minor; 25-00-00; Locations.AccessCrenel, Helpers.CanSplit2 GraybladeRight; Minor; 25-00-01; Locations.AccessCrenel, Helpers.CanSplit2 CrenelBlockChest; Minor; 26-03-00; Locations.AccessCrenel, (|Items.GripRing, Helpers.HasBottle), (|Items.PacciCane, (&Items.GripRing, (|Items.GustJar, Helpers.HasLightBow))) -Melari; Major; 00D26E; Locations.CompleteCoF +#Melari; Major; 00D26E; Locations.CompleteCoF # Castor Wilds Locations AccessWilds; Helper;; Locations.AccessWestField, Helpers.CanSplit2, (|Items.PegasusBoots, Items.RocsCape) From f32fe90e22ed1d5949dde52d379a75b7fe4acdac Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Mon, 29 Jul 2019 05:54:54 -0600 Subject: [PATCH 28/41] Make Kinstone spoilers more clear --- Randomizer/Shuffler.cs | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index df2c81e5..8f2a28d1 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -240,6 +240,11 @@ private List FillLocations(List items, List locations, int itemIndex = RNG.Next(items.Count); Item item = items[itemIndex]; Console.WriteLine($"Placing: {item.Type.ToString()}"); + if (item.Type == ItemType.KinstoneX) + { + Console.WriteLine($"Type: {item.Kinstone.ToString()}"); + } + if (item.Dungeon != "") { Console.WriteLine($"Dungeon: {item.Dungeon}"); @@ -301,7 +306,7 @@ private List GetAvailableItems(List preAvailableItems, bool printPla if (printPlaythrough) { - accessibleLocations.ForEach(location => Console.WriteLine($"Sphere {sphereCount}: {location.Contents.Type} sub {location.Contents.SubValue} at {location.Name}\n")); + accessibleLocations.ForEach(location => Console.WriteLine($"Sphere {sphereCount}: {location.Contents.Type} sub {StringUtil.AsStringHex2(location.Contents.SubValue)} at {location.Name}\n")); } List newItems = Location.GetItems(accessibleLocations); From a666fa1dd23cbfe200f8660ec6cceabea06fc667 Mon Sep 17 00:00:00 2001 From: wjg Date: Mon, 29 Jul 2019 21:58:22 +0200 Subject: [PATCH 29/41] fix some duplicate chest id's wildsdiggingcave had had the left chest twice, fortressoutsidef3 had the left chest twice --- Resources/default.logic | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/Resources/default.logic b/Resources/default.logic index 4c17f9d5..63d12881 100644 --- a/Resources/default.logic +++ b/Resources/default.logic @@ -88,7 +88,7 @@ WildsDarknutCave; Major; 2B-00-00; Locations.AccessWilds, Helpers.HasSword; Item WildsDekuCaveRight; Major; 2A-01-00; Locations.AccessWilds, Helpers.HasBow; Items.KinstoneX.YellowTotemProng WildsMulldozerHole; Major; 27-06-00; Locations.AccessWilds, (|Items.Flippers, Items.GustJar) WildsDiggingCave1; Minor; 17-00-00; Locations.AccessWilds, Items.MoleMitts -WildsDiggingCave2; Minor; 17-00-00; Locations.AccessWilds, Items.MoleMitts +WildsDiggingCave2; Minor; 17-00-01; Locations.AccessWilds, Items.MoleMitts WildsTopChest; Minor; 04-00-00; Locations.AccessWilds, Helpers.HasBow RuinsBombCave; Minor; 2A-02-00; Locations.AccessWilds, Items.KinstoneX.YellowTotemProng::3, Items.BombBag RuinsMinishCave; Minor; 27-07-00; Locations.AccessWilds, Items.KinstoneX.YellowTotemProng::3, Helpers.HasSword @@ -192,7 +192,7 @@ FortressOutsideF2Left:Fortress; Minor; 18-01-00; Locations.AccessFortress, Items FortressOutsideF2Middle:Fortress; Minor; 18-01-01; Locations.AccessFortress, Items.MoleMitts FortressOutsideF2Right:Fortress; Minor; 18-01-02; Locations.AccessFortress, Items.MoleMitts, Helpers.HasBow, Helpers.CanSplit2 FortressOutsideF3Left:Fortress; Minor; 18-02-00; Locations.AccessFortress, Items.MoleMitts -FortressOutsideF3Right:Fortress; Minor; 18-02-00; Locations.AccessFortress, Items.MoleMitts +FortressOutsideF3Right:Fortress; Minor; 18-02-01; Locations.AccessFortress, Items.MoleMitts FortressOutsideBigChest:Fortress; Major; 18-03-00; Locations.AccessFortress, Items.BombBag, Helpers.HasBow, Helpers.CanSplit2 FortressOutsideSmallChest:Fortress; Minor; 18-03-01; Locations.FortressOutsideBigChest, Items.MoleMitts FortressEyegoreKill:Fortress; DungeonItem; 58-00-00; Locations.AccessFortress, Helpers.HasBow, Helpers.CanSplit2 From 26d5656b7a290ba2b597390d91c5c7d38dfa77b3 Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Wed, 31 Jul 2019 22:15:02 +0100 Subject: [PATCH 30/41] change lang version --- MinishRandomizer.csproj | 1 + 1 file changed, 1 insertion(+) diff --git a/MinishRandomizer.csproj b/MinishRandomizer.csproj index 524c2d5e..dcf409ee 100644 --- a/MinishRandomizer.csproj +++ b/MinishRandomizer.csproj @@ -21,6 +21,7 @@ DEBUG;TRACE prompt 4 + latest AnyCPU From 9cf8169223561d79c7c10c562b36660f815b4995 Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Wed, 31 Jul 2019 22:22:07 +0100 Subject: [PATCH 31/41] add colorzcore --- .gitmodules | 3 +++ Vendor/ColorzCore | 1 + 2 files changed, 4 insertions(+) create mode 100644 .gitmodules create mode 160000 Vendor/ColorzCore diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 00000000..5c6e65f8 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Vendor/ColorzCore"] + path = Vendor/ColorzCore + url = https://github.com/minishmaker/ColorzCore.git diff --git a/Vendor/ColorzCore b/Vendor/ColorzCore new file mode 160000 index 00000000..c3a11a78 --- /dev/null +++ b/Vendor/ColorzCore @@ -0,0 +1 @@ +Subproject commit c3a11a785ef573256428134702dc24db86683db3 From 6145baaacb789212f2b05545553db42d37a91686 Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Wed, 31 Jul 2019 22:47:39 +0100 Subject: [PATCH 32/41] include colourzcore setup --- MinishRandomizer.csproj | 7 ++++++- Vendor/Language Raws/Pointer.txt | 9 +++++++++ Vendor/Language Raws/Raw_code.txt | 18 ++++++++++++++++++ 3 files changed, 33 insertions(+), 1 deletion(-) create mode 100644 Vendor/Language Raws/Pointer.txt create mode 100644 Vendor/Language Raws/Raw_code.txt diff --git a/MinishRandomizer.csproj b/MinishRandomizer.csproj index dcf409ee..fc1b3db5 100644 --- a/MinishRandomizer.csproj +++ b/MinishRandomizer.csproj @@ -92,6 +92,11 @@ - + + + {b98f7ccf-9caa-406e-88d7-2040fa99f631} + ColorzCore + + \ No newline at end of file diff --git a/Vendor/Language Raws/Pointer.txt b/Vendor/Language Raws/Pointer.txt new file mode 100644 index 00000000..38a99f12 --- /dev/null +++ b/Vendor/Language Raws/Pointer.txt @@ -0,0 +1,9 @@ +##Makes a GBA pointer pointing to offset. +POIN, 0, 4, -game:FE6:FE7:FE8 -repeatable -priority:pointer -indexMode:8 +##Offset to point to. + Offset, 0, 4, -pointer:unknown + +##Makes a GBA pointer pointing to offset without requiring word alignment. +POIN2, 0, 4, -game:FE6:FE7:FE8 -repeatable -priority:pointer -indexMode:8 -offsetMod:1 +##Offset to point to. + Offset, 0, 4, -pointer:unknown diff --git a/Vendor/Language Raws/Raw_code.txt b/Vendor/Language Raws/Raw_code.txt new file mode 100644 index 00000000..a59cb3c8 --- /dev/null +++ b/Vendor/Language Raws/Raw_code.txt @@ -0,0 +1,18 @@ + +##4 byte raw value +WORD, 0, 4, -game:FE6:FE7:FE8 -repeatable -priority:low -indexMode:8 -offsetMod:4 +##Value + Value, 0, 4 + +##2 byte raw value +SHORT, 0, 2, -game:FE6:FE7:FE8 -repeatable -priority:low -indexMode:8 -offsetMod:2 +##Value + Value, 0, 2 + +##1 byte raw value +BYTE, 0, 1, -game:FE6:FE7:FE8 -repeatable -priority:low -indexMode:8 -offsetMod:1 +##Value + Value, 0, 1 + + + From 23f6ac2543d5cff4f8585ddbcd3476bf9c340d4a Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Wed, 31 Jul 2019 22:48:34 +0100 Subject: [PATCH 33/41] include patch files for opening up game --- MinishRandomizer.csproj | 3 + Resources/Patches/ROM Buildfile.event | 83 +++++++++++ Resources/Patches/asm/flagHack.dmp | Bin 0 -> 72 bytes Resources/Patches/asm/flagHack.s | 44 ++++++ Resources/Patches/asm/newGame.dmp | Bin 0 -> 44 bytes Resources/Patches/asm/newGame.s | 35 +++++ Resources/Patches/asm/textFlags.dmp | Bin 0 -> 60 bytes Resources/Patches/asm/textFlags.s | 41 +++++ Resources/Patches/gamefixes/asm/bowDozers.dmp | Bin 0 -> 24 bytes Resources/Patches/gamefixes/asm/bowDozers.s | 8 + .../Patches/gamefixes/asm/cloudTopsHouse.dmp | Bin 0 -> 20 bytes .../Patches/gamefixes/asm/cloudTopsHouse.s | 8 + .../Patches/gamefixes/asm/dampeHouse.dmp | Bin 0 -> 72 bytes Resources/Patches/gamefixes/asm/dampeHouse.s | 36 +++++ Resources/Patches/gamefixes/asm/eenie.dmp | Bin 0 -> 24 bytes Resources/Patches/gamefixes/asm/eenie.s | 16 ++ .../Patches/gamefixes/asm/graveyardGate.dmp | Bin 0 -> 64 bytes .../Patches/gamefixes/asm/graveyardGate.s | 30 ++++ Resources/Patches/gamefixes/installer.event | 33 ++++ .../improvements/asm/ezloPleaseStop.dmp | Bin 0 -> 36 bytes .../Patches/improvements/asm/ezloPleaseStop.s | 22 +++ .../improvements/asm/skipFusionCutscene.dmp | Bin 0 -> 48 bytes .../improvements/asm/skipFusionCutscene.s | 31 ++++ .../Patches/improvements/installer.event | 21 +++ Resources/Patches/logicfixes/asm/gregal.dmp | Bin 0 -> 88 bytes Resources/Patches/logicfixes/asm/gregal.s | 52 +++++++ .../Patches/logicfixes/asm/jabberNut1.dmp | Bin 0 -> 28 bytes Resources/Patches/logicfixes/asm/jabberNut1.s | 13 ++ .../Patches/logicfixes/asm/jabberNut2.dmp | Bin 0 -> 40 bytes Resources/Patches/logicfixes/asm/jabberNut2.s | 19 +++ .../Patches/logicfixes/asm/mushroomFlag.dmp | Bin 0 -> 40 bytes .../Patches/logicfixes/asm/mushroomFlag.s | 18 +++ .../Patches/logicfixes/asm/ocarinaFlag1.dmp | Bin 0 -> 32 bytes .../Patches/logicfixes/asm/ocarinaFlag1.s | 14 ++ .../Patches/logicfixes/asm/ocarinaFlag2.dmp | Bin 0 -> 28 bytes .../Patches/logicfixes/asm/ocarinaFlag2.s | 14 ++ .../Patches/logicfixes/asm/ocarinaFlag3.dmp | Bin 0 -> 28 bytes .../Patches/logicfixes/asm/ocarinaFlag3.s | 14 ++ Resources/Patches/logicfixes/asm/skipHack.dmp | Bin 0 -> 56 bytes Resources/Patches/logicfixes/asm/skipHack.s | 32 ++++ .../Patches/logicfixes/asm/splitHack.dmp | Bin 0 -> 76 bytes Resources/Patches/logicfixes/asm/splitHack.s | 44 ++++++ Resources/Patches/logicfixes/asm/stoneFix.dmp | Bin 0 -> 52 bytes Resources/Patches/logicfixes/asm/stoneFix.s | 36 +++++ Resources/Patches/logicfixes/asm/swordFix.dmp | Bin 0 -> 56 bytes Resources/Patches/logicfixes/asm/swordFix.s | 35 +++++ Resources/Patches/logicfixes/installer.event | 141 ++++++++++++++++++ Resources/Patches/newGame.event | 82 ++++++++++ 48 files changed, 925 insertions(+) create mode 100644 Resources/Patches/ROM Buildfile.event create mode 100644 Resources/Patches/asm/flagHack.dmp create mode 100644 Resources/Patches/asm/flagHack.s create mode 100644 Resources/Patches/asm/newGame.dmp create mode 100644 Resources/Patches/asm/newGame.s create mode 100644 Resources/Patches/asm/textFlags.dmp create mode 100644 Resources/Patches/asm/textFlags.s create mode 100644 Resources/Patches/gamefixes/asm/bowDozers.dmp create mode 100644 Resources/Patches/gamefixes/asm/bowDozers.s create mode 100644 Resources/Patches/gamefixes/asm/cloudTopsHouse.dmp create mode 100644 Resources/Patches/gamefixes/asm/cloudTopsHouse.s create mode 100644 Resources/Patches/gamefixes/asm/dampeHouse.dmp create mode 100644 Resources/Patches/gamefixes/asm/dampeHouse.s create mode 100644 Resources/Patches/gamefixes/asm/eenie.dmp create mode 100644 Resources/Patches/gamefixes/asm/eenie.s create mode 100644 Resources/Patches/gamefixes/asm/graveyardGate.dmp create mode 100644 Resources/Patches/gamefixes/asm/graveyardGate.s create mode 100644 Resources/Patches/gamefixes/installer.event create mode 100644 Resources/Patches/improvements/asm/ezloPleaseStop.dmp create mode 100644 Resources/Patches/improvements/asm/ezloPleaseStop.s create mode 100644 Resources/Patches/improvements/asm/skipFusionCutscene.dmp create mode 100644 Resources/Patches/improvements/asm/skipFusionCutscene.s create mode 100644 Resources/Patches/improvements/installer.event create mode 100644 Resources/Patches/logicfixes/asm/gregal.dmp create mode 100644 Resources/Patches/logicfixes/asm/gregal.s create mode 100644 Resources/Patches/logicfixes/asm/jabberNut1.dmp create mode 100644 Resources/Patches/logicfixes/asm/jabberNut1.s create mode 100644 Resources/Patches/logicfixes/asm/jabberNut2.dmp create mode 100644 Resources/Patches/logicfixes/asm/jabberNut2.s create mode 100644 Resources/Patches/logicfixes/asm/mushroomFlag.dmp create mode 100644 Resources/Patches/logicfixes/asm/mushroomFlag.s create mode 100644 Resources/Patches/logicfixes/asm/ocarinaFlag1.dmp create mode 100644 Resources/Patches/logicfixes/asm/ocarinaFlag1.s create mode 100644 Resources/Patches/logicfixes/asm/ocarinaFlag2.dmp create mode 100644 Resources/Patches/logicfixes/asm/ocarinaFlag2.s create mode 100644 Resources/Patches/logicfixes/asm/ocarinaFlag3.dmp create mode 100644 Resources/Patches/logicfixes/asm/ocarinaFlag3.s create mode 100644 Resources/Patches/logicfixes/asm/skipHack.dmp create mode 100644 Resources/Patches/logicfixes/asm/skipHack.s create mode 100644 Resources/Patches/logicfixes/asm/splitHack.dmp create mode 100644 Resources/Patches/logicfixes/asm/splitHack.s create mode 100644 Resources/Patches/logicfixes/asm/stoneFix.dmp create mode 100644 Resources/Patches/logicfixes/asm/stoneFix.s create mode 100644 Resources/Patches/logicfixes/asm/swordFix.dmp create mode 100644 Resources/Patches/logicfixes/asm/swordFix.s create mode 100644 Resources/Patches/logicfixes/installer.event create mode 100644 Resources/Patches/newGame.event diff --git a/MinishRandomizer.csproj b/MinishRandomizer.csproj index fc1b3db5..7785ac53 100644 --- a/MinishRandomizer.csproj +++ b/MinishRandomizer.csproj @@ -99,4 +99,7 @@ + + xcopy /y /d /s "$(ProjectDir)Resources\Patches" "$(TargetDir)Patches\" + \ No newline at end of file diff --git a/Resources/Patches/ROM Buildfile.event b/Resources/Patches/ROM Buildfile.event new file mode 100644 index 00000000..f39160a6 --- /dev/null +++ b/Resources/Patches/ROM Buildfile.event @@ -0,0 +1,83 @@ +#include "Extensions/Hack Installation.txt" + +//#define DEBUG 0 +//#define TEST 0 + +#define FreeSpace $EF3340 +ORG FreeSpace + +//#define neverSkipFusionCutscene 0 //define to never skip +#define alwaysSkipFusionCutscene 0 //set to non-0 to skip, set to 0 to skip on button held + +#ifdef TEST + PUSH + ORG $EDD8+2; BYTE 0x5C 0x70 //lady kinstone + ORG $DA58+2; BYTE 0x5C 0x6C //ghost kinstone + ORG $F2528+3; BYTE 0x01 0x00 //new item in smith's room, write item to $F252B + ORG $F2538+3; BYTE 0x0D 0x00 //new item in smith's room, write item to $F253B + POP + //unify golden kinstone fusions (obviously no need to change the royal piece) + PUSH + //tornado pieces first, all set to match with 0x65 + ORG $C93E4+(8*1)+1; BYTE 0x8; ORG $C93E4+(8*1)+5; BYTE 0x1 + ORG $C93E4+(8*2)+1; BYTE 0x8; ORG $C93E4+(8*2)+5; BYTE 0x1 + ORG $C93E4+(8*3)+1; BYTE 0x8; ORG $C93E4+(8*3)+5; BYTE 0x1 + ORG $C93E4+(8*4)+1; BYTE 0x8; ORG $C93E4+(8*4)+5; BYTE 0x1 + ORG $C93E4+(8*5)+1; BYTE 0x8; ORG $C93E4+(8*5)+5; BYTE 0x1 + //swamp second, all set to match with 0x6A + ORG $C93E4+(8*6)+1; BYTE 0xD; ORG $C93E4+(8*6)+5; BYTE 0x6 + ORG $C93E4+(8*7)+1; BYTE 0xD; ORG $C93E4+(8*7)+5; BYTE 0x6 + ORG $C93E4+(8*8)+1; BYTE 0xD; ORG $C93E4+(8*8)+5; BYTE 0x6 + POP +#endif + +//text ids set flags hacks +PUSH; ORG $5E93C; jumpToHack(textFlags); POP +ALIGN 4 +textFlags: +#incbin "asm/textFlags.dmp" +POIN textFlagsTable +//we use 0x2002EA4 as our custom flag table, this is where the unused small key bytes start +ALIGN 4 +textFlagsTable: //SHORT textid flag; WORD baseoffset +SHORT 0x1F1F 0; WORD 0x2002EA4 //jabber nut sets custom flag 0 +//custom flag 1 is used for the ocarina +SHORT 0x2D08 2; WORD 0x2002EA4 //buying mushroom sets custom flag 2 +SHORT 0x125C 6; WORD 0x2002CA6 //new melari flag +SHORT 0x3D07 6; WORD 0x2002CB4 //set the simulation heart piece flag after it ends +SHORT 0 0; WORD 0 //terminator + +//sanctuary stuff is wip and probably doesn't even work right now + +//flag hacks +PUSH; ORG $7DBAC; jumpToHack(flagHack); POP +ALIGN 4 +flagHack: +#incbin "asm/flagHack.dmp" +POIN newFlags +ALIGN 4 +newFlags: //WORD originaloffset baseoffset bits +WORD 0x800C710 0x2002C9C 0x04 //Festari - check for big chu being defeated instead of having earth element for special dialogue +WORD 0x800D2A6 0x2002C9C 0x08 //Melari - instead of having fire element, check defeated gleerok +WORD 0x800D30E 0x2002C9C 0x08 //Melari - instead of having fire element, check defeated gleerok +WORD 0x800D04A 0x2002C9C 0x08 //Melari - instead of having fire element, check defeated gleerok +WORD 0x800D066 0x2002C9C 0x08 //Melari - instead of having fire element, check defeated gleerok +WORD 0x800D0BE 0x2002C9C 0x08 //Melari - instead of having fire element, check defeated gleerok +WORD 0x800D0A0 0x2002CA6 0x40 //Melari - instead of having white sword, check exited mines with sword (repurposed) +WORD 0x800D05E 0x2002CA6 0x40 //Melari - instead of having white sword, check exited mines with sword (repurposed) +WORD 0x800D944 0x2002D74 0x20 //Zeffa - check for ocarina being dropped instead of ocarina being collected +WORD 0x800D976 0x2002EA4 0x02 //Ocarina - check for a new flag for the ocarina being collected +WORD 0x800D980 0x2002EA4 0x02 //Ocarina - check for a new flag for the ocarina being collected + WORD 0x8014070 0x2002B32 0xFF //Sanctuary - Ezlo doesn't nag you about the first stone tablet + WORD 0x8013BA8 0x2002B32 0xFF //Sanctuary - Ezlo doesn't nag you about the second stone tablet + //WORD 0x8014014 0x2002B32 0xFF //Sanctuary - yet more stone tablet +WORD 0 0 0 //terminator + + //sanctuary fixes + PUSH; ORG $4E98E; SHORT 0; POP //sanctuary garden door always open + PUSH; ORG $F4A9C; BYTE 0xFF; ORG $F4C18; POIN $F4AAC; POP//sanctuary switches door never closes + +#include "newGame.event" +#include "logicfixes/installer.event" +#include "gamefixes/installer.event" +#include "improvements/installer.event" diff --git a/Resources/Patches/asm/flagHack.dmp b/Resources/Patches/asm/flagHack.dmp new file mode 100644 index 0000000000000000000000000000000000000000..339ee51a36916941a52942df1dd772b4c158e697 GIT binary patch literal 72 zcmWgG$|IwYA>hfC!Jx%_p~;E;0*~R3=bYa2+!%gvfJv{6?hJtn35OX@j29Ud7#=Vv VFoVSpxSh*nED> literal 0 HcmV?d00001 diff --git a/Resources/Patches/asm/flagHack.s b/Resources/Patches/asm/flagHack.s new file mode 100644 index 00000000..421e57e5 --- /dev/null +++ b/Resources/Patches/asm/flagHack.s @@ -0,0 +1,44 @@ +.thumb +push {r4,lr} +mov r4,r1 +ldr r0,[r4] + +@check if there is a match with our changed flags +ldr r1,flags +loop: +ldr r2,[r1] +cmp r2,#0 +beq Vanilla +cmp r2,r0 +beq New +add r1,#12 +b loop + +Vanilla: +ldr r3,=#0x80169CE +mov lr,r3 +.short 0xF800 +ldr r3,=#0x807DBB6 +mov lr,r3 +.short 0xF800 + +New: +@check the new flag +ldr r2,[r1,#4] +ldr r3,[r1,#8] +ldrb r0,[r2] +and r0,r3 +cmp r0,r3 +bne EndNewFalse +mov r0,#1 +b EndNewTrue +EndNewFalse: +mov r0,#0 +EndNewTrue: +ldr r3,=#0x807DBE8 +mov lr,r3 +.short 0xF800 + +.align +.ltorg +flags: diff --git a/Resources/Patches/asm/newGame.dmp b/Resources/Patches/asm/newGame.dmp new file mode 100644 index 0000000000000000000000000000000000000000..a03e0da7f6bb545ddb03012afd075cbe93e1aff2 GIT binary patch literal 44 zcmeyrh08}ZgF%=3LR!Z13*L&yC^7|2^z7+%ndU4GJ7vn1CW2 Qdl-Ivxys1l_L7wY0PiIilmGw# literal 0 HcmV?d00001 diff --git a/Resources/Patches/asm/textFlags.s b/Resources/Patches/asm/textFlags.s new file mode 100644 index 00000000..c4b4e5f1 --- /dev/null +++ b/Resources/Patches/asm/textFlags.s @@ -0,0 +1,41 @@ +.thumb +push {r4-r7,lr} + +@check text id, compare it to the table +push {r0-r7} +mov r4,r1 @text ID +ldr r7,table + +loop: +ldrh r0,[r7] +cmp r0,#0 +beq done +cmp r0,r4 +beq match +add r7,#8 +b loop + +match: +ldr r0,[r7,#4] @base offset +ldrh r1,[r7,#2] @flag id to set +ldr r3,=#0x801D5F4 @vanilla flag set routine +mov lr,r3 +.short 0xF800 + +done: +pop {r0-r7} + +@and return to the original routine +mov r7,r0 +mov r3,r1 +strh r3,[r7,#8] +mov r0,#0x80 +push {r3} +ldr r3,=#0x805E946 +mov lr,r3 +pop {r3} +.short 0xF800 + +.align +.ltorg +table: diff --git a/Resources/Patches/gamefixes/asm/bowDozers.dmp b/Resources/Patches/gamefixes/asm/bowDozers.dmp new file mode 100644 index 0000000000000000000000000000000000000000..fd30bab1d203458322ec15ddc61489d1f311b6b3 GIT binary patch literal 24 fcmZSB%Iv{V!KldLz~n9A&cJX_hk>c^7z+mgI$Q+a literal 0 HcmV?d00001 diff --git a/Resources/Patches/gamefixes/asm/bowDozers.s b/Resources/Patches/gamefixes/asm/bowDozers.s new file mode 100644 index 00000000..7d29bb40 --- /dev/null +++ b/Resources/Patches/gamefixes/asm/bowDozers.s @@ -0,0 +1,8 @@ +.thumb +push {lr} +ldr r0,=#0x2002CDE +ldrb r0,[r0] +mov r1,#1 +and r0,r1 +ldr r3,=#0x804C671 +bx r3 diff --git a/Resources/Patches/gamefixes/asm/cloudTopsHouse.dmp b/Resources/Patches/gamefixes/asm/cloudTopsHouse.dmp new file mode 100644 index 0000000000000000000000000000000000000000..102adbf7b73724fd2f549f94228a395ce5a6b209 GIT binary patch literal 20 bcmZSfV60$N5_D!PU{ok@XJA;a!@vXpBCZ2k literal 0 HcmV?d00001 diff --git a/Resources/Patches/gamefixes/asm/cloudTopsHouse.s b/Resources/Patches/gamefixes/asm/cloudTopsHouse.s new file mode 100644 index 00000000..d5f2422d --- /dev/null +++ b/Resources/Patches/gamefixes/asm/cloudTopsHouse.s @@ -0,0 +1,8 @@ +.thumb +ldr r0,=#0x2002CA7 +ldrb r1,[r0] +mov r2,#1 +orr r1,r2 +strb r1,[r0] +mov r0,#1 +bx lr diff --git a/Resources/Patches/gamefixes/asm/dampeHouse.dmp b/Resources/Patches/gamefixes/asm/dampeHouse.dmp new file mode 100644 index 0000000000000000000000000000000000000000..5a525c04cc17b64265f6f745f3efa9a7db05541e GIT binary patch literal 72 zcmZSBnyJ9;JoTJcQy|M`+(a^9R{Y5W9%Gn_porVbntTg MSjEC&xR-?k02FBvg8%>k literal 0 HcmV?d00001 diff --git a/Resources/Patches/gamefixes/asm/graveyardGate.s b/Resources/Patches/gamefixes/asm/graveyardGate.s new file mode 100644 index 00000000..84720082 --- /dev/null +++ b/Resources/Patches/gamefixes/asm/graveyardGate.s @@ -0,0 +1,30 @@ +.thumb +push {r4,lr} + +@check if we ever talked to dampe +ldr r0,=#0x2002CE9 +ldrb r0,[r0] +mov r1,#2 +and r0,r1 +cmp r0,#0 +beq NoDampe + +@otherwise continue +mov r0,#0x20 +ldr r3,=#0x807C654 +mov lr,r3 +.short 0xF800 +ldr r3,=#0x804BCED +bx r3 + +NoDampe: +ldr r0,=#0x80D8804 +ldr r3,=#0x804AAF8 +mov lr,r3 +.short 0xF800 +ldr r3,=#0x804BD31 +bx r3 + +.align +.ltorg +poin: diff --git a/Resources/Patches/gamefixes/installer.event b/Resources/Patches/gamefixes/installer.event new file mode 100644 index 00000000..5e736de8 --- /dev/null +++ b/Resources/Patches/gamefixes/installer.event @@ -0,0 +1,33 @@ +//fix eenie fusion +PUSH; ORG $6B73C; jumpToHack(eenie); POP +ALIGN 4 +eenie: +#incbin "asm/eenie.dmp" + +//fix bow dozer room +PUSH; ORG $4C668; jumpToHack(bowDozers); POP +ALIGN 4 +bowDozers: +#incbin "asm/bowDozers.dmp" + +//set the cloud tops house flag when we visit the map, regardless of anything else +PUSH; ORG $DCB40; POIN cloudTopsHouse|1; POP +ALIGN 4 +cloudTopsHouse: +#incbin "asm/cloudTopsHouse.dmp" + +//graveyard changes +PUSH; ORG $4BCE4; jumpToHack(graveyardGate); POP //new graveyard gate area checks +ALIGN 4 +graveyardGate: +#incbin "asm/graveyardGate.dmp" +PUSH; ORG $9A608; jumpToHack(dampeHouse); POP //dampe's house, dampe is at house if not talekd to or gate is open +ALIGN 4 +dampeHouse: +#incbin "asm/dampeHouse.dmp" + +//fix cave of flames two way key door that's in one room, used to eat two keys at once +PUSH; ORG $DF8CB; BYTE 1; POP + +//don't set simulation heart piece flag on enterting it (it's instead set when you exit it after winning by the text id set flags hack) +PUSH; ORG $4E0C8; SHORT 0 0; POP diff --git a/Resources/Patches/improvements/asm/ezloPleaseStop.dmp b/Resources/Patches/improvements/asm/ezloPleaseStop.dmp new file mode 100644 index 0000000000000000000000000000000000000000..75e818535934e9698b9bca24f3525cfb4bb00d6f GIT binary patch literal 36 scmeycl|@FOf?tizA+h3v0;eAH#l;m|+6)&z>}B+paA#mpUd_S*0N#!ZAOHXW literal 0 HcmV?d00001 diff --git a/Resources/Patches/improvements/asm/ezloPleaseStop.s b/Resources/Patches/improvements/asm/ezloPleaseStop.s new file mode 100644 index 00000000..e339cf80 --- /dev/null +++ b/Resources/Patches/improvements/asm/ezloPleaseStop.s @@ -0,0 +1,22 @@ +.thumb +push {r4-r7,lr} +mov r4,r0 +ldrb r0,[r4] +mov r6,#0x0F +and r6,r0 +ldrb r1,[r4,#1] +mov r0,#0xF0 + +@check if this is ezlo dialogue +cmp r6,#0x09 @event type +bne notEzlo +ldrb r3,[r4,#2] +cmp r3,#0x0A @ezlo dialogue type +bne notEzlo + +Ezlo: +pop {r4-r7,pc} + +notEzlo: +ldr r3,=#0x804AB23 +bx r3 diff --git a/Resources/Patches/improvements/asm/skipFusionCutscene.dmp b/Resources/Patches/improvements/asm/skipFusionCutscene.dmp new file mode 100644 index 0000000000000000000000000000000000000000..ba74cc66596183e35cee42fe9a529ed7c8a610ae GIT binary patch literal 48 zcmd;-I8ni%!F7Stn?alXBCEG_2e*>21A{i>MP_dacMv|{_JN;)`K>7z2csDm2LKsV B3km=L literal 0 HcmV?d00001 diff --git a/Resources/Patches/improvements/asm/skipFusionCutscene.s b/Resources/Patches/improvements/asm/skipFusionCutscene.s new file mode 100644 index 00000000..3e221c37 --- /dev/null +++ b/Resources/Patches/improvements/asm/skipFusionCutscene.s @@ -0,0 +1,31 @@ +.thumb +@vanilla check +add r1,r0 +ldrb r0,[r1,#3] +cmp r0,#0 +beq noCutscene + +@check if we always skip +ldr r3,always +cmp r3,#0 +bne noCutscene + +@check if a skip button is being held +ldr r3,=#0x3000FF0 +ldrh r3,[r3] +mov r2,#0x0B +and r3,r2 +cmp r3,#0 +bne noCutscene + +cutscene: +ldr r3,=#0x80A35ED +bx r3 + +noCutscene: +ldr r3,=#0x80A3601 +bx r3 + +.align +.ltorg +always: diff --git a/Resources/Patches/improvements/installer.event b/Resources/Patches/improvements/installer.event new file mode 100644 index 00000000..9f496188 --- /dev/null +++ b/Resources/Patches/improvements/installer.event @@ -0,0 +1,21 @@ +//remove all ezlo stepping triggers +PUSH; ORG $4AB14; jumpToHack(ezloPleaseStop); POP +ALIGN 4 +ezloPleaseStop: +#incbin "asm/ezloPleaseStop.dmp" + +//instant text +PUSH; ORG $562AC; SHORT 0x2501; POP + +//skip fusion cutscene if A, B or START are held +#ifndef neverSkipFusionCutscene + PUSH; ORG $A35E4; jumpToHack(skipFusionCutscene); POP + ALIGN 4 + skipFusionCutscene: + #incbin "asm/skipFusionCutscene.dmp" + WORD alwaysSkipFusionCutscene //set to non-0 to always skip fusion cutscene +#endif + +//skip minish transformation cutscenes automatically +PUSH; ORG $70DFA; SHORT 0; POP + diff --git a/Resources/Patches/logicfixes/asm/gregal.dmp b/Resources/Patches/logicfixes/asm/gregal.dmp new file mode 100644 index 0000000000000000000000000000000000000000..6266761aa2155a1439c499eeda96654f324c9a6e GIT binary patch literal 88 zcmZSB8lk}FJ@DHGM2~^#!Bs{Mjz_EZtBf31rtow8SjEBt0OiySi~s-t literal 0 HcmV?d00001 diff --git a/Resources/Patches/logicfixes/asm/mushroomFlag.s b/Resources/Patches/logicfixes/asm/mushroomFlag.s new file mode 100644 index 00000000..33cd3b46 --- /dev/null +++ b/Resources/Patches/logicfixes/asm/mushroomFlag.s @@ -0,0 +1,18 @@ +.thumb +@check our custom nut flag +ldr r0,=#0x2002EA4 @base offset +mov r1,#2 @flag id to check +ldr r3,=#0x801D5E0 @vanilla flag check routine +mov lr,r3 +.short 0xF800 +cmp r0,#0 +bne End + +@spawn the mushroom +ldr r0,=#0x80F94D4 +ldr r3,=#0x804AAF8 +mov lr,r3 +.short 0xF800 + +End: +pop {pc} diff --git a/Resources/Patches/logicfixes/asm/ocarinaFlag1.dmp b/Resources/Patches/logicfixes/asm/ocarinaFlag1.dmp new file mode 100644 index 0000000000000000000000000000000000000000..9ff41bca60ad4803ae7a024569da441751db9be2 GIT binary patch literal 32 ncmZor3St#>W=vuBW>S)@5OkI-VDgr5XJA;O$H4UWBqs*|Xuk&P literal 0 HcmV?d00001 diff --git a/Resources/Patches/logicfixes/asm/ocarinaFlag1.s b/Resources/Patches/logicfixes/asm/ocarinaFlag1.s new file mode 100644 index 00000000..5a4338f4 --- /dev/null +++ b/Resources/Patches/logicfixes/asm/ocarinaFlag1.s @@ -0,0 +1,14 @@ +.thumb +mov r2,#0x80 +lsl r2,#0x15 +orr r1,r2 +str r1,[r0,#0x40] + +@set flag and return +ldr r3,=#0x2002EA4 +mov r2,#0x02 +ldrb r1,[r3] +orr r1,r2 +strb r1,[r3] +ldr r3,=#0x809C9FD +bx r3 diff --git a/Resources/Patches/logicfixes/asm/ocarinaFlag2.dmp b/Resources/Patches/logicfixes/asm/ocarinaFlag2.dmp new file mode 100644 index 0000000000000000000000000000000000000000..841bc57dd118460605b3c24f1ea5c06fd6f283f2 GIT binary patch literal 28 jcmZSaU{YkL;Ba8jV7S1rm&segoq=JA9s^VCSr!feN|Obf literal 0 HcmV?d00001 diff --git a/Resources/Patches/logicfixes/asm/ocarinaFlag2.s b/Resources/Patches/logicfixes/asm/ocarinaFlag2.s new file mode 100644 index 00000000..3fce7d92 --- /dev/null +++ b/Resources/Patches/logicfixes/asm/ocarinaFlag2.s @@ -0,0 +1,14 @@ +.thumb +ldr r0,=#0x2002EA4 +mov r1,#0x02 +ldrb r0,[r0] +and r0,r1 +cmp r0,#0 +beq False + +True: +pop {pc} + +False: +ldr r3,=#0x804CD5D +bx r3 diff --git a/Resources/Patches/logicfixes/asm/ocarinaFlag3.dmp b/Resources/Patches/logicfixes/asm/ocarinaFlag3.dmp new file mode 100644 index 0000000000000000000000000000000000000000..f7fbfac6d2dcf2ff385b8a9504a3ac9dd72f689b GIT binary patch literal 28 jcmZSaU{YkL;Ba8jV7$QOE#WS(mw{o49s`r$GENQvNG=6F literal 0 HcmV?d00001 diff --git a/Resources/Patches/logicfixes/asm/ocarinaFlag3.s b/Resources/Patches/logicfixes/asm/ocarinaFlag3.s new file mode 100644 index 00000000..eac7e947 --- /dev/null +++ b/Resources/Patches/logicfixes/asm/ocarinaFlag3.s @@ -0,0 +1,14 @@ +.thumb +ldr r0,=#0x2002EA4 +mov r1,#0x02 +ldrb r0,[r0] +and r0,r1 +cmp r0,#0 +beq False + +True: +ldr r3,=#0x809A611 +bx r3 + +False: +pop {r4,pc} diff --git a/Resources/Patches/logicfixes/asm/skipHack.dmp b/Resources/Patches/logicfixes/asm/skipHack.dmp new file mode 100644 index 0000000000000000000000000000000000000000..89dc0e4d57e0c44fe05ba18004967f068962ae82 GIT binary patch literal 56 zcmd<}mdao;X3%24(B$;>Q7Q7y#s07+C-S literal 0 HcmV?d00001 diff --git a/Resources/Patches/logicfixes/asm/splitHack.s b/Resources/Patches/logicfixes/asm/splitHack.s new file mode 100644 index 00000000..f78eba79 --- /dev/null +++ b/Resources/Patches/logicfixes/asm/splitHack.s @@ -0,0 +1,44 @@ +.thumb +push {r0-r2} +ldr r3,=#0x3000BF0 +ldrb r0,[r3,#4] +ldrb r1,[r3,#5] +ldr r3,poin +loop: +ldrb r2,[r3] +cmp r2,#0 +beq End +cmp r2,r0 +bne Next +ldrb r2,[r3,#1] +cmp r2,#0xFF +beq skipRoom +cmp r2,r1 +bne Next +skipRoom: +ldrb r2,[r3,#2] +cmp r5,r2 +blo End +mov r5,r2 +b End +Next: +add r3,#3 +b loop + +End: +pop {r0-r2} +cmp r4,r5 +bcs goto8073EE2 + +goto8073EDC: +ldr r1,=#0x3004040 +ldr r3,=#0x8073EDD +bx r3 + +goto8073EE2: +ldr r3,=#0x8073EE3 +bx r3 + +.align +.ltorg +poin: diff --git a/Resources/Patches/logicfixes/asm/stoneFix.dmp b/Resources/Patches/logicfixes/asm/stoneFix.dmp new file mode 100644 index 0000000000000000000000000000000000000000..cb8127f8db2085b77b229ad80611814871b82b32 GIT binary patch literal 52 zcmZSB%ALXO!IZ&bz@Wu(VV2X+i!&;2Rxl{Bde3uX_`$H3Bg3&HMu9 Date: Fri, 2 Aug 2019 12:55:34 -0600 Subject: [PATCH 34/41] Rearrange and comment code, improve build steps, add ColorzCore to solution --- MinishRandomizer.sln | 11 ++ Randomizer/Dependency.cs | 5 + Randomizer/Location.cs | 69 ++++------ Randomizer/Shuffler.cs | 282 +++++++++++++++++++++++++++----------- UI/MainWindow.Designer.cs | 154 +++++++++++++++++---- UI/MainWindow.cs | 152 ++++++++++++++++---- 6 files changed, 494 insertions(+), 179 deletions(-) diff --git a/MinishRandomizer.sln b/MinishRandomizer.sln index bff0a759..c8151a23 100644 --- a/MinishRandomizer.sln +++ b/MinishRandomizer.sln @@ -5,6 +5,10 @@ VisualStudioVersion = 16.0.29001.49 MinimumVisualStudioVersion = 10.0.40219.1 Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MinishRandomizer", "MinishRandomizer.csproj", "{704DAD64-9C28-458B-8980-A945C1159FFA}" EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Vendor", "Vendor", "{E5E80AB9-F3A1-4B3F-BAC1-46C864EB76E8}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "ColorzCore", "Vendor\ColorzCore\ColorzCore\ColorzCore.csproj", "{B98F7CCF-9CAA-406E-88D7-2040FA99F631}" +EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution Debug|Any CPU = Debug|Any CPU @@ -15,10 +19,17 @@ Global {704DAD64-9C28-458B-8980-A945C1159FFA}.Debug|Any CPU.Build.0 = Debug|Any CPU {704DAD64-9C28-458B-8980-A945C1159FFA}.Release|Any CPU.ActiveCfg = Release|Any CPU {704DAD64-9C28-458B-8980-A945C1159FFA}.Release|Any CPU.Build.0 = Release|Any CPU + {B98F7CCF-9CAA-406E-88D7-2040FA99F631}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B98F7CCF-9CAA-406E-88D7-2040FA99F631}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B98F7CCF-9CAA-406E-88D7-2040FA99F631}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B98F7CCF-9CAA-406E-88D7-2040FA99F631}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {B98F7CCF-9CAA-406E-88D7-2040FA99F631} = {E5E80AB9-F3A1-4B3F-BAC1-46C864EB76E8} + EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {3200685A-8233-4883-B294-A2CBC408902C} EndGlobalSection diff --git a/Randomizer/Dependency.cs b/Randomizer/Dependency.cs index 91c30191..d2fbc7bd 100644 --- a/Randomizer/Dependency.cs +++ b/Randomizer/Dependency.cs @@ -102,6 +102,11 @@ public static List GetDependencies(string logic) return dependencies; } + ///

+ /// Split a logic string into the separate dependencies it represents + /// + /// The logic string to split + /// A list of the individual dependencies within the logic public static List SplitDependencies(string logic) { List subLogic = new List(); diff --git a/Randomizer/Location.cs b/Randomizer/Location.cs index b5329956..b6ab37f6 100644 --- a/Randomizer/Location.cs +++ b/Randomizer/Location.cs @@ -20,7 +20,7 @@ public static Location GetLocation(string locationText) if (locationParts.Length < 3) { Console.WriteLine("Too short"); - return new Location(LocationType.Untyped, "INVALID LOCATION", "", 0, null); + throw new ShuffleException("A location in the logic file lacks required fields!"); } string[] names = locationParts[0].Split(':'); @@ -30,7 +30,7 @@ public static Location GetLocation(string locationText) if (!Enum.TryParse(locationType, out LocationType type) || type == LocationType.Untyped) { Console.WriteLine("Invalid type"); - return new Location(LocationType.Untyped, "INVALID LOCATION", "", 0, null); + throw new ShuffleException($"Location \"{name}\" has invalid type \"{locationType}\"!"); } int address = GetAddressFromString(locationParts[2]); @@ -104,6 +104,11 @@ public static List GetItems(List locations) return items; } + /// + /// Turn an address string into a file address + /// + /// String representing the address + /// public static int GetAddressFromString(string addressString) { // Either direct address or area-room-chest @@ -112,36 +117,40 @@ public static int GetAddressFromString(string addressString) return 0; } + // The address is a hexadecimal number, so it can simply be parsed if (int.TryParse(addressString, NumberStyles.HexNumber, null, out int address)) { return address; } + // The address is a chest, so it should string[] chestDetails = addressString.Split('-'); if (chestDetails.Length != 3) { - return 0; + throw new ShuffleException($"Chest data \"{addressString}\" does not have a full address!"); } if (!int.TryParse(chestDetails[0], NumberStyles.HexNumber, null, out int area)) { - return 0; + throw new ShuffleException($"Chest data \"{addressString}\" has an invalid area index!"); } if (!int.TryParse(chestDetails[1], NumberStyles.HexNumber, null, out int room)) { - return 0; + throw new ShuffleException($"Chest data \"{addressString}\" has an invalid room index!"); } if (!int.TryParse(chestDetails[2], NumberStyles.HexNumber, null, out int chest)) { - return 0; + throw new ShuffleException($"Chest data \"{addressString}\" has an invalid chest index!"); } + // Look chest address up in table int areaTableAddr = ROM.Instance.reader.ReadAddr(ROM.Instance.headers.AreaMetadataBase + (area << 2)); int roomTableAddr = ROM.Instance.reader.ReadAddr(areaTableAddr + (room << 2)); int chestTableAddr = ROM.Instance.reader.ReadAddr(roomTableAddr + 0x0C); + // Chests are 8 bytes long, and the item is stored 2 bytes in return chestTableAddr + chest * 8 + 0x02; } @@ -151,14 +160,8 @@ public enum LocationType Major, Minor, DungeonItem, - NPCItem, - KinstoneItem, - HeartPieceItem, Split, Helper, - //StartingItem, - PurchaseItem, - ScrollItem, Half, Unshuffled } @@ -209,30 +212,6 @@ public void WriteLocation(Writer w) w.WriteByte((byte)Contents.Type, Address); w.WriteByte(Contents.SubValue, Address + 2); break; - case LocationType.PurchaseItem: - w.SetPosition(Address); - if (Contents.Type == ItemType.KinstoneX || Contents.Type == ItemType.ShellsX) - { - w.WriteByte((byte)ItemType.Shells30); - } - else - { - w.WriteByte((byte)Contents.Type); - } - - w.WriteByte(0xFF); - break; - case LocationType.ScrollItem: - w.SetPosition(Address); - if (Contents.Type == ItemType.KinstoneX || Contents.Type == ItemType.ShellsX) - { - w.WriteByte((byte)ItemType.Shells30); - } - else - { - w.WriteByte((byte)((byte)Contents.Type & 0x7F)); - } - break; case LocationType.Half: w.SetPosition(Address); if (Contents.Type == ItemType.KinstoneX || Contents.Type == ItemType.ShellsX) @@ -254,6 +233,10 @@ public void WriteLocation(Writer w) } } + /// + /// Read the item from the ROM + /// + /// The item contained at the address public Item GetItemContents() { ItemType type; @@ -266,14 +249,9 @@ public Item GetItemContents() subType = ROM.Instance.reader.ReadByte(Address + 2); break; case LocationType.Half: - case LocationType.PurchaseItem: type = (ItemType)ROM.Instance.reader.ReadByte(Address); subType = 0x00; break; - case LocationType.ScrollItem: - type = (ItemType)(ROM.Instance.reader.ReadByte(Address) & 0x7F); - subType = 0; - break; default: type = (ItemType)ROM.Instance.reader.ReadByte(Address); subType = ROM.Instance.reader.ReadByte(); @@ -291,6 +269,13 @@ public Item GetItemContents() } } + /// + /// Check if an item can be placed into the location + /// + /// The item to check placability of + /// The items used for checking accessibility + /// The locations used for checkign accessiblility + /// If the item can be placed in this location public bool CanPlace(Item itemToPlace, List availableItems, List locations) { switch (Type) @@ -298,8 +283,6 @@ public bool CanPlace(Item itemToPlace, List availableItems, List case LocationType.Helper: case LocationType.Untyped: return false; - case LocationType.PurchaseItem: - case LocationType.ScrollItem: case LocationType.Half: if (itemToPlace.SubValue != 0) { diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index 8f2a28d1..2d8a4092 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -56,8 +56,8 @@ public override int GetHashCode() public class Shuffler { - private Random RNG; + public int Seed; private List Locations; //private List StartingLocations; private List DungeonItems; @@ -67,7 +67,6 @@ public class Shuffler public Shuffler(string outputDirectory) { - RNG = new Random(); Locations = new List(); DungeonItems = new List(); MajorItems = new List(); @@ -77,10 +76,15 @@ public Shuffler(string outputDirectory) public void SetSeed(int seed) { + Seed = seed; RNG = new Random(seed); } - public void LoadLocations(string locationFile) + /// + /// Reads the list of locations from a file, or the default logic if none is specified + /// + /// The file to read locations from + public void LoadLocations(string locationFile = null) { Locations.Clear(); DungeonItems.Clear(); @@ -91,11 +95,13 @@ public void LoadLocations(string locationFile) if (locationFile == null) { + // Load default logic if no alternative is specified Assembly assembly = Assembly.GetExecutingAssembly(); using (Stream stream = assembly.GetManifestResourceStream("MinishRandomizer.Resources.default.logic")) using (StreamReader reader = new StreamReader(stream)) { string allLocations = reader.ReadToEnd(); + // Each line is a different location, split regardless of return form locationStrings = allLocations.Split(new[] { "\r\n", "\r", "\n" }, StringSplitOptions.None); } } @@ -106,70 +112,61 @@ public void LoadLocations(string locationFile) foreach (string locationLine in locationStrings) { + // Spaces are ignored, and everything after a # is a comment string locationString = locationLine.Split('#')[0].Replace(" ", ""); - locationString = locationString.Replace(" ", ""); - if (locationString == "") + + // Empty lines or locations are ignored + if (string.IsNullOrWhiteSpace(locationString)) { continue; } Location newLocation = Location.GetLocation(locationString); - Locations.Add(newLocation); - - switch(newLocation.Type) - { - case Location.LocationType.Untyped: - case Location.LocationType.Helper: - Console.WriteLine($"Helper or untyped {newLocation.Name}"); - break; - // Unshuffled locations are filled by default - case Location.LocationType.Unshuffled: - Console.WriteLine($"Unshuffled {newLocation.Name}"); - newLocation.Fill(newLocation.Contents); - break; - case Location.LocationType.Minor: - Console.WriteLine(newLocation.Contents.Type.ToString()); - MinorItems.Add(newLocation.Contents); - break; - case Location.LocationType.DungeonItem: - Console.WriteLine($"{newLocation.Dungeon}: {newLocation.Contents.Type.ToString()}"); - DungeonItems.Add(newLocation.Contents); - break; - case Location.LocationType.Major: - case Location.LocationType.Split: - case Location.LocationType.PurchaseItem: - case Location.LocationType.ScrollItem: - default: - Console.WriteLine($"Hey! {newLocation.Contents.Type.ToString()}"); - MajorItems.Add(newLocation.Contents); - break; - } + AddLocation(newLocation); } } - public void PatchRom(string locationFile) + private void AddLocation(Location location) { - byte[] patchContents; - if (locationFile == null) - { - Assembly assembly = Assembly.GetExecutingAssembly(); - using (Stream stream = assembly.GetManifestResourceStream("MinishRandomizer.Resources.randoPatch.ups")) - { - patchContents = new byte[stream.Length]; - stream.Read(patchContents, 0, (int)stream.Length); - } - } - else + // All locations are in the master location list + Locations.Add(location); + + // The type of the containing location determines how the item is handled + switch (location.Type) { - patchContents = File.ReadAllBytes(locationFile); + // These locations are not filled, because they don't reference an item location + case Location.LocationType.Untyped: + case Location.LocationType.Helper: + break; + // Unshuffled locations are filled by default + case Location.LocationType.Unshuffled: + location.Fill(location.Contents); + break; + // Minor locations are not logically accounted for + case Location.LocationType.Minor: + MinorItems.Add(location.Contents); + break; + // Dungeon items can only be placed within the same dungeon, and are placed first + case Location.LocationType.DungeonItem: + DungeonItems.Add(location.Contents); + break; + // Major/etc items are fully randomized + case Location.LocationType.Major: + case Location.LocationType.Split: + default: + MajorItems.Add(location.Contents); + break; } - - PatchUtil.ApplyUPS(ROM.Instance.romData, patchContents); } - public void RandomizeLocations() + /// + /// Loads and shuffles all locations + /// + /// The RNG seed used for generation + public void RandomizeLocations(int seed) { - ResetLocations(); + // Make sure the RNG is set to the seed, so the seed can be regenerated + SetSeed(seed); List unplacedItems = MajorItems.ToList(); List dungeonSpecificItems = DungeonItems.ToList(); @@ -181,27 +178,28 @@ public void RandomizeLocations() // Fill dungeon items first so there is room for them all List dungeonLocations = FillLocations(dungeonSpecificItems, unfilledLocations, unplacedItems); - // Fill non-dungeon items, taking into account the previously placed dungeon items - // This seems fairly crude; other randomizers don't seem to deal with this problem? + // Fill non-dungeon major items, checking for logic unfilledLocations.Shuffle(RNG); FillLocations(unplacedItems, unfilledLocations); - // Before shuffling Minor locations in, create a playthrough log - List finalMajorItems = GetAvailableItems(new List(), true); + // Get every item that can be logically obtained, to check if the game can be completed + List finalMajorItems = GetAvailableItems(new List()); if (!new LocationDependency("BeatVaati").DependencyFulfilled(finalMajorItems, Locations)) { throw new ShuffleException($"Randomization succeded, but could not beat Vaati!"); } + // Put the remaining items into the place wherever unfilledLocations.Shuffle(RNG); FastFillLocations(MinorItems.ToList(), unfilledLocations); if (unfilledLocations.Count != 0) { + // All locations should be filled at this point throw new ShuffleException($"There are {unfilledLocations.Count} unfilled locations!"); } - + using (MemoryStream ms = new MemoryStream(ROM.Instance.romData)) { Writer writer = new Writer(ms); @@ -214,21 +212,13 @@ public void RandomizeLocations() File.WriteAllBytes(OutputDirectory + "/mcrando.gba", ROM.Instance.romData); } - private void ResetLocations() - { - foreach (Location location in Locations) - { - location.SetDefaultContents(); - location.InvalidateCache(); - - if (location.Type != Location.LocationType.Unshuffled) - { - location.Filled = false; - } - } - } - - // Based off of the RandomAssumed ALttPR filler + /// + /// Uniformly fills items in locations, checking to make sure the items are logically available. + /// + /// The items to fill with + /// The locations to be filled + /// The items that are available by default + /// A list of the locations that were filled private List FillLocations(List items, List locations, List assumedItems = null) { List filledLocations = new List(); @@ -237,8 +227,11 @@ private List FillLocations(List items, List locations, for (int i = items.Count - 1; i >= 0; i--) { + // Get a random item from the list and save its index int itemIndex = RNG.Next(items.Count); Item item = items[itemIndex]; + + // Write placing information to spoiler log Console.WriteLine($"Placing: {item.Type.ToString()}"); if (item.Type == ItemType.KinstoneX) { @@ -250,16 +243,19 @@ private List FillLocations(List items, List locations, Console.WriteLine($"Dungeon: {item.Dungeon}"); } + // Take item out of pool items.RemoveAt(itemIndex); List availableItems = GetAvailableItems(items.Concat(assumedItems).ToList()); + // Find locations that are available for placing the item List availableLocations = locations.Where(location => location.CanPlace(item, availableItems, Locations)).ToList(); if (availableLocations.Count <= 0) { + // The filler broke, show all available items and get out availableItems.ForEach(itm => Console.WriteLine($"{itm.Type} sub {itm.SubValue}")); - throw new ShuffleException($"Could not place {item.Type}"); + throw new ShuffleException($"Could not place {item.Type}!"); } int locationIndex = RNG.Next(availableLocations.Count); @@ -278,8 +274,14 @@ private List FillLocations(List items, List locations, return filledLocations; } + /// + /// Fill items in locations without checking logic for speed + /// + /// The items to be filled + /// The locations in which to fill the items private void FastFillLocations(List items, List locations) { + // Don't need to check logic, cause the items being placed do not affect logic foreach (Item item in items) { locations[0].Fill(item); @@ -287,15 +289,21 @@ private void FastFillLocations(List items, List locations) } } - // Gets all the available items with a given item set, looping until there are no more items left to get - private List GetAvailableItems(List preAvailableItems, bool printPlaythrough = false) + + /// + /// Gets all the available items with a given item set, looping until there are no more items left to get + /// + /// Items that are available from the start + /// A list of all the items that are logically accessible + private List GetAvailableItems(List preAvailableItems) { List availableItems = preAvailableItems.ToList(); List filledLocations = Locations.Where(location => location.Filled && location.Type != Location.LocationType.Helper && location.Type != Location.LocationType.Untyped).ToList(); int previousSize; - int sphereCount = 1; + + // Get "spheres" until the next sphere contains no new items do { // Doesn't touch the cache to prevent incorrect caching @@ -304,20 +312,132 @@ private List GetAvailableItems(List preAvailableItems, bool printPla filledLocations.RemoveAll(location => accessibleLocations.Contains(location)); - if (printPlaythrough) + List newItems = Location.GetItems(accessibleLocations); + + availableItems.AddRange(newItems); + } + while (previousSize > 0); + + return availableItems; + } + + /// + /// Get a byte[] of the randomized data + /// + /// The data of the randomized ROM + public byte[] GetRandomizedRom() + { + // Create a copy of the ROM data to modify for output + byte[] outputBytes = new byte[ROM.Instance.romData.Length]; + Array.Copy(ROM.Instance.romData, 0, outputBytes, 0, outputBytes.Length); + + using (MemoryStream ms = new MemoryStream(outputBytes)) + { + Writer writer = new Writer(ms); + foreach (Location location in Locations) { - accessibleLocations.ForEach(location => Console.WriteLine($"Sphere {sphereCount}: {location.Contents.Type} sub {StringUtil.AsStringHex2(location.Contents.SubValue)} at {location.Name}\n")); + location.WriteLocation(writer); } + } - List newItems = Location.GetItems(accessibleLocations); + return outputBytes; + } + /// + /// Get the contents of the spoiler log, including playthrough + /// + /// The contents of the spoiler log + public string GetSpoiler() + { + StringBuilder spoilerBuilder = new StringBuilder(); + spoilerBuilder.AppendLine("Spoiler for Minish Cap Randomizer"); + spoilerBuilder.AppendLine($"Seed: {Seed}"); + + spoilerBuilder.AppendLine(); + AppendLocationSpoiler(spoilerBuilder); + + spoilerBuilder.AppendLine(); + AppendPlaythroughSpoiler(spoilerBuilder); + + + return spoilerBuilder.ToString(); + } + + /// + /// Create list of filled locations and their contents + /// + /// The running spoiler log builder to append the locations to< + private void AppendLocationSpoiler(StringBuilder spoilerBuilder) + { + spoilerBuilder.AppendLine("Location Contents:"); + // Get the locations that have been filled + List filledLocations = Locations.Where(location => location.Filled && location.Type != Location.LocationType.Helper && location.Type != Location.LocationType.Untyped).ToList(); + + foreach (Location location in filledLocations) + { + spoilerBuilder.AppendLine($"{location.Name}: {location.Contents.Type}"); + + AppendSubvalue(spoilerBuilder, location); + + spoilerBuilder.AppendLine(); + } + } + + /// + /// Create list of items in the order they can logically be collected + /// + /// The running spoiler log builder to append the playthrough to + private void AppendPlaythroughSpoiler(StringBuilder spoilerBuilder) + { + spoilerBuilder.AppendLine("Playthrough:"); + + List filledLocations = Locations.Where(location => location.Filled && location.Type != Location.LocationType.Helper && location.Type != Location.LocationType.Untyped).ToList(); + List availableItems = new List(); + + int previousSize; + int sphereCounter = 1; + + do + { + List accessibleLocations = filledLocations.Where(location => location.IsAccessible(availableItems, Locations, false)).ToList(); + previousSize = accessibleLocations.Count; + + filledLocations.RemoveAll(location => accessibleLocations.Contains(location)); + + List newItems = Location.GetItems(accessibleLocations); availableItems.AddRange(newItems); - sphereCount++; + foreach (Location location in accessibleLocations) + { + spoilerBuilder.AppendLine($"Sphere {sphereCounter}: {location.Contents.Type} in {location.Name}"); + + AppendSubvalue(spoilerBuilder, location); + spoilerBuilder.AppendLine(); + } + + sphereCounter++; + spoilerBuilder.AppendLine(); } while (previousSize > 0); + } - return availableItems; + private void AppendSubvalue(StringBuilder spoilerBuilder, Location location) + { + // Display subvalue if relevant + if (location.Contents.Type == ItemType.KinstoneX) + { + spoilerBuilder.AppendLine($"Kinstone Type: {location.Contents.Kinstone}"); + } + else if (location.Contents.SubValue != 0) + { + spoilerBuilder.AppendLine($"Subvalue: {location.Contents.SubValue}"); + } + + // Display dungeon contents if relevant + if (!string.IsNullOrEmpty(location.Contents.Dungeon)) + { + spoilerBuilder.AppendLine($"Dungeon: {location.Contents.Dungeon}"); + } } } } diff --git a/UI/MainWindow.Designer.cs b/UI/MainWindow.Designer.cs index 6060a762..2e000053 100644 --- a/UI/MainWindow.Designer.cs +++ b/UI/MainWindow.Designer.cs @@ -36,17 +36,26 @@ private void InitializeComponent() this.openToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); this.randomize = new System.Windows.Forms.Button(); this.generalTab = new System.Windows.Forms.TabPage(); + this.seedLabel = new System.Windows.Forms.Label(); + this.seedField = new System.Windows.Forms.TextBox(); + this.customPatchCheckBox = new System.Windows.Forms.CheckBox(); + this.customPatchPath = new System.Windows.Forms.TextBox(); + this.browsePatchButton = new System.Windows.Forms.Button(); this.customLogicPath = new System.Windows.Forms.TextBox(); this.browseLogicButton = new System.Windows.Forms.Button(); this.customLogicCheckBox = new System.Windows.Forms.CheckBox(); this.mainTabs = new System.Windows.Forms.TabControl(); - this.browsePatchButton = new System.Windows.Forms.Button(); - this.customPatchPath = new System.Windows.Forms.TextBox(); - this.customPatchCheckBox = new System.Windows.Forms.CheckBox(); + this.generatedTab = new System.Windows.Forms.TabPage(); + this.generatedSeedValue = new System.Windows.Forms.Label(); + this.generatedSeedLabel = new System.Windows.Forms.Label(); + this.saveSpoilerButton = new System.Windows.Forms.Button(); + this.savePatchButton = new System.Windows.Forms.Button(); + this.saveRomButton = new System.Windows.Forms.Button(); this.statusStrip1.SuspendLayout(); this.menuStrip1.SuspendLayout(); this.generalTab.SuspendLayout(); this.mainTabs.SuspendLayout(); + this.generatedTab.SuspendLayout(); this.SuspendLayout(); // // statusStrip1 @@ -101,6 +110,8 @@ private void InitializeComponent() // // generalTab // + this.generalTab.Controls.Add(this.seedLabel); + this.generalTab.Controls.Add(this.seedField); this.generalTab.Controls.Add(this.customPatchCheckBox); this.generalTab.Controls.Add(this.customPatchPath); this.generalTab.Controls.Add(this.browsePatchButton); @@ -115,6 +126,52 @@ private void InitializeComponent() this.generalTab.Text = "General"; this.generalTab.UseVisualStyleBackColor = true; // + // seedLabel + // + this.seedLabel.AutoSize = true; + this.seedLabel.Location = new System.Drawing.Point(6, 13); + this.seedLabel.Name = "seedLabel"; + this.seedLabel.Size = new System.Drawing.Size(35, 13); + this.seedLabel.TabIndex = 12; + this.seedLabel.Text = "Seed:"; + // + // seedField + // + this.seedField.Location = new System.Drawing.Point(47, 10); + this.seedField.Name = "seedField"; + this.seedField.Size = new System.Drawing.Size(100, 20); + this.seedField.TabIndex = 10; + // + // customPatchCheckBox + // + this.customPatchCheckBox.AutoSize = true; + this.customPatchCheckBox.Location = new System.Drawing.Point(5, 100); + this.customPatchCheckBox.Name = "customPatchCheckBox"; + this.customPatchCheckBox.Size = new System.Drawing.Size(114, 17); + this.customPatchCheckBox.TabIndex = 11; + this.customPatchCheckBox.Text = "Use Custom Patch"; + this.customPatchCheckBox.UseVisualStyleBackColor = true; + this.customPatchCheckBox.CheckedChanged += new System.EventHandler(this.CustomPatchCheckBox_CheckedChanged); + // + // customPatchPath + // + this.customPatchPath.Enabled = false; + this.customPatchPath.Location = new System.Drawing.Point(113, 123); + this.customPatchPath.Name = "customPatchPath"; + this.customPatchPath.Size = new System.Drawing.Size(206, 20); + this.customPatchPath.TabIndex = 10; + // + // browsePatchButton + // + this.browsePatchButton.Enabled = false; + this.browsePatchButton.Location = new System.Drawing.Point(32, 123); + this.browsePatchButton.Name = "browsePatchButton"; + this.browsePatchButton.Size = new System.Drawing.Size(75, 23); + this.browsePatchButton.TabIndex = 9; + this.browsePatchButton.Text = "Browse..."; + this.browsePatchButton.UseVisualStyleBackColor = true; + this.browsePatchButton.Click += new System.EventHandler(this.BrowsePatchButton_Click); + // // customLogicPath // this.customLogicPath.Enabled = false; @@ -148,41 +205,74 @@ private void InitializeComponent() // mainTabs // this.mainTabs.Controls.Add(this.generalTab); + //this.mainTabs.Controls.Add(this.generatedTab); this.mainTabs.Location = new System.Drawing.Point(9, 27); this.mainTabs.Name = "mainTabs"; this.mainTabs.SelectedIndex = 0; this.mainTabs.Size = new System.Drawing.Size(369, 235); this.mainTabs.TabIndex = 9; // - // browsePatchButton + // generatedTab // - this.browsePatchButton.Enabled = false; - this.browsePatchButton.Location = new System.Drawing.Point(32, 123); - this.browsePatchButton.Name = "browsePatchButton"; - this.browsePatchButton.Size = new System.Drawing.Size(75, 23); - this.browsePatchButton.TabIndex = 9; - this.browsePatchButton.Text = "Browse..."; - this.browsePatchButton.UseVisualStyleBackColor = true; - this.browsePatchButton.Click += new System.EventHandler(this.BrowsePatchButton_Click); + this.generatedTab.Controls.Add(this.generatedSeedValue); + this.generatedTab.Controls.Add(this.generatedSeedLabel); + this.generatedTab.Controls.Add(this.saveSpoilerButton); + this.generatedTab.Controls.Add(this.savePatchButton); + this.generatedTab.Controls.Add(this.saveRomButton); + this.generatedTab.Location = new System.Drawing.Point(4, 22); + this.generatedTab.Name = "generatedTab"; + this.generatedTab.Padding = new System.Windows.Forms.Padding(3); + this.generatedTab.Size = new System.Drawing.Size(361, 209); + this.generatedTab.TabIndex = 1; + this.generatedTab.Text = "Seed"; + this.generatedTab.UseVisualStyleBackColor = true; // - // customPatchPath + // generatedSeedValue // - this.customPatchPath.Enabled = false; - this.customPatchPath.Location = new System.Drawing.Point(113, 123); - this.customPatchPath.Name = "customPatchPath"; - this.customPatchPath.Size = new System.Drawing.Size(206, 20); - this.customPatchPath.TabIndex = 10; + this.generatedSeedValue.AutoSize = true; + this.generatedSeedValue.Location = new System.Drawing.Point(51, 13); + this.generatedSeedValue.Name = "generatedSeedValue"; + this.generatedSeedValue.Size = new System.Drawing.Size(0, 13); + this.generatedSeedValue.TabIndex = 4; // - // customPatchCheckBox + // generatedSeedLabel // - this.customPatchCheckBox.AutoSize = true; - this.customPatchCheckBox.Location = new System.Drawing.Point(5, 100); - this.customPatchCheckBox.Name = "customPatchCheckBox"; - this.customPatchCheckBox.Size = new System.Drawing.Size(114, 17); - this.customPatchCheckBox.TabIndex = 11; - this.customPatchCheckBox.Text = "Use Custom Patch"; - this.customPatchCheckBox.UseVisualStyleBackColor = true; - this.customPatchCheckBox.CheckedChanged += new System.EventHandler(this.CustomPatchCheckBox_CheckedChanged); + this.generatedSeedLabel.AutoSize = true; + this.generatedSeedLabel.Location = new System.Drawing.Point(16, 13); + this.generatedSeedLabel.Name = "generatedSeedLabel"; + this.generatedSeedLabel.Size = new System.Drawing.Size(38, 13); + this.generatedSeedLabel.TabIndex = 3; + this.generatedSeedLabel.Text = "Seed: "; + // + // saveSpoilerButton + // + this.saveSpoilerButton.Location = new System.Drawing.Point(181, 180); + this.saveSpoilerButton.Name = "saveSpoilerButton"; + this.saveSpoilerButton.Size = new System.Drawing.Size(75, 23); + this.saveSpoilerButton.TabIndex = 2; + this.saveSpoilerButton.Text = "Save Spoiler"; + this.saveSpoilerButton.UseVisualStyleBackColor = true; + this.saveSpoilerButton.Click += new System.EventHandler(this.SaveSpoilerButton_Click); + // + // savePatchButton + // + this.savePatchButton.Location = new System.Drawing.Point(100, 180); + this.savePatchButton.Name = "savePatchButton"; + this.savePatchButton.Size = new System.Drawing.Size(75, 23); + this.savePatchButton.TabIndex = 1; + this.savePatchButton.Text = "Save Patch"; + this.savePatchButton.UseVisualStyleBackColor = true; + this.savePatchButton.Click += new System.EventHandler(this.SavePatchButton_Click); + // + // saveRomButton + // + this.saveRomButton.Location = new System.Drawing.Point(19, 180); + this.saveRomButton.Name = "saveRomButton"; + this.saveRomButton.Size = new System.Drawing.Size(75, 23); + this.saveRomButton.TabIndex = 0; + this.saveRomButton.Text = "Save ROM"; + this.saveRomButton.UseVisualStyleBackColor = true; + this.saveRomButton.Click += new System.EventHandler(this.SaveRomButton_Click); // // MainWindow // @@ -204,6 +294,8 @@ private void InitializeComponent() this.generalTab.ResumeLayout(false); this.generalTab.PerformLayout(); this.mainTabs.ResumeLayout(false); + this.generatedTab.ResumeLayout(false); + this.generatedTab.PerformLayout(); this.ResumeLayout(false); this.PerformLayout(); @@ -225,6 +317,14 @@ private void InitializeComponent() private System.Windows.Forms.CheckBox customPatchCheckBox; private System.Windows.Forms.TextBox customPatchPath; private System.Windows.Forms.Button browsePatchButton; + private System.Windows.Forms.Label seedLabel; + private System.Windows.Forms.TextBox seedField; + private System.Windows.Forms.TabPage generatedTab; + private System.Windows.Forms.Button saveSpoilerButton; + private System.Windows.Forms.Button savePatchButton; + private System.Windows.Forms.Button saveRomButton; + private System.Windows.Forms.Label generatedSeedLabel; + private System.Windows.Forms.Label generatedSeedValue; } } diff --git a/UI/MainWindow.cs b/UI/MainWindow.cs index 172d47db..340c7aa6 100644 --- a/UI/MainWindow.cs +++ b/UI/MainWindow.cs @@ -15,12 +15,17 @@ namespace MinishRandomizer { public partial class MainWindow : Form { + private ROM ROM_; private Shuffler shuffler; + private bool randomized; public MainWindow() { InitializeComponent(); + + // Initialize seed to random value + seedField.Text = new Random().Next().ToString(); } private void OpenToolStripMenuItem_Click(object sender, EventArgs e) @@ -32,7 +37,12 @@ private void Randomize_Click(object sender, EventArgs e) { if (ROM_ == null) { - return; + LoadRom(); + + if (ROM_ == null) + { + return; + } } @@ -43,7 +53,37 @@ private void Randomize_Click(object sender, EventArgs e) try { - shuffler.RandomizeLocations(); + if (int.TryParse(seedField.Text, out int seed)) + { + if (customLogicCheckBox.Checked) + { + shuffler.LoadLocations(customLogicPath.Text); + } + else + { + shuffler.LoadLocations(); + } + + + shuffler.RandomizeLocations(seed); + } + else + { + MessageBox.Show("The seed value is not valid!\nMake sure it's not too large and only contains numeric characters."); + } + + // First randomization needs to change the tab + if (!randomized) + { + // Change the tab to the seed output tab + mainTabs.Controls.Add(generatedTab); + mainTabs.SelectedTab = generatedTab; + } + + randomized = true; + + // Show ROM information on seed page + generatedSeedValue.Text = seed.ToString(); } catch (ShuffleException error) { @@ -82,31 +122,6 @@ private void LoadRom() } shuffler = new Shuffler(Path.GetDirectoryName(ROM.Instance.path)); - - try - { - if (customLogicCheckBox.Checked) - { - shuffler.LoadLocations(customLogicPath.Text); - } - else - { - shuffler.LoadLocations(null); - } - - if (customPatchCheckBox.Checked) - { - shuffler.PatchRom(customPatchPath.Text); - } - else - { - shuffler.PatchRom(null); - } - } - catch (ShuffleException error) - { - MessageBox.Show(error.Message); - } } private void CustomLogicCheckBox_CheckedChanged(object sender, EventArgs e) @@ -141,7 +156,7 @@ private void BrowsePatchButton_Click(object sender, EventArgs e) { OpenFileDialog ofd = new OpenFileDialog { - Filter = "UPS Patch|*.ups|All Files|*.*", + Filter = "UPS Patches|*.ups|All Files|*.*", Title = "Select Custom Patch" }; @@ -152,5 +167,86 @@ private void BrowsePatchButton_Click(object sender, EventArgs e) customPatchPath.Text = ofd.FileName; } + + private void SaveRomButton_Click(object sender, EventArgs e) + { + if (!randomized) + { + return; + } + + // Get the default name for the saved ROM + string fileName = $"MinishRandomizer_{shuffler.Seed}"; + + SaveFileDialog sfd = new SaveFileDialog + { + Filter = "GBA ROMs|*.gba|All Files|*.*", + Title = "Save ROM", + FileName = fileName + }; + + if (sfd.ShowDialog() != DialogResult.OK) + { + return; + } + + // Write output to ROM, then add patches + byte[] outputRom = shuffler.GetRandomizedRom(); + File.WriteAllBytes(sfd.FileName, outputRom); + } + + private void SavePatchButton_Click(object sender, EventArgs e) + { + if (!randomized) + { + return; + } + + // Get the default name for the saved patch + string fileName = $"MinishRandomizer_{shuffler.Seed}_patch"; + + SaveFileDialog sfd = new SaveFileDialog + { + Filter = "UPS Patches|*.ups|All Files|*.*", + Title = "Save Patch", + FileName = fileName + }; + + if (sfd.ShowDialog() != DialogResult.OK) + { + return; + } + + // Get UPS patch of ROM + //byte[] patch = + //File.WriteAllText(sfd.FileName, spoilerLog); + } + + private void SaveSpoilerButton_Click(object sender, EventArgs e) + { + if (!randomized) + { + return; + } + + // Get the default name for the saved patch + string fileName = $"MinishRandomizer_{shuffler.Seed}_spoiler"; + + SaveFileDialog sfd = new SaveFileDialog + { + Filter = "Text files|*.txt|All Files|*.*", + Title = "Save Spoiler", + FileName = fileName + }; + + if (sfd.ShowDialog() != DialogResult.OK) + { + return; + } + + // Write output to ROM, then add patches + string spoilerLog = shuffler.GetSpoiler(); + File.WriteAllText(sfd.FileName, spoilerLog); + } } } From 8e4d45c30ae15dddea6ff7f64367bb20c3d20ab1 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Fri, 2 Aug 2019 22:45:43 -0600 Subject: [PATCH 35/41] Build with EA instead of a UPS patch --- Randomizer/Shuffler.cs | 25 ++++++++++++++----------- UI/MainWindow.cs | 12 +++++++++++- 2 files changed, 25 insertions(+), 12 deletions(-) diff --git a/Randomizer/Shuffler.cs b/Randomizer/Shuffler.cs index 2d8a4092..992eda3a 100644 --- a/Randomizer/Shuffler.cs +++ b/Randomizer/Shuffler.cs @@ -159,6 +159,20 @@ private void AddLocation(Location location) } } + public void ApplyPatch(string romLocation, string patchFile = null) + { + if (string.IsNullOrEmpty(patchFile)) + { + // Get directory of MinishRandomizer + string assemblyPath = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + patchFile = assemblyPath + "/Patches/ROM buildfile.event"; + } + + string[] args = new[] { "A", "FE8", "-input:" + patchFile, "-output:" + romLocation }; + + ColorzCore.Program.Main(args); + } + /// /// Loads and shuffles all locations /// @@ -199,17 +213,6 @@ public void RandomizeLocations(int seed) // All locations should be filled at this point throw new ShuffleException($"There are {unfilledLocations.Count} unfilled locations!"); } - - using (MemoryStream ms = new MemoryStream(ROM.Instance.romData)) - { - Writer writer = new Writer(ms); - foreach (Location location in Locations) - { - location.WriteLocation(writer); - } - } - - File.WriteAllBytes(OutputDirectory + "/mcrando.gba", ROM.Instance.romData); } /// diff --git a/UI/MainWindow.cs b/UI/MainWindow.cs index 340c7aa6..212cac53 100644 --- a/UI/MainWindow.cs +++ b/UI/MainWindow.cs @@ -156,7 +156,7 @@ private void BrowsePatchButton_Click(object sender, EventArgs e) { OpenFileDialog ofd = new OpenFileDialog { - Filter = "UPS Patches|*.ups|All Files|*.*", + Filter = "Event Assembler Buildfiles|*.event|All Files|*.*", Title = "Select Custom Patch" }; @@ -193,6 +193,16 @@ private void SaveRomButton_Click(object sender, EventArgs e) // Write output to ROM, then add patches byte[] outputRom = shuffler.GetRandomizedRom(); File.WriteAllBytes(sfd.FileName, outputRom); + + if (customPatchCheckBox.Checked) + { + shuffler.ApplyPatch(sfd.FileName, customPatchPath.Text); + } + else + { + // Use the default patch + shuffler.ApplyPatch(sfd.FileName); + } } private void SavePatchButton_Click(object sender, EventArgs e) From 998f2f51f32d38cebadd4795fed732672082dc58 Mon Sep 17 00:00:00 2001 From: 21aslade <21aslade@go.dsdmail.net> Date: Sat, 3 Aug 2019 14:39:05 -0600 Subject: [PATCH 36/41] Copy language raws to build --- MinishRandomizer.csproj | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/MinishRandomizer.csproj b/MinishRandomizer.csproj index 7785ac53..30e730da 100644 --- a/MinishRandomizer.csproj +++ b/MinishRandomizer.csproj @@ -100,6 +100,7 @@ - xcopy /y /d /s "$(ProjectDir)Resources\Patches" "$(TargetDir)Patches\" + xcopy /y /d /s "$(ProjectDir)Resources\Patches" "$(TargetDir)Patches\"; +xcopy /y /d /s "$(ProjectDir)Vendor\Language Raws" "$(TargetDir)Language Raws\" \ No newline at end of file From b3bafd97ff109d6b5d76dd7c60fd4258503b40e8 Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Sun, 4 Aug 2019 14:38:00 +0100 Subject: [PATCH 37/41] fix build issues --- MinishRandomizer.csproj | 2 +- Resources/Patches/ROM Buildfile.event | 3 ++- Resources/Patches/logicfixes/installer.event | 2 +- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/MinishRandomizer.csproj b/MinishRandomizer.csproj index 30e730da..cbb5ffc2 100644 --- a/MinishRandomizer.csproj +++ b/MinishRandomizer.csproj @@ -100,7 +100,7 @@ - xcopy /y /d /s "$(ProjectDir)Resources\Patches" "$(TargetDir)Patches\"; + xcopy /y /d /s "$(ProjectDir)Resources\Patches" "$(TargetDir)Patches\" xcopy /y /d /s "$(ProjectDir)Vendor\Language Raws" "$(TargetDir)Language Raws\" \ No newline at end of file diff --git a/Resources/Patches/ROM Buildfile.event b/Resources/Patches/ROM Buildfile.event index f39160a6..bcfec42b 100644 --- a/Resources/Patches/ROM Buildfile.event +++ b/Resources/Patches/ROM Buildfile.event @@ -1,4 +1,4 @@ -#include "Extensions/Hack Installation.txt" +#define jumpToHack(offset) "BYTE 0x00 0x4B 0x18 0x47; POIN (offset|0x1)" //#define DEBUG 0 //#define TEST 0 @@ -9,6 +9,7 @@ ORG FreeSpace //#define neverSkipFusionCutscene 0 //define to never skip #define alwaysSkipFusionCutscene 0 //set to non-0 to skip, set to 0 to skip on button held + #ifdef TEST PUSH ORG $EDD8+2; BYTE 0x5C 0x70 //lady kinstone diff --git a/Resources/Patches/logicfixes/installer.event b/Resources/Patches/logicfixes/installer.event index 9a717016..2e474929 100644 --- a/Resources/Patches/logicfixes/installer.event +++ b/Resources/Patches/logicfixes/installer.event @@ -121,7 +121,7 @@ PUSH; ORG $94934; jumpToHack(jabberNut2); POP ALIGN 4 jabberNut2: #incbin "asm/jabberNut2.dmp" -PUSH; ORG $9D6411; String(Go get the nut!); BYTE 0; POP //gentari sends you get the nut +PUSH; ORG $9D6411; String("Go get the nut!"); BYTE 0; POP //gentari sends you get the nut //hyrule town fixes PUSH; ORG $4F0C8; SHORT 0x46C0 0x2000; POP //no carpenters blocking north exit From 07f500857efbda2869ea105f81e80d7177c1cadb Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Sun, 4 Aug 2019 15:12:37 +0100 Subject: [PATCH 38/41] prevent ocarina induced softlocking Co-Authored-By: leonarthcg --- MinishRandomizer.csproj | 1 + Resources/Patches/newGame.event | 4 ++++ 2 files changed, 5 insertions(+) diff --git a/MinishRandomizer.csproj b/MinishRandomizer.csproj index cbb5ffc2..cc2aab22 100644 --- a/MinishRandomizer.csproj +++ b/MinishRandomizer.csproj @@ -31,6 +31,7 @@ TRACE prompt 4 + latest diff --git a/Resources/Patches/newGame.event b/Resources/Patches/newGame.event index eb884dab..19b71b73 100644 --- a/Resources/Patches/newGame.event +++ b/Resources/Patches/newGame.event @@ -18,6 +18,7 @@ POIN startingFlags; WORD 0x2002C9C 0xF4 POIN hurdyFusion; WORD 0x2002C87 1 POIN kinstoneBag; WORD 0x2002B4B 1 POIN fusionNumber; WORD 0x2002B57 1 +POIN cityCrest; WORD 0x2002A83 1 #ifdef DEBUG POIN DEBUGITEMS; WORD 0x2002B32 7 POIN DEBUGUPGRADES; WORD 0x2002B3F 7 @@ -44,6 +45,9 @@ BYTE 0x22 /*area*/ 0x15 /*room*/ 4 /*facing direction*/ 0 /*transition type*/; S canOpenMenu: BYTE 1 +cityCrest: +BYTE 8 + haveMap: BYTE 0x40 From 063d89caa251b1cfef0d285bbb90ba5e5466ff36 Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Sun, 4 Aug 2019 15:24:45 +0100 Subject: [PATCH 39/41] add completion popup, disable patch button for now --- UI/MainWindow.Designer.cs | 1 + UI/MainWindow.cs | 2 ++ 2 files changed, 3 insertions(+) diff --git a/UI/MainWindow.Designer.cs b/UI/MainWindow.Designer.cs index 2e000053..f38411f4 100644 --- a/UI/MainWindow.Designer.cs +++ b/UI/MainWindow.Designer.cs @@ -256,6 +256,7 @@ private void InitializeComponent() // // savePatchButton // + this.savePatchButton.Enabled = false; this.savePatchButton.Location = new System.Drawing.Point(100, 180); this.savePatchButton.Name = "savePatchButton"; this.savePatchButton.Size = new System.Drawing.Size(75, 23); diff --git a/UI/MainWindow.cs b/UI/MainWindow.cs index 212cac53..c03c87bf 100644 --- a/UI/MainWindow.cs +++ b/UI/MainWindow.cs @@ -203,6 +203,8 @@ private void SaveRomButton_Click(object sender, EventArgs e) // Use the default patch shuffler.ApplyPatch(sfd.FileName); } + + MessageBox.Show("ROM successfully saved.", "Success", MessageBoxButtons.OK, MessageBoxIcon.Information); } private void SavePatchButton_Click(object sender, EventArgs e) From f4cefdf9c2ed29b09c9c615cd4c78a466e2b7089 Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Sun, 4 Aug 2019 15:50:54 +0100 Subject: [PATCH 40/41] Update README.md --- README.md | 22 +++++++++++++++++----- 1 file changed, 17 insertions(+), 5 deletions(-) diff --git a/README.md b/README.md index d76afe82..0c4351a4 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,23 @@ [![Discord](https://discordapp.com/api/guilds/342341497024151553/embed.png?style=shield)](https://discord.gg/ndFuWbV) [![Twitter Follow](https://img.shields.io/badge/follow-%40minishmaker-blue.svg?style=flat&logo=twitter)](https://twitter.com/minishmaker) -This program aims to randomize the item locations in such a way that all items are be available. This works sometimes, but currently is rather bugged. +This program creates a randomized version of The Legend of Zelda: The Minish Cap. -It's currently in early development; it likely will fail randomization and be broken until you reload the ROM. -When it's finished to the point that it can randomize the entire game without failing often, it'll be set up on a website similar to other popular randomizers. +Current version: 0.1.0a -Logic only goes up to the Cave of Flames currently; in the future it'll be extended to cover the whole game. +### Current Logic +At the moment, all key items, gold kinstones, and quest items are randomized. We plan to extend this as far as possible within the game. **Due to the alpha nature of the software, we can't promise every seed is completable! Let us know where you got stuck in our [Discord server](https://discord.gg/ndFuWbV) and we'll check it out.** -Please join our [Discord server](https://discord.gg/ndFuWbV) to discuss the project! +### Usage +Download the latest zip from the [releases](https://github.com/minishmaker/randomizer/releases) page, you'll need all the files it comes with for now. Choose a seed or leave it default, then hit randomize. You'll need to provide an EU copy of Minish Cap, don't ask us where to get one. Hitting 'Save ROM' will prompt you to save the randomized game. You can also save a spoiler log if you get stuck. + +### Known Issues +- Spoiler log could be much clearer, we're working on that. Let us know in the [Discord server](https://discord.gg/ndFuWbV) and we'll tell you what it means. +- Any route requiring chests on the bottom floor of Deepwood shrine (aside from the compass chest) are broken, as the lilypad doesn't spawn. +- The game softlocks after defeating Mazaal, the boss in Fortress of Winds. +- Items such as spin attack, bombs, fairy fountains, can still be obtained the way the game intends. + + + +Please join our [Discord server](https://discord.gg/ndFuWbV) to discuss the project and assist with testing! +We're also working on trackers for assisting with the randomizer, and would love any and all feedback. From 1d3f24b0b535c549f215cb2bbe8034fa23518c3d Mon Sep 17 00:00:00 2001 From: Mike Nisbet Date: Sun, 4 Aug 2019 15:54:04 +0100 Subject: [PATCH 41/41] Update README.md --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 0c4351a4..9dc86e31 100644 --- a/README.md +++ b/README.md @@ -17,7 +17,7 @@ Download the latest zip from the [releases](https://github.com/minishmaker/rando - Spoiler log could be much clearer, we're working on that. Let us know in the [Discord server](https://discord.gg/ndFuWbV) and we'll tell you what it means. - Any route requiring chests on the bottom floor of Deepwood shrine (aside from the compass chest) are broken, as the lilypad doesn't spawn. - The game softlocks after defeating Mazaal, the boss in Fortress of Winds. -- Items such as spin attack, bombs, fairy fountains, can still be obtained the way the game intends. +- Items such as spin attack, bombs, pegasus boots, fairy fountains, can still be obtained the way the game intends.