diff --git a/OTAPI.Patcher/Targets/PCServerTarget.cs b/OTAPI.Patcher/Targets/PCServerTarget.cs index bc86d7505..b8c75568c 100644 --- a/OTAPI.Patcher/Targets/PCServerTarget.cs +++ b/OTAPI.Patcher/Targets/PCServerTarget.cs @@ -210,6 +210,7 @@ public void Patch() mm.Module.GetType("Terraria.NPC").CreateHooks(mm); mm.Module.GetType("Terraria.WorldGen").CreateHooks(mm); mm.Module.GetType("Terraria.Chat.ChatHelper").CreateHooks(mm); + mm.Module.GetType("Terraria.GameContent.ItemDropRules.CommonCode").CreateHooks(mm); mm.Module.GetType("Terraria.IO.WorldFile").CreateHooks(mm); mm.Module.GetType("Terraria.Net.NetManager").CreateHooks(mm); mm.Module.GetType("Terraria.Projectile").CreateHooks(mm); diff --git a/OTAPI.Scripts/Mods/HookNpcBossBag.Server.cs b/OTAPI.Scripts/Mods/HookNpcBossBag.Server.cs deleted file mode 100644 index 30d02b273..000000000 --- a/OTAPI.Scripts/Mods/HookNpcBossBag.Server.cs +++ /dev/null @@ -1,145 +0,0 @@ -/* -Copyright (C) 2020 DeathCradle - -This file is part of Open Terraria API v3 (OTAPI) - -This program is free software: you can redistribute it and/or modify -it under the terms of the GNU General Public License as published by -the Free Software Foundation, either version 3 of the License, or -(at your option) any later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program. If not, see . -*/ -#pragma warning disable CS8321 // Local function is declared but never used -#pragma warning disable CS0436 // Type conflicts with imported type - -using System; -using System.Linq; -using ModFramework; -using Mono.Cecil; -using Mono.Cecil.Cil; - -/// -/// @doc Creates Hooks.NPC.BossBag. Allows plugins to cancel boss bag items. -/// -[Modification(ModType.PreMerge, "Hooking npc boss bags")] -[MonoMod.MonoModIgnore] -void HookNpcBossBag(ModFramework.ModFwModder modder) -{ - // replace NewItem calls, and handle the -1 result to cancel the method from actioning. - - var csr = modder.GetILCursor(() => (new Terraria.NPC()).DropItemInstanced(default, default, 0, 0, false)); - var callback = csr.Module.ImportReference( -#if TerrariaServer_EntitySourcesActive || Terraria_EntitySourcesActive || tModLoader_EntitySourcesActive - modder.GetMethodDefinition(() => OTAPI.Hooks.NPC.InvokeBossBag(null, 0, 0, 0, 0, 0, 0, false, 0, false, false, null)) -#else - modder.GetMethodDefinition(() => OTAPI.Hooks.NPC.InvokeBossBag(0, 0, 0, 0, 0, 0, false, 0, false, false, null)) -#endif - ); - - var instructions = csr.Body.Instructions.Where(x => x.OpCode == OpCodes.Call - && x.Operand is MethodReference mref && mref.Name == "NewItem" - && x.Next.OpCode == OpCodes.Stloc_0); - - if (instructions.Count() != 1) throw new NotSupportedException("Only one server NewItem call expected in DropBossBags."); - - var ins = instructions.First(); - - ins.Operand = callback; - - csr.Goto(ins); - csr.EmitAll( - new { OpCodes.Ldarg_0 } - ); - - csr.Goto(ins.Next.Next); - csr.EmitAll( - new { OpCodes.Ldloc_0 }, - new { OpCodes.Ldc_I4_M1 }, - new { OpCodes.Ceq }, - new { OpCodes.Brfalse_S, Operand = ins.Next.Next }, - new { OpCodes.Ret } - ); -} - -namespace OTAPI -{ - public static partial class Hooks - { - public static partial class NPC - { - public class BossBagEventArgs : EventArgs - { - public HookResult? Result { get; set; } - -#if TerrariaServer_EntitySourcesActive || Terraria_EntitySourcesActive || tModLoader_EntitySourcesActive - public Terraria.DataStructures.IEntitySource Source { get; set; } -#endif - - public Terraria.NPC Npc { get; set; } - public int X { get; set; } - public int Y { get; set; } - public int Width { get; set; } - public int Height { get; set; } - public int Type { get; set; } - public int Stack { get; set; } - public bool NoBroadcast { get; set; } - public int Pfix { get; set; } - public bool NoGrabDelay { get; set; } - public bool ReverseLookup { get; set; } - } - public static event EventHandler BossBag; - - public static int InvokeBossBag( -#if TerrariaServer_EntitySourcesActive || Terraria_EntitySourcesActive || tModLoader_EntitySourcesActive - Terraria.DataStructures.IEntitySource Source, -#endif - int X, - int Y, - int Width, - int Height, - int Type, - int Stack, - bool noBroadcast, - int pfix, - bool noGrabDelay, - bool reverseLookup, - Terraria.NPC npc - ) - { - var args = new BossBagEventArgs() - { -#if TerrariaServer_EntitySourcesActive || Terraria_EntitySourcesActive || tModLoader_EntitySourcesActive - Source = Source, -#endif - X = X, - Y = Y, - Width = Width, - Height = Height, - Type = Type, - Stack = Stack, - NoBroadcast = noBroadcast, - Pfix = pfix, - NoGrabDelay = noGrabDelay, - ReverseLookup = reverseLookup, - Npc = npc, - }; - BossBag?.Invoke(null, args); - if (args.Result == HookResult.Cancel) - return -1; - -#if TerrariaServer_EntitySourcesActive || Terraria_EntitySourcesActive || tModLoader_EntitySourcesActive - return Terraria.Item.NewItem(Source, args.X, args.Y, args.Width, args.Height, args.Type, args.Stack, args.NoBroadcast, args.Pfix, args.NoGrabDelay, args.ReverseLookup); -#else - return Terraria.Item.NewItem(args.X, args.Y, args.Width, args.Height, args.Type, args.Stack, args.NoBroadcast, args.Pfix, args.NoGrabDelay, args.ReverseLookup); -#endif - } - } - } -} \ No newline at end of file diff --git a/OTAPI.Scripts/Patches/PatchNpcBossBag.Server.cs b/OTAPI.Scripts/Patches/PatchNpcBossBag.Server.cs new file mode 100644 index 000000000..e4128e22f --- /dev/null +++ b/OTAPI.Scripts/Patches/PatchNpcBossBag.Server.cs @@ -0,0 +1,112 @@ +/* +Copyright (C) 2020 DeathCradle + +This file is part of Open Terraria API v3 (OTAPI) + +This program is free software: you can redistribute it and/or modify +it under the terms of the GNU General Public License as published by +the Free Software Foundation, either version 3 of the License, or +(at your option) any later version. + +This program is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +GNU General Public License for more details. + +You should have received a copy of the GNU General Public License +along with this program. If not, see . +*/ +#pragma warning disable CS8321 // Local function is declared but never used +#pragma warning disable CS0436 // Type conflicts with imported type + +using System; +using ModFramework; +using MonoMod.Cil; +using Mono.Cecil; +using Mono.Cecil.Cil; +using System.Linq; +using MonoMod; + +/// +/// @doc Creates Hooks.NPC.BossBag. Allows plugins to customize bosses loot as well as distributing it. +/// + +[MonoModIgnore] +partial class NpcBossBag +{ + [Modification(ModType.PreMerge, "Hooking NPC Boss Bag")] + static void HookNpcBossBag(ModFwModder modder) + { + foreach (var csr in new[] { + modder.GetILCursor(() => (new Terraria.NPC()).DropItemInstanced(default, default, 0, 0, false)), + modder.GetILCursor(() => Terraria.GameContent.ItemDropRules.CommonCode.DropItemLocalPerClientAndSetNPCMoneyTo0(default, default, default, default)) + }) + { + csr.GotoNext(MoveType.After, + i => i.OpCode == OpCodes.Ldsfld && + i.Operand is FieldReference fieldReference && + fieldReference.Name == "netMode" && + fieldReference.DeclaringType.FullName == "Terraria.Main", + i => i.OpCode == OpCodes.Ldc_I4_2, + i => i.OpCode == OpCodes.Bne_Un + ); + + csr.Emit(OpCodes.Ldarg_0); + if (csr.Method.Name == "DropItemLocalPerClientAndSetNPCMoneyTo0") + { + csr.Emit(OpCodes.Ldarg_1); + csr.Emit(OpCodes.Ldarg_2); + csr.Emit(OpCodes.Ldarg_3); + } + else + { + csr.Emit(OpCodes.Ldarg_3); + csr.Emit(OpCodes.Ldarg, 4); + csr.Emit(OpCodes.Ldarg, 5); + } + csr.EmitDelegate(OTAPI.Hooks.NPC.InvokeBossBag); + csr.Emit(OpCodes.Nop); + csr.Emit(OpCodes.Nop); + + csr.Previous.Previous.OpCode = OpCodes.Brtrue; + csr.Previous.Previous.Operand = csr.Next; + + var targetBranch = csr.Method.Body.Instructions.Reverse().Where(x => x.OpCode == OpCodes.Ldarg_0).FirstOrDefault(); + + csr.Previous.OpCode = OpCodes.Br; + csr.Previous.Operand = targetBranch; + } + } +} + +namespace OTAPI +{ + public static partial class Hooks + { + public static partial class NPC + { + public class BossBagEventArgs : EventArgs + { + public HookResult? Result { get; set; } + public Terraria.NPC NPC { get; set; } + public int ItemID { get; set; } + public int Stack { get; set; } + public bool InteractionRequired { get; set; } + } + public static event EventHandler BossBag; + + public static bool InvokeBossBag(Terraria.NPC npc, int itemid, int stack, bool interactionRequired) + { + var args = new BossBagEventArgs() + { + NPC = npc, + ItemID = itemid, + Stack = stack, + InteractionRequired = interactionRequired + }; + BossBag?.Invoke(null, args); + return args.Result != HookResult.Cancel; + } + } + } +} diff --git a/OTAPI.Scripts/Patches/PatchNpcStrikeArgs.Server.cs b/OTAPI.Scripts/Patches/PatchNpcStrikeArgs.Server.cs index 4280e9ad9..f4ede2d7e 100644 --- a/OTAPI.Scripts/Patches/PatchNpcStrikeArgs.Server.cs +++ b/OTAPI.Scripts/Patches/PatchNpcStrikeArgs.Server.cs @@ -15,27 +15,27 @@ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the You should have received a copy of the GNU General Public License along with this program. If not, see . -*/ +*/ #if !tModLoaderServer_V1_3 using ModFramework; -using Mono.Cecil; -using Mono.Cecil.Cil; -using MonoMod; +using Mono.Cecil; +using Mono.Cecil.Cil; +using MonoMod; using System; -using System.Linq; - -/// -/// @doc Adds a Terraria.Entity entity parameter to Terraria.NPC.StrikeNPC. -/// -[MonoModIgnore] -partial class NpcStrikeArgs -{ - //static ParameterDefinition Entity { get; set; } - //static MethodDefinition StrikeNPC { get; set; } - - [Modification(ModType.PreMerge, "Patching in entity source for NPC strike")] - static void PatchNpcStrikeArgs(ModFwModder modder) - { +using System.Linq; + +/// +/// @doc Adds a Terraria.Entity entity parameter to Terraria.NPC.StrikeNPC. +/// +[MonoModIgnore] +partial class NpcStrikeArgs +{ + //static ParameterDefinition Entity { get; set; } + //static MethodDefinition StrikeNPC { get; set; } + + [Modification(ModType.PreMerge, "Patching in entity source for NPC strike")] + static void PatchNpcStrikeArgs(ModFwModder modder) + { var csr = modder.GetILCursor(() => (new Terraria.NPC()).StrikeNPC(0, 0, 0, false, false, false)); var redirects = csr.Method.DeclaringType.Methods .Where(x => (HookEmitter.HookMethodNamePrefix + x.Name) == csr.Method.Name || ("orig_" + x.Name) == csr.Method.Name) @@ -45,16 +45,16 @@ static void PatchNpcStrikeArgs(ModFwModder modder) foreach (var method in redirects.Append(csr)) { ParameterDefinition Entity; - method.Method.Parameters.Add(Entity = new ("entity", - ParameterAttributes.HasDefault | ParameterAttributes.Optional, - - modder.Module.ImportReference(modder.GetDefinition()) + method.Method.Parameters.Add(Entity = new ("entity", + ParameterAttributes.HasDefault | ParameterAttributes.Optional, + + modder.Module.ImportReference(modder.GetDefinition()) ) { Constant = null }); - modder.OnRewritingMethodBody += (MonoModder modder, MethodBody body, Instruction instr, int instri) => + modder.OnRewritingMethodBody += (MonoModder modder, MethodBody body, Instruction instr, int instri) => { if (instr.Operand is MethodReference methodReference) { @@ -102,7 +102,7 @@ static void PatchNpcStrikeArgs(ModFwModder modder) } } }; - } - } + } + } } #endif \ No newline at end of file