Skip to content

Commit cd2d425

Browse files
Main Menu level sets (v1.1.4.0)
New Options: * Main Menu playlists (and level sets) can be selected as the main menu (from the Playlist/Level Set Options menu). Levels are randomly chosen based on the setting below. * Decide Main Menu on Startup - Level set main menus will only choose a random level during each startup. Otherwise, random levels will be decided every time the main menu loads. Fixes: * The Playlist Options button had some faulty logic that prevented it from showing up in the Choose Main Menu level grid. * Mod state settings are now properly changed after renaming/deleting a playlist file. Refactors: * Apparently LevelSetOptionsMenu wasn't modular enough. So pain has been added to the equation (Controls are initialized once, but after that you can't pick and choose controls. You need a separate menu for each setup.)
1 parent 9f215d3 commit cd2d425

File tree

15 files changed

+405
-36
lines changed

15 files changed

+405
-36
lines changed

Distance.LevelSelectAdditions/ConfigurationLogic.cs

Lines changed: 94 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
using Distance.LevelSelectAdditions.Events;
2+
using Distance.LevelSelectAdditions.Extensions;
23
using Distance.LevelSelectAdditions.Sorting;
34
using Reactor.API.Configuration;
45
using System;
@@ -164,6 +165,28 @@ public bool FixLevelSelectScrollBug
164165
set => Set(State_LastLevelSets_ID, value);
165166
}
166167

168+
169+
private const string ProfileMainMenuLevelSets_ID = "profile.mainmenu_levelsets";
170+
public Dictionary<string, string> ProfileMainMenuLevelSets
171+
{
172+
get => Convert(ProfileMainMenuLevelSets_ID, new Dictionary<string, string>(), overwriteNull: true);
173+
set => Set(ProfileMainMenuLevelSets_ID, value);
174+
}
175+
176+
private const string State_LastProfileMainMenuLevels_ID = "state.last_mainmenu_levels";
177+
public Dictionary<string, string> State_LastProfileMainMenuLevels
178+
{
179+
get => Convert(State_LastProfileMainMenuLevels_ID, new Dictionary<string, string>(), overwriteNull: true);
180+
set => Set(State_LastProfileMainMenuLevels_ID, value);
181+
}
182+
183+
private const string RandomStartupMainMenu_ID = "profile.random_startup_mainmenu";
184+
public bool RandomStartupMainMenu
185+
{
186+
get => Get<bool>(RandomStartupMainMenu_ID);
187+
set => Set(RandomStartupMainMenu_ID, value);
188+
}
189+
167190
#endregion
168191

