• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

CyclopsMC / IntegratedDynamics / 18042102834

26 Sep 2025 03:25PM UTC coverage: 44.791% (-0.1%) from 44.905%
18042102834

push

github

rubensworks
Merge remote-tracking branch 'origin/master-1.20-lts' into master-1.21-lts

2572 of 8540 branches covered (30.12%)

Branch coverage included in aggregate %.

11761 of 23460 relevant lines covered (50.13%)

2.38 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

0.0
/src/main/java/org/cyclops/integrateddynamics/core/logicprogrammer/ValueTypeRecipeLPElement.java
1
package org.cyclops.integrateddynamics.core.logicprogrammer;
2

3
import com.google.common.collect.Lists;
4
import com.google.common.collect.Maps;
5
import lombok.Getter;
6
import lombok.Setter;
7
import net.minecraft.ResourceLocationException;
8
import net.minecraft.client.Minecraft;
9
import net.minecraft.core.HolderSet;
10
import net.minecraft.core.NonNullList;
11
import net.minecraft.core.registries.BuiltInRegistries;
12
import net.minecraft.core.registries.Registries;
13
import net.minecraft.network.chat.Component;
14
import net.minecraft.resources.ResourceLocation;
15
import net.minecraft.tags.TagKey;
16
import net.minecraft.world.Container;
17
import net.minecraft.world.entity.player.Player;
18
import net.minecraft.world.inventory.ClickType;
19
import net.minecraft.world.inventory.Slot;
20
import net.minecraft.world.item.Item;
21
import net.minecraft.world.item.ItemStack;
22
import net.minecraft.world.item.Items;
23
import net.neoforged.api.distmarker.Dist;
24
import net.neoforged.api.distmarker.OnlyIn;
25
import net.neoforged.neoforge.capabilities.Capabilities;
26
import net.neoforged.neoforge.fluids.FluidStack;
27
import net.neoforged.neoforge.fluids.capability.IFluidHandler;
28
import net.neoforged.neoforge.fluids.capability.IFluidHandlerItem;
29
import org.cyclops.commoncapabilities.api.capability.fluidhandler.FluidMatch;
30
import org.cyclops.commoncapabilities.api.capability.recipehandler.IPrototypedIngredientAlternatives;
31
import org.cyclops.commoncapabilities.api.capability.recipehandler.IRecipeDefinition;
32
import org.cyclops.commoncapabilities.api.capability.recipehandler.PrototypedIngredientAlternativesList;
33
import org.cyclops.commoncapabilities.api.capability.recipehandler.RecipeDefinition;
34
import org.cyclops.commoncapabilities.api.ingredient.IPrototypedIngredient;
35
import org.cyclops.commoncapabilities.api.ingredient.IngredientComponent;
36
import org.cyclops.commoncapabilities.api.ingredient.MixedIngredients;
37
import org.cyclops.commoncapabilities.api.ingredient.PrototypedIngredient;
38
import org.cyclops.cyclopscore.helper.FluidHelpers;
39
import org.cyclops.cyclopscore.helper.MinecraftHelpers;
40
import org.cyclops.cyclopscore.inventory.slot.SlotExtended;
41
import org.cyclops.integrateddynamics.IntegratedDynamics;
42
import org.cyclops.integrateddynamics.api.client.gui.subgui.ISubGuiBox;
43
import org.cyclops.integrateddynamics.api.evaluate.variable.IValue;
44
import org.cyclops.integrateddynamics.api.logicprogrammer.IConfigRenderPattern;
45
import org.cyclops.integrateddynamics.api.logicprogrammer.ILogicProgrammerElementType;
46
import org.cyclops.integrateddynamics.client.gui.container.ContainerScreenLogicProgrammerBase;
47
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueObjectTypeRecipe;
48
import org.cyclops.integrateddynamics.core.evaluate.variable.ValueTypes;
49
import org.cyclops.integrateddynamics.core.helper.Helpers;
50
import org.cyclops.integrateddynamics.core.helper.L10NValues;
51
import org.cyclops.integrateddynamics.core.ingredient.ItemMatchProperties;
52
import org.cyclops.integrateddynamics.inventory.container.ContainerLogicProgrammerBase;
53
import org.cyclops.integrateddynamics.network.packet.LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket;
54

