diff --git a/.github/workflows/build-debug.yaml b/.github/workflows/build-debug.yaml
index b4ded7d4e..35eb3f934 100644
--- a/.github/workflows/build-debug.yaml
+++ b/.github/workflows/build-debug.yaml
@@ -5,7 +5,9 @@ on:
branches-ignore:
- main
- testing
- - '5.1'
+ - extras
+ - sgl
+ - '5.2'
pull_request:
env:
diff --git a/.github/workflows/build-latest.yaml b/.github/workflows/build-latest.yaml
index 06f3f7c8d..54c37e8ae 100644
--- a/.github/workflows/build-latest.yaml
+++ b/.github/workflows/build-latest.yaml
@@ -5,7 +5,9 @@ on:
branches:
- main
- testing
- - '5.1'
+ - extras
+ - sgl
+ - '5.2'
env:
# I'm not a fan of the telemetry as-is, but this also suppresses some lines in the build log.
diff --git a/CoreSourceGenerator/CoreSourceGenerator.csproj b/CoreSourceGenerator/CoreSourceGenerator.csproj
index bed7384f5..b20c92c0b 100644
--- a/CoreSourceGenerator/CoreSourceGenerator.csproj
+++ b/CoreSourceGenerator/CoreSourceGenerator.csproj
@@ -11,10 +11,7 @@
Debug;Release;Unsafe Debug
AnyCPU
-
- true
- true
-
+
true
false
diff --git a/CoreSourceGenerator/FlagsSerializeGenerator.cs b/CoreSourceGenerator/FlagsSerializeGenerator.cs
index 2decebcdf..683f04bca 100644
--- a/CoreSourceGenerator/FlagsSerializeGenerator.cs
+++ b/CoreSourceGenerator/FlagsSerializeGenerator.cs
@@ -58,7 +58,7 @@ private static bool IsPartialClass(SyntaxNode node)
.Select(f =>
{
// Look up the source code for the declaration to find if it has a default value
- var equalsSyntax = f.DeclaringSyntaxReferences[0].GetSyntax() switch
+ var equalsSyntax = f.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() switch
{
PropertyDeclarationSyntax property => property.Initializer,
VariableDeclaratorSyntax variable => variable.Initializer,
@@ -81,18 +81,32 @@ private static bool IsPartialClass(SyntaxNode node)
var serializeFields = classSymbol.GetMembers()
.OfType()
.Where(f => !HasIgnoreInFlagsAttribute(f))
- .Select(f => new SerializedFieldInfo()
+ .Select(f =>
{
- FieldName = f.Name,
- FieldType = f.Type.ToDisplayString(),
- IsDifficultyOnly = HasDifficultyOnlyAttribute(f),
- IsConditionallyIncluded = HasConditionallyIncludedInFlagsAttribute(f),
- DefaultValue = GetDefaultValue(f),
- IsEnum = f.Type.TypeKind == TypeKind.Enum,
- EnumSymbol = f.Type.TypeKind == TypeKind.Enum ? f.Type as INamedTypeSymbol : null,
- Minimum = GetCustomMinimum(f),
- Maximum = GetCustomMaximum(f),
- CustomSerializerName = GetCustomFlagSerializer(f),
+ var dictionaryInterface = f.Type.AllInterfaces
+ .FirstOrDefault(i => i.OriginalDefinition.ToDisplayString().StartsWith("System.Collections.Generic.IDictionary"));
+ var innerType = dictionaryInterface != null ? dictionaryInterface.TypeArguments[0] : f.Type;
+
+ var equalsSyntax = f.DeclaringSyntaxReferences.FirstOrDefault()?.GetSyntax() switch
+ {
+ PropertyDeclarationSyntax property => property.Initializer,
+ VariableDeclaratorSyntax variable => variable.Initializer,
+ _ => null
+ };
+
+ return new SerializedFieldInfo()
+ {
+ FieldName = f.Name,
+ FieldType = f.Type.ToDisplayString(),
+ IsDifficultyOnly = HasDifficultyOnlyAttribute(f),
+ IsConditionallyIncluded = HasConditionallyIncludedInFlagsAttribute(f),
+ DefaultValue = equalsSyntax?.Value.ToString(),
+ IsEnum = f.Type.TypeKind == TypeKind.Enum,
+ EnumSymbol = f.Type.TypeKind == TypeKind.Enum ? f.Type as INamedTypeSymbol : null,
+ Minimum = GetCustomMinimum(f),
+ Maximum = GetCustomMaximum(f),
+ CustomSerializerName = GetCustomFlagSerializer(f),
+ };
}).ToList();
classInfo.SerializedFields.AddRange(serializeFields);
@@ -140,14 +154,6 @@ private static bool HasDifficultyOnlyAttribute(IFieldSymbol field)
.Any(attr => attr.AttributeClass?.Name.StartsWith("DifficultyOnly") ?? false);
}
- private static string GetDefaultValue(IFieldSymbol f)
- {
- var defaultAttr = f.GetAttributes()
- .FirstOrDefault(a => a.AttributeClass?.ToDisplayString() == "System.ComponentModel.DefaultValueAttribute");
- var arg = defaultAttr?.ConstructorArguments[0];
- return arg != null ? FormatArgument(arg.Value) : "default";
- }
-
private static bool HasIgnoreInFlagsAttribute(IFieldSymbol field)
{
return field.GetAttributes()
@@ -277,6 +283,7 @@ private static string GenerateCode(ClassGenerationInfo classInfo)
GenerateSerializeMethod(sb, classInfo.SerializedFields, indent);
GenerateDeserializeMethod(sb, classInfo.SerializedFields, indent);
+ GenerateWithMethod(sb, classInfo.ClassName, classInfo.ReactiveFields, indent);
// Generate helper methods for enum serialization
GenerateSerializerList(sb, classInfo.SerializedFields, indent);
@@ -316,7 +323,10 @@ private static void GenerateReactiveProperty(StringBuilder sb, ReactiveFieldInfo
sb.AppendLine($"{indent} {{");
sb.AppendLine($"{indent} {field.FieldName} = value;");
sb.AppendLine($"{indent} OnPropertyChanged(nameof({field.PropertyName}));");
- sb.AppendLine($"{indent} OnPropertyChanged(\"Flags\");");
+ sb.AppendLine($"{indent} if (!_inDeserializeFlags)");
+ sb.AppendLine($"{indent} {{");
+ sb.AppendLine($"{indent} OnPropertyChanged(\"Flags\");");
+ sb.AppendLine($"{indent} }}");
sb.AppendLine($"{indent} }}");
sb.AppendLine($"{indent} }}");
sb.AppendLine($"{indent} }}{defaultValue}");
@@ -400,6 +410,24 @@ private static void GenerateDeserializeMethod(StringBuilder sb, List fields, string indent)
+ {
+ sb.AppendLine();
+ sb.AppendLine($"{indent} public {className} With({className} other)");
+ sb.AppendLine($"{indent} {{");
+ sb.AppendLine($"{indent} {className} result = new();");
+ sb.AppendLine();
+
+ foreach (var field in fields)
+ {
+ sb.AppendLine($"{indent} result.{field.PropertyName} = !Equals(other.{field.PropertyName}, this.{field.PropertyName}) ? other.{field.PropertyName} : this.{field.PropertyName};");
+ }
+
+ sb.AppendLine();
+ sb.AppendLine($"{indent} return result;");
+ sb.AppendLine($"{indent} }}");
+ }
+
private static void GenerateSerializerList(StringBuilder sb, List fields, string indent)
{
sb.AppendLine();
diff --git a/CrossPlatformUI.Browser/CrossPlatformUI.Browser.csproj b/CrossPlatformUI.Browser/CrossPlatformUI.Browser.csproj
index 3bbe79176..a63b7f6d5 100644
--- a/CrossPlatformUI.Browser/CrossPlatformUI.Browser.csproj
+++ b/CrossPlatformUI.Browser/CrossPlatformUI.Browser.csproj
@@ -22,8 +22,14 @@
false
false
+ true
+ true
+
+
+
+
true
diff --git a/CrossPlatformUI.Browser/wwwroot/main.js b/CrossPlatformUI.Browser/wwwroot/main.js
index 97d8e590e..8d590e303 100644
--- a/CrossPlatformUI.Browser/wwwroot/main.js
+++ b/CrossPlatformUI.Browser/wwwroot/main.js
@@ -1,7 +1,7 @@
import { dotnet } from './_framework/dotnet.js'
import { compile } from './js65/libassembler.js'
-const BUNDLE_DOWNLOAD_SIZE = 80 * 1024 * 1024; // used for progress bar - doesn't have to be exact
+const BUNDLE_DOWNLOAD_SIZE = 71 * 1024 * 1024; // used for progress bar - doesn't have to be exact
const is_browser = typeof window != "undefined";
if (!is_browser) throw new Error(`Expected to be running in a browser`);
@@ -17,8 +17,22 @@ function showError(msg) {
(() => {
const origFetch = globalThis.fetch.bind(globalThis);
+ // This is a custom cache workaround for GitHub Pages arbitrarily
+ // sending different ETags for the same files. (It might depend on
+ // which CDN handles the request.)
+ // Instead rely on GitHub Pages setting Last - Modified to be the
+ // same for all files when the page is updated, including this script.
+ const deployTimestampPromise = (async () => {
+ try {
+ const r = await origFetch(import.meta.url, { cache: "default" });
+ return Date.parse(r.headers.get("Last-Modified") || "") || 0;
+ } catch {
+ return 0;
+ }
+ })();
+
// track cumulative progress across all boot resources
- let loadedKnownBytes = 0;
+ let completedBytes = 0;
const inFlight = new Map(); // id -> {loaded,total}
let started = false;
let finishTimer;
@@ -40,24 +54,57 @@ function showError(msg) {
};
const updateOverall = () => {
- updateProgress(loadedKnownBytes / BUNDLE_DOWNLOAD_SIZE);
+ let loaded = completedBytes;
+ for (const r of inFlight.values()) {
+ loaded += r.loaded;
+ }
+ updateProgress(loaded / BUNDLE_DOWNLOAD_SIZE);
};
- globalThis.fetch = async (input, init) => {
- const res = await origFetch(input, init).catch((e) => {
- showError("Network error while loading app. Please reload.");
- throw e;
- });
+ const fetchError = (e) => {
+ showError("Network error while loading app. Please reload.");
+ throw e;
+ };
+ globalThis.fetch = async (input, init) => {
+ const deployTs = await deployTimestampPromise;
const url = typeof input === "string" ? input : (input && input.url) || "";
- if (!isBootResource(url, res) || !res.body || res.bodyUsed) {
- return res; // leave non-boot fetches alone
+ const absoluteUrl = new URL(url, window.location.href).href;
+
+ var fetchResult;
+
+ if (deployTs !== 0) {
+ // check if transferSize is non-zero (there was no local cache)
+ const cachedFetchResult = await origFetch(input, { ...init, cache: "force-cache" }).catch(fetchError);
+ const perfEntries = performance?.getEntriesByName(absoluteUrl, "resource");
+ const lastEntry = perfEntries?.[perfEntries.length - 1];
+ const wasNetworkRequest = lastEntry?.transferSize > 0;
+
+ var staleCache = false;
+ if (!wasNetworkRequest) {
+ // if we have a reference timestamp, ensure the cached copy isn't
+ // much older than the deploy timestamp.
+ const lm = Date.parse(cachedFetchResult.headers.get("Last-Modified"));
+ staleCache = !lm || lm < deployTs - 180_000;
+ }
+
+ if (!staleCache) {
+ fetchResult = cachedFetchResult;
+ }
+ }
+
+ if (!fetchResult) {
+ fetchResult = await origFetch(input, { ...init, cache: "default" }).catch(fetchError);
+ }
+
+ if (!isBootResource(url, fetchResult) || !fetchResult.body || fetchResult.bodyUsed) {
+ return fetchResult; // leave non-boot fetches alone
}
started = true;
- const contentLength = parseInt(res.headers.get("Content-Length") || "0", 10);
+ const contentLength = parseInt(fetchResult.headers.get("Content-Length") || "0", 10);
const id = Math.random().toString(36).slice(2);
- const reader = res.body.getReader();
+ const reader = fetchResult.body.getReader();
inFlight.set(id, { loaded: 0, total: contentLength });
@@ -67,21 +114,26 @@ function showError(msg) {
showError("Error reading a resource stream. Please reload.");
throw e;
});
+
if (done) {
- controller.close();
- // account for any rounding misses
const r = inFlight.get(id);
- if (r && r.total > 0) loadedKnownBytes += (r.total - r.loaded);
- inFlight.delete(id);
+
+ if (r) {
+ completedBytes += r.loaded;
+ inFlight.delete(id);
+ }
+
updateOverall();
+ controller.close();
return;
}
+
controller.enqueue(value);
- const r = inFlight.get(id);
- if (r) {
- r.loaded += value.length;
- if (r.total > 0) loadedKnownBytes += value.length;
- inFlight.set(id, r);
+
+ const current = inFlight.get(id);
+ if (current) {
+ current.loaded += value.length;
+ inFlight.set(id, current);
}
updateOverall();
},
@@ -89,9 +141,9 @@ function showError(msg) {
});
return new Response(stream, {
- headers: res.headers,
- status: res.status,
- statusText: res.statusText
+ headers: fetchResult.headers,
+ status: fetchResult.status,
+ statusText: fetchResult.statusText
});
};
diff --git a/CrossPlatformUI/App.axaml b/CrossPlatformUI/App.axaml
index 7f0193b75..54562a97b 100644
--- a/CrossPlatformUI/App.axaml
+++ b/CrossPlatformUI/App.axaml
@@ -6,6 +6,7 @@
xmlns:dialogHostAvalonia="clr-namespace:DialogHostAvalonia;assembly=DialogHost.Avalonia"
xmlns:avalonia="clr-namespace:Material.Icons.Avalonia;assembly=Material.Icons.Avalonia"
xmlns:assists="clr-namespace:Material.Styles.Assists;assembly=Material.Styles"
+ xmlns:controls="clr-namespace:Material.Styles.Controls;assembly=Material.Styles"
xmlns:behaviors="clr-namespace:CrossPlatformUI.Behaviors"
x:Class="CrossPlatformUI.App">
@@ -103,6 +104,11 @@
+
+
diff --git a/CrossPlatformUI/App.axaml.cs b/CrossPlatformUI/App.axaml.cs
index dd1061c36..2c7439c5f 100644
--- a/CrossPlatformUI/App.axaml.cs
+++ b/CrossPlatformUI/App.axaml.cs
@@ -2,7 +2,6 @@
using System.Diagnostics;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
-using System.Reactive.Linq;
using System.Reflection;
using System.Text.Json;
using System.Text.Json.Serialization;
@@ -13,6 +12,7 @@
using Avalonia.Markup.Xaml;
using Material.Styles.Assists;
using Microsoft.Extensions.DependencyInjection;
+using ReactiveUI.Primitives;
using Z2Randomizer.RandomizerCore;
using CrossPlatformUI.Services;
using CrossPlatformUI.ViewModels;
@@ -96,10 +96,11 @@ public override void OnFrameworkInitializationCompleted()
var spriteLoaderService = App.Current?.Services?.GetService();
Debug.Assert(spriteLoaderService != null);
- spriteLoaderService.Sprites
- .Where(sprites => sprites.Count > 0)
- .Take(1)
- .Subscribe(sprites =>
+ SubscribeExtensions.Subscribe(
+ spriteLoaderService.Sprites
+ .Where(sprites => sprites.Count > 0)
+ .Take(1),
+ sprites =>
{
var sprite = sprites.FirstOrDefault(loaded => loaded.DisplayName == main.Config.SpriteName);
if (sprite != null)
diff --git a/CrossPlatformUI/AppViewLocator.cs b/CrossPlatformUI/AppViewLocator.cs
index 31c243b05..c876c65ec 100644
--- a/CrossPlatformUI/AppViewLocator.cs
+++ b/CrossPlatformUI/AppViewLocator.cs
@@ -21,8 +21,16 @@ public class AppViewLocator : IViewLocator
public IViewFor? ResolveView(object? viewModel, string? contract = null)
=> viewModel is null ? null : CreateView(viewModel);
+ public IViewFor? ResolveView(object? viewModel)
+ => viewModel is null ? null : CreateView(viewModel);
+
public IViewFor? ResolveView(string? contract = null) where TViewModel : class
{
throw new NotImplementedException();
}
+
+ public IViewFor? ResolveView() where TViewModel : class
+ {
+ throw new NotImplementedException();
+ }
}
diff --git a/CrossPlatformUI/Assets/palace-blockers.png b/CrossPlatformUI/Assets/palace-blockers.png
deleted file mode 100644
index 7493ce1b0..000000000
Binary files a/CrossPlatformUI/Assets/palace-blockers.png and /dev/null differ
diff --git a/CrossPlatformUI/CrossPlatformUI.csproj b/CrossPlatformUI/CrossPlatformUI.csproj
index 36406d1c1..f9f77db42 100644
--- a/CrossPlatformUI/CrossPlatformUI.csproj
+++ b/CrossPlatformUI/CrossPlatformUI.csproj
@@ -13,11 +13,6 @@
true
-
-
- $(NoWarn);IL2026
-
-
true
false
@@ -35,17 +30,23 @@
+
-
+
all
+
+
+
+
+
diff --git a/CrossPlatformUI/Lang/Resources.resx b/CrossPlatformUI/Lang/Resources.resx
index 9c3c371fd..49a78cbfb 100644
--- a/CrossPlatformUI/Lang/Resources.resx
+++ b/CrossPlatformUI/Lang/Resources.resx
@@ -231,9 +231,20 @@ group is the same as the number in that group that steal EXP in vanilla.
If selected, the amount of EXP each stealer steals is between 50% and 150% of the vanilla
amount.
-
-Randomly reassigns which enemies are immune to stabs. The number of enemies that are
-immune in each group is the same as the number in that group that are immune in vanilla.
+
+Vanilla:
+Uses the game's original sword immunities. (Tektites and Zoras)
+
+Shuffle:
+Randomly reassigns which enemies are immune to sword attacks and require the Fire spell
+to damage. On average, the same number of enemies are immune as in the vanilla game.
+
+Shuffle/None if Fire unusable:
+Uses Shuffle if the Fire spell is usable (not replaced by Dash nor linked with Fairy).
+Otherwise, removes all sword immunities.
+
+None:
+Removes all sword immunities.
Randomizes the color palettes of enemies and NPCs. Health bar color is also randomized.
@@ -701,9 +712,9 @@ linear.
make room.
Random Item Rooms Per Palace:
+ • Each palace gets 0–3 item rooms (varies by palace length/style).
• Clouds in the entrance screen and Ironknuckle statues by the entrance elevator
indicate how many items there are in the palace.
- • Each palace gets 0–3 item rooms (varies by palace length/style).
If selected, the normal zelda 2 rooms will be available for palace generation. Note that
@@ -775,8 +786,10 @@ considered duplicates of each other. Has no effect on vanilla palaces. Does not
prevent duplicates for Random Walk generated palaces.
-Allows rooms that require an item or spell to traverse to appear in any palace. Otherwise,
-palaces can only contain blocks according to the following table.
+Allows rooms that cannot be passed without a specific item or spell to appear in any palace.
+Otherwise, the allowed requirements for rooms in palaces are based on the items and spells
+needed to pass through every room in that palace in vanilla (from either direction).
+See the Wiki for the full table.
Removes dead-end rooms from the room pool. This helps newer players who are not yet
diff --git a/CrossPlatformUI/Presets/BeginnerPreset.cs b/CrossPlatformUI/Presets/BeginnerPreset.cs
index 7a70ff6e0..e12092791 100644
--- a/CrossPlatformUI/Presets/BeginnerPreset.cs
+++ b/CrossPlatformUI/Presets/BeginnerPreset.cs
@@ -12,28 +12,14 @@ public static class BeginnerPreset
//Start
StartWithCandle = true,
StartWithLife = true,
- ShuffleStartingItems = false,
- ShuffleStartingSpells = false,
- MaxHeartContainers = MaxHeartsOption.EIGHT,
- StartingHeartContainersMin = 4,
- StartingHeartContainersMax = 4,
StartingTechniques = StartingTechs.DOWNSTAB,
StartingLives = StartingLives.Lives5,
- IndeterminateOptionRate = IndeterminateOptionRate.HALF,
//Overworld
- PalacesCanSwapContinents = false,
- ShuffleGP = false,
- ShuffleEncounters = false,
- AllowUnsafePathEncounters = false,
- IncludeLavaInEncounterShuffle = false,
EncounterRate = EncounterRate.HALF,
- RiverDevilBlockerOption = RiverDevilBlockerOption.PATH,
EastRocks = true,
GenerateBaguWoods = false,
LessImportantLocationsOption = LessImportantLocationsOption.REMOVE,
- RestrictConnectionCaveShuffle = true,
- AllowConnectionCavesToBeBlocked = false,
GoodBoots = true,
HidePalace = false,
HideKasuto = false,
@@ -45,41 +31,24 @@ public static class BeginnerPreset
EastBiome = Biome.VANILLALIKE,
DmBiome = Biome.VANILLALIKE,
MazeBiome = Biome.VANILLALIKE,
- WestClimate = ClimateEnum.VANILLA_WEIGHTED_WEST,
- EastClimate = ClimateEnum.VANILLA_WEIGHTED_EAST,
- DmClimate = ClimateEnum.CLASSIC,
- ContinentConnectionType = ContinentConnectionType.NORMAL,
+ WestClimate = ClimateEnum.VANILLA_WEIGHTED,
+ EastClimate = ClimateEnum.VANILLA_WEIGHTED,
//Palaces
NormalPalaceStyle = PalaceStyle.VANILLA_WEIGHTED,
GpStyle = PalaceStyle.TOWER,
NormalPalaceLength = PalaceLengthOption.MEDIUM,
GpLength = PalaceLengthOption.SHORT,
- IncludeVanillaRooms = true,
Includev4_0Rooms = true,
Includev5_0Rooms = true,
- IncludeExpertRooms = false,
- TBirdRequired = true,
- RemoveTBird = false,
- PalacesToCompleteMin = 6,
- PalacesToCompleteMax = 6,
RestartAtPalacesOnGameOver = true,
Global5050JarDrop = true,
ReduceDripperVariance = true,
ChangePalacePallettes = true,
- RandomizeBossItemDrop = false,
- PalaceDropStyle = PalaceDropStyle.ENTRANCE,
- BossRoomsExitType = BossRoomsExitType.OVERWORLD,
NoDuplicateRoomsByLayout = true,
- BlockingRoomsInAnyPalace = false,
- HardBosses = false,
- PalaceItemRoomCount = PalaceItemRoomCount.ONE,
DarkLinkMinDistance = BossRoomMinDistance.SHORT,
//Levels
- AttackLevelCap = 8,
- MagicLevelCap = 8,
- LifeLevelCap = 8,
AttackEffectiveness = AttackEffectiveness.HIGH,
MagicEffectiveness = MagicEffectiveness.LOW_COST,
LifeEffectiveness = LifeEffectiveness.HIGH,
@@ -87,16 +56,10 @@ public static class BeginnerPreset
//Spells
ShuffleSpellLocations = true,
DisableMagicContainerRequirements = true,
- RandomizeSpellSpellEnemy = false,
- SwapUpAndDownStab = false,
- FireOption = FireOption.NORMAL,
//Enemies
ShuffleOverworldEnemies = true,
ShufflePalaceEnemies = true,
- DripperEnemyOption = DripperEnemyOption.ONLY_BOTS,
- MixLargeAndSmallEnemies = false,
- GeneratorsAlwaysMatch = true,
EnemyXPDrops = XPEffectiveness.RANDOM_HIGH,
@@ -105,16 +68,10 @@ public static class BeginnerPreset
ShuffleOverworldItems = true,
MixOverworldAndPalaceItems = true,
IncludePBagCavesInItemShuffle = true,
- IncludeSwordTechsInShuffle = false,
- IncludeQuestItemsInShuffle = false,
- IncludeSpellsInShuffle = false,
ShuffleSmallItems = true,
- RemoveSpellItems = false,
- ShufflePBagAmounts = false,
PalacesContainExtraKeys = true,
RandomizeNewKasutoJarRequirements = true,
- AllowImportantItemDuplicates = false,
PreventSpellItemChains = false,
//Drops
diff --git a/CrossPlatformUI/Presets/FullShufflePreset.cs b/CrossPlatformUI/Presets/FullShufflePreset.cs
index 9c20c5ff7..57825a380 100644
--- a/CrossPlatformUI/Presets/FullShufflePreset.cs
+++ b/CrossPlatformUI/Presets/FullShufflePreset.cs
@@ -9,14 +9,6 @@ public static class FullShufflePreset
{
//Start
StartWithCandle = true,
- ShuffleStartingItems = false,
- ShuffleStartingSpells = false,
- MaxHeartContainers = MaxHeartsOption.EIGHT,
- StartingHeartContainersMin = 4,
- StartingHeartContainersMax = 4,
- StartingTechniques = StartingTechs.NONE,
- StartingLives = StartingLives.Lives3,
- IndeterminateOptionRate = IndeterminateOptionRate.HALF,
//Overworld
PalacesCanSwapContinents = true,
@@ -28,8 +20,6 @@ public static class FullShufflePreset
RiverDevilBlockerOption = RiverDevilBlockerOption.RANDOM,
EastRocks = true,
GenerateBaguWoods = false,
- LessImportantLocationsOption = LessImportantLocationsOption.HIDE,
- RestrictConnectionCaveShuffle = true,
AllowConnectionCavesToBeBlocked = true,
GoodBoots = true,
HidePalace = null,
@@ -39,35 +29,26 @@ public static class FullShufflePreset
EastBiome = Biome.RANDOM_NO_VANILLA_OR_SHUFFLE,
DmBiome = Biome.RANDOM_NO_VANILLA_OR_SHUFFLE,
MazeBiome = Biome.VANILLALIKE,
- WestClimate = ClimateEnum.VANILLA_WEIGHTED_WEST,
- EastClimate = ClimateEnum.VANILLA_WEIGHTED_EAST,
- DmClimate = ClimateEnum.CLASSIC,
+ WestClimate = ClimateEnum.VANILLA_WEIGHTED,
+ EastClimate = ClimateEnum.VANILLA_WEIGHTED,
ContinentConnectionType = ContinentConnectionType.ANYTHING_GOES,
//Palaces
NormalPalaceStyle = PalaceStyle.RANDOM_PER_PALACE,
GpStyle = PalaceStyle.RANDOM_WALK,
- NormalPalaceLength = PalaceLengthOption.FULL,
GpLength = PalaceLengthOption.SHORT,
- IncludeVanillaRooms = true,
Includev4_0Rooms = true,
Includev5_0Rooms = true,
IncludeExpertRooms = true,
- TBirdRequired = true,
- RemoveTBird = false,
- PalacesToCompleteMin = 6,
- PalacesToCompleteMax = 6,
RestartAtPalacesOnGameOver = true,
Global5050JarDrop = true,
ReduceDripperVariance = true,
ChangePalacePallettes = true,
RandomizeBossItemDrop = true,
PalaceDropStyle = PalaceDropStyle.RANDOM,
- BossRoomsExitType = BossRoomsExitType.OVERWORLD,
NoDuplicateRoomsByLayout = true,
BlockingRoomsInAnyPalace = true,
HardBosses = true,
- PalaceItemRoomCount = PalaceItemRoomCount.ONE,
DarkLinkMinDistance = BossRoomMinDistance.SHORT,
//Levels
@@ -75,9 +56,6 @@ public static class FullShufflePreset
ShuffleMagicExperience = true,
ShuffleLifeExperience = true,
- AttackLevelCap = 8,
- MagicLevelCap = 8,
- LifeLevelCap = 8,
AttackEffectiveness = AttackEffectiveness.AVERAGE,
MagicEffectiveness = MagicEffectiveness.AVERAGE,
LifeEffectiveness = LifeEffectiveness.AVERAGE,
@@ -87,7 +65,6 @@ public static class FullShufflePreset
ShuffleSpellLocations = true,
DisableMagicContainerRequirements = true,
RandomizeSpellSpellEnemy = true,
- SwapUpAndDownStab = false,
FireOption = FireOption.PAIR_WITH_RANDOM,
//Enemies
@@ -95,13 +72,12 @@ public static class FullShufflePreset
ShufflePalaceEnemies = true,
DripperEnemyOption = DripperEnemyOption.ANY_GROUND_ENEMY,
MixLargeAndSmallEnemies = true,
- GeneratorsAlwaysMatch = true,
ShuffleEnemyHP = EnemyLifeOption.MEDIUM,
ShuffleBossHP = EnemyLifeOption.MEDIUM,
ShuffleXPStealers = true,
ShuffleXPStolenAmount = true,
- ShuffleSwordImmunity = true,
+ SwordImmunityOption = SwordImmunityOption.SHUFFLE,
EnemyXPDrops = XPEffectiveness.RANDOM,
//Items
@@ -114,11 +90,7 @@ public static class FullShufflePreset
IncludeSpellsInShuffle = true,
ShuffleSmallItems = true,
- RemoveSpellItems = false,
- ShufflePBagAmounts = false,
- PalacesContainExtraKeys = false,
RandomizeNewKasutoJarRequirements = true,
- AllowImportantItemDuplicates = false,
PreventSpellItemChains = false,
//Drops
@@ -131,6 +103,5 @@ public static class FullShufflePreset
EnableSpellItemHints = true,
EnableTownNameHints = true,
- RevealWalkthroughWalls = false,
};
}
diff --git a/CrossPlatformUI/Presets/MaxRando2025Preset.cs b/CrossPlatformUI/Presets/MaxRando2025Preset.cs
index 4f6a7f105..eb67c09f0 100644
--- a/CrossPlatformUI/Presets/MaxRando2025Preset.cs
+++ b/CrossPlatformUI/Presets/MaxRando2025Preset.cs
@@ -14,7 +14,6 @@ public static class MaxRando2025Preset
StartingHeartContainersMax = 8,
StartingTechniques = StartingTechs.RANDOM,
StartingLives = StartingLives.LivesRandom,
- IndeterminateOptionRate = IndeterminateOptionRate.HALF,
//Overworld
PalacesCanSwapContinents = true,
@@ -23,11 +22,6 @@ public static class MaxRando2025Preset
AllowUnsafePathEncounters = true,
IncludeLavaInEncounterShuffle = true,
EncounterRate = EncounterRate.HALF,
- RiverDevilBlockerOption = RiverDevilBlockerOption.PATH,
- EastRocks = false,
- GenerateBaguWoods = true,
- LessImportantLocationsOption = LessImportantLocationsOption.HIDE,
- RestrictConnectionCaveShuffle = true,
AllowConnectionCavesToBeBlocked = true,
GoodBoots = true,
HidePalace = null,
@@ -45,39 +39,25 @@ public static class MaxRando2025Preset
//Palaces
NormalPalaceStyle = PalaceStyle.RECONSTRUCTED,
GpStyle = PalaceStyle.RECONSTRUCTED,
- RandomStylesAllowVanilla = false,
- NormalPalaceLength = PalaceLengthOption.FULL,
GpLength = PalaceLengthOption.RANDOM,
- IncludeVanillaRooms = true,
Includev4_0Rooms = true,
- Includev5_0Rooms = false,
IncludeExpertRooms = true,
TBirdRequired = false,
- RemoveTBird = false,
PalacesToCompleteMin = 0,
- PalacesToCompleteMax = 6,
RestartAtPalacesOnGameOver = true,
- Global5050JarDrop = false,
- ReduceDripperVariance = false,
ChangePalacePallettes = true,
RandomizeBossItemDrop = true,
PalaceDropStyle = PalaceDropStyle.ANY_EXIT,
BossRoomsExitType = BossRoomsExitType.PALACE,
- NoDuplicateRoomsByLayout = false,
NoDuplicateRoomsByEnemies = true,
BlockingRoomsInAnyPalace = true,
HardBosses = true,
- PalaceItemRoomCount = PalaceItemRoomCount.ONE,
- DarkLinkMinDistance = BossRoomMinDistance.NONE,
//Levels
ShuffleAttackExperience = true,
ShuffleMagicExperience = true,
ShuffleLifeExperience = true,
- AttackLevelCap = 8,
- MagicLevelCap = 8,
- LifeLevelCap = 8,
AttackEffectiveness = AttackEffectiveness.AVERAGE,
MagicEffectiveness = MagicEffectiveness.AVERAGE,
LifeEffectiveness = LifeEffectiveness.AVERAGE,
@@ -87,7 +67,6 @@ public static class MaxRando2025Preset
ShuffleSpellLocations = true,
DisableMagicContainerRequirements = true,
RandomizeSpellSpellEnemy = true,
- SwapUpAndDownStab = false,
FireOption = FireOption.RANDOM,
//Enemies
@@ -95,13 +74,12 @@ public static class MaxRando2025Preset
ShufflePalaceEnemies = true,
DripperEnemyOption = DripperEnemyOption.ANY_GROUND_ENEMY,
MixLargeAndSmallEnemies = true,
- GeneratorsAlwaysMatch = true,
ShuffleEnemyHP = EnemyLifeOption.MEDIUM,
ShuffleBossHP = EnemyLifeOption.MEDIUM,
ShuffleXPStealers = true,
ShuffleXPStolenAmount = true,
- ShuffleSwordImmunity = true,
+ SwordImmunityOption = SwordImmunityOption.SHUFFLE,
EnemyXPDrops = XPEffectiveness.RANDOM,
//Items
@@ -114,11 +92,8 @@ public static class MaxRando2025Preset
IncludeSpellsInShuffle = true,
ShuffleSmallItems = true,
- RemoveSpellItems = false,
ShufflePBagAmounts = true,
- PalacesContainExtraKeys = false,
RandomizeNewKasutoJarRequirements = true,
- AllowImportantItemDuplicates = false,
PreventSpellItemChains = false,
//Drops
@@ -131,6 +106,5 @@ public static class MaxRando2025Preset
EnableSpellItemHints = true,
EnableTownNameHints = true,
- RevealWalkthroughWalls = false,
};
}
diff --git a/CrossPlatformUI/Presets/MaxRandoPreset.cs b/CrossPlatformUI/Presets/MaxRandoPreset.cs
index b807f7f1b..869b15b42 100644
--- a/CrossPlatformUI/Presets/MaxRandoPreset.cs
+++ b/CrossPlatformUI/Presets/MaxRandoPreset.cs
@@ -16,7 +16,6 @@ public static class MaxRandoPreset
StartingMagicContainersMax = 8,
StartingTechniques = StartingTechs.RANDOM,
StartingLives = StartingLives.LivesRandom,
- IndeterminateOptionRate = IndeterminateOptionRate.HALF,
//Overworld
PalacesCanSwapContinents = true,
@@ -29,7 +28,6 @@ public static class MaxRandoPreset
EastRocks = null,
GenerateBaguWoods = null,
LessImportantLocationsOption = LessImportantLocationsOption.RANDOM,
- RestrictConnectionCaveShuffle = true,
AllowConnectionCavesToBeBlocked = true,
GoodBoots = true,
HidePalace = null,
@@ -47,17 +45,13 @@ public static class MaxRandoPreset
//Palaces
NormalPalaceStyle = PalaceStyle.RANDOM_PER_PALACE,
GpStyle = PalaceStyle.RANDOM,
- RandomStylesAllowVanilla = false,
NormalPalaceLength = PalaceLengthOption.RANDOM,
GpLength = PalaceLengthOption.RANDOM,
- IncludeVanillaRooms = true,
Includev4_0Rooms = true,
Includev5_0Rooms = true,
IncludeExpertRooms = true,
TBirdRequired = false,
- RemoveTBird = false,
PalacesToCompleteMin = 0,
- PalacesToCompleteMax = 6,
RestartAtPalacesOnGameOver = true,
Global5050JarDrop = true,
ReduceDripperVariance = true,
@@ -69,16 +63,12 @@ public static class MaxRandoPreset
BlockingRoomsInAnyPalace = true,
HardBosses = true,
PalaceItemRoomCount = PalaceItemRoomCount.RANDOM_INCLUDE_ZERO,
- DarkLinkMinDistance = BossRoomMinDistance.NONE,
//Levels
ShuffleAttackExperience = true,
ShuffleMagicExperience = true,
ShuffleLifeExperience = true,
- AttackLevelCap = 8,
- MagicLevelCap = 8,
- LifeLevelCap = 8,
AttackEffectiveness = AttackEffectiveness.AVERAGE,
MagicEffectiveness = MagicEffectiveness.AVERAGE,
LifeEffectiveness = LifeEffectiveness.AVERAGE,
@@ -88,7 +78,6 @@ public static class MaxRandoPreset
ShuffleSpellLocations = true,
DisableMagicContainerRequirements = true,
RandomizeSpellSpellEnemy = true,
- SwapUpAndDownStab = false,
FireOption = FireOption.RANDOM,
//Enemies
@@ -96,13 +85,12 @@ public static class MaxRandoPreset
ShufflePalaceEnemies = true,
DripperEnemyOption = DripperEnemyOption.ANY_GROUND_ENEMY,
MixLargeAndSmallEnemies = true,
- GeneratorsAlwaysMatch = true,
ShuffleEnemyHP = EnemyLifeOption.MEDIUM,
ShuffleBossHP = EnemyLifeOption.MEDIUM,
ShuffleXPStealers = true,
ShuffleXPStolenAmount = true,
- ShuffleSwordImmunity = true,
+ SwordImmunityOption = SwordImmunityOption.SHUFFLE,
EnemyXPDrops = XPEffectiveness.WIDE,
//Items
@@ -115,11 +103,8 @@ public static class MaxRandoPreset
IncludeSpellsInShuffle = true,
ShuffleSmallItems = true,
- RemoveSpellItems = false,
ShufflePBagAmounts = true,
- PalacesContainExtraKeys = false,
RandomizeNewKasutoJarRequirements = true,
- AllowImportantItemDuplicates = false,
PreventSpellItemChains = false,
//Drops
@@ -132,6 +117,5 @@ public static class MaxRandoPreset
EnableSpellItemHints = true,
EnableTownNameHints = true,
- RevealWalkthroughWalls = false,
};
}
diff --git a/CrossPlatformUI/Presets/NormalPreset.cs b/CrossPlatformUI/Presets/NormalPreset.cs
index da9c12433..8d0985e2f 100644
--- a/CrossPlatformUI/Presets/NormalPreset.cs
+++ b/CrossPlatformUI/Presets/NormalPreset.cs
@@ -9,40 +9,26 @@ public static class NormalPreset
{
//Start
StartWithCandle = true,
- ShuffleStartingItems = false,
- ShuffleStartingSpells = false,
- MaxHeartContainers = MaxHeartsOption.EIGHT,
- StartingHeartContainersMin = 4,
- StartingHeartContainersMax = 4,
- StartingTechniques = StartingTechs.NONE,
- StartingLives = StartingLives.Lives3,
- IndeterminateOptionRate = IndeterminateOptionRate.HALF,
//Overworld
PalacesCanSwapContinents = true,
ShuffleGP = true,
- ShuffleEncounters = false,
- AllowUnsafePathEncounters = false,
- IncludeLavaInEncounterShuffle = false,
EncounterRate = EncounterRate.HALF,
RiverDevilBlockerOption = RiverDevilBlockerOption.RANDOM,
EastRocks = true,
GenerateBaguWoods = false,
LessImportantLocationsOption = LessImportantLocationsOption.REMOVE,
- RestrictConnectionCaveShuffle = true,
AllowConnectionCavesToBeBlocked = true,
GoodBoots = true,
HidePalace = null,
HideKasuto = null,
- ShuffleWhichLocationIsHidden = false,
DmSize = DmSizeOption.MEDIUM,
WestBiome = Biome.RANDOM_NO_VANILLA_OR_SHUFFLE,
EastBiome = Biome.RANDOM_NO_VANILLA_OR_SHUFFLE,
MazeBiome = Biome.VANILLALIKE,
DmBiome = Biome.RANDOM_NO_VANILLA_OR_SHUFFLE,
- WestClimate = ClimateEnum.VANILLA_WEIGHTED_WEST,
- EastClimate = ClimateEnum.VANILLA_WEIGHTED_EAST,
- DmClimate = ClimateEnum.CLASSIC,
+ WestClimate = ClimateEnum.VANILLA_WEIGHTED,
+ EastClimate = ClimateEnum.VANILLA_WEIGHTED,
ContinentConnectionType = ContinentConnectionType.TRANSPORTATION_SHUFFLE,
//Palaces
@@ -50,25 +36,16 @@ public static class NormalPreset
GpStyle = PalaceStyle.RANDOM_WALK,
NormalPalaceLength = PalaceLengthOption.MEDIUM,
GpLength = PalaceLengthOption.SHORT,
- IncludeVanillaRooms = true,
Includev4_0Rooms = true,
Includev5_0Rooms = true,
- IncludeExpertRooms = false,
- TBirdRequired = true,
- RemoveTBird = false,
- PalacesToCompleteMin = 6,
- PalacesToCompleteMax = 6,
RestartAtPalacesOnGameOver = true,
Global5050JarDrop = true,
ReduceDripperVariance = true,
ChangePalacePallettes = true,
- RandomizeBossItemDrop = false,
PalaceDropStyle = PalaceDropStyle.RANDOM,
- BossRoomsExitType = BossRoomsExitType.OVERWORLD,
NoDuplicateRoomsByLayout = true,
BlockingRoomsInAnyPalace = true,
HardBosses = true,
- PalaceItemRoomCount = PalaceItemRoomCount.ONE,
DarkLinkMinDistance = BossRoomMinDistance.SHORT,
//Levels
@@ -76,33 +53,24 @@ public static class NormalPreset
ShuffleMagicExperience = true,
ShuffleLifeExperience = true,
- AttackLevelCap = 8,
- MagicLevelCap = 8,
- LifeLevelCap = 8,
AttackEffectiveness = AttackEffectiveness.AVERAGE,
MagicEffectiveness = MagicEffectiveness.AVERAGE,
LifeEffectiveness = LifeEffectiveness.AVERAGE,
//Spells
- ShuffleLifeRefillAmount = false,
ShuffleSpellLocations = true,
DisableMagicContainerRequirements = true,
- RandomizeSpellSpellEnemy = false,
- SwapUpAndDownStab = false,
- FireOption = FireOption.NORMAL,
//Enemies
ShuffleOverworldEnemies = true,
ShufflePalaceEnemies = true,
DripperEnemyOption = DripperEnemyOption.EASIER_GROUND_ENEMIES,
- MixLargeAndSmallEnemies = false,
- GeneratorsAlwaysMatch = true,
ShuffleEnemyHP = EnemyLifeOption.MEDIUM,
ShuffleBossHP = EnemyLifeOption.MEDIUM,
ShuffleXPStealers = true,
ShuffleXPStolenAmount = true,
- ShuffleSwordImmunity = true,
+ SwordImmunityOption = SwordImmunityOption.SHUFFLE,
EnemyXPDrops = XPEffectiveness.RANDOM,
//Items
@@ -110,16 +78,10 @@ public static class NormalPreset
ShuffleOverworldItems = true,
MixOverworldAndPalaceItems = true,
IncludePBagCavesInItemShuffle = true,
- IncludeSwordTechsInShuffle = false,
IncludeQuestItemsInShuffle = true,
- IncludeSpellsInShuffle = false,
ShuffleSmallItems = true,
- RemoveSpellItems = false,
- ShufflePBagAmounts = false,
- PalacesContainExtraKeys = false,
RandomizeNewKasutoJarRequirements = true,
- AllowImportantItemDuplicates = false,
PreventSpellItemChains = false,
//Drops
@@ -132,6 +94,5 @@ public static class NormalPreset
EnableSpellItemHints = true,
EnableTownNameHints = true,
- RevealWalkthroughWalls = false,
};
}
diff --git a/CrossPlatformUI/Presets/RandomPercentPreset.cs b/CrossPlatformUI/Presets/RandomPercentPreset.cs
index c42abdacc..5984237c0 100644
--- a/CrossPlatformUI/Presets/RandomPercentPreset.cs
+++ b/CrossPlatformUI/Presets/RandomPercentPreset.cs
@@ -15,14 +15,11 @@ public static class RandomPercentPreset
StartingHeartContainersMax = 8,
StartingTechniques = StartingTechs.RANDOM,
StartingLives = StartingLives.LivesRandom,
- IndeterminateOptionRate = IndeterminateOptionRate.HALF,
//Overworld
PalacesCanSwapContinents = null,
ShuffleGP = null,
ShuffleEncounters = null,
- AllowUnsafePathEncounters = false,
- IncludeLavaInEncounterShuffle = false,
EncounterRate = EncounterRate.HALF,
RiverDevilBlockerOption = RiverDevilBlockerOption.RANDOM,
EastRocks = null,
@@ -53,30 +50,21 @@ public static class RandomPercentPreset
Includev4_0Rooms = null,
Includev5_0Rooms = null,
TBirdRequired = null,
- RemoveTBird = false,
PalacesToCompleteMin = 0,
- PalacesToCompleteMax = 6,
RestartAtPalacesOnGameOver = true,
Global5050JarDrop = null,
- ReduceDripperVariance = false,
ChangePalacePallettes = true,
RandomizeBossItemDrop = true,
PalaceDropStyle = PalaceDropStyle.ANYTHING_GOES,
BossRoomsExitType = BossRoomsExitType.RANDOM_PER_PALACE,
NoDuplicateRoomsByLayout = true,
BlockingRoomsInAnyPalace = true,
- HardBosses = false,
- PalaceItemRoomCount = PalaceItemRoomCount.ONE,
- DarkLinkMinDistance = BossRoomMinDistance.NONE,
//Levels
ShuffleAttackExperience = true,
ShuffleMagicExperience = true,
ShuffleLifeExperience = true,
- AttackLevelCap = 8,
- MagicLevelCap = 8,
- LifeLevelCap = 8,
AttackEffectiveness = AttackEffectiveness.AVERAGE,
MagicEffectiveness = MagicEffectiveness.AVERAGE,
LifeEffectiveness = LifeEffectiveness.AVERAGE,
@@ -100,7 +88,7 @@ public static class RandomPercentPreset
ShuffleBossHP = EnemyLifeOption.MEDIUM,
ShuffleXPStealers = true,
ShuffleXPStolenAmount = true,
- ShuffleSwordImmunity = true,
+ SwordImmunityOption = SwordImmunityOption.SHUFFLE,
EnemyXPDrops = XPEffectiveness.RANDOM,
//Items
@@ -117,7 +105,6 @@ public static class RandomPercentPreset
ShufflePBagAmounts = null,
PalacesContainExtraKeys = null,
RandomizeNewKasutoJarRequirements = true,
- AllowImportantItemDuplicates = false,
PreventSpellItemChains = false,
//Drops
@@ -130,6 +117,5 @@ public static class RandomPercentPreset
EnableSpellItemHints = null,
EnableTownNameHints = null,
- RevealWalkthroughWalls = false,
};
}
diff --git a/CrossPlatformUI/Presets/Sgl2025Preset.cs b/CrossPlatformUI/Presets/Sgl2025Preset.cs
index 2bb6ea103..09ed259aa 100644
--- a/CrossPlatformUI/Presets/Sgl2025Preset.cs
+++ b/CrossPlatformUI/Presets/Sgl2025Preset.cs
@@ -9,40 +9,23 @@ public static class Sgl2025Preset
//Start
StartWithCandle = true,
StartWithCross = true,
- ShuffleStartingItems = false,
StartWithFire = true,
- ShuffleStartingSpells = false,
MaxHeartContainers = MaxHeartsOption.SIX,
StartingHeartContainersMin = 3,
StartingHeartContainersMax = 3,
- StartingTechniques = StartingTechs.NONE,
- StartingLives = StartingLives.Lives3,
- IndeterminateOptionRate = IndeterminateOptionRate.HALF,
//Overworld
PalacesCanSwapContinents = true,
ShuffleGP = true,
- ShuffleEncounters = false,
- AllowUnsafePathEncounters = false,
- IncludeLavaInEncounterShuffle = false,
EncounterRate = EncounterRate.HALF,
- RiverDevilBlockerOption = RiverDevilBlockerOption.PATH,
- EastRocks = false,
- GenerateBaguWoods = true,
- LessImportantLocationsOption = LessImportantLocationsOption.HIDE,
- RestrictConnectionCaveShuffle = true,
AllowConnectionCavesToBeBlocked = true,
GoodBoots = true,
HidePalace = null,
HideKasuto = null,
- ShuffleWhichLocationIsHidden = false,
WestBiome = Biome.RANDOM_NO_VANILLA_OR_SHUFFLE,
EastBiome = Biome.RANDOM_NO_VANILLA_OR_SHUFFLE,
MazeBiome = Biome.VANILLALIKE,
DmBiome = Biome.RANDOM_NO_VANILLA_OR_SHUFFLE,
- WestClimate = ClimateEnum.CLASSIC,
- EastClimate = ClimateEnum.CLASSIC,
- DmClimate = ClimateEnum.CLASSIC,
ContinentConnectionType = ContinentConnectionType.TRANSPORTATION_SHUFFLE,
//Palaces
@@ -50,27 +33,15 @@ public static class Sgl2025Preset
GpStyle = PalaceStyle.RECONSTRUCTED,
NormalPalaceLength = PalaceLengthOption.SHORT,
GpLength = PalaceLengthOption.MEDIUM,
- IncludeVanillaRooms = true,
Includev4_0Rooms = true,
- Includev5_0Rooms = false,
IncludeExpertRooms = true,
- TBirdRequired = true,
- PalacesToCompleteMin = 6,
- PalacesToCompleteMax = 6,
RestartAtPalacesOnGameOver = true,
- Global5050JarDrop = false,
- ReduceDripperVariance = false,
ChangePalacePallettes = true,
- RandomizeBossItemDrop = false,
PalaceDropStyle = PalaceDropStyle.ANY_EXIT,
- BossRoomsExitType = BossRoomsExitType.OVERWORLD,
- NoDuplicateRoomsByLayout = false,
NoDuplicateRoomsByEnemies = true,
BlockingRoomsInAnyPalace = true,
HardBosses = true,
AggressiveTbird = true,
- PalaceItemRoomCount = PalaceItemRoomCount.ONE,
- DarkLinkMinDistance = BossRoomMinDistance.NONE,
//Levels
ShuffleAttackExperience = true,
@@ -78,55 +49,33 @@ public static class Sgl2025Preset
ShuffleLifeExperience = true,
AttackLevelCap = 6,
- MagicLevelCap = 8,
- LifeLevelCap = 8,
AttackEffectiveness = AttackEffectiveness.AVERAGE_LOW, // was AttackValues = [2, 3, 4, 6, 8, 12, 14, 16];
MagicEffectiveness = MagicEffectiveness.AVERAGE,
- LifeEffectiveness = LifeEffectiveness.VANILLA,
//Spells
- ShuffleLifeRefillAmount = false,
ShuffleSpellLocations = true,
DisableMagicContainerRequirements = true,
- RandomizeSpellSpellEnemy = false,
SwapUpAndDownStab = true,
- FireOption = FireOption.NORMAL,
// +"Expensive Thunder"
//Enemies
ShuffleOverworldEnemies = true,
ShufflePalaceEnemies = true,
- DripperEnemyOption = DripperEnemyOption.ONLY_BOTS,
MixLargeAndSmallEnemies = true,
- GeneratorsAlwaysMatch = true,
ShuffleEnemyHP = EnemyLifeOption.MEDIUM, // was 100-150%
- ShuffleBossHP = EnemyLifeOption.VANILLA,
ShuffleXPStealers = true,
ShuffleXPStolenAmount = true,
- ShuffleSwordImmunity = false,
EnemyXPDrops = XPEffectiveness.SLIGHTLY_HIGH, // was +0 to +2
//Items
ShufflePalaceItems = true,
ShuffleOverworldItems = true,
MixOverworldAndPalaceItems = true,
- IncludePBagCavesInItemShuffle = false,
- IncludeSwordTechsInShuffle = false,
- IncludeQuestItemsInShuffle = false,
- IncludeSpellsInShuffle = false,
- ShuffleSmallItems = false,
- RemoveSpellItems = false,
- ShufflePBagAmounts = false,
- PalacesContainExtraKeys = false,
- RandomizeNewKasutoJarRequirements = false,
- AllowImportantItemDuplicates = false,
PreventSpellItemChains = false,
//Drops
- ShuffleItemDropFrequency = false,
- RandomizeDrops = false,
StandardizeDrops = true,
SmallEnemiesCanDropBlueJar = true,
LargeEnemiesCanDropRedJar = true,
@@ -137,6 +86,5 @@ public static class Sgl2025Preset
EnableSpellItemHints = true,
EnableTownNameHints = true,
- RevealWalkthroughWalls = false,
};
}
diff --git a/CrossPlatformUI/Presets/StandardPreset.cs b/CrossPlatformUI/Presets/StandardPreset.cs
index 4a28d6f8a..2827c986c 100644
--- a/CrossPlatformUI/Presets/StandardPreset.cs
+++ b/CrossPlatformUI/Presets/StandardPreset.cs
@@ -8,15 +8,6 @@ public static class StandardPreset
public static readonly RandomizerConfiguration Preset = new()
{
//Start
- StartWithCandle = false,
- ShuffleStartingItems = false,
- ShuffleStartingSpells = false,
- MaxHeartContainers = MaxHeartsOption.EIGHT,
- StartingHeartContainersMin = 4,
- StartingHeartContainersMax = 4,
- StartingTechniques = StartingTechs.NONE,
- StartingLives = StartingLives.Lives3,
- IndeterminateOptionRate = IndeterminateOptionRate.HALF,
//Overworld
PalacesCanSwapContinents = true,
@@ -28,8 +19,6 @@ public static class StandardPreset
RiverDevilBlockerOption = RiverDevilBlockerOption.RANDOM,
EastRocks = true,
GenerateBaguWoods = false,
- LessImportantLocationsOption = LessImportantLocationsOption.HIDE,
- RestrictConnectionCaveShuffle = true,
AllowConnectionCavesToBeBlocked = true,
GoodBoots = true,
HidePalace = null,
@@ -39,31 +28,21 @@ public static class StandardPreset
EastBiome = Biome.RANDOM_NO_VANILLA_OR_SHUFFLE,
MazeBiome = Biome.VANILLALIKE,
DmBiome = Biome.RANDOM_NO_VANILLA_OR_SHUFFLE,
- WestClimate = ClimateEnum.CLASSIC,
- EastClimate = ClimateEnum.CLASSIC,
- DmClimate = ClimateEnum.CLASSIC,
ContinentConnectionType = ContinentConnectionType.ANYTHING_GOES,
//Palaces
NormalPalaceStyle = PalaceStyle.RANDOM_WALK,
GpStyle = PalaceStyle.RANDOM_WALK,
- NormalPalaceLength = PalaceLengthOption.FULL,
GpLength = PalaceLengthOption.SHORT,
- IncludeVanillaRooms = true,
Includev4_0Rooms = true,
Includev5_0Rooms = true,
IncludeExpertRooms = true,
- TBirdRequired = true,
- RemoveTBird = false,
- PalacesToCompleteMin = 6,
- PalacesToCompleteMax = 6,
RestartAtPalacesOnGameOver = true,
Global5050JarDrop = true,
ReduceDripperVariance = true,
ChangePalacePallettes = true,
RandomizeBossItemDrop = true,
PalaceDropStyle = PalaceDropStyle.ANY_EXIT,
- BossRoomsExitType = BossRoomsExitType.OVERWORLD,
NoDuplicateRoomsByLayout = true,
BlockingRoomsInAnyPalace = true,
HardBosses = true,
@@ -75,9 +54,6 @@ public static class StandardPreset
ShuffleMagicExperience = true,
ShuffleLifeExperience = true,
- AttackLevelCap = 8,
- MagicLevelCap = 8,
- LifeLevelCap = 8,
AttackEffectiveness = AttackEffectiveness.AVERAGE,
MagicEffectiveness = MagicEffectiveness.AVERAGE,
LifeEffectiveness = LifeEffectiveness.AVERAGE,
@@ -87,21 +63,18 @@ public static class StandardPreset
ShuffleSpellLocations = true,
DisableMagicContainerRequirements = true,
RandomizeSpellSpellEnemy = true,
- SwapUpAndDownStab = false,
FireOption = FireOption.PAIR_WITH_RANDOM,
//Enemies
ShuffleOverworldEnemies = true,
ShufflePalaceEnemies = true,
- DripperEnemyOption = DripperEnemyOption.ONLY_BOTS,
MixLargeAndSmallEnemies = true,
- GeneratorsAlwaysMatch = true,
ShuffleEnemyHP = EnemyLifeOption.MEDIUM,
ShuffleBossHP = EnemyLifeOption.MEDIUM,
ShuffleXPStealers = true,
ShuffleXPStolenAmount = true,
- ShuffleSwordImmunity = true,
+ SwordImmunityOption = SwordImmunityOption.SHUFFLE,
EnemyXPDrops = XPEffectiveness.RANDOM,
//Items
@@ -109,16 +82,11 @@ public static class StandardPreset
ShuffleOverworldItems = true,
MixOverworldAndPalaceItems = true,
IncludePBagCavesInItemShuffle = true,
- IncludeSwordTechsInShuffle = false,
IncludeQuestItemsInShuffle = true,
IncludeSpellsInShuffle = true,
ShuffleSmallItems = true,
- RemoveSpellItems = false,
- ShufflePBagAmounts = false,
- PalacesContainExtraKeys = false,
RandomizeNewKasutoJarRequirements = true,
- AllowImportantItemDuplicates = false,
PreventSpellItemChains = false,
//Drops
@@ -131,7 +99,5 @@ public static class StandardPreset
EnableSpellItemHints = true,
EnableTownNameHints = true,
- GenerateSpoiler = false,
- RevealWalkthroughWalls = false,
};
}
diff --git a/CrossPlatformUI/Presets/StandardSwissPreset.cs b/CrossPlatformUI/Presets/StandardSwissPreset.cs
index 9fbe0138f..eeb8d9a59 100644
--- a/CrossPlatformUI/Presets/StandardSwissPreset.cs
+++ b/CrossPlatformUI/Presets/StandardSwissPreset.cs
@@ -9,27 +9,16 @@ public static class StandardSwissPreset
{
//Start
StartWithCandle = true,
- ShuffleStartingItems = false,
- ShuffleStartingSpells = false,
- MaxHeartContainers = MaxHeartsOption.EIGHT,
- StartingHeartContainersMin = 4,
- StartingHeartContainersMax = 4,
- StartingTechniques = StartingTechs.NONE,
- StartingLives = StartingLives.Lives3,
- IndeterminateOptionRate = IndeterminateOptionRate.HALF,
//Overworld
PalacesCanSwapContinents = true,
ShuffleGP = true,
ShuffleEncounters = true,
AllowUnsafePathEncounters = true,
- IncludeLavaInEncounterShuffle = false,
EncounterRate = EncounterRate.HALF,
RiverDevilBlockerOption = RiverDevilBlockerOption.RANDOM,
EastRocks = true,
GenerateBaguWoods = false,
- LessImportantLocationsOption = LessImportantLocationsOption.HIDE,
- RestrictConnectionCaveShuffle = true,
AllowConnectionCavesToBeBlocked = true,
GoodBoots = true,
HidePalace = null,
@@ -39,31 +28,20 @@ public static class StandardSwissPreset
EastBiome = Biome.RANDOM_NO_VANILLA_OR_SHUFFLE,
MazeBiome = Biome.VANILLALIKE,
DmBiome = Biome.RANDOM_NO_VANILLA_OR_SHUFFLE,
- WestClimate = ClimateEnum.CLASSIC,
- EastClimate = ClimateEnum.CLASSIC,
- DmClimate = ClimateEnum.CLASSIC,
ContinentConnectionType = ContinentConnectionType.ANYTHING_GOES,
//Palaces
NormalPalaceStyle = PalaceStyle.RANDOM_WALK,
GpStyle = PalaceStyle.RANDOM_WALK,
- NormalPalaceLength = PalaceLengthOption.FULL,
GpLength = PalaceLengthOption.SHORT,
- IncludeVanillaRooms = true,
Includev4_0Rooms = true,
Includev5_0Rooms = true,
IncludeExpertRooms = true,
- TBirdRequired = true,
- RemoveTBird = false,
- PalacesToCompleteMin = 6,
- PalacesToCompleteMax = 6,
RestartAtPalacesOnGameOver = true,
Global5050JarDrop = true,
ReduceDripperVariance = true,
ChangePalacePallettes = true,
- RandomizeBossItemDrop = false,
PalaceDropStyle = PalaceDropStyle.ANY_EXIT,
- BossRoomsExitType = BossRoomsExitType.OVERWORLD,
NoDuplicateRoomsByLayout = true,
BlockingRoomsInAnyPalace = true,
HardBosses = true,
@@ -75,19 +53,14 @@ public static class StandardSwissPreset
ShuffleMagicExperience = true,
ShuffleLifeExperience = true,
- AttackLevelCap = 8,
- MagicLevelCap = 8,
- LifeLevelCap = 8,
AttackEffectiveness = AttackEffectiveness.AVERAGE,
MagicEffectiveness = MagicEffectiveness.AVERAGE,
LifeEffectiveness = LifeEffectiveness.AVERAGE,
//Spells
- ShuffleLifeRefillAmount = false,
ShuffleSpellLocations = true,
DisableMagicContainerRequirements = true,
RandomizeSpellSpellEnemy = true,
- SwapUpAndDownStab = false,
FireOption = FireOption.PAIR_WITH_RANDOM,
//Enemies
@@ -95,13 +68,12 @@ public static class StandardSwissPreset
ShufflePalaceEnemies = true,
DripperEnemyOption = DripperEnemyOption.ANY_GROUND_ENEMY,
MixLargeAndSmallEnemies = true,
- GeneratorsAlwaysMatch = true,
ShuffleEnemyHP = EnemyLifeOption.MEDIUM,
ShuffleBossHP = EnemyLifeOption.MEDIUM,
ShuffleXPStealers = true,
ShuffleXPStolenAmount = true,
- ShuffleSwordImmunity = true,
+ SwordImmunityOption = SwordImmunityOption.SHUFFLE,
EnemyXPDrops = XPEffectiveness.RANDOM,
//Items
@@ -109,16 +81,11 @@ public static class StandardSwissPreset
ShuffleOverworldItems = true,
MixOverworldAndPalaceItems = true,
IncludePBagCavesInItemShuffle = true,
- IncludeSwordTechsInShuffle = false,
IncludeQuestItemsInShuffle = true,
IncludeSpellsInShuffle = true,
ShuffleSmallItems = true,
- RemoveSpellItems = false,
- ShufflePBagAmounts = false,
- PalacesContainExtraKeys = false,
RandomizeNewKasutoJarRequirements = true,
- AllowImportantItemDuplicates = false,
PreventSpellItemChains = false,
//Drops
@@ -131,7 +98,6 @@ public static class StandardSwissPreset
EnableSpellItemHints = true,
EnableTownNameHints = true,
- GenerateSpoiler = false,
RevealWalkthroughWalls = true,
};
}
diff --git a/CrossPlatformUI/Presets/UpstartsTournamentPreset.cs b/CrossPlatformUI/Presets/UpstartsTournamentPreset.cs
index 9ea3f0899..ab6b1d85f 100644
--- a/CrossPlatformUI/Presets/UpstartsTournamentPreset.cs
+++ b/CrossPlatformUI/Presets/UpstartsTournamentPreset.cs
@@ -9,14 +9,6 @@ public static class UpstartsTournamentPreset
{
//Start
StartWithCandle = true,
- ShuffleStartingItems = false,
- ShuffleStartingSpells = false,
- MaxHeartContainers = MaxHeartsOption.EIGHT,
- StartingHeartContainersMin = 4,
- StartingHeartContainersMax = 4,
- StartingTechniques = StartingTechs.NONE,
- StartingLives = StartingLives.Lives3,
- IndeterminateOptionRate = IndeterminateOptionRate.HALF,
//Overworld
PalacesCanSwapContinents = true,
@@ -25,10 +17,6 @@ public static class UpstartsTournamentPreset
AllowUnsafePathEncounters = true,
IncludeLavaInEncounterShuffle = true,
EncounterRate = EncounterRate.HALF,
- EastRocks = false,
- GenerateBaguWoods = true,
- LessImportantLocationsOption = LessImportantLocationsOption.HIDE,
- RestrictConnectionCaveShuffle = true,
AllowConnectionCavesToBeBlocked = true,
GoodBoots = true,
HidePalace = null,
@@ -39,39 +27,24 @@ public static class UpstartsTournamentPreset
EastBiome = Biome.RANDOM_NO_VANILLA_OR_SHUFFLE,
MazeBiome = Biome.VANILLALIKE,
DmBiome = Biome.RANDOM_NO_VANILLA_OR_SHUFFLE,
- WestClimate = ClimateEnum.CLASSIC,
- EastClimate = ClimateEnum.CLASSIC,
- DmClimate = ClimateEnum.CLASSIC,
//Palaces
NormalPalaceStyle = PalaceStyle.RECONSTRUCTED,
GpStyle = PalaceStyle.RECONSTRUCTED,
- NormalPalaceLength = PalaceLengthOption.FULL,
GpLength = PalaceLengthOption.SHORT,
- IncludeVanillaRooms = true,
Includev4_0Rooms = true,
- Includev5_0Rooms = false,
IncludeExpertRooms = true,
- TBirdRequired = true,
- PalacesToCompleteMin = 6,
- PalacesToCompleteMax = 6,
RestartAtPalacesOnGameOver = true,
ChangePalacePallettes = true,
PalaceDropStyle = PalaceDropStyle.ANY_EXIT,
- BossRoomsExitType = BossRoomsExitType.OVERWORLD,
- NoDuplicateRoomsByLayout = false,
NoDuplicateRoomsByEnemies = true,
BlockingRoomsInAnyPalace = true,
HardBosses = true,
- PalaceItemRoomCount = PalaceItemRoomCount.ONE,
//Levels
ShuffleAttackExperience = true,
ShuffleMagicExperience = true,
ShuffleLifeExperience = true,
- AttackLevelCap = 8,
- MagicLevelCap = 8,
- LifeLevelCap = 8,
AttackEffectiveness = AttackEffectiveness.AVERAGE,
MagicEffectiveness = MagicEffectiveness.AVERAGE,
LifeEffectiveness = LifeEffectiveness.AVERAGE,
@@ -80,8 +53,6 @@ public static class UpstartsTournamentPreset
ShuffleLifeRefillAmount = true,
ShuffleSpellLocations = true,
DisableMagicContainerRequirements = true,
- RandomizeSpellSpellEnemy = false,
- SwapUpAndDownStab = false,
FireOption = FireOption.PAIR_WITH_RANDOM,
//Enemies
@@ -89,13 +60,12 @@ public static class UpstartsTournamentPreset
ShufflePalaceEnemies = true,
DripperEnemyOption = DripperEnemyOption.ANY_GROUND_ENEMY,
MixLargeAndSmallEnemies = true,
- GeneratorsAlwaysMatch = true,
ShuffleEnemyHP = EnemyLifeOption.MEDIUM,
ShuffleBossHP = EnemyLifeOption.MEDIUM,
ShuffleXPStealers = true,
ShuffleXPStolenAmount = true,
- ShuffleSwordImmunity = true,
+ SwordImmunityOption = SwordImmunityOption.SHUFFLE,
EnemyXPDrops = XPEffectiveness.RANDOM,
//Items
@@ -103,16 +73,10 @@ public static class UpstartsTournamentPreset
ShuffleOverworldItems = true,
MixOverworldAndPalaceItems = true,
IncludePBagCavesInItemShuffle = true,
- IncludeSwordTechsInShuffle = false,
- IncludeQuestItemsInShuffle = false,
- IncludeSpellsInShuffle = false,
ShuffleSmallItems = true,
- RemoveSpellItems = false,
ShufflePBagAmounts = true,
- PalacesContainExtraKeys = false,
RandomizeNewKasutoJarRequirements = true,
- AllowImportantItemDuplicates = false,
PreventSpellItemChains = false,
//Drops
@@ -125,6 +89,5 @@ public static class UpstartsTournamentPreset
EnableSpellItemHints = true,
EnableTownNameHints = true,
- RevealWalkthroughWalls = false,
};
}
diff --git a/CrossPlatformUI/Presets/VanillaPreset.cs b/CrossPlatformUI/Presets/VanillaPreset.cs
new file mode 100644
index 000000000..9755612b0
--- /dev/null
+++ b/CrossPlatformUI/Presets/VanillaPreset.cs
@@ -0,0 +1,11 @@
+using Z2Randomizer.RandomizerCore;
+
+namespace CrossPlatformUI.Presets;
+
+///
+/// Uses all the default values, which should always be vanilla.
+///
+public static class VanillaPreset
+{
+ public static readonly RandomizerConfiguration Preset = new();
+}
diff --git a/CrossPlatformUI/Services/SpriteLoaderService.cs b/CrossPlatformUI/Services/SpriteLoaderService.cs
index 582832a2c..e95e908c8 100644
--- a/CrossPlatformUI/Services/SpriteLoaderService.cs
+++ b/CrossPlatformUI/Services/SpriteLoaderService.cs
@@ -1,8 +1,9 @@
using System;
using System.Collections.Generic;
using System.IO;
-using System.Reactive.Linq;
using System.Threading.Tasks;
+using ReactiveUI.Primitives;
+using ReactiveUI.Primitives.Signals;
using Z2Randomizer.RandomizerCore;
namespace CrossPlatformUI.Services;
@@ -16,7 +17,7 @@ public class SpriteLoaderService
public SpriteLoaderService(IFileSystemService fileSystemService)
{
_fileService = fileSystemService;
- Sprites = Observable.Defer(() => Observable.FromAsync(LoadSpritesAsync)).Replay(1).AutoConnect();
+ Sprites = Signal.Defer(() => Signal.FromAsync(LoadSpritesAsync)).Replay(1).AutoConnect();
}
private async Task> LoadSpritesAsync()
diff --git a/CrossPlatformUI/ThemeHelper.cs b/CrossPlatformUI/ThemeHelper.cs
index 316769508..fbb23aa69 100644
--- a/CrossPlatformUI/ThemeHelper.cs
+++ b/CrossPlatformUI/ThemeHelper.cs
@@ -1,4 +1,5 @@
-using Avalonia.Media;
+using Avalonia.Controls;
+using Avalonia.Media;
using Avalonia.Styling;
using Material.Styles.Themes;
@@ -14,6 +15,9 @@ public class ThemeHelper
private static IBrush DarkNonStandardFlagEnabledBackground = SolidColorBrush.Parse("#80b10600");
private static IBrush LightNonStandardFlagEnabledBackground = SolidColorBrush.Parse("#d2b30000");
+ private static IBrush DarkInvalidFlagUnderline = SolidColorBrush.Parse("#b10600");
+ private static IBrush LightInvalidFlagUnderline = SolidColorBrush.Parse("#b30000");
+
static ThemeHelper() {
DarkMaterialTheme = Theme.Create(Theme.Dark, Color.Parse("#ffbc33"), Color.Parse("#969696"));
DarkMaterialTheme.Paper = Color.Parse("#1a1a1a");
@@ -63,4 +67,15 @@ public static IBrush GetFlagAlertBackgroundBrush(string? theme, bool alert)
return Brushes.Transparent;
}
}
+
+ public static IBrush GetFlagUnderlineBrush(string? theme, bool valid)
+ {
+ // Matches the brushes the Material TextBox template uses by default (underline) and on error.
+ var app = App.Current;
+ if (app != null && app.TryFindResource(valid ? "MaterialTextBoxBorderBrush" : "MaterialValidationErrorBrush", out var brush) && brush is IBrush b)
+ {
+ return b;
+ }
+ return valid ? Brushes.Gray : (IsDark(theme) ? DarkInvalidFlagUnderline : LightInvalidFlagUnderline);
+ }
}
diff --git a/CrossPlatformUI/ViewModels/GenerateRomViewModel.cs b/CrossPlatformUI/ViewModels/GenerateRomViewModel.cs
index 8bf7dcf41..49ea5c2f4 100644
--- a/CrossPlatformUI/ViewModels/GenerateRomViewModel.cs
+++ b/CrossPlatformUI/ViewModels/GenerateRomViewModel.cs
@@ -1,9 +1,6 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
-using System.Reactive;
-using System.Reactive.Disposables;
-using System.Reactive.Subjects;
using System.IO;
using System.Reflection;
using System.Text.Json.Serialization;
@@ -13,7 +10,8 @@
using Avalonia.Input.Platform;
using Avalonia.Threading;
using ReactiveUI;
-using ReactiveUI.Validation.Helpers;
+using ReactiveUI.Primitives.Disposables;
+using RxVoid = ReactiveUI.Primitives.RxVoid;
using Z2Randomizer.RandomizerCore;
using Z2Randomizer.RandomizerCore.Sidescroll;
using CrossPlatformUI.Services;
@@ -22,7 +20,7 @@
namespace CrossPlatformUI.ViewModels;
[RequiresUnreferencedCode("")]
-public class GenerateRomViewModel : ReactiveValidationObject, IRoutableViewModel, IActivatableViewModel
+public class GenerateRomViewModel : ReactiveObject, IRoutableViewModel, IActivatableViewModel
{
#pragma warning disable CS8618
@@ -63,12 +61,12 @@ await clipboard.SetTextAsync($"""
this.WhenActivated(Randomize);
return;
- async void Randomize(CompositeDisposable disposables)
+ async void Randomize(MultipleDisposable disposables)
{
if (!Main.GenerateRomDialogOpen) return;
runningMutex.Wait();
- isRunning.OnNext(true);
+ IsRunning = true;
lastError = null;
HasError = false;
@@ -177,7 +175,7 @@ async void GenerateSeed()
{
tokenSource.Dispose();
tokenSource = null;
- isRunning.OnNext(false);
+ IsRunning = false;
runningMutex.Release();
}
}
@@ -210,13 +208,13 @@ public string ProgressBody {
}
[JsonIgnore]
- public ReactiveCommand CancelGeneration { get; }
+ public ReactiveCommand CancelGeneration { get; }
[JsonIgnore]
- public ReactiveCommand CopyError { get; }
+ public ReactiveCommand CopyError { get; }
private readonly SemaphoreSlim runningMutex = new SemaphoreSlim(1, 1);
- private readonly BehaviorSubject isRunning = new BehaviorSubject(false);
- public IObservable IsRunning => isRunning;
+ private bool isRunning;
+ public bool IsRunning { get => isRunning; set => this.RaiseAndSetIfChanged(ref isRunning, value); }
private CancellationTokenSource? tokenSource;
diff --git a/CrossPlatformUI/ViewModels/MainViewModel.cs b/CrossPlatformUI/ViewModels/MainViewModel.cs
index b2e3f446b..9a27b9df1 100644
--- a/CrossPlatformUI/ViewModels/MainViewModel.cs
+++ b/CrossPlatformUI/ViewModels/MainViewModel.cs
@@ -1,24 +1,24 @@
using System;
using System.ComponentModel;
using System.Diagnostics.CodeAnalysis;
-using System.Reactive;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
using System.Text.Json.Serialization;
using ReactiveUI;
-using ReactiveUI.Validation.Helpers;
+using ReactiveUI.Primitives;
+using ReactiveUI.Primitives.Disposables;
+using ReactiveUI.Primitives.Signals;
+using RxVoid = ReactiveUI.Primitives.RxVoid;
using Z2Randomizer.RandomizerCore;
namespace CrossPlatformUI.ViewModels;
[RequiresUnreferencedCode("")]
-public class MainViewModel : ReactiveValidationObject, IScreen, IActivatableViewModel
+public class MainViewModel : ReactiveObject, IScreen, IActivatableViewModel
{
public string? OutputFilePath { get; set; }
private RandomizerConfiguration config = new();
/// Useful inexpensive shared observable for views to attach onto
/// for chaining change detection logic
- public IObservable FlagsChanged { get; }
+ public IObservable FlagsChanged { get; }
public IObservable FlagsObservable { get; }
@@ -40,12 +40,12 @@ public class MainViewModel : ReactiveValidationObject, IScreen, IActivatableView
public MainViewModel()
{
- FlagsChanged = Observable.FromEventPattern(
+ FlagsChanged = Signal.FromEventPattern(
h => Config.PropertyChanged += h,
h => Config.PropertyChanged -= h)
.Where(e => e.EventArgs.PropertyName == "Flags")
- .Select(_ => Unit.Default)
- .StartWith(Unit.Default)
+ .Select(_ => default(RxVoid))
+ .StartWith(default(RxVoid))
.Replay(1)
.RefCount();
@@ -59,17 +59,17 @@ public MainViewModel()
GenerateRomViewModel = new(this);
SaveNewPresetViewModel = new(this);
RandomizerViewModel = new(this);
- Router.Navigate.Execute(RandomizerViewModel);
+ SubscribeExtensions.Subscribe(Router.Navigate.Execute(RandomizerViewModel));
GenerateRom = ReactiveCommand.CreateFromObservable(
() => Router.Navigate.Execute()
);
- this.WhenActivated((CompositeDisposable disposables) =>
+ this.WhenActivated((MultipleDisposable disposables) =>
{
if (!RomFileViewModel.HasRomData)
{
- Router.Navigate.Execute(RomFileViewModel);
+ SubscribeExtensions.Subscribe(Router.Navigate.Execute(RomFileViewModel));
}
});
}
@@ -109,7 +109,7 @@ public bool ShuffleAllExpState
// The command that navigates a user to first view model.
[JsonIgnore]
- public ReactiveCommand GenerateRom { get; }
+ public ReactiveCommand GenerateRom { get; }
[JsonIgnore]
public GenerateRomViewModel GenerateRomViewModel { get; }
diff --git a/CrossPlatformUI/ViewModels/RandomizerViewModel.cs b/CrossPlatformUI/ViewModels/RandomizerViewModel.cs
index 8cf82394a..ab345a561 100644
--- a/CrossPlatformUI/ViewModels/RandomizerViewModel.cs
+++ b/CrossPlatformUI/ViewModels/RandomizerViewModel.cs
@@ -1,18 +1,15 @@
using System;
using System.Diagnostics.CodeAnalysis;
using System.Linq;
-using System.Reactive;
-using System.Reactive.Disposables;
-using System.Reactive.Disposables.Fluent;
-using System.Reactive.Linq;
-using System.Reactive.Subjects;
using System.Text.Json.Serialization;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using Avalonia.Controls;
+using Avalonia.Media;
using ReactiveUI;
-using ReactiveUI.Validation.Extensions;
-using ReactiveUI.Validation.Helpers;
+using ReactiveUI.Primitives;
+using ReactiveUI.Primitives.Disposables;
+using RxVoid = ReactiveUI.Primitives.RxVoid;
using Z2Randomizer.RandomizerCore;
using CrossPlatformUI.Presets;
using CrossPlatformUI.Services;
@@ -21,35 +18,21 @@
namespace CrossPlatformUI.ViewModels;
[RequiresUnreferencedCode("ReactiveUI uses reflection")]
-public class RandomizerViewModel : ReactiveValidationObject, IRoutableViewModel, IActivatableViewModel
+public class RandomizerViewModel : ReactiveObject, IRoutableViewModel, IActivatableViewModel
{
[JsonIgnore]
public IObservable CanGenerateObservable { get; private set; }
[JsonIgnore]
- public BehaviorSubject FlagsValidSubject = new(true);
+ public IObservable FlagInputUnderlineObservable { get; }
- private static bool IsFlagStringValid(string flags) => FlagPasteParser.IsValidFlagString(flags);
+ private bool flagsValid = true;
+ private bool FlagsValid { get => flagsValid; set => this.RaiseAndSetIfChanged(ref flagsValid, value); }
- private string flagInput = "";
+ private static bool IsFlagStringValid(string flags) => FlagPasteParser.IsValidFlagString(flags);
[JsonIgnore]
- public string FlagInput
- {
- get => flagInput;
- set
- {
- var trimmedValue = value?.Trim() ?? "";
- var (extractedFlags, extractedSeed) = FlagPasteParser.Parse(trimmedValue);
-
- if (Main is not null && !string.IsNullOrEmpty(extractedSeed))
- {
- Main.Config.Seed = extractedSeed;
- }
-
- this.RaiseAndSetIfChanged(ref flagInput, extractedFlags ?? trimmedValue);
- }
- }
+ public string FlagInput { get; set { field = value.Trim(); this.RaisePropertyChanged(); } } = "";
[JsonIgnore]
public string Seed
@@ -62,18 +45,17 @@ public string Seed
}
}
- [JsonIgnore]
- public BehaviorSubject ThemeVariantSubject = new("");
+ private string themeVariantName = "";
public string ThemeVariantName
{
get
{
- return ThemeVariantSubject.Value;
+ return themeVariantName;
}
set
{
ThemeHelper.SetTheme(value);
- ThemeVariantSubject.OnNext(value);
+ this.RaiseAndSetIfChanged(ref themeVariantName, value);
}
}
private int currentTabIndex;
@@ -95,6 +77,7 @@ public RandomizerViewModel(MainViewModel main)
HostScreen = Main;
BiomesViewModel = new(Main);
PalacesViewModel = new(Main);
+ SpellsViewModel = new(Main);
ItemsViewModel = new(Main);
HintsViewModel = new(Main, this);
CustomizeViewModel = new(Main);
@@ -145,9 +128,16 @@ public RandomizerViewModel(MainViewModel main)
var seedValidObservable = this.WhenAnyValue(x => x.Main.Config.Seed, seed => !string.IsNullOrWhiteSpace(seed));
- CanGenerateObservable = Observable.CombineLatest(
- FlagsValidSubject, seedValidObservable, Main.RomFileViewModel.HasRomDataObservable, Main.GenerateRomViewModel.IsRunning,
- (flagsValid, seedValid, hasRom, isRunning) => flagsValid && seedValid && hasRom && !isRunning);
+ CanGenerateObservable = this.WhenAnyValue(x => x.FlagsValid).CombineLatest(
+ seedValidObservable,
+ Main.RomFileViewModel.HasRomDataObservable,
+ Main.GenerateRomViewModel.WhenAnyValue(x => x.IsRunning),
+ (flagsValid, seedValid, hasRom, isRunning) =>
+ flagsValid && seedValid && hasRom && !isRunning);
+
+ FlagInputUnderlineObservable = this.WhenAnyValue(x => x.ThemeVariantName)
+ .CombineLatest(this.WhenAnyValue(x => x.FlagsValid), (theme, valid) =>
+ ThemeHelper.GetFlagUnderlineBrush(theme, valid));
Generate = ReactiveCommand.Create(() =>
{
@@ -214,7 +204,7 @@ public RandomizerViewModel(MainViewModel main)
return localPath;
}
- private void OnActivate(CompositeDisposable disposables)
+ private void OnActivate(MultipleDisposable disposables)
{
var loadedFlags = Main.Config.SerializeFlags(); // this serializes the configuration
var defaultFlags = new RandomizerConfiguration().SerializeFlags();
@@ -225,17 +215,35 @@ private void OnActivate(CompositeDisposable disposables)
}
// flag updates from RandomizerConfiguration always overwrites our flag input
- Main.FlagsObservable
- .Do(x => FlagsValidSubject.OnNext(true))
- .Subscribe(flags => FlagInput = flags)
+ SubscribeExtensions.Subscribe(Main.FlagsObservable, flags =>
+ {
+ FlagsValid = true;
+ FlagInput = flags;
+ })
.DisposeWith(disposables);
- this.WhenAnyValue(x => x.FlagInput)
- .WithLatestFrom(Main.FlagsObservable,
- (Input, Current) => (Input, Current, IsValid: Input == Current || IsFlagStringValid(Input)))
- .Do(x => FlagsValidSubject.OnNext(x.IsValid))
- .Where(x => x.IsValid && x.Input != x.Current)
- .Subscribe(x => Main.Config.DeserializeFlags(x.Input))
+ SubscribeExtensions.Subscribe(
+ this.WhenAnyValue(viewModel => viewModel.FlagInput)
+ .WithLatestFrom(Main.FlagsObservable, (Input, Current) => (Input, Current)),
+ tuple =>
+ {
+ var isNew = tuple.Input != tuple.Current;
+ if (isNew)
+ {
+ bool isValid = IsFlagStringValid(tuple.Input);
+ FlagsValid = isValid;
+ if (isValid)
+ {
+ Main.Config.DeserializeFlags(tuple.Input);
+ }
+ }
+ else if (!FlagsValid)
+ {
+ // The input matches the currently loaded flags (always valid), but the flag is
+ // stale after a prior invalid paste left it false.
+ FlagsValid = true;
+ }
+ })
.DisposeWith(disposables);
Main.Config.PropertyChanged += (sender, args) =>
@@ -248,18 +256,21 @@ private void OnActivate(CompositeDisposable disposables)
}
};
- this.ValidationRule(x => x.FlagInput, FlagsValidSubject, "Invalid Flags");
+ // ReactiveUI.Validation (latest 7.1.0) is not updated to ReactiveUI 24 and can't be used
+ // this.ValidationRule(viewModel => viewModel.FlagsValid, this.WhenAnyValue(x => x.FlagsValid), "Invalid Flags");
AddValidationRules();
}
private void AddValidationRules()
{
- Main.Config.WhenAnyValue(
- x => x.ShuffleOverworldEnemies,
- x => x.ShufflePalaceEnemies,
- (a,b) => (a ?? true) || (b ?? true)
- ).Subscribe(_ =>
+ SubscribeExtensions.Subscribe(
+ Main.Config.WhenAnyValue(
+ x => x.ShuffleOverworldEnemies,
+ x => x.ShufflePalaceEnemies,
+ (a,b) => (a ?? true) || (b ?? true)
+ ),
+ _ =>
{
var overworldEnemyShuffle = Main.Config.ShuffleOverworldEnemies ?? true;
var palaceEnemyShuffle = Main.Config.ShufflePalaceEnemies ?? true;
@@ -275,12 +286,14 @@ private void AddValidationRules()
});
// When PalaceItems and OverworldItems are off, then don't allow MixingOverworldAndPalaceItems
- Main.Config.WhenAnyValue(
- x => x.ShufflePalaceItems,
- x => x.ShuffleOverworldItems,
- (palaceItems, overworldItems) =>
- !(palaceItems ?? true) || !(overworldItems ?? true)
- ).Subscribe(_ =>
+ SubscribeExtensions.Subscribe(
+ Main.Config.WhenAnyValue(
+ x => x.ShufflePalaceItems,
+ x => x.ShuffleOverworldItems,
+ (palaceItems, overworldItems) =>
+ !(palaceItems ?? true) || !(overworldItems ?? true)
+ ),
+ _ =>
{
if(!(Main.Config.ShufflePalaceItems ?? true) || !(Main.Config.ShufflePalaceItems ?? true))
{
@@ -289,16 +302,14 @@ private void AddValidationRules()
});
// If shuffle overworld items is off, turn off pbag cave item shuffle too
- Main.Config.ObservableForProperty(x => x.ShuffleOverworldItems)
- .Subscribe(x =>
+ SubscribeExtensions.Subscribe(Main.Config.ObservableForProperty(x => x.ShuffleOverworldItems), x =>
{
if (x.Value ?? true) return;
Main.Config.IncludePBagCavesInItemShuffle = false;
});
// If Palaces can't swap continents
- Main.Config.ObservableForProperty(x => x.ShuffleEncounters)
- .Subscribe(x =>
+ SubscribeExtensions.Subscribe(Main.Config.ObservableForProperty(x => x.ShuffleEncounters), x =>
{
if (x.Value ?? true) return;
Main.Config.IncludeLavaInEncounterShuffle = false;
@@ -306,14 +317,13 @@ private void AddValidationRules()
});
// If shuffle palaces is off, then don't allow shuffling GP
- Main.Config.ObservableForProperty(x => x.PalacesCanSwapContinents)
- .Subscribe(x =>
+ SubscribeExtensions.Subscribe(Main.Config.ObservableForProperty(x => x.PalacesCanSwapContinents), x =>
{
if (x.Value ?? true) return;
Main.Config.ShuffleGP = false;
});
- Main.ObservableForProperty(x => x.ShuffleAllExpState).Subscribe(x =>
+ SubscribeExtensions.Subscribe(Main.ObservableForProperty(x => x.ShuffleAllExpState), x =>
{
if (!x.Value) return;
Main.Config.ShuffleAttackExperience = true;
@@ -330,34 +340,35 @@ private void AddValidationRules()
[JsonIgnore]
public BiomesViewModel BiomesViewModel { get; }
public PalacesViewModel PalacesViewModel { get; }
+ public SpellsViewModel SpellsViewModel { get; }
public ItemsViewModel ItemsViewModel { get; }
public HintsViewModel HintsViewModel { get; }
public CustomizeViewModel CustomizeViewModel { get; }
[JsonIgnore]
- public ReactiveCommand RerollSeed { get; }
+ public ReactiveCommand RerollSeed { get; }
[JsonIgnore]
- public ReactiveCommand Generate { get; }
+ public ReactiveCommand Generate { get; }
[JsonIgnore]
- public ReactiveCommand SaveFolder { get; }
+ public ReactiveCommand SaveFolder { get; }
[JsonIgnore]
- public ReactiveCommand CheckForUpdates { get; }
+ public ReactiveCommand CheckForUpdates { get; }
[JsonIgnore]
- public ReactiveCommand ToggleTheme { get; }
+ public ReactiveCommand ToggleTheme { get; }
[JsonIgnore]
- public ReactiveCommand VisitDiscord { get; }
+ public ReactiveCommand VisitDiscord { get; }
[JsonIgnore]
- public ReactiveCommand VisitWiki { get; }
+ public ReactiveCommand VisitWiki { get; }
[JsonIgnore]
- public ReactiveCommand SaveNewPreset { get; }
+ public ReactiveCommand SaveNewPreset { get; }
[JsonIgnore]
- public ReactiveCommand SaveAsPreset { get; }
+ public ReactiveCommand SaveAsPreset { get; }
[JsonIgnore]
- public ReactiveCommand ClearSavedPreset { get; }
+ public ReactiveCommand ClearSavedPreset { get; }
[JsonIgnore]
- public ReactiveCommand LoadPreset { get; }
+ public ReactiveCommand LoadPreset { get; }
[JsonIgnore]
- public ReactiveCommand LoadRom { get; }
+ public ReactiveCommand LoadRom { get; }
// Unique identifier for the routable view model.
[JsonIgnore]
diff --git a/CrossPlatformUI/ViewModels/RomFileViewModel.cs b/CrossPlatformUI/ViewModels/RomFileViewModel.cs
index dec251ba2..b3b1ab2ea 100644
--- a/CrossPlatformUI/ViewModels/RomFileViewModel.cs
+++ b/CrossPlatformUI/ViewModels/RomFileViewModel.cs
@@ -1,11 +1,11 @@
using System;
using System.Diagnostics.CodeAnalysis;
-using System.Reactive;
using System.Text.Json.Serialization;
using System.Threading;
using System.Threading.Tasks;
using Microsoft.Extensions.DependencyInjection;
using ReactiveUI;
+using RxVoid = ReactiveUI.Primitives.RxVoid;
using SD.Tools.BCLExtensions.CollectionsRelated;
using Z2Randomizer.RandomizerCore;
using CrossPlatformUI.Services;
@@ -93,7 +93,7 @@ private async Task OpenFileInternal(CancellationToken token)
Main.OutputFilePath = new Uri(file.Path, ".").LocalPath;
}
}
- HostScreen.Router.NavigateBack.Execute();
+ HostScreen.Router.NavigateBack.Execute().Subscribe();
}
else
{
@@ -102,7 +102,7 @@ private async Task OpenFileInternal(CancellationToken token)
}
[JsonIgnore]
- public ReactiveCommand OpenFileCommand { get; }
+ public ReactiveCommand OpenFileCommand { get; }
[JsonIgnore]
public MainViewModel Main { get; }
[JsonIgnore]
diff --git a/CrossPlatformUI/ViewModels/SaveNewPresetViewModel.cs b/CrossPlatformUI/ViewModels/SaveNewPresetViewModel.cs
index 57235a973..430420fa3 100644
--- a/CrossPlatformUI/ViewModels/SaveNewPresetViewModel.cs
+++ b/CrossPlatformUI/ViewModels/SaveNewPresetViewModel.cs
@@ -1,14 +1,13 @@
using System;
using System.Collections.ObjectModel;
using System.Diagnostics.CodeAnalysis;
-using System.Reactive;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
using System.Text.Json;
using System.Text.Json.Serialization;
using DynamicData.Binding;
using ReactiveUI;
-using ReactiveUI.Validation.Helpers;
+using ReactiveUI.Primitives;
+using ReactiveUI.Primitives.Disposables;
+using RxVoid = ReactiveUI.Primitives.RxVoid;
using Z2Randomizer.RandomizerCore;
namespace CrossPlatformUI.ViewModels;
@@ -67,7 +66,7 @@ public CustomPreset(string preset, RandomizerConfiguration config)
}
[RequiresUnreferencedCode("ReactiveUI uses reflection")]
-public class SaveNewPresetViewModel : ReactiveValidationObject, IRoutableViewModel, IActivatableViewModel
+public class SaveNewPresetViewModel : ReactiveObject, IRoutableViewModel, IActivatableViewModel
{
#pragma warning disable CS8618
@@ -91,7 +90,7 @@ public SaveNewPresetViewModel(MainViewModel main)
{
Main.SaveNewPresetDialogOpen = false;
});
- this.WhenActivated((CompositeDisposable disposables) =>
+ this.WhenActivated((MultipleDisposable disposables) =>
{
PresetName = string.Empty;
});
@@ -117,9 +116,9 @@ public SaveNewPresetViewModel(MainViewModel main)
[JsonIgnore]
private MainViewModel Main { get; }
[JsonIgnore]
- public ReactiveCommand SavePreset { get; }
+ public ReactiveCommand SavePreset { get; }
[JsonIgnore]
- public ReactiveCommand CancelPreset { get; }
+ public ReactiveCommand CancelPreset { get; }
[JsonIgnore]
diff --git a/CrossPlatformUI/ViewModels/Tabs/BiomesViewModel.cs b/CrossPlatformUI/ViewModels/Tabs/BiomesViewModel.cs
index 5b0e5706a..ed7faf9d8 100644
--- a/CrossPlatformUI/ViewModels/Tabs/BiomesViewModel.cs
+++ b/CrossPlatformUI/ViewModels/Tabs/BiomesViewModel.cs
@@ -1,8 +1,8 @@
using System;
using System.Diagnostics.CodeAnalysis;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
using ReactiveUI;
+using ReactiveUI.Primitives;
+using ReactiveUI.Primitives.Disposables;
namespace CrossPlatformUI.ViewModels.Tabs;
@@ -26,7 +26,7 @@ public BiomesViewModel(MainViewModel main)
this.WhenActivated(OnActivate);
}
- internal void OnActivate(CompositeDisposable disposables)
+ internal void OnActivate(MultipleDisposable disposables)
{
}
}
diff --git a/CrossPlatformUI/ViewModels/Tabs/CustomizeViewModel.cs b/CrossPlatformUI/ViewModels/Tabs/CustomizeViewModel.cs
index 6180bdecd..16fdaeacc 100644
--- a/CrossPlatformUI/ViewModels/Tabs/CustomizeViewModel.cs
+++ b/CrossPlatformUI/ViewModels/Tabs/CustomizeViewModel.cs
@@ -1,7 +1,7 @@
using System.Diagnostics.CodeAnalysis;
-using System.Reactive.Linq;
using System.Text.Json.Serialization;
using ReactiveUI;
+using ReactiveUI.Primitives;
using Z2Randomizer.RandomizerCore;
namespace CrossPlatformUI.ViewModels.Tabs;
diff --git a/CrossPlatformUI/ViewModels/Tabs/HintsViewModel.cs b/CrossPlatformUI/ViewModels/Tabs/HintsViewModel.cs
index d82396b5f..918a4b545 100644
--- a/CrossPlatformUI/ViewModels/Tabs/HintsViewModel.cs
+++ b/CrossPlatformUI/ViewModels/Tabs/HintsViewModel.cs
@@ -1,9 +1,9 @@
using System;
using System.Diagnostics.CodeAnalysis;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
using Avalonia.Media;
using ReactiveUI;
+using ReactiveUI.Primitives;
+using ReactiveUI.Primitives.Disposables;
namespace CrossPlatformUI.ViewModels.Tabs;
@@ -23,13 +23,14 @@ public HintsViewModel(MainViewModel main, RandomizerViewModel randomizerViewMode
.Select(_ => Main.Config.GenerateSpoiler)
.DistinctUntilChanged();
- HeaderBackgroundObservable = randomizerViewModel.ThemeVariantSubject.CombineLatest(alertFlags)
- .Select(pair => ThemeHelper.GetFlagAlertBackgroundBrush(pair.First, pair.Second));
+ HeaderBackgroundObservable = randomizerViewModel.WhenAnyValue(x => x.ThemeVariantName)
+ .CombineLatest(alertFlags, (theme, alert) =>
+ ThemeHelper.GetFlagAlertBackgroundBrush(theme, alert));
this.WhenActivated(OnActivate);
}
- internal void OnActivate(CompositeDisposable disposables)
+ internal void OnActivate(MultipleDisposable disposables)
{
}
}
diff --git a/CrossPlatformUI/ViewModels/Tabs/ItemsViewModel.cs b/CrossPlatformUI/ViewModels/Tabs/ItemsViewModel.cs
index 3e1fa3d5d..ca1bdc2b1 100644
--- a/CrossPlatformUI/ViewModels/Tabs/ItemsViewModel.cs
+++ b/CrossPlatformUI/ViewModels/Tabs/ItemsViewModel.cs
@@ -1,8 +1,8 @@
using System;
using System.Diagnostics.CodeAnalysis;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
using ReactiveUI;
+using ReactiveUI.Primitives;
+using ReactiveUI.Primitives.Disposables;
namespace CrossPlatformUI.ViewModels.Tabs;
@@ -20,7 +20,7 @@ public ItemsViewModel(MainViewModel main)
this.WhenActivated(OnActivate);
}
- internal void OnActivate(CompositeDisposable disposables)
+ internal void OnActivate(MultipleDisposable disposables)
{
}
}
diff --git a/CrossPlatformUI/ViewModels/Tabs/PalacesViewModel.cs b/CrossPlatformUI/ViewModels/Tabs/PalacesViewModel.cs
index 37748c7da..1bfe631d5 100644
--- a/CrossPlatformUI/ViewModels/Tabs/PalacesViewModel.cs
+++ b/CrossPlatformUI/ViewModels/Tabs/PalacesViewModel.cs
@@ -1,8 +1,8 @@
+using ReactiveUI;
+using ReactiveUI.Primitives.Disposables;
using System;
using System.Diagnostics.CodeAnalysis;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
-using ReactiveUI;
+using ReactiveUI.Primitives;
namespace CrossPlatformUI.ViewModels.Tabs;
@@ -12,15 +12,36 @@ public class PalacesViewModel : ReactiveObject, IActivatableViewModel
public ViewModelActivator Activator { get; }
public MainViewModel Main { get; }
+ public IObservable BossRoomsExitTypeIncludedObservable { get; }
+ public IObservable NoDuplicateRoomsByLayoutIncludedObservable { get; }
+ public IObservable NoDuplicateRoomsByEnemiesIncludedObservable { get; }
public IObservable RandomStylesAllowVanillaIncludedObservable { get; }
public IObservable RemoveLongDeadEndsIncludedObservable { get; }
+ public IObservable IncludeVanillaRoomsIncludedObservable { get; }
+ public IObservable Includev4_0RoomsIncludedObservable { get; }
+ public IObservable Includev5_0RoomsIncludedObservable { get; }
public IObservable IncludeExpertRoomsIncludedObservable { get; }
+ public IObservable BlockingRoomsInAnyPalaceIncludedObservable { get; }
+ public IObservable RemoveTBirdIncludedObservable { get; }
+ public IObservable TBirdRequiredIncludedObservable { get; }
public PalacesViewModel(MainViewModel main)
{
Main = main;
Activator = new();
+ BossRoomsExitTypeIncludedObservable = Main.FlagsChanged
+ .Select(_ => Main.Config.bossRoomsExitTypeIncluded())
+ .DistinctUntilChanged();
+
+ NoDuplicateRoomsByLayoutIncludedObservable = Main.FlagsChanged
+ .Select(_ => Main.Config.noDuplicateRoomsByLayoutIncluded())
+ .DistinctUntilChanged();
+
+ NoDuplicateRoomsByEnemiesIncludedObservable = Main.FlagsChanged
+ .Select(_ => Main.Config.noDuplicateRoomsByEnemiesIncluded())
+ .DistinctUntilChanged();
+
RandomStylesAllowVanillaIncludedObservable = Main.FlagsChanged
.Select(_ => Main.Config.randomStylesAllowVanillaIncluded())
.DistinctUntilChanged();
@@ -29,14 +50,38 @@ public PalacesViewModel(MainViewModel main)
.Select(_ => Main.Config.removeLongDeadEndsIncluded())
.DistinctUntilChanged();
+ IncludeVanillaRoomsIncludedObservable = Main.FlagsChanged
+ .Select(_ => Main.Config.includeVanillaRoomsIncluded())
+ .DistinctUntilChanged();
+
+ Includev4_0RoomsIncludedObservable = Main.FlagsChanged
+ .Select(_ => Main.Config.includev4_0RoomsIncluded())
+ .DistinctUntilChanged();
+
+ Includev5_0RoomsIncludedObservable = Main.FlagsChanged
+ .Select(_ => Main.Config.includev5_0RoomsIncluded())
+ .DistinctUntilChanged();
+
IncludeExpertRoomsIncludedObservable = Main.FlagsChanged
.Select(_ => Main.Config.includeExpertRoomsIncluded())
.DistinctUntilChanged();
+ BlockingRoomsInAnyPalaceIncludedObservable = Main.FlagsChanged
+ .Select(_ => Main.Config.blockingRoomsInAnyPalaceIncluded())
+ .DistinctUntilChanged();
+
+ RemoveTBirdIncludedObservable = Main.FlagsChanged
+ .Select(_ => Main.Config.removeTBirdIncluded())
+ .DistinctUntilChanged();
+
+ TBirdRequiredIncludedObservable = Main.FlagsChanged
+ .Select(_ => Main.Config.tBirdRequiredIncluded())
+ .DistinctUntilChanged();
+
this.WhenActivated(OnActivate);
}
- internal void OnActivate(CompositeDisposable disposables)
+ internal void OnActivate(MultipleDisposable disposables)
{
}
}
diff --git a/CrossPlatformUI/ViewModels/Tabs/SpellsViewModel.cs b/CrossPlatformUI/ViewModels/Tabs/SpellsViewModel.cs
new file mode 100644
index 000000000..0231451e7
--- /dev/null
+++ b/CrossPlatformUI/ViewModels/Tabs/SpellsViewModel.cs
@@ -0,0 +1,26 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using ReactiveUI;
+using ReactiveUI.Primitives;
+using ReactiveUI.Primitives.Disposables;
+
+namespace CrossPlatformUI.ViewModels.Tabs;
+
+[RequiresUnreferencedCode("ReactiveUI uses reflection")]
+public class SpellsViewModel : ReactiveObject, IActivatableViewModel
+{
+ public ViewModelActivator Activator { get; }
+ public MainViewModel Main { get; }
+
+ public SpellsViewModel(MainViewModel main)
+ {
+ Main = main;
+ Activator = new();
+
+ this.WhenActivated(OnActivate);
+ }
+
+ internal void OnActivate(MultipleDisposable disposables)
+ {
+ }
+}
diff --git a/CrossPlatformUI/ViewModels/Tabs/SpritePreviewViewModel.cs b/CrossPlatformUI/ViewModels/Tabs/SpritePreviewViewModel.cs
index ab7bdf281..fd757e5ee 100644
--- a/CrossPlatformUI/ViewModels/Tabs/SpritePreviewViewModel.cs
+++ b/CrossPlatformUI/ViewModels/Tabs/SpritePreviewViewModel.cs
@@ -1,24 +1,23 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
+using System.Diagnostics.CodeAnalysis;
using System.Linq;
-using System.Reactive.Disposables;
-using System.Reactive.Linq;
-using System.Reactive.Threading.Tasks;
using System.Text.Json.Serialization;
-using System.Diagnostics.CodeAnalysis;
-using System.Reactive.Disposables.Fluent;
using System.Threading;
using System.Threading.Tasks;
+using Microsoft.Extensions.DependencyInjection;
using Avalonia;
using Avalonia.Media.Imaging;
using Avalonia.Platform;
using Avalonia.Threading;
-using CrossPlatformUI.Services;
+using ReactiveUI;
+using ReactiveUI.Primitives;
+using ReactiveUI.Primitives.Disposables;
+using ReactiveUI.Primitives.Signals;
using DynamicData;
using DynamicData.Binding;
-using Microsoft.Extensions.DependencyInjection;
-using ReactiveUI;
+using CrossPlatformUI.Services;
using Z2Randomizer.RandomizerCore;
namespace CrossPlatformUI.ViewModels.Tabs;
@@ -119,7 +118,7 @@ public LoadedCharacterSprite? Sprite
[JsonIgnore]
public ObservableCollectionExtended Options { get; } = new();
- private CompositeDisposable _disposables = new();
+ private MultipleDisposable _disposables = new();
[JsonIgnore]
public ViewModelActivator Activator { get; }
@@ -135,19 +134,17 @@ public SpritePreviewViewModel(MainViewModel main)
Activator = new();
this.WhenActivated(OnActivate);
- options
- .Connect()
- .Bind(Options)
- .Subscribe()
+ SubscribeExtensions.Subscribe(options.Connect().Bind(Options))
.DisposeWith(_disposables);
var spriteLoaderService = App.Current?.Services?.GetService();
Debug.Assert(spriteLoaderService != null);
- spriteLoaderService.Sprites
- .Where(sprites => sprites.Count > 0)
- .Take(1)
- .Subscribe(sprites =>
+ SubscribeExtensions.Subscribe(
+ spriteLoaderService.Sprites
+ .Where(sprites => sprites.Count > 0)
+ .Take(1),
+ sprites =>
{
Dispatcher.UIThread.Post(() => LoadCharacterSprites(sprites), DispatcherPriority.Background);
})
@@ -159,7 +156,7 @@ public SpritePreviewViewModel(MainViewModel main)
_disposables.Dispose();
}
- internal void OnActivate(CompositeDisposable disposables)
+ internal void OnActivate(MultipleDisposable disposables)
{
IObservable<(string? DisplayName, NesColor, NesColor, NesColor)> settingsObservable = this.WhenAnyValue(
x => x.Main.Config.Sprite,
@@ -181,21 +178,21 @@ internal void OnActivate(CompositeDisposable disposables)
var hasRomObservable = Main.RomFileViewModel.ObservableForProperty(x => x.HasRomData, false, false);
var optionsObservable = options.Connect().ToCollection();
- Observable.CombineLatest(
- settingsObservable,
- hasRomObservable,
- optionsObservable,
- (settings, hasRom, options) => (settings, hasRom, options)
- )
- .Where(t => t.hasRom.Value && t.options.Count > 0)
- .Subscribe(t => {
- backgroundUpdateTask.CancelAsync().ToObservable().Subscribe(_ =>
+ SubscribeExtensions.Subscribe(
+ settingsObservable
+ .CombineLatest(hasRomObservable, optionsObservable, (settings, hasRom, options) => (settings, hasRom, options))
+ .Where(t => t.hasRom.Value && t.options.Count > 0),
+ t =>
+ {
+ SubscribeExtensions.Subscribe(
+ Signal.FromAsync(async () => { await backgroundUpdateTask.CancelAsync(); return default(RxVoid); }),
+ _ =>
{
backgroundUpdateTask = new CancellationTokenSource();
Dispatcher.UIThread.Post(() => UpdateCharacterSprites(Main.RomFileViewModel.RomData, backgroundUpdateTask.Token), DispatcherPriority.Background);
});
})
- .DisposeWith(disposables);
+ .DisposeWith(_disposables);
}
void LoadCharacterSprites(IReadOnlyList sprites)
diff --git a/CrossPlatformUI/Views/GenerateRomView.axaml.cs b/CrossPlatformUI/Views/GenerateRomView.axaml.cs
index 0e680317c..8b22eb558 100644
--- a/CrossPlatformUI/Views/GenerateRomView.axaml.cs
+++ b/CrossPlatformUI/Views/GenerateRomView.axaml.cs
@@ -1,7 +1,8 @@
using System.Diagnostics.CodeAnalysis;
-using static System.ObservableExtensions;
using ReactiveUI;
using ReactiveUI.Avalonia;
+using ReactiveUI.Primitives;
+using ReactiveUI.Primitives.Disposables;
using CrossPlatformUI.ViewModels;
namespace CrossPlatformUI.Views;
@@ -12,22 +13,29 @@ public partial class GenerateRomView : ReactiveUserControl
public GenerateRomView()
{
InitializeComponent();
- this.WhenActivated(disposables =>
+ this.WhenActivated((MultipleDisposable disposables) =>
{
- CancelGen.WhenAnyValue(x => x.IsVisible).Subscribe(_ =>
- {
- if (CancelGen?.IsVisible ?? false)
+ SubscribeExtensions.Subscribe(
+ CancelGen.WhenAnyValue(x => x.IsVisible),
+ _ =>
{
- CancelGen.Focus();
- }
- });
- CloseGen.WhenAnyValue(x => x.IsVisible).Subscribe(_ =>
- {
- if (CloseGen?.IsVisible ?? false)
+ if (CancelGen?.IsVisible ?? false)
+ {
+ CancelGen.Focus();
+ }
+ })
+ .DisposeWith(disposables);
+
+ SubscribeExtensions.Subscribe(
+ CloseGen.WhenAnyValue(x => x.IsVisible),
+ _ =>
{
- CloseGen.Focus();
- }
- });
+ if (CloseGen?.IsVisible ?? false)
+ {
+ CloseGen.Focus();
+ }
+ })
+ .DisposeWith(disposables);
});
}
}
\ No newline at end of file
diff --git a/CrossPlatformUI/Views/MainView.axaml b/CrossPlatformUI/Views/MainView.axaml
index fc3d4029b..f4be93ee3 100644
--- a/CrossPlatformUI/Views/MainView.axaml
+++ b/CrossPlatformUI/Views/MainView.axaml
@@ -30,7 +30,7 @@
-
+
diff --git a/CrossPlatformUI/Views/MainView.axaml.cs b/CrossPlatformUI/Views/MainView.axaml.cs
index f6f454afb..1f206db8e 100644
--- a/CrossPlatformUI/Views/MainView.axaml.cs
+++ b/CrossPlatformUI/Views/MainView.axaml.cs
@@ -2,6 +2,7 @@
using Avalonia.Markup.Xaml;
using ReactiveUI;
using ReactiveUI.Avalonia;
+using ReactiveUI.Primitives.Disposables;
using CrossPlatformUI.ViewModels;
namespace CrossPlatformUI.Views;
@@ -11,7 +12,7 @@ public partial class MainView : ReactiveUserControl
{
public MainView()
{
- this.WhenActivated(disposables => { });
+ this.WhenActivated((MultipleDisposable disposables) => { });
AvaloniaXamlLoader.Load(this);
}
}
\ No newline at end of file
diff --git a/CrossPlatformUI/Views/MainWindow.axaml.cs b/CrossPlatformUI/Views/MainWindow.axaml.cs
index 3c72d3610..d13c1360b 100644
--- a/CrossPlatformUI/Views/MainWindow.axaml.cs
+++ b/CrossPlatformUI/Views/MainWindow.axaml.cs
@@ -3,6 +3,7 @@
using Avalonia;
using ReactiveUI;
using ReactiveUI.Avalonia;
+using ReactiveUI.Primitives.Disposables;
using CrossPlatformUI.ViewModels;
namespace CrossPlatformUI.Views;
@@ -14,7 +15,7 @@ public MainWindow()
{
InitializeComponent();
// Prevent the previewer's DataContext from being set when the application is run.
- this.WhenActivated(disposables => {
+ this.WhenActivated((MultipleDisposable disposables) => {
var context = DataContext as MainViewModel;
ClientSize = new Size(context!.WindowSize.Width,context.WindowSize.Height);
PositionChanged += (_, args) =>
diff --git a/CrossPlatformUI/Views/NesColorDropdown.axaml b/CrossPlatformUI/Views/NesColorDropdown.axaml
index ac0761779..de79d87a1 100644
--- a/CrossPlatformUI/Views/NesColorDropdown.axaml
+++ b/CrossPlatformUI/Views/NesColorDropdown.axaml
@@ -3,10 +3,10 @@
xmlns:assists="clr-namespace:Material.Styles.Assists;assembly=Material.Styles"
x:Class="CrossPlatformUI.Views.NesColorDropdown"
Width="340" Height="40">
-
-
+
+
-