diff --git a/Celeste.Mod.mm/Mod/Meta/MapMeta.cs b/Celeste.Mod.mm/Mod/Meta/MapMeta.cs index b685591e6..8b1b9546f 100644 --- a/Celeste.Mod.mm/Mod/Meta/MapMeta.cs +++ b/Celeste.Mod.mm/Mod/Meta/MapMeta.cs @@ -336,6 +336,7 @@ public MapMetaModeProperties(BinaryPacker.Element meta) { public bool? HeartIsEnd { get; set; } public bool? SeekerSlowdown { get; set; } public bool? TheoInBubble { get; set; } + public bool? CoreModeIceTileOverlay { get; set; } public patch_ModeProperties Convert() => new patch_ModeProperties() { @@ -356,6 +357,7 @@ public void Parse(BinaryPacker.Element meta) { meta.AttrIfBool("HeartIsEnd", v => HeartIsEnd = v); meta.AttrIfBool("SeekerSlowdown", v => SeekerSlowdown = v); meta.AttrIfBool("TheoInBubble", v => TheoInBubble = v); + meta.AttrIfBool("CoreModeIceTileOverlay", v => CoreModeIceTileOverlay = v); BinaryPacker.Element child; diff --git a/Celeste.Mod.mm/Patches/LevelLoader.cs b/Celeste.Mod.mm/Patches/LevelLoader.cs index e91f45102..879f38946 100644 --- a/Celeste.Mod.mm/Patches/LevelLoader.cs +++ b/Celeste.Mod.mm/Patches/LevelLoader.cs @@ -250,6 +250,7 @@ private XmlDocument getModdedSpritesXml(string path) { [MonoModIgnore] // We don't want to change anything about the method... [PatchLoadingThreadAddEvent] // ... except for manually manipulating the method via MonoModRules [PatchLoadingThreadAddSubHudRenderer] + [PatchLoadingThreadForMapMetadata] private extern void LoadingThread(); private void LoadingThread_Safe() { @@ -325,6 +326,9 @@ public override void Update() { } } + private static bool _ShouldAddIceTileOverlay(MapData mapData) + => ((patch_MapData) mapData).Meta?.CoreModeIceTileOverlay ?? false; + } } @@ -366,6 +370,12 @@ class PatchLoadingThreadAddSubHudRendererAttribute : Attribute { } [MonoModCustomMethodAttribute(nameof(MonoModRules.PatchLoadingThreadAddEvent))] class PatchLoadingThreadAddEventAttribute : Attribute { } + /// + /// Handles map metadata properties in some places throughout the loading thread. + /// + [MonoModCustomMethodAttribute(nameof(MonoModRules.PatchLoadingThreadForMapMetadata))] + class PatchLoadingThreadForMapMetadataAttribute : Attribute { } + static partial class MonoModRules { public static void PatchLevelLoaderOrigCtor(ILContext context, CustomAttribute attrib) { @@ -431,5 +441,40 @@ public static void PatchLoadingThreadAddEvent(ILContext context, CustomAttribute cursor.Emit(OpCodes.Call, m_Everest_Events_LevelLoader_LoadingThread); } + public static void PatchLoadingThreadForMapMetadata(ILContext context, CustomAttribute attrib) { + MethodDefinition m_shouldAddIceTileOverlay = context.Method.DeclaringType.FindMethod("_ShouldAddIceTileOverlay"); + + ILCursor cursor = new(context); + + // first, get the local for MapData + int loc_mapData = -1; + cursor.GotoNext( + instr => instr.MatchCallvirt("Celeste.Session", "get_MapData"), + instr => instr.MatchStloc(out loc_mapData) + ); + + /* + now let's change this bit of code + if (session.Area.ID == 9) { ... } + to this + if (session.Area.ID == 9 || _ShouldAddIceTileOverlay(mapData)) { ... } + */ + ILLabel addIceTileOverlaybel = cursor.DefineLabel(); + ILLabel branchOutLabel = null; + cursor.GotoNext(instr => instr.MatchLdcI4(9)); + cursor.GotoNext(MoveType.Before, instr => instr.MatchBneUn(out branchOutLabel)); + + // for simplicity, we'll remove the bne.un.s, and replace it with a different branch instruction + cursor.Remove(); + + cursor.Emit(OpCodes.Beq_S, addIceTileOverlaybel); // beq.s <=> bne.un.s + + cursor.EmitLdloc(loc_mapData); + cursor.Emit(OpCodes.Call, m_shouldAddIceTileOverlay); + cursor.Emit(OpCodes.Brfalse_S, branchOutLabel); + + cursor.MarkLabel(addIceTileOverlaybel); + } + } }