Skip to content

Commit 1e7d48e

Browse files
committed
Use custom heart colors (set up for journals in collabs) in file select as well
1 parent 420e47a commit 1e7d48e

File tree

2 files changed

+64
-0
lines changed

2 files changed

+64
-0
lines changed

LobbyHelper.cs

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ public static class LobbyHelper {
2323
private static ILHook hookOnOuiFileSelectSlotGolden;
2424
private static ILHook hookOnOuiJournalPoemLines;
2525
private static ILHook hookOnLevelSetSwitch;
26+
private static ILHook hookOnOuiFileSelectSlotRender;
2627

2728
private static HashSet<string> collabNames = new HashSet<string>();
2829

@@ -140,6 +141,7 @@ internal static void Load() {
140141
hookOnOuiFileSelectRenderStrawberryStamp = new ILHook(typeof(OuiFileSelectSlot).GetMethod("orig_Render"), modSelectSlotCollectedStrawberries);
141142
hookOnOuiJournalPoemLines = new ILHook(typeof(OuiJournalPoem).GetNestedType("PoemLine", BindingFlags.NonPublic).GetMethod("Render"), modJournalPoemHeartColors);
142143
hookOnOuiFileSelectSlotGolden = new ILHook(typeof(OuiFileSelectSlot).GetMethod("get_Golden", BindingFlags.NonPublic | BindingFlags.Instance), modSelectSlotCollectedStrawberries);
144+
hookOnOuiFileSelectSlotRender = new ILHook(typeof(OuiFileSelectSlot).GetMethod("orig_Render"), modOuiFileSelectSlotRender);
143145
}
144146

145147
internal static void Unload() {
@@ -164,6 +166,7 @@ internal static void Unload() {
164166
hookOnOuiFileSelectRenderStrawberryStamp?.Dispose();
165167
hookOnOuiJournalPoemLines?.Dispose();
166168
hookOnOuiFileSelectSlotGolden?.Dispose();
169+
hookOnOuiFileSelectSlotRender?.Dispose();
167170
}
168171

169172
public static void OnSessionCreated() {
@@ -483,6 +486,28 @@ private static void onOuiFileSelectSlotShow(On.Celeste.OuiFileSelectSlot.orig_Sh
483486
self.Strawberries.OutOf = maxStrawberryCount;
484487
}
485488

489+
// figure out if some hearts are customized, and store it in DynData so that a IL hook can access it later.
490+
SaveData oldInstance = SaveData.Instance;
491+
SaveData.Instance = self.SaveData;
492+
List<string> customJournalHearts = new List<string>();
493+
if (self.SaveData != null) {
494+
foreach (AreaStats item in self.SaveData.Areas_Safe) {
495+
if (item.ID_Safe > self.SaveData.UnlockedAreas_Safe) {
496+
break;
497+
}
498+
if (!AreaData.Areas[item.ID_Safe].Interlude_Safe && AreaData.Areas[item.ID_Safe].CanFullClear) {
499+
string lobbyLevelSetName = GetLobbyLevelSet(item.GetSID());
500+
if (lobbyLevelSetName != null && MTN.Journal.Has("CollabUtils2Hearts/" + lobbyLevelSetName)) {
501+
customJournalHearts.Add("CollabUtils2Hearts/" + lobbyLevelSetName);
502+
} else {
503+
customJournalHearts.Add(null);
504+
}
505+
}
506+
}
507+
}
508+
new DynData<OuiFileSelectSlot>(self)["collabutils2_customhearts"] = customJournalHearts;
509+
SaveData.Instance = oldInstance;
510+
486511
// Restore the last area if it was replaced at the beginning of this method.
487512
if (savedLastArea != null) {
488513
self.SaveData.LastArea_Safe = savedLastArea.Value;
@@ -546,6 +571,41 @@ private static void modSelectSlotCollectedStrawberries(ILContext il) {
546571
}
547572
}
548573

574+
private static void modOuiFileSelectSlotRender(ILContext il) {
575+
ILCursor cursor = new ILCursor(il);
576+
577+
if (cursor.TryGotoNext(MoveType.After,
578+
instr => instr.MatchCall<string>("Concat"),
579+
instr => instr.MatchCallvirt<Atlas>("get_Item"))) {
580+
581+
// we are after the heart was loaded for file select, and before it is rendered.
582+
// so we can intercept it and give the game another heart instead...
583+
// but we first need to know which heart it is by getting the loop index.
584+
// this loop index is used to read from the OuiFileSelectSlot.Cassettes array, so we can anchor on that
585+
ILCursor cursorLoopIndex = new ILCursor(il);
586+
587+
if (cursorLoopIndex.TryGotoNext(MoveType.After,
588+
instr => instr.MatchLdfld<OuiFileSelectSlot>("Cassettes"),
589+
instr => true,
590+
instr => instr.OpCode == OpCodes.Callvirt && (instr.Operand as MethodReference).Name == "get_Item")) {
591+
592+
Instruction loopIndex = cursorLoopIndex.Prev.Previous;
593+
Logger.Log("CollabUtils2/LobbyHelper", $"Modding heart colors on file select at {cursor.Index} in IL for {il.Method.Name}, using loop index {loopIndex}");
594+
595+
cursor.Emit(OpCodes.Ldarg_0);
596+
cursor.Emit(loopIndex.OpCode, loopIndex.Operand);
597+
598+
cursor.EmitDelegate<Func<MTexture, OuiFileSelectSlot, int, MTexture>>((orig, self, index) => {
599+
List<string> customJournalHearts = new DynData<OuiFileSelectSlot>(self).Get<List<string>>("collabutils2_customhearts");
600+
if (customJournalHearts != null && customJournalHearts[index] != null) {
601+
return MTN.Journal[customJournalHearts[index]]; // "Journal" and not "FileSelect" because it re-uses the setup people made for their custom journals.
602+
}
603+
return orig;
604+
});
605+
}
606+
}
607+
}
608+
549609
private static void modJournalPoemHeartColors(ILContext il) {
550610
ILCursor cursor = new ILCursor(il);
551611

UI/OuiJournalCollabProgressInLobby.cs

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,8 @@ public static List<OuiJournalCollabProgressInLobby> GeneratePages(OuiJournal jou
9494
.Where(map => !AreaData.Get(map).Interlude_Safe)
9595
.ToList();
9696

97+
// sort maps by icon name if all of their icons start with [number]-...
98+
// because then we know the ordering is intentional (and not accidental like easy > hard > medium)
9799
Regex startsWithNumber = new Regex(".*/[0-9]+-.*");
98100
if (sortedMaps.Select(map => AreaData.Get(map).Icon ?? "").All(icon => startsWithNumber.IsMatch(icon))) {
99101
sortedMaps.Sort((a, b) => {
@@ -103,11 +105,13 @@ public static List<OuiJournalCollabProgressInLobby> GeneratePages(OuiJournal jou
103105
bool aHeartSide = LobbyHelper.IsHeartSide(a.GetSID());
104106
bool bHeartSide = LobbyHelper.IsHeartSide(b.GetSID());
105107

108+
// heart sides should appear last.
106109
if (aHeartSide && !bHeartSide)
107110
return 1;
108111
if (!aHeartSide && bHeartSide)
109112
return -1;
110113

114+
// sort by icon name, then by map bin name.
111115
return adata.Icon == bdata.Icon ? adata.Name.CompareTo(bdata.Name) : adata.Icon.CompareTo(bdata.Icon);
112116
});
113117
}

0 commit comments

Comments
 (0)