169192
#region Helpers
@@ -192,23 +215,65 @@ public string GetStateLastLevelSetID(LevelSelectMenuAbstract.DisplayType display
192215
{
193216
if (State_LastLevelSetIDs.TryGetValue(displayType, out var lastPlaylists_mode))
194217
{
195-
if (lastPlaylists_mode.TryGetValue(modeID, out string pathName))
218+
if (lastPlaylists_mode.TryGetValue(modeID, out string levelSetID))
196219
{
197-
return pathName;
220+
return levelSetID;
198221
}
199222
}
200223
return null;
201224
}
202225

203-
public void SetStateLastLevelSetID(LevelSelectMenuAbstract.DisplayType displayType, GameModeID modeID, string pathName)
226+
public void SetStateLastLevelSetID(LevelSelectMenuAbstract.DisplayType displayType, GameModeID modeID, string levelSetID)
204227
{
205228
var lastPlaylists_display = State_LastLevelSetIDs;
206229
if (!lastPlaylists_display.TryGetValue(displayType, out Dictionary<GameModeID, string> lastPlaylists_mode))
207230
{
208231
lastPlaylists_mode = new Dictionary<GameModeID, string>();
209232
lastPlaylists_display[displayType] = lastPlaylists_mode;
210233
}
211-
lastPlaylists_mode[modeID] = pathName;
234+
lastPlaylists_mode[modeID] = levelSetID;
235+
this.Save(); // auto save
236+
}
237+
238+
public string GetProfileMainMenuRelativePathID(string profileName)
239+
{
240+
if (this.ProfileMainMenuLevelSets.TryGetValue(profileName, out string relativePathID))
241+
{
242+
return relativePathID;
243+
}
244+
return null;
245+
}
246+
247+
// Returns true if the level set was changed.
248+
public bool SetProfileMainMenuRelativePathID(string profileName, string relativePathID)
249+
{
250+
var profileMainMenuLevelSets = this.ProfileMainMenuLevelSets;
251+
if (profileMainMenuLevelSets.TryGetValue(profileName, out string oldRelativePathID))
252+
{
253+
if (relativePathID == oldRelativePathID)
254+
{
255+
return false;
256+
}
257+
}
258+
// Clear state for last level used.
259+
this.SetStateLastMainMenuLevelRelativePath(profileName, null);
260+
profileMainMenuLevelSets[profileName] = relativePathID;
261+
this.Save(); // auto save
262+
return true;
263+
}
264+
265+
public string GetStateLastMainMenuLevelRelativePath(string profileName)
266+
{
267+
if (this.State_LastProfileMainMenuLevels.TryGetValue(profileName, out string relativeLevelPath))
268+
{
269+
return relativeLevelPath;
270+
}
271+
return null;
272+
}
273+
274+
public void SetStateLastMainMenuLevelRelativePath(string profileName, string relativeLevelPath)
275+
{
276+
this.State_LastProfileMainMenuLevels[profileName] = relativeLevelPath;
212277
this.Save(); // auto save
213278
}
214279

@@ -238,7 +303,19 @@ private void OnPlaylistFileRenamed(PlaylistFileRenamed.Data data)
238303
}
239304
}
240305

306+
var profileMainMenuLevelSets = this.ProfileMainMenuLevelSets;
307+
// Use ToArray to enumerate with foreach and allow updating values.
308+
foreach (var profilePair in profileMainMenuLevelSets.ToArray())
309+
{
310+
if (string.Equals(profilePair.Value, data.oldLevelSetID, StringComparison.InvariantCultureIgnoreCase))
311+
{
312+
profileMainMenuLevelSets[profilePair.Key] = data.playlist.GetRelativePathID();
313+
}
314+
}
315+
241316
//TODO: When LevelSetOptions dictionary gets added, enumerate over and rename here too.
317+
318+
this.Save(); // auto save
242319
}
243320

244321
private void OnPlaylistFileDeleted(PlaylistFileDeleted.Data data)
@@ -256,7 +333,19 @@ private void OnPlaylistFileDeleted(PlaylistFileDeleted.Data data)
256333
}
257334
}
258335

336+
var profileMainMenuLevelSets = this.ProfileMainMenuLevelSets;
337+
// Use ToArray to enumerate with foreach and allow updating values.
338+
foreach (var profilePair in profileMainMenuLevelSets.ToArray())
339+
{
340+
if (string.Equals(profilePair.Value, data.levelSetID, StringComparison.InvariantCultureIgnoreCase))
341+
{
342+
profileMainMenuLevelSets[profilePair.Key] = null;
343+
}
344+
}
345+
259346
//TODO: When LevelSetOptions dictionary gets added, enumerate over and delete here too.
347+
348+
this.Save(); // auto save
260349
}
261350

262351
public void Awake()
@@ -283,6 +372,7 @@ public void Awake()
283372
Get(EnableRateWorkshopLevelButton_ID, true);
284373
Get(HideChooseMainMenuUnusedButtons_ID, true);
285374
//Get(FixLevelSelectScrollBug_ID, true); // Always enable this fix
375+
Get(RandomStartupMainMenu_ID, false);
286376

287377
// Save settings, and any defaults that may have been added.
288378
Save();

