Skip to content
Draft
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
33 changes: 33 additions & 0 deletions Celeste.Mod.mm/Mod/Entities/ISpeed.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using Microsoft.Xna.Framework;
using Mono.Cecil;
using MonoMod.InlineRT;

namespace Celeste.Mod {
/// <summary>
/// </summary>
public interface ISpeed {
/// <summary>
/// </summary>
public Vector2 Speed { get; set; }
}
}

namespace MonoMod {

/// <summary>
/// Patch the given class to tack on the ISpeed interface
/// </summary>
[MonoModCustomAttribute(nameof(MonoModRules.PatchSpeedInterface))]
class PatchSpeedInterfaceAttribute : Attribute { }

static partial class MonoModRules {

public static void PatchSpeedInterface(ICustomAttributeProvider provider, CustomAttribute attrib) {
InterfaceImplementation i_ISpeed = new InterfaceImplementation(MonoModRule.Modder.FindType("Celeste.Mod.ISpeed"));

((TypeDefinition) provider).Interfaces.Add(i_ISpeed);
}

}
}
13 changes: 13 additions & 0 deletions Celeste.Mod.mm/MonoModRules.cs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
using System.Reflection;
using ICustomAttributeProvider = Mono.Cecil.ICustomAttributeProvider;
using MethodAttributes = Mono.Cecil.MethodAttributes;
using PropertyAttributes = Mono.Cecil.PropertyAttributes;

