Skip to content

Commit 8dc455c

Browse files
Active Main Menu icons (v1.1.4.1)
New Features: * Green camera icon shows inside level grid cells for the active main menu. * Green play arrow icon shows next to the active main menu level set. Changes: * Removed medal logo for main menu level grid cells (as that spot is now taken up by the camera icon).
1 parent cd2d425 commit 8dc455c

File tree

7 files changed

+346
-2
lines changed

7 files changed

+346
-2
lines changed

Distance.LevelSelectAdditions/Distance.LevelSelectAdditions.csproj

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,8 @@
9595
<Compile Include="Extensions\Assembly-CSharp\LevelSelectMenuLogic.cs" />
9696
<Compile Include="Extensions\Assembly-CSharp\NGUIExtensions.cs" />
9797
<Compile Include="Extensions\mscorlib\System\String.cs" />
98+
<Compile Include="Harmony\Assembly-CSharp\LevelGridCell\OnDisplayedVirtual.cs" />
99+
<Compile Include="Harmony\Assembly-CSharp\LevelGridCell\Update.cs" />
98100
<Compile Include="Harmony\Assembly-CSharp\LevelGridGrid\PushGrid.cs" />
99101
<Compile Include="Harmony\Assembly-CSharp\LevelGridMenu\CreateAndAddLevelSet.cs" />
100102
<Compile Include="Harmony\Assembly-CSharp\LevelGridMenu\CreateEntries.cs" />
@@ -103,6 +105,7 @@
103105
<Compile Include="Harmony\Assembly-CSharp\LevelGridMenu\OnLevelEntrySelected.cs" />
104106
<Compile Include="Harmony\Assembly-CSharp\LevelGridMenu\PlaylistEntry\ctor.cs" />
105107
<Compile Include="Harmony\Assembly-CSharp\LevelGridMenu\PlaylistEntry\get_Color_.cs" />
108+
<Compile Include="Harmony\Assembly-CSharp\LevelGridPlaylistButton\OnDisplayedVirtual.cs" />
106109
<Compile Include="Harmony\Assembly-CSharp\LevelPlaylist\Create.cs" />
107110
<Compile Include="Harmony\Assembly-CSharp\LevelPlaylist\Load.cs" />
108111
<Compile Include="Harmony\Assembly-CSharp\LevelPlaylist\Save.cs" />
@@ -123,6 +126,7 @@
123126
<Compile Include="Helpers\MainMenuLevelSetHelper.cs" />
124127
<Compile Include="Helpers\Sanitizer.cs" />
125128
<Compile Include="Mod.cs" />
129+
<Compile Include="Scripts\LevelGridButtonCurrentMainMenuLogic.cs" />
126130
<Compile Include="Scripts\LevelPlaylistEntryUpdateLogic.cs" />
127131
<Compile Include="Scripts\LevelSelectWorkshopRateButtonLogic.cs" />
128132
<Compile Include="Scripts\Menus\LevelSetMenuType.cs" />
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
using Distance.LevelSelectAdditions.Scripts;
2+
using HarmonyLib;
3+
4+
namespace Distance.LevelSelectAdditions.Harmony
5+
{
6+
/// <summary>
7+
/// Patch to add an icon showing if this level grid cell is the active main menu.
8+
/// </summary>
9+
[HarmonyPatch(typeof(LevelGridCell), nameof(LevelGridCell.OnDisplayedVirtual))]
10+
internal static class LevelGridCell__OnDisplayedVirtual
11+
{
12+
[HarmonyPostfix]
13+
internal static void Postfix(LevelGridCell __instance)
14+
{
15+
LevelGridGrid.LevelEntry entry = __instance.entry_ as LevelGridGrid.LevelEntry;
16+
bool isMainMenu = entry.levelGridMenu_.DisplayType_ == LevelSelectMenuAbstract.DisplayType.ChooseMainMenuLevel;
17+
18+
// Hide medal logo when in Choose Main Menu level.
19+
__instance.medalLogo_.gameObject.SetActive(!isMainMenu);
20+
21+
var compoundData = LevelGridButtonCurrentMainMenuLogic.GetOrCreate(__instance);
22+
if (compoundData)
23+
{
24+
// Show camera icon when this is the current main menu level.
25+
compoundData.UpdateCurrentMainMenuIcon();
26+
if (compoundData.IsCoveringUnplayedCircle)
27+
{
28+
// We're hijacking the Unplayed circle, prevent it from appearing during UpdateOrangeDot().
29+
__instance.unplayed_.gameObject.SetActive(false);
30+
__instance.isNew_ = false;
31+
}
32+
}
33+
}
34+
}
35+
}
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
using Distance.LevelSelectAdditions.Scripts;
2+
using HarmonyLib;
3+
4+
namespace Distance.LevelSelectAdditions.Harmony
5+
{
6+
/// <summary>
7+
/// Patch to force level grid cells to stay enabled, in order to update the active main menu icon's highlight color.
8+
/// This is needed because the normal Update method disables the level grid cell after the image is loaded.
9+
/// </summary>
10+
[HarmonyPatch(typeof(LevelGridCell), nameof(LevelGridCell.Update))]
11+
internal static class LevelGridCell__Update
12+
{
13+
[HarmonyPostfix]
14+
internal static void Postfix(LevelGridCell __instance)
15+
{
16+
// We only need to stay enabled when Choosing Main Menu levels.
17+
LevelGridGrid.LevelEntry entry = __instance.entry_ as LevelGridGrid.LevelEntry;
18+
bool isMainMenu = entry.levelGridMenu_.DisplayType_ == LevelSelectMenuAbstract.DisplayType.ChooseMainMenuLevel;
19+
20+
if (isMainMenu && LevelGridButtonCurrentMainMenuLogic.ShowIconHighlight)
21+
{
22+
__instance.enabled = true;
23+
}
24+
}
25+
}
26+
}
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
using Distance.LevelSelectAdditions.Scripts;
2+
using HarmonyLib;
3+
4+
namespace Distance.LevelSelectAdditions.Harmony
5+
{
6+
/// <summary>
7+
/// Patch to add an icon showing if this playlist button is the active main menu collection.
8+
/// </summary>
9+
[HarmonyPatch(typeof(LevelGridPlaylistButton), nameof(LevelGridPlaylistButton.OnDisplayedVirtual))]
10+
internal static class LevelGridPlaylistButton__OnDisplayedVirtual
11+
{
12+
[HarmonyPostfix]
13+
internal static void Postfix(LevelGridPlaylistButton __instance)
14+
{
15+
LevelGridMenu.PlaylistEntry entry = __instance.entry_ as LevelGridMenu.PlaylistEntry;
16+
17+
var compoundData = LevelGridButtonCurrentMainMenuLogic.GetOrCreate(__instance);
18+
if (compoundData)
19+
{
20+
// Show camera icon when this is the current main menu level.
21+
compoundData.UpdateCurrentMainMenuIcon();
22+
if (compoundData.IsCoveringUnplayedCircle)
23+
{
24+
// We're hijacking the Unplayed circle, prevent it from appearing during UpdateOrangeDot().
25+
__instance.unplayed_.gameObject.SetActive(false);
26+
entry.isNew_ = false;
27+
}
28+
}
29+
}
30+
}
31+
}