Distance.LevelSelectAdditions/Distance.LevelSelectAdditions.csproj

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,7 @@
9999
<Compile Include="Harmony\Assembly-CSharp\LevelGridMenu\CreateAndAddLevelSet.cs" />
100100
<Compile Include="Harmony\Assembly-CSharp\LevelGridMenu\CreateEntries.cs" />
101101
<Compile Include="Harmony\Assembly-CSharp\LevelGridMenu\Display.cs" />
102+
<Compile Include="Harmony\Assembly-CSharp\LevelGridMenu\OnGridCellClicked.cs" />
102103
<Compile Include="Harmony\Assembly-CSharp\LevelGridMenu\OnLevelEntrySelected.cs" />
103104
<Compile Include="Harmony\Assembly-CSharp\LevelGridMenu\PlaylistEntry\ctor.cs" />
104105
<Compile Include="Harmony\Assembly-CSharp\LevelGridMenu\PlaylistEntry\get_Color_.cs" />
@@ -117,7 +118,9 @@
117118
<Compile Include="Harmony\Assembly-CSharp\LevelSelectMenuLogic\Start.cs" />
118119
<Compile Include="Harmony\Assembly-CSharp\LevelSelectMenuLogic\StartPlaylist.cs" />
119120
<Compile Include="Harmony\Assembly-CSharp\LevelSelectMenuLogic\UpdateInput.cs" />
121+
<Compile Include="Harmony\Assembly-CSharp\MainMenuLogic\Start.cs" />
120122
<Compile Include="Harmony\Assembly-CSharp\OptionsMenuLogic\GetSubmenus.cs" />
123+
<Compile Include="Helpers\MainMenuLevelSetHelper.cs" />
121124
<Compile Include="Helpers\Sanitizer.cs" />
122125
<Compile Include="Mod.cs" />
123126
<Compile Include="Scripts\LevelPlaylistEntryUpdateLogic.cs" />

Distance.LevelSelectAdditions/Extensions/Assembly-CSharp/LevelPlaylist.cs

Lines changed: 52 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -75,32 +75,76 @@ public static bool IsPersonalLevelSet(this LevelPlaylist playlist)
7575
public const string IDPrefixName = "name" + IDPrefixSeparator;
7676

7777