namespace MonoMod {
#region Helper Patch Attributes
Expand All @@ -23,6 +24,12 @@ class MakeEntryPointAttribute : Attribute { }
[MonoModCustomMethodAttribute(nameof(MonoModRules.PatchInterface))]
class PatchInterfaceAttribute : Attribute { }

/// <summary>
/// Helper for patching properties force-implemented by an interface
/// </summary>
[MonoModCustomMethodAttribute(nameof(MonoModRules.PatchInterfaceProperty))]
class PatchInterfacePropertyAttribute : Attribute { }

/// <summary>
/// Forcibly changes a given member's name.
/// </summary>
Expand Down Expand Up @@ -228,6 +235,12 @@ public static void PatchInterface(MethodDefinition method, CustomAttribute attri
method.Attributes |= flags;
}

public static void PatchInterfaceProperty(PropertyDefinition property, CustomAttribute attrib) {
MethodAttributes flags = MethodAttributes.Virtual | MethodAttributes.Final | MethodAttributes.NewSlot;
if (property.GetMethod is {} @get) @get.Attributes |= flags;
if (property.SetMethod is {} @set) @set.Attributes |= flags;
}

public static void ForceName(ICustomAttributeProvider cap, CustomAttribute attrib) {
if (cap is IMemberDefinition member)
member.Name = (string) attrib.ConstructorArguments[0].Value;
Expand Down
9 changes: 7 additions & 2 deletions Celeste.Mod.mm/Patches/Glider.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@
#pragma warning disable CS0626 // Method, operator, or accessor is marked external and has no attributes on it
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value

using Celeste.Mod;
using Microsoft.Xna.Framework;
using MonoMod;

namespace Celeste {
class patch_Glider : Glider {
[PatchSpeedInterface]
class patch_Glider : Glider, ISpeed {
public patch_Holdable Hold; // avoids extra cast

[PatchInterfaceProperty]
Vector2 ISpeed.Speed { get => Speed; set => Speed = value; }

public patch_Glider(Vector2 position, bool bubble, bool tutorial)
: base(position, bubble, tutorial) {
}
Expand All @@ -21,4 +26,4 @@ public void ctor(Vector2 position, bool bubble, bool tutorial) {
Hold.SpeedSetter = (speed) => { Speed = speed; };
}
}
}
}
7 changes: 6 additions & 1 deletion Celeste.Mod.mm/Patches/Holdable.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,16 @@
using Microsoft.Xna.Framework;
using MonoMod;
using System;
using Celeste.Mod;

namespace Celeste {
class patch_Holdable : Holdable {
[PatchSpeedInterface]
class patch_Holdable : Holdable, ISpeed {
public Action<Vector2> SpeedSetter;

[PatchInterfaceProperty]
public Vector2 Speed { get => GetSpeed(); set => SetSpeed(value); }

[MonoModLinkTo("Celeste.Holdable", "System.Void .ctor(System.Single)")]
[MonoModForceCall]
[MonoModRemove]
Expand Down
9 changes: 9 additions & 0 deletions Celeste.Mod.mm/Patches/MoveBlock.cs
Original file line number Diff line number Diff line change
Expand Up @@ -7,8 +7,10 @@
using MonoMod.Cil;
using MonoMod.InlineRT;
using MonoMod.Utils;
using Celeste.Mod;

namespace Celeste {
[PatchSpeedInterface]
class patch_MoveBlock : MoveBlock {

public patch_MoveBlock(Vector2 position, int width, int height, MoveBlock.Directions direction, bool canSteer, bool fast)
Expand All @@ -19,6 +21,13 @@ public patch_MoveBlock(Vector2 position, int width, int height, MoveBlock.Direct
[MonoModIgnore]
[PatchMoveBlockController]
private extern IEnumerator Controller();

class patch_Debris : ISpeed {
private Vector2 speed;

[PatchInterfaceProperty]
Vector2 ISpeed.Speed { get => speed; set => speed = value; }
}
}
}

Expand Down
7 changes: 5 additions & 2 deletions Celeste.Mod.mm/Patches/Player.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,8 @@
using _Player = Celeste.Player;

namespace Celeste {
class patch_Player : Player {
[PatchSpeedInterface]
class patch_Player : Player, ISpeed {

// We're effectively in Player, but still need to "expose" private fields to our mod.
private bool wasDashB;
Expand All @@ -43,14 +44,16 @@ class patch_Player : Player {
}
}


public bool IsIntroState {
get {
int state = StateMachine.State;
return state is >= StIntroWalk and <= StIntroWakeUp or StIntroMoonJump or StIntroThinkForABit;
}
}

[PatchInterfaceProperty]
Vector2 ISpeed.Speed { get => Speed; set => Speed = value; }

public patch_Player(Vector2 position, PlayerSpriteMode spriteMode)
: base(position, spriteMode) {
// no-op. MonoMod ignores this - we only need this to make the compiler shut up.
Expand Down
6 changes: 5 additions & 1 deletion Celeste.Mod.mm/Patches/Seeker.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,15 @@
using _Seeker = Celeste.Seeker;

namespace Celeste {
public class patch_Seeker : Seeker {
[PatchSpeedInterface]
public class patch_Seeker : Seeker, ISpeed {

// We're effectively in Seeker, but still need to "expose" private fields to our mod.
private StateMachine State;

[PatchInterfaceProperty]
Vector2 ISpeed.Speed { get => Speed; set => Speed = value; }

// no-op - only here to make
public patch_Seeker(EntityData data, Vector2 offset)
: base(data, offset) {
Expand Down
22 changes: 16 additions & 6 deletions Celeste.Mod.mm/Patches/Solid.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using Mono.Cecil;
using Celeste.Mod;
using Microsoft.Xna.Framework;
using Mono.Cecil;
using Mono.Cecil.Cil;
using Monocle;
using MonoMod;
Expand All @@ -8,22 +10,30 @@
using System;

namespace Celeste {
internal class patch_Solid {
[PatchSpeedInterface]
internal class patch_Solid : Solid, ISpeed {
[PatchInterfaceProperty]
Vector2 ISpeed.Speed { get => Speed; set => Speed = value; }

public patch_Solid(Vector2 position, float width, float height, bool safe) : base(position, width, height, safe) {
// no-op. MonoMod ignores this - we only need this to make the compiler shut up.
}

[MonoModIgnore]
[PatchSolidAwake]
public extern void Awake(Scene scene);
public extern new void Awake(Scene scene);

[MonoModIgnore]
[ForceNoInlining]
public extern bool HasPlayerClimbing();
public extern new bool HasPlayerClimbing();

[MonoModIgnore]
[ForceNoInlining]
public extern bool HasPlayerOnTop();
public extern new bool HasPlayerOnTop();

[MonoModIgnore]
[ForceNoInlining]
public extern bool HasPlayerRider();
public extern new bool HasPlayerRider();
}
}

Expand Down
9 changes: 7 additions & 2 deletions Celeste.Mod.mm/Patches/TheoCrystal.cs
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,18 @@
#pragma warning disable CS0626 // Method, operator, or accessor is marked external and has no attributes on it
#pragma warning disable CS0649 // Field is never assigned to, and will always have its default value

using Celeste.Mod;
using Microsoft.Xna.Framework;
using MonoMod;

namespace Celeste {
class patch_TheoCrystal : TheoCrystal {
[PatchSpeedInterface]
class patch_TheoCrystal : TheoCrystal, ISpeed {
public patch_Holdable Hold; // avoids extra cast

[PatchInterfaceProperty]
Vector2 ISpeed.Speed { get => Speed; set => Speed = value; }

public patch_TheoCrystal(EntityData data, Vector2 offset)
: base(data, offset) {
}
Expand All @@ -21,4 +26,4 @@ public void ctor(Vector2 position) {
Hold.SpeedSetter = (speed) => { Speed = speed; };
}
}
}
}
Loading