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

CyclopsMC / IntegratedDynamics / 15366166327

31 May 2025 05:58PM UTC coverage: 44.766% (-0.5%) from 45.303%
15366166327

push

github

rubensworks
Bump mod version

2565 of 8515 branches covered (30.12%)

Branch coverage included in aggregate %.

11734 of 23427 relevant lines covered (50.09%)

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.stream.Collectors;
60

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

281
    @Override
282
    public void deactivate() {
283

284
    }
×
285

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

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

332
    @Override
333
    public boolean slotClick(int slotId, Slot slot, int mouseButton, ClickType clickType, Player player) {
334
        if (slotId >= SLOT_OFFSET && slotId < 9 + SLOT_OFFSET) {
×
335
            if (clickType == ClickType.QUICK_MOVE && mouseButton == 0) {
×
336
                if (player.level().isClientSide()) {
×
337
                    int id = slotId - SLOT_OFFSET;
×
338
                    lastGui.setPropertySubGui(id);
×
339
                }
340
                return true;
×
341
            } else {
342
                // Similar logic as ContainerExtended.adjustPhantomSlot
343
                ItemMatchProperties props = getInputStacks().get(slotId - SLOT_OFFSET);
×
344
                int quantityCurrent = props.getTagQuantity();
×
345
                int quantityNew;
346
                if (clickType == ClickType.QUICK_MOVE) {
×
347
                    quantityNew = mouseButton == 0 ? (quantityCurrent + 1) / 2 : quantityCurrent * 2;
×
348
                } else {
349
                    quantityNew = mouseButton == 0 ? quantityCurrent - 1 : quantityCurrent + 1;
×
350
                }
351

352
                if (quantityNew > slot.getMaxStackSize()) {
×
353
                    quantityNew = slot.getMaxStackSize();
×
354
                }
355

356
                props.setTagQuantity(quantityNew);
×
357
                if (!props.getItemStack().isEmpty()) {
×
358
                    props.getItemStack().setCount(quantityNew);
×
359
                }
360

361
                if (quantityNew <= 0) {
×
362
                    props.setItemTag(null);
×
363
                    props.setTagQuantity(1);
×
364
                    if (MinecraftHelpers.isClientSideThread()) {
×
365
                        refreshPropertiesGui(slotId - SLOT_OFFSET);
×
366
                    }
367
                }
368
            }
369
        }
370

371
        return super.slotClick(slotId, slot, mouseButton, clickType, player);
×
372
    }
373

374
    @Override
375
    public Slot createSlot(Container temporaryInputSlots, int slotId, int x, int y) {
376
        SlotExtended slot = new SlotExtended(temporaryInputSlots, slotId, x, y) {
×
377
            @Override
378
            public boolean mayPlace(ItemStack itemStack) {
379
                return ValueTypeRecipeLPElement.this.isItemValidForSlot(slotId, itemStack);
×
380
            }
381

382
            @Override
383
            public ItemStack getItem() {
384
                if (MinecraftHelpers.isClientSideThread() && slotId < 9) {
×
385
                    ItemMatchProperties props = getInputStacks().get(slotId);
×
386
                    String tagName = props.getItemTag();
×
387
                    if (tagName != null) {
×
388
                        try {
389
                            Optional<HolderSet.Named<Item>> tag = BuiltInRegistries.ITEM.getTag(TagKey.create(Registries.ITEM, ResourceLocation.parse(tagName)));
×
390
                            if (!tag.isEmpty()) {
×
391
                                List<Item> items = tag.stream()
×
392
                                        .flatMap(holders -> holders.stream())
×
393
                                        .map(holder -> holder.value()).toList();
×
394
                                int tick = ((int) Minecraft.getInstance().level.getGameTime()) / TICK_DELAY;
×
395
                                Item item = items.get(tick % items.size());
×
396
                                return new ItemStack(item, props.getTagQuantity());
×
397
                            }
398
                        } catch (ResourceLocationException e) {
×
399
                            // Ignore invalid tags
400
                        }
×
401
                    }
402
                }
403
                return super.getItem();
×
404
            }
405
        };
406
        slot.setPhantom(true);
×
407
        return slot;
×
408
    }
