Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions Celeste.Mod.mm/Patches/LevelData.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
using System;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Monocle;
using MonoMod;
using MonoMod.Cil;
using MonoMod.InlineRT;
Expand All @@ -16,6 +17,10 @@ public class patch_LevelData : LevelData {

public Vector2? DefaultSpawn;

// custom room color
// if null, `EditorColorIndex` will be used to determine the room color
public Color? CustomEditorColor;

public patch_LevelData(BinaryPacker.Element data) : base(data) {
// no-op. MonoMod ignores this - we only need this to make the compiler shut up.
}
Expand All @@ -29,6 +34,9 @@ public patch_LevelData(BinaryPacker.Element data) : base(data) {
[MonoModConstructor]
public void ctor(BinaryPacker.Element data) {
orig_ctor(data);

string color = data.Attr("color");
CustomEditorColor = string.IsNullOrEmpty(color) ? null : Calc.HexToColor(color);
}

private void CheckForDefaultSpawn(BinaryPacker.Element spawn, Vector2 coords) {
Expand Down
67 changes: 59 additions & 8 deletions Celeste.Mod.mm/Patches/LevelTemplate.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using Celeste.Mod.Helpers;
using Microsoft.Xna.Framework;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Monocle;
Expand All @@ -10,26 +11,41 @@

namespace Celeste.Editor {
class patch_LevelTemplate : LevelTemplate {
// expose this private field to our patch
private static new Color[] fgTilesColor;

// custom room color
// if null, `EditorColorIndex` will be used to determine the room color
public Color? CustomEditorColor;
public Color EditorColor => CustomEditorColor ?? fgTilesColor[EditorColorIndex];

public patch_LevelTemplate(LevelData data)
: base(data) {
// no-op. MonoMod ignores this - we only need this to make the compiler shut up.
}

[MonoModConstructor]

[MonoModIgnore] // we don't want to change anything in the method...
[PatchTrackableStrawberryCheck] // except manipulating it with MonoModRules
public extern void ctor(LevelData data);
public extern void orig_ctor(LevelData data);

[MonoModConstructor]
public void ctor(LevelData data) {
orig_ctor(data);

[AddLevelTemplateCulling]
CustomEditorColor = ((patch_LevelData) data).CustomEditorColor;
}

[MonoModIgnore]
public new extern void RenderContents(Camera camera, System.Collections.Generic.List<LevelTemplate> allLevels);

[AddLevelTemplateCulling]
[PatchLevelTemplateRenderContents]
public new extern void RenderContents(Camera camera, System.Collections.Generic.List<LevelTemplate> allLevels);

[MonoModIgnore]
public new extern void RenderOutline(Camera camera);

[AddLevelTemplateCulling]
public new extern void RenderOutline(Camera camera);

[MonoModIgnore]
[AddLevelTemplateCulling]
public new extern void RenderHighlight(Camera camera, bool hovered, bool selected);

[MethodImpl(MethodImplOptions.AggressiveInlining)]
Expand All @@ -46,6 +62,12 @@ namespace MonoMod {
[MonoModCustomMethodAttribute(nameof(MonoModRules.AddLevelTemplateCulling))]
class AddLevelTemplateCullingAttribute : Attribute { }

/// <summary>
/// Patch `LevelTemplate.RenderContent` to support custom template colors.
/// </summary>
[MonoModCustomMethodAttribute(nameof(MonoModRules.PatchLevelTemplateRenderContents))]
class PatchLevelTemplateRenderContents : Attribute { }

static partial class MonoModRules {
public static void AddLevelTemplateCulling(ILContext il, CustomAttribute attrib) {
var cursor = new ILCursor(il);
Expand All @@ -63,5 +85,34 @@ public static void AddLevelTemplateCulling(ILContext il, CustomAttribute attrib)
cursor.Emit(OpCodes.Ret);
cursor.MarkLabel(label);
}

public static void PatchLevelTemplateRenderContents(ILContext il, CustomAttribute attrib) {
/*
* patch the `fgTilesColor` lookup to use `EditorColor` instead
* ```diff
* - Draw.Rect(X + solid.X, Y + solid.Y, solid.Width, solid.Height, Dummy ? dummyFgTilesColor : fgTilesColor[EditorColorIndex]);
* + Draw.Rect(X + solid.X, Y + solid.Y, solid.Width, solid.Height, Dummy ? dummyFgTilesColor : EditorColor);
* ```
*/

MethodDefinition m_LevelTemplate_get_EditorColor = il.Method.DeclaringType.FindMethod("get_EditorColor")!;

ILCursor cursor = new(il);

/*
* IL_0170: ldsfld valuetype [FNA]Microsoft.Xna.Framework.Color[] Celeste.Editor.LevelTemplate::fgTilesColor
* IL_0175: ldarg.0
* IL_0176: ldfld int32 Celeste.Editor.LevelTemplate::EditorColorIndex
* IL_017b: ldelem [FNA]Microsoft.Xna.Framework.Color
*/
cursor.GotoNext(MoveType.Before,
instr => instr.MatchLdsfld("Celeste.Editor.LevelTemplate", "fgTilesColor"),
instr => instr.MatchLdarg0(),
instr => instr.MatchLdfld("Celeste.Editor.LevelTemplate", "EditorColorIndex"),
instr => instr.MatchLdelemAny("Microsoft.Xna.Framework.Color"));
cursor.RemoveRange(4);
cursor.EmitLdarg0();
cursor.EmitCall(m_LevelTemplate_get_EditorColor);
}
}
}
Loading