diff --git a/Celeste.Mod.mm/Mod/Registry/DecalRegistryHandlers/StaticMoverDecalRegistryHandler.cs b/Celeste.Mod.mm/Mod/Registry/DecalRegistryHandlers/StaticMoverDecalRegistryHandler.cs index 5a59191c2..875f1da8f 100644 --- a/Celeste.Mod.mm/Mod/Registry/DecalRegistryHandlers/StaticMoverDecalRegistryHandler.cs +++ b/Celeste.Mod.mm/Mod/Registry/DecalRegistryHandlers/StaticMoverDecalRegistryHandler.cs @@ -4,6 +4,7 @@ namespace Celeste.Mod.Registry.DecalRegistryHandlers; internal sealed class StaticMoverDecalRegistryHandler : DecalRegistryHandler { private int _x, _y, _width, _height; + private bool _jumpThrus; public override string Name => "staticMover"; @@ -12,6 +13,7 @@ public override void Parse(XmlAttributeCollection xml) { _y = Get(xml, "y", 0); _width = Get(xml, "width", 16); _height = Get(xml, "height", 16); + _jumpThrus = GetBool(xml, "jumpThrus", false); } public override void ApplyTo(Decal decal) { @@ -19,6 +21,6 @@ public override void ApplyTo(Decal decal) { decal.ScaleRectangle(ref x, ref y, ref width, ref height); - ((patch_Decal)decal).MakeStaticMover(x, y, width, height); + ((patch_Decal)decal).MakeStaticMover(x, y, width, height, _jumpThrus); } } \ No newline at end of file diff --git a/Celeste.Mod.mm/Patches/Decal.cs b/Celeste.Mod.mm/Patches/Decal.cs index 03def2c59..d22799885 100644 --- a/Celeste.Mod.mm/Patches/Decal.cs +++ b/Celeste.Mod.mm/Patches/Decal.cs @@ -55,6 +55,8 @@ public Vector2 Scale { private StaticMover staticMover; + public List Solids => solids; + public patch_Decal(string texture, Vector2 position, Vector2 scale, int depth) : base(texture, position, scale, depth) { // no-op. MonoMod ignores this - we only need this to make the compiler shut up. @@ -231,15 +233,24 @@ public void MakeStaticMover(int x, int y, int w, int h, bool jumpThrus = false) SolidChecker = s => !solids.Contains(s) && s.CollideRect(new Rectangle((int) X + x, (int) Y + y, w, h)), OnDestroy = () => { RemoveSelf(); - solids.ForEach(s => s.RemoveSelf()); + solids.ForEach(s => { + s.DestroyStaticMovers(); + s.RemoveSelf(); + }); }, OnDisable = () => { Active = Visible = Collidable = false; - solids.ForEach(s => s.Collidable = false); + solids.ForEach(s => { + s.Collidable = false; + s.DisableStaticMovers(); + }); }, OnEnable = () => { Active = Visible = Collidable = true; - solids.ForEach(s => s.Collidable = true); + solids.ForEach(s => { + s.Collidable = true; + s.EnableStaticMovers(); + }); }, OnMove = v => { Position += v; @@ -249,17 +260,23 @@ public void MakeStaticMover(int x, int y, int w, int h, bool jumpThrus = false) s.MoveV(v.Y, liftSpeed.Y); }); }, - OnShake = v => Position += v, + OnShake = v => { + Position += v; + solids.ForEach(s => s.OnShake(v)); + }, OnAttach = p => { p.Add(new EntityRemovedListener(() => { RemoveSelf(); - solids.ForEach(s => s.RemoveSelf()); + solids.ForEach(s => { + s.DestroyStaticMovers(); + s.RemoveSelf(); + }); })); CoreModule.Session.AttachedDecals.Add(string.Format("{0}||{1}||{2}", Name, Position.X, Position.Y)); } }; if (jumpThrus) - staticMover.JumpThruChecker = s => s.CollideRect(new Rectangle((int) X + x, (int) X + y, w, h)); + staticMover.JumpThruChecker = s => s.CollideRect(new Rectangle((int) X + x, (int) Y + y, w, h)); Add(staticMover); } @@ -298,9 +315,12 @@ public void MakeOverlay() { public override void Awake(Scene scene) { base.Awake(scene); - if (staticMover?.Platform == null && CoreModule.Session.AttachedDecals.Contains(string.Format("{0}||{1}||{2}", Name, Position.X, Position.Y))) { - RemoveSelf(); - } + + ((patch_EntityList) (object) scene.Entities).PostAwake += () => { + if (staticMover?.Platform == null && CoreModule.Session.AttachedDecals.Contains(string.Format("{0}||{1}||{2}", Name, Position.X, Position.Y))) { + RemoveSelf(); + } + }; } public extern void orig_Added(Scene scene); diff --git a/Celeste.Mod.mm/Patches/Monocle/EntityList.cs b/Celeste.Mod.mm/Patches/Monocle/EntityList.cs index dc8c98a97..7ccd0ad45 100644 --- a/Celeste.Mod.mm/Patches/Monocle/EntityList.cs +++ b/Celeste.Mod.mm/Patches/Monocle/EntityList.cs @@ -11,6 +11,7 @@ using System; using System.Collections.Generic; using System.Linq; +using System.Threading; namespace Monocle { // No public constructors. @@ -25,6 +26,8 @@ class patch_EntityList { /// public List ToAdd => toAdd; + public event Action PostAwake; + internal void ClearEntities() { entities.Clear(); } @@ -53,6 +56,8 @@ private void _HandleEntityData(Entity entity) { // so this should be very rare in practice. } } + + private void _PostAwake() => Interlocked.Exchange(ref PostAwake, null)?.Invoke(); } public static class EntityListExt { @@ -107,6 +112,9 @@ public static void PatchEntityListUpdate(ILContext context, CustomAttribute attr } public static void PatchEntityListUpdateLists(ILContext context, CustomAttribute attrib) { + TypeDefinition t_EntityList = MonoModRule.Modder.FindType("Monocle.EntityList").Resolve(); + MethodDefinition m_PostAwake = t_EntityList.FindMethod("_PostAwake"); + ILCursor cursor = new ILCursor(context); // if (!current.Contains(entity)) { if (current.Add(entity)) { @@ -139,6 +147,11 @@ public static void PatchEntityListUpdateLists(ILContext context, CustomAttribute cursor.GotoPrev(instr => instr.OpCode == OpCodes.Callvirt && (instr.Operand as MethodReference).GetID().Contains("List`1::Contains")); cursor.Prev.Previous.Operand = currentOperand; cursor.Next.Operand = hashRemoveOperand; + + // at the end, call PostAwake + cursor.GotoNext(MoveType.AfterLabel, instr => instr.MatchRet()); + cursor.EmitLdarg0(); + cursor.EmitCallvirt(m_PostAwake); } public static void PatchEntityListAdd(ILContext context, CustomAttribute attrib) {