409

410
    @Override
411
    public int getItemStackSizeLimit() {
412
        return 64;
×
413
    }
414

415
    protected Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> getInputs(List<ItemMatchProperties> itemStacks,
416
                                                                                                      ItemStack fluid, int fluidAmount,
417
                                                                                                      long energy) {
418
        // Cut of itemStacks list until last non-empty stack
419
        int lastNonEmpty = 0;
×
420
        for (int i = 0; i < itemStacks.size(); i++) {
×
421
            if (itemStacks.get(i).isValid()) {
×
422
                lastNonEmpty = i + 1;
×
423
            }
424
        }
425
        itemStacks = itemStacks.subList(0, lastNonEmpty);
×
426

427
        // Override fluid amount
428
        FluidStack fluidStack = Helpers.getFluidStack(fluid);
×
429
        if (!fluidStack.isEmpty()) {
×
430
            fluidStack.setAmount(fluidAmount);
×
431
        }
432

433
        Map<IngredientComponent<?, ?>, List<IPrototypedIngredientAlternatives<?, ?>>> inputs = Maps.newIdentityHashMap();
×
434
        List<IPrototypedIngredientAlternatives<ItemStack, Integer>> items = itemStacks.stream()
×
435
                .map(ItemMatchProperties::createPrototypedIngredient)
×
436
                .collect(Collectors.toList());
×
437
        List<IPrototypedIngredientAlternatives<FluidStack, Integer>> fluids = !fluidStack.isEmpty()
×
438
                ? Collections.singletonList(new PrototypedIngredientAlternativesList<>(
×
439
                        Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.FLUIDSTACK, fluidStack, FluidMatch.FLUID | FluidMatch.DATA))))
×
440
                : Collections.emptyList();
×
441
        List<IPrototypedIngredientAlternatives<Long, Boolean>> energies = energy > 0 ?
×
442
                Collections.singletonList(new PrototypedIngredientAlternativesList<>(
×
443
                        Collections.singletonList(new PrototypedIngredient<>(IngredientComponent.ENERGY, energy, false))))
×
444
                : Collections.emptyList();
×
445
        if (!items.isEmpty()) {
×
446
            inputs.put(IngredientComponent.ITEMSTACK, (List) items);
×
447
        }
448
        if (!fluids.isEmpty()) {
×
449
            inputs.put(IngredientComponent.FLUIDSTACK, (List) fluids);
×
450
        }
451
        if (!energies.isEmpty()) {
×
452
            inputs.put(IngredientComponent.ENERGY, (List) energies);
×
453
        }
454

455
        return inputs;
×
456
    }
457

458
    protected Map<IngredientComponent<?, ?>, List<Boolean>> getInputsReusable(List<ItemMatchProperties> itemStacks) {
459
        Map<IngredientComponent<?, ?>, List<Boolean>> inputs = Maps.newIdentityHashMap();
×
460

461
        List<Boolean> items = itemStacks.stream()
×
462
                .map(ItemMatchProperties::isReusable)
×
463
                .collect(Collectors.toList());
×
464
        if (!items.isEmpty()) {
×
465
            inputs.put(IngredientComponent.ITEMSTACK, (List) items);
×
466
        }
467

468
        return inputs;
×
469
    }
470