Distance.LevelSelectAdditions/Properties/AssemblyInfo.cs

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -32,8 +32,8 @@
3232
// You can specify all the values or you can default the Build and Revision Numbers
3333
// by using the '*' as shown below:
3434
// [assembly: AssemblyVersion("1.0.*")]
35-
[assembly: AssemblyVersion("1.1.4.0")]
36-
[assembly: AssemblyFileVersion("1.1.4.0")]
35+
[assembly: AssemblyVersion("1.1.4.1")]
36+
[assembly: AssemblyFileVersion("1.1.4.1")]
3737

3838

3939

Lines changed: 246 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,246 @@
1+
using Distance.LevelSelectAdditions.Extensions;
2+
using UnityEngine;
3+
4+
namespace Distance.LevelSelectAdditions.Scripts
5+
{
6+
public class LevelGridButtonCurrentMainMenuLogic : MonoBehaviour
7+
{
8+
public const string PlaylistIconSpriteName = "Play";
9+
public const string CellIconSpriteName = "CameraIcon";
10+
private const int PlaylistIconSize = 14; // or 16
11+
private const int CellIconSize = 28;
12+
private const int CellIconLocation = 2; // 0 = TopLeft (unplayed), 1 = TopRight, 2 = BottomRight (medal)
13+
14+
public const bool ShowIconImprint = false; // Show a an empty slot for where the camera icon would be.
15+
public const bool ShowIconHighlight = false; // Color the camera icon black when highlighted.
16+
public const bool ShowIconShadow = true; // Show a bottom-right shadow under the camera icon.
17+
18+
private static readonly Color IconColor = new Color(0.6235f, 0.9059f, 0.502f); // green, rgb(159,231,128)
19+
private static readonly Color IconImprintColor = new Color(0.286f, 0.286f, 0.286f, 0.298f); // gray, rgba(73,73,73,76)
20+
private static readonly Color IconHighlightColor = Color.black;
21+
private static readonly Color IconShadowColor = new Color(0f, 0f, 0f, 0.502f); // transparent, rgba(0,0,0,128)
22+
23+
24+
public LevelGridCell GridCell { get; internal set; }
25+
public LevelGridPlaylistButton PlaylistButton { get; internal set; }
26+
public LevelGridMenu LevelGridMenu { get; internal set; }
27+
public bool IsPlaylist { get; internal set; }
28+
29+
public bool IsCurrentMainMenu { get; internal set; }
30+
31+
public bool IsCoveringUnplayedCircle => this.IsCurrentMainMenu && (this.IsPlaylist || CellIconLocation == 0);
32+
33+
public InterpolateUIPanelAlphaLogic panelInterp_;
34+
public UISprite iconSprite_;
35+
public UISprite iconSpriteShadow_;
36+
37+
38+
public void UpdateCurrentMainMenuIcon()
39+
{
40+
this.panelInterp_.Reset(1f);
41+
42+
bool isMainMenu = this.LevelGridMenu.DisplayType_ == LevelSelectMenuAbstract.DisplayType.ChooseMainMenuLevel;
43+
if (isMainMenu)
44+
{
45+
Profile profile = G.Sys.ProfileManager_.CurrentProfile_;
46+
string relativePath, currentRelativePath;
47+
48+
if (this.IsPlaylist)
49+
{
50+
LevelGridMenu.PlaylistEntry playlistEntry = this.PlaylistButton.entry_ as LevelGridMenu.PlaylistEntry;
51+
relativePath = playlistEntry.Playlist_.GetRelativePathID();
52+
currentRelativePath = Mod.Instance.Config.GetProfileMainMenuRelativePathID(profile.Name_);
53+
}
54+
else
55+
{
56+
LevelGridGrid.LevelEntry levelEntry = this.GridCell.entry_ as LevelGridGrid.LevelEntry;
57+
relativePath = levelEntry.levelInfo_.relativePath_;
58+
currentRelativePath = profile.MainMenuLevelRelativePath_;
59+
}
60+
61+
this.IsCurrentMainMenu = currentRelativePath == relativePath;
62+
63+
if (this.IsCurrentMainMenu)
64+
{
65+
this.iconSprite_.color = IconColor;
66+
}
67+
else if (ShowIconImprint && !this.IsPlaylist)
68+
{
69+
this.iconSprite_.color = IconImprintColor;
70+
}
71+
72+
this.iconSpriteShadow_?.gameObject.SetActive(ShowIconShadow && !this.IsPlaylist && this.IsCurrentMainMenu);
73+
this.iconSprite_.gameObject.SetActive((ShowIconImprint && !this.IsPlaylist) || this.IsCurrentMainMenu);
74+
75+
// Ensure the iconSprite ALWAYS draws above iconSpriteShadow.
76+
if (this.iconSpriteShadow_)
77+
{
78+
this.iconSprite_.depth = 6;
79+
this.iconSpriteShadow_.depth = 5;
80+
}
81+
}
82+
else
83+
{
84+
this.IsCurrentMainMenu = false;
85+
this.iconSpriteShadow_?.gameObject.SetActive(false);
86+
this.iconSprite_.gameObject.SetActive(false);
87+
}
88+
}
89+
90+
private void Update()
91+
{
92+
if (!this.IsPlaylist && this.iconSprite_ && this.IsCurrentMainMenu)
93+
{
94+
// Don't waste your time, entry_.isSelected_ is never true.
95+
if (this.GridCell.buttonList_.selectedEntry_ == this.GridCell.entry_ && ShowIconHighlight)
96+
{
97+
this.iconSprite_.color = IconHighlightColor;
98+
}
99+
else
100+
{
101+
this.iconSprite_.color = IconColor;
102+
}
103+
}
104+
}
105+
106+
private bool SetupCurrentMainMenuIcon(GameObject unplayedCircle)
107+
{
108+
GameObject newUnplayedCircle = UnityEngine.Object.Instantiate(unplayedCircle, unplayedCircle.transform.parent);
109+
newUnplayedCircle.name = "CurrentMainMenuIcon";
110+
111+
this.panelInterp_ = newUnplayedCircle.GetComponent<InterpolateUIPanelAlphaLogic>();
112+
GameObject newCircle = newUnplayedCircle.transform.Find("Circle").gameObject;
113+
if (!newCircle)
114+
{
115+
Mod.Instance.Logger.Error("\"Circle\" game object not found");
116+
return false;
117+
}
118+
119+
newCircle.name = "IconSprite";
120+
121+
// Setup our icon.
122+
this.iconSprite_ = newCircle.GetComponent<UISprite>();
123+
this.iconSprite_.color = IconColor;
124+
125+
if (this.IsPlaylist)
126+
{
127+
this.iconSprite_.spriteName = PlaylistIconSpriteName;
128+
this.iconSprite_.width = PlaylistIconSize;
129+
this.iconSprite_.height = PlaylistIconSize;
130+
131+
// Reposition our icon.
132+
Vector3 mainMenuPos = this.panelInterp_.gameObject.transform.position;
133+
// Better alignment for camera icon at new size (we can't go too far left since it'll clip).
134+
// Old (relative to Button 0003): -1.4475 0.3825 0
135+
// New (relative to Button 0003): -1.445 0.385 0
136+
mainMenuPos.x += 0.0025f;
137+
mainMenuPos.y += 0.0025f;
138+
this.panelInterp_.gameObject.transform.position = mainMenuPos;
139+
}
140+
else
141+
{
142+
this.iconSprite_.spriteName = CellIconSpriteName;
143+
this.iconSprite_.width = CellIconSize;
144+
this.iconSprite_.height = CellIconSize;
145+
146+
// Reposition our icon.
147+
// Better alignment for camera icon at new size, and move to the top-left corner (instead of top-right).
148+
// Old (relative to Button 0012): -0.375 -0.4325 0
149+
// New (relative to Button 0012): -0.0075 -0.45 0
150+
// NOTE: If we use the alt position (covers the medal logo), the icon will be hard to see when the cell is highlighted.
151+
// Alt (relative to Button 0012): -0.0075 -0.705 0
152+
153+
// 0 = TopLeft (unplayed), 1 = TopRight, 2 = BottomRight (medal)
154+
Vector3 mainMenuPos = this.panelInterp_.gameObject.transform.position;
155+
if (CellIconLocation == 0)
156+
{
157+
mainMenuPos.x += 0.0150f; // Left
158+
}
159+
else
160+
{
161+
mainMenuPos.x += 0.3675f; // Right
162+
}
163+
if (CellIconLocation == 0 || CellIconLocation == 1)
164+
{
165+
mainMenuPos.y -= 0.0175f; // Top
166+
}
167+
else
168+
{
169+
mainMenuPos.y -= 0.2725f; // Bottom
170+
}
171+
this.panelInterp_.gameObject.transform.position = mainMenuPos;
172+
173+
174+
// Create a shadow that sits behind the icon to the bottom right by 1.5 pixels.
175+
if (ShowIconShadow)
176+
{
177+
178+
GameObject newCircleShadow = UnityEngine.Object.Instantiate(newCircle, newCircle.transform.parent);
179+
newCircleShadow.name = "IconSpriteShadow";
180+
181+
this.iconSpriteShadow_ = newCircleShadow.GetComponent<UISprite>();
182+
183+
this.iconSpriteShadow_.color = IconShadowColor;
184+
this.iconSpriteShadow_.spriteName = CellIconSpriteName;
185+
this.iconSpriteShadow_.width = CellIconSize;
186+
this.iconSpriteShadow_.height = CellIconSize;
187+
188+
Vector3 shadowPos = this.iconSpriteShadow_.transform.localPosition;
189+
shadowPos.x += 1.5f; // 1f;
190+
shadowPos.y -= 1.5f; // 1f;
191+
this.iconSpriteShadow_.transform.localPosition = shadowPos;
192+
}
193+
}
194+
195+
// What does this actually do? Is it necessary?
196+
// (This is coppied from Gsl.Centrifuge.Distance's `VersionNumber`)
197+
newUnplayedCircle.ForEachChildObjectDepthFirstRecursive((obj) => {
198+
obj.SetActive(true);
199+
});
200+
201+
this.iconSpriteShadow_?.gameObject.SetActive(false);
202+
this.iconSprite_.gameObject.SetActive(false);
203+
204+
return true;
205+
}
206+
207+
public static LevelGridButtonCurrentMainMenuLogic GetOrCreate(LevelGridCell gridCell)
208+
{
209+
var compoundData = gridCell.GetComponent<LevelGridButtonCurrentMainMenuLogic>();
210+
if (!compoundData)
211+
{
212+
// First-time setup
213+
LevelGridGrid.LevelEntry entry = gridCell.entry_ as LevelGridGrid.LevelEntry;
214+
215+
compoundData = gridCell.gameObject.AddComponent<LevelGridButtonCurrentMainMenuLogic>();
216+
compoundData.GridCell = gridCell;
217+
compoundData.LevelGridMenu = entry.levelGridMenu_;
218+
compoundData.IsPlaylist = false;
219+
220+
compoundData.SetupCurrentMainMenuIcon(gridCell.panelInterp_.gameObject);
221+
}
222+
223+
return compoundData;
224+
}
225+
226+
227+
public static LevelGridButtonCurrentMainMenuLogic GetOrCreate(LevelGridPlaylistButton playlistButton)
228+
{
229+
var compoundData = playlistButton.GetComponent<LevelGridButtonCurrentMainMenuLogic>();
230+
if (!compoundData)
231+
{
232+
// First-time setup
233+
LevelGridMenu.PlaylistEntry entry = playlistButton.entry_ as LevelGridMenu.PlaylistEntry;
234+
235+
compoundData = playlistButton.gameObject.AddComponent<LevelGridButtonCurrentMainMenuLogic>();
236+
compoundData.PlaylistButton = playlistButton;
237+
compoundData.LevelGridMenu = entry.levelGridMenu_;
238+
compoundData.IsPlaylist = true;
239+
240+
compoundData.SetupCurrentMainMenuIcon(playlistButton.panelInterp_.gameObject);
241+
}
242+
243+
return compoundData;
244+
}
245+
}
246+
}

README.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,8 @@ Distance mod that extends the level selection UI by adding more options.
1313
* Remember the last-accessed level set/playlist, so that the game will return to your original place after playing a level appearing in multiple playlists.
1414
* Fix scrolling bug in Advanced level select menu where you could only scroll to the very top or bottom entry.
1515
* Choose level sets and playlists as the main menu, allowing to randomly see a variety of menus without manually swapping them.
16+
* Shows a green camera icon inside the level grid cell for the active main menu (where the medal logo would show).
17+
* Shows a green play arrow icon next to the active main menu level set (if there is one).
1618

1719
## Current Options
1820

0 commit comments

Comments
 (0)