55
import java.util.Collections;
56
import java.util.List;
57
import java.util.Map;
58
import java.util.Optional;
59
import java.util.function.Consumer;
60
import java.util.stream.Collectors;
61

62
/**
63
 * Element for recipes.
64
 * This is hardcoded to only support items, fluids and energy
65
 * @author rubensworks
66
 */
67
public class ValueTypeRecipeLPElement extends ValueTypeLPElementBase {
68

69
    public static final int SLOT_OFFSET = 4;
70
    public static final int TICK_DELAY = 30;
71

72
    @OnlyIn(Dist.CLIENT)
73
    public ValueTypeRecipeLPElementMasterSubGui lastGui;
74

75
    @Getter
×
76
    private NonNullList<ItemMatchProperties> inputStacks;
77
    private ItemStack inputFluid;
78
    @Getter
×
79
    @Setter
×
80
    private String inputFluidAmount = "0";
81
    @Getter
×
82
    @Setter
×
83
    private String inputEnergy = "0";
84
    private NonNullList<ItemStack> outputStacks;
85
    private ItemStack outputFluid;
86
    @Getter
×
87
    @Setter
×
88
    private String outputFluidAmount = "0";
89
    @Getter
×
90
    @Setter
×
91
    private String outputEnergy = "0";
92

93
    public ValueTypeRecipeLPElement() {
94
        super(ValueTypes.OBJECT_RECIPE);
×
95
    }
×
96

97
    @Override
98
    public ILogicProgrammerElementType getType() {
99
        return LogicProgrammerElementTypes.VALUETYPE;
×
100
    }
101

102
    @Override
103
    public IConfigRenderPattern getRenderPattern() {
104
        return IConfigRenderPattern.RECIPE;
×
105
    }
106

107
    @Override
108
    public void onInputSlotUpdated(Player player, int slotId, ItemStack itemStack) {
109
        if (inputStacks == null) {
×
110
            return;
×
111
        }
112

113
        if (slotId >= 0 && slotId < 9) {
×
114
            ItemStack itemStackOld = inputStacks.get(slotId).getItemStack();
×
115
            if (itemStackOld.getItem() != itemStack.getItem()) {
×
116
                inputStacks.set(slotId, new ItemMatchProperties(itemStack.copy()));
×
117
                if (MinecraftHelpers.isClientSideThread()) {
×
118
                    refreshPropertiesGui(slotId);
×
119
                }
120
            }
121
        }
122
        if (slotId == 9) {
×
123
            inputFluid = itemStack.copy();
×
124
            if (inputFluidAmount.equalsIgnoreCase("0")) {
×
125
                int amount = FluidHelpers.getAmount(Helpers.getFluidStack(inputFluid));
×
126
                inputFluidAmount = Integer.toString(amount);
×
127
                if (MinecraftHelpers.isClientSideThread() && lastGui != null) {
×
128
                    refreshInputFluidAmountBox();
×
129
                }
130
            }
131
        }
132
        if (slotId > 9 && slotId < 13) {
×
133
            outputStacks.set(slotId - 10, itemStack.copy());
×
134
        }
135
        if (slotId == 13) {
×
136
            outputFluid = itemStack.copy();
×
137
            if (outputFluidAmount.equalsIgnoreCase("0")) {
×
138
                int amount = FluidHelpers.getAmount(Helpers.getFluidStack(outputFluid));
×
139
                outputFluidAmount = Integer.toString(amount);
×
140
                if (MinecraftHelpers.isClientSideThread() && lastGui != null) {
×
141
                    refreshOutputFluidAmountBox();
×
142
                }
143
            }
144
        }
145
    }
×
146

147
    @OnlyIn(Dist.CLIENT)
148
    protected void refreshPropertiesGui(int slot) {
149
        if (this.lastGui != null && this.lastGui.isPropertySubGuiActive(slot)) {
×
150
            this.lastGui.propertiesSubGuis.get(slot).loadStateToGui();
×
151
        }
152
    }
×
153

154
    @OnlyIn(Dist.CLIENT)
155
    protected void refreshInputFluidAmountBox() {
156
        if (this.lastGui != null && this.lastGui.subGuiRecipe.getInputFluidAmountBox() != null) {
×
157
            this.lastGui.subGuiRecipe.getInputFluidAmountBox().setValue(inputFluidAmount);
×
158
        }
159
    }
×
160

161
    @OnlyIn(Dist.CLIENT)
162
    protected void refreshOutputFluidAmountBox() {
163
        if (this.lastGui != null && this.lastGui.subGuiRecipe.getOutputFluidAmountBox() != null) {
×
164
            this.lastGui.subGuiRecipe.getOutputFluidAmountBox().setValue(outputFluidAmount);
×
165
        }
166
    }
×
167

168
    public void sendSlotPropertiesToServer(int slotId, ItemMatchProperties props) {
169
        IntegratedDynamics._instance.getPacketHandler().sendToServer(
×
170
                new LogicProgrammerValueTypeRecipeSlotPropertiesChangedPacket(
171
                        slotId, props.isNbt(), props.getItemTag() == null ? "" : props.getItemTag(), props.getTagQuantity(), props.isReusable()));
×
172
    }
×
173

174
    // Used by ID-Compat for JEI recipe transfer handler
175
    public boolean isValidForRecipeGrid(List<ItemMatchProperties> itemInputs, List<FluidStack> fluidInputs,
176
                                        List<ItemStack> itemOutputs, List<FluidStack> fluidOutputs) {
177
        return itemInputs.size() <= 9 && itemOutputs.size() <= 3
×
178
                && fluidInputs.size() <= 1 && fluidOutputs.size() <= 1;
×
179
    }
180

181
    protected void putItemPropertiesInContainer(ContainerLogicProgrammerBase container, int slot, ItemMatchProperties props) {
182
        putStackInContainer(container, slot, props.getItemStack());
×
183
        getInputStacks().set(slot, props);
×
184
    }
×
185

186
    protected void putStackInContainer(ContainerLogicProgrammerBase container, int slot, ItemStack itemStack) {
187
        // Offset: Player inventory, recipe grid slots
188
        container.setItem(container.getItems().size() - (36 + 14) + slot, 0, itemStack);
×
189
    }
×
190

191
    // Used by ID-Compat for JEI recipe transfer handler
192
    public void setRecipeGrid(ContainerLogicProgrammerBase container,
193
                              List<ItemMatchProperties> itemInputs, List<FluidStack> fluidInputs,
194
                              List<ItemStack> itemOutputs, List<FluidStack> fluidOutputs) {
195
        int slot = 0;
×
196

197
        // Fill input item slots
198
        for (ItemMatchProperties itemInput : itemInputs) {
×
199
            putItemPropertiesInContainer(container, slot, itemInput);
×
200
            slot++;
×
201
        }
×
202
        while (slot < 9) {
×
203
            putItemPropertiesInContainer(container, slot, new ItemMatchProperties(ItemStack.EMPTY));
×
204
            slot++;
×
205
        }
206

207
        // Fill input fluid slot
208
        slot = 9;
×
209
        FluidStack fluidStackInput = FluidStack.EMPTY;
×
210
        if (fluidInputs.size() > 0) {
×
211
            fluidStackInput = fluidInputs.get(0);
×
212
        }
213
        putStackInContainer(container, slot, fluidStackInput.isEmpty() ? ItemStack.EMPTY : getFluidBucket(fluidStackInput));
×
214
        inputFluidAmount = String.valueOf(FluidHelpers.getAmount(fluidStackInput));
×
215
        if (MinecraftHelpers.isClientSideThread()) {
×
216
            refreshInputFluidAmountBox();
×
217
        }
218

219
        // Fill input output slots
220
        slot = 10;
×
221
        for (ItemStack itemOutput : itemOutputs) {
×
222
            putStackInContainer(container, slot, itemOutput);
×
223
            slot++;
×
224
        }
×
225
        while (slot < 13) {
×
226
            putStackInContainer(container, slot, ItemStack.EMPTY);
×
227
            slot++;
×
228
        }
229

230
        // Fill output fluid slot
231
        slot = 13;
×
232
        FluidStack fluidStackOutput = FluidStack.EMPTY;
×
233
        if (fluidOutputs.size() > 0) {
×
234
            fluidStackOutput = fluidOutputs.get(0);
×
235
        }
236
        putStackInContainer(container, slot, fluidStackOutput.isEmpty() ? ItemStack.EMPTY : getFluidBucket(fluidStackOutput));
×
237
        outputFluidAmount = String.valueOf(FluidHelpers.getAmount(fluidStackOutput));
×
238
        if (MinecraftHelpers.isClientSideThread()) {
×
239
            refreshOutputFluidAmountBox();
×
240
        }
241
    }
×
242

243
    public static ItemStack getFluidBucket(FluidStack fluidStack) {
244
        ItemStack itemStack = new ItemStack(Items.BUCKET);
×
245
        IFluidHandlerItem fluidHandler = itemStack.getCapability(Capabilities.FluidHandler.ITEM);
×
246
        fluidHandler.fill(new FluidStack(fluidStack.getFluid(), FluidHelpers.BUCKET_VOLUME), IFluidHandler.FluidAction.EXECUTE);
×
247
        return fluidHandler.getContainer();
×
248
    }
249

250
    protected boolean isInputValid() {
251
        return inputStacks.stream().anyMatch(ItemMatchProperties::isValid)
×
252
                || !inputFluid.isEmpty() || !inputFluidAmount.equalsIgnoreCase("0")
×
253
                || !inputEnergy.equalsIgnoreCase("0");
×
254
    }
255

256
    protected boolean isOutputValid() {
257
        return outputStacks.stream().anyMatch(stack -> !stack.isEmpty())
×
258
                || !outputFluid.isEmpty() || !outputFluidAmount.equalsIgnoreCase("0")
×
259
                || !outputEnergy.equalsIgnoreCase("0");
×
260
    }
261

262
    @Override
263
    public boolean canWriteElementPre() {
264
        return isInputValid() == isOutputValid(); // Not &&, because we also allow fully blank recipes
×
265
    }
266

267
    @Override
268
    public void activate() {
269
        inputStacks = NonNullList.withSize(9, new ItemMatchProperties(ItemStack.EMPTY));
×
270
        for (int i = 0; i < 9; i++) {
×
271
            inputStacks.set(i, new ItemMatchProperties(ItemStack.EMPTY));
×
272
        }
273
        inputFluid = ItemStack.EMPTY;
×
274
        inputFluidAmount = "0";
×
275
        inputEnergy = "0";
×
276
        outputStacks = NonNullList.withSize(3, ItemStack.EMPTY);
×
277
        outputFluid = ItemStack.EMPTY;
×
278
        outputFluidAmount = "0";
×
279
        outputEnergy = "0";
×
280
    }
×
281

282
    @Override
283
    public void deactivate() {
284

285
    }
×
286

287
    @Override
288
    public Component validate() {
289
        if (!inputFluid.isEmpty() && Helpers.getFluidStack(inputFluid).isEmpty()) {
×
290
            return Component.translatable(L10NValues.VALUETYPE_OBJECT_FLUID_ERROR_NOFLUID);
×
291
        }
292
        if (!outputFluid.isEmpty() && Helpers.getFluidStack(outputFluid).isEmpty()) {
×
293
            return Component.translatable(L10NValues.VALUETYPE_OBJECT_FLUID_ERROR_NOFLUID);
×
294
        }
295
        try {
296
            Integer.parseInt(inputFluidAmount);
×
297
        } catch (NumberFormatException e) {
×
298
            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, inputFluidAmount);
×
299
        }
×
300
        try {
301
            Integer.parseInt(outputFluidAmount);
×
302
        } catch (NumberFormatException e) {
×
303
            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, outputFluidAmount);
×
304
        }
