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;