diff --git a/src/Randomizer.App/RomGenerator.cs b/src/Randomizer.App/RomGenerator.cs index e2feff65d..73d9fb7be 100644 --- a/src/Randomizer.App/RomGenerator.cs +++ b/src/Randomizer.App/RomGenerator.cs @@ -11,6 +11,7 @@ using Randomizer.App.ViewModels; using Randomizer.Shared; using Randomizer.Shared.Models; +using Randomizer.SMZ3; using Randomizer.SMZ3.FileData; using Randomizer.SMZ3.Generation; using Randomizer.SMZ3.Regions; @@ -42,28 +43,36 @@ public RomGenerator(Smz3Randomizer randomizer, /// True if the rom was generated successfully, false otherwise public bool GenerateRom(RandomizerOptions options, out string path, out string error, out GeneratedRom rom) { - var bytes = GenerateRomBytes(options, out var seed); - - var folderPath = Path.Combine(options.RomOutputPath, $"{DateTimeOffset.Now:yyyyMMdd-HHmmss}_{seed.Seed}"); - Directory.CreateDirectory(folderPath); - - var romFileName = $"SMZ3_Cas_{DateTimeOffset.Now:yyyyMMdd-HHmmss}_{seed.Seed}.sfc"; - var romPath = Path.Combine(folderPath, romFileName); - EnableMsu1Support(options, bytes, romPath, out var msuError); - Rom.UpdateChecksum(bytes); - File.WriteAllBytes(romPath, bytes); + try + { + var bytes = GenerateRomBytes(options, out var seed); + var folderPath = Path.Combine(options.RomOutputPath, $"{DateTimeOffset.Now:yyyyMMdd-HHmmss}_{seed.Seed}"); + Directory.CreateDirectory(folderPath); - var spoilerLog = GetSpoilerLog(options, seed); - var spoilerPath = Path.ChangeExtension(romPath, ".txt"); - File.WriteAllText(spoilerPath, spoilerLog); + var romFileName = $"SMZ3_Cas_{DateTimeOffset.Now:yyyyMMdd-HHmmss}_{seed.Seed}.sfc"; + var romPath = Path.Combine(folderPath, romFileName); + EnableMsu1Support(options, bytes, romPath, out var msuError); + Rom.UpdateChecksum(bytes); + File.WriteAllBytes(romPath, bytes); - rom = SaveSeedToDatabase(options, seed, romPath, spoilerPath); + var spoilerLog = GetSpoilerLog(options, seed); + var spoilerPath = Path.ChangeExtension(romPath, ".txt"); + File.WriteAllText(spoilerPath, spoilerLog); - error = msuError; - path = romPath; + rom = SaveSeedToDatabase(options, seed, romPath, spoilerPath); - return true; + error = msuError; + path = romPath; + return true; + } + catch (RandomizerGenerationException e) + { + path = null; + error = $"Error generating rom\n{e.Message}\nPlease try again. If it persists, try modifying your seed settings."; + rom = null; + return false; + } } /// @@ -144,12 +153,7 @@ protected byte[] GenerateRomBytes(RandomizerOptions options, out SeedData seed) /// The db entry for the generated rom protected GeneratedRom SaveSeedToDatabase(RandomizerOptions options, SeedData seed, string romPath, string spoilerPath) { - var jsonOptions = new JsonSerializerOptions - { - Encoder = JavaScriptEncoder.UnsafeRelaxedJsonEscaping - }; - - var settings = JsonSerializer.Serialize(options.ToConfig(), jsonOptions); + var settings = Config.ToConfigString(options.ToConfig(), true); var rom = new GeneratedRom() { @@ -186,11 +190,21 @@ private string GetSpoilerLog(RandomizerOptions options, SeedData seed) log.AppendLine(Underline($"SMZ3 Cas’ spoiler log", '=')); log.AppendLine($"Generated on {DateTime.Now:F}"); log.AppendLine($"Seed: {options.SeedOptions.Seed} (actual: {seed.Seed})"); - log.AppendLine($"Sword: {options.SeedOptions.SwordLocation}"); - log.AppendLine($"Morph: {options.SeedOptions.MorphLocation}"); - log.AppendLine($"Bombs: {options.SeedOptions.MorphBombsLocation}"); - log.AppendLine($"Shaktool: {options.SeedOptions.ShaktoolItem}"); - log.AppendLine($"Peg World: {options.SeedOptions.PegWorldItem}"); + log.AppendLine($"Settings String: {Config.ToConfigString(seed.Playthrough.Config, true)}"); + log.AppendLine($"Early Items: {string.Join(',', seed.Playthrough.Config.EarlyItems.Select(x => x.ToString()).ToArray())}"); + + var locationPrefs = new List(); + foreach (var (locationId, value) in seed.Playthrough.Config.LocationItems) + { + var itemPref = value < Enum.GetValues(typeof(ItemPool)).Length ? ((ItemPool)value).ToString() : ((ItemType)value).ToString(); + locationPrefs.Add($"{seed.Worlds[0].World.Locations.First(x => x.Id == locationId).Name} - {itemPref}"); + } + log.AppendLine($"Location Preferences: {string.Join(',', locationPrefs.ToArray())}"); + + var type = options.LogicConfig.GetType(); + var logicOptions = string.Join(',', type.GetProperties().Select(x => $"{x.Name}: {x.GetValue(seed.Playthrough.Config.LogicConfig)}")); + log.AppendLine($"Logic Options: {logicOptions}"); + log.AppendLine((options.SeedOptions.Keysanity ? "[Keysanity] " : "") + (options.SeedOptions.Race ? "[Race] " : "")); if (File.Exists(options.PatchOptions.Msu1Path)) diff --git a/src/Randomizer.App/ViewModels/RandomizerOptions.cs b/src/Randomizer.App/ViewModels/RandomizerOptions.cs index d28240857..aad6f18ee 100644 --- a/src/Randomizer.App/ViewModels/RandomizerOptions.cs +++ b/src/Randomizer.App/ViewModels/RandomizerOptions.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.ComponentModel; using System.IO; using System.Runtime.CompilerServices; @@ -54,7 +55,7 @@ public RandomizerOptions(GeneralOptions generalOptions, [JsonPropertyName("Logic")] public LogicConfig LogicConfig { get; set; } - public bool ItemLocationsExpanded { get; set; } = false; + public bool EarlyItemsExpanded { get; set; } = false; public bool CustomizationExpanded { get; set; } = false; public bool LogicExpanded { get; set; } = false; @@ -65,6 +66,7 @@ public RandomizerOptions(GeneralOptions generalOptions, public double WindowHeight { get; set; } = 600d; + public string RomOutputPath { get => Directory.Exists(GeneralOptions.RomOutputPath) @@ -84,34 +86,75 @@ public void Save(string path) File.WriteAllText(path, json); } - public Config ToConfig() => new() + public Config ToConfig() { - GameMode = GameMode.Normal, - Z3Logic = Z3Logic.Normal, - SMLogic = SMLogic.Normal, - ItemLocations = + if (string.IsNullOrWhiteSpace(SeedOptions.ConfigString)) { + return new() + { + GameMode = GameMode.Normal, + Z3Logic = Z3Logic.Normal, + SMLogic = SMLogic.Normal, + ItemLocations = + { + [ItemType.ProgressiveSword] = SeedOptions.SwordLocation, + [ItemType.Morph] = SeedOptions.MorphLocation, + [ItemType.Bombs] = SeedOptions.MorphBombsLocation, + [ItemType.Boots] = SeedOptions.PegasusBootsLocation, + [ItemType.SpaceJump] = SeedOptions.SpaceJumpLocation, + }, + ShaktoolItemPool = SeedOptions.ShaktoolItem, + PegWorldItemPool = SeedOptions.PegWorldItem, + KeyShuffle = SeedOptions.Keysanity ? KeyShuffle.Keysanity : KeyShuffle.None, + Race = SeedOptions.Race, + ExtendedMsuSupport = PatchOptions.CanEnableExtendedSoundtrack && PatchOptions.EnableExtendedSoundtrack, + ShuffleDungeonMusic = PatchOptions.ShuffleDungeonMusic, + HeartColor = PatchOptions.HeartColor, + LowHealthBeepSpeed = PatchOptions.LowHealthBeepSpeed, + DisableLowEnergyBeep = PatchOptions.DisableLowEnergyBeep, + CasualSMPatches = PatchOptions.CasualSuperMetroidPatches, + MenuSpeed = PatchOptions.MenuSpeed, + LinkName = PatchOptions.LinkSprite == Sprite.DefaultLink ? "Link" : PatchOptions.LinkSprite.Name, + SamusName = PatchOptions.SamusSprite == Sprite.DefaultSamus ? "Samus" : PatchOptions.SamusSprite.Name, + LocationItems = SeedOptions.LocationItems, + EarlyItems = SeedOptions.EarlyItems, + LogicConfig = LogicConfig.Clone() + }; + } + else { - [ItemType.ProgressiveSword] = SeedOptions.SwordLocation, - [ItemType.Morph] = SeedOptions.MorphLocation, - [ItemType.Bombs] = SeedOptions.MorphBombsLocation, - [ItemType.Boots] = SeedOptions.PegasusBootsLocation, - [ItemType.SpaceJump] = SeedOptions.SpaceJumpLocation, - }, - ShaktoolItemPool = SeedOptions.ShaktoolItem, - PegWorldItemPool = SeedOptions.PegWorldItem, - KeyShuffle = SeedOptions.Keysanity ? KeyShuffle.Keysanity : KeyShuffle.None, - Race = SeedOptions.Race, - ExtendedMsuSupport = PatchOptions.CanEnableExtendedSoundtrack && PatchOptions.EnableExtendedSoundtrack, - ShuffleDungeonMusic = PatchOptions.ShuffleDungeonMusic, - HeartColor = PatchOptions.HeartColor, - LowHealthBeepSpeed = PatchOptions.LowHealthBeepSpeed, - DisableLowEnergyBeep = PatchOptions.DisableLowEnergyBeep, - CasualSMPatches = PatchOptions.CasualSuperMetroidPatches, - MenuSpeed = PatchOptions.MenuSpeed, - LinkName = PatchOptions.LinkSprite == Sprite.DefaultLink ? "Link" : PatchOptions.LinkSprite.Name, - SamusName = PatchOptions.SamusSprite == Sprite.DefaultSamus ? "Samus" : PatchOptions.SamusSprite.Name, - LogicConfig = LogicConfig.Clone() - }; + var oldConfig = Config.FromConfigString(SeedOptions.ConfigString); + return new Config() + { + GameMode = GameMode.Normal, + Z3Logic = Z3Logic.Normal, + SMLogic = SMLogic.Normal, + ItemLocations = + { + [ItemType.ProgressiveSword] = SeedOptions.SwordLocation, + [ItemType.Morph] = SeedOptions.MorphLocation, + [ItemType.Bombs] = SeedOptions.MorphBombsLocation, + [ItemType.Boots] = SeedOptions.PegasusBootsLocation, + [ItemType.SpaceJump] = SeedOptions.SpaceJumpLocation, + }, + ShaktoolItemPool = SeedOptions.ShaktoolItem, + PegWorldItemPool = SeedOptions.PegWorldItem, + KeyShuffle = SeedOptions.Keysanity ? KeyShuffle.Keysanity : KeyShuffle.None, + Race = SeedOptions.Race, + ExtendedMsuSupport = PatchOptions.CanEnableExtendedSoundtrack && PatchOptions.EnableExtendedSoundtrack, + ShuffleDungeonMusic = PatchOptions.ShuffleDungeonMusic, + HeartColor = PatchOptions.HeartColor, + LowHealthBeepSpeed = PatchOptions.LowHealthBeepSpeed, + DisableLowEnergyBeep = PatchOptions.DisableLowEnergyBeep, + CasualSMPatches = PatchOptions.CasualSuperMetroidPatches, + MenuSpeed = PatchOptions.MenuSpeed, + LinkName = PatchOptions.LinkSprite == Sprite.DefaultLink ? "Link" : PatchOptions.LinkSprite.Name, + SamusName = PatchOptions.SamusSprite == Sprite.DefaultSamus ? "Samus" : PatchOptions.SamusSprite.Name, + LocationItems = oldConfig.LocationItems, + EarlyItems = oldConfig.EarlyItems, + LogicConfig = oldConfig.LogicConfig + }; + } + } public RandomizerOptions Clone() { diff --git a/src/Randomizer.App/ViewModels/SeedOptions.cs b/src/Randomizer.App/ViewModels/SeedOptions.cs index d2a30e680..45f96fa0b 100644 --- a/src/Randomizer.App/ViewModels/SeedOptions.cs +++ b/src/Randomizer.App/ViewModels/SeedOptions.cs @@ -6,7 +6,7 @@ using System.Text.Json; using System.Text.Json.Serialization; using System.Windows; - +using Randomizer.Shared; using Randomizer.SMZ3; namespace Randomizer.App.ViewModels @@ -19,6 +19,9 @@ public class SeedOptions [JsonIgnore] public string Seed { get; set; } + [JsonIgnore] + public string ConfigString { get; set; } + public ItemPlacement SwordLocation { get; set; } public ItemPlacement MorphLocation { get; set; } @@ -36,5 +39,9 @@ public class SeedOptions public bool Keysanity { get; set; } public bool Race { get; set; } + + public ISet EarlyItems { get; set; } = new HashSet(); + + public IDictionary LocationItems { get; set; } = new Dictionary(); } } diff --git a/src/Randomizer.App/Windows/GenerateRomWindow.xaml b/src/Randomizer.App/Windows/GenerateRomWindow.xaml index b749eefc7..1551be5c6 100644 --- a/src/Randomizer.App/Windows/GenerateRomWindow.xaml +++ b/src/Randomizer.App/Windows/GenerateRomWindow.xaml @@ -32,114 +32,33 @@ - + - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + Margin="24,11,11,11"> + + + + + + + + + + + + +