×
305
        try {
306
            Long.parseLong(inputEnergy);
×
307
        } catch (NumberFormatException e) {
×
308
            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, inputEnergy);
×
309
        }
×
310
        try {
311
            Long.parseLong(outputEnergy);
×
312
        } catch (NumberFormatException e) {
×
313
            return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, outputEnergy);
×
314
        }
×
315
        // Validate input item tag strings if they are defined
316
        for (ItemMatchProperties inputStack : inputStacks) {
×
317
            if (inputStack.getItemTag() != null) {
×
318
                try {
319
                    ResourceLocation.parse(inputStack.getItemTag());
×
320
                } catch (ResourceLocationException e) {
×
321
                    return Component.translatable(L10NValues.VALUETYPE_ERROR_INVALIDINPUT, inputStack.getItemTag());
×
322
                }
×
323
            }
324
        }
×
325
        return null;
×
326
    }
327

328
    @Override
329
    public boolean isItemValidForSlot(int slotId, ItemStack itemStack) {
330
        return true;
×
331
    }
332

333
    @Override
334
    public boolean slotClick(int slotId, Slot slot, int mouseButton, ClickType clickType, Player player) {
335
        return slotClickCommon(slotId, slot, mouseButton, clickType, player, getInputStacks(), 9, (i) -> {
×
336
            if (MinecraftHelpers.isClientSideThread()) {
×
337
                lastGui.setPropertySubGui(i);
×
338
            }
339
        }, (i) -> {
×
340
            if (MinecraftHelpers.isClientSideThread()) {
×
341
                this.refreshPropertiesGui(i);
×
342
            }
343
        }) || super.slotClick(slotId, slot, mouseButton, clickType, player);
×
344
    }