78-
public static string GetLevelSetID(this LevelPlaylist playlist)
78+
public static string GetLevelSetID(this LevelPlaylist playlist, bool relativePath = false)
7979
{
80+
string result;
81+
8082
var playlistData = playlist.GetComponent<LevelPlaylistCompoundData>();
8183
if (playlistData && playlistData.FilePath != null)
8284
{
8385
string path = new FileInfo(playlistData.FilePath).FullName.UniformPathSeparators();
8486
string resourcesPath = new DirectoryInfo(Path.Combine(Application.dataPath, "Resources")).FullName.UniformPathSeparatorsTrimmed() + "/";
87+
string personalPath = new DirectoryInfo(Resource.personalDistanceDirPath_).FullName.UniformPathSeparatorsTrimmed() + "/";
8588

8689
// Only assign if we have a FilePath to determine with,
8790
// otherwise assume its a resource playlist until a FileName may be assigned.
8891
if (path.StartsWith(resourcesPath, StringComparison.InvariantCultureIgnoreCase))
8992
{
90-
return IDPrefixResources + path.Substring(resourcesPath.Length/* + 1*/).ToLowerInvariant();
93+
result = IDPrefixResources + path.Substring(resourcesPath.Length/* + 1*/);
94+
}
95+
else if (path.StartsWith(personalPath, StringComparison.InvariantCultureIgnoreCase))
96+
{
97+
result = IDPrefixPersonal + path.Substring(personalPath.Length/* + 1*/);
98+
}
99+
else
100+
{
101+
result = IDPrefixPath + path;
91102
}
103+
}
104+
else
105+
{
106+
result = IDPrefixName + playlist.Name_;
107+
}
92108

93-
string personalPath = new DirectoryInfo(Resource.personalDistanceDirPath_).FullName.UniformPathSeparatorsTrimmed() + "/";
109+
if (!relativePath)
110+
{
111+
result = result.ToLowerInvariant();
112+
}
113+
return result;
114+
}
115+
116+
public static string GetRelativePathID(this LevelPlaylist playlist) => playlist.GetLevelSetID(true);
117+
118+
public static string RelativePathIDToAbsolutePath(this string relativePathID, out bool isName)
119+
{
120+
string[] parts = relativePathID.Split(new string[] { IDPrefixSeparator }, 2, StringSplitOptions.None);
121+
if (parts.Length == 2)
122+
{
123+
string prefix = parts[0] + IDPrefixSeparator;
124+
string relativePath = parts[parts.Length - 1];
94125

95-
if (path.StartsWith(personalPath, StringComparison.InvariantCultureIgnoreCase))
126+
switch (prefix)
96127
{
97-
return IDPrefixPersonal + path.Substring(personalPath.Length/* + 1*/).ToLowerInvariant();
98-
}
128+
case IDPrefixResources:
129+
isName = false;
130+
return Path.Combine(new DirectoryInfo(Path.Combine(Application.dataPath, "Resources")).FullName, relativePath);
99131

100-
return IDPrefixPath + path.ToLowerInvariant();
132+
case IDPrefixPersonal:
133+
isName = false;
134+
return Path.Combine(new DirectoryInfo(Resource.personalDistanceDirPath_).FullName, relativePath);
135+
136+
case IDPrefixPath:
137+
isName = false;
138+
return relativePath;
139+
140+
case IDPrefixName:
141+
isName = true;
142+
return relativePath;
143+
}
101144
}
102145

103-
return IDPrefixName + playlist.Name_.ToLowerInvariant();
146+
isName = false;
147+
return null;
104148
}
105149