471
    protected Map<IngredientComponent<?, ?>, List<?>> getOutputs(List<ItemStack> itemStacksIn,
472
                                                                 ItemStack fluid, int fluidAmount,
473
                                                                 long energy) {
474
        // Cut of itemStacks list until last non-empty stack
475
        List<ItemStack> itemStacks = Lists.newArrayList();
×
476
        for (int i = 0; i < itemStacksIn.size(); i++) {
×
477
            if (!itemStacksIn.get(i).isEmpty()) {
×
478
                itemStacks.add(itemStacksIn.get(i));
×
479
            }
480
        }
481

482
        // Override fluid amount
483
        FluidStack fluidStack = Helpers.getFluidStack(fluid);
×
484
        if (!fluidStack.isEmpty()) {
×
485
            fluidStack.setAmount(fluidAmount);
×
486
        }
487

488
        Map<IngredientComponent<?, ?>, List<?>> outputs = Maps.newIdentityHashMap();
×
489
        if (!itemStacks.isEmpty()) {
×
490
            outputs.put(IngredientComponent.ITEMSTACK, itemStacks);
×
491
        }
492
        if (!fluidStack.isEmpty()) {
×
493
            outputs.put(IngredientComponent.FLUIDSTACK, Collections.singletonList(fluidStack));
×
494
        }
495
        if (energy > 0) {
×
496
            outputs.put(IngredientComponent.ENERGY, Collections.singletonList(energy));
×
497
        }
498

499
        return outputs;
×
500
    }
501

502
    @Override
503
    public IValue getValue() {
504
        if (!isInputValid() && !isOutputValid()) {
×
505
            return ValueObjectTypeRecipe.ValueRecipe.of(null);
×
506
        }
507
        return ValueObjectTypeRecipe.ValueRecipe.of(
×
508
                new RecipeDefinition(getInputs(this.inputStacks, this.inputFluid,
×
509
                        Integer.parseInt(this.inputFluidAmount), Long.parseLong(this.inputEnergy)),
×
510
                getInputsReusable(this.inputStacks),
×
511
                new MixedIngredients(getOutputs(this.outputStacks, this.outputFluid,
×
512
                        Integer.parseInt(this.outputFluidAmount), Long.parseLong(this.outputEnergy)))));
×
513
    }
514

515
    @Override
516
    public void setValue(IValue value) {
517
        ValueObjectTypeRecipe.ValueRecipe valueRecipe = (ValueObjectTypeRecipe.ValueRecipe) value;
×
518
        valueRecipe.getRawValue().ifPresent(recipe -> {
×
519
            loadInputStacks(recipe);
×
520
            loadInputFluid(recipe);
×
521
            loadInputEnergy(recipe);
×
522

523
            loadOutputStacks(recipe);
×
524
            loadOutputFluid(recipe);
×
525
            loadOutputEnergy(recipe);
×
526
        });
×
527
    }
×
528

529
    private void loadInputStacks(IRecipeDefinition recipe) {
530
        List<IPrototypedIngredientAlternatives<ItemStack, Integer>> listAlternatives = recipe.getInputs(IngredientComponent.ITEMSTACK);
×
531
        for (int i = 0; i < listAlternatives.size(); i++) {
×
532
            IPrototypedIngredientAlternatives<ItemStack, Integer> prototypes = listAlternatives.get(i);
×
533
            boolean reusable = recipe.isInputReusable(IngredientComponent.ITEMSTACK, i);
×
534
            ItemMatchProperties itemMatchProperties = ItemMatchProperties.fromPrototypedIngredient(prototypes, reusable);
×
535
            this.inputStacks.set(i, itemMatchProperties);
×
536
        }
537
    }
×
538

539
    private <T, M> Optional<T> loadFirstInput(IRecipeDefinition recipe, IngredientComponent<T, M> ingredientComponent) {
540
        List<IPrototypedIngredientAlternatives<T, M>> listAlternatives = recipe.getInputs(ingredientComponent);
×
541
        if (listAlternatives.size() > 0) {
×
542
            IPrototypedIngredientAlternatives<T, M> prototypes = listAlternatives.get(0);
×
543
            if (prototypes.getAlternatives().size() > 0) {
×
544
                IPrototypedIngredient<T, M> prototype = prototypes.getAlternatives().stream().findFirst().get();
×
545
                return Optional.of(prototype.getPrototype());
×
546
            }
547
        }
548
        return Optional.empty();
×
549
    }
550

551
    private void loadInputFluid(IRecipeDefinition recipe) {
552
        loadFirstInput(recipe, IngredientComponent.FLUIDSTACK).ifPresent(fluidStack -> {
×
553
            this.inputFluid = fluidStack.getFluid().getFluidType().getBucket(fluidStack);
×
554
            this.inputFluidAmount = Integer.toString(fluidStack.getAmount());
×
555
        });
×
556
    }
