diff --git a/Celeste.Mod.mm/Mod/Everest/Extensions.cs b/Celeste.Mod.mm/Mod/Everest/Extensions.cs index a9d62e73e..93188da3e 100644 --- a/Celeste.Mod.mm/Mod/Everest/Extensions.cs +++ b/Celeste.Mod.mm/Mod/Everest/Extensions.cs @@ -417,6 +417,14 @@ public static int AttrInt(this BinaryPacker.Element el, string name, int default return int.Parse(obj.ToString(), CultureInfo.InvariantCulture); } + public static float? AttrNullableFloat(this BinaryPacker.Element el, string name) { + if (el.Attributes == null || !el.Attributes.TryGetValue(name, out object obj)) + return null; + if (obj is float v) + return v; + return float.Parse(obj.ToString(), CultureInfo.InvariantCulture); + } + public static bool AttrRef(this BinaryPacker.Element el, string name, ref string value) { if (el.HasAttr(name)) { value = el.Attr(name); diff --git a/Celeste.Mod.mm/Mod/Registry/DecalRegistryHandlers/ParallaxDecalRegistryHandler.cs b/Celeste.Mod.mm/Mod/Registry/DecalRegistryHandlers/ParallaxDecalRegistryHandler.cs index e6c24c7e1..07fa858b9 100644 --- a/Celeste.Mod.mm/Mod/Registry/DecalRegistryHandlers/ParallaxDecalRegistryHandler.cs +++ b/Celeste.Mod.mm/Mod/Registry/DecalRegistryHandlers/ParallaxDecalRegistryHandler.cs @@ -1,4 +1,4 @@ -using System.Globalization; +using System.Globalization; using System.Xml; namespace Celeste.Mod.Registry.DecalRegistryHandlers; diff --git a/Celeste.Mod.mm/Patches/Decal.cs b/Celeste.Mod.mm/Patches/Decal.cs index 97bf09d0e..fdc8fb989 100644 --- a/Celeste.Mod.mm/Patches/Decal.cs +++ b/Celeste.Mod.mm/Patches/Decal.cs @@ -37,7 +37,11 @@ public Vector2 Scale { public Color Color; #pragma warning disable CS0649 + private bool parallax; + private float parallaxAmount; + public bool DepthSetByPlacement; + public bool ParallaxSetByPlacement; #pragma warning restore CS0649 private bool scaredAnimal; @@ -148,9 +152,14 @@ public void ctor(string texture, Vector2 position, Vector2 scale, int depth, flo ctor(texture, position, scale, depth, rotation, patch_Calc.HexToColorWithAlpha(color_hex)); } - [MonoModIgnore] + [MonoModReplace] [MonoModPublic] - public extern void MakeParallax(float amount); + public void MakeParallax(float amount) { + if (ParallaxSetByPlacement) return; + + parallax = (amount != 0f); + parallaxAmount = amount; + } [MonoModIgnore] [MonoModPublic] diff --git a/Celeste.Mod.mm/Patches/DecalData.cs b/Celeste.Mod.mm/Patches/DecalData.cs index 24df5a6c9..ac4208518 100644 --- a/Celeste.Mod.mm/Patches/DecalData.cs +++ b/Celeste.Mod.mm/Patches/DecalData.cs @@ -1,19 +1,24 @@ #pragma warning disable CS0649 // The field is never assigned and will always be null +using System; + namespace Celeste { class patch_DecalData : DecalData { public float Rotation; public string ColorHex; public int? Depth; + public float? Parallax; + + // The following methods are used by MonoModRules.PatchLevelLoaderDecalCreation to avoid having to deal with generics - public int GetDepth(int fallback) { - return Depth ?? fallback; - } + [Obsolete("Everest implementation detail; should not be used by mods. Instead, directly use the Depth field.")] + public bool HasDepth() => Depth.HasValue; + [Obsolete("Everest implementation detail; should not be used by mods. Instead, directly use the Depth field.")] + public int GetDepth(int fallback) => Depth ?? fallback; - public bool HasDepth() { - return Depth.HasValue; - } + internal bool HasParallax() => Parallax.HasValue; + internal float GetParallax() => Parallax ?? 0f; } } diff --git a/Celeste.Mod.mm/Patches/Level.cs b/Celeste.Mod.mm/Patches/Level.cs index 48c80f4f6..bdac5ca58 100644 --- a/Celeste.Mod.mm/Patches/Level.cs +++ b/Celeste.Mod.mm/Patches/Level.cs @@ -916,11 +916,16 @@ public static void PatchLevelLoaderDecalCreation(ILContext context, CustomAttrib FieldDefinition f_DecalData_Depth = t_DecalData.FindField("Depth"); FieldDefinition f_Decal_DepthSetByPlacement = t_Decal.FindField("DepthSetByPlacement"); + FieldDefinition f_Decal_ParallaxSetByPlacement = t_Decal.FindField("ParallaxSetByPlacement"); MethodDefinition m_DecalData_HasDepth = t_DecalData.FindMethod("HasDepth"); MethodDefinition m_DecalData_GetDepth = t_DecalData.FindMethod("GetDepth"); - MethodDefinition m_Decal_ctor = t_Decal.FindMethod("System.Void .ctor(System.String,Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Vector2,System.Int32,System.Single,System.String)"); + MethodDefinition m_DecalData_HasParallax = t_DecalData.FindMethod("HasParallax"); + MethodDefinition m_DecalData_GetParallax = t_DecalData.FindMethod("GetParallax"); + + MethodDefinition m_Decal_ctor = t_Decal.FindMethod("System.Void .ctor(System.String,Microsoft.Xna.Framework.Vector2,Microsoft.Xna.Framework.Vector2,System.Int32,System.Single,System.String)"); + MethodDefinition m_Decal_MakeParallax = t_Decal.FindMethod("MakeParallax"); ILCursor cursor = new ILCursor(context); @@ -948,16 +953,32 @@ public static void PatchLevelLoaderDecalCreation(ILContext context, CustomAttrib cursor.Emit(OpCodes.Newobj, m_Decal_ctor); cursor.Remove(); - // if the depth was set in the DecalData... - ILLabel after_set = cursor.DefineLabel(); + // if the depth was set in the DecalData (and therefore is already set on the Decal)... + ILLabel after_set_depth = cursor.DefineLabel(); cursor.Emit(OpCodes.Ldloc_S, (byte) loc_decaldata); cursor.Emit(OpCodes.Call, m_DecalData_HasDepth); - cursor.Emit(OpCodes.Brfalse_S, after_set); + cursor.Emit(OpCodes.Brfalse_S, after_set_depth); // store that information in the Decal cursor.Emit(OpCodes.Dup); cursor.Emit(OpCodes.Ldc_I4_1); cursor.Emit(OpCodes.Stfld, f_Decal_DepthSetByPlacement); - cursor.MarkLabel(after_set); + cursor.MarkLabel(after_set_depth); + + // if the parallax was set in the DecalData (but not yet on the Decal)... + ILLabel after_set_parallax = cursor.DefineLabel(); + cursor.Emit(OpCodes.Ldloc_S, (byte) loc_decaldata); + cursor.Emit(OpCodes.Call, m_DecalData_HasParallax); + cursor.Emit(OpCodes.Brfalse_S, after_set_parallax); + // set the parallax... + cursor.Emit(OpCodes.Dup); + cursor.Emit(OpCodes.Ldloc_S, (byte) loc_decaldata); + cursor.Emit(OpCodes.Call, m_DecalData_GetParallax); + cursor.Emit(OpCodes.Call, m_Decal_MakeParallax); + // ...and finally the fact that the parallax was set here + cursor.Emit(OpCodes.Dup); + cursor.Emit(OpCodes.Ldc_I4_1); + cursor.Emit(OpCodes.Stfld, f_Decal_ParallaxSetByPlacement); + cursor.MarkLabel(after_set_parallax); matches++; } diff --git a/Celeste.Mod.mm/Patches/LevelData.cs b/Celeste.Mod.mm/Patches/LevelData.cs index 734839082..5557b5d97 100644 --- a/Celeste.Mod.mm/Patches/LevelData.cs +++ b/Celeste.Mod.mm/Patches/LevelData.cs @@ -167,14 +167,16 @@ public static void PatchLevelDataDecalLoader(ILContext context, CustomAttribute TypeDefinition t_BinaryPackerElement = MonoModRule.Modder.FindType("Celeste.BinaryPacker/Element").Resolve(); TypeDefinition t_Extensions = MonoModRule.Modder.FindType("Celeste.Mod.Extensions").Resolve(); - MethodDefinition m_BinaryPackerElementHasAttr = t_BinaryPackerElement.FindMethod("HasAttr"); - MethodDefinition m_BinaryPackerElementAttr = t_BinaryPackerElement.FindMethod("Attr"); - MethodDefinition m_BinaryPackerElementAttrFloat = t_BinaryPackerElement.FindMethod("AttrFloat"); - MethodDefinition m_BinaryPackerElementAttrNullableInt = t_Extensions.FindMethod("AttrNullableInt"); + MethodDefinition m_BinaryPackerElementHasAttr = t_BinaryPackerElement.FindMethod("HasAttr"); + MethodDefinition m_BinaryPackerElementAttr = t_BinaryPackerElement.FindMethod("Attr"); + MethodDefinition m_BinaryPackerElementAttrFloat = t_BinaryPackerElement.FindMethod("AttrFloat"); + MethodDefinition m_BinaryPackerElementAttrNullableInt = t_Extensions.FindMethod("AttrNullableInt"); + MethodDefinition m_BinaryPackerElementAttrNullableFloat = t_Extensions.FindMethod("AttrNullableFloat"); FieldDefinition f_DecalDataRotation = t_DecalData.FindField("Rotation"); FieldDefinition f_DecalDataColorHex = t_DecalData.FindField("ColorHex"); FieldDefinition f_DecalDataDepth = t_DecalData.FindField("Depth"); + FieldDefinition f_DecalDataParallax = t_DecalData.FindField("Parallax"); ILCursor cursor = new ILCursor(context); @@ -191,7 +193,9 @@ public static void PatchLevelDataDecalLoader(ILContext context, CustomAttribute // decaldata.Rotation = element.AttrFloat("rotation", 0.0f); // decaldata.ColorHex = element.AttrString("color", ""); // if (element.HasAttr("depth")) - // decaldata.Depth = element.AttrNullableInt("depth"); + // decaldata.Depth = element.AttrNullableInt("depth"); + // if (element.HasAttr("parallax")) + // decaldata.Parallax = element.AttrNullableFloat("parallax"); // copy the reference to the DecalData cursor.Emit(OpCodes.Dup); @@ -232,6 +236,25 @@ public static void PatchLevelDataDecalLoader(ILContext context, CustomAttribute cursor.MarkLabel(after_attr_depth); + // find out if there is a parallax field in the BinaryPacker.Element + cursor.Emit(OpCodes.Ldloc, loc_element); + cursor.Emit(OpCodes.Ldstr, "parallax"); + cursor.Emit(OpCodes.Call, m_BinaryPackerElementHasAttr); + // if not, skip to after setting it + ILLabel after_attr_parallax = cursor.DefineLabel(); + cursor.Emit(OpCodes.Brfalse_S, after_attr_parallax); + + // copy the reference to the DecalData again + cursor.Emit(OpCodes.Dup); + // load the parallax from the BinaryPacker.Element + cursor.Emit(OpCodes.Ldloc, loc_element); + cursor.Emit(OpCodes.Ldstr, "parallax"); + cursor.Emit(OpCodes.Call, m_BinaryPackerElementAttrNullableFloat); + // put the parallax into the DecalData + cursor.Emit(OpCodes.Stfld, f_DecalDataParallax); + + cursor.MarkLabel(after_attr_parallax); + matches++; } if (matches != 2) {