-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathInventoryInteraction.cs
496 lines (408 loc) · 22.9 KB
/
InventoryInteraction.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
using HarmonyLib;
using static ExtraSlots.Slots;
using static ExtraSlots.ExtraSlots;
using System.Linq;
using System.Collections.Generic;
using UnityEngine;
namespace ExtraSlots
{
internal static class InventoryInteraction
{
public static void UpdatePlayerInventorySize()
{
if (CurrentPlayer == null)
return;
if (CurrentPlayer.m_inventory.m_height != InventoryHeightFull)
LogInfo($"Player inventory height changed {CurrentPlayer.m_inventory.m_height} -> {InventoryHeightFull}");
CurrentPlayer.m_inventory.m_height = InventoryHeightFull;
if (CurrentPlayer.m_tombstone?.GetComponent<Container>() is Container tombstone)
tombstone.m_height = InventoryHeightFull;
CurrentPlayer.m_inventory.Changed();
ItemsSlotsValidation.ValidateItems();
}
[HarmonyPatch(typeof(Player), nameof(Player.Awake))]
private static class Player_Awake_ExcludeRedundantSlots
{
private static void Postfix(Player __instance)
{
__instance.m_inventory.m_height = InventoryHeightFull;
}
}
[HarmonyPatch(typeof(Player), nameof(Player.OnSpawned))]
private static class Player_OnSpawned_UpdateInventoryOnSpawn
{
private static void Postfix(Player __instance)
{
if (__instance != Player.m_localPlayer)
return;
if (!IsAwaitingForSlotsUpdate())
UpdatePlayerInventorySize();
}
}
[HarmonyPatch(typeof(Player), nameof(Player.Update))]
private static class Player_Update_UpdateInventoryHeight
{
private static Container tombstoneContainer = null!;
private static void Postfix(Player __instance)
{
__instance.m_inventory.m_height = InventoryHeightFull;
tombstoneContainer ??= __instance.m_tombstone.GetComponent<Container>();
if (tombstoneContainer != null)
tombstoneContainer.m_height = __instance.m_inventory.m_height;
}
}
[HarmonyPatch(typeof(Player), nameof(Player.Save))]
private static class Player_Save_SaveLastEquippedSlots
{
private static void Prefix(Player __instance)
{
if (__instance.GetInventory() != PlayerInventory)
return;
SaveLastEquippedSlotsToItems();
}
}
[HarmonyPatch(typeof(Player), nameof(Player.AutoPickup))]
public static class Player_AutoPickup_PreventAutoPickupInExtraSlots
{
public static bool preventAddItem = false;
[HarmonyPriority(Priority.First)]
private static void Prefix(Player __instance) => preventAddItem = preventAutoPickup.Value && __instance == CurrentPlayer;
[HarmonyPriority(Priority.First)]
private static void Postfix() => preventAddItem = false;
}
[HarmonyPatch(typeof(Inventory), nameof(Inventory.SlotsUsedPercentage))]
private static class Inventory_SlotsUsedPercentage_ExcludeRedundantSlots
{
private static void Postfix(Inventory __instance, ref float __result)
{
if (__instance != PlayerInventory)
return;
__result = (float)__instance.m_inventory.Count / InventorySizeActive * 100f;
LogDebug($"Inventory.SlotsUsedPercentage: {__result}");
}
}
[HarmonyPatch(typeof(Inventory), nameof(Inventory.GetEmptySlots))]
private static class Inventory_GetEmptySlots_CheckRegularInventoryAndQuickSlots
{
[HarmonyPriority(Priority.First)]
private static void Postfix(Inventory __instance, ref int __result)
{
if (__instance != PlayerInventory)
return;
__result = InventoryHeightPlayer * __instance.m_width - __instance.m_inventory.Count(item => !API.IsItemInSlot(item)) + (Player_AutoPickup_PreventAutoPickupInExtraSlots.preventAddItem ? 0 :GetEmptyQuickSlots());
LogDebug($"Inventory.GetEmptySlots: {__result}, AutoPickup: {Player_AutoPickup_PreventAutoPickupInExtraSlots.preventAddItem}");
}
}
[HarmonyPatch(typeof(Inventory), nameof(Inventory.FindEmptySlot))]
private static class Inventory_FindEmptySlot_FindAppropriateSlot
{
[HarmonyPriority(Priority.First)]
private static void Prefix(Inventory __instance)
{
if (__instance != PlayerInventory)
return;
__instance.m_height = InventoryHeightPlayer;
}
[HarmonyPriority(Priority.First)]
private static void Postfix(Inventory __instance, ref Vector2i __result)
{
if (__instance != PlayerInventory)
return;
__instance.m_height = InventoryHeightFull;
if (__result == emptyPosition
&& InventoryGui.instance.m_craftTimer >= InventoryGui.instance.m_craftDuration
&& InventoryGui.instance.m_craftUpgradeItem is ItemDrop.ItemData item
&& TryFindFreeSlotForItem(item, out Slot slot))
{
__result = slot.GridPosition;
LogDebug($"Inventory.FindEmptySlot for upgraded item {item.m_shared.m_name} {__result}");
}
if (__result == emptyPosition && TryFindFreeEquipmentSlotForItem(Inventory_AddItem_ByName_FindAppropriateSlot.itemToFindSlot, out Slot slot1))
{
__result = slot1.GridPosition;
LogDebug($"Inventory.FindEmptySlot free equipment slot for AddItem_ByName item {Inventory_AddItem_ByName_FindAppropriateSlot.itemToFindSlot.m_shared.m_name} {__result}");
}
if (__result == emptyPosition && TryFindFreeSlotForItem(Inventory_AddItem_ByName_FindAppropriateSlot.itemToFindSlot, out Slot slot2))
{
__result = slot2.GridPosition;
LogDebug($"Inventory.FindEmptySlot free slot for AddItem_ByName item {Inventory_AddItem_ByName_FindAppropriateSlot.itemToFindSlot.m_shared.m_name} {__result}");
}
if (__result == emptyPosition && !Player_AutoPickup_PreventAutoPickupInExtraSlots.preventAddItem)
{
__result = FindEmptyQuickSlot();
LogDebug($"Inventory.FindEmptySlot free quick slot {__result}");
}
if (__result == emptyPosition && Inventory_AddItem_ByName_FindAppropriateSlot.itemToFindSlot != null && TryMakeFreeSpaceInPlayerInventory(tryFindRegularInventorySlot: true, out Vector2i gridPos))
{
__result = gridPos;
LogDebug($"Inventory.FindEmptySlot made free space for AddItem_ByName item {Inventory_AddItem_ByName_FindAppropriateSlot.itemToFindSlot.m_shared.m_name} {__result}");
}
}
}
[HarmonyPatch(typeof(Inventory), nameof(Inventory.HaveEmptySlot))]
private static class Inventory_HaveEmptySlot_CheckRegularInventoryAndQuickSlots
{
[HarmonyPriority(Priority.First)]
private static void Postfix(Inventory __instance, ref bool __result)
{
__result = __instance.GetEmptySlots() > 0;
}
}
private static bool PassDropItem(string source, InventoryGrid grid, Inventory fromInventory, ItemDrop.ItemData item, Vector2i pos)
{
if (item.m_gridPos == pos)
return true;
// If the equipped item from slot is dropped at player inventory
if (grid.m_inventory == PlayerInventory && GetItemSlot(item) is Slot itemSlot && itemSlot.IsEquipmentSlot && Player.m_localPlayer.IsItemEquiped(item))
{
if (GetSlotInGrid(pos) is not Slot posSlot)
{
LogDebug($"{source} Prevented dropping equipped item {item.m_shared.m_name} {item.m_gridPos} into regular inventory {pos}");
return false;
};
if (!IsSameSlotType(itemSlot, posSlot))
{
LogDebug($"{source} Prevented dropping equipped item {item.m_shared.m_name} {item.m_gridPos} into slot with other type {posSlot}");
return false;
}
}
// If target slot is in player inventory and is extra slot
if (grid.m_inventory == PlayerInventory && GetSlotInGrid(pos) is Slot targetSlot)
{
// If the dropped item is unfit for target slot
if (!targetSlot.ItemFits(item))
{
LogDebug($"{source} Prevented dropping {item.m_shared.m_name} {item.m_gridPos} into unfit slot {targetSlot}");
return false;
}
// If the dropped item is not from equipment slot and target item is equipped item at equipment slot
if (targetSlot.IsEquipmentSlot && targetSlot.Item != null && Player.m_localPlayer.IsItemEquiped(targetSlot.Item) && (GetItemSlot(item) is not Slot fromSlot || !fromSlot.IsEquipmentSlot))
{
LogDebug($"{source} Prevented dropping {item.m_shared.m_name} {item.m_gridPos} into occupied equipment slot {targetSlot}");
return false;
}
}
ItemDrop.ItemData itemAt = grid.m_inventory.GetItemAt(pos.x, pos.y);
// If dropped item is in slot and interchanged item is unfit for dragged item slot
if (itemAt != null && fromInventory == PlayerInventory && GetSlotInGrid(item.m_gridPos) is Slot slot1 && !slot1.ItemFits(itemAt))
{
LogDebug($"{source} Prevented swapping {item.m_shared.m_name} {slot1} with unfit item {itemAt.m_shared.m_name} {pos}");
return false;
}
return true;
}
[HarmonyPatch(typeof(InventoryGui), nameof(InventoryGui.OnSelectedItem))]
public static class InventoryGui_OnSelectedItem_GetEquippedDragItem
{
public static bool Prefix(InventoryGui __instance, InventoryGrid grid, Vector2i pos)
{
if (Player.m_localPlayer && !Player.m_localPlayer.IsTeleporting() && __instance.m_dragGo && __instance.m_dragItem != null && __instance.m_dragInventory != null)
return PassDropItem("InventoryGui.OnSelectedItem", grid, __instance.m_dragInventory, __instance.m_dragItem, pos);
return true;
}
}
[HarmonyPatch(typeof(InventoryGrid), nameof(InventoryGrid.DropItem))]
public static class InventoryGrid_DropItem_DropPrevention
{
public static bool Prefix(InventoryGrid __instance, Inventory fromInventory, ItemDrop.ItemData item, Vector2i pos) => PassDropItem("InventoryGrid.DropItem", __instance, fromInventory, item, pos);
}
[HarmonyPatch(typeof(Inventory), nameof(Inventory.AddItem), typeof(ItemDrop.ItemData), typeof(int), typeof(int), typeof(int))]
private static class Inventory_AddItem_ItemData_amount_x_y_TargetPositionRerouting
{
[HarmonyPriority(Priority.Last)]
private static void Prefix(Inventory __instance, ItemDrop.ItemData item, ref int x, ref int y)
{
if (__instance != PlayerInventory)
return;
// Known materials and player keys are not yet loaded, custom components are not initialized, skip validation
if (Inventory_AddItem_OnLoad_FindAppropriateSlot.inCall && CurrentPlayer.m_isLoading)
return;
if (item == null)
return;
// If another item is at grid - let stack logic go
if (__instance.GetItemAt(x, y) is ItemDrop.ItemData gridTakenItem)
{
LogDebug($"Inventory.AddItem X Y item {item.m_shared.m_name} adding at {x},{y} position is taken {gridTakenItem.m_shared.m_name}");
return;
}
// If the dropped item fits for target slot
if (GetSlotInGrid(new Vector2i(x, y)) is not Slot slot || slot.ItemFits(item))
return;
LogDebug($"Inventory.AddItem X Y item {item.m_shared.m_name} adding at {x},{y} unfits slot {slot} {slot.GridPosition}");
if (TryFindFreeSlotForItem(item, out Slot freeSlot))
{
LogDebug($"Inventory.AddItem X Y Rerouted {item.m_shared.m_name} from {x},{y} to free slot {freeSlot} {freeSlot.GridPosition}");
x = freeSlot.GridPosition.x;
y = freeSlot.GridPosition.y;
}
if (TryMakeFreeSpaceInPlayerInventory(tryFindRegularInventorySlot: true, out Vector2i gridPos))
{
LogDebug($"Inventory.AddItem X Y Rerouted {item.m_shared.m_name} from {x},{y} to created free space {gridPos}");
x = gridPos.x;
y = gridPos.y;
}
}
[HarmonyPriority(Priority.Last)]
private static void Postfix(Inventory __instance, ItemDrop.ItemData item, int x, int y, int amount, ref bool __result)
{
if (__instance == PlayerInventory && Inventory_AddItem_OnLoad_FindAppropriateSlot.inCall && !__result)
{
amount = Mathf.Min(amount, item.m_stack);
// Prevent item disappearing
ItemDrop.ItemData itemData = item.Clone();
itemData.m_stack = amount;
LogMessage($"Item dissappearing prevention at Inventory.AddItem_OnLoad -> Inventory.AddItem_ItemData_amount_x_y: item {item.m_shared.m_name} at {x},{y} amount {amount}");
if (TryFindFreeEquipmentSlotForItem(itemData, out Slot equipmentSlot))
{
itemData.m_gridPos = equipmentSlot.GridPosition;
LogDebug($"Inventory.AddItem_ItemData_amount_x_y found free equipment slot for item {itemData.m_shared.m_name}. Position rerouted {x},{y} -> {itemData.m_gridPos}");
}
else if (TryFindFreeSlotForItem(itemData, out Slot slot))
{
itemData.m_gridPos = slot.GridPosition;
LogDebug($"Inventory.AddItem_ItemData_amount_x_y found free slot for item {itemData.m_shared.m_name}. Position rerouted {x},{y} -> {itemData.m_gridPos}");
}
else if (TryMakeFreeSpaceInPlayerInventory(tryFindRegularInventorySlot: true, out Vector2i gridPos))
{
itemData.m_gridPos = gridPos;
LogDebug($"Inventory.AddItem_ItemData_amount_x_y made free space for item {itemData.m_shared.m_name}. Position rerouted {x},{y} -> {itemData.m_gridPos}");
}
else
{
itemData.m_gridPos = new Vector2i(InventoryWidth - 1, InventoryHeightFull - 1); // Put in the last slot and item will find its place sooner or later
LogDebug($"Inventory.AddItem_ItemData_amount_x_y item {itemData.m_shared.m_name} put in the last slot to find place later. Position rerouted {x},{y} -> {itemData.m_gridPos}");
}
__instance.m_inventory.Add(itemData);
item.m_stack -= amount;
__result = true;
__instance.Changed();
}
}
}
[HarmonyPatch(typeof(Inventory), nameof(Inventory.CanAddItem), typeof(ItemDrop.ItemData), typeof(int))]
private static class Inventory_CanAddItem_ItemData_TryFindAppropriateExtraSlot
{
[HarmonyPriority(Priority.First)]
private static void Prefix(Inventory __instance)
{
if (__instance != PlayerInventory)
return;
__instance.m_height = InventoryHeightPlayer;
}
[HarmonyPriority(Priority.First)]
private static void Postfix(Inventory __instance, ItemDrop.ItemData item, int stack, ref bool __result)
{
if (__instance != PlayerInventory)
return;
__instance.m_height = InventoryHeightFull;
if (__result)
return;
int freeStackSpace = __instance.FindFreeStackSpace(item.m_shared.m_name, item.m_worldLevel);
int freeQuickSlotStackSpace = __instance.GetEmptySlots() * item.m_shared.m_maxStackSize;
if (__result = freeStackSpace + freeQuickSlotStackSpace >= stack)
LogDebug($"Inventory.CanAddItem_ItemData_int item {item.m_shared.m_name} result {__result}, free stack space: {freeStackSpace}, free quick slot stack space: {freeQuickSlotStackSpace}, have free stack space");
else if (stack <= item.m_shared.m_maxStackSize && !Player_AutoPickup_PreventAutoPickupInExtraSlots.preventAddItem)
{
if (__result = TryFindFreeSlotForItem(item, out Slot slot))
LogDebug($"Inventory.CanAddItem_ItemData_int item {item.m_shared.m_name} result {__result}, free stack space: {freeStackSpace}, free quick slot stack space: {freeQuickSlotStackSpace}, no free stack space, free single slot found {slot} {slot.GridPosition}");
}
}
}
[HarmonyPatch(typeof(Inventory), nameof(Inventory.AddItem), typeof(ItemDrop.ItemData))]
private static class Inventory_AddItem_ItemData_TryFindAppropriateExtraSlot
{
[HarmonyPriority(Priority.First)]
private static void Postfix(Inventory __instance, ItemDrop.ItemData item, ref bool __result)
{
if (__instance != PlayerInventory)
return;
if (__result)
return;
if (Player_AutoPickup_PreventAutoPickupInExtraSlots.preventAddItem)
return;
if (!TryFindFreeSlotForItem(item, out Slot slot))
return;
LogDebug($"Inventory.AddItem_Item item {item.m_shared.m_name} found free slot {slot} {slot.GridPosition}");
item.m_gridPos = slot.GridPosition;
__instance.m_inventory.Add(item);
__instance.Changed();
__result = true;
}
}
[HarmonyPatch(typeof(Inventory), nameof(Inventory.AddItem), typeof(ItemDrop.ItemData), typeof(Vector2i))]
private static class Inventory_AddItem_ItemData_pos_TargetPositionRerouting
{
[HarmonyPriority(Priority.First)]
private static void Prefix(Inventory __instance, ItemDrop.ItemData item, ref Vector2i pos)
{
if (__instance != PlayerInventory)
return;
if (item == null)
return;
// If already overlapping or not slot or slot fit - let logic go
if (__instance.GetItemAt(pos.x, pos.y) != null || GetSlotInGrid(pos) is not Slot slot || slot.ItemFits(item))
return;
// If inventory has available free stack items with the same quality - let stack logic go
if (item.m_shared.m_maxStackSize > 1)
{
int freeStacks = __instance.GetAllItems()
.Where(itemInv => item.m_shared.m_name == itemInv.m_shared.m_name && item.m_quality == itemInv.m_quality && item.m_worldLevel == itemInv.m_worldLevel)
.Sum(itemInv => itemInv.m_shared.m_maxStackSize - itemInv.m_stack);
if (freeStacks > item.m_stack)
return;
LogDebug($"Inventory.AddItem_Item_Vector2i item {item.m_shared.m_name}x{item.m_stack} adding at {pos} not enough free stack space {freeStacks}");
}
if (TryFindFreeSlotForItem(item, out Slot freeSlot))
{
LogDebug($"Inventory.AddItem_Item_Vector2i Rerouted {item.m_shared.m_name} from {pos} to free slot {freeSlot} {freeSlot.GridPosition}");
pos = freeSlot.GridPosition;
return;
}
if (TryMakeFreeSpaceInPlayerInventory(tryFindRegularInventorySlot: true, out Vector2i gridPos))
{
LogDebug($"Inventory.AddItem_Item_Vector2i Rerouted {item.m_shared.m_name} from {pos} to created free space {gridPos}");
pos = gridPos;
return;
}
}
}
[HarmonyPatch(typeof(Inventory), nameof(Inventory.AddItem), typeof(string), typeof(int), typeof(int), typeof(int), typeof(long), typeof(string), typeof(Vector2i), typeof(bool))]
public static class Inventory_AddItem_ByName_FindAppropriateSlot
{
public static ItemDrop.ItemData itemToFindSlot = null;
[HarmonyPriority(Priority.First)]
private static void Prefix(Inventory __instance, string name)
{
if (__instance != PlayerInventory)
return;
ItemDrop component = ObjectDB.instance?.GetItemPrefab(name)?.GetComponent<ItemDrop>();
if (component == null)
return;
if (component.m_itemData.m_shared.m_maxStackSize > 1)
return;
itemToFindSlot = component.m_itemData;
}
[HarmonyPriority(Priority.First)]
private static void Postfix() => itemToFindSlot = null;
}
[HarmonyPatch(typeof(Inventory), nameof(Inventory.AddItem), typeof(string), typeof(int), typeof(float), typeof(Vector2i), typeof(bool), typeof(int), typeof(int), typeof(long), typeof(string), typeof(Dictionary<string, string>), typeof(int), typeof(bool))]
public static class Inventory_AddItem_OnLoad_FindAppropriateSlot
{
public static bool inCall = false;
[HarmonyPriority(Priority.First)]
private static void Prefix() => inCall = true;
[HarmonyPriority(Priority.First)]
private static void Postfix() => inCall = false;
}
[HarmonyPatch(typeof(Inventory), nameof(Inventory.MoveInventoryToGrave))]
private static class Inventory_MoveInventoryToGrave_UpdateGraveInventory
{
private static void Prefix(Inventory original)
{
if (original != PlayerInventory)
return;
original.m_height = InventoryHeightFull;
}
}
}
}