×
557

558
    private void loadInputEnergy(IRecipeDefinition recipe) {
559
        loadFirstInput(recipe, IngredientComponent.ENERGY).ifPresent(energy -> this.inputEnergy = Long.toString(energy));
×
560
    }
×
561

562
    private void loadOutputStacks(IRecipeDefinition recipe) {
563
        List<ItemStack> instances = recipe.getOutput().getInstances(IngredientComponent.ITEMSTACK);
×
564
        if (instances.size() > 0) {
×
565
            outputStacks.set(0, instances.get(0));
×
566
        }
567
        if (instances.size() > 1) {
×
568
            outputStacks.set(1, instances.get(1));
×
569
        }
570
        if (instances.size() > 2) {
×
571
            outputStacks.set(2, instances.get(2));
×
572
        }
573
    }
×
574

575
    private <T, M> Optional<T> loadFirstOutput(IRecipeDefinition recipe, IngredientComponent<T, M> ingredientComponent) {
576
        List<T> instances = recipe.getOutput().getInstances(ingredientComponent);
×
577
        if (instances.size() > 0) {
×
578
            return Optional.of(instances.get(0));
×
579
        }
580
        return Optional.empty();
×
581
    }
582

583
    private void loadOutputFluid(IRecipeDefinition recipe) {
584
        loadFirstOutput(recipe, IngredientComponent.FLUIDSTACK).ifPresent(fluidStack -> {
×
585
            this.outputFluid = fluidStack.getFluid().getFluidType().getBucket(fluidStack);
×
586
            this.outputFluidAmount = Integer.toString(fluidStack.getAmount());
×
587
        });
×
588
    }
×
589

590
    private void loadOutputEnergy(IRecipeDefinition recipe) {
591
        loadFirstOutput(recipe, IngredientComponent.ENERGY).ifPresent(energy -> this.outputEnergy = Long.toString(energy));
×
592
    }
×
593

594
    @Override
595
    @OnlyIn(Dist.CLIENT)
596
    public void setValueInGui(ISubGuiBox subGui) {
597
        ValueTypeRecipeLPElementRecipeSubGui gui = ((ValueTypeRecipeLPElementMasterSubGui) subGui).getSubGuiRecipe();
×
598
        setValueInContainer(gui.container);
×
599
        if (gui.getInputFluidAmountBox() != null) {
×
600
            gui.getInputFluidAmountBox().setValue(this.inputFluidAmount);
×
601
            gui.getInputEnergyBox().setValue(this.inputEnergy);
×
602
            gui.getOutputFluidAmountBox().setValue(this.outputFluidAmount);
×
603
            gui.getOutputEnergyBox().setValue(this.outputEnergy);
×
604
        }
605
    }
×
606

607
    @Override
608
    public void setValueInContainer(ContainerLogicProgrammerBase container) {
609
        Container slots = container.getTemporaryInputSlots();
×
610

611
        // Input slots
612
        for (int i = 0; i < this.inputStacks.size(); i++) {
×
613
            ItemMatchProperties entry = this.inputStacks.get(i);
×
614
            slots.setItem(i, entry.getItemStack());
×
615
        }
616
        slots.setItem(9, this.inputFluid);
×
617

618
        // Output slots
619
        for (int i = 0; i < this.outputStacks.size(); i++) {
×
620
            slots.setItem(10 + i, this.outputStacks.get(i));
×
621
            // No need to set slot type, as this can't be changed for output stacks
622
        }
623
        slots.setItem(13, this.outputFluid);
×
624
    }
×
625

626
    @Override
627
    @OnlyIn(Dist.CLIENT)
628
    public ISubGuiBox createSubGui(int baseX, int baseY, int maxWidth, int maxHeight,
629
                                   ContainerScreenLogicProgrammerBase gui, ContainerLogicProgrammerBase container) {
630
        return lastGui = new ValueTypeRecipeLPElementMasterSubGui(this, baseX, baseY, maxWidth, maxHeight, gui, container);
×
631
    }
632

633
}
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