Skip to content

Commit 932a295

Browse files
committed
Implement Quick Stack hook for
1 parent ec432e2 commit 932a295

1 file changed

Lines changed: 65 additions & 1 deletion

File tree

OTAPI.Scripts/Patches/HookChestQuickStack.Server.cs

Lines changed: 65 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,11 @@ You should have received a copy of the GNU General Public License
2424
using MonoMod;
2525
using MonoMod.Cil;
2626
using System;
27+
using System.Collections.Generic;
2728
using System.Linq;
29+
using Terraria;
30+
using Terraria.GameContent;
31+
using Terraria.ID;
2832

2933
/// <summary>
3034
/// @doc Creates Hooks.Chest.QuickStack.
@@ -39,7 +43,67 @@ partial class ChestHooks
3943
static void HookChestQuickStack(ModFwModder modder)
4044
{
4145
#if TerrariaServer_1450_OrAbove || Terraria__1450_OrAbove || tModLoader_1450_OrAbove
42-
Console.WriteLine("[TODO] reimplement HookChestQuickStack for 1.4.5+");
46+
// used for the out parameter only
47+
List<int> _blockedChests;
48+
49+
// DO NOT use GetILCursor(). The instruction body does not have jumps transformed into labels,
50+
// a process which is required to happen for the edits below to work.
51+
var ctx = new ILContext(modder.GetMethodDefinition(() => QuickStacking.Transfer(default, default, out _blockedChests, false)));
52+
ctx.Invoke((ctx) =>
53+
{
54+
var csr = new ILCursor(ctx);
55+
int count = 0;
56+
57+
while (csr.TryGotoNext(
58+
MoveType.Before,
59+
i => i.MatchLdarg(0),
60+
i => i.MatchLdloc(out _),
61+
i => i.MatchCall(typeof(QuickStacking), nameof(QuickStacking.Consolidate))
62+
))
63+
{
64+
if (++count > 2)
65+
{
66+
throw new Exception($"More than two matches of {nameof(QuickStacking.Consolidate)}.");
67+
}
68+
69+
// Both targets are preceded by a jump instruction exiting the loop
70+
var endInstruction = csr.Prev.Operand;
71+
72+
csr.MoveAfterLabels();
73+
74+
// Load player ID (source.slots[0].Player.whoAmI)
75+
// source
76+
csr.Emit(OpCodes.Ldarg_0);
77+
// ^.slots
78+
csr.Emit(OpCodes.Ldfld, modder.GetFieldDefinition(() => default(QuickStacking.SourceInventory).slots));
79+
// ^[0]
80+
csr.Emit(OpCodes.Ldc_I4_0);
81+
csr.Emit(OpCodes.Ldelem_Any, typeof(PlayerItemSlotID.SlotReference));
82+
// ^.Player
83+
csr.Emit(OpCodes.Ldfld, modder.GetFieldDefinition(() => default(PlayerItemSlotID.SlotReference).Player));
84+
// ^.whoAmI
85+
csr.Emit(OpCodes.Ldfld, modder.GetFieldDefinition(() => default(Player)!.whoAmI));
86+
87+
// Load item
88+
// NOTE: this is very fragile, but I can't think of a way to write it better without significantly bloating the code
89+
csr.Emit(OpCodes.Ldloc_S, (byte)(count == 1 ? 6 : 9));
90+
91+
// Load chest index
92+
// NOTE: this is very fragile, but I can't think of a way to write it better without significantly bloating the code
93+
csr.Emit(OpCodes.Ldloc_S, (byte)(count == 1 ? 7 : 10));
94+
// ^.ChestIndex (property get)
95+
csr.Emit(OpCodes.Callvirt, typeof(QuickStacking.DestinationHelper).GetProperty("ChestIndex")!.GetGetMethod());
96+
97+
// Call hook
98+
csr.Emit(OpCodes.Call, modder.GetMethodDefinition(() => OTAPI.Hooks.Chest.InvokeQuickStack(default, default!, default)));
99+
100+
// Continue if handled
101+
csr.Emit(OpCodes.Brtrue, endInstruction);
102+
103+
// Move after Consolidate call
104+
csr.Goto(csr.Index + 3);
105+
}
106+
});
43107
#else
44108
var csr = modder.GetILCursor(() => Terraria.Chest.PutItemInNearbyChest(null, default));
45109
PutItemInNearbyChest = csr.Method;

0 commit comments

Comments
 (0)