@@ -24,7 +24,11 @@ You should have received a copy of the GNU General Public License
2424using MonoMod ;
2525using MonoMod . Cil ;
2626using System ;
27+ using System . Collections . Generic ;
2728using 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