106150
/*public static bool IsLevelSetID(this LevelPlaylist playlist, string otherLevelSetID)

Distance.LevelSelectAdditions/Harmony/Assembly-CSharp/LevelGridGrid/PushGrid.cs

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,11 +45,15 @@ internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruct
4545
// Before: ldloc. (menuPanel)
4646
// Before: callvirt MenuPanel.Push
4747
// NOTE: (i - 3) to insert before the ldloc used to call the MenuPanel.Push instance method.
48+
// MOVE LABELS FROM ldloc. TO ldarg.0
49+
var labels = new List<Label>(codes[i - 3].labels);
50+
codes[i - 3].labels.Clear();
4851
codes.InsertRange(i - 3, new CodeInstruction[]
4952
{
5053
new CodeInstruction(OpCodes.Ldarg_0, null),
5154
new CodeInstruction(OpCodes.Call, typeof(LevelGridGrid__PushGrid).GetMethod(nameof(AddMenuPanelButtons_))),
5255
});
56+
codes[i - 3].labels.AddRange(labels);
5357

5458
break;
5559
}
@@ -66,13 +70,14 @@ public static void AddMenuPanelButtons_(LevelGridGrid levelGridGrid)
6670
if (Mod.Instance.Config.EnableLevelSetOptionsMenu)
6771
{
6872
MenuPanel menuPanel = levelGridGrid.gridPanel_.GetComponent<MenuPanel>();
69-
if (menuPanel && !levelGridGrid.levelGridMenu_.IsSimpleMenu_)
73+
bool isMainMenu = levelGridGrid.levelGridMenu_.displayType_ == LevelSelectMenuAbstract.DisplayType.ChooseMainMenuLevel;
74+
if (menuPanel && (!levelGridGrid.levelGridMenu_.IsSimpleMenu_ || isMainMenu))
7075
{
7176
if (!levelGridGrid.playlist_.IsResourcesPlaylist())
7277
{
7378
menuPanel.SetBottomLeftButton(InputAction.MenuStart, "PLAYLIST\nOPTIONS");
7479
}
75-
else if (Mod.BasicLevelSetOptionsSupported)
80+
else if (Mod.BasicLevelSetOptionsSupported || isMainMenu)
7681
{
7782
menuPanel.SetBottomLeftButton(InputAction.MenuStart, "LEVEL SET\nOPTIONS");
7883
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using Distance.LevelSelectAdditions.Helpers;
2+
using HarmonyLib;
3+
4+
namespace Distance.LevelSelectAdditions.Harmony
5+
{
6+
/// <summary>
7+
/// Patch to handle resetting the selected main menu (to remove level set selections).
8+
/// </summary>
9+
[HarmonyPatch(typeof(LevelGridMenu), nameof(LevelGridMenu.OnGridCellClicked))]
10+
internal static class LevelGridMenu__OnGridCellClicked
11+
{
12+
[HarmonyPrefix]
13+
internal static void Prefix(LevelGridMenu __instance)
14+
{
15+
if (__instance.levelSelectFinished_)
16+
{
17+
return;
18+
}
19+
if (__instance.displayType_ == LevelSelectMenuAbstract.DisplayType.ChooseMainMenuLevel)
20+
{
21+
// Clear level set used as main menu. The user has selected an individual level as their main menu.
22+
MainMenuLevelSetHelper.SetMainMenuLevelSet(null);
23+
}
24+
}
25+
}
26+
}

Distance.LevelSelectAdditions/Harmony/Assembly-CSharp/LevelSelectMenuLogic/OnLevelButtonClicked.cs

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using HarmonyLib;
1+
using Distance.LevelSelectAdditions.Helpers;
2+
using HarmonyLib;
23
using System.Collections.Generic;
34
using System.Linq;
45
using System.Reflection;
@@ -8,10 +9,25 @@ namespace Distance.LevelSelectAdditions.Harmony
89
{
910
/// <summary>
1011
/// Patch to enable adding a selected level in Playlist Mode while in the Choose Main Menu level display type.
12+
/// <para/>
13+
/// Also includes patch to handle resetting the selected main menu (to remove level set selections).
1114
/// </summary>
1215
[HarmonyPatch(typeof(LevelSelectMenuLogic), nameof(LevelSelectMenuLogic.OnLevelButtonClicked))]
1316
internal static class LevelSelectMenuLogic__OnLevelButtonClicked
1417
{
18+
[HarmonyPrefix]
19+
internal static void Prefix(LevelSelectMenuLogic __instance)
20+
{
21+
if (__instance.CurrentLevelLocked_)
22+
{
23+
return;
24+
}
25+
if (__instance.displayType_ == LevelSelectMenuAbstract.DisplayType.ChooseMainMenuLevel)
26+
{
27+
MainMenuLevelSetHelper.SetMainMenuLevelSet(null);
28+
}
29+
}
30+
1531
[HarmonyTranspiler]
1632
internal static IEnumerable<CodeInstruction> Transpiler(IEnumerable<CodeInstruction> instructions)
1733
{
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
using Distance.LevelSelectAdditions.Helpers;
2+
using HarmonyLib;
3+
4+
namespace Distance.LevelSelectAdditions.Harmony
5+
{
6+
/// <summary>
7+
/// Patch to trigger changing the level set main menu level.
8+
/// </summary>
9+
[HarmonyPatch(typeof(MainMenuLogic), nameof(MainMenuLogic.Start))]
10+
internal static class MainMenuLogic__Start
11+
{
12+
[HarmonyPrefix]
13+
internal static void Prefix(MainMenuLogic __instance)
14+
{
15+
MainMenuLevelSetHelper.ChooseNextMainMenu();
16+
}
17+
}
18+
}

0 commit comments

Comments
 (0)