Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Hide C-Side/Golden postcard if LevelSet has no C-Sides #378

Merged
merged 1 commit into from
Sep 28, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 28 additions & 15 deletions Celeste.Mod.mm/MonoModRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -206,10 +206,10 @@ class PatchPathfinderRenderAttribute : Attribute { };
class PatchTotalHeartGemChecksAttribute : Attribute { };

/// <summary>
/// Same as above, but for references in routines.
/// Patch TotalHeartGems to refer to TotalHeartGemsInVanilla, and whether to show the UnlockCSide postcard
/// </summary>
[MonoModCustomMethodAttribute(nameof(MonoModRules.PatchTotalHeartGemChecksInRoutine))]
class PatchTotalHeartGemChecksInRoutineAttribute : Attribute { };
[MonoModCustomMethodAttribute(nameof(MonoModRules.PatchTotalHeartGemCSidePostcard))]
class PatchTotalHeartGemCSidePostcardAttribute : Attribute { };

/// <summary>
/// Patch a reference to TotalHeartGems in the OuiJournalGlobal constructor to unharcode the check for golden berry unlock.
Expand Down Expand Up @@ -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<Instruction> 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__"))
Expand All @@ -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) {
Expand Down
22 changes: 22 additions & 0 deletions Celeste.Mod.mm/Patches/LevelSetStats.cs
Original file line number Diff line number Diff line change
@@ -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;
}
}
}
}
2 changes: 1 addition & 1 deletion Celeste.Mod.mm/Patches/OverworldLoader.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}
}