JilloSlug/src/features/ImmuneToDartMaggotsFeature.cs

78 lines
2.6 KiB
C#

using System;
using SlugBase.Features;
using static SlugBase.Features.FeatureTypes;
using MonoMod.Cil;
using Mono.Cecil.Cil;
namespace JilloSlug.Features;
internal static class ImmuneToDartMaggotsFeature {
public static readonly PlayerFeature<bool> ImmuneToDartMaggots = PlayerBool("jillo/immune_to_dart_maggots");
public static void AddHooks() {
IL.DartMaggot.Update += DartMaggot_Update;
}
private static bool DartMaggotCanStun(DartMaggot maggot) {
if (maggot.stuckInChunk.owner is Player && ImmuneToDartMaggots.TryGet(maggot.stuckInChunk.owner as Player, out var immune) && immune) {
return false; // skip
}
return true; // proceed as normal
}
private static void ILInsertHandleDartMaggotStun(ILCursor c, ILLabel skipLabel) {
c.Emit(OpCodes.Ldarg_0);
c.EmitDelegate<Func<DartMaggot, bool>>(maggot => {
return DartMaggotCanStun(maggot);
});
c.Emit(OpCodes.Brfalse, skipLabel);
}
private static void DartMaggot_Update(ILContext il) {
ILCursor c = new ILCursor(il);
// matching for `stuckInChunk.owner is Creature && ...`
c.GotoNext(MoveType.After,
i => i.MatchLdarg(0),
i => i.MatchLdfld<DartMaggot>("stuckInChunk"),
i => i.MatchCallOrCallvirt<BodyChunk>("get_owner"),
i => i.MatchIsinst<Creature>(),
i => i.Match(OpCodes.Brfalse_S)
);
// last instruction will be a brfalse.s; capture the label it goes to
ILLabel skipLabel = c.Prev.Operand as ILLabel;
// we're now ready to write another condition
ILInsertHandleDartMaggotStun(c, skipLabel);
// comments will be unrepeated; same functionality
// matching for `stuckInChunk.owner is Player && ...`
c.GotoNext(MoveType.After,
i => i.MatchLdarg(0),
i => i.MatchLdfld<DartMaggot>("stuckInChunk"),
i => i.MatchCallOrCallvirt<BodyChunk>("get_owner"),
i => i.MatchIsinst<Player>(),
i => i.Match(OpCodes.Brfalse_S)
);
skipLabel = c.Prev.Operand as ILLabel;
ILInsertHandleDartMaggotStun(c, skipLabel);
// matching for `stuckInChunk.owner is Creature`
c.GotoNext(MoveType.After,
i => i.MatchLdarg(0),
i => i.MatchLdfld<DartMaggot>("stuckInChunk"),
i => i.MatchCallOrCallvirt<BodyChunk>("get_owner"),
i => i.MatchIsinst<Creature>(),
i => i.Match(OpCodes.Brfalse) // watch out! this one isn't _S
);
skipLabel = c.Prev.Operand as ILLabel;
ILInsertHandleDartMaggotStun(c, skipLabel);
}
}