345

346
    public static boolean slotClickCommon(int slotId, Slot slot, int mouseButton, ClickType clickType, Player player,
347
                                          List<ItemMatchProperties> inputStacks, int propertySlotCount,
348
                                          Consumer<Integer> setPropertySubGui,
349
                                          Consumer<Integer> refreshPropertiesGui) {
350
        if (slotId >= SLOT_OFFSET && slotId < propertySlotCount + SLOT_OFFSET) {
×
351
            if (clickType == ClickType.QUICK_MOVE && mouseButton == 0) {
×
352
                if (player.level().isClientSide()) {
×
353
                    int id = slotId - SLOT_OFFSET;
×
354
                    setPropertySubGui.accept(id);
×
355
                }
356
                return true;
×
357
            } else {
358
                // Similar logic as ContainerExtended.adjustPhantomSlot
359
                ItemMatchProperties props = inputStacks.get(slotId - SLOT_OFFSET);
×
360
                int quantityCurrent = props.getTagQuantity();
×
361
                int quantityNew;
362
                if (clickType == ClickType.QUICK_MOVE) {
×
363
                    quantityNew = mouseButton == 0 ? (quantityCurrent + 1) / 2 : quantityCurrent * 2;
×
364
                } else {
365
                    quantityNew = mouseButton == 0 ? quantityCurrent - 1 : quantityCurrent + 1;
×
366
                }
367

368
                if (quantityNew > slot.getMaxStackSize()) {
×
369
                    quantityNew = slot.getMaxStackSize();
×
370
                }
371

372
                props.setTagQuantity(quantityNew);
×
373
                if (!props.getItemStack().isEmpty()) {
×
374
                    props.getItemStack().setCount(quantityNew);
×
375
                }
376

377
                if (quantityNew <= 0) {
×
378
                    props.setItemTag(null);
×
379
                    props.setTagQuantity(1);
×
380
                    if (MinecraftHelpers.isClientSideThread()) {
×
381
                        refreshPropertiesGui.accept(slotId - SLOT_OFFSET);
×
382
                    }
383
                }
384
            }
385
        }
386

387
        return false;
×
388
    }
