-
Notifications
You must be signed in to change notification settings - Fork 101
/
CraftingGUI.cs
255 lines (210 loc) · 7.98 KB
/
CraftingGUI.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
using System;
using System.Collections.Generic;
using MagicStorage.Common.Systems;
using MagicStorage.Common.Systems.RecurrentRecipes;
using MagicStorage.Components;
using Terraria;
using Terraria.ID;
using Terraria.ModLoader;
using Terraria.ModLoader.IO;
namespace MagicStorage
{
// Method implementations can also be found in UI/GUIs/CraftingGUI.X.cs
public static partial class CraftingGUI
{
public const int RecipeButtonsAvailableChoice = 0;
//Button location could either be the third (2) or fourth (3) option depending on if the favoriting config is enabled
public static int RecipeButtonsBlacklistChoice => MagicStorageConfig.CraftingFavoritingEnabled ? 3 : 2;
public const int RecipeButtonsFavoritesChoice = 2;
public const int Padding = 4;
public const int RecipeColumns = 10;
public const int IngredientColumns = 7;
public const float InventoryScale = 0.85f;
public const float SmallScale = 0.7f;
public const int StartMaxCraftTimer = 20;
public const int StartMaxRightClickTimer = 20;
public const float ScrollBar2ViewSize = 1f;
public const float RecipeScrollBarViewSize = 1f;
internal static Recipe selectedRecipe;
[ThreadStatic]
public static bool CatchDroppedItems;
[ThreadStatic]
public static List<Item> DroppedItems;
internal static void Unload()
{
ClearAllCollections();
PlayerZoneCache.FreeCache(true);
}
internal static void ClearAllCollections() {
recipes.Clear();
recipeAvailable.Clear();
storageItems.Clear();
storageItemsFromModules.Clear();
sourceItems.Clear();
storageItemInfo.Clear();
items.Clear();
itemCounts.Clear();
itemCountsByPrefix.Clear();
sourceItemsFromModules.Clear();
blockStorageItems.Clear();
selectedRecipe = null;
ResetRecentRecipeCache();
ResetRefreshCache();
}
internal static void Reset() {
Campfire = false;
craftTimer = 0;
maxCraftTimer = StartMaxCraftTimer;
craftAmountTarget = 1;
}
internal static TEStorageHeart GetHeart() => StoragePlayer.LocalPlayer.GetStorageHeart();
internal static TECraftingAccess GetCraftingEntity() => StoragePlayer.LocalPlayer.GetCraftingAccess();
internal static List<Item> GetCraftingStations() => GetCraftingEntity()?.stations ?? new();
/// <summary>
/// Returns the recursion crafting tree for <paramref name="recipe"/> if it exists and recursion is enabled, or <see langword="null"/> otherwise.
/// </summary>
/// <param name="recipe">The recipe</param>
/// <param name="toCraft">The quantity of the final recipe's crafted item to create</param>
/// <param name="blockedSubrecipeIngredient">An optional item ID representing ingredient trees that should be ignored</param>
public static OrderedRecipeTree GetCraftingTree(Recipe recipe, int toCraft = 1, int blockedSubrecipeIngredient = 0) {
if (!MagicStorageConfig.IsRecursionEnabled || !recipe.TryGetRecursiveRecipe(out RecursiveRecipe recursiveRecipe))
return null;
return recursiveRecipe.GetCraftingTree(toCraft, available: GetCurrentInventory(), blockedSubrecipeIngredient);
}
public static bool RecipeGroupMatch(Recipe recipe, int inventoryType, int requiredType)
{
foreach (int num in recipe.acceptedGroups)
{
RecipeGroup recipeGroup = RecipeGroup.recipeGroups[num];
if (recipeGroup.ContainsItem(inventoryType) && recipeGroup.ContainsItem(requiredType))
return true;
}
return false;
}
internal static void SetSelectedRecipe(Recipe recipe)
{
ArgumentNullException.ThrowIfNull(recipe);
NetHelper.Report(true, "Reassigning current recipe...");
selectedRecipe = recipe;
RefreshStorageItems();
blockStorageItems.Clear();
NetHelper.Report(true, "Successfully reassigned current recipe!");
}
/// <summary>
/// Attempts to craft a certain amount of items from a Crafting Interface
/// </summary>
/// <param name="craftingAccess">The tile entity for the Crafting Interface to craft items from</param>
/// <param name="toCraft">How many items should be crafted</param>
public static void Craft(TECraftingAccess craftingAccess, int toCraft) {
if (craftingAccess is null)
return;
StoragePlayer.StorageHeartAccessWrapper wrapper = new(craftingAccess);
//OpenStorage() handles setting the CraftingGUI to use the new storage and Dispose()/CloseStorage() handles reverting it back
if (wrapper.Valid) {
using (wrapper.OpenStorage())
Craft(toCraft);
}
}
private static Dictionary<int, int> GetItemCountsWithBlockedItemsRemoved(bool cloneIfBlockEmpty = false) {
if (!cloneIfBlockEmpty && blockStorageItems.Count == 0)
return itemCounts;
Dictionary<int, int> counts = new(itemCounts);
foreach (var data in blockStorageItems) {
if (counts.TryGetValue(data.Type, out int quantity) && itemCountsByPrefix.TryGetValue(data.Type, out var prefixCounts) && prefixCounts.TryGetValue(data.Prefix, out int prefixQuantity) && prefixQuantity > 0) {
quantity -= prefixQuantity;
if (quantity <= 0)
counts.Remove(data.Type);
else
counts[data.Type] = quantity;
}
}
return counts;
}
public static AvailableRecipeObjects GetCurrentInventory(bool cloneIfBlockEmpty = false) {
bool[] availableRecipes = currentlyThreading && MagicUI.activeThread.state is ThreadState { recipeConditionsMetSnapshot: bool[] snapshot } ? snapshot : null;
return new AvailableRecipeObjects(adjTiles, GetItemCountsWithBlockedItemsRemoved(cloneIfBlockEmpty), availableRecipes);
}
internal static List<Item> HandleCraftWithdrawAndDeposit(TEStorageHeart heart, List<Item> toWithdraw, List<Item> results)
{
var items = new List<Item>();
foreach (Item tryWithdraw in toWithdraw)
{
Item withdrawn = heart.TryWithdraw(tryWithdraw, false);
if (!withdrawn.IsAir)
items.Add(withdrawn);
if (withdrawn.stack < tryWithdraw.stack)
{
for (int k = 0; k < items.Count; k++)
{
heart.DepositItem(items[k]);
if (items[k].IsAir)
{
items.RemoveAt(k);
k--;
}
}
return items;
}
}
items.Clear();
foreach (Item result in results)
{
heart.DepositItem(result);
if (!result.IsAir)
items.Add(result);
}
return items;
}
internal static bool TryDepositResult(Item item)
{
int oldStack = item.stack;
TEStorageHeart heart = GetHeart();
if (heart is null)
return false;
heart.TryDeposit(item);
return oldStack != item.stack;
}
internal static Item DoWithdrawResult(int amountToWithdraw, bool toInventory = false)
{
TEStorageHeart heart = GetHeart();
if (heart is null)
return new Item();
Item clone = result.Clone();
clone.stack = Math.Min(amountToWithdraw, clone.maxStack);
if (Main.netMode == NetmodeID.MultiplayerClient) {
ModPacket packet = heart.PrepareClientRequest(toInventory ? TEStorageHeart.Operation.WithdrawToInventoryThenTryModuleInventory : TEStorageHeart.Operation.WithdrawThenTryModuleInventory);
ItemIO.Send(clone, packet, true, true);
packet.Send();
return new Item();
}
Item withdrawn = heart.Withdraw(clone, false);
if (withdrawn.IsAir)
withdrawn = TryToWithdrawFromModuleItems(clone, true);
return withdrawn;
}
internal static Item TryToWithdrawFromModuleItems(Item toWithdraw, bool wasAlreadyCloned) {
Item withdrawn;
if (items.Count != numItemsWithoutSimulators) {
//Heart did not contain the item; try to withdraw from the module items
Item item = wasAlreadyCloned ? toWithdraw : toWithdraw.Clone();
TEStorageUnit.WithdrawFromItemCollection(sourceItemsFromModules, item, out withdrawn,
onItemRemoved: k => {
int index = k + numItemsWithoutSimulators;
items.RemoveAt(index);
},
onItemStackReduced: (k, stack) => {
int index = k + numItemsWithoutSimulators;
Item item = items[index];
itemCounts[item.type] -= stack;
itemCountsByPrefix[item.type][item.prefix] -= stack;
});
if (!withdrawn.IsAir) {
MagicUI.SetRefresh();
SetNextDefaultRecipeCollectionToRefresh(withdrawn.type);
}
} else
withdrawn = new Item();
return withdrawn;
}
}
}