├── .gitattributes
├── .github
└── workflows
│ └── github-pages.yml
├── .gitignore
├── LICENSE
├── LOGIC_README.md
├── README.md
├── RandomizerMod.sln
├── RandomizerMod
├── Extensions
│ ├── ICExtensions.cs
│ └── StringExtensions.cs
├── IC
│ ├── CostConversion.cs
│ ├── DupeUIDef.cs
│ ├── Export.cs
│ ├── GSComparison.cs
│ ├── Grubfather.cs
│ ├── HelperLogModule.cs
│ ├── NailUpgradeWarningModule.cs
│ ├── PlatformList.cs
│ ├── RandoItemTag.cs
│ ├── RandoPlacementTag.cs
│ ├── RandomizerModule.cs
│ ├── Seer.cs
│ ├── Shops.cs
│ ├── TrackerLogModule.cs
│ └── TrackerUpdate.cs
├── Localization.cs
├── LogHelper.cs
├── Logging
│ ├── ItemSpoilerLog.cs
│ ├── LogArguments.cs
│ ├── LogManager.cs
│ ├── NotchCostSpoilerLog.cs
│ ├── RandoLogger.cs
│ ├── SettingsLog.cs
│ └── TransitionSpoilerLog.cs
├── Menu
│ ├── Hash.cs
│ ├── ModMenu.cs
│ ├── RandomizerMenu.cs
│ └── RandomizerMenuAPI.cs
├── PriorityEvent.cs
├── Properties
│ └── AssemblyInfo.cs
├── RC
│ ├── CharmNotchCosts.cs
│ ├── CustomGeoItem.cs
│ ├── ItemPlacement.cs
│ ├── LogicConstUtil.cs
│ ├── LogicGeoCost.cs
│ ├── LogicInts
│ │ ├── NotchCostInt.cs
│ │ ├── SafeNotchCostInt.cs
│ │ └── StartLocationDelta.cs
│ ├── PlaceholderItem.cs
│ ├── ProgressionInitializer.cs
│ ├── RCData.cs
│ ├── RandoController.cs
│ ├── RandoModContext.cs
│ ├── RandoModItem.cs
│ ├── RandoModLocation.cs
│ ├── RandoModTransition.cs
│ ├── RandoVariableResolver.cs
│ ├── Requests
│ │ ├── Bucket.cs
│ │ ├── BuiltinRequests.cs
│ │ ├── CDFWeightedArray.cs
│ │ ├── GroupBuilder.cs
│ │ ├── ICFactory.cs
│ │ ├── ItemGroupBuilder.cs
│ │ ├── ItemRequestInfo.cs
│ │ ├── LocationRequestInfo.cs
│ │ ├── RBConsts.cs
│ │ ├── RandoFactory.cs
│ │ ├── RequestBuilder.cs
│ │ ├── SelfDualTransitionGroupBuilder.cs
│ │ ├── StageBuilder.cs
│ │ ├── SymmetricTransitionGroupBuilder.cs
│ │ ├── TransitionGroupBuilder.cs
│ │ └── TransitionRequestInfo.cs
│ ├── SplitCloakItem.cs
│ ├── StateVariables
│ │ ├── BenchResetVariable.cs
│ │ ├── CastSpellVariable.cs
│ │ ├── EquipCharmVariable.cs
│ │ ├── FlowerProviderVariable.cs
│ │ ├── FragileCharmVariable.cs
│ │ ├── HPStateManager.cs
│ │ ├── HotSpringResetVariable.cs
│ │ ├── LifebloodCountVariable.cs
│ │ ├── RegainSoulVariable.cs
│ │ ├── SaveQuitResetVariable.cs
│ │ ├── ShadeStateVariable.cs
│ │ ├── ShriekPogoVariable.cs
│ │ ├── SlopeballVariable.cs
│ │ ├── SoulStateManager.cs
│ │ ├── SpendSoulVariable.cs
│ │ ├── StagStateVariable.cs
│ │ ├── StartRespawnResetVariable.cs
│ │ ├── StateModifierWrapper.cs
│ │ ├── TakeDamageVariable.cs
│ │ ├── WarpToBenchResetVariable.cs
│ │ ├── WarpToStartResetVariable.cs
│ │ └── WhiteFragmentEquipVariable.cs
│ └── TransitionPlacement.cs
├── RandomizerData
│ ├── CostDef.cs
│ ├── Data.cs
│ ├── ItemDef.cs
│ ├── JsonUtil.cs
│ ├── LocationDef.cs
│ ├── PoolDef.cs
│ ├── PoolNames.cs
│ ├── RoomDef.cs
│ ├── StartDef.cs
│ ├── TransitionDef.cs
│ └── VanillaDef.cs
├── RandomizerMod.cs
├── RandomizerMod.csproj
├── Resources
│ ├── Data
│ │ ├── costs.json
│ │ ├── items.json
│ │ ├── locations.json
│ │ ├── logic_settings.json
│ │ ├── pools.json
│ │ ├── rooms.json
│ │ ├── starts.json
│ │ └── transitions.json
│ ├── Logic
│ │ ├── items.json
│ │ ├── locations.json
│ │ ├── macros.json
│ │ ├── state.json
│ │ ├── terms.json
│ │ ├── transitions.json
│ │ └── waypoints.json
│ ├── entries.txt
│ └── logo.png
└── Settings
│ ├── BinaryFormatting.cs
│ ├── CostSettings.cs
│ ├── CursedSettings.cs
│ ├── DuplicateItemSettings.cs
│ ├── GenerationSettings.cs
│ ├── GlobalSettings.cs
│ ├── LongLocationSettings.cs
│ ├── MiscSettings.cs
│ ├── NoveltySettings.cs
│ ├── PoolSettings.cs
│ ├── Presets
│ ├── Captions.cs
│ ├── CostPresetData.cs
│ ├── CursePresetData.cs
│ ├── DuplicateItemPresetData.cs
│ ├── LongLocationPresetData.cs
│ ├── MiscPresetData.cs
│ ├── NoveltyPresetData.cs
│ ├── PoolPresetData.cs
│ ├── ProgressionDepthPresetData.cs
│ ├── SkipPresetData.cs
│ ├── SplitGroupPresetData.cs
│ ├── StartItemPresetData.cs
│ ├── StartLocationPresetData.cs
│ └── TransitionPresetData.cs
│ ├── ProgressionDepthSettings.cs
│ ├── RandomizerSettings.cs
│ ├── SettingsModule.cs
│ ├── SettingsPM.cs
│ ├── SkipSettings.cs
│ ├── SplitGroupSettings.cs
│ ├── StartItemSettings.cs
│ ├── StartLocationSettings.cs
│ ├── TrackerData.cs
│ ├── TransitionSettings.cs
│ └── Util.cs
└── RandomizerModTests
├── Extensions.cs
├── LogicFixture.cs
├── MiscTests.cs
├── RandomizerModTests.csproj
├── StateVariables
├── CastSpellVariableTests.cs
├── EquipCharmVariableTests.cs
├── HPStateManagerTests.cs
├── LifebloodCountVariableTests.cs
├── ShadeSkipVariableTests.cs
├── ShriekPogoVariableTests.cs
├── SlopeballVariableTests.cs
├── SoulStateManagerTests.cs
└── TakeDamageVariableTests.cs
└── Usings.cs
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.github/workflows/github-pages.yml:
--------------------------------------------------------------------------------
1 | name: Docs
2 |
3 | on:
4 | push:
5 | branches: [ master, docs-source ]
6 |
7 | jobs:
8 | generate-docs:
9 | runs-on: windows-2022
10 |
11 | steps:
12 | - name: Checkout
13 | uses: actions/checkout@v3
14 | with:
15 | ref: master
16 | - name: Checkout doc source
17 | uses: actions/checkout@v3
18 | with:
19 | ref: docs-source
20 | path: docs
21 |
22 | # setup .NET
23 | - name: Setup .NET
24 | uses: actions/setup-dotnet@v3
25 |
26 | # Install DocFX
27 | - name: Setup DocFX
28 | uses: crazy-max/ghaction-chocolatey@v1
29 | with:
30 | args: install docfx
31 |
32 | # Build and publish docs
33 | - name: DocFX build
34 | working-directory: docs
35 | run: docfx docfx.json
36 | continue-on-error: false
37 |
38 | - name: Publish
39 | uses: peaceiris/actions-gh-pages@v3
40 | with:
41 | github_token: ${{ secrets.GITHUB_TOKEN }}
42 | publish_dir: docs/_site
43 | force_orphan: true
44 |
--------------------------------------------------------------------------------
/RandomizerMod.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio Version 17
4 | VisualStudioVersion = 17.5.33530.505
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "RandomizerMod", "RandomizerMod\RandomizerMod.csproj", "{8B1AB441-2E8A-49EB-87BD-8E1C9729AD00}"
7 | EndProject
8 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RandomizerModTests", "RandomizerModTests\RandomizerModTests.csproj", "{5167F8F2-9E73-473B-B039-F03CC1499702}"
9 | EndProject
10 | Global
11 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
12 | Debug|Any CPU = Debug|Any CPU
13 | Release|Any CPU = Release|Any CPU
14 | EndGlobalSection
15 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
16 | {8B1AB441-2E8A-49EB-87BD-8E1C9729AD00}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
17 | {8B1AB441-2E8A-49EB-87BD-8E1C9729AD00}.Debug|Any CPU.Build.0 = Debug|Any CPU
18 | {8B1AB441-2E8A-49EB-87BD-8E1C9729AD00}.Release|Any CPU.ActiveCfg = Release|Any CPU
19 | {8B1AB441-2E8A-49EB-87BD-8E1C9729AD00}.Release|Any CPU.Build.0 = Release|Any CPU
20 | {5167F8F2-9E73-473B-B039-F03CC1499702}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
21 | {5167F8F2-9E73-473B-B039-F03CC1499702}.Debug|Any CPU.Build.0 = Debug|Any CPU
22 | {5167F8F2-9E73-473B-B039-F03CC1499702}.Release|Any CPU.ActiveCfg = Release|Any CPU
23 | {5167F8F2-9E73-473B-B039-F03CC1499702}.Release|Any CPU.Build.0 = Release|Any CPU
24 | EndGlobalSection
25 | GlobalSection(SolutionProperties) = preSolution
26 | HideSolutionNode = FALSE
27 | EndGlobalSection
28 | GlobalSection(ExtensibilityGlobals) = postSolution
29 | SolutionGuid = {37A3DAA4-C744-471C-BD9D-9DF4229E8EFC}
30 | EndGlobalSection
31 | EndGlobal
32 |
--------------------------------------------------------------------------------
/RandomizerMod/Extensions/ICExtensions.cs:
--------------------------------------------------------------------------------
1 | using ItemChanger;
2 | using RandomizerMod.IC;
3 | using RandomizerMod.RC;
4 |
5 | namespace RandomizerMod.Extensions
6 | {
7 | ///
8 | /// Extensions for accessing randomizer data from IC classes.
9 | ///
10 | public static class ICExtensions
11 | {
12 | ///
13 | /// Enumerates the ItemPlacements indicated by the placement's RandoPlacementTag.
14 | ///
Returns an empty enumerable if the placement does not have a tag.
15 | ///
16 | public static IEnumerable RandoPlacements(this AbstractPlacement placement)
17 | {
18 | if (placement.GetTag(out RandoPlacementTag tag))
19 | {
20 | return tag.ids.Select(id => RandomizerMod.RS.Context.itemPlacements[id]);
21 | }
22 | return Enumerable.Empty();
23 | }
24 |
25 | ///
26 | /// Gets a RandoModLocation corresponding to the placement's RandoPlacementTag. Returns null if the placement does not have a tag.
27 | ///
Warning: different RandoModLocations corresponding to the same placement may have different behavior due to costs.
28 | ///
29 | public static RandoModLocation? RandoLocation(this AbstractPlacement placement)
30 | {
31 | if (placement.GetTag(out RandoPlacementTag tag))
32 | {
33 | return RandomizerMod.RS.Context.itemPlacements[tag.ids.First()].Location;
34 | }
35 | return null;
36 | }
37 |
38 | ///
39 | /// Gets the ItemPlacement indicated by the item's RandoItemTag. Returns default if the item does not have a tag.
40 | ///
41 | public static ItemPlacement RandoPlacement(this AbstractItem item)
42 | {
43 | if (item.GetTag(out RandoItemTag tag))
44 | {
45 | return RandomizerMod.RS.Context.itemPlacements[tag.id];
46 | }
47 | return default;
48 | }
49 |
50 | ///
51 | /// Gets the RandoModItem corresponding to the item's RandoItemTag. Returns null if the item does not have a tag.
52 | ///
53 | public static RandoModItem? RandoItem(this AbstractItem item)
54 | {
55 | return item.RandoPlacement().Item;
56 | }
57 |
58 | ///
59 | /// Gets the RandoModLocation corresponding to the item's RandoItemTag. Returns null if the item does not have a tag.
60 | ///
61 | public static RandoModLocation? RandoLocation(this AbstractItem item)
62 | {
63 | return item.RandoPlacement().Location;
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/RandomizerMod/Extensions/StringExtensions.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Extensions
2 | {
3 | public static class StringExtensions
4 | {
5 | public static int GetStableHashCode(this string str)
6 | {
7 | unchecked
8 | {
9 | int hash1 = 5381;
10 | int hash2 = hash1;
11 |
12 | for (int i = 0; i < str.Length && str[i] != '\0'; i += 2)
13 | {
14 | hash1 = ((hash1 << 5) + hash1) ^ str[i];
15 | if (i == str.Length - 1 || str[i + 1] == '\0')
16 | break;
17 | hash2 = ((hash2 << 5) + hash2) ^ str[i + 1];
18 | }
19 |
20 | return hash1 + (hash2 * 1566083941);
21 | }
22 | }
23 |
24 | public static bool TryToEnum(this string self, out T val) where T : Enum
25 | {
26 | try
27 | {
28 | val = (T)Enum.Parse(typeof(T), self);
29 | return true;
30 | }
31 | catch
32 | {
33 | val = default;
34 | return false;
35 | }
36 | }
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/RandomizerMod/IC/DupeUIDef.cs:
--------------------------------------------------------------------------------
1 | using ItemChanger;
2 | using ItemChanger.UIDefs;
3 | using UnityEngine;
4 |
5 | namespace RandomizerMod.IC
6 | {
7 | ///
8 | /// Class for converting an arbitrary UIDef on a redundant item to a MsgUIDef which displays the amount of geo replacing the item.
9 | ///
10 | public class DupeUIDef : MsgUIDef
11 | {
12 | public static MsgUIDef Convert(int geoAmount, UIDef orig)
13 | {
14 | if (orig is MsgUIDef msgDef)
15 | {
16 | return new SplitUIDef
17 | {
18 | preview = new BoxedString(msgDef.GetPreviewName()),
19 | name = new BoxedString($"{geoAmount} ({msgDef.GetPostviewName()})"),
20 | shopDesc = msgDef.shopDesc?.Clone(),
21 | sprite = msgDef.sprite?.Clone(),
22 | };
23 | }
24 | else return new DupeUIDef(geoAmount, orig);
25 | }
26 |
27 | private DupeUIDef(int geoAmount, UIDef inner)
28 | {
29 | GeoAmount = geoAmount;
30 | Inner = inner;
31 |
32 | if (inner is null)
33 | {
34 | base.name = new BoxedString($"{GeoAmount} (Dupe)");
35 | base.shopDesc = new BoxedString("");
36 | base.sprite = new ItemChangerSprite("ShopIcons.Geo");
37 | }
38 | else
39 | {
40 | // these fields will not be accessed normally, but should still have values for compatibility
41 | base.name = new BoxedString($"{GeoAmount} Geo ({inner?.GetPostviewName()})");
42 | base.shopDesc = new BoxedString(inner?.GetShopDesc());
43 | base.sprite = new EmptySprite();
44 | }
45 | }
46 |
47 | public int GeoAmount;
48 | public UIDef Inner;
49 | public override Sprite GetSprite() => Inner is not null
50 | ? Inner.GetSprite()
51 | : base.GetSprite();
52 | public override string GetPreviewName() => Inner is not null
53 | ? Inner.GetPreviewName()
54 | : base.GetPreviewName();
55 | public override string GetPostviewName() => Inner is not null
56 | ? $"{GeoAmount} Geo ({Inner?.GetPostviewName()})"
57 | : base.GetPostviewName();
58 | public override string GetShopDesc() => Inner is not null
59 | ? Inner.GetShopDesc()
60 | : base.GetShopDesc();
61 | public override UIDef Clone()
62 | {
63 | return new DupeUIDef(GeoAmount, Inner);
64 | }
65 | }
66 | }
67 |
--------------------------------------------------------------------------------
/RandomizerMod/IC/GSComparison.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using ItemChanger;
7 | using RandomizerMod.Settings;
8 |
9 | namespace RandomizerMod.IC
10 | {
11 | ///
12 | /// Compare a settings field to a given value.
13 | ///
14 | public class GSComparison : IBool where T : IComparable
15 | {
16 | ///
17 | /// Dot separators: e.g. SkipSettings.MildSkips
18 | ///
19 | public string FieldPath { get; init; }
20 |
21 | public T Target { get; init; }
22 |
23 | public ComparisonOperator Op { get; init; } = ComparisonOperator.Eq;
24 |
25 | public bool Value => ItemChanger.Extensions.Extensions.Compare((T)RandomizerMod.RS.GenerationSettings.Get(FieldPath), Op, Target);
26 |
27 | public IBool Clone() => (IBool)MemberwiseClone();
28 |
29 | public GSComparison() { }
30 |
31 | public GSComparison(string fieldName)
32 | {
33 | FieldPath = Util.GetPath(fieldName);
34 | }
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/RandomizerMod/IC/Grubfather.cs:
--------------------------------------------------------------------------------
1 | using ItemChanger;
2 | using RandomizerMod.Settings;
3 | using RandomizerMod.RC;
4 | using ItemChanger.Locations;
5 |
6 | namespace RandomizerMod.IC
7 | {
8 | public static class Grubfather
9 | {
10 | public static AbstractPlacement GetGrubfatherPlacement(ICFactory factory)
11 | {
12 | AbstractPlacement p = factory.MakeLocation(LocationNames.Grubfather).Wrap();
13 | p.AddTag().destroyRewards = GetRandomizedGrubRewards(factory.gs);
14 | return p;
15 | }
16 |
17 | public static GrubfatherRewards GetRandomizedGrubRewards(GenerationSettings gs)
18 | {
19 | GrubfatherRewards gr = GrubfatherRewards.None;
20 | if (gs.PoolSettings.Charms)
21 | {
22 | gr |= GrubfatherRewards.Grubsong | GrubfatherRewards.GrubberflysElegy;
23 | }
24 | if (gs.PoolSettings.MaskShards)
25 | {
26 | gr |= GrubfatherRewards.MaskShard;
27 | }
28 | if (gs.PoolSettings.PaleOre)
29 | {
30 | gr |= GrubfatherRewards.PaleOre;
31 | }
32 | if (gs.PoolSettings.Relics)
33 | {
34 | gr |= GrubfatherRewards.HallownestSeal | GrubfatherRewards.KingsIdol;
35 | }
36 | if (gs.PoolSettings.RancidEggs)
37 | {
38 | gr |= GrubfatherRewards.RancidEgg;
39 | }
40 |
41 | return gr;
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/RandomizerMod/IC/NailUpgradeWarningModule.cs:
--------------------------------------------------------------------------------
1 | using ItemChanger;
2 | using ItemChanger.Modules;
3 |
4 | namespace RandomizerMod.IC
5 | {
6 | /* From LOGIC_README.md:
7 | - With skips allowed, the player is advised to take care to not get locked out of certain required pogos. Obtain:
8 | - No more than 1 nail upgrade before claw or wings
9 | - No more than 3 nail upgrades before claw
10 | - In Split Claw mode, instead obtain no more than 2 nail upgrades with only a single claw piece, and any number of nail upgrades with a single claw piece as well as wings
11 | */
12 |
13 | public class NailUpgradeWarningModule : Module
14 | {
15 | public override void Initialize()
16 | {
17 | ToggleLanguageHook(true);
18 | }
19 |
20 | public override void Unload()
21 | {
22 | ToggleLanguageHook(false);
23 | }
24 |
25 | private void ToggleLanguageHook(bool toggle)
26 | {
27 | if (toggle)
28 | {
29 | Events.AddLanguageEdit(new("Nailsmith", "NAILSMITH_OFFER_ORE"), NailUpgradeWarning);
30 | }
31 | else
32 | {
33 | Events.RemoveLanguageEdit(new("Nailsmith", "NAILSMITH_OFFER_ORE"), NailUpgradeWarning);
34 | }
35 | }
36 |
37 | private static void NailUpgradeWarning(ref string s)
38 | {
39 | bool claw = PlayerData.instance.GetBool(nameof(PlayerData.hasWalljump));
40 | bool wings = PlayerData.instance.GetBool(nameof(PlayerData.hasDoubleJump));
41 | SplitClaw? scm = ItemChangerMod.Modules.Get();
42 | int level = PlayerData.instance.GetInt(nameof(PlayerData.nailSmithUpgrades));
43 | const int repetitions = 10;
44 |
45 | switch (level + 1)
46 | {
47 | case >= 2 when !(claw || wings || scm is not null && scm.hasWalljumpAny):
48 | CreateMessage(
49 | Localize("WARNING -- obtaining more than one nail upgrade before collecting one of Mantis Claw or Monarch Wings may lock out required enemy pogos!"),
50 | repetitions, ref s);
51 | break;
52 | case >= 3 when scm is not null && !(scm.hasWalljumpAny && wings || scm.hasWalljumpBoth):
53 | CreateMessage(
54 | Localize("WARNING -- obtaining more than two nail upgrades before collecting two out of three of Left Mantis Claw, Right Mantis Claw, and Monarch Wings may lock out required enemy pogos!"),
55 | repetitions, ref s);
56 | break;
57 | case >= 4 when !(claw && wings):
58 | CreateMessage(
59 | Localize("WARNING -- obtaining more than three nail upgrades before collecting both Mantis Claw and Monarch Wings may lock out required enemy pogos!"),
60 | repetitions, ref s);
61 | break;
62 | }
63 | }
64 |
65 | private static void CreateMessage(string warning, int times, ref string result)
66 | {
67 | result = string.Empty;
68 | for (int i = 0; i < times - 1; i++)
69 | {
70 | result += warning + $" ({i + 1}/{times})" + "";
71 | }
72 | result += warning + $" ({times}/{times})";
73 | }
74 |
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/RandomizerMod/IC/RandoItemTag.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using ItemChanger;
7 |
8 | namespace RandomizerMod.IC
9 | {
10 | public class RandoItemTag : Tag
11 | {
12 | public static event Action AfterRandoItemGive;
13 | public static event Action OnLoad;
14 |
15 | public int id;
16 | public bool obtained = false;
17 |
18 | public override void Load(object parent)
19 | {
20 | ((AbstractItem)parent).AfterGive += Broadcast;
21 | try
22 | {
23 | OnLoad?.Invoke((AbstractItem)parent, this);
24 | }
25 | catch (Exception e)
26 | {
27 | LogError($"Error invoking RandoItemTag.OnLoad:\n{e}");
28 | }
29 | }
30 |
31 | public override void Unload(object parent)
32 | {
33 | ((AbstractItem)parent).AfterGive -= Broadcast;
34 | }
35 |
36 | private void Broadcast(ReadOnlyGiveEventArgs args)
37 | {
38 | if (!obtained) AfterRandoItemGive?.Invoke(id, args);
39 | obtained = true;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/RandomizerMod/IC/RandoPlacementTag.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using ItemChanger;
7 |
8 | namespace RandomizerMod.IC
9 | {
10 | public class RandoPlacementTag : Tag
11 | {
12 | public static event Action OnRandoPlacementVisitStateChanged;
13 | public static event Action OnLoad;
14 |
15 | ///
16 | /// The ids of the item placements managed by this placement.
17 | ///
18 | public List ids = new();
19 |
20 | public override void Load(object parent)
21 | {
22 | ((AbstractPlacement)parent).OnVisitStateChanged += OnVisitStateChanged;
23 | try
24 | {
25 | OnLoad?.Invoke((AbstractPlacement)parent, this);
26 | }
27 | catch (Exception e)
28 | {
29 | LogError($"Error invoking RandoPlacementTag.OnLoad:\n{e}");
30 | }
31 | }
32 |
33 | public override void Unload(object parent)
34 | {
35 | ((AbstractPlacement)parent).OnVisitStateChanged -= OnVisitStateChanged;
36 | }
37 |
38 | private void OnVisitStateChanged(VisitStateChangedEventArgs args)
39 | {
40 | OnRandoPlacementVisitStateChanged?.Invoke(args);
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/RandomizerMod/IC/Seer.cs:
--------------------------------------------------------------------------------
1 | using ItemChanger;
2 | using RandomizerMod.Settings;
3 | using RandomizerMod.RC;
4 | using ItemChanger.Locations;
5 |
6 | namespace RandomizerMod.IC
7 | {
8 | public static class Seer
9 | {
10 | public static AbstractPlacement GetSeerPlacement(ICFactory factory)
11 | {
12 | AbstractPlacement p = factory.MakeLocation(LocationNames.Seer).Wrap();
13 | p.AddTag().destroyRewards = GetRandomizedSeerRewards(factory.gs);
14 | return p;
15 | }
16 | public static SeerRewards GetRandomizedSeerRewards(GenerationSettings gs)
17 | {
18 | SeerRewards sr = SeerRewards.None;
19 | if (gs.PoolSettings.Relics)
20 | {
21 | sr |= SeerRewards.HallownestSeal | SeerRewards.ArcaneEgg;
22 | }
23 | if (gs.PoolSettings.PaleOre)
24 | {
25 | sr |= SeerRewards.PaleOre;
26 | }
27 | if (gs.PoolSettings.Charms)
28 | {
29 | sr |= SeerRewards.DreamWielder;
30 | }
31 | if (gs.PoolSettings.VesselFragments)
32 | {
33 | sr |= SeerRewards.VesselFragment;
34 | }
35 | if (gs.PoolSettings.Skills)
36 | {
37 | sr |= SeerRewards.DreamGate | SeerRewards.AwokenDreamNail;
38 | }
39 | if (gs.PoolSettings.MaskShards)
40 | {
41 | sr |= SeerRewards.MaskShard;
42 | }
43 |
44 | return sr;
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/RandomizerMod/IC/Shops.cs:
--------------------------------------------------------------------------------
1 | using ItemChanger;
2 | using RandomizerMod.Settings;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 |
9 | namespace RandomizerMod.IC
10 | {
11 | public static class Shops
12 | {
13 | public static DefaultShopItems GetDefaultShopItems(GenerationSettings gs)
14 | {
15 | DefaultShopItems items = DefaultShopItems.None;
16 |
17 | items |= DefaultShopItems.IseldaMapPins;
18 | items |= DefaultShopItems.IseldaMapMarkers;
19 | items |= DefaultShopItems.LegEaterRepair;
20 |
21 | if (!gs.PoolSettings.Keys)
22 | {
23 | items |= DefaultShopItems.SlyLantern;
24 | items |= DefaultShopItems.SlySimpleKey;
25 | items |= DefaultShopItems.SlyKeyElegantKey;
26 | }
27 |
28 | if (!gs.PoolSettings.Charms)
29 | {
30 | items |= DefaultShopItems.SlyCharms;
31 | items |= DefaultShopItems.SlyKeyCharms;
32 | items |= DefaultShopItems.IseldaCharms;
33 | items |= DefaultShopItems.SalubraCharms;
34 | items |= DefaultShopItems.LegEaterCharms;
35 | }
36 |
37 | if (!gs.PoolSettings.Maps)
38 | {
39 | items |= DefaultShopItems.IseldaQuill;
40 | items |= DefaultShopItems.IseldaMaps;
41 | }
42 |
43 | if (!gs.PoolSettings.MaskShards)
44 | {
45 | items |= DefaultShopItems.SlyMaskShards;
46 | }
47 |
48 | if (!gs.PoolSettings.VesselFragments)
49 | {
50 | items |= DefaultShopItems.SlyVesselFragments;
51 | }
52 |
53 | if (!gs.PoolSettings.RancidEggs)
54 | {
55 | items |= DefaultShopItems.SlyRancidEgg;
56 | }
57 |
58 | if (!gs.PoolSettings.CharmNotches && gs.MiscSettings.SalubraNotches == MiscSettings.SalubraNotchesSetting.GroupedWithCharmNotchesPool
59 | || gs.MiscSettings.SalubraNotches == MiscSettings.SalubraNotchesSetting.Vanilla
60 | || gs.MiscSettings.SalubraNotches == MiscSettings.SalubraNotchesSetting.AutoGivenAtCharmThreshold)
61 | {
62 | items |= DefaultShopItems.SalubraNotches;
63 | items |= DefaultShopItems.SalubraBlessing;
64 | }
65 |
66 | return items;
67 | }
68 | }
69 | }
70 |
--------------------------------------------------------------------------------
/RandomizerMod/LogHelper.cs:
--------------------------------------------------------------------------------
1 | // ReSharper disable file UnusedMember.Global
2 |
3 |
4 | using System;
5 | using System.Diagnostics;
6 |
7 | namespace RandomizerMod
8 | {
9 | public static class LogHelper
10 | {
11 | public static event Action OnLog;
12 |
13 | public static void Log(string message = "")
14 | {
15 | OnLog?.Invoke(message);
16 | }
17 |
18 | public static void Log(object message)
19 | {
20 | Log(message.ToString());
21 | }
22 |
23 | [Conditional("DEBUG")]
24 | public static void LogDebug(string message)
25 | {
26 | OnLog?.Invoke(message);
27 | }
28 |
29 | public static void LogError(string message)
30 | {
31 | OnLog?.Invoke(message);
32 | }
33 |
34 | public static void LogError(object message)
35 | {
36 | OnLog?.Invoke(message.ToString());
37 | }
38 |
39 | public static void LogWarn(string message)
40 | {
41 | OnLog?.Invoke(message);
42 | }
43 | }
44 | }
45 |
--------------------------------------------------------------------------------
/RandomizerMod/Logging/ItemSpoilerLog.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using RandomizerCore;
3 | using RandomizerMod.RC;
4 |
5 | namespace RandomizerMod.Logging
6 | {
7 | class ItemSpoilerLog : RandoLogger
8 | {
9 | public readonly struct SpoilerEntry
10 | {
11 | public readonly string item;
12 | public readonly string location;
13 | public readonly string[] costs;
14 |
15 | public SpoilerEntry(ItemPlacement p)
16 | {
17 | item = p.Item.Name;
18 | location = p.Location.Name;
19 | costs = p.Location.costs?.Select(c => c.ToString())?.ToArray();
20 | }
21 | }
22 |
23 | public override void Log(LogArguments args)
24 | {
25 | JsonSerializer js = new()
26 | {
27 | DefaultValueHandling = DefaultValueHandling.Ignore,
28 | Formatting = Formatting.Indented,
29 | };
30 | using StringWriter sw = new();
31 | js.Serialize(sw, args.ctx.itemPlacements.Select(p => new SpoilerEntry(p)).ToList());
32 | LogManager.Write(sw.ToString(), "ItemSpoilerLog.json");
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/RandomizerMod/Logging/LogArguments.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore;
2 | using RandomizerMod.RC;
3 | using RandomizerMod.Settings;
4 |
5 | namespace RandomizerMod.Logging
6 | {
7 | public class LogArguments
8 | {
9 | public object randomizer { get; init; }
10 | public GenerationSettings gs { get; init; }
11 | public RandoModContext ctx { get; init; }
12 | public Dictionary properties { get; init; } = new();
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/RandomizerMod/Logging/NotchCostSpoilerLog.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 |
3 | namespace RandomizerMod.Logging
4 | {
5 | class NotchCostSpoilerLog : RandoLogger
6 | {
7 | static string[] _charmNames = new string[]
8 | {
9 | "Gathering_Swarm",
10 | "Wayward_Compass",
11 | "Grubsong",
12 | "Stalwart_Shell",
13 | "Baldur_Shell",
14 | "Fury_of_the_Fallen",
15 | "Quick_Focus",
16 | "Lifeblood_Heart",
17 | "Lifeblood_Core",
18 | "Defender's_Crest",
19 | "Flukenest",
20 | "Thorns_of_Agony",
21 | "Mark_of_Pride",
22 | "Steady_Body",
23 | "Heavy_Blow",
24 | "Sharp_Shadow",
25 | "Spore_Shroom",
26 | "Longnail",
27 | "Shaman_Stone",
28 | "Soul_Catcher",
29 | "Soul_Eater",
30 | "Glowing_Womb",
31 | "Fragile_Heart",
32 | "Fragile_Greed",
33 | "Fragile_Strength",
34 | "Nailmaster's_Glory",
35 | "Joni's_Blessing",
36 | "Shape_of_Unn",
37 | "Hiveblood",
38 | "Dream_Wielder",
39 | "Dashmaster",
40 | "Quick_Slash",
41 | "Spell_Twister",
42 | "Deep_Focus",
43 | "Grubberfly's_Elegy",
44 | "Kingsoul",
45 | "Sprintmaster",
46 | "Dreamshield",
47 | "Weaversong",
48 | "Grimmchild",
49 | };
50 |
51 | public override void Log(LogArguments args)
52 | {
53 | List notchCosts = args.ctx.notchCosts;
54 | if (notchCosts is null || notchCosts.Count < 40) return;
55 |
56 | Dictionary costLookup = new(40);
57 | for (int i = 0; i < 40; i++)
58 | {
59 | costLookup[_charmNames[i]] = notchCosts[i];
60 | }
61 |
62 | JsonSerializer js = new()
63 | {
64 | DefaultValueHandling = DefaultValueHandling.Ignore,
65 | Formatting = Formatting.Indented,
66 | };
67 | LogManager.Write((tw) => js.Serialize(tw, costLookup), "NotchCostSpoilerLog.json");
68 | }
69 | }
70 | }
71 |
--------------------------------------------------------------------------------
/RandomizerMod/Logging/RandoLogger.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Logging
2 | {
3 | public abstract class RandoLogger
4 | {
5 | public abstract void Log(LogArguments args);
6 | internal void DoLog(LogArguments args)
7 | {
8 | try
9 | {
10 | Log(args);
11 | }
12 | catch (Exception e)
13 | {
14 | LogError($"Error in RandoLogger {GetType().Name}:\n{e}");
15 | }
16 | }
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/RandomizerMod/Logging/SettingsLog.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using RandomizerMod.RandomizerData;
3 |
4 | namespace RandomizerMod.Logging
5 | {
6 | public class SettingsLog : RandoLogger
7 | {
8 | public static event Action AfterLogSettings;
9 |
10 | public override void Log(LogArguments args)
11 | {
12 | LogManager.Write(tw =>
13 | {
14 | tw.WriteLine("Logging RandomizerMod GenerationSettings:");
15 | using JsonTextWriter jtw = new(tw);
16 | JsonUtil._js.Serialize(jtw, args.gs);
17 | tw.WriteLine();
18 | tw.WriteLine("Logging menu GenerationSettings code:");
19 | tw.WriteLine(RandomizerMod.GS.DefaultMenuSettings.Serialize());
20 | tw.WriteLine("Logging final GenerationSettings code:");
21 | tw.WriteLine(args.gs.Serialize());
22 | try
23 | {
24 | AfterLogSettings?.Invoke(args, tw);
25 | }
26 | catch (Exception e)
27 | {
28 | LogError($"Error invoking AfterLogSettings:\n{e}");
29 | }
30 | }, "settings.txt");
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/RandomizerMod/Logging/TransitionSpoilerLog.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore;
2 | using RandomizerMod.RC;
3 |
4 | namespace RandomizerMod.Logging
5 | {
6 | class TransitionSpoilerLog : RandoLogger
7 | {
8 | public readonly struct SpoilerEntry
9 | {
10 | public readonly string source;
11 | public readonly string target;
12 |
13 | public SpoilerEntry(TransitionPlacement p)
14 | {
15 | source = p.Source.Name;
16 | target = p.Target.Name;
17 | }
18 | }
19 |
20 | public override void Log(LogArguments args)
21 | {
22 | string contents = RandomizerData.JsonUtil.Serialize(args.ctx.transitionPlacements.Select(p => new SpoilerEntry(p)).ToList());
23 | LogManager.Write(contents, "TransitionSpoilerLog.json");
24 | }
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/RandomizerMod/Menu/Hash.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Menu
2 | {
3 | public static class Hash
4 | {
5 | public const int Length = 4;
6 | public static string[] Entries;
7 |
8 | public static string[] GetHash(int seed, int length = Length)
9 | {
10 | Random rng = new(seed + length);
11 | string[] arr = new string[length];
12 | for (int i = 0; i < length; i++)
13 | {
14 | arr[i] = Entries[rng.Next(Entries.Length)];
15 | }
16 |
17 | return arr;
18 | }
19 |
20 | static Hash()
21 | {
22 | using Stream stream = typeof(Hash).Assembly.GetManifestResourceStream("RandomizerMod.Resources.entries.txt");
23 | using StreamReader sr = new(stream);
24 | List strs = new();
25 | while (sr.ReadLine() is string s) strs.Add(s);
26 | Entries = strs.ToArray();
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/RandomizerMod/Menu/ModMenu.cs:
--------------------------------------------------------------------------------
1 | using ItemChanger.Internal.Menu;
2 | using DirectoryOptions = RandomizerMod.Menu.RandomizerMenu.DirectoryOptions;
3 |
4 | namespace RandomizerMod.Menu
5 | {
6 | public static class ModMenu
7 | {
8 | public static MenuScreen GetRandomizerMenuScreen(MenuScreen modListMenu)
9 | {
10 | ModMenuScreenBuilder builder = new(Localize("Randomizer 4"), modListMenu);
11 | builder.AddButton(Localize("Open Log Folder"), null, () => RandomizerMenu.OpenFile(null, string.Empty, DirectoryOptions.RecentLogFolder));
12 | builder.AddButton(Localize("Open Helper Log"), null, () => RandomizerMenu.OpenFile(null, "HelperLog.txt", DirectoryOptions.RecentLogFolder));
13 | builder.AddButton(Localize("Open Tracker Log"), null, () => RandomizerMenu.OpenFile(null, "TrackerLog.txt", DirectoryOptions.RecentLogFolder));
14 | #if DEBUG
15 | builder.AddButton(Localize("Reset Profiling Data"), null, () => RandomizerCore.Profiling.Reset());
16 | #endif
17 | return builder.CreateMenuScreen();
18 | }
19 | }
20 | }
21 |
--------------------------------------------------------------------------------
/RandomizerMod/PriorityEvent.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Collections;
2 |
3 | namespace RandomizerMod
4 | {
5 | public class PriorityEvent
6 | {
7 | public PriorityEvent(out IPriorityEventOwner p)
8 | {
9 | p = new PriorityEventOwner(this);
10 | }
11 |
12 | private readonly SortedArrayList _entries = new(Comparer.Default, EqualityComparer.Default);
13 | private PriorityEntry[] _cachedSubscriberArray;
14 | private bool _cacheInvalidated = true;
15 |
16 | public void Subscribe(float key, T value)
17 | {
18 | _entries.Add(new(key, value));
19 | _cacheInvalidated = true;
20 | }
21 |
22 | public void Unsubscribe(float key, T value)
23 | {
24 | if (_entries.Remove(new(key, value)))
25 | {
26 | _cacheInvalidated = true;
27 | }
28 | }
29 |
30 | public interface IPriorityEventOwner
31 | {
32 | ///
33 | /// Returns an ordered enumerable of the event's subscribers at the moment of the request.
34 | ///
35 | IEnumerable GetSubscribers();
36 | ///
37 | /// Returns an ordered enumerable of the event's subscribers in the specified priority range, with inclusive bounds.
38 | ///
39 | IEnumerable GetSubscriberRange(float min, float max);
40 |
41 | }
42 |
43 | private class PriorityEventOwner : IPriorityEventOwner
44 | {
45 | public PriorityEventOwner(PriorityEvent _parent) => this._parent = _parent;
46 | private readonly PriorityEvent _parent;
47 | public IEnumerable GetSubscribers()
48 | {
49 | if (_parent._cacheInvalidated)
50 | {
51 | _parent._cachedSubscriberArray = _parent._entries.ToArray();
52 | _parent._cacheInvalidated = false;
53 | }
54 |
55 | return _parent._cachedSubscriberArray.Select(kvp => kvp.Value);
56 | }
57 |
58 | public IEnumerable GetSubscriberRange(float min, float max)
59 | {
60 | int lb = _parent._entries.FindInclusiveLowerBound(new (min, default));
61 | int ub = _parent._entries.FindExclusiveUpperBound(new (max, default));
62 |
63 | T[] result = new T[ub - lb];
64 | for (int i = lb; i < ub; i++) result[i - lb] = _parent._entries[i].Value;
65 | return result;
66 | }
67 | }
68 |
69 | public readonly record struct PriorityEntry(float Key, T Value) : IComparable
70 | {
71 | public int CompareTo(PriorityEvent.PriorityEntry other)
72 | {
73 | return Key.CompareTo(other.Key);
74 | }
75 | }
76 | }
77 | }
78 |
--------------------------------------------------------------------------------
/RandomizerMod/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Reflection;
2 | using System.Runtime.CompilerServices;
3 | using System.Runtime.InteropServices;
4 |
5 | // Setting ComVisible to false makes the types in this assembly not visible
6 | // to COM components. If you need to access a type in this assembly from
7 | // COM, set the ComVisible attribute to true on that type.
8 | [assembly: ComVisible(false)]
9 |
10 | // The following GUID is for the ID of the typelib if this project is exposed to COM
11 | [assembly: Guid("8b1ab441-2e8a-49eb-87bd-8e1c9729ad00")]
12 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/CharmNotchCosts.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.RC
2 | {
3 | public static class CharmNotchCosts
4 | {
5 | public static int[] _vanillaCosts;
6 |
7 | public static int GetVanillaCost(int id) => _vanillaCosts[id - 1];
8 |
9 | public static int[] GetUniformlyRandomCosts(Random rng, int min, int max)
10 | {
11 | if (max > 240) throw new ArgumentOutOfRangeException(nameof(max));
12 | if (min < 0 || min > max) throw new ArgumentOutOfRangeException(nameof(min));
13 |
14 | int count = rng.Next(min, max + 1);
15 | int[] costs = new int[40];
16 |
17 | for (int i = 0; i < count; i++)
18 | {
19 | int index;
20 | do
21 | {
22 | index = rng.Next(40);
23 | }
24 | while (costs[index] >= 6);
25 |
26 | costs[index]++;
27 | }
28 |
29 | return costs;
30 | }
31 |
32 |
33 | static CharmNotchCosts()
34 | {
35 | _vanillaCosts = new int[]
36 | {
37 | 1,
38 | 1,
39 | 1,
40 | 2,
41 | 2,
42 | 2,
43 | 3,
44 | 2,
45 | 3,
46 | 1,
47 | 3,
48 | 1,
49 | 3,
50 | 1,
51 | 2,
52 | 2,
53 | 1,
54 | 2,
55 | 3,
56 | 2,
57 | 4,
58 | 2,
59 | 2,
60 | 2,
61 | 3,
62 | 1,
63 | 4,
64 | 2,
65 | 4,
66 | 1,
67 | 2,
68 | 3,
69 | 2,
70 | 4,
71 | 3,
72 | 5,
73 | 1,
74 | 3,
75 | 2,
76 | 2
77 | };
78 |
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/CustomGeoItem.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerCore.LogicItems;
3 |
4 | namespace RandomizerMod.RC
5 | {
6 | public class CustomGeoItem : RandoModItem
7 | {
8 | public int geo;
9 |
10 | [Newtonsoft.Json.JsonConstructor]
11 | private CustomGeoItem() { }
12 |
13 | public CustomGeoItem(LogicManager lm, int geo)
14 | {
15 | this.geo = geo;
16 | item = new SingleItem($"{geo}_Geo", new(lm.GetTermStrict("GEO"), geo));
17 | }
18 | }
19 | }
20 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/ItemPlacement.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore;
2 | using System;
3 | using System.Collections.Generic;
4 | using System.Linq;
5 | using System.Text;
6 | using System.Threading.Tasks;
7 |
8 | namespace RandomizerMod.RC
9 | {
10 | public readonly record struct ItemPlacement(RandoModItem Item, RandoModLocation Location)
11 | {
12 | ///
13 | /// The index of the item placement in the RandoModContext item placements. Initialized to -1 if the placement is not part of the ctx.
14 | ///
15 | public int Index { get; init; } = -1;
16 |
17 | public void Deconstruct(out RandoModItem item, out RandoModLocation location)
18 | {
19 | item = Item;
20 | location = Location;
21 | }
22 |
23 | public static implicit operator GeneralizedPlacement(ItemPlacement p) => new(p.Item, p.Location);
24 | public static explicit operator ItemPlacement(GeneralizedPlacement p) => new((RandoModItem)p.Item, (RandoModLocation)p.Location);
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/LogicConstUtil.cs:
--------------------------------------------------------------------------------
1 | using RandomizerMod.RandomizerData;
2 |
3 | namespace RandomizerMod.RC
4 | {
5 | public static class LogicConstUtil
6 | {
7 | private static readonly string[] _charmTerms;
8 | private static readonly Dictionary _charmIDs;
9 |
10 | ///
11 | /// Returns the term name corresponding to the (1-based) charm ID.
12 | ///
13 | public static string GetCharmTerm(int charmID)
14 | {
15 | return _charmTerms[charmID - 1];
16 | }
17 |
18 | ///
19 | /// Returns the (1-based) charm ID corresponding to the charm term name.
20 | ///
21 | public static int GetCharmID(string charmTermName)
22 | {
23 | return _charmIDs[charmTermName];
24 | }
25 |
26 | static LogicConstUtil()
27 | {
28 | // TODO: const string fields?
29 | _charmTerms = JsonUtil.Deserialize>>("RandomizerMod.Resources.Logic.terms.json")["SignedByte"].SkipWhile(t => t != "Gathering_Swarm").Take(40).ToArray();
30 | _charmIDs = _charmTerms.Select((s, i) => (s, i)).ToDictionary(p => p.s, p => p.i + 1);
31 | _charmIDs["White_Fragment"] = _charmIDs["Queen_Fragment"] = _charmIDs["King_Fragment"] = _charmIDs["Kingsoul"] = _charmIDs["Void_Heart"] = 36; // aliases of WHITEFRAGMENT
32 | }
33 | }
34 | }
35 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/LogicGeoCost.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using RandomizerCore.Logic;
7 |
8 | namespace RandomizerMod.RC
9 | {
10 | public class LogicGeoCost : LogicCost
11 | {
12 | public Term CanReplenishGeoWaypoint { get; init; }
13 | public int GeoAmount { get; set; }
14 |
15 | public LogicGeoCost() { }
16 |
17 | public LogicGeoCost(LogicManager lm, int amount)
18 | {
19 | CanReplenishGeoWaypoint = lm.GetTermStrict("Can_Replenish_Geo");
20 | GeoAmount = amount;
21 | }
22 |
23 | public override bool CanGet(ProgressionManager pm)
24 | {
25 | return pm.Has(CanReplenishGeoWaypoint.Id);
26 | }
27 |
28 | public override IEnumerable GetTerms()
29 | {
30 | yield return CanReplenishGeoWaypoint;
31 | }
32 |
33 | public override string ToString()
34 | {
35 | return $"LogicGeoCost {{{GeoAmount} Geo}}";
36 | }
37 |
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/LogicInts/NotchCostInt.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 |
3 | namespace RandomizerMod.RC.LogicInts
4 | {
5 | ///
6 | /// LogicInt which returns 1 less than the number of notches to equip the charm with or without overcharming.
7 | ///
8 | [Obsolete("Use EquipCharmVariable")]
9 | public class NotchCostInt : LogicInt
10 | {
11 | // the ids should correspond to the 1-40 charm nums (i.e. 1-indexed)
12 | public readonly int[] charmIDs;
13 | public override string Name { get; }
14 | public const string Prefix = "$NotchCost";
15 |
16 | public NotchCostInt(params int[] charmIDs)
17 | {
18 | this.charmIDs = charmIDs;
19 | Array.Sort(charmIDs);
20 | Name = $"$NotchCost[{string.Join(",", charmIDs)}]";
21 | }
22 |
23 | public static bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
24 | {
25 | if (VariableResolver.TryMatchPrefix(term, Prefix, out string[] parameters))
26 | {
27 | int[] charmIDs = parameters.Select(int.Parse).ToArray();
28 | variable = new NotchCostInt(charmIDs);
29 | return true;
30 | }
31 | variable = null;
32 | return false;
33 | }
34 |
35 | public override int GetValue(object? sender, ProgressionManager pm)
36 | {
37 | List notchCosts = (pm.ctx as RandoModContext)?.notchCosts;
38 | if (notchCosts != null && notchCosts.Count >= charmIDs[charmIDs.Length - 1])
39 | {
40 | return charmIDs.Sum(i => notchCosts[i - 1]) - charmIDs.Max(i => notchCosts[i - 1]);
41 | }
42 | else
43 | {
44 | return charmIDs.Sum(i => CharmNotchCosts.GetVanillaCost(i)) - charmIDs.Max(i => CharmNotchCosts.GetVanillaCost(i));
45 | }
46 | }
47 |
48 | public override IEnumerable GetTerms() => Enumerable.Empty();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/LogicInts/SafeNotchCostInt.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 |
3 | namespace RandomizerMod.RC.LogicInts
4 | {
5 | ///
6 | /// LogicInt which returns 1 less than the number of notches needed to equip the charm without overcharming.
7 | ///
8 | [Obsolete("Use EquipCharmVariable")]
9 | public class SafeNotchCostInt : LogicInt
10 | {
11 | // the ids should correspond to the 1-40 charm nums (i.e. 1-indexed)
12 | public readonly int[] charmIDs;
13 | public override string Name { get; }
14 | public const string Prefix = "$SafeNotchCost";
15 |
16 | public SafeNotchCostInt(params int[] charmIDs)
17 | {
18 | this.charmIDs = charmIDs;
19 | Array.Sort(charmIDs);
20 | Name = $"$SafeNotchCost[{string.Join(",", charmIDs)}]";
21 | }
22 |
23 | public static bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
24 | {
25 | if (VariableResolver.TryMatchPrefix(term, Prefix, out string[] parameters))
26 | {
27 | int[] charmIDs = parameters.Select(int.Parse).ToArray();
28 | variable = new SafeNotchCostInt(charmIDs);
29 | return true;
30 | }
31 | variable = null;
32 | return false;
33 | }
34 |
35 | public override int GetValue(object? sender, ProgressionManager pm)
36 | {
37 | List notchCosts = (pm.ctx as RandoModContext)?.notchCosts;
38 | if (notchCosts != null && notchCosts.Count >= charmIDs[charmIDs.Length - 1])
39 | {
40 | return charmIDs.Sum(i => notchCosts[i - 1]) - 1;
41 | }
42 | else
43 | {
44 | return charmIDs.Sum(i => CharmNotchCosts.GetVanillaCost(i)) - 1;
45 | }
46 | }
47 |
48 | public override IEnumerable GetTerms() => Enumerable.Empty();
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/LogicInts/StartLocationDelta.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerCore.Logic.StateLogic;
3 |
4 | namespace RandomizerMod.RC.LogicInts
5 | {
6 | ///
7 | /// LogicInt which is true exactly when the GenerationSettings StartLocation equals its argument.
8 | ///
9 | public class StartLocationDelta : StateProvider
10 | {
11 | protected readonly Term? StartStateTerm; // null in files generated before state logic
12 | public override string Name { get; }
13 | public string Location { get; }
14 | public const string Prefix = "$StartLocation";
15 |
16 | public StartLocationDelta(string name, LogicManager lm, string location)
17 | {
18 | Location = location;
19 | Name = name;
20 | StartStateTerm = lm.GetTerm("Start_State");
21 | }
22 |
23 | public static bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
24 | {
25 | if (VariableResolver.TryMatchPrefix(term, Prefix, out string[] parameters))
26 | {
27 | string location = parameters[0];
28 | variable = new StartLocationDelta(term, lm, location);
29 | return true;
30 | }
31 | variable = null;
32 | return false;
33 | }
34 |
35 | public override StateUnion? GetInputState(object? sender, ProgressionManager pm)
36 | {
37 | return ((RandoModContext)pm.ctx).GenerationSettings.StartLocationSettings.StartLocation == Location
38 | ? StartStateTerm is not null
39 | ? pm.GetState(StartStateTerm)
40 | : StateUnion.Empty
41 | : null;
42 | }
43 |
44 | public override IEnumerable GetTerms()
45 | {
46 | if (StartStateTerm is not null) yield return StartStateTerm;
47 | }
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/PlaceholderItem.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.Linq;
4 | using System.Text;
5 | using System.Threading.Tasks;
6 | using RandomizerCore;
7 | using RandomizerCore.LogicItems;
8 |
9 | namespace RandomizerMod.RC
10 | {
11 | ///
12 | /// A wrapper for a RandoModItem to allow it to be ignored by logic.
13 | ///
Used mainly with duplicate progression, to avoid skewing.
14 | ///
15 | public class PlaceholderItem : RandoModItem
16 | {
17 | public const string Prefix = "Placeholder-";
18 |
19 | public PlaceholderItem(RandoModItem innerItem, bool wrapped = true)
20 | {
21 | this.innerItem = innerItem;
22 | this.info = innerItem.info?.Clone() ?? new();
23 | this.info.realItemCreator ??= ((factory, next) => factory.MakeItemWithEvents(innerItem.Name, next));
24 | if (wrapped) Wrap();
25 | else Unwrap();
26 | }
27 |
28 | ///
29 | /// Hides the innerItem of the PlaceholderItem from logic.
30 | ///
31 | public void Wrap()
32 | {
33 | wrapped = true;
34 | item = new EmptyItem($"Placeholder-{innerItem.Name}");
35 | }
36 |
37 | ///
38 | /// Makes the innerItem of the PlaceholderItem visible to logic.
39 | ///
40 | public void Unwrap()
41 | {
42 | wrapped = false;
43 | item = innerItem.item;
44 | }
45 |
46 | public readonly RandoModItem innerItem;
47 | public bool wrapped;
48 | }
49 | }
50 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/ProgressionInitializer.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore;
2 | using RandomizerCore.Logic;
3 | using RandomizerMod.RandomizerData;
4 | using RandomizerMod.Settings;
5 |
6 | namespace RandomizerMod.RC
7 | {
8 | public class ProgressionInitializer : ILogicItem
9 | {
10 | ///
11 | /// Event invoked after base randomizer term modifiers are added to the initializer.
12 | ///
13 | public static event Action OnCreateProgressionInitializer;
14 |
15 | public ProgressionInitializer() { }
16 | public ProgressionInitializer(LogicManager lm, GenerationSettings gs, StartDef startDef)
17 | {
18 | foreach (string setting in Data.GetApplicableLogicSettings(gs))
19 | {
20 | Setters.Add(new(lm.GetTermStrict(setting), 1));
21 | }
22 |
23 | Setters.Add(new(lm.GetTermStrict(gs.TransitionSettings.Mode switch
24 | {
25 | TransitionSettings.TransitionMode.None => "ITEMRANDO",
26 | TransitionSettings.TransitionMode.MapAreaRandomizer => "MAPAREARANDO",
27 | TransitionSettings.TransitionMode.FullAreaRandomizer => "FULLAREARANDO",
28 | _ => "ROOMRANDO",
29 | }), 1));
30 |
31 | foreach (TermValue tv in startDef.GetStartLocationProgression(lm))
32 | {
33 | if (tv.Term.Type == TermType.State) StartStateLinkedTerms.Add(tv.Term);
34 | else Setters.Add(tv);
35 | }
36 |
37 | Setters.Add(new(lm.GetTermStrict("GRUBS"), -gs.CostSettings.GrubTolerance));
38 | Setters.Add(new(lm.GetTermStrict("ESSENCE"), -gs.CostSettings.EssenceTolerance));
39 | Setters.Add(new(lm.GetTermStrict("RANCIDEGGS"), -gs.CostSettings.EggTolerance));
40 | Setters.Add(new(lm.GetTermStrict("CHARMS"), -gs.CostSettings.CharmTolerance));
41 |
42 | Setters.Add(new(lm.GetTermStrict("MASKSHARDS"), 20 - 4 * gs.CursedSettings.CursedMasks));
43 | Setters.Add(new(lm.GetTermStrict("NOTCHES"), 3 - gs.CursedSettings.CursedNotches));
44 |
45 | StartStateTerm = lm.GetTerm("Start_State");
46 |
47 | try
48 | {
49 | OnCreateProgressionInitializer?.Invoke(lm, gs, this);
50 | }
51 | catch (Exception e)
52 | {
53 | throw new InvalidOperationException("Error invoking OnCreateProgressionInitializer", e);
54 | }
55 | }
56 |
57 | public List Setters = new();
58 | public List Increments = new();
59 | public List StartStateLinkedTerms = new();
60 | public Term? StartStateTerm;
61 |
62 | public string Name => "Progression Initializer";
63 |
64 | public void AddTo(ProgressionManager pm)
65 | {
66 | foreach (TermValue tv in Setters) pm.Set(tv);
67 | foreach (TermValue tv in Increments) pm.Incr(tv);
68 | if (StartStateTerm is not null && !pm.mu.HasCustomLongTermRevertPoint)
69 | {
70 | foreach (Term t in StartStateLinkedTerms) pm.mu.LinkState(StartStateTerm, t);
71 | }
72 | }
73 |
74 | public IEnumerable GetAffectedTerms()
75 | {
76 | foreach (TermValue tv in Setters) yield return tv.Term;
77 | foreach (TermValue tv in Increments) yield return tv.Term;
78 | if (StartStateTerm is not null) yield return StartStateTerm;
79 | }
80 | }
81 | }
82 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/RCData.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Json;
2 | using RandomizerCore.Logic;
3 | using RandomizerMod.Settings;
4 |
5 | namespace RandomizerMod.RC
6 | {
7 | public static class RCData
8 | {
9 | private static readonly (LogicFileType type, string fileName)[] files = new[]
10 | {
11 | (LogicFileType.Terms, "terms"),
12 | (LogicFileType.Macros, "macros"),
13 | (LogicFileType.Waypoints, "waypoints"),
14 | (LogicFileType.Transitions, "transitions"),
15 | (LogicFileType.Locations, "locations"),
16 | (LogicFileType.ItemStrings, "items"),
17 | (LogicFileType.StateData, "state"),
18 | };
19 |
20 | ///
21 | /// Creates a new LogicManager, allowing edits from local files and runtime hooks.
22 | ///
23 | public static LogicManager GetNewLogicManager(GenerationSettings gs)
24 | {
25 | LogicManagerBuilder lmb = new() { VariableResolver = new RandoVariableResolver() };
26 | ILogicFormat fmt = new JsonLogicFormat();
27 |
28 | foreach ((LogicFileType type, string fileName) in files)
29 | {
30 | lmb.DeserializeFile(type, fmt, RandomizerMod.Assembly.GetManifestResourceStream($"RandomizerMod.Resources.Logic.{fileName}.json"));
31 | }
32 |
33 | foreach (var a in _runtimeLogicOverrideOwner.GetSubscribers())
34 | {
35 | try
36 | {
37 | a?.Invoke(gs, lmb);
38 | }
39 | catch (Exception e)
40 | {
41 | throw new InvalidOperationException($"Error invoking logic override event from {a?.Method?.DeclaringType?.AssemblyQualifiedName ?? "unknown source"}", e);
42 | }
43 | }
44 |
45 | return new(lmb);
46 | }
47 |
48 | ///
49 | /// Event invoked when building the LogicManager for randomization.
50 | ///
A subscriber with a nonpositive key is invoked before local logic edits.
51 | ///
A subscriber with a positive key is invoked after local logic edits.
52 | ///
53 | public static readonly PriorityEvent> RuntimeLogicOverride = new(out _runtimeLogicOverrideOwner);
54 | private static readonly PriorityEvent>.IPriorityEventOwner _runtimeLogicOverrideOwner;
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/RandoModContext.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using RandomizerCore;
3 | using RandomizerCore.Logic;
4 | using RandomizerMod.RandomizerData;
5 | using RandomizerMod.Settings;
6 |
7 | namespace RandomizerMod.RC
8 | {
9 | public class RandoModContext : RandoContext
10 | {
11 | public RandoModContext(LogicManager LM) : base(LM) { }
12 |
13 | public RandoModContext(GenerationSettings gs, StartDef startDef) : base(RCData.GetNewLogicManager(gs))
14 | {
15 | base.InitialProgression = new ProgressionInitializer(LM, gs, startDef);
16 | this.GenerationSettings = gs;
17 | this.StartDef = startDef;
18 | }
19 |
20 | public RandoModContext(RandoModContext ctx) : base(ctx.LM)
21 | {
22 | notchCosts = ctx.notchCosts.ToList();
23 | itemPlacements = ctx.itemPlacements.ToList();
24 | transitionPlacements = ctx.transitionPlacements.ToList();
25 | StartDef = ctx.StartDef;
26 | InitialProgression = ctx.InitialProgression;
27 | Vanilla = ctx.Vanilla.ToList();
28 | GenerationSettings = ctx.GenerationSettings;
29 | }
30 |
31 | public GenerationSettings GenerationSettings { get; init; }
32 | public StartDef StartDef { get; init; }
33 |
34 | public List Vanilla = new();
35 | public List itemPlacements = new();
36 | [JsonProperty(NullValueHandling = NullValueHandling.Ignore)]
37 | public List transitionPlacements = new();
38 | public List notchCosts = new();
39 | ///
40 | /// Additional data needed for logic; e.g. parameters for LogicVariables like notch costs, etc.
41 | ///
42 | public Dictionary Properties { get; } = [];
43 |
44 | public override IEnumerable EnumerateExistingPlacements()
45 | {
46 | foreach (GeneralizedPlacement p in Vanilla) yield return p;
47 | foreach (ItemPlacement p in itemPlacements) yield return p;
48 | foreach (TransitionPlacement p in transitionPlacements) yield return p;
49 | }
50 |
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/RandoModItem.cs:
--------------------------------------------------------------------------------
1 | using ItemChanger;
2 | using RandomizerCore;
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 | using System.Text;
7 | using System.Threading.Tasks;
8 | using Newtonsoft.Json;
9 | using RandomizerMod.RandomizerData;
10 |
11 | namespace RandomizerMod.RC
12 | {
13 | public class RandoModItem : RandoItem
14 | {
15 | ///
16 | /// The ItemRequestInfo associated with the item. May be null if the item does not require modification.
17 | ///
This field is not serialized and will be null upon reloading the game.
18 | ///
19 | [JsonIgnore] public ItemRequestInfo? info;
20 | ///
21 | /// The ItemDef associated with the location. Preferred over Data.GetItemDef, since this preserves modified item data.
22 | ///
This field is serialized, and is safe to use after reloading the game. May rarely be null for external items which choose not to supply a value.
23 | ///
24 | public ItemDef ItemDef;
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/RandoModLocation.cs:
--------------------------------------------------------------------------------
1 | using ItemChanger;
2 | using Newtonsoft.Json;
3 | using RandomizerCore;
4 | using RandomizerMod.RandomizerData;
5 |
6 | namespace RandomizerMod.RC
7 | {
8 | public class RandoModLocation : RandoLocation
9 | {
10 | ///
11 | /// The LocationRequestInfo associated with the location. May be null if the location does not require modification.
12 | ///
This field is not serialized and will be null upon reloading the game.
13 | ///
14 | [JsonIgnore] public LocationRequestInfo? info;
15 | ///
16 | /// The LocationDef associated with the location. Preferred over Data.GetLocationDef, since this preserves modified location data.
17 | ///
This field is serialized, and is safe to use after reloading the game. May rarely be null for external locations which choose not to supply a value.
18 | ///
19 | public LocationDef LocationDef;
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/RandoModTransition.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using RandomizerCore;
3 | using RandomizerCore.Logic;
4 | using RandomizerMod.RandomizerData;
5 |
6 | namespace RandomizerMod.RC
7 | {
8 | public class RandoModTransition : RandoTransition
9 | {
10 | ///
11 | /// The TransitionRequestInfo associated with the transition. May be null if the transition does not require modification.
12 | ///
This field is not serialized and will be null upon reloading the game.
13 | ///
14 | [JsonIgnore] public TransitionRequestInfo? info;
15 | ///
16 | /// The TransitionDef associated with the location. Preferred over Data.GetTransitionDef, since this preserves modified transition data.
17 | ///
This field is serialized, and is safe to use after reloading the game.
18 | ///
19 | public TransitionDef TransitionDef;
20 | public RandoModTransition(LogicTransition lt) : base(lt) { }
21 | }
22 | }
23 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/RandoVariableResolver.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerMod.RC.LogicInts;
3 | using RandomizerMod.RC.StateVariables;
4 |
5 | namespace RandomizerMod.RC
6 | {
7 | public class RandoVariableResolver : VariableResolver
8 | {
9 | public override bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
10 | {
11 | if (base.TryMatch(lm, term, out variable)) return true;
12 |
13 | if (StartLocationDelta.TryMatch(lm, term, out variable)) return true;
14 | if (BenchResetVariable.TryMatch(lm, term, out variable)) return true;
15 | if (CastSpellVariable.TryMatch(lm, term, out variable)) return true;
16 | if (SlopeballVariable.TryMatch(lm, term, out variable)) return true;
17 | if (ShriekPogoVariable.TryMatch(lm, term, out variable)) return true;
18 | if (SoulStateManager.TryMatch(lm, term, out variable)) return true;
19 | if (SpendSoulVariable.TryMatch(lm, term, out variable)) return true;
20 | if (RegainSoulVariable.TryMatch(lm, term, out variable)) return true;
21 | if (EquipCharmVariable.TryMatch(lm, term, out variable)) return true;
22 | if (HotSpringResetVariable.TryMatch(lm, term, out variable)) return true;
23 | if (ShadeStateVariable.TryMatch(lm, term, out variable)) return true;
24 | if (TakeDamageVariable.TryMatch(lm, term, out variable)) return true;
25 | if (HPStateManager.TryMatch(lm, term, out variable)) return true;
26 | if (LifebloodCountVariable.TryMatch(lm, term, out variable)) return true;
27 | if (StagStateVariable.TryMatch(lm, term, out variable)) return true;
28 | if (FlowerProviderVariable.TryMatch(lm, term, out variable)) return true;
29 | if (SaveQuitResetVariable.TryMatch(lm, term, out variable)) return true;
30 | if (StartRespawnResetVariable.TryMatch(lm, term, out variable)) return true;
31 | if (WarpToStartResetVariable.TryMatch(lm, term, out variable)) return true;
32 | if (WarpToBenchResetVariable.TryMatch(lm, term, out variable)) return true;
33 |
34 | #pragma warning disable CS0618 // Type or member is obsolete
35 | if (NotchCostInt.TryMatch(lm, term, out variable)) return true;
36 | if (SafeNotchCostInt.TryMatch(lm, term, out variable)) return true;
37 | #pragma warning restore CS0618 // Type or member is obsolete
38 |
39 | return false;
40 | }
41 | }
42 | }
43 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/Requests/Bucket.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.RC
2 | {
3 | public class Bucket
4 | {
5 | public Bucket()
6 | {
7 | _buckets = new();
8 | }
9 |
10 | public Bucket(IEqualityComparer equalityComparer)
11 | {
12 | _buckets = new(equalityComparer);
13 | }
14 |
15 | private readonly Dictionary _buckets;
16 |
17 | public void Increment(T t, int value)
18 | {
19 | _buckets.TryGetValue(t, out int current);
20 | _buckets[t] = current + value;
21 | }
22 |
23 | public int GetCount(T t)
24 | {
25 | _buckets.TryGetValue(t, out int value);
26 | return value;
27 | }
28 |
29 | public void AddRange(IEnumerable ts)
30 | {
31 | foreach (T t in ts) Increment(t, 1);
32 | }
33 |
34 | public void Add(T t)
35 | {
36 | Increment(t, 1);
37 | }
38 |
39 | public void RemoveAll(T t)
40 | {
41 | _buckets.Remove(t);
42 | }
43 |
44 | public void Remove(T t, int count)
45 | {
46 | if (_buckets.TryGetValue(t, out int value))
47 | {
48 | if (value > count) _buckets[t] = value - count;
49 | else _buckets.Remove(t);
50 | }
51 | }
52 |
53 | public void Replace(T old, T replaceWith)
54 | {
55 | if (_buckets.TryGetValue(old, out int value))
56 | {
57 | _buckets.Remove(old);
58 | Increment(replaceWith, value);
59 | }
60 | }
61 |
62 | public void Replace(T old, Func> replacer)
63 | {
64 | if (_buckets.TryGetValue(old, out int value))
65 | {
66 | _buckets.Remove(old);
67 | AddRange(replacer(value));
68 | }
69 | }
70 |
71 | public void Set(T t, int value)
72 | {
73 | _buckets[t] = value;
74 | }
75 |
76 | public IEnumerable EnumerateDistinct()
77 | {
78 | foreach (KeyValuePair kvp in _buckets)
79 | {
80 | if (kvp.Value > 0) yield return kvp.Key;
81 | }
82 | }
83 |
84 | public IEnumerable EnumerateWithMultiplicity()
85 | {
86 | foreach (KeyValuePair kvp in _buckets)
87 | {
88 | for (int i = 0; i < kvp.Value; i++) yield return kvp.Key;
89 | }
90 | }
91 |
92 | public CDFWeightedArray ToWeightedArray()
93 | {
94 | T[] values = _buckets.Where(kvp => kvp.Value > 0).Select(kvp => kvp.Key).ToArray();
95 | double total = GetTotal();
96 | double[] cumulativeDensities = new double[values.Length];
97 | double previous = 0.0;
98 | for (int i = 0; i < values.Length; i++)
99 | {
100 | previous = cumulativeDensities[i] = previous + _buckets[values[i]] / total;
101 | }
102 | return new(values, cumulativeDensities);
103 | }
104 |
105 | public int GetTotal() => _buckets.Values.Sum();
106 | }
107 | }
108 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/Requests/CDFWeightedArray.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.RC
2 | {
3 | public struct CDFWeightedArray
4 | {
5 | public readonly T[] values;
6 | public readonly double[] cumulativeDensities;
7 |
8 | ///
9 | /// Creates a new CDFWeightedArray with the given values. Densities should have the same positive length as values, and its entries should be increasing positive numbers, with last entry 1.
10 | ///
11 | public CDFWeightedArray(T[] values, double[] cumulativeDensities)
12 | {
13 | this.values = values;
14 | this.cumulativeDensities = cumulativeDensities;
15 | }
16 |
17 | ///
18 | /// Creates a new CDFWeightedArray with the given values.
19 | ///
If cumulative, densities should have the same positive length as values, and its entries should be increasing positive numbers, with last entry 1.
20 | ///
If noncumulative, densities should have the same positive length as values, and its entries should be nonnegative numbers.
21 | ///
22 | public CDFWeightedArray(T[] values, double[] densities, bool cumulative)
23 | {
24 | this.values = values;
25 | if (cumulative)
26 | {
27 | this.cumulativeDensities = densities;
28 | }
29 | else
30 | {
31 | this.cumulativeDensities = new double[densities.Length];
32 | double total = cumulativeDensities.Sum();
33 | double cdf = 0.0;
34 | for (int i = 0; i < densities.Length; i++)
35 | {
36 | this.cumulativeDensities[i] = cdf += densities[i] / total;
37 | }
38 | }
39 | }
40 |
41 | ///
42 | /// Randomly selects a value from the array using the CDF weights.
43 | ///
Chooses a random number between 0 and 1, and then returns the value with the least weight greater than the
44 | ///
45 | public T Next(Random rng)
46 | {
47 | if (values.Length == 1) return values[0]; // don't burn rng samples unnecessarily
48 |
49 | double d = rng.NextDouble();
50 | for (int i = 0; i < values.Length; i++)
51 | {
52 | if (cumulativeDensities[i] > d) return values[i];
53 | }
54 | return values[values.Length - 1];
55 | }
56 | }
57 | }
58 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/Requests/GroupBuilder.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Randomization;
2 |
3 | namespace RandomizerMod.RC
4 | {
5 | public abstract class GroupBuilder
6 | {
7 | public string label;
8 | public string stageLabel;
9 | public Action? onPermute;
10 | public GroupPlacementStrategy? strategy;
11 | ///
12 | /// An action invoked on each group created by the GroupBuilder. Note that some GroupBuilders may create multiple groups.
13 | ///
14 | public Action? OnCreateGroup;
15 |
16 | public abstract void Apply(List groups, RandoFactory factory);
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/Requests/ItemGroupBuilder.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore;
2 | using RandomizerCore.Randomization;
3 |
4 | namespace RandomizerMod.RC
5 | {
6 | public class ItemGroupBuilder : GroupBuilder
7 | {
8 | public ItemGroupBuilder() { }
9 |
10 | public delegate IEnumerable ItemPaddingHandler(RandoFactory factory, int count);
11 | public delegate IEnumerable LocationPaddingHandler(RandoFactory factory, int count);
12 | public ItemPaddingHandler? ItemPadder;
13 | public LocationPaddingHandler? LocationPadder;
14 |
15 | public readonly Bucket Items = new();
16 | public readonly Bucket Locations = new();
17 |
18 |
19 | public override void Apply(List groups, RandoFactory factory)
20 | {
21 | List items = new();
22 | foreach (string i in Items.EnumerateWithMultiplicity())
23 | {
24 | items.Add(factory.MakeItem(i));
25 | }
26 | List locations = new();
27 | foreach (string i in Locations.EnumerateWithMultiplicity())
28 | {
29 | locations.Add(factory.MakeLocation(i));
30 | }
31 |
32 | int diff = items.Count - locations.Count;
33 | if (diff > 0 && LocationPadder != null)
34 | {
35 | locations.AddRange(LocationPadder(factory, diff));
36 | }
37 | else if (diff < 0 && ItemPadder != null)
38 | {
39 | items.AddRange(ItemPadder(factory, -diff));
40 | }
41 |
42 | if (items.Count != locations.Count) throw new InvalidOperationException($"Failed to build group {label} due to unbalanced counts.");
43 |
44 | RandomizationGroup group = new()
45 | {
46 | Items = items.ToArray(),
47 | Locations = locations.ToArray(),
48 | Label = label,
49 | Strategy = strategy ?? factory.gs.ProgressionDepthSettings.GetItemPlacementStrategy(),
50 | };
51 | group.OnPermute += onPermute;
52 | groups.Add(group);
53 | OnCreateGroup?.Invoke(group);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/Requests/ItemRequestInfo.cs:
--------------------------------------------------------------------------------
1 | using ItemChanger;
2 | using Newtonsoft.Json;
3 | using RandomizerCore;
4 | using RandomizerMod.RandomizerData;
5 |
6 | namespace RandomizerMod.RC
7 | {
8 | public class ItemRequestInfo
9 | {
10 | public Func? randoItemCreator;
11 | public Action? onRandoItemCreation;
12 | public Action? onRandomizerFinish;
13 | public Func? realItemCreator;
14 | public Func? getItemDef;
15 |
16 | public void AddGetItemDefModifier(string name, Func modifier)
17 | {
18 | Func get = getItemDef ?? (() => Data.GetItemDef(name));
19 | getItemDef = () => modifier(get());
20 | }
21 |
22 | public ItemRequestInfo Clone()
23 | {
24 | return new ItemRequestInfo
25 | {
26 | randoItemCreator = (Func)randoItemCreator?.Clone(),
27 | onRandoItemCreation = (Action)onRandoItemCreation?.Clone(),
28 | onRandomizerFinish = (Action)onRandomizerFinish?.Clone(),
29 | realItemCreator = (Func)realItemCreator?.Clone(),
30 | getItemDef = (Func)getItemDef?.Clone()
31 | };
32 | }
33 |
34 | public void AppendTo(ItemRequestInfo info)
35 | {
36 | if (randoItemCreator != null) info.randoItemCreator = randoItemCreator;
37 | info.onRandoItemCreation += onRandoItemCreation;
38 | info.onRandomizerFinish += onRandomizerFinish;
39 | if (realItemCreator != null) info.realItemCreator = realItemCreator;
40 | if (getItemDef != null) info.getItemDef = getItemDef;
41 | }
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/Requests/LocationRequestInfo.cs:
--------------------------------------------------------------------------------
1 | using ItemChanger;
2 | using Newtonsoft.Json;
3 | using RandomizerCore;
4 | using RandomizerMod.RandomizerData;
5 | using RandomizerMod.Settings;
6 |
7 | namespace RandomizerMod.RC
8 | {
9 | public class LocationRequestInfo
10 | {
11 | public Func? randoLocationCreator;
12 | public Action? onRandoLocationCreation;
13 | public Action? onRandomizerFinish;
14 | public Func? customPlacementFetch;
15 | public Action? onPlacementFetch;
16 | public Action? customAddToPlacement;
17 | public Func? getLocationDef;
18 |
19 | public void AddGetLocationDefModifier(string name, Func modifier)
20 | {
21 | Func get = getLocationDef ?? (() => Data.GetLocationDef(name));
22 | getLocationDef = () => modifier(get());
23 | }
24 |
25 | public LocationRequestInfo Clone()
26 | {
27 | return new LocationRequestInfo
28 | {
29 | randoLocationCreator = (Func)randoLocationCreator?.Clone(),
30 | onRandoLocationCreation = (Action)onRandoLocationCreation?.Clone(),
31 | onRandomizerFinish = (Action)onRandomizerFinish?.Clone(),
32 | customPlacementFetch = (Func)customPlacementFetch?.Clone(),
33 | onPlacementFetch = (Action)onPlacementFetch?.Clone(),
34 | customAddToPlacement = (Action)customAddToPlacement?.Clone(),
35 | getLocationDef = (Func)getLocationDef?.Clone(),
36 | };
37 | }
38 |
39 | public void AppendTo(LocationRequestInfo info)
40 | {
41 | if (randoLocationCreator != null) info.randoLocationCreator = randoLocationCreator;
42 | info.onRandoLocationCreation += onRandoLocationCreation;
43 | info.onRandomizerFinish += onRandomizerFinish;
44 | if (customPlacementFetch != null) info.customPlacementFetch = customPlacementFetch;
45 | info.onPlacementFetch += onPlacementFetch;
46 | info.customAddToPlacement += customAddToPlacement;
47 | if (getLocationDef != null) info.getLocationDef = getLocationDef;
48 | }
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/Requests/RBConsts.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.RC
2 | {
3 | ///
4 | /// Strings used by the RequestBuilder as labels.
5 | ///
6 | public static class RBConsts
7 | {
8 | ///
9 | /// Label for the transition stage, in transition rando only.
10 | ///
11 | public const string MainTransitionStage = "Main Transition Stage";
12 | ///
13 | /// Label for the default stage, in any rando.
14 | ///
15 | public const string MainItemStage = "Main Item Stage";
16 |
17 | ///
18 | /// Label for the default group of the default stage, in any rando.
19 | ///
20 | public const string MainItemGroup = "Main Item Group";
21 |
22 | ///
23 | /// Label for the stage of grubs and mimics, when mimics are randomized but grubs are not randomized.
24 | ///
25 | public const string GrubMimicStage = "Grub Mimic Stage";
26 | ///
27 | /// Label for the group of grubs and mimics, when mimics are randomized but grubs are not randomized.
28 | ///
29 | public const string GrubMimicGroup = "Grub Mimic Group";
30 |
31 | ///
32 | /// Prefix used for split groups. The label is formed by appending an integer between 1 and 99 according to the setting.
33 | ///
34 | public const string SplitGroupPrefix = "Split Group ";
35 |
36 | ///
37 | /// Label for the corresponding matched transition group.
38 | ///
39 | public const string InLeftOutRightGroup = "Left -> Right";
40 | ///
41 | /// Label for the corresponding matched transition group.
42 | ///
43 | public const string InRightOutLeftGroup = "Right -> Left";
44 | ///
45 | /// Label for the corresponding matched transition group.
46 | ///
47 | public const string InBotOutTopGroup = "Bot -> Top";
48 | ///
49 | /// Label for the corresponding matched transition group.
50 | ///
51 | public const string InTopOutBotGroup = "Top -> Bot";
52 | ///
53 | /// Label for the group of one-way transitions in any transition rando.
54 | ///
55 | public const string OneWayGroup = "One Way Transitions";
56 | ///
57 | /// Label for the group of two-way transitions, in non-matched transition rando.
58 | ///
59 | public const string TwoWayGroup = "Two Way Transitions";
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/Requests/SelfDualTransitionGroupBuilder.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore;
2 | using RandomizerCore.Randomization;
3 |
4 | namespace RandomizerMod.RC
5 | {
6 | public class SelfDualTransitionGroupBuilder : GroupBuilder
7 | {
8 | public bool coupled;
9 | public readonly Bucket Transitions = new();
10 |
11 | public override void Apply(List groups, RandoFactory factory)
12 | {
13 | List ts = new();
14 | foreach (string s in Transitions.EnumerateWithMultiplicity())
15 | {
16 | ts.Add(factory.MakeTransition(s));
17 | }
18 |
19 | if (coupled)
20 | {
21 | CoupledRandomizationGroup g = new()
22 | {
23 | Items = ts.ToArray(),
24 | Locations = ts.ToArray(),
25 | Label = label,
26 | Strategy = strategy ?? factory.gs.ProgressionDepthSettings.GetTransitionPlacementStrategy(),
27 | Validator = new WeakTransitionValidator(),
28 | };
29 | g.Dual = g;
30 | groups.Add(g);
31 | OnCreateGroup?.Invoke(g);
32 | }
33 | else
34 | {
35 | RandomizationGroup g = new()
36 | {
37 | Items = ts.ToArray(),
38 | Locations = ts.ToArray(),
39 | Label = label,
40 | Strategy = strategy ?? factory.gs.ProgressionDepthSettings.GetTransitionPlacementStrategy(),
41 | };
42 | groups.Add(g);
43 | OnCreateGroup?.Invoke(g);
44 | }
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/Requests/StageBuilder.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.ObjectModel;
2 | using RandomizerCore.Randomization;
3 |
4 | namespace RandomizerMod.RC
5 | {
6 | public class StageBuilder
7 | {
8 | public readonly string label;
9 | public StagePlacementStrategy strategy;
10 | private readonly Dictionary groupLookup;
11 | private readonly List groups;
12 | public readonly ReadOnlyCollection Groups;
13 |
14 | public StageBuilder(string label)
15 | {
16 | this.label = label;
17 | this.groupLookup = new();
18 | this.groups = new();
19 | this.Groups = new(groups);
20 | }
21 |
22 | public void Add(GroupBuilder gb)
23 | {
24 | if (gb == null) throw new ArgumentNullException(nameof(gb));
25 | if (gb.label == null || groupLookup.ContainsKey(gb.label)) throw new ArgumentException(nameof(gb));
26 | gb.stageLabel = label;
27 | groups.Add(gb);
28 | groupLookup.Add(gb.label, gb);
29 | }
30 |
31 | public void Insert(int index, GroupBuilder gb)
32 | {
33 | if (gb == null) throw new ArgumentNullException(nameof(gb));
34 | if (gb.label == null || groupLookup.ContainsKey(gb.label)) throw new ArgumentException(nameof(gb));
35 | gb.stageLabel = label;
36 | groups.Insert(index, gb);
37 | groupLookup.Add(gb.label, gb);
38 | }
39 |
40 | public ItemGroupBuilder AddItemGroup(string group)
41 | {
42 | return InsertItemGroup(group, groups.Count);
43 | }
44 |
45 | public ItemGroupBuilder InsertItemGroup(string group, int index)
46 | {
47 | if (groupLookup.ContainsKey(group)) throw new ArgumentException(nameof(group));
48 |
49 | ItemGroupBuilder gb = new()
50 | {
51 | label = group,
52 | stageLabel = label,
53 | };
54 |
55 | groups.Insert(index, gb);
56 | groupLookup.Add(group, gb);
57 | return gb;
58 | }
59 |
60 | public bool TryGetGroup(string group, out GroupBuilder gb)
61 | {
62 | return groupLookup.TryGetValue(group, out gb);
63 | }
64 |
65 | public GroupBuilder Get(string group)
66 | {
67 | return groupLookup[group];
68 | }
69 |
70 | public RandomizationStage ToRandomizationStage(RandoFactory factory)
71 | {
72 | List rgs = new();
73 | foreach (GroupBuilder gb in groupLookup.Values)
74 | {
75 | gb.Apply(rgs, factory);
76 | }
77 |
78 | return new RandomizationStage
79 | {
80 | label = label,
81 | groups = rgs.ToArray(),
82 | strategy = strategy ?? new StagePlacementStrategy(),
83 | };
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/Requests/SymmetricTransitionGroupBuilder.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore;
2 | using RandomizerCore.Randomization;
3 |
4 | namespace RandomizerMod.RC
5 | {
6 | public class SymmetricTransitionGroupBuilder : GroupBuilder
7 | {
8 | public readonly Bucket Group1 = new();
9 | public readonly Bucket Group2 = new();
10 | public bool coupled;
11 | public string reverseLabel;
12 |
13 | public override void Apply(List groups, RandoFactory factory)
14 | {
15 | if (Group1.GetTotal() != Group2.GetTotal())
16 | {
17 | throw new InvalidOperationException($"Failed to build group {label} due to unbalanced counts.");
18 | }
19 |
20 | List t1s = new();
21 | foreach (string s in Group1.EnumerateWithMultiplicity())
22 | {
23 | t1s.Add(factory.MakeTransition(s));
24 | }
25 |
26 | List t2s = new();
27 | foreach (string s in Group2.EnumerateWithMultiplicity())
28 | {
29 | t2s.Add(factory.MakeTransition(s));
30 | }
31 |
32 | if (coupled)
33 | {
34 | CoupledRandomizationGroup g1 = new()
35 | {
36 | Items = t1s.ToArray(),
37 | Locations = t2s.ToArray(),
38 | Label = label,
39 | Strategy = strategy ?? factory.gs.ProgressionDepthSettings.GetTransitionPlacementStrategy(),
40 | Validator = new WeakTransitionValidator(),
41 | };
42 | CoupledRandomizationGroup g2 = new()
43 | {
44 | Items = t2s.ToArray(),
45 | Locations = t1s.ToArray(),
46 | Label = reverseLabel,
47 | Strategy = strategy?.Clone() ?? factory.gs.ProgressionDepthSettings.GetTransitionPlacementStrategy(),
48 | Validator = new WeakTransitionValidator(),
49 | };
50 | g1.Dual = g2;
51 | g2.Dual = g1;
52 |
53 | groups.Add(g1);
54 | groups.Add(g2);
55 | OnCreateGroup?.Invoke(g1);
56 | OnCreateGroup?.Invoke(g2);
57 | }
58 | else
59 | {
60 | RandomizationGroup g1 = new()
61 | {
62 | Items = t1s.ToArray(),
63 | Locations = t2s.ToArray(),
64 | Label = label,
65 | Strategy = strategy ?? factory.gs.ProgressionDepthSettings.GetTransitionPlacementStrategy(),
66 | };
67 | RandomizationGroup g2 = new()
68 | {
69 | Items = t2s.ToArray(),
70 | Locations = t1s.ToArray(),
71 | Label = reverseLabel,
72 | Strategy = strategy?.Clone() ?? factory.gs.ProgressionDepthSettings.GetTransitionPlacementStrategy(),
73 | };
74 |
75 | groups.Add(g1);
76 | groups.Add(g2);
77 | OnCreateGroup?.Invoke(g1);
78 | OnCreateGroup?.Invoke(g2);
79 | }
80 | }
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/Requests/TransitionGroupBuilder.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore;
2 | using RandomizerCore.Randomization;
3 |
4 | namespace RandomizerMod.RC
5 | {
6 | public class TransitionGroupBuilder : GroupBuilder
7 | {
8 | public Bucket Sources { get; } = new();
9 | public Bucket Targets { get; } = new();
10 |
11 | public override void Apply(List groups, RandoFactory factory)
12 | {
13 | if (Sources.GetTotal() != Targets.GetTotal())
14 | {
15 | throw new InvalidOperationException($"Failed to build group {label} due to unbalanced counts.");
16 | }
17 |
18 | List locations = new();
19 | foreach (string s in Sources.EnumerateWithMultiplicity())
20 | {
21 | locations.Add(factory.MakeTransition(s));
22 | }
23 |
24 | List items = new();
25 | foreach (string s in Targets.EnumerateWithMultiplicity())
26 | {
27 | items.Add(factory.MakeTransition(s));
28 | }
29 |
30 | RandomizationGroup g = new()
31 | {
32 | Label = label,
33 | Items = items.ToArray(),
34 | Locations = locations.ToArray(),
35 | Strategy = strategy ?? factory.gs.ProgressionDepthSettings.GetTransitionPlacementStrategy(),
36 | };
37 | groups.Add(g);
38 | OnCreateGroup?.Invoke(g);
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/Requests/TransitionRequestInfo.cs:
--------------------------------------------------------------------------------
1 | using ItemChanger;
2 | using RandomizerCore;
3 | using RandomizerMod.RandomizerData;
4 |
5 | namespace RandomizerMod.RC
6 | {
7 | public class TransitionRequestInfo
8 | {
9 | public Func? randoTransitionCreator;
10 | public Action? onRandoTransitionCreation;
11 | public Action? onRandomizerFinish;
12 | public Func? realTargetCreator;
13 | public Func realSourceCreator;
14 | public Func? getTransitionDef;
15 |
16 | public void AddGetTransitionDefModifier(string name, Func modifier)
17 | {
18 | Func get = getTransitionDef ?? (() => Data.GetTransitionDef(name));
19 | getTransitionDef = () => modifier(get());
20 | }
21 |
22 | public TransitionRequestInfo Clone()
23 | {
24 | return new TransitionRequestInfo
25 | {
26 | randoTransitionCreator = (Func)randoTransitionCreator?.Clone(),
27 | onRandoTransitionCreation = (Action)onRandoTransitionCreation?.Clone(),
28 | onRandomizerFinish = (Action)onRandomizerFinish?.Clone(),
29 | realTargetCreator = (Func)realTargetCreator?.Clone(),
30 | realSourceCreator = (Func)realSourceCreator?.Clone(),
31 | getTransitionDef = (Func)getTransitionDef?.Clone()
32 | };
33 | }
34 |
35 | public void AppendTo(TransitionRequestInfo info)
36 | {
37 | if (randoTransitionCreator != null) info.randoTransitionCreator = randoTransitionCreator;
38 | info.onRandoTransitionCreation += onRandoTransitionCreation;
39 | info.onRandomizerFinish += onRandomizerFinish;
40 | if (realTargetCreator != null) info.realTargetCreator = realTargetCreator;
41 | if (realSourceCreator != null) info.realSourceCreator = realSourceCreator;
42 | if (getTransitionDef != null) info.getTransitionDef = getTransitionDef;
43 | }
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/SplitCloakItem.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore;
2 | using RandomizerCore.Logic;
3 |
4 | namespace RandomizerMod.RC
5 | {
6 | [Obsolete("Use StringItem for split cloak items")]
7 | public record SplitCloakItem(string Name, bool LeftBiased, Term LeftDashTerm, Term RightDashTerm) : LogicItem(Name)
8 | {
9 | public override void AddTo(ProgressionManager pm)
10 | {
11 | /*
12 | // behavior when left and right must be obtained before shade cloak
13 | bool noLeftDash = pm.Get(LeftDashTerm.Id) < 1;
14 | bool noRightDash = pm.Get(RightDashTerm.Id) < 1;
15 | // Left Dash behavior
16 | if (noLeftDash && (LeftBiased || !noRightDash)) pm.Incr(LeftDashTerm.Id, 1);
17 | // Right Dash behavior
18 | else if (noRightDash && (!LeftBiased || !noLeftDash)) pm.Incr(RightDashTerm.Id, 1);
19 | // Shade Cloak behavior (increments both flags)
20 | else
21 | {
22 | pm.Incr(LeftDashTerm.Id, 1);
23 | pm.Incr(RightDashTerm.Id, 1);
24 | }
25 | */
26 |
27 | // behavior when split shade cloak of one direction can be obtained, but not the other
28 | bool hasLeftDash = pm.Has(LeftDashTerm.Id);
29 | bool hasRightDash = pm.Has(RightDashTerm.Id);
30 | bool hasAnyShadowDash = pm.Has(LeftDashTerm.Id, 2) || pm.Has(RightDashTerm.Id, 2);
31 |
32 | if (hasLeftDash && hasRightDash && hasAnyShadowDash)
33 | {
34 | return; // dupe
35 | }
36 | else if (hasLeftDash && hasRightDash) // full shade cloak behavior
37 | {
38 | pm.Incr(LeftDashTerm, 1);
39 | pm.Incr(RightDashTerm, 1);
40 | return;
41 | }
42 | else if (LeftBiased)
43 | {
44 | if (!hasLeftDash && hasAnyShadowDash) // left shade cloak behavior
45 | {
46 | pm.Incr(LeftDashTerm, 2);
47 | return;
48 | }
49 | else // left cloak behavior
50 | {
51 | pm.Incr(LeftDashTerm, 1);
52 | return;
53 | }
54 | }
55 | else
56 | {
57 | if (!hasRightDash && hasAnyShadowDash) // right shade cloak behavior
58 | {
59 | pm.Incr(RightDashTerm, 2);
60 | return;
61 | }
62 | else // right cloak behavior
63 | {
64 | pm.Incr(RightDashTerm, 1);
65 | return;
66 | }
67 | }
68 | }
69 |
70 | public override IEnumerable GetAffectedTerms()
71 | {
72 | yield return LeftDashTerm;
73 | yield return RightDashTerm;
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/StateVariables/FlowerProviderVariable.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerCore.Logic.StateLogic;
3 |
4 | namespace RandomizerMod.RC.StateVariables
5 | {
6 | /*
7 | * Prefix: $FLOWERGET
8 | * Required Parameters: none
9 | * Optiional Parameters: none
10 | */
11 | public class FlowerProviderVariable : StateModifier
12 | {
13 | public override string Name { get; }
14 | protected readonly StateBool NoFlower;
15 | public const string Prefix = "$FLOWERGET";
16 |
17 | public static bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
18 | {
19 | if (term == Prefix)
20 | {
21 | variable = new FlowerProviderVariable(term, lm);
22 | return true;
23 | }
24 | variable = default;
25 | return false;
26 | }
27 |
28 | public FlowerProviderVariable(string name, LogicManager lm)
29 | {
30 | Name = name;
31 | try
32 | {
33 | NoFlower = lm.StateManager.GetBoolStrict("NOFLOWER");
34 | }
35 | catch (Exception e)
36 | {
37 | throw new InvalidOperationException("Error constructing FlowerProviderVariable", e);
38 | }
39 | }
40 |
41 | protected FlowerProviderVariable(string name)
42 | {
43 | Name = name;
44 | }
45 |
46 | public override IEnumerable GetTerms()
47 | {
48 | return Enumerable.Empty();
49 | }
50 |
51 | public override IEnumerable? ProvideState(object? sender, ProgressionManager pm)
52 | {
53 | return Enumerable.Empty();
54 | }
55 |
56 | public override IEnumerable ModifyState(object? sender, ProgressionManager pm, LazyStateBuilder state)
57 | {
58 | state.SetBool(NoFlower, false);
59 | yield return state;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/StateVariables/FragileCharmVariable.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerCore.Logic.StateLogic;
3 |
4 | namespace RandomizerMod.RC.StateVariables
5 | {
6 | /*
7 | * Equip logic for Fragile Heart, Greed, and Strength. See documentation for EquipCharmVariable for variable pattern.
8 | */
9 | public class FragileCharmVariable : EquipCharmVariable
10 | {
11 | protected readonly Term RepairTerm;
12 | protected readonly StateBool BreakBool;
13 |
14 | public FragileCharmVariable(string name, string charmName, int charmID, LogicManager lm) : this(name, charmName, charmID, lm, "Can_Repair_Fragile_Charms", charmID switch
15 | {
16 | 23 => "BROKEHEART",
17 | 24 => "BROKEGREED",
18 | 25 => "BROKESTRENGTH",
19 | _ => throw new ArgumentException($"Error constructing FCV from {name}: Unknown fragile charm id {charmID}.")
20 | }) { }
21 |
22 | public FragileCharmVariable(string name, string charmName, int charmID, LogicManager lm, string repairTermName, string breakBoolName) : base(name, charmName, charmID, lm)
23 | {
24 | RepairTerm = lm.GetTermStrict(repairTermName) ?? throw new ArgumentException($"Error constructing ECV from {name}: {repairTermName} term does not exist?");
25 | BreakBool = lm.StateManager.GetBoolStrict(breakBoolName) ?? throw new ArgumentException($"Error constructing ECV from {name}: could not find {breakBoolName} state bool.");
26 | }
27 |
28 | public override IEnumerable GetTerms()
29 | {
30 | return base.GetTerms().Append(RepairTerm);
31 | }
32 |
33 | public override bool HasStateRequirements(ProgressionManager pm, T state)
34 | {
35 | return base.HasStateRequirements(pm, state) && (pm.Has(CharmTerm, 2) || !state.GetBool(BreakBool) && pm.Has(RepairTerm));
36 | }
37 |
38 | public void BreakCharm(ProgressionManager pm, ref LazyStateBuilder state)
39 | {
40 | if (pm.Has(CharmTerm, 2)) return;
41 | if (state.GetBool(CharmBool))
42 | {
43 | state.SetBool(CharmBool, false);
44 | state.Increment(UsedNotchesInt, -((RandoModContext)pm.ctx).notchCosts[CharmID - 1]);
45 | if (state.GetBool(Overcharmed)) state.SetBool(Overcharmed, false);
46 | }
47 | state.SetBool(AnticharmBool, true);
48 | state.SetBool(BreakBool, true);
49 | }
50 |
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/StateVariables/HotSpringResetVariable.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerCore.Logic.StateLogic;
3 |
4 | namespace RandomizerMod.RC.StateVariables
5 | {
6 | /*
7 | * Prefix: $HOTSPRINGRESET
8 | * Required Parameters: none
9 | * Optiional Parameters: none
10 | */
11 | public class HotSpringResetVariable : StateModifier
12 | {
13 | public override string Name { get; }
14 | protected readonly ISoulStateManager SSM;
15 | protected readonly IHPStateManager HPSM;
16 | public const string Prefix = "$HOTSPRINGRESET";
17 |
18 | public HotSpringResetVariable(string name, LogicManager lm)
19 | {
20 | Name = name;
21 | try
22 | {
23 | SSM = (ISoulStateManager)lm.GetVariableStrict(SoulStateManager.Prefix);
24 | HPSM = (IHPStateManager)lm.GetVariableStrict(HPStateManager.Prefix);
25 | }
26 | catch (Exception e)
27 | {
28 | throw new InvalidOperationException("Error constructing HotSpringResetVariable", e);
29 | }
30 | }
31 |
32 | public override IEnumerable GetTerms()
33 | {
34 | foreach (Term t in SSM.GetTerms(ISoulStateManager.SSMOperation.RestoreSoul)) yield return t;
35 | foreach (Term t in HPSM.GetTerms(IHPStateManager.HPSMOperation.RestoreWhiteHealth)) yield return t;
36 | }
37 |
38 | public static bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
39 | {
40 | if (term == Prefix)
41 | {
42 | variable = new HotSpringResetVariable(term, lm);
43 | return true;
44 | }
45 | variable = default;
46 | return false;
47 | }
48 |
49 | public override IEnumerable? ProvideState(object? sender, ProgressionManager pm)
50 | {
51 | return [];
52 | }
53 |
54 | public override IEnumerable ModifyState(object? sender, ProgressionManager pm, LazyStateBuilder state)
55 | {
56 | SSM.TryRestoreAllSoul(pm, ref state, restoreReserves: true);
57 | return HPSM.RestoreWhiteHealth(pm, state);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/StateVariables/LifebloodCountVariable.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerCore.Logic.StateLogic;
3 |
4 | namespace RandomizerMod.RC.StateVariables
5 | {
6 | /*
7 | * Prefix: $LIFEBLOOD
8 | * Required Parameters:
9 | - If any parameters are provided, the first parameter must parse to int to give the required number of blue masks (including Joni masks).
10 | If absent, defaults to 1.
11 | * Optional Parameters: none
12 | * Filters to states with determined hp which have at least a certain number of blue masks, including Joni masks.
13 | */
14 | public class LifebloodCountVariable : StateModifier
15 | {
16 | public override string Name { get; }
17 | public const string Prefix = "$LIFEBLOOD";
18 |
19 | protected readonly int RequiredBlueMasks;
20 | protected readonly IHPStateManager HPSM;
21 | protected readonly EquipCharmVariable JonisBlessing;
22 |
23 | public LifebloodCountVariable(string name, LogicManager lm, int requiredBlueMasks)
24 | {
25 | this.Name = name;
26 | RequiredBlueMasks = requiredBlueMasks;
27 | try
28 | {
29 | HPSM = (IHPStateManager)lm.GetVariableStrict(HPStateManager.Prefix);
30 | JonisBlessing = (EquipCharmVariable)lm.GetVariableStrict(EquipCharmVariable.GetName("Joni's_Blessing"));
31 | }
32 | catch (Exception e)
33 | {
34 | throw new InvalidOperationException("Error constructing LifebloodCountVariable", e);
35 | }
36 | }
37 |
38 | public override IEnumerable GetTerms()
39 | {
40 | return HPSM.GetTerms(IHPStateManager.HPSMOperation.GetHPInfo);
41 | }
42 |
43 | public static bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
44 | {
45 | if (VariableResolver.TryMatchPrefix(term, Prefix, out string[] parameters))
46 | {
47 | int amount = parameters.Length == 0 ? 1 : int.Parse(parameters[0]);
48 | variable = new LifebloodCountVariable(term, lm, amount);
49 | return true;
50 | }
51 | variable = default;
52 | return false;
53 | }
54 |
55 | public override IEnumerable ModifyState(object? sender, ProgressionManager pm, LazyStateBuilder state)
56 | {
57 | return HPSM.DetermineHP(pm, state).Where(s => HPSM.GetHPInfo(pm, s) is IHPStateManager.StrictHPInfo hp
58 | && hp.CurrentBlueHP + (JonisBlessing.IsEquipped(s) ? hp.CurrentWhiteHP : 0) >= RequiredBlueMasks);
59 | }
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/StateVariables/RegainSoulVariable.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerCore.Logic.StateLogic;
3 |
4 | namespace RandomizerMod.RC.StateVariables
5 | {
6 | /*
7 | * Prefix: $REGAINSOUL
8 | * Required Parameters:
9 |
10 | * Optional Parameters:
11 | - The first parameter, if given, must parse to int to give the regain amount. Otherwise, fully restores soul.
12 | */
13 | public class RegainSoulVariable : StateModifier
14 | {
15 | public override string Name { get; }
16 | public const string Prefix = "$REGAINSOUL";
17 |
18 | protected readonly int? Amount;
19 | protected readonly ISoulStateManager SSM;
20 |
21 | public RegainSoulVariable(string name, LogicManager lm, int? amount)
22 | {
23 | Name = name;
24 | this.Amount = amount;
25 | try
26 | {
27 | SSM = (ISoulStateManager)lm.GetVariableStrict(SoulStateManager.Prefix);
28 | }
29 | catch (Exception e)
30 | {
31 | throw new InvalidOperationException($"Error constructing RegainSoulVariable", e);
32 | }
33 | }
34 |
35 | public static bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
36 | {
37 | if (VariableResolver.TryMatchPrefix(term, Prefix, out string[] parameters))
38 | {
39 | int amount;
40 | if (parameters.Length == 0)
41 | {
42 | amount = -1;
43 | }
44 | else if (parameters.Length == 1 && int.TryParse(parameters[0], out amount)) { }
45 | else
46 | {
47 | throw new ArgumentException($"{term} is missing amount argument for RegainSoulVariable.");
48 | }
49 |
50 | variable = new RegainSoulVariable(term, lm, amount >= 0 ? amount : null);
51 | return true;
52 | }
53 | variable = default;
54 | return false;
55 | }
56 |
57 | public override IEnumerable GetTerms()
58 | {
59 | return SSM.GetTerms(ISoulStateManager.SSMOperation.RestoreSoul);
60 | }
61 |
62 | public override IEnumerable? ProvideState(object? sender, ProgressionManager pm)
63 | {
64 | return [];
65 | }
66 |
67 | public override IEnumerable ModifyState(object? sender, ProgressionManager pm, LazyStateBuilder state)
68 | {
69 | return Amount.HasValue ? SSM.RestoreSoul(pm, state, Amount.Value) : SSM.RestoreAllSoul(pm, state, restoreReserves: true);
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/StateVariables/SaveQuitResetVariable.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerCore.Logic.StateLogic;
3 |
4 | namespace RandomizerMod.RC.StateVariables
5 | {
6 | /*
7 | * Prefix: $SAVEQUITRESET
8 | * Required Parameters: none
9 | * Optional Parameters: none
10 | * Provides the effect of warping via Benchwarp or savequit, regardless of destination type.
11 | */
12 | public class SaveQuitResetVariable : StateModifier
13 | {
14 | public override string Name { get; }
15 | public const string Prefix = "$SAVEQUITRESET";
16 |
17 | protected readonly StateBool NoFlower;
18 | protected readonly StateBool UsedShade;
19 | protected readonly StateInt RequiredMaxSoul;
20 | protected readonly ISoulStateManager SSM;
21 | protected readonly IHPStateManager HPSM;
22 |
23 | public SaveQuitResetVariable(string term, LogicManager lm)
24 | {
25 | Name = term;
26 | try
27 | {
28 | NoFlower = lm.StateManager.GetBoolStrict("NOFLOWER");
29 | UsedShade = lm.StateManager.GetBoolStrict("USEDSHADE");
30 | RequiredMaxSoul = lm.StateManager.GetIntStrict("REQUIREDMAXSOUL");
31 |
32 | SSM = (ISoulStateManager)lm.GetVariableStrict(SoulStateManager.Prefix);
33 | HPSM = (IHPStateManager)lm.GetVariableStrict(HPStateManager.Prefix);
34 | }
35 | catch (Exception e)
36 | {
37 | throw new InvalidOperationException("Error constructing SaveQuitResetVariable", e);
38 | }
39 | }
40 |
41 | public static bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
42 | {
43 | if (VariableResolver.TryMatchPrefix(term, Prefix, out _))
44 | {
45 | variable = new SaveQuitResetVariable(term, lm);
46 | return true;
47 | }
48 | variable = default;
49 | return false;
50 | }
51 |
52 | public override IEnumerable GetTerms()
53 | {
54 | foreach (Term t in SSM.GetTerms()) yield return t;
55 | foreach (Term t in HPSM.GetTerms(IHPStateManager.HPSMOperation.RestoreWhiteHealth)) yield return t;
56 | }
57 |
58 | public override IEnumerable? ProvideState(object? sender, ProgressionManager pm)
59 | {
60 | return [];
61 | }
62 |
63 | public override IEnumerable ModifyState(object? sender, ProgressionManager pm, LazyStateBuilder state)
64 | {
65 | state.SetBool(NoFlower, true); // not game accurate, but we do this to prevent warps from being required for flower quest.
66 | SSM.TrySpendAllSoul(pm, ref state); // zero out soul. A subsequent modifier will handle bench / start respawn soul effects.
67 | state.SetBool(UsedShade, false); // not necessary to reset shade variables for typical use, but in the case of warping to a non-start hard respawn, it would be correct to reset them here.
68 | state.SetInt(RequiredMaxSoul, 0);
69 | return HPSM.RestoreWhiteHealth(pm, state); // bad to chain this into a cheaper restore on the bench...
70 | }
71 | }
72 | }
73 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/StateVariables/SlopeballVariable.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerCore.Logic.StateLogic;
3 |
4 | namespace RandomizerMod.RC.StateVariables
5 | {
6 | public class SlopeballVariable : StateModifierWrapper
7 | {
8 | public override string Name { get; }
9 |
10 | protected readonly Term slopeballSkips;
11 | protected readonly Term fireball;
12 | public const string Prefix = "$SLOPEBALL";
13 | protected override string InnerPrefix => CastSpellVariable.Prefix;
14 |
15 | public static bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
16 | {
17 | if (term.StartsWith(Prefix))
18 | {
19 | variable = new SlopeballVariable(term, lm);
20 | return true;
21 | }
22 | variable = default;
23 | return false;
24 | }
25 |
26 | public SlopeballVariable(string name, LogicManager lm) : base(name, lm)
27 | {
28 | Name = name;
29 | try
30 | {
31 | slopeballSkips = lm.GetTermStrict("SLOPEBALLSKIPS");
32 | fireball = lm.GetTermStrict("FIREBALL");
33 | }
34 | catch (Exception e)
35 | {
36 | throw new InvalidOperationException("Error constructing SlopeballVariable", e);
37 | }
38 | }
39 |
40 | public override IEnumerable GetTerms()
41 | {
42 | yield return slopeballSkips;
43 | yield return fireball;
44 | foreach (Term t in InnerVariable.GetTerms()) yield return t;
45 | }
46 |
47 | public override IEnumerable ModifyState(object? sender, ProgressionManager pm, LazyStateBuilder state)
48 | {
49 | if (!pm.Has(slopeballSkips) || !pm.Has(fireball)) return Enumerable.Empty();
50 | return InnerVariable.ModifyState(sender, pm, state);
51 | }
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/StateVariables/SpendSoulVariable.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic.StateLogic;
2 | using RandomizerCore.Logic;
3 |
4 | namespace RandomizerMod.RC.StateVariables
5 | {
6 | /*
7 | * Prefix: $SPENDSOUL
8 | * Required Parameters:
9 | - The first parameter must parse to int to give the spend amount.
10 | * Optional Parameters: none
11 | */
12 | public class SpendSoulVariable : StateModifier
13 | {
14 | public override string Name { get; }
15 | public const string Prefix = "$SPENDSOUL";
16 |
17 | protected readonly int Amount;
18 | protected readonly ISoulStateManager SSM;
19 |
20 | public SpendSoulVariable(string name, LogicManager lm, int amount)
21 | {
22 | Name = name;
23 | this.Amount = amount;
24 | try
25 | {
26 | SSM = (ISoulStateManager)lm.GetVariableStrict(SoulStateManager.Prefix);
27 | }
28 | catch (Exception e)
29 | {
30 | throw new InvalidOperationException($"Error constructing SpendSoulVariable", e);
31 | }
32 | }
33 |
34 | public static bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
35 | {
36 | if (VariableResolver.TryMatchPrefix(term, Prefix, out string[] parameters))
37 | {
38 | if (parameters.Length < 1 || !int.TryParse(parameters[0], out int amount))
39 | {
40 | throw new ArgumentException($"{term} is missing amount argument for SpendSoulVariable.");
41 | }
42 |
43 | variable = new SpendSoulVariable(term, lm, amount);
44 | return true;
45 | }
46 | variable = default;
47 | return false;
48 | }
49 |
50 | public override IEnumerable GetTerms()
51 | {
52 | return SSM.GetTerms(ISoulStateManager.SSMOperation.SpendSoul);
53 | }
54 |
55 | public override IEnumerable ModifyState(object? sender, ProgressionManager pm, LazyStateBuilder state)
56 | {
57 | return SSM.SpendSoul(pm, state, Amount);
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/StateVariables/StagStateVariable.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerCore.Logic.StateLogic;
3 |
4 | namespace RandomizerMod.RC.StateVariables
5 | {
6 | /*
7 | * Prefix: $STAGSTATEMODIFIER
8 | * Required Parameters: none
9 | * Optiional Parameters: none
10 | */
11 | public class StagStateVariable : StateModifier
12 | {
13 | public override string Name { get; }
14 | protected readonly StateBool NoFlower;
15 | public const string Prefix = "$STAGSTATEMODIFIER";
16 |
17 | public static bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
18 | {
19 | if (term == Prefix)
20 | {
21 | variable = new StagStateVariable(term, lm);
22 | return true;
23 | }
24 | variable = default;
25 | return false;
26 | }
27 |
28 | protected StagStateVariable(string name)
29 | {
30 | Name = name;
31 | }
32 |
33 | public StagStateVariable(string name, LogicManager lm)
34 | {
35 | Name = name;
36 | try
37 | {
38 | NoFlower = lm.StateManager.GetBoolStrict("NOFLOWER");
39 | }
40 | catch (Exception e)
41 | {
42 | throw new InvalidOperationException("Error constructing StagStateVariable", e);
43 | }
44 | }
45 |
46 | public override IEnumerable GetTerms()
47 | {
48 | return Enumerable.Empty();
49 | }
50 |
51 | public override IEnumerable? ProvideState(object? sender, ProgressionManager pm)
52 | {
53 | return Enumerable.Empty();
54 | }
55 |
56 | public override IEnumerable ModifyState(object? sender, ProgressionManager pm, LazyStateBuilder state)
57 | {
58 | state.SetBool(NoFlower, true);
59 | yield return state;
60 | }
61 | }
62 | }
63 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/StateVariables/StartRespawnResetVariable.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerCore.Logic.StateLogic;
3 |
4 | namespace RandomizerMod.RC.StateVariables
5 | {
6 | /*
7 | * Prefix: $STARTRESPAWN
8 | * Required Parameters: none
9 | * Optional Parameters: none
10 | * Provides the effect of the start respawn. Typically applied in sequence after $SAVEQUITRESET as part of $WARPTOSTART.
11 | */
12 | public class StartRespawnResetVariable : StateModifier
13 | {
14 | public override string Name { get; }
15 | public const string Prefix = "$STARTRESPAWN";
16 |
17 | protected readonly ISoulStateManager SSM;
18 |
19 | public StartRespawnResetVariable(string term, LogicManager lm)
20 | {
21 | Name = term;
22 | try
23 | {
24 | SSM = (ISoulStateManager)lm.GetVariableStrict(SoulStateManager.Prefix);
25 | }
26 | catch (Exception e)
27 | {
28 | throw new InvalidOperationException("Error constructing StartRespawnResetVariable", e);
29 | }
30 | }
31 |
32 | public override IEnumerable GetTerms()
33 | {
34 | return SSM.GetTerms(ISoulStateManager.SSMOperation.RestoreSoul);
35 | }
36 |
37 | public static bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
38 | {
39 | if (VariableResolver.TryMatchPrefix(term, Prefix, out _))
40 | {
41 | variable = new StartRespawnResetVariable(term, lm);
42 | return true;
43 | }
44 | variable = default;
45 | return false;
46 | }
47 |
48 | public override IEnumerable? ProvideState(object? sender, ProgressionManager pm)
49 | {
50 | return [];
51 | }
52 |
53 | public override IEnumerable ModifyState(object? sender, ProgressionManager pm, LazyStateBuilder state)
54 | {
55 | return SSM.RestoreAllSoul(pm, state, true);
56 | }
57 | }
58 | }
59 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/StateVariables/StateModifierWrapper.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerCore.Logic.StateLogic;
3 | using System.Security.Cryptography;
4 |
5 | namespace RandomizerMod.RC.StateVariables
6 | {
7 | ///
8 | /// Base class which handles passing the parameters in a given state modifier name to an inner state modifier.
9 | ///
10 | public abstract class StateModifierWrapper : StateModifier where T : StateModifier
11 | {
12 | public readonly T InnerVariable;
13 | public override string Name { get; }
14 | protected abstract string InnerPrefix { get; }
15 | ///
16 | /// The parameters which were not consumed, and thus were passed to the inner variable.
17 | ///
18 | protected readonly string[] InnerParameters;
19 |
20 | protected StateModifierWrapper(string name, LogicManager lm)
21 | {
22 | Name = name;
23 | try
24 | {
25 | int i = name.IndexOf('[');
26 | string innerName;
27 | if (i >= 0 && VariableResolver.TryMatchPrefix(name, name.Substring(0, i), out string[] parameters))
28 | {
29 | InnerParameters = parameters.Where(p => !Consume(p)).ToArray();
30 | innerName = InnerParameters.Length > 0 ? $"{InnerPrefix}[{string.Join(",", InnerParameters)}]" : InnerPrefix;
31 | }
32 | else
33 | {
34 | InnerParameters = Array.Empty();
35 | innerName = InnerPrefix;
36 | }
37 | InnerVariable = (T)lm.GetVariableStrict(innerName);
38 | }
39 | catch (Exception e)
40 | {
41 | throw new InvalidOperationException("Error constructing " + GetType().Name, e);
42 | }
43 | }
44 |
45 | ///
46 | /// Indicates that the parameter should not be passed to the inner variable.
47 | ///
48 | protected virtual bool Consume(string parameter) => false;
49 | }
50 | }
51 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/StateVariables/TakeDamageVariable.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerCore.Logic.StateLogic;
3 |
4 | namespace RandomizerMod.RC.StateVariables
5 | {
6 | /*
7 | * Prefix: $TAKEDAMAGE
8 | * Required Parameters:
9 | - If any parameters are provided, the first parameter must parse to int to give the damage amount. If absent, defaults to 1.
10 | * Optional Parameters: none
11 | * Implements taking damage from a single hit. Assumes enough time to focus/hiveblood before and after the hit.
12 | */
13 | public class TakeDamageVariable : StateModifier
14 | {
15 | public override string Name { get; }
16 | public const string Prefix = "$TAKEDAMAGE";
17 | protected readonly int Amount;
18 | protected readonly IHPStateManager HPSM;
19 |
20 | public TakeDamageVariable(string name, LogicManager lm, int amount)
21 | {
22 | Name = name;
23 | Amount = amount;
24 | try
25 | {
26 | HPSM = (IHPStateManager)lm.GetVariableStrict(HPStateManager.Prefix);
27 | }
28 | catch (Exception e)
29 | {
30 | throw new InvalidOperationException("Error constructing TakeDamageVariable", e);
31 | }
32 | }
33 |
34 | public static bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
35 | {
36 | if (VariableResolver.TryMatchPrefix(term, Prefix, out string[] parameters))
37 | {
38 | int amount = parameters.Length == 0 ? 1 : int.Parse(parameters[0]);
39 | variable = new TakeDamageVariable(term, lm, amount);
40 | return true;
41 | }
42 | variable = default;
43 | return false;
44 | }
45 |
46 | public override IEnumerable GetTerms()
47 | {
48 | return HPSM.GetTerms(IHPStateManager.HPSMOperation.TakeDamage);
49 | }
50 |
51 | public override IEnumerable ModifyState(object? sender, ProgressionManager pm, LazyStateBuilder state)
52 | {
53 | return HPSM.TakeDamage(pm, state, Amount);
54 | }
55 | }
56 | }
57 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/StateVariables/WarpToBenchResetVariable.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerCore.Logic.StateLogic;
3 |
4 | namespace RandomizerMod.RC.StateVariables
5 | {
6 | /*
7 | * Prefix: $WARPTOBENCH
8 | * Required Parameters: none
9 | * Optional Parameters: none
10 | * Provides the effect of warping to a bench via Benchwarp or savequit. Does not verify whether the player can warp to a bench.
11 | */
12 | public class WarpToBenchResetVariable : StateModifier
13 | {
14 | public override string Name { get; }
15 | public const string Prefix = "$WARPTOBENCH";
16 |
17 | protected readonly SaveQuitResetVariable SaveQuitReset;
18 | protected readonly BenchResetVariable BenchReset;
19 |
20 | public WarpToBenchResetVariable(string name, LogicManager lm)
21 | {
22 | Name = name;
23 | try
24 | {
25 | SaveQuitReset = (SaveQuitResetVariable)lm.GetVariableStrict(SaveQuitResetVariable.Prefix);
26 | BenchReset = (BenchResetVariable)lm.GetVariableStrict(BenchResetVariable.Prefix);
27 | }
28 | catch (Exception e)
29 | {
30 | throw new InvalidOperationException("Error constructing WarpToBenchResetVariable", e);
31 | }
32 | }
33 |
34 | public static bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
35 | {
36 | if (VariableResolver.TryMatchPrefix(term, Prefix, out _))
37 | {
38 | variable = new WarpToBenchResetVariable(term, lm);
39 | return true;
40 | }
41 | variable = default;
42 | return false;
43 | }
44 |
45 | public override IEnumerable? ProvideState(object? sender, ProgressionManager pm)
46 | {
47 | return [];
48 | }
49 |
50 | public override IEnumerable ModifyState(object? sender, ProgressionManager pm, LazyStateBuilder state)
51 | {
52 | return SaveQuitReset.ModifyState(sender, pm, state).SelectMany(s => BenchReset.ModifyState(sender, pm, s));
53 | }
54 |
55 | public override IEnumerable GetTerms()
56 | {
57 | return SaveQuitReset.GetTerms().Concat(BenchReset.GetTerms());
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/StateVariables/WarpToStartResetVariable.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerCore.Logic.StateLogic;
3 |
4 | namespace RandomizerMod.RC.StateVariables
5 | {
6 | /*
7 | * Prefix: $WARPTOSTART
8 | * Required Parameters: none
9 | * Optional Parameters: none
10 | * Provides the effect of warping to start via Benchwarp or savequit.
11 | */
12 | public class WarpToStartResetVariable : StateModifier
13 | {
14 | public override string Name { get; }
15 | public const string Prefix = "$WARPTOSTART";
16 |
17 | protected readonly SaveQuitResetVariable SaveQuitReset;
18 | protected readonly StartRespawnResetVariable StartRespawnReset;
19 |
20 | public WarpToStartResetVariable(string name, LogicManager lm)
21 | {
22 | Name = name;
23 | try
24 | {
25 | SaveQuitReset = (SaveQuitResetVariable)lm.GetVariableStrict(SaveQuitResetVariable.Prefix);
26 | StartRespawnReset = (StartRespawnResetVariable)lm.GetVariableStrict(StartRespawnResetVariable.Prefix);
27 | }
28 | catch (Exception e)
29 | {
30 | throw new InvalidOperationException("Error constructing WarpToStartResetVariable", e);
31 | }
32 | }
33 |
34 | public static bool TryMatch(LogicManager lm, string term, out LogicVariable variable)
35 | {
36 | if (VariableResolver.TryMatchPrefix(term, Prefix, out _))
37 | {
38 | variable = new WarpToStartResetVariable(term, lm);
39 | return true;
40 | }
41 | variable = default;
42 | return false;
43 | }
44 |
45 | public override IEnumerable? ProvideState(object? sender, ProgressionManager pm)
46 | {
47 | return Enumerable.Empty();
48 | }
49 |
50 | public override IEnumerable ModifyState(object? sender, ProgressionManager pm, LazyStateBuilder state)
51 | {
52 | return SaveQuitReset.ModifyState(sender, pm, state).SelectMany(s => StartRespawnReset.ModifyState(sender, pm, s));
53 | }
54 |
55 | public override IEnumerable GetTerms()
56 | {
57 | return SaveQuitReset.GetTerms().Concat(StartRespawnReset.GetTerms());
58 | }
59 | }
60 | }
61 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/StateVariables/WhiteFragmentEquipVariable.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 |
3 | namespace RandomizerMod.RC.StateVariables
4 | {
5 | /*
6 | * Equip logic for Kingsoul/Void Heart. See documentation for EquipCharmVariable for variable pattern.
7 | */
8 | public class WhiteFragmentEquipVariable : EquipCharmVariable
9 | {
10 | protected readonly int Threshold;
11 |
12 | public WhiteFragmentEquipVariable(string name, string charmName, LogicManager lm) : base(name, "WHITEFRAGMENT", 36, lm)
13 | {
14 | Threshold = charmName switch
15 | {
16 | "Void_Heart" => 3,
17 | "Kingsoul" or _ => 2,
18 | };
19 | }
20 |
21 | public override bool HasCharmProgression(ProgressionManager pm)
22 | {
23 | return pm.Has(CharmTerm, Threshold);
24 | }
25 |
26 | public override int GetNotchCost(ProgressionManager pm, T state)
27 | {
28 | return pm.Get(CharmTerm) switch
29 | {
30 | <= 2 => base.GetNotchCost(pm, state),
31 | > 2 => 0,
32 | };
33 | }
34 | }
35 | }
36 |
--------------------------------------------------------------------------------
/RandomizerMod/RC/TransitionPlacement.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore;
2 |
3 | namespace RandomizerMod.RC
4 | {
5 | public record struct TransitionPlacement(RandoModTransition Target, RandoModTransition Source)
6 | {
7 | public void Deconstruct(out RandoModTransition target, out RandoModTransition source)
8 | {
9 | target = this.Target;
10 | source = this.Source;
11 | }
12 |
13 | public static implicit operator GeneralizedPlacement(TransitionPlacement p) => new(p.Target, p.Source);
14 | public static explicit operator TransitionPlacement(GeneralizedPlacement p) => new((RandoModTransition)p.Item, (RandoModTransition)p.Location);
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/RandomizerMod/RandomizerData/CostDef.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Logic;
2 | using RandomizerMod.RC;
3 |
4 | namespace RandomizerMod.RandomizerData
5 | {
6 | public record CostDef(string Term, int Amount)
7 | {
8 | public virtual LogicCost ToLogicCost(LogicManager lm)
9 | {
10 | return Term switch
11 | {
12 | "GEO" => new LogicGeoCost(lm, Amount),
13 | _ => new SimpleCost(lm.GetTermStrict(Term), Amount),
14 | };
15 | }
16 | }
17 | }
18 |
--------------------------------------------------------------------------------
/RandomizerMod/RandomizerData/ItemDef.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.RandomizerData
2 | {
3 | public record ItemDef
4 | {
5 | public string Name { get; init; }
6 | public string Pool { get; init; } // for items in multiple pools, give the most common pool.
7 | public int PriceCap { get; init; }
8 | public bool MajorItem { get; init; } // reserved for the most useful items in the randomizer, used by CursedSettings to penalize progression
9 | }
10 |
11 | /*
12 | [Flags]
13 | public enum ItemPoolFlags : long
14 | {
15 | None = 0L,
16 | IsSkill = 1L << 0,
17 | IsKey = 1L << 1,
18 | IsCharm = 1L << 2,
19 | IsMask = 1L << 3,
20 | IsVessel = 1L << 4,
21 | IsNotch = 1L << 5,
22 | IsOre = 1L << 6,
23 | IsGeoChest = 1L << 7,
24 | IsEgg = 1L << 8,
25 | IsRelic = 1L << 9,
26 | IsRoot = 1L << 10,
27 | IsDreamWarriorEssence = 1L << 11,
28 | IsDreamBossEssence = 1L << 12,
29 | IsGrub = 1L << 13,
30 | IsMimic = 1L << 14,
31 | IsMap = 1L << 15,
32 | IsStag = 1L << 16,
33 | IsCocoon = 1L << 17,
34 | IsFlame = 1L << 18,
35 | IsHunterJournalEntry = 1L << 19,
36 | IsRock = 1L << 20,
37 | IsSoul = 1L << 21,
38 | IsLore = 1L << 22,
39 | IsCustomAbility = 1L << 23,
40 | IsDreamer = 1L << 24, // oops
41 | }
42 |
43 | [Flags]
44 | public enum ItemPropertyFlags : long
45 | {
46 | None = 0L,
47 | IsUnique = 1L << 0,
48 | IsBigItem = 1L << 1,
49 | IsSpell = 1L << 2,
50 | IsGeo = 1L << 3,
51 | // ???
52 | }
53 | */
54 | }
55 |
--------------------------------------------------------------------------------
/RandomizerMod/RandomizerData/JsonUtil.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using Newtonsoft.Json.Serialization;
3 | using Newtonsoft.Json.Converters;
4 | using System.IO;
5 | using System.Reflection;
6 |
7 | namespace RandomizerMod.RandomizerData
8 | {
9 | public static class JsonUtil
10 | {
11 | public static readonly JsonSerializer _js;
12 |
13 | public static T Deserialize(string embeddedResourcePath)
14 | {
15 | using (StreamReader sr = new StreamReader(typeof(JsonUtil).Assembly.GetManifestResourceStream(embeddedResourcePath)))
16 | using (var jtr = new JsonTextReader(sr))
17 | {
18 | return _js.Deserialize(jtr);
19 | }
20 | }
21 |
22 | public static T DeserializeString(string json)
23 | {
24 | using (StringReader sr = new StringReader(json))
25 | using (var jtr = new JsonTextReader(sr))
26 | {
27 | return _js.Deserialize(jtr);
28 | }
29 | }
30 |
31 | public static void Serialize(object o, string fileName)
32 | {
33 | File.WriteAllText(Path.Combine(Path.GetDirectoryName(typeof(JsonUtil).Assembly.Location), fileName), Serialize(o));
34 | }
35 |
36 | public static string Serialize(object o)
37 | {
38 | using (StringWriter sw = new StringWriter())
39 | {
40 | _js.Serialize(sw, o);
41 | sw.Flush();
42 | return sw.ToString();
43 | }
44 | }
45 |
46 | public static void Serialize(TextWriter tw, object o)
47 | {
48 | _js.Serialize(tw, o);
49 | }
50 |
51 | static JsonUtil()
52 | {
53 | _js = new JsonSerializer
54 | {
55 | DefaultValueHandling = DefaultValueHandling.Include,
56 | Formatting = Formatting.Indented,
57 | TypeNameHandling = TypeNameHandling.Auto,
58 | };
59 |
60 | _js.Converters.Add(new StringEnumConverter());
61 | }
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/RandomizerMod/RandomizerData/LocationDef.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 |
3 | namespace RandomizerMod.RandomizerData
4 | {
5 | public record LocationDef
6 | {
7 | public string Name { get; init; }
8 | public string SceneName { get; init; }
9 | [JsonIgnore] public virtual string TitledArea { get => Data.GetRoomDef(SceneName)?.TitledArea; }
10 | [JsonIgnore] public virtual string MapArea { get => Data.GetRoomDef(SceneName)?.MapArea; }
11 | ///
12 | /// If true, copies of this location after the first may be shuffled among other flexible count locations by the RequestBuilder.
13 | ///
14 | public bool FlexibleCount { get; init; }
15 | ///
16 | /// If true, copies of this location after the first will receive severe penalties to prevent multiple progression items.
17 | ///
18 | public bool AdditionalProgressionPenalty { get; init; }
19 | }
20 |
21 | // Incomplete ideas for potential enhancements below:
22 | /*
23 | [Flags]
24 | public enum LocationPoolFlags : long
25 | {
26 | None = 0L,
27 | IsSkill = 1L << 0,
28 | IsKey = 1L << 1,
29 | IsCharm = 1L << 2,
30 | IsMask = 1L << 3,
31 | IsVessel = 1L << 4,
32 | IsNotch = 1L << 5,
33 | IsOre = 1L << 6,
34 | IsGeoChest = 1L << 7,
35 | IsEgg = 1L << 8,
36 | IsRelic = 1L << 9,
37 | IsRoot = 1L << 10,
38 | IsDreamWarriorEssence = 1L << 11,
39 | IsDreamBossEssence = 1L << 12,
40 | IsGrub = 1L << 13,
41 | IsMimic = 1L << 14,
42 | IsMap = 1L << 15,
43 | IsStag = 1L << 16,
44 | IsCocoon = 1L << 17,
45 | IsFlame = 1L << 18,
46 | IsHunterJournalEntry = 1L << 19,
47 | IsRock = 1L << 20,
48 | IsSoul = 1L << 21,
49 | IsLore = 1L << 22,
50 | IsCustomAbility = 1L << 23,
51 | IsDreamer = 1L << 24, // oops
52 | }
53 |
54 | [Flags]
55 | public enum LocationPropertyFlags : long
56 | {
57 | None,
58 |
59 | IsChest,
60 | IsShop,
61 | }
62 | */
63 | }
64 |
--------------------------------------------------------------------------------
/RandomizerMod/RandomizerData/PoolDef.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.RandomizerData
2 | {
3 | ///
4 | /// Data structure representing a collection of items and locations that can be optionally randomized.
5 | ///
6 | public class PoolDef
7 | {
8 | ///
9 | /// The name of the pool.
10 | ///
11 | public string Name { get; init; }
12 | ///
13 | /// A slightly broader classification which merges smaller pools into larger ones (e.g. Focus into Skill, etc). Used by SplitGroupSettings.
14 | ///
15 | public string Group { get; init; }
16 | public string Path { get; init; }
17 | public string[] IncludeItems { get; init; }
18 | public string[] IncludeLocations { get; init; }
19 | public VanillaDef[] Vanilla { get; init; }
20 |
21 | public bool IsIncluded(Settings.GenerationSettings gs)
22 | {
23 | if (!string.IsNullOrEmpty(Path)
24 | && Settings.Util.Get(gs, Path) is bool value
25 | && value) return true;
26 | else return false;
27 | }
28 |
29 | public bool IsVanilla(Settings.GenerationSettings gs)
30 | {
31 | if (!string.IsNullOrEmpty(Path)
32 | && Settings.Util.Get(gs, Path) is bool value
33 | && !value) return true;
34 | else return false;
35 | }
36 |
37 | }
38 | }
39 |
--------------------------------------------------------------------------------
/RandomizerMod/RandomizerData/PoolNames.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.RandomizerData
2 | {
3 | public static class PoolNames
4 | {
5 | public const string Dreamer = "Dreamer";
6 | public const string Skill = "Skill";
7 | public const string Charm = "Charm";
8 | public const string Key = "Key";
9 | public const string Focus = "Focus";
10 | public const string Swim = "Swim";
11 | public const string Mask = "Mask";
12 | public const string CursedMask = "CursedMask";
13 | public const string Vessel = "Vessel";
14 | public const string Notch = "Notch";
15 | public const string SalubraNotch = "SalubraNotch";
16 | public const string CursedNotch = "CursedNotch";
17 | public const string Ore = "Ore";
18 | public const string Geo = "Geo";
19 | public const string JunkPitChest = "JunkPitChest";
20 | public const string Egg = "Egg";
21 | public const string Relic = "Relic";
22 | public const string Root = "Root";
23 | public const string DreamWarrior = "DreamWarrior";
24 | public const string DreamBoss = "DreamBoss";
25 | public const string Grub = "Grub";
26 | public const string Mimic = "Mimic";
27 | public const string Map = "Map";
28 | public const string Stag = "Stag";
29 | public const string Cocoon = "Cocoon";
30 | public const string Flame = "Flame";
31 | public const string Journal = "Journal";
32 | public const string ElevatorPass = "ElevatorPass";
33 | public const string SplitCloak = "SplitCloak";
34 | public const string SplitClaw = "SplitClaw";
35 | public const string CursedNail = "CursedNail";
36 | public const string SplitSuperdash = "SplitSuperdash";
37 | public const string EggShop = "EggShop";
38 | public const string Rock = "Rock";
39 | public const string Boss_Geo = "Boss_Geo";
40 | public const string Soul = "Soul";
41 | public const string Lore = "Lore";
42 | }
43 | }
44 |
--------------------------------------------------------------------------------
/RandomizerMod/RandomizerData/RoomDef.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.RandomizerData
2 | {
3 | public record RoomDef
4 | {
5 | public string SceneName { get; init; }
6 | public string MapArea { get; init; }
7 | public string TitledArea { get; init; }
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/RandomizerMod/RandomizerData/StartDef.cs:
--------------------------------------------------------------------------------
1 | using GlobalEnums;
2 | using RandomizerCore;
3 | using RandomizerCore.Logic;
4 | using RandomizerMod.Settings;
5 |
6 | namespace RandomizerMod.RandomizerData
7 | {
8 | public record StartDef
9 | {
10 | ///
11 | /// The name of the start. Names should be unique.
12 | ///
13 | public string Name { get; init; }
14 | ///
15 | /// The scene of the start location in-game.
16 | ///
17 | public string SceneName { get; init; }
18 | ///
19 | /// The x-coordinate of the start location in-game.
20 | ///
21 | public float X { get; init; }
22 | ///
23 | /// The y-coordinate of the start location in-game.
24 | ///
25 | public float Y { get; init; }
26 | ///
27 | /// The map zone of the start location in-game.
28 | ///
29 | public MapZone Zone { get; init; }
30 |
31 | ///
32 | /// The transition which is used as the initial logical progression for this start location.
33 | ///
34 | public string Transition { get; init; }
35 |
36 | ///
37 | /// Logic evaluated by the SettingsPM to determine whether the start can be selected in the menu. Must not be null.
38 | ///
39 | public string Logic { get; init; }
40 | ///
41 | /// Logic evaluated by the SettingsPM to determine whether the start can be randomly selected. If null, Logic is used instead.
42 | ///
43 | public string RandoLogic { get; init; }
44 | ///
45 | /// Flag which determines whether the start is given a button in the Start Locations menu. Hidden starts can still be randomly selected.
46 | ///
47 | public bool ExcludeFromMenu { get; init; }
48 |
49 | public virtual bool CanBeSelected(SettingsPM pm)
50 | {
51 | return pm.Evaluate(Logic);
52 | }
53 |
54 | public virtual bool CanBeRandomized(SettingsPM pm)
55 | {
56 | return pm.Evaluate(RandoLogic ?? Logic);
57 | }
58 |
59 | public virtual bool DisplayInMenu(SettingsPM pm)
60 | {
61 | return !ExcludeFromMenu;
62 | }
63 |
64 | ///
65 | /// Returns a sequence of term values which will be treated as setters by the ProgressionInitializer.
66 | ///
State-valued terms in the sequence will be linked to Start_State, regardless of the int parameter.
67 | ///
68 | public virtual IEnumerable GetStartLocationProgression(LogicManager lm)
69 | {
70 | yield return new(lm.GetTermStrict(Transition), 1);
71 | }
72 |
73 | public virtual ItemChanger.StartDef ToItemChangerStartDef()
74 | {
75 | return new ItemChanger.StartDef
76 | {
77 | SceneName = SceneName,
78 | X = X,
79 | Y = Y,
80 | MapZone = (int)Zone,
81 | RespawnFacingRight = true,
82 | SpecialEffects = ItemChanger.SpecialStartEffects.Default | ItemChanger.SpecialStartEffects.SlowSoulRefill,
83 | };
84 | }
85 | }
86 | }
87 |
--------------------------------------------------------------------------------
/RandomizerMod/RandomizerData/TransitionDef.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 |
3 | namespace RandomizerMod.RandomizerData
4 | {
5 | public record TransitionDef
6 | {
7 | [JsonIgnore] public string Name => $"{SceneName}[{DoorName}]";
8 | public string SceneName { get; init; }
9 | public string DoorName { get; init; }
10 | [JsonIgnore] public virtual string TitledArea { get => Data.GetRoomDef(SceneName)?.TitledArea; }
11 | [JsonIgnore] public virtual string MapArea { get => Data.GetRoomDef(SceneName)?.MapArea; }
12 | public string VanillaTarget { get; init; }
13 | public TransitionDirection Direction { get; init; }
14 | public bool IsTitledAreaTransition { get; init; }
15 | public bool IsMapAreaTransition { get; init; }
16 | public TransitionSides Sides { get; init; }
17 | }
18 |
19 | public enum TransitionSides
20 | {
21 | Both = 0,
22 | ///
23 | /// A one way transition exiting a scene.
24 | ///
25 | OneWayIn = 1,
26 | ///
27 | /// A one way transition entering a scene.
28 | ///
29 | OneWayOut = 2,
30 | }
31 |
32 | public enum TransitionDirection
33 | {
34 | Door,
35 | Left,
36 | Right,
37 | Top,
38 | Bot,
39 | }
40 |
41 | }
42 |
--------------------------------------------------------------------------------
/RandomizerMod/RandomizerData/VanillaDef.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.RandomizerData
2 | {
3 | public record VanillaDef(string Item, string Location, CostDef[]? Costs = null)
4 | {
5 | public virtual bool Equals(VanillaDef other)
6 | {
7 | return other != null && Item == other.Item && Location == other.Location &&
8 | (Costs == other.Costs || (Costs != null && other.Costs != null && Costs.SequenceEqual(other.Costs)));
9 | }
10 |
11 | public override int GetHashCode()
12 | {
13 | return Item.GetHashCode() ^ Location.GetHashCode() + (Costs != null ? Costs.Length : -1);
14 | }
15 | }
16 | }
17 |
--------------------------------------------------------------------------------
/RandomizerMod/Resources/Data/logic_settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "PRECISEMOVEMENT": "SkipSettings.PreciseMovement",
3 | "PROFICIENTCOMBAT": "SkipSettings.ProficientCombat",
4 | "BACKGROUNDPOGOS": "SkipSettings.BackgroundObjectPogos",
5 | "ENEMYPOGOS": "SkipSettings.EnemyPogos",
6 | "OBSCURESKIPS": "SkipSettings.ObscureSkips",
7 | "SHADESKIPS": "SkipSettings.ShadeSkips",
8 | "INFECTIONSKIPS": "SkipSettings.InfectionSkips",
9 | "FIREBALLSKIPS": "SkipSettings.FireballSkips",
10 | "SPIKETUNNELS": "SkipSettings.SpikeTunnels",
11 | "ACIDSKIPS": "SkipSettings.AcidSkips",
12 | "DAMAGEBOOSTS": "SkipSettings.DamageBoosts",
13 | "DANGEROUSSKIPS": "SkipSettings.DangerousSkips",
14 | "DARKROOMS": "SkipSettings.DarkRooms",
15 | "SLOPEBALLSKIPS": "SkipSettings.Slopeballs",
16 | "SHRIEKPOGOSKIPS": "SkipSettings.ShriekPogos",
17 | "COMPLEXSKIPS": "SkipSettings.ComplexSkips",
18 | "DIFFICULTSKIPS": "SkipSettings.DifficultSkips",
19 | "RANDOMNAIL": "NoveltySettings.RandomizeNail",
20 | "CURSED": "CursedSettings.RemoveSpellUpgrades",
21 | "RANDOMFOCUS": "NoveltySettings.RandomizeFocus",
22 | "RANDOMELEVATORS": "NoveltySettings.RandomizeElevatorPass"
23 | }
--------------------------------------------------------------------------------
/RandomizerMod/Resources/Logic/state.json:
--------------------------------------------------------------------------------
1 | {
2 | "Fields": {
3 | "Bool": [
4 | "USEDSHADE",
5 | "OVERCHARMED",
6 | "CANNOTOVERCHARM",
7 | "CANNOTREGAINSOUL",
8 | "CANNOTSHADESKIP",
9 | "BROKEHEART",
10 | "BROKEGREED",
11 | "BROKESTRENGTH",
12 | "NOFLOWER",
13 | "NOPASSEDCHARMEQUIP",
14 | "CHARM1",
15 | "CHARM2",
16 | "CHARM3",
17 | "CHARM4",
18 | "CHARM5",
19 | "CHARM6",
20 | "CHARM7",
21 | "CHARM8",
22 | "CHARM9",
23 | "CHARM10",
24 | "CHARM11",
25 | "CHARM12",
26 | "CHARM13",
27 | "CHARM14",
28 | "CHARM15",
29 | "CHARM16",
30 | "CHARM17",
31 | "CHARM18",
32 | "CHARM19",
33 | "CHARM20",
34 | "CHARM21",
35 | "CHARM22",
36 | "CHARM23",
37 | "CHARM24",
38 | "CHARM25",
39 | "CHARM26",
40 | "CHARM27",
41 | "CHARM28",
42 | "CHARM29",
43 | "CHARM30",
44 | "CHARM31",
45 | "CHARM32",
46 | "CHARM33",
47 | "CHARM34",
48 | "CHARM35",
49 | "CHARM36",
50 | "CHARM37",
51 | "CHARM38",
52 | "CHARM39",
53 | "CHARM40",
54 |
55 | "noCHARM1",
56 | "noCHARM2",
57 | "noCHARM3",
58 | "noCHARM4",
59 | "noCHARM5",
60 | "noCHARM6",
61 | "noCHARM7",
62 | "noCHARM8",
63 | "noCHARM9",
64 | "noCHARM10",
65 | "noCHARM11",
66 | "noCHARM12",
67 | "noCHARM13",
68 | "noCHARM14",
69 | "noCHARM15",
70 | "noCHARM16",
71 | "noCHARM17",
72 | "noCHARM18",
73 | "noCHARM19",
74 | "noCHARM20",
75 | "noCHARM21",
76 | "noCHARM22",
77 | "noCHARM23",
78 | "noCHARM24",
79 | "noCHARM25",
80 | "noCHARM26",
81 | "noCHARM27",
82 | "noCHARM28",
83 | "noCHARM29",
84 | "noCHARM30",
85 | "noCHARM31",
86 | "noCHARM32",
87 | "noCHARM33",
88 | "noCHARM34",
89 | "noCHARM35",
90 | "noCHARM36",
91 | "noCHARM37",
92 | "noCHARM38",
93 | "noCHARM39",
94 | "noCHARM40"
95 |
96 | ],
97 | "Int": [
98 | "SPENTSOUL",
99 | "SPENTRESERVESOUL",
100 | "SOULLIMITER",
101 | "REQUIREDMAXSOUL",
102 | "SPENTHP",
103 | "SPENTBLUEHP",
104 | "LAZYSPENTHP",
105 | "USEDNOTCHES",
106 | "MAXNOTCHCOST"
107 | ]
108 | },
109 | "Properties": {
110 | "NOFLOWER": {
111 | "DefaultValue": true
112 | },
113 | "NOPASSEDCHARMEQUIP": {
114 | "DefaultValue": true
115 | }
116 | },
117 | "NamedStates": {},
118 | "NamedStateUnions": {}
119 | }
120 |
--------------------------------------------------------------------------------
/RandomizerMod/Resources/entries.txt:
--------------------------------------------------------------------------------
1 | Great Hopper
2 | Mantis Petra
3 | Gluttonous Husk
4 | Pilflip
5 | Garpede
6 | Weathered Mask
7 | Uumuu
8 | Violent Husk
9 | Tiktik
10 | Sharp Baldur
11 | Death Loodle
12 | Winged Sentry
13 | Wandering Husk
14 | Husk Miner
15 | Obble
16 | Mossy Vagabond
17 | Crawlid
18 | Maskfly
19 | Hornet
20 | Gruzzer
21 | Lance Sentry
22 | Husk Bully
23 | Grimmkin Novice
24 | Goam
25 | Mistake
26 | Nightmare King
27 | Leaping Husk
28 | Nosk
29 | Aspid Mother
30 | Boofly
31 | Great Nailsage Sly
32 | Sibling
33 | Gorb
34 | Loodle
35 | Deepling
36 | Armoured Squit
37 | Fungified Husk
38 | Primal Aspid
39 | Baldur
40 | Grey Prince Zote
41 | Little Weaver
42 | Corpse Creeper
43 | Squit
44 | Charged Lumafly
45 | Hollow Knight
46 | Mosskin
47 | Broken Vessel
48 | Fungling
49 | Ooma
50 | Husk Warrior
51 | Mantis Warrior
52 | Mossfly
53 | Hiveling
54 | Mosscreep
55 | Hopping Zoteling
56 | Kingsmould
57 | Volatile Mosskin
58 | Stalking Devout
59 | Lesser Mawlek
60 | Wingmould
61 | Shrumeling
62 | Aluba
63 | Mantis Lords
64 | Aspid Hunter
65 | Volt Twister
66 | Aspid Hatchling
67 | Crystal Hunter
68 | Void Idol
69 | Shrumal Ogre
70 | No Eyes
71 | Radiance
72 | Glimback
73 | Seal of Binding
74 | Watcher Knight
75 | Xero
76 | Vengefly
77 | Lifeseed
78 | Pure Vessel
79 | Shielded Fool
80 | Traitor Lord
81 | Hive Guardian
82 | Spiny Husk
83 | Cowardly Husk
84 | Hopper
85 | Gorgeous Husk
86 | Dirtcarver
87 | Menderbug
88 | Marmu
89 | Soul Twister
90 | God Tamer
91 | Nailmasters Oro & Mato
92 | Bluggsac
93 | Sporg
94 | Flukemarm
95 | Pale Lurker
96 | Husk Hornhead
97 | Gruz Mother
98 | Mantis Youth
99 | Vengefly King
100 | Massive Moss Charger
101 | Dung Defender
102 | Grimmkin Master
103 | Shrumal Warrior
104 | Hive Knight
105 | Winged Zoteling
106 | Duranda
107 | Moss Charger
108 | Elder Hu
109 | Heavy Sentry
110 | Flukefey
111 | Husk Hive
112 | Paintmaster Sheo
113 | Entombed Husk
114 | Durandoo
115 | Volatile Zoteling
116 | Soul Master
117 | Mantis Traitor
118 | Markoth
119 | Grimmkin Nightmare
120 | Sturdy Fool
121 | Heavy Fool
122 | White Defender
123 | Shadow Creeper
124 | Elder Baldur
125 | Royal Retainer
126 | Hive Soldier
127 | Void Tendrils
128 | Shade
129 | Ambloom
130 | Flukemunga
131 | Folly
132 | Slobbering Husk
133 | Crystallised Husk
134 | Crystal Crawler
135 | Volatile Gruzzer
136 | Grub Mimic
137 | Great Husk Sentry
138 | Crystal Guardian
139 | The Collector
140 | Zote
141 | Flukemon
142 | Lightseed
143 | Galien
144 | Husk Guard
145 | Grimm
146 | Maggot
147 | Husk Dandy
148 | Gulka
149 | Hunter's Mark
150 | Carver Hatcher
151 | Winged Fool
152 | Shardmite
153 | Husk Sentry
154 | Mawlurk
155 | Deephunter
156 | False Knight
157 | Belfly
158 | Fool Eater
159 | Brooding Mawlek
160 | Moss Knight
161 | Fungoon
162 | Soul Warrior
163 | Oblobble
164 | Infected Balloon
165 | Battle Obble
166 | Uoma
167 | Furious Vengefly
168 | Hwurmp
--------------------------------------------------------------------------------
/RandomizerMod/Resources/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/homothetyhk/RandomizerMod/06b601c05ce08c90df2452c858855314e8037e45/RandomizerMod/Resources/logo.png
--------------------------------------------------------------------------------
/RandomizerMod/Settings/CostSettings.cs:
--------------------------------------------------------------------------------
1 | using MenuChanger.Attributes;
2 |
3 | namespace RandomizerMod.Settings
4 | {
5 | public class CostSettings : SettingsModule
6 | {
7 | [DynamicBound(nameof(MaximumGrubCost), true)]
8 | [MenuRange(0, 46)]
9 | public int MinimumGrubCost;
10 |
11 | [TriggerValidation(nameof(GrubTolerance))]
12 | [DynamicBound(nameof(MinimumGrubCost), false)]
13 | [MenuRange(0, 46)]
14 | public int MaximumGrubCost;
15 |
16 | [DynamicBound(nameof(GrubToleranceUB), true)]
17 | [MenuRange(0, 46)]
18 | public int GrubTolerance;
19 | private int GrubToleranceUB => 46 - MaximumGrubCost;
20 |
21 |
22 | [DynamicBound(nameof(MaximumEssenceCost), true)]
23 | [MenuRange(0, 2800)]
24 | public int MinimumEssenceCost;
25 |
26 | [DynamicBound(nameof(MinimumEssenceCost), false)]
27 | [MenuRange(0, 2800)]
28 | public int MaximumEssenceCost;
29 |
30 | [MenuRange(0, 250)]
31 | public int EssenceTolerance;
32 |
33 |
34 | [DynamicBound(nameof(MaximumEggCost), true)]
35 | [MenuRange(0, 21)]
36 | public int MinimumEggCost;
37 |
38 | [TriggerValidation(nameof(EggTolerance))]
39 | [DynamicBound(nameof(MinimumEggCost), false)]
40 | [MenuRange(0, 21)]
41 | public int MaximumEggCost;
42 |
43 | [DynamicBound(nameof(EggToleranceUB), true)]
44 | [MenuRange(0, 21)]
45 | public int EggTolerance;
46 | private int EggToleranceUB => 21 - MaximumEggCost;
47 |
48 |
49 | [DynamicBound(nameof(MaximumCharmCost), true)]
50 | [MenuRange(0, 40)]
51 | public int MinimumCharmCost;
52 |
53 | [TriggerValidation(nameof(CharmTolerance))]
54 | [DynamicBound(nameof(MinimumCharmCost), false)]
55 | [MenuRange(0, 40)]
56 | public int MaximumCharmCost;
57 |
58 | [DynamicBound(nameof(CharmToleranceUB), true)]
59 | [MenuRange(0, 40)]
60 | public int CharmTolerance;
61 | private int CharmToleranceUB => 40 - MaximumCharmCost;
62 |
63 |
64 | public override void Clamp(GenerationSettings gs)
65 | {
66 | if (MaximumGrubCost < MinimumGrubCost) MaximumGrubCost = MinimumGrubCost;
67 | if (GrubTolerance + MaximumGrubCost > 46) GrubTolerance = 46 - MaximumGrubCost;
68 |
69 | if (MaximumEssenceCost < MinimumEssenceCost) MaximumEssenceCost = MinimumEssenceCost;
70 |
71 | if (MaximumEggCost < MinimumEggCost) MaximumEggCost = MinimumEggCost;
72 | if (EggTolerance + MaximumEggCost > 21) EggTolerance = 21 - MaximumEggCost;
73 |
74 | if (MaximumCharmCost < MinimumCharmCost) MaximumCharmCost = MinimumCharmCost;
75 | if (CharmTolerance + MaximumCharmCost > 40) CharmTolerance = 40 - MaximumCharmCost;
76 | }
77 | }
78 | }
79 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/CursedSettings.cs:
--------------------------------------------------------------------------------
1 | using MenuChanger.Attributes;
2 | using RandomizerCore.Extensions;
3 | using System.Text;
4 |
5 | namespace RandomizerMod.Settings
6 | {
7 | [Serializable]
8 | public class CursedSettings : SettingsModule
9 | {
10 | public bool LongerProgressionChains;
11 | public bool ReplaceJunkWithOneGeo;
12 | public bool RemoveSpellUpgrades;
13 | public bool Deranged;
14 | [MenuRange(0, 4)]
15 | public int CursedMasks;
16 | [MenuRange(0, 2)]
17 | public int CursedNotches;
18 | public bool RandomizeMimics;
19 | [MinValue(0)]
20 | public int MaximumGrubsReplacedByMimics;
21 |
22 | public string ToMultiline()
23 | {
24 | StringBuilder sb = new("Curses");
25 | foreach (var field in Util.GetFieldNames(typeof(CursedSettings)))
26 | {
27 | sb.AppendLine($"{field.FromCamelCase()}: {Util.Get(this, field)}");
28 | }
29 |
30 | return sb.ToString();
31 | }
32 | }
33 | }
34 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/DuplicateItemSettings.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Settings
2 | {
3 | public class DuplicateItemSettings : SettingsModule
4 | {
5 | public bool MothwingCloak;
6 | public bool MantisClaw;
7 | public bool CrystalHeart;
8 | public bool MonarchWings;
9 | public bool ShadeCloak;
10 | public bool DreamNail;
11 | public bool VoidHeart;
12 | public bool Dreamer;
13 | public bool SwimmingItems;
14 | public bool LevelOneSpells;
15 |
16 | public bool LevelTwoSpells;
17 | public bool Grimmchild;
18 | public bool NailArts;
19 | public bool CursedNailItems;
20 | public bool DuplicateUniqueKeys;
21 | public SimpleKeySetting SimpleKeyHandling;
22 | public SplitItemSetting SplitClawHandling;
23 | public SplitItemSetting SplitCloakHandling;
24 | public SplitItemSetting SplitSuperdashHandling;
25 | public enum SimpleKeySetting
26 | {
27 | NoDupe,
28 | TwoExtraKeysInLogic,
29 | TwoDupeKeys,
30 | }
31 |
32 | public enum SplitItemSetting
33 | {
34 | NoDupe,
35 | DupeLeft,
36 | DupeRight,
37 | DupeRandom,
38 | DupeBoth,
39 | }
40 | }
41 | }
42 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/GlobalSettings.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Settings
2 | {
3 | public class GlobalSettings
4 | {
5 | public GenerationSettings DefaultMenuSettings = new();
6 | public List Profiles = new(){ null };
7 |
8 | public static bool IsInvalid(GlobalSettings value)
9 | {
10 | return value is null || value.Profiles is null || value.DefaultMenuSettings is null;
11 | }
12 | }
13 |
14 | public class MenuProfile
15 | {
16 | public string name;
17 | public GenerationSettings settings;
18 | public override string ToString()
19 | {
20 | return name;
21 | }
22 | }
23 | }
24 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/LongLocationSettings.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Settings
2 | {
3 | public class LongLocationSettings : SettingsModule
4 | {
5 | public enum WPSetting
6 | {
7 | Allowed,
8 | ExcludePathOfPain,
9 | ExcludeWhitePalace
10 | }
11 |
12 | public enum BossEssenceSetting
13 | {
14 | All,
15 | ExcludeZoteAndWhiteDefender,
16 | ExcludeAllDreamBosses,
17 | ExcludeAllDreamWarriors
18 | }
19 |
20 | public enum CostItemHintSettings
21 | {
22 | CostAndName,
23 | CostOnly,
24 | NameOnly,
25 | None
26 | }
27 |
28 | public WPSetting WhitePalaceRando;
29 | public BossEssenceSetting BossEssenceRando;
30 |
31 | public bool ColosseumPreview;
32 | public bool KingFragmentPreview;
33 |
34 | public bool FlowerQuestPreview;
35 | public bool GreyPrinceZotePreview;
36 |
37 | public bool WhisperingRootPreview;
38 | public bool DreamerPreview;
39 |
40 | public bool AbyssShriekPreview;
41 | public bool VoidHeartPreview;
42 |
43 | public bool GodtunerPreview;
44 | public bool LoreTabletPreview;
45 |
46 | public bool BasinFountainPreview;
47 | public bool NailmasterPreview;
48 |
49 | public bool StagPreview;
50 | public bool MapPreview;
51 |
52 | public bool DivinePreview;
53 |
54 | public CostItemHintSettings GeoShopPreview;
55 | public CostItemHintSettings GrubfatherPreview;
56 | public CostItemHintSettings SeerPreview;
57 | public CostItemHintSettings EggShopPreview;
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/MiscSettings.cs:
--------------------------------------------------------------------------------
1 | using MenuChanger.Attributes;
2 |
3 | namespace RandomizerMod.Settings
4 | {
5 | public class MiscSettings : SettingsModule
6 | {
7 | public bool RandomizeNotchCosts;
8 | [MenuRange(0, 240)][DynamicBound(nameof(MaxRandomNotchTotal), true)] public int MinRandomNotchTotal = 70;
9 | [MenuRange(0, 240)][DynamicBound(nameof(MinRandomNotchTotal), false)] public int MaxRandomNotchTotal = 110;
10 | public bool ExtraPlatforms;
11 | public SalubraNotchesSetting SalubraNotches;
12 | public MaskShardType MaskShards;
13 | public VesselFragmentType VesselFragments;
14 | public bool SteelSoul;
15 | public ToggleableFireballSetting FireballUpgrade;
16 |
17 | public enum MaskShardType
18 | {
19 | FourShardsPerMask,
20 | TwoShardsPerMask,
21 | OneShardPerMask
22 | }
23 |
24 | public enum VesselFragmentType
25 | {
26 | ThreeFragmentsPerVessel,
27 | TwoFragmentsPerVessel,
28 | OneFragmentPerVessel
29 | }
30 |
31 | public enum SalubraNotchesSetting
32 | {
33 | GroupedWithCharmNotchesPool,
34 | Vanilla,
35 | Randomized,
36 | AutoGivenAtCharmThreshold
37 | }
38 |
39 | public enum ToggleableFireballSetting
40 | {
41 | Normal,
42 | Deferred,
43 | Toggleable
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/NoveltySettings.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Settings
2 | {
3 | public class NoveltySettings : SettingsModule
4 | {
5 | public bool RandomizeSwim;
6 | public bool RandomizeElevatorPass;
7 | public bool RandomizeNail;
8 | public bool RandomizeFocus;
9 | public bool SplitClaw;
10 | public bool SplitCloak;
11 | public bool SplitSuperdash;
12 | public bool EggShop;
13 | }
14 | }
15 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/PoolSettings.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Settings
2 | {
3 | public class PoolSettings : SettingsModule
4 | {
5 | public bool Dreamers;
6 | public bool Skills;
7 | public bool Charms;
8 | public bool Keys;
9 | public bool MaskShards;
10 | public bool VesselFragments;
11 | public bool PaleOre;
12 | public bool CharmNotches;
13 | public bool GeoChests;
14 | public bool Relics;
15 | public bool RancidEggs;
16 | public bool Stags;
17 | public bool Maps;
18 | public bool WhisperingRoots;
19 | public bool Grubs;
20 | public bool LifebloodCocoons;
21 | public bool SoulTotems;
22 | public bool GrimmkinFlames;
23 | public bool GeoRocks;
24 | public bool BossEssence;
25 | public bool BossGeo;
26 | public bool LoreTablets;
27 |
28 | public bool JournalEntries;
29 | public bool JunkPitChests;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/Presets/CostPresetData.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Settings.Presets
2 | {
3 | public static class CostPresetData
4 | {
5 | public static CostSettings Standard;
6 | public static CostSettings More;
7 | public static CostSettings Less;
8 | public static CostSettings Expert;
9 | public static Dictionary CostPresets;
10 |
11 | static CostPresetData()
12 | {
13 | Standard = new CostSettings
14 | {
15 | GrubTolerance = 2,
16 | MinimumGrubCost = 1,
17 | MaximumGrubCost = 23,
18 | EssenceTolerance = 150,
19 | MinimumEssenceCost = 1,
20 | MaximumEssenceCost = 900,
21 | MinimumEggCost = 1,
22 | MaximumEggCost = 15,
23 | EggTolerance = 2,
24 | MinimumCharmCost = 1,
25 | MaximumCharmCost = 20,
26 | CharmTolerance = 2,
27 | };
28 | Less = new CostSettings
29 | {
30 | GrubTolerance = 2,
31 | MinimumGrubCost = 1,
32 | MaximumGrubCost = 15,
33 | EssenceTolerance = 150,
34 | MinimumEssenceCost = 1,
35 | MaximumEssenceCost = 600,
36 | MinimumEggCost = 1,
37 | MaximumEggCost = 10,
38 | EggTolerance = 2,
39 | MinimumCharmCost = 1,
40 | MaximumCharmCost = 10,
41 | CharmTolerance = 2,
42 | };
43 | More = new CostSettings
44 | {
45 | GrubTolerance = 4,
46 | MinimumGrubCost = 1,
47 | MaximumGrubCost = 42,
48 | EssenceTolerance = 200,
49 | MinimumEssenceCost = 1,
50 | MaximumEssenceCost = 1800,
51 | MinimumEggCost = 1,
52 | MaximumEggCost = 19,
53 | EggTolerance = 2,
54 | MinimumCharmCost = 1,
55 | MaximumCharmCost = 38,
56 | CharmTolerance = 2,
57 | };
58 | Expert = new CostSettings
59 | {
60 | GrubTolerance = 0,
61 | MinimumGrubCost = 5,
62 | MaximumGrubCost = 42,
63 | EssenceTolerance = 20,
64 | MinimumEssenceCost = 1,
65 | MaximumEssenceCost = 1800,
66 | MinimumEggCost = 5,
67 | MaximumEggCost = 15,
68 | EggTolerance = 0,
69 | MinimumCharmCost = 5,
70 | MaximumCharmCost = 40,
71 | CharmTolerance = 0,
72 | };
73 |
74 | CostPresets = new Dictionary
75 | {
76 | { "Standard", Standard },
77 | { "More", More },
78 | { "Less", Less },
79 | { "Expert", Expert },
80 | };
81 | }
82 | }
83 | }
84 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/Presets/CursePresetData.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Settings.Presets
2 | {
3 | public static class CursePresetData
4 | {
5 | public static CursedSettings None;
6 | public static CursedSettings Classic;
7 | public static CursedSettings Modern;
8 | public static CursedSettings UltraCursed;
9 |
10 | public static Dictionary CursedPresets;
11 |
12 | static CursePresetData()
13 | {
14 | None = new CursedSettings
15 | {
16 | ReplaceJunkWithOneGeo = false,
17 | RemoveSpellUpgrades = false,
18 | LongerProgressionChains = false,
19 | Deranged = false,
20 | CursedMasks = 0,
21 | CursedNotches = 0,
22 | RandomizeMimics = false,
23 | MaximumGrubsReplacedByMimics = 0,
24 | };
25 | Classic = new CursedSettings
26 | {
27 | ReplaceJunkWithOneGeo = true,
28 | RemoveSpellUpgrades = true,
29 | LongerProgressionChains = true,
30 | Deranged = false,
31 | CursedMasks = 0,
32 | CursedNotches = 0,
33 | RandomizeMimics = false,
34 | MaximumGrubsReplacedByMimics = 0,
35 | };
36 | Modern = new CursedSettings
37 | {
38 | ReplaceJunkWithOneGeo = false,
39 | RemoveSpellUpgrades = false,
40 | LongerProgressionChains = false,
41 | Deranged = true,
42 | CursedMasks = 4,
43 | CursedNotches = 2,
44 | RandomizeMimics = true,
45 | MaximumGrubsReplacedByMimics = 10,
46 | };
47 | UltraCursed = new CursedSettings
48 | {
49 | ReplaceJunkWithOneGeo = true,
50 | RemoveSpellUpgrades = true,
51 | LongerProgressionChains = true,
52 | Deranged = true,
53 | CursedMasks = 4,
54 | CursedNotches = 2,
55 | RandomizeMimics = true,
56 | MaximumGrubsReplacedByMimics = 10,
57 | };
58 |
59 | CursedPresets = new Dictionary
60 | {
61 | { "None", None },
62 | { "Classic", Classic },
63 | { "Modern", Modern },
64 | { "Ultra Cursed", UltraCursed },
65 | };
66 | }
67 | }
68 | }
69 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/Presets/DuplicateItemPresetData.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Settings.Presets
2 | {
3 | public static class DuplicateItemPresetData
4 | {
5 | public static DuplicateItemSettings DuplicateMajorItems;
6 | public static DuplicateItemSettings None;
7 | public static Dictionary Presets;
8 |
9 |
10 | static DuplicateItemPresetData()
11 | {
12 | DuplicateMajorItems = new()
13 | {
14 | MothwingCloak = true,
15 | MantisClaw = true,
16 | CrystalHeart = true,
17 | MonarchWings = true,
18 | ShadeCloak = true,
19 | DreamNail = true,
20 | VoidHeart = true,
21 | Dreamer = true,
22 | SwimmingItems = true,
23 | LevelOneSpells = true,
24 | LevelTwoSpells = false,
25 | Grimmchild = false,
26 | NailArts = false,
27 | CursedNailItems = false,
28 | DuplicateUniqueKeys = false,
29 | SimpleKeyHandling = DuplicateItemSettings.SimpleKeySetting.TwoExtraKeysInLogic,
30 | SplitClawHandling = DuplicateItemSettings.SplitItemSetting.NoDupe,
31 | SplitCloakHandling = DuplicateItemSettings.SplitItemSetting.DupeBoth,
32 | };
33 | None = new()
34 | {
35 | MothwingCloak = false,
36 | MantisClaw = false,
37 | CrystalHeart = false,
38 | MonarchWings = false,
39 | ShadeCloak = false,
40 | DreamNail = false,
41 | VoidHeart = false,
42 | Dreamer = false,
43 | SwimmingItems = false,
44 | LevelOneSpells = false,
45 | LevelTwoSpells = false,
46 | Grimmchild = false,
47 | NailArts = false,
48 | CursedNailItems = false,
49 | DuplicateUniqueKeys = false,
50 | SimpleKeyHandling = DuplicateItemSettings.SimpleKeySetting.NoDupe,
51 | SplitClawHandling = DuplicateItemSettings.SplitItemSetting.NoDupe,
52 | SplitCloakHandling = DuplicateItemSettings.SplitItemSetting.NoDupe,
53 | };
54 |
55 | Presets = new()
56 | {
57 | { "Duplicate Major Items", DuplicateMajorItems },
58 | { "None", None },
59 | };
60 | }
61 |
62 | }
63 | }
64 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/Presets/MiscPresetData.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Settings.Presets
2 | {
3 | public static class MiscPresetData
4 | {
5 | public static MiscSettings Standard;
6 | public static MiscSettings Classic;
7 | public static MiscSettings ConsolidatedItems;
8 | public static Dictionary MiscPresets;
9 |
10 | static MiscPresetData()
11 | {
12 | Standard = new MiscSettings
13 | {
14 | MaskShards = MiscSettings.MaskShardType.FourShardsPerMask,
15 | VesselFragments = MiscSettings.VesselFragmentType.ThreeFragmentsPerVessel,
16 | RandomizeNotchCosts = true,
17 | MinRandomNotchTotal = 70,
18 | MaxRandomNotchTotal = 110,
19 | ExtraPlatforms = true,
20 | SalubraNotches = MiscSettings.SalubraNotchesSetting.GroupedWithCharmNotchesPool,
21 | SteelSoul = false,
22 | FireballUpgrade = MiscSettings.ToggleableFireballSetting.Normal,
23 | };
24 |
25 | Classic = new MiscSettings
26 | {
27 | MaskShards = MiscSettings.MaskShardType.FourShardsPerMask,
28 | VesselFragments = MiscSettings.VesselFragmentType.ThreeFragmentsPerVessel,
29 | RandomizeNotchCosts = false,
30 | MinRandomNotchTotal = 70,
31 | MaxRandomNotchTotal = 110,
32 | ExtraPlatforms = true,
33 | SalubraNotches = MiscSettings.SalubraNotchesSetting.AutoGivenAtCharmThreshold,
34 | SteelSoul = false,
35 | FireballUpgrade = MiscSettings.ToggleableFireballSetting.Normal,
36 | };
37 |
38 | ConsolidatedItems = new MiscSettings
39 | {
40 | MaskShards = MiscSettings.MaskShardType.OneShardPerMask,
41 | VesselFragments = MiscSettings.VesselFragmentType.OneFragmentPerVessel,
42 | RandomizeNotchCosts = true,
43 | MinRandomNotchTotal = 70,
44 | MaxRandomNotchTotal = 110,
45 | ExtraPlatforms = true,
46 | SalubraNotches = MiscSettings.SalubraNotchesSetting.GroupedWithCharmNotchesPool,
47 | SteelSoul = false,
48 | FireballUpgrade = MiscSettings.ToggleableFireballSetting.Normal,
49 | };
50 |
51 | MiscPresets = new Dictionary
52 | {
53 | { "Standard", Standard },
54 | { "Classic", Classic },
55 | { "Consolidated Items", ConsolidatedItems },
56 | };
57 | }
58 | }
59 | }
60 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/Presets/NoveltyPresetData.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Settings.Presets
2 | {
3 | public static class NoveltyPresetData
4 | {
5 | public static Dictionary NoveltyPresets;
6 | public static NoveltySettings None;
7 | public static NoveltySettings Basic;
8 | public static NoveltySettings Clawful;
9 | public static NoveltySettings SplitStuff;
10 | public static NoveltySettings Everything;
11 |
12 |
13 | static NoveltyPresetData()
14 | {
15 | None = new()
16 | {
17 | RandomizeSwim = false,
18 | RandomizeElevatorPass = false,
19 | RandomizeNail = false,
20 | RandomizeFocus = false,
21 | SplitClaw = false,
22 | SplitCloak = false,
23 | SplitSuperdash = false,
24 | EggShop = false,
25 | };
26 |
27 | Basic = new()
28 | {
29 | RandomizeSwim = true,
30 | RandomizeElevatorPass = true,
31 | RandomizeNail = false,
32 | RandomizeFocus = false,
33 | SplitClaw = false,
34 | SplitCloak = false,
35 | SplitSuperdash = false,
36 | EggShop = true,
37 | };
38 |
39 | Clawful = new()
40 | {
41 | RandomizeSwim = true,
42 | RandomizeElevatorPass = true,
43 | RandomizeNail = false,
44 | RandomizeFocus = false,
45 | SplitClaw = true,
46 | SplitCloak = false,
47 | EggShop = true,
48 | };
49 |
50 | SplitStuff = new()
51 | {
52 | RandomizeSwim = true,
53 | RandomizeElevatorPass = true,
54 | RandomizeNail = false,
55 | RandomizeFocus = false,
56 | SplitClaw = true,
57 | SplitCloak = true,
58 | SplitSuperdash = true,
59 | EggShop = true,
60 | };
61 |
62 | Everything = new()
63 | {
64 | RandomizeSwim = true,
65 | RandomizeElevatorPass = true,
66 | RandomizeNail = true,
67 | RandomizeFocus = true,
68 | SplitClaw = true,
69 | SplitCloak = true,
70 | SplitSuperdash = true,
71 | EggShop = true,
72 | };
73 |
74 | NoveltyPresets = new()
75 | {
76 | { "Basic", Basic },
77 | { "Clawful", Clawful },
78 | { "Split Stuff", SplitStuff },
79 | { "Everything", Everything },
80 | { "None", None },
81 | };
82 | }
83 | }
84 | }
85 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/Presets/ProgressionDepthPresetData.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Settings.Presets
2 | {
3 | public static class ProgressionDepthPresetData
4 | {
5 | public static ProgressionDepthSettings Default;
6 | public static ProgressionDepthSettings Relaxed;
7 | public static ProgressionDepthSettings Unweighted;
8 | public static ProgressionDepthSettings DelayedWeight;
9 | public static Dictionary Presets;
10 |
11 | static ProgressionDepthPresetData()
12 | {
13 | Default = new();
14 | Relaxed = new()
15 | {
16 | LocationPriorityTransformCoefficient = 2.5f,
17 | LocationPriorityTransformType = RandomizerCore.Randomization.PriorityTransformUtil.TransformType.SquareRoot
18 | };
19 | Unweighted = new()
20 | {
21 | ItemLocationPriorityInteraction = RandomizerCore.Randomization.PriorityTransformUtil.ItemPriorityDepthEffect.Ignore,
22 | LocationPriorityTransformCoefficient = 0f,
23 | LocationPriorityTransformType = RandomizerCore.Randomization.PriorityTransformUtil.TransformType.Linear,
24 | DuplicateItemPenalty = false,
25 | MultiLocationPenalty = false,
26 | TransitionPriorityTransformCoefficient = 0f,
27 | TransitionTransitionPriorityInteraction = RandomizerCore.Randomization.PriorityTransformUtil.ItemPriorityDepthEffect.Ignore,
28 | TransitionPriorityTransformType = RandomizerCore.Randomization.PriorityTransformUtil.TransformType.Linear,
29 | };
30 | DelayedWeight = new()
31 | {
32 | ItemLocationPriorityInteraction = RandomizerCore.Randomization.PriorityTransformUtil.ItemPriorityDepthEffect.Fade,
33 | LocationPriorityTransformCoefficient = 0.1f,
34 | LocationPriorityTransformType = RandomizerCore.Randomization.PriorityTransformUtil.TransformType.Quadratic,
35 | };
36 |
37 | Presets = new()
38 | {
39 | { "Default", Default },
40 | { "Relaxed", Relaxed },
41 | { "Unweighted", Unweighted },
42 | { "Delayed Weight", DelayedWeight},
43 | };
44 | }
45 | }
46 | }
47 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/Presets/StartItemPresetData.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Settings.Presets
2 | {
3 | public static class StartItemPresetData
4 | {
5 | public static StartItemSettings EarlyGeo;
6 | public static StartItemSettings GeoAndStartItems;
7 | public static StartItemSettings None;
8 |
9 | public static Dictionary StartItemPresets;
10 |
11 |
12 |
13 |
14 | static StartItemPresetData()
15 | {
16 | EarlyGeo = new StartItemSettings
17 | {
18 | MinimumStartGeo = 300,
19 | MaximumStartGeo = 600,
20 | };
21 |
22 | GeoAndStartItems = new StartItemSettings
23 | {
24 | MinimumStartGeo = 300,
25 | MaximumStartGeo = 600,
26 | Stags = StartItemSettings.StartStagType.ZeroOrMoreRandomStags,
27 | Charms = StartItemSettings.StartCharmType.ZeroOrMore,
28 | HorizontalMovement = StartItemSettings.StartHorizontalType.ZeroOrMore,
29 | VerticalMovement = StartItemSettings.StartVerticalType.ZeroOrMore,
30 | MiscItems = StartItemSettings.StartMiscItems.ZeroOrMore,
31 | };
32 |
33 | None = new StartItemSettings
34 | {
35 | MinimumStartGeo = 0,
36 | MaximumStartGeo = 0,
37 | };
38 |
39 | StartItemPresets = new Dictionary
40 | {
41 | { "Early Geo", EarlyGeo },
42 | { "Geo and Start Items", GeoAndStartItems },
43 | { "None", None },
44 | };
45 | }
46 | }
47 | }
48 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/Presets/StartLocationPresetData.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Settings.Presets
2 | {
3 | public static class StartLocationPresetData
4 | {
5 | public static StartLocationSettings KingsPass;
6 | public static StartLocationSettings RandomNoKP;
7 | public static StartLocationSettings RandomWithKP;
8 |
9 | public static Dictionary StartLocationPresets;
10 |
11 | static StartLocationPresetData()
12 | {
13 | KingsPass = new StartLocationSettings
14 | {
15 | StartLocationType = StartLocationSettings.RandomizeStartLocationType.Fixed,
16 | StartLocation = "King's Pass",
17 | };
18 |
19 | RandomNoKP = new StartLocationSettings
20 | {
21 | StartLocationType = StartLocationSettings.RandomizeStartLocationType.RandomExcludingKP,
22 | StartLocation = null,
23 | };
24 |
25 | RandomWithKP = new StartLocationSettings
26 | {
27 | StartLocationType = StartLocationSettings.RandomizeStartLocationType.Random,
28 | StartLocation = null,
29 | };
30 |
31 | StartLocationPresets = new Dictionary
32 | {
33 | { "King's Pass", KingsPass },
34 | { "Random (no King's Pass)", RandomNoKP },
35 | { "Random (allow King's Pass)", RandomWithKP },
36 | };
37 | }
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/Presets/TransitionPresetData.cs:
--------------------------------------------------------------------------------
1 | using static RandomizerMod.Settings.TransitionSettings;
2 |
3 | namespace RandomizerMod.Settings.Presets
4 | {
5 | public static class TransitionPresetData
6 | {
7 | public static TransitionSettings None;
8 | public static TransitionSettings MapArea;
9 | public static TransitionSettings Area;
10 | public static TransitionSettings Room;
11 | public static TransitionSettings ConnectedAreaRoom;
12 | public static TransitionSettings Chaos;
13 | public static Dictionary TransitionPresets;
14 |
15 | static TransitionPresetData()
16 | {
17 | None = new TransitionSettings
18 | {
19 | Mode = TransitionMode.None,
20 | AreaConstraint = AreaConstraintSetting.None,
21 | TransitionMatching = TransitionMatchingSetting.MatchingDirections,
22 | Coupled = true,
23 | };
24 |
25 | MapArea = new TransitionSettings
26 | {
27 | Mode = TransitionMode.MapAreaRandomizer,
28 | AreaConstraint = AreaConstraintSetting.None,
29 | TransitionMatching = TransitionMatchingSetting.MatchingDirections,
30 | Coupled = true,
31 | };
32 |
33 | Area = new TransitionSettings
34 | {
35 | Mode = TransitionMode.FullAreaRandomizer,
36 | AreaConstraint = AreaConstraintSetting.None,
37 | TransitionMatching = TransitionMatchingSetting.MatchingDirections,
38 | Coupled = true,
39 | };
40 |
41 | ConnectedAreaRoom = new TransitionSettings
42 | {
43 | Mode = TransitionMode.RoomRandomizer,
44 | AreaConstraint = AreaConstraintSetting.MoreConnectedMapAreas,
45 | TransitionMatching = TransitionMatchingSetting.MatchingDirections,
46 | Coupled = true,
47 | };
48 |
49 | Room = new TransitionSettings
50 | {
51 | Mode = TransitionMode.RoomRandomizer,
52 | AreaConstraint = AreaConstraintSetting.None,
53 | TransitionMatching = TransitionMatchingSetting.MatchingDirections,
54 | Coupled = true,
55 | };
56 |
57 | Chaos = new TransitionSettings
58 | {
59 | Mode = TransitionMode.RoomRandomizer,
60 | AreaConstraint = AreaConstraintSetting.None,
61 | TransitionMatching = TransitionMatchingSetting.NonmatchingDirections,
62 | Coupled = false,
63 | };
64 |
65 | TransitionPresets = new Dictionary
66 | {
67 | { "None", None },
68 | { "Map Area Rando", MapArea },
69 | { "Full Area Rando", Area },
70 | { "Connected-Area Room Rando", ConnectedAreaRoom },
71 | { "Room Rando", Room },
72 | { "Chaos Room Rando", Chaos },
73 | };
74 | }
75 | }
76 | }
77 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/ProgressionDepthSettings.cs:
--------------------------------------------------------------------------------
1 | using RandomizerCore.Randomization;
2 | using static RandomizerCore.Randomization.PriorityTransformUtil;
3 |
4 | namespace RandomizerMod.Settings
5 | {
6 | public class ProgressionDepthSettings : SettingsModule
7 | {
8 | public bool MultiLocationPenalty = true;
9 | public bool DuplicateItemPenalty = true;
10 |
11 | public TransformType LocationPriorityTransformType = TransformType.Linear;
12 | public ItemPriorityDepthEffect ItemLocationPriorityInteraction = ItemPriorityDepthEffect.Cliff;
13 | public float LocationPriorityTransformCoefficient = 3f;
14 |
15 | public TransformType TransitionPriorityTransformType = TransformType.SquareRoot;
16 | public ItemPriorityDepthEffect TransitionTransitionPriorityInteraction = ItemPriorityDepthEffect.Cliff;
17 | public float TransitionPriorityTransformCoefficient = 1f;
18 |
19 | public DefaultGroupPlacementStrategy GetItemPlacementStrategy()
20 | {
21 | return new(CreateTransform(LocationPriorityTransformCoefficient, LocationPriorityTransformType, ItemLocationPriorityInteraction));
22 | }
23 |
24 | public DefaultGroupPlacementStrategy GetTransitionPlacementStrategy()
25 | {
26 | return new(CreateTransform(TransitionPriorityTransformCoefficient, TransitionPriorityTransformType, TransitionTransitionPriorityInteraction));
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/RandomizerSettings.cs:
--------------------------------------------------------------------------------
1 | using Newtonsoft.Json;
2 | using RandomizerCore.Json;
3 | using RandomizerMod.RC;
4 |
5 | namespace RandomizerMod.Settings
6 | {
7 | public class RandomizerSettings
8 | {
9 | public GenerationSettings GenerationSettings;
10 | public int ProfileID = -1; // LocalSettings load before GameManager.instance.profileId or PlayerData.instance.profileId are loaded.
11 | public TrackerData TrackerData;
12 | public TrackerData TrackerDataWithoutSequenceBreaks;
13 | [JsonIgnore]
14 | public RandoModContext Context;
15 |
16 | public void Setup()
17 | {
18 | if (GenerationSettings != null && Context == null && ProfileID >= 0)
19 | {
20 | string rawSpoilerPath = Path.Combine(Logging.LogManager.UserDirectory, "RawSpoiler.json");
21 | if (!File.Exists(rawSpoilerPath))
22 | {
23 | LogError($"No file found at {rawSpoilerPath}!");
24 | return;
25 | }
26 |
27 | using FileStream fs = File.OpenRead(rawSpoilerPath);
28 | using StreamReader sr = new(fs);
29 | using JsonTextReader jtr = new(sr);
30 | try
31 | {
32 | Context = JsonUtil.DeserializeFromReader(jtr);
33 | }
34 | catch (Exception e)
35 | {
36 | LogError($"Error deserializing raw spoiler from {rawSpoilerPath}\n:{e}");
37 | }
38 | try
39 | {
40 | TrackerData?.Setup(GenerationSettings, Context);
41 | TrackerDataWithoutSequenceBreaks?.Setup(GenerationSettings, Context);
42 | }
43 | catch (Exception e)
44 | {
45 | LogError($"Error setting up tracker data:\n{e}");
46 | }
47 |
48 | Logging.LogManager.UpdateRecent(ProfileID);
49 | }
50 | }
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/SettingsModule.cs:
--------------------------------------------------------------------------------
1 | using MenuChanger.Attributes;
2 | using System.Reflection;
3 |
4 | namespace RandomizerMod.Settings
5 | {
6 | public abstract class SettingsModule : ICloneable
7 | {
8 | ///
9 | /// Randomize the fields of the module.
10 | ///
11 | public virtual void Randomize(Random rng)
12 | {
13 | foreach (FieldInfo f in Util.GetOrderedFields(GetType()))
14 | {
15 | Type T = f.FieldType;
16 |
17 | if (T == typeof(bool))
18 | {
19 | f.SetValue(this, rng.Next(2) == 0);
20 | }
21 | else if (T == typeof(int) || T.IsEnum && Enum.GetUnderlyingType(T) == typeof(int))
22 | {
23 | int maxValue = int.MaxValue - 1;
24 | int minValue = int.MinValue;
25 |
26 | if (f.GetCustomAttribute() is MenuRangeAttribute range)
27 | {
28 | maxValue = (int)range.max;
29 | minValue = (int)range.min;
30 | }
31 | else
32 | {
33 | if (f.GetCustomAttribute() is MaxValueAttribute max) maxValue = max.Value;
34 | else if (T.IsEnum)
35 | {
36 | maxValue = Enum.GetValues(T).Cast().Max();
37 | }
38 |
39 | if (f.GetCustomAttribute() is MinValueAttribute min) minValue = min.Value;
40 | else if (T.IsEnum)
41 | {
42 | minValue = Enum.GetValues(T).Cast().Min();
43 | }
44 | }
45 |
46 | f.SetValue(this, rng.Next(minValue, maxValue + 1));
47 | }
48 | }
49 | }
50 |
51 | ///
52 | /// Fix all compatibility or range issues with current settings.
53 | ///
54 | public virtual void Clamp(GenerationSettings gs)
55 | {
56 |
57 | }
58 |
59 | public virtual void CopyTo(SettingsModule target)
60 | {
61 | Type T = GetType();
62 | if (target.GetType() != T) throw new ArgumentException(nameof(target));
63 | foreach (FieldInfo f in Util.GetOrderedFields(T))
64 | {
65 | f.SetValue(target, f.GetValue(this));
66 | }
67 | }
68 |
69 | public virtual object Clone()
70 | {
71 | return MemberwiseClone();
72 | }
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/SkipSettings.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Settings
2 | {
3 | public class SkipSettings : SettingsModule
4 | {
5 | public bool PreciseMovement;
6 | public bool ProficientCombat;
7 | public bool BackgroundObjectPogos;
8 | public bool EnemyPogos;
9 | public bool ObscureSkips;
10 | public bool ShadeSkips;
11 | public bool InfectionSkips;
12 | public bool FireballSkips;
13 | public bool SpikeTunnels;
14 | public bool AcidSkips;
15 | public bool DamageBoosts;
16 | public bool DangerousSkips;
17 | public bool DarkRooms;
18 | public bool Slopeballs;
19 | public bool ShriekPogos;
20 | public bool ComplexSkips;
21 | public bool DifficultSkips;
22 | public override void Clamp(GenerationSettings gs)
23 | {
24 | base.Clamp(gs);
25 | if (gs.MiscSettings.FireballUpgrade != MiscSettings.ToggleableFireballSetting.Toggleable) Slopeballs = false;
26 | if (gs.MiscSettings.SteelSoul) ShadeSkips = false;
27 | }
28 | }
29 | }
30 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/SplitGroupSettings.cs:
--------------------------------------------------------------------------------
1 | using MenuChanger.Attributes;
2 | using RandomizerMod.RandomizerData;
3 | using System.Reflection;
4 |
5 | namespace RandomizerMod.Settings
6 | {
7 | public class SplitGroupSettings : SettingsModule
8 | {
9 | public bool RandomizeOnStart;
10 |
11 | [MenuRange(-1, 99)]
12 | public int Dreamers;
13 | [MenuRange(-1, 99)]
14 | public int Skills;
15 | [MenuRange(-1, 99)]
16 | public int Charms;
17 | [MenuRange(-1, 99)]
18 | public int Keys;
19 | [MenuRange(-1, 99)]
20 | public int MaskShards;
21 | [MenuRange(-1, 99)]
22 | public int VesselFragments;
23 | [MenuRange(-1, 99)]
24 | public int CharmNotches;
25 | [MenuRange(-1, 99)]
26 | public int PaleOre;
27 | [MenuRange(-1, 99)]
28 | public int GeoChests;
29 | [MenuRange(-1, 99)]
30 | public int RancidEggs;
31 | [MenuRange(-1, 99)]
32 | public int Relics;
33 | [MenuRange(-1, 99)]
34 | public int WhisperingRoots;
35 | [MenuRange(-1, 99)]
36 | public int BossEssence;
37 | [MenuRange(-1, 99)]
38 | public int Grubs;
39 | [MenuRange(-1, 99)]
40 | public int Mimics;
41 | [MenuRange(-1, 99)]
42 | public int Maps;
43 | [MenuRange(-1, 99)]
44 | public int Stags;
45 | [MenuRange(-1, 99)]
46 | public int LifebloodCocoons;
47 | [MenuRange(-1, 99)]
48 | public int GrimmkinFlames;
49 | [MenuRange(-1, 99)]
50 | public int JournalEntries;
51 | [MenuRange(-1, 99)]
52 | public int GeoRocks;
53 | [MenuRange(-1, 99)]
54 | public int BossGeo;
55 | [MenuRange(-1, 99)]
56 | public int SoulTotems;
57 | [MenuRange(-1, 99)]
58 | public int LoreTablets;
59 |
60 | public override void Randomize(Random rng)
61 | {
62 | foreach (FieldInfo fi in IntFields.Values)
63 | {
64 | int e = (int)fi.GetValue(this);
65 | if (e < 0 || e > 2) continue;
66 | fi.SetValue(this, rng.Next(3));
67 | }
68 | }
69 |
70 | public static readonly Dictionary IntFields = typeof(SplitGroupSettings)
71 | .GetFields(BindingFlags.Instance | BindingFlags.Public)
72 | .Where(fi => fi.FieldType == typeof(int))
73 | .ToDictionary(fi => fi.Name);
74 |
75 | public bool TryGetValue(PoolDef def, out int value)
76 | {
77 | if (def.Group != null && IntFields.TryGetValue(def.Group, out FieldInfo fi))
78 | {
79 | int i = (int)fi.GetValue(this);
80 | if (i >= 0)
81 | {
82 | value = i;
83 | return true;
84 | }
85 | }
86 | value = -1;
87 | return false;
88 | }
89 |
90 | }
91 | }
92 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/StartItemSettings.cs:
--------------------------------------------------------------------------------
1 | using MenuChanger.Attributes;
2 |
3 | namespace RandomizerMod.Settings
4 | {
5 | [Serializable]
6 | public class StartItemSettings : SettingsModule
7 | {
8 | [DynamicBound(nameof(MaximumStartGeo), true)]
9 | [MenuRange(0, 50000)]
10 | public int MinimumStartGeo;
11 |
12 | [DynamicBound(nameof(MinimumStartGeo), false)]
13 | [MenuRange(0, 50000)]
14 | public int MaximumStartGeo;
15 |
16 | public enum StartVerticalType
17 | {
18 | None,
19 | ZeroOrMore,
20 | OneRandomItem,
21 | MantisClaw,
22 | MonarchWings,
23 | All,
24 | }
25 | public StartVerticalType VerticalMovement;
26 |
27 | public enum StartHorizontalType
28 | {
29 | None,
30 | ZeroOrMore,
31 | OneRandomItem,
32 | MothwingCloak,
33 | CrystalHeart,
34 | All
35 | }
36 | public StartHorizontalType HorizontalMovement;
37 |
38 | public enum StartCharmType
39 | {
40 | None,
41 | ZeroOrMore,
42 | OneRandomItem,
43 | }
44 | public StartCharmType Charms;
45 |
46 | public enum StartStagType
47 | {
48 | None,
49 | DirtmouthStag,
50 | ZeroOrMoreRandomStags,
51 | OneRandomStag,
52 | ManyRandomStags,
53 | AllStags
54 | }
55 | public StartStagType Stags;
56 |
57 | public enum StartMiscItems
58 | {
59 | None,
60 | ZeroOrMore,
61 | Many,
62 | DreamNail,
63 | DreamNailAndMore,
64 | }
65 | public StartMiscItems MiscItems;
66 | }
67 | }
68 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/StartLocationSettings.cs:
--------------------------------------------------------------------------------
1 | using RandomizerMod.RandomizerData;
2 |
3 | namespace RandomizerMod.Settings
4 | {
5 | [Serializable]
6 | public class StartLocationSettings : SettingsModule
7 | {
8 | public enum RandomizeStartLocationType
9 | {
10 | Fixed,
11 | RandomExcludingKP,
12 | Random,
13 | }
14 |
15 | public RandomizeStartLocationType StartLocationType;
16 |
17 | public string StartLocation;
18 |
19 | public override void Clamp(GenerationSettings gs)
20 | {
21 | base.Clamp(gs);
22 | if (StartLocationType == RandomizeStartLocationType.Fixed && StartLocation == null)
23 | {
24 | LogWarn("Found null fixed start location during Clamp.");
25 | StartLocation = Data.GetStartNames().First();
26 | }
27 | }
28 |
29 | public void SetStartLocation(string start) => StartLocation = start;
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/RandomizerMod/Settings/TransitionSettings.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerMod.Settings
2 | {
3 | [Serializable]
4 | public class TransitionSettings : SettingsModule
5 | {
6 | public enum TransitionMode
7 | {
8 | None,
9 | MapAreaRandomizer,
10 | FullAreaRandomizer,
11 | RoomRandomizer,
12 | }
13 | public TransitionMode Mode;
14 |
15 | public enum AreaConstraintSetting
16 | {
17 | None,
18 | MoreConnectedMapAreas,
19 | MoreConnectedTitledAreas,
20 | }
21 |
22 | public AreaConstraintSetting AreaConstraint;
23 |
24 | /*
25 | // This will likely be difficult to implement -- not many rooms which don't have items or npcs or events
26 | // and then even fewer combinations which give matching transition counts
27 | public enum RemoveRoomsSetting
28 | {
29 | None,
30 | RemoveEmptyHallways,
31 | AggressivelyRemoveRooms,
32 | }
33 | public RemoveRoomsSetting Remove;
34 | */
35 |
36 | public enum TransitionMatchingSetting
37 | {
38 | MatchingDirections,
39 | MatchingDirectionsAndNoDoorToDoor,
40 | NonmatchingDirections
41 | }
42 | public TransitionMatchingSetting TransitionMatching;
43 | public bool Coupled = true;
44 | }
45 | }
46 |
--------------------------------------------------------------------------------
/RandomizerModTests/Extensions.cs:
--------------------------------------------------------------------------------
1 | namespace RandomizerModTests
2 | {
3 | public static class Extensions
4 | {
5 | public static IEnumerable