389

390
    @Override
391
    public Slot createSlot(Container temporaryInputSlots, int slotId, int x, int y) {
392
        SlotExtended slot = new SlotExtended(temporaryInputSlots, slotId, x, y) {
×
393
            @Override
394
            public boolean mayPlace(ItemStack itemStack) {
395
                return ValueTypeRecipeLPElement.this.isItemValidForSlot(slotId, itemStack);
×
396
            }
397

398
            @Override
399
            public ItemStack getItem() {
400
                if (MinecraftHelpers.isClientSideThread() && slotId < 9) {
×
401
                    return getRotatingItemFromTag(getInputStacks().get(slotId))
×
402
                            .orElseGet(super::getItem);
×
403
                }
404
                return super.getItem();
×
405
            }
406
        };
407
        slot.setPhantom(true);
×
408
        return slot;
×
409
    }
410

411
    public static Optional<ItemStack> getRotatingItemFromTag(ItemMatchProperties props) {
412
        String tagName = props.getItemTag();
×
413
        if (tagName != null) {
×
414
            try {
415
                Optional<HolderSet.Named<Item>> tag = BuiltInRegistries.ITEM.getTag(TagKey.create(Registries.ITEM, ResourceLocation.parse(tagName)));
×
416
                if (!tag.isEmpty()) {
×
417
                    List<Item> items = tag.stream()
×
418
                            .flatMap(holders -> holders.stream())
×
419
                            .map(holder -> holder.value()).toList();
×
420
                    int tick = ((int) Minecraft.getInstance().level.getGameTime()) / TICK_DELAY;
×
421
                    Item item = items.get(tick % items.size());
×
422
                    return Optional.of(new ItemStack(item, props.getTagQuantity()));
×
423
                }
424
            } catch (ResourceLocationException e) {
×
425
                // Ignore invalid tags
426
            }
×
427
        }
428
        return Optional.empty();
×
429
    }
