From 3a5f12b0e72237328db077ea5a0201f4c9252844 Mon Sep 17 00:00:00 2001 From: AAA1459 <72622056+AAA1459@users.noreply.github.com> Date: Fri, 23 Aug 2024 14:27:39 +0800 Subject: [PATCH 1/2] Make DeathEffect support transparent colors and... ...custom texture --- Celeste.Mod.mm/Patches/DeathEffect.cs | 98 +++++++++++++++++++++++++-- 1 file changed, 91 insertions(+), 7 deletions(-) diff --git a/Celeste.Mod.mm/Patches/DeathEffect.cs b/Celeste.Mod.mm/Patches/DeathEffect.cs index f8d0a8fc6..febfdfe8c 100644 --- a/Celeste.Mod.mm/Patches/DeathEffect.cs +++ b/Celeste.Mod.mm/Patches/DeathEffect.cs @@ -1,24 +1,50 @@ -using System; +using System; +using System.Xml; using Microsoft.Xna.Framework; +using Microsoft.Xna.Framework.Graphics; using Mono.Cecil; using Mono.Cecil.Cil; +using Monocle; using MonoMod; -using MonoMod.Cil; +using MonoMod.Cil; +using MonoMod.InlineRT; +using MonoMod.Utils; namespace Celeste { class patch_DeathEffect : DeathEffect { + // no-op. MonoMod ignores this - we only need this to make the compiler shut up. public patch_DeathEffect(Color color, Vector2 offset) : base(color, offset) { } + + public MTexture Texture; + + /// + /// a temporary texture used to the next DeathEffect.Draw get call + /// + public static MTexture _texture; + [MonoModIgnore] [PatchDeathEffectUpdate] public override extern void Update(); [MonoModReplace] public override void Render() { - if (Entity != null) - Draw(Entity.Position + Position, Color, Percent); + if (Entity != null) { + _texture = Texture; + Draw(Entity.Position + Position, Color, Percent); + } + } + + [MonoModIgnore] + [PatchDeathEffectDraw] + public static new void Draw(Vector2 position, Color color, float ease) { } + + // Because of the auto naming rules of .Hook, we shouldn't name this method same as 'Draw' + public static void DrawWithTexture(MTexture texture, Vector2 position, Color color, float ease) { + _texture = texture; + Draw(position, color, ease); } } } @@ -28,15 +54,73 @@ namespace MonoMod { /// Patch DeathEffect.Update to fix death effects never get removed /// [MonoModCustomMethodAttribute(nameof(MonoModRules.PatchDeathEffectUpdate))] - class PatchDeathEffectUpdateAttribute : Attribute { } + class PatchDeathEffectUpdateAttribute : Attribute { } + + [MonoModCustomMethodAttribute(nameof(MonoModRules.PatchDeathEffectDraw))] + class PatchDeathEffectDrawAttribute : Attribute { + } static partial class MonoModRules { - public static void PatchDeathEffectUpdate(ILContext context, CustomAttribute attrib) { ILCursor cursor = new ILCursor(context); cursor.GotoNext(instr => instr.OpCode == OpCodes.Ble_Un_S); cursor.Next.OpCode = OpCodes.Blt_Un_S; - } + } + + public static void PatchDeathEffectDraw(ILContext context, CustomAttribute attrib) { + TypeDefinition t_DeathEffect = MonoModRule.Modder.Module.GetType("Celeste.DeathEffect"); + FieldReference m_DeathEffect_texture = t_DeathEffect.FindField("_texture"); + + TypeReference t_Color = MonoModRule.Modder.Module.ImportReference(MonoModRule.Modder.FindType("Microsoft.Xna.Framework.Color").Resolve()); + MethodReference m_Color_get_Black = MonoModRule.Modder.Module.ImportReference(t_Color.Resolve().FindProperty("Black").GetMethod); + VariableDefinition v_outline = new VariableDefinition(t_Color); + context.Body.Variables.Add(v_outline); + + MethodReference m_Color_get_A = MonoModRule.Modder.Module.ImportReference(t_Color.Resolve().FindProperty("A").GetMethod); + MethodReference m_Color_op_Multiply = MonoModRule.Modder.Module.ImportReference(t_Color.Resolve().FindMethod("op_Multiply")); + + ILCursor cursor = new ILCursor(context); + + // Color outline = Color.Black * (this.Color.A / 255f); + cursor.EmitCall(m_Color_get_Black); + cursor.EmitLdarga(1); + cursor.EmitCall(m_Color_get_A); + cursor.EmitConvR4(); + cursor.EmitLdcR4(255f); + cursor.EmitDiv(); + cursor.EmitCall(m_Color_op_Multiply); + cursor.EmitStloc(v_outline); + + // Color.White => Color.White * (this.Color.A / 255f); + cursor.GotoNext(MoveType.After, instr => instr.MatchCall("Microsoft.Xna.Framework.Color", "get_White")); + cursor.EmitLdarga(1); + cursor.EmitCall(m_Color_get_A); + cursor.EmitConvR4(); + cursor.EmitLdcR4(255f); + cursor.EmitDiv(); + cursor.EmitCall(m_Color_op_Multiply); + + // GFX.Game["characters/player/hair00"] => (_texture ?? GFX.Game["characters/player/hair00"]); + ILLabel Ifnull = cursor.DefineLabel(); + cursor.GotoNext(MoveType.AfterLabel, instr => instr.MatchLdsfld("Celeste.GFX", "Game")); + cursor.EmitLdsfld(m_DeathEffect_texture); + cursor.EmitDup(); + cursor.Emit(OpCodes.Brtrue_S, Ifnull); + cursor.EmitPop(); + cursor.GotoNext(instr => instr.MatchStloc(out int i)); + cursor.MarkLabel(Ifnull); + // Color.Black => outline; + for (int i = 4; i > 0; i--) { + cursor.GotoNext(instr => instr.MatchCall("Microsoft.Xna.Framework.Color", "get_Black")); + cursor.Remove(); + cursor.EmitLdloc(v_outline); + } + + // _texture = null; + cursor.GotoNext(instr => instr.MatchRet()); + cursor.EmitLdnull(); + cursor.EmitStsfld(m_DeathEffect_texture); + } } } From f526a376091c925b3f30e06a5ea4191bf0b198c3 Mon Sep 17 00:00:00 2001 From: AAA1459 <72622056+AAA1459@users.noreply.github.com> Date: Tue, 10 Sep 2024 10:21:09 +0800 Subject: [PATCH 2/2] change _texture to a private field --- Celeste.Mod.mm/Patches/DeathEffect.cs | 130 +++++++++++++------------- 1 file changed, 65 insertions(+), 65 deletions(-) diff --git a/Celeste.Mod.mm/Patches/DeathEffect.cs b/Celeste.Mod.mm/Patches/DeathEffect.cs index febfdfe8c..9cd254eaa 100644 --- a/Celeste.Mod.mm/Patches/DeathEffect.cs +++ b/Celeste.Mod.mm/Patches/DeathEffect.cs @@ -1,13 +1,13 @@ -using System; -using System.Xml; +using System; +using System.Xml; using Microsoft.Xna.Framework; -using Microsoft.Xna.Framework.Graphics; +using Microsoft.Xna.Framework.Graphics; using Mono.Cecil; using Mono.Cecil.Cil; -using Monocle; +using Monocle; using MonoMod; -using MonoMod.Cil; -using MonoMod.InlineRT; +using MonoMod.Cil; +using MonoMod.InlineRT; using MonoMod.Utils; namespace Celeste { @@ -18,12 +18,12 @@ public patch_DeathEffect(Color color, Vector2 offset) : base(color, offset) { } - public MTexture Texture; - + public MTexture Texture; + /// /// a temporary texture used to the next DeathEffect.Draw get call - /// - public static MTexture _texture; + /// + private static MTexture _texture; [MonoModIgnore] [PatchDeathEffectUpdate] @@ -33,17 +33,17 @@ public patch_DeathEffect(Color color, Vector2 offset) public override void Render() { if (Entity != null) { _texture = Texture; - Draw(Entity.Position + Position, Color, Percent); + Draw(Entity.Position + Position, Color, Percent); } - } - - [MonoModIgnore] - [PatchDeathEffectDraw] - public static new void Draw(Vector2 position, Color color, float ease) { } - - // Because of the auto naming rules of .Hook, we shouldn't name this method same as 'Draw' - public static void DrawWithTexture(MTexture texture, Vector2 position, Color color, float ease) { - _texture = texture; + } + + [MonoModIgnore] + [PatchDeathEffectDraw] + public static new void Draw(Vector2 position, Color color, float ease) { } + + // Because of the auto naming rules of .Hook, we shouldn't name this method same as 'Draw' + public static void DrawWithTexture(MTexture texture, Vector2 position, Color color, float ease) { + _texture = texture; Draw(position, color, ease); } } @@ -54,10 +54,10 @@ namespace MonoMod { /// Patch DeathEffect.Update to fix death effects never get removed /// [MonoModCustomMethodAttribute(nameof(MonoModRules.PatchDeathEffectUpdate))] - class PatchDeathEffectUpdateAttribute : Attribute { } - - [MonoModCustomMethodAttribute(nameof(MonoModRules.PatchDeathEffectDraw))] - class PatchDeathEffectDrawAttribute : Attribute { + class PatchDeathEffectUpdateAttribute : Attribute { } + + [MonoModCustomMethodAttribute(nameof(MonoModRules.PatchDeathEffectDraw))] + class PatchDeathEffectDrawAttribute : Attribute { } static partial class MonoModRules { @@ -65,56 +65,56 @@ public static void PatchDeathEffectUpdate(ILContext context, CustomAttribute att ILCursor cursor = new ILCursor(context); cursor.GotoNext(instr => instr.OpCode == OpCodes.Ble_Un_S); cursor.Next.OpCode = OpCodes.Blt_Un_S; - } - - public static void PatchDeathEffectDraw(ILContext context, CustomAttribute attrib) { - TypeDefinition t_DeathEffect = MonoModRule.Modder.Module.GetType("Celeste.DeathEffect"); + } + + public static void PatchDeathEffectDraw(ILContext context, CustomAttribute attrib) { + TypeDefinition t_DeathEffect = MonoModRule.Modder.Module.GetType("Celeste.DeathEffect"); FieldReference m_DeathEffect_texture = t_DeathEffect.FindField("_texture"); - TypeReference t_Color = MonoModRule.Modder.Module.ImportReference(MonoModRule.Modder.FindType("Microsoft.Xna.Framework.Color").Resolve()); - MethodReference m_Color_get_Black = MonoModRule.Modder.Module.ImportReference(t_Color.Resolve().FindProperty("Black").GetMethod); + TypeReference t_Color = MonoModRule.Modder.Module.ImportReference(MonoModRule.Modder.FindType("Microsoft.Xna.Framework.Color").Resolve()); + MethodReference m_Color_get_Black = MonoModRule.Modder.Module.ImportReference(t_Color.Resolve().FindProperty("Black").GetMethod); VariableDefinition v_outline = new VariableDefinition(t_Color); - context.Body.Variables.Add(v_outline); - - MethodReference m_Color_get_A = MonoModRule.Modder.Module.ImportReference(t_Color.Resolve().FindProperty("A").GetMethod); - MethodReference m_Color_op_Multiply = MonoModRule.Modder.Module.ImportReference(t_Color.Resolve().FindMethod("op_Multiply")); - - ILCursor cursor = new ILCursor(context); - - // Color outline = Color.Black * (this.Color.A / 255f); - cursor.EmitCall(m_Color_get_Black); - cursor.EmitLdarga(1); - cursor.EmitCall(m_Color_get_A); - cursor.EmitConvR4(); - cursor.EmitLdcR4(255f); - cursor.EmitDiv(); - cursor.EmitCall(m_Color_op_Multiply); - cursor.EmitStloc(v_outline); - - // Color.White => Color.White * (this.Color.A / 255f); - cursor.GotoNext(MoveType.After, instr => instr.MatchCall("Microsoft.Xna.Framework.Color", "get_White")); - cursor.EmitLdarga(1); - cursor.EmitCall(m_Color_get_A); - cursor.EmitConvR4(); - cursor.EmitLdcR4(255f); - cursor.EmitDiv(); - cursor.EmitCall(m_Color_op_Multiply); - - // GFX.Game["characters/player/hair00"] => (_texture ?? GFX.Game["characters/player/hair00"]); - ILLabel Ifnull = cursor.DefineLabel(); - cursor.GotoNext(MoveType.AfterLabel, instr => instr.MatchLdsfld("Celeste.GFX", "Game")); - cursor.EmitLdsfld(m_DeathEffect_texture); - cursor.EmitDup(); + context.Body.Variables.Add(v_outline); + + MethodReference m_Color_get_A = MonoModRule.Modder.Module.ImportReference(t_Color.Resolve().FindProperty("A").GetMethod); + MethodReference m_Color_op_Multiply = MonoModRule.Modder.Module.ImportReference(t_Color.Resolve().FindMethod("op_Multiply")); + + ILCursor cursor = new ILCursor(context); + + // Color outline = Color.Black * (this.Color.A / 255f); + cursor.EmitCall(m_Color_get_Black); + cursor.EmitLdarga(1); + cursor.EmitCall(m_Color_get_A); + cursor.EmitConvR4(); + cursor.EmitLdcR4(255f); + cursor.EmitDiv(); + cursor.EmitCall(m_Color_op_Multiply); + cursor.EmitStloc(v_outline); + + // Color.White => Color.White * (this.Color.A / 255f); + cursor.GotoNext(MoveType.After, instr => instr.MatchCall("Microsoft.Xna.Framework.Color", "get_White")); + cursor.EmitLdarga(1); + cursor.EmitCall(m_Color_get_A); + cursor.EmitConvR4(); + cursor.EmitLdcR4(255f); + cursor.EmitDiv(); + cursor.EmitCall(m_Color_op_Multiply); + + // GFX.Game["characters/player/hair00"] => (_texture ?? GFX.Game["characters/player/hair00"]); + ILLabel Ifnull = cursor.DefineLabel(); + cursor.GotoNext(MoveType.AfterLabel, instr => instr.MatchLdsfld("Celeste.GFX", "Game")); + cursor.EmitLdsfld(m_DeathEffect_texture); + cursor.EmitDup(); cursor.Emit(OpCodes.Brtrue_S, Ifnull); cursor.EmitPop(); cursor.GotoNext(instr => instr.MatchStloc(out int i)); cursor.MarkLabel(Ifnull); // Color.Black => outline; - for (int i = 4; i > 0; i--) { - cursor.GotoNext(instr => instr.MatchCall("Microsoft.Xna.Framework.Color", "get_Black")); - cursor.Remove(); - cursor.EmitLdloc(v_outline); + for (int i = 4; i > 0; i--) { + cursor.GotoNext(instr => instr.MatchCall("Microsoft.Xna.Framework.Color", "get_Black")); + cursor.Remove(); + cursor.EmitLdloc(v_outline); } // _texture = null;