Skip to content
Open
Show file tree
Hide file tree
Changes from 9 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
54 changes: 54 additions & 0 deletions Celeste.Mod.mm/Patches/Level.cs
Original file line number Diff line number Diff line change
Expand Up @@ -106,6 +106,24 @@ public static void RegisterLoadOverride(Level level, LoadOverride loadOverride)
[PatchLevelUpdate] // ... except for manually manipulating the method via MonoModRules
public extern new void Update();

// ... this is gonna be fun
[MonoModIgnore] // don't put this in `Level` when we're done
internal extern static void base_FreezeFrameUpdate(); // dummy method, will be replaced with the actual `base.FreezeFrameUpdate` call in the IL patch
[PatchLevelFreezeFrameUpdate] // add the `virtual` flag to this method so it overrides the one in `patch_Scene` properly and calls `base.FreezeFrameUpdate`
// todo: does there need to be anything else in here?
public void FreezeFrameUpdate() {
Everest.Events.Level.BeforeFreezeFrameUpdate(this);

base_FreezeFrameUpdate();

foreach (PostUpdateHook component in Tracker.GetComponents<PostUpdateHook>()) {
if (component.Entity.Active && component.Entity.TagCheck(TagsExt.FreezeFrameUpdate)) {
component.OnPostUpdate();
}
}
Everest.Events.Level.AfterFreezeFrameUpdate(this);
}
Comment thread
aonkeeper4 marked this conversation as resolved.

/// <summary>
/// Flash the screen a solid color. Respects the user's advanced photosensitivity settings.
/// </summary>
Expand Down Expand Up @@ -797,6 +815,20 @@ internal static void BeforeUpdate(_Level level)
public static event Action<_Level> OnAfterUpdate;
internal static void AfterUpdate(_Level level)
=> OnAfterUpdate?.Invoke(level);

/// <summary>
/// Called at the very beginning of <see cref="patch_Level.FreezeFrameUpdate"/>.
/// </summary>
public static event Action<_Level> OnBeforeFreezeFrameUpdate;
internal static void BeforeFreezeFrameUpdate(_Level level)
=> OnBeforeFreezeFrameUpdate?.Invoke(level);

/// <summary>
/// Called at the very end of <see cref="patch_Level.FreezeFrameUpdate"/>.
/// </summary>
public static event Action<_Level> OnAfterFreezeFrameUpdate;
internal static void AfterFreezeFrameUpdate(_Level level)
=> OnAfterFreezeFrameUpdate?.Invoke(level);
}
}
}
Expand All @@ -820,6 +852,12 @@ class PatchLevelLoaderDecalCreationAttribute : Attribute { }
/// </summary>
[MonoModCustomMethodAttribute(nameof(MonoModRules.PatchLevelUpdate))]
class PatchLevelUpdateAttribute : Attribute { }

/// <summary>
/// Patch our <see cref="Celeste.patch_Level.FreezeFrameUpdate"/> to be marked as <c>virtual</c> so it actually overrides the base method, and to actually call the base method instead of the dummy.
/// </summary>
[MonoModCustomMethodAttribute(nameof(MonoModRules.PatchLevelFreezeFrameUpdate))]
class PatchLevelFreezeFrameUpdateAttribute : Attribute { }

/// <summary>
/// Patch the Godzilla-sized level rendering method instead of reimplementing it in Everest.
Expand Down Expand Up @@ -1165,6 +1203,22 @@ ldc.i4.s 9
}
}

public static void PatchLevelFreezeFrameUpdate(ILContext context, CustomAttribute attrib) {
// mark the method as `virtual` so it registers as an override
context.Method.IsVirtual = true;

TypeDefinition t_Scene = context.Method.DeclaringType.BaseType.Resolve();
MethodReference m_Scene_FreezeFrameUpdate = t_Scene.FindMethod("FreezeFrameUpdate");

ILCursor cursor = new ILCursor(context);

// replace the dummy method with the actual call to `base.FreezeFrameUpdate`
cursor.GotoNext(MoveType.Before, instr => instr.MatchCall("Celeste.Level", "base_FreezeFrameUpdate"));
cursor.Remove();
cursor.EmitLdarg0();
cursor.EmitCall(m_Scene_FreezeFrameUpdate);
}