430

431
    @Override
432
    public int getItemStackSizeLimit() {
433
        return 64;
×
434
    }
435

436
    protected Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> getInputs(List<ItemMatchProperties> itemStacks,
437
                                                                                                      ItemStack fluid, int fluidAmount,
438
                                                                                                      long energy) {
439
        // Cut of itemStacks list until last non-empty stack
440
        int lastNonEmpty = 0;
×
441
        for (int i = 0; i < itemStacks.size(); i++) {
×
442
            if (itemStacks.get(i).isValid()) {
×
443
                lastNonEmpty = i + 1;
×
444
            }
445
        }
446
        itemStacks = itemStacks.subList(0, lastNonEmpty);
×
447

448
        // Override fluid amount
449
        FluidStack fluidStack = Helpers.getFluidStack(fluid);
×
450
        if (!fluidStack.isEmpty()) {
×
451
            fluidStack.setAmount(fluidAmount);
×
452
        }
453

454
        Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
×
455
        List<IPrototypedIngredientAlternatives<ItemStack, Integer>> items = itemStacks.stream()
×
456
                .map(ItemMatchProperties::createPrototypedIngredient)
×
457
                .collect(Collectors.toList());
×
458
        List<IPrototypedIngredientAlternatives<FluidStack, Integer>> fluids = !fluidStack.isEmpty()
×
459
                ? Collections.singletonList(new PrototypedIngredientAlternativesList<>(
×
460
                        Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.FLUIDSTACK, fluidStack, FluidMatch.FLUID | FluidMatch.DATA))))
×
461
                : Collections.emptyList();
×
462
        List<IPrototypedIngredientAlternatives<Long, Boolean>> energies = energy > 0 ?
×
463
                Collections.singletonList(new PrototypedIngredientAlternativesList<>(
×
464
                        Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ENERGY, energy, false))))
×
465
                : Collections.emptyList();
×
466
        if (!items.isEmpty()) {
×
467
            inputs.put(IngredientComponent.ITEMSTACK, (List) items);
×
468
        }
469
        if (!fluids.isEmpty()) {
×
470
            inputs.put(IngredientComponent.FLUIDSTACK, (List) fluids);
×
471
        }
472
        if (!energies.isEmpty()) {
×
473
            inputs.put(IngredientComponent.ENERGY, (List) energies);
×
474
        }
475

476
        return inputs;
×
477
    }
478

479
    public static Map<IngredientComponent<?, ?>, List<Boolean>> getInputsReusable(List<ItemMatchProperties> itemStacks) {
480
        Map<IngredientComponent<?, ?>, List<Boolean>> inputs = Maps.newIdentityHashMap();
×
481

482
        List<Boolean> items = itemStacks.stream()
×
483
                .map(ItemMatchProperties::isReusable)
×
484
                .collect(Collectors.toList());
×
485
        if (!items.isEmpty()) {
×
486
            inputs.put(IngredientComponent.ITEMSTACK, (List) items);
×
487
        }
488

489
        return inputs;
×
490
    }
491

