From a5622499ef3aae789807e1383151f84d281e9812 Mon Sep 17 00:00:00 2001
From: coloursofnoise <53288101+coloursofnoise@users.noreply.github.com>
Date: Sat, 18 Sep 2021 23:25:43 -0700
Subject: [PATCH] Hide C-Side/Golden postcard if LevelSet has no C-Sides
---
Celeste.Mod.mm/MonoModRules.cs | 43 +++++++++++++++--------
Celeste.Mod.mm/Patches/LevelSetStats.cs | 22 ++++++++++++
Celeste.Mod.mm/Patches/OverworldLoader.cs | 2 +-
3 files changed, 51 insertions(+), 16 deletions(-)
create mode 100644 Celeste.Mod.mm/Patches/LevelSetStats.cs
diff --git a/Celeste.Mod.mm/MonoModRules.cs b/Celeste.Mod.mm/MonoModRules.cs
index c7ad21b5d..ad49673d9 100644
--- a/Celeste.Mod.mm/MonoModRules.cs
+++ b/Celeste.Mod.mm/MonoModRules.cs
@@ -206,10 +206,10 @@ class PatchPathfinderRenderAttribute : Attribute { };
class PatchTotalHeartGemChecksAttribute : Attribute { };
///
- /// Same as above, but for references in routines.
+ /// Patch TotalHeartGems to refer to TotalHeartGemsInVanilla, and whether to show the UnlockCSide postcard
///
- [MonoModCustomMethodAttribute(nameof(MonoModRules.PatchTotalHeartGemChecksInRoutine))]
- class PatchTotalHeartGemChecksInRoutineAttribute : Attribute { };
+ [MonoModCustomMethodAttribute(nameof(MonoModRules.PatchTotalHeartGemCSidePostcard))]
+ class PatchTotalHeartGemCSidePostcardAttribute : Attribute { };
///
/// Patch a reference to TotalHeartGems in the OuiJournalGlobal constructor to unharcode the check for golden berry unlock.
@@ -1475,20 +1475,21 @@ public static void PatchPathfinderRender(ILContext context, CustomAttribute attr
}
- public static void PatchTotalHeartGemChecks(MethodDefinition method, CustomAttribute attrib) {
- MethodDefinition m_getTotalHeartGemsInVanilla = method.Module.GetType("Celeste.SaveData").FindMethod("System.Int32 get_TotalHeartGemsInVanilla()");
+ public static void PatchTotalHeartGemChecks(ILContext context, CustomAttribute attrib) {
+ MethodDefinition m_getTotalHeartGemsInVanilla = context.Module.GetType("Celeste.SaveData").FindMethod("System.Int32 get_TotalHeartGemsInVanilla()");
- Mono.Collections.Generic.Collection instrs = method.Body.Instructions;
- for (int instri = 0; instri < instrs.Count; instri++) {
- Instruction instr = instrs[instri];
+ ILCursor cursor = new ILCursor(context);
- if (instr.MatchCallvirt(out MethodReference m) && m.Name == "get_TotalHeartGems") {
- // replace the call to the TotalHeartGems property with a call to TotalHeartGemsInVanilla.
- instr.Operand = m_getTotalHeartGemsInVanilla;
- }
- }
+ cursor.GotoNext(instr => instr.MatchCallvirt("Celeste.SaveData", "get_TotalHeartGems"));
+ cursor.Next.Operand = m_getTotalHeartGemsInVanilla;
}
- public static void PatchTotalHeartGemChecksInRoutine(MethodDefinition method, CustomAttribute attrib) {
+
+ public static void PatchTotalHeartGemCSidePostcard(MethodDefinition method, CustomAttribute attrib) {
+ FieldDefinition f_SaveData_Instance = method.Module.GetType("Celeste.SaveData").FindField("Instance");
+ MethodDefinition m_SaveData_get_LevelSetStats = method.Module.GetType("Celeste.SaveData").FindMethod("Celeste.LevelSetStats get_LevelSetStats()");
+ MethodDefinition m_LevelSetStats_get_MaxAreaMode = method.Module.GetType("Celeste.LevelSetStats").FindMethod("System.Int32 get_MaxAreaMode()");
+
+
// Routines are stored in compiler-generated methods.
foreach (TypeDefinition nest in method.DeclaringType.NestedTypes) {
if (!nest.Name.StartsWith("<" + method.Name + ">d__"))
@@ -1497,7 +1498,19 @@ public static void PatchTotalHeartGemChecksInRoutine(MethodDefinition method, Cu
break;
}
- PatchTotalHeartGemChecks(method, attrib);
+ new ILContext(method).Invoke(il => {
+ ILCursor cursor = new ILCursor(il);
+
+ cursor.GotoNext(MoveType.After, instr => instr.MatchLdfld("Celeste.Session", "UnlockedCSide"));
+ cursor.Emit(cursor.Next.OpCode, cursor.Next.Operand);
+ cursor.Emit(OpCodes.Ldsfld, f_SaveData_Instance);
+ cursor.Emit(OpCodes.Callvirt, m_SaveData_get_LevelSetStats);
+ cursor.Emit(OpCodes.Callvirt, m_LevelSetStats_get_MaxAreaMode);
+ cursor.Emit(OpCodes.Ldc_I4_2);
+ cursor.Next.OpCode = OpCodes.Blt_S;
+
+ PatchTotalHeartGemChecks(il, attrib);
+ });
}
public static void PatchOuiJournalStatsHeartGemCheck(ILContext context, CustomAttribute attrib) {
diff --git a/Celeste.Mod.mm/Patches/LevelSetStats.cs b/Celeste.Mod.mm/Patches/LevelSetStats.cs
new file mode 100644
index 000000000..58ac2431a
--- /dev/null
+++ b/Celeste.Mod.mm/Patches/LevelSetStats.cs
@@ -0,0 +1,22 @@
+namespace Celeste {
+ class patch_LevelSetStats : LevelSetStats {
+ public int MaxAreaMode {
+ get {
+ if (Name == "Celeste") {
+ return (int) AreaMode.CSide;
+ }
+ int areaOffset = AreaOffset;
+ int maxAreaMode = 0;
+ for (int i = 0; i <= MaxArea; i++) {
+ ModeProperties[] mode = AreaData.Areas[areaOffset + i].Mode;
+ foreach (ModeProperties modeProperties in mode) {
+ if ((int) modeProperties.MapData.Area.Mode > maxAreaMode) {
+ maxAreaMode = (int) modeProperties.MapData.Area.Mode;
+ }
+ }
+ }
+ return maxAreaMode;
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/Celeste.Mod.mm/Patches/OverworldLoader.cs b/Celeste.Mod.mm/Patches/OverworldLoader.cs
index b687d372f..6d57dc8a6 100644
--- a/Celeste.Mod.mm/Patches/OverworldLoader.cs
+++ b/Celeste.Mod.mm/Patches/OverworldLoader.cs
@@ -18,7 +18,7 @@ public patch_OverworldLoader(Overworld.StartMode startMode, HiresSnow snow = nul
private extern void CheckVariantsPostcardAtLaunch();
[MonoModIgnore] // don't change anything in the method...
- [PatchTotalHeartGemChecksInRoutine] // except for replacing TotalHeartGems with TotalHeartGemsInVanilla through MonoModRules
+ [PatchTotalHeartGemCSidePostcard] // except for replacing TotalHeartGems with TotalHeartGemsInVanilla through MonoModRules
private extern IEnumerator Routine(Session session);
}
}