public static void PatchLevelRender(ILContext context, CustomAttribute attrib) {
FieldDefinition f_SubHudRenderer = context.Method.DeclaringType.FindField("SubHudRenderer");

Expand Down
13 changes: 13 additions & 0 deletions Celeste.Mod.mm/Patches/Monocle/Engine.cs
Original file line number Diff line number Diff line change
Expand Up @@ -208,6 +208,8 @@ public static void PatchEngineUpdate(ILContext context, CustomAttribute attrib)
MethodReference m_GetTimeRateComponentMultiplier = t_Engine.FindMethod("GetTimeRateComponentMultiplier");
VariableDefinition v_componentTimeRate = new(context.Import(typeof(float)));
context.Method.Body.Variables.Add(v_componentTimeRate);
TypeDefinition t_Scene = f_scene.FieldType.Resolve();
MethodReference m_Scene_FreezeFrameUpdate = t_Scene.FindMethod("FreezeFrameUpdate");

ILCursor cursor = new ILCursor(context);
cursor.GotoNext(
Expand All @@ -234,6 +236,17 @@ public static void PatchEngineUpdate(ILContext context, CustomAttribute attrib)
instr => instr.MatchMul());
cursor.EmitLdloc(v_componentTimeRate);
cursor.EmitMul();

cursor.GotoNext(MoveType.Before,
instr => instr.MatchLdsfld("Monocle.Engine", "FreezeTimer"),
instr => instr.MatchCall("Monocle.Engine", "get_RawDeltaTime"),
instr => instr.MatchSub(),
instr => instr.MatchLdcR4(0f),
instr => instr.MatchCall("System.Math", "Max"),
instr => instr.MatchStsfld("Monocle.Engine", "FreezeTimer"));
cursor.EmitLdarg0();
cursor.EmitLdfld(f_scene);
cursor.EmitCallvirt(m_Scene_FreezeFrameUpdate);
Comment thread
aonkeeper4 marked this conversation as resolved.
}

public static void PatchEngineCctor(ILContext context, CustomAttribute attrib) {
Expand Down
20 changes: 19 additions & 1 deletion Celeste.Mod.mm/Patches/Monocle/Scene.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Celeste.Mod.Registry;
using Celeste;
using Celeste.Mod.Registry;
using MonoMod;
using System;
using System.Collections.Generic;
Expand Down Expand Up @@ -48,5 +49,22 @@ public IEnumerable<Entity> FindEntitiesWithSid(string sid) {
public new virtual void AfterUpdate() {
Interlocked.Exchange(ref OnEndOfFrame, null)?.Invoke();
}

/// <summary>
/// Called during freeze frames instead of the normal <see cref="Scene.Update"/>.
/// </summary>
// todo: allow some renderers to update?
public virtual void FreezeFrameUpdate() {
if (!Paused) {
foreach (patch_Entity entity in this[TagsExt.FreezeFrameUpdate]) {
entity._PreUpdate();
if (entity.Active)
{
entity.Update();
}
entity._PostUpdate();
Comment thread
aonkeeper4 marked this conversation as resolved.
Outdated
}
}
}
}
}
5 changes: 5 additions & 0 deletions Celeste.Mod.mm/Patches/Tags.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ class patch_Tags {
public static void Initialize() {
orig_Initialize();
TagsExt.SubHUD = new BitTag("subHUD");
TagsExt.FreezeFrameUpdate = new BitTag("freezeFrameUpdate");
}

}
Expand All @@ -20,5 +21,9 @@ public static class TagsExt {
/// </summary>
public static BitTag SubHUD;

/// <summary>
/// Tag to be used for entities that should update during freeze frames.
Comment thread
aonkeeper4 marked this conversation as resolved.
Outdated
/// </summary>
public static BitTag FreezeFrameUpdate;
}
}
Loading