492
    protected Map<IngredientComponent<?, ?>, List<?>> getOutputs(List<ItemStack> itemStacksIn,
493
                                                                 ItemStack fluid, int fluidAmount,
494
                                                                 long energy) {
495
        // Cut of itemStacks list until last non-empty stack
496
        List<ItemStack> itemStacks = Lists.newArrayList();
×
497
        for (int i = 0; i < itemStacksIn.size(); i++) {
×
498
            if (!itemStacksIn.get(i).isEmpty()) {
×
499
                itemStacks.add(itemStacksIn.get(i));
×
500
            }
501
        }
502

503
        // Override fluid amount
504
        FluidStack fluidStack = Helpers.getFluidStack(fluid);
×
505
        if (!fluidStack.isEmpty()) {
×
506
            fluidStack.setAmount(fluidAmount);
×
507
        }
508

509
        Map<IngredientComponent<?, ?>, List<?>> outputs = Maps.newIdentityHashMap();
×
510
        if (!itemStacks.isEmpty()) {
×
511
            outputs.put(IngredientComponent.ITEMSTACK, itemStacks);
×
512
        }
513
        if (!fluidStack.isEmpty()) {
×
514
            outputs.put(IngredientComponent.FLUIDSTACK, Collections.singletonList(fluidStack));
×
515
        }
516
        if (energy > 0) {
×
517
            outputs.put(IngredientComponent.ENERGY, Collections.singletonList(energy));
×
518
        }
519

520
        return outputs;
×
521
    }
522

523
    @Override
524
    public IValue getValue() {
525
        if (!isInputValid() && !isOutputValid()) {
×
526
            return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
527
        }
528
        return ValueObjectTypeRecipe.ValueRecipe.of(
×
529
                new RecipeDefinition(getInputs(this.inputStacks, this.inputFluid,
×
530
                        Integer.parseInt(this.inputFluidAmount), Long.parseLong(this.inputEnergy)),
×
531
                getInputsReusable(this.inputStacks),
×
532
                new MixedIngredients(getOutputs(this.outputStacks, this.outputFluid,
×
533
                        Integer.parseInt(this.outputFluidAmount), Long.parseLong(this.outputEnergy)))));
×
534
    }
535

536
    @Override
537
    public void setValue(IValue value) {
538
        ValueObjectTypeRecipe.ValueRecipe valueRecipe = (ValueObjectTypeRecipe.ValueRecipe) value;
×
539
        valueRecipe.getRawValue().ifPresent(recipe -> {
×
540
            loadInputStacks(recipe);
×
541
            loadInputFluid(recipe);
×
542
            loadInputEnergy(recipe);
×
543

544
            loadOutputStacks(recipe);
×
545
            loadOutputFluid(recipe);
×
546
            loadOutputEnergy(recipe);
×
547
        });
×
548
    }
×
549

550
    private void loadInputStacks(IRecipeDefinition recipe) {
551
        List<IPrototypedIngredientAlternatives<ItemStack, Integer>> listAlternatives = recipe.getInputs(IngredientComponent.ITEMSTACK);
×
552
        for (int i = 0; i < listAlternatives.size(); i++) {
×
553
            IPrototypedIngredientAlternatives<ItemStack, Integer> prototypes = listAlternatives.get(i);
×
554
            boolean reusable = recipe.isInputReusable(IngredientComponent.ITEMSTACK, i);
×
555
            ItemMatchProperties itemMatchProperties = ItemMatchProperties.fromPrototypedIngredient(prototypes, reusable);
×
556
            this.inputStacks.set(i, itemMatchProperties);
×
557
        }
558
    }
×
559

560
    private <T, M> Optional<T> loadFirstInput(IRecipeDefinition recipe, IngredientComponent<T, M> ingredientComponent) {
561
        List<IPrototypedIngredientAlternatives<T, M>> listAlternatives = recipe.getInputs(ingredientComponent);
×
562
        if (listAlternatives.size() > 0) {
×
563
            IPrototypedIngredientAlternatives<T, M> prototypes = listAlternatives.get(0);
×
564
            if (prototypes.getAlternatives().size() > 0) {
×
565
                IPrototypedIngredient<T, M> prototype = prototypes.getAlternatives().stream().findFirst().get();
×
566
                return Optional.of(prototype.getPrototype());
×
567
            }
568
        }
569
        return Optional.empty();
×
570
    }
571

