111 lines
4.5 KiB
C#
111 lines
4.5 KiB
C#
using System;
|
|
using Mono.Cecil.Cil;
|
|
using MonoMod.Cil;
|
|
using SlugBase.Features;
|
|
using static SlugBase.Features.FeatureTypes;
|
|
|
|
namespace JilloSlug.Features;
|
|
|
|
internal static class CreateSlimeMoldFeature {
|
|
public static readonly PlayerFeature<bool> CreateSlimeMold = PlayerBool("jillo/create_slime_mold");
|
|
|
|
public static void AddHooks() {
|
|
On.Player.Regurgitate += Player_Regurgitate;
|
|
IL.Player.GrabUpdate += Player_GrabUpdate;
|
|
IL.PlayerGraphics.Update += PlayerGraphics_Update;
|
|
}
|
|
|
|
private static void Player_Regurgitate(On.Player.orig_Regurgitate orig, Player self) {
|
|
if (self.objectInStomach == null && CreateSlimeMold.TryGet(self, out var createSlimeMold) && createSlimeMold) {
|
|
self.objectInStomach = new AbstractConsumable(self.room.world, AbstractPhysicalObject.AbstractObjectType.SlimeMold, null, self.room.GetWorldCoordinate(self.firstChunk.pos), self.room.game.GetNewID(), -1, -1, null);
|
|
JilloSlimeMold.SetJilloSlimeMold(self.objectInStomach);
|
|
}
|
|
orig(self);
|
|
}
|
|
|
|
private static void Player_GrabUpdate(ILContext il) {
|
|
ILCursor c = new ILCursor(il);
|
|
|
|
// replace all mentions of `isGourmand` with the equivalent of `(isGourmand || isJillo)`
|
|
|
|
// match `isGourmand`, brfalse edition
|
|
while (
|
|
c.TryGotoNext(MoveType.After,
|
|
i => i.MatchLdarg(0),
|
|
i => i.MatchCallOrCallvirt<Player>("get_isGourmand"),
|
|
i => i.Match(OpCodes.Brfalse_S) || i.Match(OpCodes.Brfalse)
|
|
)
|
|
) {
|
|
// this is the condition we should skip to if our check succeeds, replicating the behavior if the vanilla check was to succeed
|
|
ILLabel skipGourmandCond = c.MarkLabel();
|
|
c.GotoPrev(MoveType.Before, i => i.MatchLdarg(0), i => i.Match(OpCodes.Call) || i.Match(OpCodes.Callvirt));
|
|
|
|
// insert the condition
|
|
c.Emit(OpCodes.Ldarg_0);
|
|
c.EmitDelegate<Func<Player, bool>>(player => {
|
|
return CreateSlimeMold.TryGet(player, out var shouldCreateSlimeMold) && shouldCreateSlimeMold;
|
|
});
|
|
// if it's true, skip ahead
|
|
c.Emit(OpCodes.Brtrue_S, skipGourmandCond);
|
|
|
|
// move forwards to avoid an infloop
|
|
c.GotoNext(MoveType.After, i => i.Match(OpCodes.Brfalse_S) || i.Match(OpCodes.Brfalse));
|
|
}
|
|
|
|
c.Index = 0;
|
|
|
|
// match `isGourmand`, brtrue edition
|
|
while (
|
|
c.TryGotoNext(MoveType.After,
|
|
i => i.MatchLdarg(0),
|
|
i => i.MatchCallOrCallvirt<Player>("get_isGourmand"),
|
|
i => i.Match(OpCodes.Brtrue_S) || i.Match(OpCodes.Brtrue)
|
|
)
|
|
) {
|
|
// a lot easier here, since you can just insert another cond
|
|
|
|
ILLabel proceedCond = c.Prev.Operand as ILLabel;
|
|
|
|
// insert the condition
|
|
c.Emit(OpCodes.Ldarg_0);
|
|
c.EmitDelegate<Func<Player, bool>>(player => {
|
|
return CreateSlimeMold.TryGet(player, out var shouldCreateSlimeMold) && shouldCreateSlimeMold;
|
|
});
|
|
// if it's true, proceed as usual
|
|
c.Emit(OpCodes.Brtrue_S, proceedCond);
|
|
}
|
|
}
|
|
|
|
private static void PlayerGraphics_Update(ILContext il) {
|
|
ILCursor c = new ILCursor(il);
|
|
|
|
// match for `player.objectInStomach != null`
|
|
c.GotoNext(MoveType.After,
|
|
i => i.MatchLdarg(0),
|
|
i => i.MatchLdfld<PlayerGraphics>("player"),
|
|
i => i.MatchLdfld<Player>("objectInStomach"),
|
|
i => i.Match(OpCodes.Brtrue_S)
|
|
);
|
|
|
|
// match for `player.SlugCatClass == MoreSlugcatsEnums.SlugcatStatsName.Gourmand ...`
|
|
c.GotoNext(MoveType.After,
|
|
i => i.MatchLdarg(0),
|
|
i => i.MatchLdfld<PlayerGraphics>("player"),
|
|
i => i.MatchLdfld<Player>("SlugCatClass"),
|
|
i => i.MatchLdsfld<MoreSlugcats.MoreSlugcatsEnums.SlugcatStatsName>("Gourmand"),
|
|
i => i.Match(OpCodes.Call), // this is a mess of generics; not matching this, but it's the equation call
|
|
i => i.Match(OpCodes.Brtrue_S)
|
|
);
|
|
|
|
ILLabel proceedCond = c.Prev.Operand as ILLabel;
|
|
|
|
// insert our condition
|
|
c.Emit(OpCodes.Ldarg_0);
|
|
c.EmitDelegate<Func<PlayerGraphics, bool>>(playerGraphics => {
|
|
return CreateSlimeMold.TryGet(playerGraphics.player, out var shouldCreateSlimeMold) && shouldCreateSlimeMold;
|
|
});
|
|
// if it's true, proceed as usual
|
|
c.Emit(OpCodes.Brtrue_S, proceedCond);
|
|
}
|
|
}
|