From 7553544c40006bffea21a16b91b1d0440d0049d8 Mon Sep 17 00:00:00 2001 From: SilverDorian46 <86711559+SilverDorian46@users.noreply.github.com> Date: Thu, 19 Dec 2024 18:11:28 +0400 Subject: [PATCH 1/3] Implement soundParam tileset attribute --- Celeste.Mod.mm/MonoModRules.Game.cs | 10 ++++++++-- Celeste.Mod.mm/Patches/Autotiler.cs | 16 +++++++++++++++- Celeste.Mod.mm/Patches/LevelLoader.cs | 3 +++ Celeste.Mod.mm/Patches/Player.cs | 12 ++++++++++-- Celeste.Mod.mm/Patches/SurfaceIndex.cs | 11 ++++++++++- 5 files changed, 46 insertions(+), 6 deletions(-) diff --git a/Celeste.Mod.mm/MonoModRules.Game.cs b/Celeste.Mod.mm/MonoModRules.Game.cs index 064af6cc8..9135449ad 100644 --- a/Celeste.Mod.mm/MonoModRules.Game.cs +++ b/Celeste.Mod.mm/MonoModRules.Game.cs @@ -295,6 +295,7 @@ public static void PatchConfigUIUpdate(ILContext il, CustomAttribute attrib) { private static void PatchPlaySurfaceIndex(ILCursor cursor, string name) { MethodDefinition m_SurfaceIndex_GetPathFromIndex = cursor.Module.GetType("Celeste.SurfaceIndex").FindMethod("System.String GetPathFromIndex(System.Int32)"); + MethodDefinition m_SurfaceIndex_GetSoundParamFromIndex = cursor.Module.GetType("Celeste.SurfaceIndex").FindMethod("System.Int32 GetSoundParamFromIndex(System.Int32)"); MethodReference m_String_Concat = MonoModRule.Modder.Module.ImportReference( MonoModRule.Modder.FindType("System.String").Resolve() .FindMethod("System.String Concat(System.String,System.String)") @@ -313,12 +314,13 @@ private static void PatchPlaySurfaceIndex(ILCursor cursor, string name) { /* Change: Play("event:/char/madeline{$name}", "surface_index", platformByPriority.GetStepSoundIndex(this)); to: - Play(SurfaceIndex.GetPathFromIndex(platformByPriority.GetStepSoundIndex(this)) + $name, "surface_index", platformByPriority.GetStepSoundIndex(this)); + Play(SurfaceIndex.GetPathFromIndex(platformByPriority.GetStepSoundIndex(this)) + $name, "surface_index", + SurfaceIndex.GetSoundParamFromIndex(platformByPriority.GetStepSoundIndex(this))); OR Change (for walls): Play("event:/char/madeline/{$name}", "surface_index", platformByPriority.GetWallSoundIndex(this, (int)Facing)); to: Play(SurfaceIndex.GetPathFromIndex(platformByPriority.GetWallSoundIndex(this, (int)Facing)) + $name, "surface_index", - platformByPriority.GetWallSoundIndex(this, (int)Facing)); + SurfaceIndex.GetSoundParamFromIndex(platformByPriority.GetWallSoundIndex(this, (int)Facing))); */ cursor.Emit(OpCodes.Ldloc, loc_Platform); @@ -337,6 +339,10 @@ OR Change (for walls): cursor.Emit(OpCodes.Call, m_String_Concat); // Remove hardcoded event string cursor.Remove(); + + // Then jump to after the referenced GetSoundIndex callvirt. + cursor.GotoNext(MoveType.After, instr => instr.MatchCallvirt(m_Platform_GetSoundIndex)); + cursor.Emit(OpCodes.Call, m_SurfaceIndex_GetSoundParamFromIndex); } public static void PatchMinMaxBlendFunction(ILContext il, CustomAttribute attrib) { diff --git a/Celeste.Mod.mm/Patches/Autotiler.cs b/Celeste.Mod.mm/Patches/Autotiler.cs index caabee2bf..7433ed7ab 100644 --- a/Celeste.Mod.mm/Patches/Autotiler.cs +++ b/Celeste.Mod.mm/Patches/Autotiler.cs @@ -70,13 +70,27 @@ private void ReadInto(patch_TerrainType data, Tileset tileset, XmlElement xml) { ReadIntoCustomTemplate(data, tileset, xml); } - if (xml.HasAttr("soundPath") && xml.HasAttr("sound")) { // Could accommodate for no sound attr, but requiring it should improve clarity on user's end + /*if (xml.HasAttr("soundPath") && xml.HasAttr("sound")) { // Could accommodate for no sound attr, but requiring it should improve clarity on user's end SurfaceIndex.TileToIndex[xml.AttrChar("id")] = xml.AttrInt("sound"); patch_SurfaceIndex.IndexToCustomPath[xml.AttrInt("sound")] = (xml.Attr("soundPath").StartsWith("event:/") ? "" : "event:/") + xml.Attr("soundPath"); } else if (xml.HasAttr("sound")) { SurfaceIndex.TileToIndex[xml.AttrChar("id")] = xml.AttrInt("sound"); } else if (!SurfaceIndex.TileToIndex.ContainsKey(xml.AttrChar("id"))) { SurfaceIndex.TileToIndex[xml.AttrChar("id")] = 0; // fall back to no sound + }*/ + // Rewritten below in order to support more sound attributes + + if (xml.HasAttr("sound")) { + SurfaceIndex.TileToIndex[xml.AttrChar("id")] = xml.AttrInt("sound"); + // Could accommodate for no sound attr, but requiring it should improve clarity on user's end + if (xml.HasAttr("soundPath")) { + patch_SurfaceIndex.IndexToCustomPath[xml.AttrInt("sound")] = (xml.Attr("soundPath").StartsWith("event:/") ? "" : "event:/") + xml.Attr("soundPath"); + } + if (xml.HasAttr("soundParam")) { + patch_SurfaceIndex.IndexToSoundParam[xml.AttrInt("sound")] = xml.AttrInt("soundParam"); + } + } else if (!SurfaceIndex.TileToIndex.ContainsKey(xml.AttrChar("id"))) { + SurfaceIndex.TileToIndex[xml.AttrChar("id")] = 0; // fall back to no sound } if (xml.HasAttr("debris")) diff --git a/Celeste.Mod.mm/Patches/LevelLoader.cs b/Celeste.Mod.mm/Patches/LevelLoader.cs index 8e60ea882..5334289f9 100644 --- a/Celeste.Mod.mm/Patches/LevelLoader.cs +++ b/Celeste.Mod.mm/Patches/LevelLoader.cs @@ -86,6 +86,9 @@ public void ctor(Session session, Vector2? startPosition = default) { // Clear any custom tileset sound paths patch_SurfaceIndex.IndexToCustomPath.Clear(); + // Clear any sound parameter overrides + patch_SurfaceIndex.IndexToSoundParam.Clear(); + string path = ""; try { diff --git a/Celeste.Mod.mm/Patches/Player.cs b/Celeste.Mod.mm/Patches/Player.cs index eba189260..068efa325 100644 --- a/Celeste.Mod.mm/Patches/Player.cs +++ b/Celeste.Mod.mm/Patches/Player.cs @@ -475,6 +475,7 @@ public static void PatchPlayerStarFlyReturnToNormalHitbox(ILContext context, Cus public static void PatchPlayerOnCollideV(ILContext context, CustomAttribute attrib) { MethodDefinition m_SurfaceIndex_GetPathFromIndex = context.Module.GetType("Celeste.SurfaceIndex").FindMethod("System.String GetPathFromIndex(System.Int32)"); + MethodDefinition m_SurfaceIndex_GetSoundParamFromIndex = context.Module.GetType("Celeste.SurfaceIndex").FindMethod("System.Int32 GetSoundParamFromIndex(System.Int32)"); MethodReference m_String_Concat = MonoModRule.Modder.Module.ImportReference( MonoModRule.Modder.FindType("System.String").Resolve() .FindMethod("System.String Concat(System.String,System.String)") @@ -498,7 +499,7 @@ and get the variable index of num2 /* Change Play((playFootstepOnLand > 0f) ? "event:/char/madeline/footstep" : "event:/char/madeline/landing", "surface_index", num2); to - Play(SurfaceIndex.GetPathFromIndex(num2) + ((playFootstepOnLand > 0f) ? "/footstep" : "/landing"), "surface_index", num2); + Play(SurfaceIndex.GetPathFromIndex(num2) + ((playFootstepOnLand > 0f) ? "/footstep" : "/landing"), "surface_index", SurfaceIndex.GetSoundParamFromIndex(num2)); */ cursor.GotoNext(MoveType.AfterLabel, instr => instr.MatchLdarg(0), instr => instr.MatchLdfld("Celeste.Player", "playFootstepOnLand")); cursor.Emit(OpCodes.Ldloc, loc_landSoundIdx); @@ -509,6 +510,9 @@ and get the variable index of num2 .Next.Operand = "/footstep"; cursor.GotoNext(MoveType.AfterLabel, instr => instr.MatchLdstr("surface_index")); cursor.Emit(OpCodes.Call, m_String_Concat); + + cursor.GotoNext(MoveType.After, instr => instr.MatchLdloc(loc_landSoundIdx)); + cursor.Emit(OpCodes.Call, m_SurfaceIndex_GetSoundParamFromIndex); } public static void PatchPlayerClimbBegin(ILContext context, CustomAttribute attrib) { @@ -519,6 +523,7 @@ public static void PatchPlayerOrigWallJump(ILContext context, CustomAttribute at // Doesn't use PatchPlaySurfaceIndex because index, not platform, is stored in the local variable MethodDefinition m_SurfaceIndex_GetPathFromIndex = context.Module.GetType("Celeste.SurfaceIndex").FindMethod("System.String GetPathFromIndex(System.Int32)"); + MethodDefinition m_SurfaceIndex_GetSoundParamFromIndex = context.Module.GetType("Celeste.SurfaceIndex").FindMethod("System.Int32 GetSoundParamFromIndex(System.Int32)"); MethodReference m_String_Concat = MonoModRule.Modder.Module.ImportReference( MonoModRule.Modder.FindType("System.String").Resolve() .FindMethod("System.String Concat(System.String,System.String)") @@ -529,7 +534,7 @@ public static void PatchPlayerOrigWallJump(ILContext context, CustomAttribute at /* Change: Play("event:/char/madeline/landing", "surface_index", num); to: - Play(SurfaceIndex.GetPathFromIndex(num) + "/landing", "surface_index", num); + Play(SurfaceIndex.GetPathFromIndex(num) + "/landing", "surface_index", SurfaceIndex.GetSoundParamFromIndex(num)); */ cursor.GotoNext(MoveType.AfterLabel, instr => instr.MatchLdstr("event:/char/madeline/landing")); cursor.Emit(OpCodes.Ldloc_0); @@ -537,6 +542,9 @@ public static void PatchPlayerOrigWallJump(ILContext context, CustomAttribute at cursor.Emit(OpCodes.Ldstr, "/landing"); cursor.Emit(OpCodes.Call, m_String_Concat); cursor.Remove(); + + cursor.GotoNext(MoveType.After, instr => instr.MatchLdloc0()); + cursor.Emit(OpCodes.Call, m_SurfaceIndex_GetSoundParamFromIndex); } public static void PatchPlayerCtor(MethodDefinition method, CustomAttribute attrib) { diff --git a/Celeste.Mod.mm/Patches/SurfaceIndex.cs b/Celeste.Mod.mm/Patches/SurfaceIndex.cs index 0d5860fad..8a055aab0 100644 --- a/Celeste.Mod.mm/Patches/SurfaceIndex.cs +++ b/Celeste.Mod.mm/Patches/SurfaceIndex.cs @@ -3,7 +3,9 @@ namespace Celeste { class patch_SurfaceIndex: SurfaceIndex { - public static Dictionary IndexToCustomPath = new Dictionary(); + public static Dictionary IndexToCustomPath = new Dictionary(); + + public static Dictionary IndexToSoundParam = new Dictionary(); public static string GetPathFromIndex(int key) { if (IndexToCustomPath.TryGetValue(key, out string path)) { @@ -12,5 +14,12 @@ public static string GetPathFromIndex(int key) { return "event:/char/madeline"; } + public static int GetSoundParamFromIndex(int key) { + if (IndexToSoundParam.TryGetValue(key, out int value)) { + return value; + } + return key; + } + } } From cdaf8a20b0221b28a17e614a57c911fa0d60df2c Mon Sep 17 00:00:00 2001 From: SilverDorian46 <86711559+SilverDorian46@users.noreply.github.com> Date: Sat, 21 Dec 2024 04:09:54 +0400 Subject: [PATCH 2/3] Implement custom dust particles, created via Autotiler XML --- Celeste.Mod.mm/Patches/Autotiler.cs | 123 +++++++++++++++++++++++++ Celeste.Mod.mm/Patches/LevelLoader.cs | 4 + Celeste.Mod.mm/Patches/Player.cs | 79 ++++++++++++++++ Celeste.Mod.mm/Patches/SurfaceIndex.cs | 14 ++- 4 files changed, 219 insertions(+), 1 deletion(-) diff --git a/Celeste.Mod.mm/Patches/Autotiler.cs b/Celeste.Mod.mm/Patches/Autotiler.cs index 7433ed7ab..31a7b26ac 100644 --- a/Celeste.Mod.mm/Patches/Autotiler.cs +++ b/Celeste.Mod.mm/Patches/Autotiler.cs @@ -4,8 +4,13 @@ using Celeste.Mod; using Celeste.Mod.Helpers; using Microsoft.Xna.Framework; +using Mono.Cecil; +using Mono.Cecil.Cil; using Monocle; using MonoMod; +using MonoMod.Cil; +using MonoMod.InlineRT; +using MonoMod.Utils; using System; using System.Collections.Generic; using System.Xml; @@ -15,11 +20,82 @@ class patch_Autotiler : Autotiler { private Dictionary lookup; + private Dictionary dustParticlesLookup; + public patch_Autotiler(string filename) : base(filename) { // no-op. MonoMod ignores this - we only need this to make the compiler shut up. } + [MonoModIgnore] + [MonoModConstructor] + [PatchAutotilerCtor] + public extern void ctor(string filename); + + private void ReadDustParticlesData(string filename) { + Dictionary particleTypes = new Dictionary(StringComparer.OrdinalIgnoreCase) { + { "Dust", ParticleTypes.Dust }, + { "SparkyDust", ParticleTypes.SparkyDust } + }; + + foreach (XmlElement item in Calc.LoadContentXML(filename).GetElementsByTagName("DustParticles")) { + foreach (object child in item.ChildNodes) { + if (child is XmlElement xml) { + string name = xml.Name; + if (particleTypes.ContainsKey(name)) { + if (name.Equals("Dust", StringComparison.OrdinalIgnoreCase) || name.Equals("SparkyDust", StringComparison.OrdinalIgnoreCase)) { + throw new Exception($"The dust particle name '{name}' is reserved for a preset particle type!"); + } + throw new Exception($"Duplicate dust particle name: '{name}'!"); + } + + ParticleType particleType; + float wallSlideOffsetX; + if (xml.HasAttr("copy")) { + string copy = xml.Attr("copy"); + if (!particleTypes.TryGetValue(copy, out ParticleType copyType)) { + throw new Exception($"Copied dust particle type '{copy}' must be either 'Dust', 'SparkyDust', or defined before the dust particle types that copy it!"); + } + particleType = new ParticleType(copyType); + wallSlideOffsetX = (copyType == ParticleTypes.Dust) ? 5 : 2; + } else { + particleType = new ParticleType(ParticleTypes.Dust); + wallSlideOffsetX = 5; + } + + if (xml.HasAttr("sourceChooser")) { + particleType.SourceChooser = new Chooser(GFX.Game.GetAtlasSubtextures(xml.Attr("sourceChooser")).ToArray()); + } + if (xml.HasAttr("color")) { + particleType.Color = xml.AttrHexColor("color"); + } + if (xml.HasAttr("color2")) { + particleType.Color2 = xml.AttrHexColor("color2"); + } + if (xml.HasAttr("colorMode")) { + if (Enum.TryParse(xml.Attr("colorMode"), ignoreCase: true, out ParticleType.ColorModes colorMode)) { + particleType.ColorMode = colorMode; + } else { + throw new Exception("Color mode must be either 'Static', 'Choose', 'Blink', or 'Fade'!"); + } + } + + if (dustParticlesLookup.TryGetValue(name, out int index)) { + patch_SurfaceIndex.IndexToDustParticles[index] = particleType; + + if (xml.HasAttr("wallSlideOffsetX")) { + patch_SurfaceIndex.DustParticlesToWallSlideOffsetX[particleType] = xml.AttrFloat("wallSlideOffsetX"); + } else { + patch_SurfaceIndex.DustParticlesToWallSlideOffsetX[particleType] = wallSlideOffsetX; + } + } + + particleTypes.Add(name, particleType); + } + } + } + } + public extern Generated orig_GenerateOverlay(char id, int x, int y, int tilesX, int tilesY, VirtualMap mapData); public new Generated GenerateOverlay(char id, int x, int y, int tilesX, int tilesY, VirtualMap mapData) { // be sure our overlay doesn't cross null segments, because they might just not get rendered there. @@ -89,6 +165,9 @@ private void ReadInto(patch_TerrainType data, Tileset tileset, XmlElement xml) { if (xml.HasAttr("soundParam")) { patch_SurfaceIndex.IndexToSoundParam[xml.AttrInt("sound")] = xml.AttrInt("soundParam"); } + if (xml.HasAttr("dustParticles")) { + dustParticlesLookup[xml.Attr("dustParticles")] = xml.AttrInt("sound"); + } } else if (!SurfaceIndex.TileToIndex.ContainsKey(xml.AttrChar("id"))) { SurfaceIndex.TileToIndex[xml.AttrChar("id")] = 0; // fall back to no sound } @@ -467,3 +546,47 @@ private class patch_Masked { } } + +namespace MonoMod { + /// + /// Patches the constructor to read XML data for custom dust particles. + /// + [MonoModCustomMethodAttribute(nameof(MonoModRules.PatchAutotilerCtor))] + class PatchAutotilerCtorAttribute : Attribute { } + + static partial class MonoModRules { + + public static void PatchAutotilerCtor(ILContext il, CustomAttribute attrib) { + TypeDefinition t_Autotiler = il.Module.GetType("Celeste.Autotiler"); + FieldDefinition f_dustParticlesLookup = t_Autotiler.FindField("dustParticlesLookup"); + MethodReference m_Dictionary_ctor = MonoModRule.Modder.Module.ImportReference(f_dustParticlesLookup.FieldType.Resolve().FindMethod("System.Void .ctor()")); + m_Dictionary_ctor.DeclaringType = f_dustParticlesLookup.FieldType; + + MethodDefinition m_ReadDustParticlesData = t_Autotiler.FindMethod("ReadDustParticlesData"); + + ILCursor cursor = new ILCursor(il); + + // Initialise our new dictionary for use. + cursor.Emit(OpCodes.Ldarg_0); + cursor.Emit(OpCodes.Newobj, m_Dictionary_ctor); + cursor.Emit(OpCodes.Stfld, f_dustParticlesLookup); + + // Then, insert our method at the end. + cursor.Index = -1; + cursor.MoveAfterLabels(); + // cursor.Emit(OpCodes.Ldarg_0); + /* + Apparently, instructions emitted here would end up inside the finally block, even though we're already after the endfinally. + That would in turn make these instructions unreachable. + We'll have to work around it by changing the next instruction instead of emitting a new one here. + */ + OpCode nextOpcode = cursor.Next.OpCode; + cursor.Next.OpCode = OpCodes.Ldarg_0; + cursor.Index++; + // Alright, now we can emit new instructions. + cursor.Emit(OpCodes.Ldarg_1); + cursor.Emit(OpCodes.Callvirt, m_ReadDustParticlesData); + cursor.Emit(nextOpcode); + } + } +} diff --git a/Celeste.Mod.mm/Patches/LevelLoader.cs b/Celeste.Mod.mm/Patches/LevelLoader.cs index 5334289f9..7cf673f2e 100644 --- a/Celeste.Mod.mm/Patches/LevelLoader.cs +++ b/Celeste.Mod.mm/Patches/LevelLoader.cs @@ -89,6 +89,10 @@ public void ctor(Session session, Vector2? startPosition = default) { // Clear any sound parameter overrides patch_SurfaceIndex.IndexToSoundParam.Clear(); + // Clear any custom dust particle types + patch_SurfaceIndex.IndexToDustParticles.Clear(); + patch_SurfaceIndex.DustParticlesToWallSlideOffsetX.Clear(); + string path = ""; try { diff --git a/Celeste.Mod.mm/Patches/Player.cs b/Celeste.Mod.mm/Patches/Player.cs index 068efa325..a7786c111 100644 --- a/Celeste.Mod.mm/Patches/Player.cs +++ b/Celeste.Mod.mm/Patches/Player.cs @@ -329,6 +329,18 @@ public override void SceneEnd(Scene scene) { [MonoModIgnore] [PatchPlayerApproachMaxMove] private extern int NormalUpdate(); + + [MonoModIgnore] + [PatchPlayerDustParticleFromSurfaceIndex] + private extern ParticleType DustParticleFromSurfaceIndex(int index); + + [MonoModIgnore] + [PatchPlayerCreateWallSlideParticles] + public new extern void CreateWallSlideParticles(int dir); + + private static bool _TryGetWallSlideOffsetX(ParticleType particleType, out float value) { + return patch_SurfaceIndex.DustParticlesToWallSlideOffsetX.TryGetValue(particleType, out value); + } } public static class PlayerExt { @@ -404,6 +416,18 @@ class PatchPlayerExplodeLaunchAttribute : Attribute { } [MonoModCustomMethodAttribute(nameof(MonoModRules.PatchPlayerApproachMaxMove))] class PatchPlayerApproachMaxMoveAttribute : Attribute { } + /// + /// Patches the method to get the custom dust particle type for the index, if there is. + /// + [MonoModCustomMethodAttribute(nameof(MonoModRules.PatchPlayerDustParticleFromSurfaceIndex))] + class PatchPlayerDustParticleFromSurfaceIndexAttribute : Attribute { } + + /// + /// Patches the method to get the X-offset position for the custom dust particle type, if there is. + /// + [MonoModCustomMethodAttribute(nameof(MonoModRules.PatchPlayerCreateWallSlideParticles))] + class PatchPlayerCreateWallSlideParticlesAttribute : Attribute { } + static partial class MonoModRules { public static void PatchPlayerOrigUpdate(ILContext context, CustomAttribute attrib) { @@ -609,5 +633,60 @@ public static void PatchPlayerApproachMaxMove(ILContext context, CustomAttribute } } } + + + public static void PatchPlayerDustParticleFromSurfaceIndex(MethodDefinition method, CustomAttribute attrib) { + // First, set NoInlining. + method.NoInlining = true; + + // Then begin patching. + new ILContext(method).Invoke(il => { + MethodDefinition m_SurfaceIndex_GetDustParticleTypeFromIndex = il.Module.GetType("Celeste.SurfaceIndex").FindMethod("Monocle.ParticleType GetDustParticleTypeFromIndex(System.Int32)"); + + ILCursor cursor = new ILCursor(il); + + /* + Inject the following at the start: + ParticleType dustParticleTypeFromIndex = SurfaceIndex.GetDustParticleTypeFromIndex(index); + if (dustParticleTypeFromIndex != null) { + return dustParticleTypeFromIndex; + } + */ + ILLabel branchIfNullLabel = cursor.DefineLabel(); + + cursor.Emit(OpCodes.Ldarg_1); + cursor.Emit(OpCodes.Call, m_SurfaceIndex_GetDustParticleTypeFromIndex); + cursor.Emit(OpCodes.Dup); + cursor.Emit(OpCodes.Brfalse_S, branchIfNullLabel); + cursor.Emit(OpCodes.Ret); + cursor.MarkLabel(branchIfNullLabel); + cursor.Emit(OpCodes.Pop); + }); + } + + public static void PatchPlayerCreateWallSlideParticles(ILContext il, CustomAttribute attrib) { + MethodDefinition m_TryGetWallSlideOffsetX = il.Method.DeclaringType.FindMethod("_TryGetWallSlideOffsetX"); + + ILCursor cursor = new ILCursor(il); + + /* + Replace: + float num = ((particleType == ParticleTypes.Dust) ? 5f : 2f); + with: + if (!_TryGetWallSlideOffsetX(particleType, out float num)) { + num = ((particleType == ParticleTypes.Dust) ? 5f : 2f); + } + */ + ILLabel branchIfValueLabel = cursor.DefineLabel(); + + cursor.GotoNext(MoveType.AfterLabel, instr => instr.MatchLdloc2()); + cursor.Emit(OpCodes.Ldloc_2); + cursor.Emit(OpCodes.Ldloca, 3); + cursor.Emit(OpCodes.Call, m_TryGetWallSlideOffsetX); + cursor.Emit(OpCodes.Brtrue_S, branchIfValueLabel); + + cursor.GotoNext(MoveType.After, instr => instr.MatchStloc3()); + cursor.MarkLabel(branchIfValueLabel); + } } } diff --git a/Celeste.Mod.mm/Patches/SurfaceIndex.cs b/Celeste.Mod.mm/Patches/SurfaceIndex.cs index 8a055aab0..1bd05ef13 100644 --- a/Celeste.Mod.mm/Patches/SurfaceIndex.cs +++ b/Celeste.Mod.mm/Patches/SurfaceIndex.cs @@ -1,4 +1,5 @@ -using System.Collections.Generic; +using Monocle; +using System.Collections.Generic; namespace Celeste { class patch_SurfaceIndex: SurfaceIndex { @@ -7,6 +8,10 @@ class patch_SurfaceIndex: SurfaceIndex { public static Dictionary IndexToSoundParam = new Dictionary(); + public static Dictionary IndexToDustParticles = new Dictionary(); + + public static Dictionary DustParticlesToWallSlideOffsetX = new Dictionary(); + public static string GetPathFromIndex(int key) { if (IndexToCustomPath.TryGetValue(key, out string path)) { return path; @@ -21,5 +26,12 @@ public static int GetSoundParamFromIndex(int key) { return key; } + public static ParticleType GetDustParticleTypeFromIndex(int key) { + if (IndexToDustParticles.TryGetValue(key, out ParticleType particles)) { + return particles; + } + return null; + } + } } From 165ddda54f7a1b7a63a7b5910d8b885f7b09da74 Mon Sep 17 00:00:00 2001 From: SilverDorian46 <86711559+SilverDorian46@users.noreply.github.com> Date: Sat, 21 Dec 2024 12:48:22 +0400 Subject: [PATCH 3/3] Try to get the WallSlideOffsetX value from copied particle type --- Celeste.Mod.mm/Patches/Autotiler.cs | 4 +++- Celeste.Mod.mm/Patches/Player.cs | 2 +- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/Celeste.Mod.mm/Patches/Autotiler.cs b/Celeste.Mod.mm/Patches/Autotiler.cs index 31a7b26ac..f8e077cc8 100644 --- a/Celeste.Mod.mm/Patches/Autotiler.cs +++ b/Celeste.Mod.mm/Patches/Autotiler.cs @@ -57,7 +57,9 @@ private void ReadDustParticlesData(string filename) { throw new Exception($"Copied dust particle type '{copy}' must be either 'Dust', 'SparkyDust', or defined before the dust particle types that copy it!"); } particleType = new ParticleType(copyType); - wallSlideOffsetX = (copyType == ParticleTypes.Dust) ? 5 : 2; + if (!patch_Player._TryGetWallSlideOffsetX(copyType, out wallSlideOffsetX)) { + wallSlideOffsetX = (copyType == ParticleTypes.Dust) ? 5 : 2; + } } else { particleType = new ParticleType(ParticleTypes.Dust); wallSlideOffsetX = 5; diff --git a/Celeste.Mod.mm/Patches/Player.cs b/Celeste.Mod.mm/Patches/Player.cs index a7786c111..76aeb97fe 100644 --- a/Celeste.Mod.mm/Patches/Player.cs +++ b/Celeste.Mod.mm/Patches/Player.cs @@ -338,7 +338,7 @@ public override void SceneEnd(Scene scene) { [PatchPlayerCreateWallSlideParticles] public new extern void CreateWallSlideParticles(int dir); - private static bool _TryGetWallSlideOffsetX(ParticleType particleType, out float value) { + public static bool _TryGetWallSlideOffsetX(ParticleType particleType, out float value) { return patch_SurfaceIndex.DustParticlesToWallSlideOffsetX.TryGetValue(particleType, out value); } }