572
    private void loadInputFluid(IRecipeDefinition recipe) {
573
        loadFirstInput(recipe, IngredientComponent.FLUIDSTACK).ifPresent(fluidStack -> {
×
574
            this.inputFluid = fluidStack.getFluid().getFluidType().getBucket(fluidStack);
×
575
            this.inputFluidAmount = Integer.toString(fluidStack.getAmount());
×
576
        });
×
577
    }
×
578

579
    private void loadInputEnergy(IRecipeDefinition recipe) {
580
        loadFirstInput(recipe, IngredientComponent.ENERGY).ifPresent(energy -> this.inputEnergy = Long.toString(energy));
×
581
    }
×
582

583
    private void loadOutputStacks(IRecipeDefinition recipe) {
584
        List<ItemStack> instances = recipe.getOutput().getInstances(IngredientComponent.ITEMSTACK);
×
585
        if (instances.size() > 0) {
×
586
            outputStacks.set(0, instances.get(0));
×
587
        }
588
        if (instances.size() > 1) {
×
589
            outputStacks.set(1, instances.get(1));
×
590
        }
591
        if (instances.size() > 2) {
×
592
            outputStacks.set(2, instances.get(2));
×
593
        }
594
    }
×
595

596
    private <T, M> Optional<T> loadFirstOutput(IRecipeDefinition recipe, IngredientComponent<T, M> ingredientComponent) {
597
        List<T> instances = recipe.getOutput().getInstances(ingredientComponent);
×
598
        if (instances.size() > 0) {
×
599
            return Optional.of(instances.get(0));
×
600
        }
601
        return Optional.empty();
×
602
    }
603

604
    private void loadOutputFluid(IRecipeDefinition recipe) {
605
        loadFirstOutput(recipe, IngredientComponent.FLUIDSTACK).ifPresent(fluidStack -> {
×
606
            this.outputFluid = fluidStack.getFluid().getFluidType().getBucket(fluidStack);
×
607
            this.outputFluidAmount = Integer.toString(fluidStack.getAmount());
×
608
        });
×
609
    }
×
610

611
    private void loadOutputEnergy(IRecipeDefinition recipe) {
612
        loadFirstOutput(recipe, IngredientComponent.ENERGY).ifPresent(energy -> this.outputEnergy = Long.toString(energy));
×
613
    }
×
614

615
    @Override
616
    @OnlyIn(Dist.CLIENT)
617
    public void setValueInGui(ISubGuiBox subGui) {
618
        ValueTypeRecipeLPElementRecipeSubGui gui = ((ValueTypeRecipeLPElementMasterSubGui) subGui).getSubGuiRecipe();
×
619
        setValueInContainer(gui.container);
×
620
        if (gui.getInputFluidAmountBox() != null) {
×
621
            gui.getInputFluidAmountBox().setValue(this.inputFluidAmount);
×
622
            gui.getInputEnergyBox().setValue(this.inputEnergy);
×
623
            gui.getOutputFluidAmountBox().setValue(this.outputFluidAmount);
×
624
            gui.getOutputEnergyBox().setValue(this.outputEnergy);
×
625
        }
626
    }
×
627

628
    @Override
629
    public void setValueInContainer(ContainerLogicProgrammerBase container) {
630
        Container slots = container.getTemporaryInputSlots();
×
631

632
        // Input slots
633
        for (int i = 0; i < this.inputStacks.size(); i++) {
×
634
            ItemMatchProperties entry = this.inputStacks.get(i);
×
635
            slots.setItem(i, entry.getItemStack());
×
636
        }
637
        slots.setItem(9, this.inputFluid);
×
638

639
        // Output slots
640
        for (int i = 0; i < this.outputStacks.size(); i++) {
×
641
            slots.setItem(10 + i, this.outputStacks.get(i));
×
642
            // No need to set slot type, as this can't be changed for output stacks
643
        }
644
        slots.setItem(13, this.outputFluid);
×
645
    }
×
646

647
    @Override
648
    @OnlyIn(Dist.CLIENT)
649
    public ISubGuiBox createSubGui(int baseX, int baseY, int maxWidth, int maxHeight,
650
                                   ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {
651
        return lastGui = new ValueTypeRecipeLPElementMasterSubGui(this, baseX, baseY, maxWidth, maxHeight, gui, container);
×
652
    }
653

654
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc