/
ModifierRemovalRecipe.java
338 lines (296 loc) · 12.5 KB
/
ModifierRemovalRecipe.java
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
package slimeknights.tconstruct.tools.recipe;
import com.google.common.collect.ImmutableList;
import com.google.gson.JsonArray;
import com.google.gson.JsonObject;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import lombok.experimental.Accessors;
import net.minecraft.core.Registry;
import net.minecraft.data.recipes.FinishedRecipe;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.GsonHelper;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.item.crafting.RecipeSerializer;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.Level;
import net.minecraftforge.items.ItemHandlerHelper;
import slimeknights.mantle.data.predicate.IJsonPredicate;
import slimeknights.mantle.recipe.helper.LoggingRecipeSerializer;
import slimeknights.mantle.recipe.ingredient.SizedIngredient;
import slimeknights.mantle.util.JsonHelper;
import slimeknights.tconstruct.TConstruct;
import slimeknights.tconstruct.common.TinkerTags;
import slimeknights.tconstruct.library.json.predicate.modifier.ModifierPredicate;
import slimeknights.tconstruct.library.modifiers.Modifier;
import slimeknights.tconstruct.library.modifiers.ModifierEntry;
import slimeknights.tconstruct.library.modifiers.ModifierId;
import slimeknights.tconstruct.library.modifiers.TinkerHooks;
import slimeknights.tconstruct.library.recipe.ITinkerableContainer;
import slimeknights.tconstruct.library.recipe.RecipeResult;
import slimeknights.tconstruct.library.recipe.modifiers.ModifierRecipeLookup;
import slimeknights.tconstruct.library.recipe.modifiers.ModifierSalvage;
import slimeknights.tconstruct.library.recipe.modifiers.adding.ModifierRecipe;
import slimeknights.tconstruct.library.recipe.worktable.AbstractSizedIngredientRecipeBuilder;
import slimeknights.tconstruct.library.recipe.worktable.AbstractWorktableRecipe;
import slimeknights.tconstruct.library.tools.item.IModifiableDisplay;
import slimeknights.tconstruct.library.tools.nbt.IToolStackView;
import slimeknights.tconstruct.library.tools.nbt.ToolStack;
import slimeknights.tconstruct.library.utils.JsonUtils;
import slimeknights.tconstruct.tools.TinkerModifiers;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.Consumer;
import java.util.function.Predicate;
public class ModifierRemovalRecipe extends AbstractWorktableRecipe {
private static final Component TITLE = TConstruct.makeTranslation("recipe", "remove_modifier.title");
private static final Component DESCRIPTION = TConstruct.makeTranslation("recipe", "remove_modifier.description");
private static final Component NO_MODIFIERS = TConstruct.makeTranslation("recipe", "remove_modifier.no_modifiers");
private final SizedIngredient sizedTool;
private final List<ItemStack> leftovers;
private final IJsonPredicate<ModifierId> modifierPredicate;
protected final Predicate<ModifierEntry> entryPredicate;
private List<ModifierEntry> displayModifiers;
public ModifierRemovalRecipe(ResourceLocation id, SizedIngredient toolRequirement, List<SizedIngredient> inputs, List<ItemStack> leftovers, IJsonPredicate<ModifierId> modifierPredicate) {
super(id, Ingredient.EMPTY, inputs);
this.sizedTool = toolRequirement;
this.leftovers = leftovers;
this.modifierPredicate = modifierPredicate;
this.entryPredicate = mod -> modifierPredicate.matches(mod.getId());
}
@Override
public Component getTitle() {
return TITLE;
}
@Override
public boolean matches(ITinkerableContainer inv, Level world) {
if (!sizedTool.test(inv.getTinkerableStack())) {
return false;
}
return ModifierRecipe.checkMatch(inv, inputs);
}
/** Filters the given modifier list */
protected List<ModifierEntry> filter(@Nullable IToolStackView tool, List<ModifierEntry> modifiers) {
if (modifierPredicate != ModifierPredicate.ANY) {
return modifiers.stream().filter(entryPredicate).toList();
}
return modifiers;
}
@Override
public List<ModifierEntry> getModifierOptions(@Nullable ITinkerableContainer inv) {
if (inv == null) {
if (displayModifiers == null) {
displayModifiers = filter(null, ModifierRecipeLookup.getRecipeModifierList());
}
return displayModifiers;
}
return filter(inv.getTinkerable(), inv.getTinkerable().getUpgrades().getModifiers());
}
@Override
public Component getDescription(@Nullable ITinkerableContainer inv) {
if (inv != null && inv.getTinkerable().getUpgrades().getModifiers().stream().noneMatch(entryPredicate)) {
return NO_MODIFIERS;
}
return DESCRIPTION;
}
@Override
public RecipeResult<ToolStack> getResult(ITinkerableContainer inv, ModifierEntry entry) {
ToolStack tool = inv.getTinkerable();
// salvage
tool = tool.copy();
ModifierId modifierId = entry.getId();
ModifierSalvage salvage = ModifierRecipeLookup.getSalvage(inv.getTinkerableStack(), tool, modifierId, entry.getLevel());
// restore the slots
if (salvage != null) {
salvage.updateTool(tool);
}
// first remove hook, primarily for removing raw NBT which is highly discouraged using
int newLevel = tool.getModifierLevel(modifierId) - 1;
Modifier modifier = entry.getModifier();
if (newLevel <= 0) {
modifier.getHook(TinkerHooks.RAW_DATA).removeRawData(tool, modifier, tool.getRestrictedNBT());
}
// remove the actual modifier
tool.removeModifier(modifierId, 1);
// ensure the tool is still valid
Component error = tool.tryValidate();
if (error != null) {
return RecipeResult.failure(error);
}
// if this was the last level, validate the tool is still valid without it
if (newLevel <= 0) {
error = modifier.getHook(TinkerHooks.REMOVE).onRemoved(tool, modifier);
if (error != null) {
return RecipeResult.failure(error);
}
}
// check the modifier requirements
Component validated = ModifierRecipeLookup.checkRequirements(inv.getTinkerableStack(), tool);
if (validated != null) {
return RecipeResult.failure(validated);
}
// successfully removed
return RecipeResult.success(tool);
}
@Override
public int toolResultSize() {
return 64;
}
@Override
public void updateInputs(IToolStackView result, ITinkerableContainer.Mutable inv, ModifierEntry selected, boolean isServer) {
super.updateInputs(result, inv, selected, isServer);
if (isServer) {
for (ItemStack stack : leftovers) {
inv.giveItem(stack.copy());
}
}
}
@Override
public RecipeSerializer<?> getSerializer() {
return TinkerModifiers.removeModifierSerializer.get();
}
/* JEI */
/** Gets a list of tools to display */
@Override
public List<ItemStack> getInputTools() {
if (tools == null) {
tools = sizedTool.getMatchingStacks().stream().map(stack -> {
ItemStack tool = IModifiableDisplay.getDisplayStack(stack.getItem());
if (stack.getCount() > 1) {
tool = ItemHandlerHelper.copyStackWithSize(tool, stack.getCount());
}
return tool;
}).toList();
}
return tools;
}
/** Factory interface for modifier removal recipes */
@FunctionalInterface
public interface Factory {
ModifierRemovalRecipe create(ResourceLocation id, SizedIngredient toolRequirement, List<SizedIngredient> inputs, List<ItemStack> leftovers, IJsonPredicate<ModifierId> modifierPredicate);
}
@RequiredArgsConstructor
public static class Serializer implements LoggingRecipeSerializer<ModifierRemovalRecipe> {
private final Factory factory;
@Override
public ModifierRemovalRecipe fromJson(ResourceLocation id, JsonObject json) {
SizedIngredient tool;
if (json.has("tools")) {
tool = SizedIngredient.deserialize(GsonHelper.getAsJsonObject(json, "tools"));
} else {
tool = SizedIngredient.fromTag(TinkerTags.Items.MODIFIABLE);
}
List<SizedIngredient> ingredients = JsonHelper.parseList(json, "inputs", SizedIngredient::deserialize);
List<ItemStack> leftovers = Collections.emptyList();
if (json.has("leftovers")) {
leftovers = JsonHelper.parseList(json, "leftovers", JsonUtils::convertToItemStack);
}
IJsonPredicate<ModifierId> modifierPredicate = ModifierPredicate.ANY;
if (json.has("modifier_predicate")) {
modifierPredicate = ModifierPredicate.LOADER.getIfPresent(json, "modifier_predicate");
}
return factory.create(id, tool, ingredients, leftovers, modifierPredicate);
}
@Nullable
@Override
public ModifierRemovalRecipe fromNetworkSafe(ResourceLocation id, FriendlyByteBuf buffer) {
SizedIngredient tool = SizedIngredient.read(buffer);
int size = buffer.readVarInt();
ImmutableList.Builder<SizedIngredient> ingredients = ImmutableList.builder();
for (int i = 0; i < size; i++) {
ingredients.add(SizedIngredient.read(buffer));
}
size = buffer.readVarInt();
ImmutableList.Builder<ItemStack> leftovers = ImmutableList.builder();
for (int i = 0; i < size; i++) {
leftovers.add(buffer.readItem());
}
IJsonPredicate<ModifierId> modifierPredicate = ModifierPredicate.LOADER.fromNetwork(buffer);
return factory.create(id, tool, ingredients.build(), leftovers.build(), modifierPredicate);
}
@Override
public void toNetworkSafe(FriendlyByteBuf buffer, ModifierRemovalRecipe recipe) {
recipe.sizedTool.write(buffer);
buffer.writeVarInt(recipe.inputs.size());
for (SizedIngredient ingredient : recipe.inputs) {
ingredient.write(buffer);
}
buffer.writeVarInt(recipe.leftovers.size());
for (ItemStack itemStack : recipe.leftovers) {
buffer.writeItem(itemStack);
}
ModifierPredicate.LOADER.toNetwork(recipe.modifierPredicate, buffer);
}
}
@RequiredArgsConstructor(staticName = "removal")
public static class Builder extends AbstractSizedIngredientRecipeBuilder<Builder> {
private final RecipeSerializer<? extends ModifierRemovalRecipe> serializer;
private final List<ItemStack> leftovers = new ArrayList<>();
private SizedIngredient tools = SizedIngredient.EMPTY;
@Setter @Accessors(fluent = true)
private IJsonPredicate<ModifierId> modifierPredicate = ModifierPredicate.ANY;
public static Builder removal() {
return removal(TinkerModifiers.removeModifierSerializer.get());
}
/** Sets the tool requirement for this recipe */
public Builder setTools(SizedIngredient ingredient) {
this.tools = ingredient;
return this;
}
/** Sets the tool requirement for this recipe */
public Builder setTools(Ingredient ingredient) {
return setTools(SizedIngredient.of(ingredient));
}
/** Adds a leftover stack to the recipe */
public Builder addLeftover(ItemStack stack) {
leftovers.add(stack);
return this;
}
/** Adds a leftover stack to the recipe */
public Builder addLeftover(ItemLike item) {
return addLeftover(new ItemStack(item));
}
@Override
public void save(Consumer<FinishedRecipe> consumer) {
save(consumer, Registry.ITEM.getKey(leftovers.get(0).getItem()));
}
@Override
public void save(Consumer<FinishedRecipe> consumer, ResourceLocation id) {
if (inputs.isEmpty()) {
throw new IllegalStateException("Must have at least one input");
}
ResourceLocation advancementId = buildOptionalAdvancement(id, "modifiers");
consumer.accept(new Finished(id, advancementId));
}
private class Finished extends SizedFinishedRecipe {
public Finished(ResourceLocation ID, @Nullable ResourceLocation advancementID) {
super(ID, advancementID);
}
@Override
public void serializeRecipeData(JsonObject json) {
super.serializeRecipeData(json);
SizedIngredient ingredient = tools;
if (ingredient == SizedIngredient.EMPTY) {
ingredient = SizedIngredient.fromTag(TinkerTags.Items.MODIFIABLE);
}
json.add("tools", ingredient.serialize());
if (!leftovers.isEmpty()) {
JsonArray array = new JsonArray();
for (ItemStack stack : leftovers) {
array.add(JsonUtils.serializeItemStack(stack));
}
json.add("leftovers", array);
}
json.add("modifier_predicate", ModifierPredicate.LOADER.serialize(modifierPredicate));
}
@Override
public RecipeSerializer<?> getType() {
return serializer;
}
}
}
}