Skip to content

Commit b5c618b

Browse files
committed
Show ingredient alternatives in flat crafting plan, Closes #189
1 parent 400f940 commit b5c618b

4 files changed

Lines changed: 131 additions & 44 deletions

File tree

src/main/java/org/cyclops/integratedterminals/api/terminalstorage/crafting/ITerminalCraftingPlanFlat.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,9 +68,16 @@ public static interface IEntry {
6868

6969
/**
7070
* @return The entry instance.
71+
* @deprecated Use {@link #getInstances()} instead. TODO: rm in next major
7172
*/
73+
@Deprecated
7274
public IPrototypedIngredient<?, ?> getInstance();
7375

76+
/**
77+
* @return The alternative entry instances for this entry. Never empty.
78+
*/
79+
public List<IPrototypedIngredient<?, ?>> getInstances();
80+
7481
/**
7582
* @return The number of instances to craft.
7683
*/

src/main/java/org/cyclops/integratedterminals/api/terminalstorage/crafting/TerminalCraftingPlanFlatStatic.java

Lines changed: 40 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -210,27 +210,44 @@ public static <I> TerminalCraftingPlanFlatStatic<I> deserialize(CompoundTag tag,
210210

211211
public static class Entry implements ITerminalCraftingPlanFlat.IEntry {
212212

213-
private final IPrototypedIngredient<?, ?> instance;
213+
private final List<IPrototypedIngredient<?, ?>> instances;
214214
private long quantityToCraft;
215215
private long quantityCrafting;
216216
private long quantityInStorage;
217217
private long quantityMissing;
218218

219-
public Entry(IPrototypedIngredient<?, ?> instance, long quantityToCraft, long quantityCrafting, long quantityInStorage, long quantityMissing) {
220-
this.instance = instance;
219+
public Entry(List<IPrototypedIngredient<?, ?>> instances, long quantityToCraft, long quantityCrafting, long quantityInStorage, long quantityMissing) {
220+
this.instances = instances;
221221
this.quantityToCraft = quantityToCraft;
222222
this.quantityCrafting = quantityCrafting;
223223
this.quantityInStorage = quantityInStorage;
224224
this.quantityMissing = quantityMissing;
225225
}
226226

227+
public Entry(List<IPrototypedIngredient<?, ?>> instances) {
228+
this(instances, 0, 0, 0, 0);
229+
}
230+
231+
/**
232+
* @deprecated Use {@link #Entry(List)} instead. TODO: rm in next major
233+
*/
234+
@Deprecated
227235
public Entry(IPrototypedIngredient<?, ?> instance) {
228-
this(instance, 0, 0, 0, 0);
236+
this(List.of(instance), 0, 0, 0, 0);
229237
}
230238

239+
/**
240+
* @deprecated Use {@link #getInstances()} instead. TODO: rm in next major
241+
*/
231242
@Override
243+
@Deprecated
232244
public IPrototypedIngredient<?, ?> getInstance() {
233-
return instance;
245+
return instances.get(0);
246+
}
247+
248+
@Override
249+
public List<IPrototypedIngredient<?, ?>> getInstances() {
250+
return instances;
234251
}
235252

236253
@Override
@@ -271,7 +288,13 @@ public void setQuantityMissing(long quantityMissing) {
271288

272289
public static CompoundTag serialize(TerminalCraftingPlanFlatStatic.Entry entry) {
273290
CompoundTag tag = new CompoundTag();
274-
tag.put("instance", IPrototypedIngredient.serialize(entry.getInstance()));
291+
292+
// New multi-instance field for alternatives
293+
ListTag instancesTag = new ListTag();
294+
for (IPrototypedIngredient<?, ?> instance : entry.getInstances()) {
295+
instancesTag.add(IPrototypedIngredient.serialize((PrototypedIngredient) instance));
296+
}
297+
tag.put("instances", instancesTag);
275298
tag.putLong("quantityToCraft", entry.getQuantityToCraft());
276299
tag.putLong("quantityCrafting", entry.getQuantityCrafting());
277300
tag.putLong("quantityInStorage", entry.getQuantityInStorage());
@@ -296,13 +319,22 @@ public static TerminalCraftingPlanFlatStatic.Entry deserialize(CompoundTag tag)
296319
throw new IllegalArgumentException("Could not find a quantityMissing entry in the given tag");
297320
}
298321

299-
IPrototypedIngredient<?, ?> instance = IPrototypedIngredient.deserialize(tag.getCompound("instance"));
322+
// Prefer the new \"instances\" list if present, otherwise fall back to the legacy single instance.
323+
List<IPrototypedIngredient<?, ?>> instances = Lists.newArrayList();
324+
if (tag.contains("instances", Tag.TAG_LIST)) {
325+
ListTag instancesTag = tag.getList("instances", Tag.TAG_COMPOUND);
326+
for (Tag base : instancesTag) {
327+
instances.add(IPrototypedIngredient.deserialize((CompoundTag) base));
328+
}
329+
} else {
330+
instances.add(IPrototypedIngredient.deserialize(tag.getCompound("instance"))); // TODO: rm in next major
331+
}
300332
long quantityToCraft = tag.getLong("quantityToCraft");
301333
long quantityCrafting = tag.getLong("quantityCrafting");
302334
long quantityInStorage = tag.getLong("quantityInStorage");
303335
long quantityMissing = tag.getLong("quantityMissing");
304336

305-
return new TerminalCraftingPlanFlatStatic.Entry(instance, quantityToCraft, quantityCrafting, quantityInStorage, quantityMissing);
337+
return new TerminalCraftingPlanFlatStatic.Entry(instances, quantityToCraft, quantityCrafting, quantityInStorage, quantityMissing);
306338
}
307339

308340
}

src/main/java/org/cyclops/integratedterminals/api/terminalstorage/crafting/TerminalCraftingPlanStatic.java

Lines changed: 61 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -176,25 +176,64 @@ public ITerminalCraftingPlanFlat<I> flatten() {
176176
}
177177

178178
public static class IndexedEntries {
179-
private final Map<IPrototypedIngredient<?, ?>, TerminalCraftingPlanFlatStatic.Entry> indexedEntries;
179+
/**
180+
* Entries indexed by a canonical list of prototype ingredients.
181+
* <p>
182+
* Each key is a list of {@link IPrototypedIngredient} where:
183+
* <ul>
184+
* <li>Every element has quantity 1.</li>
185+
* <li>Every element uses the exact-match-no-quantity condition.</li>
186+
* </ul>
187+
* The original (possibly non-normalized) alternatives list is stored inside the
188+
* {@link TerminalCraftingPlanFlatStatic.Entry} for rendering purposes.
189+
*/
190+
private final Map<List<IPrototypedIngredient<?, ?>>, TerminalCraftingPlanFlatStatic.Entry> indexedEntries;
180191

181192
public IndexedEntries() {
182193
this.indexedEntries = Maps.newHashMap();
183194
}
184195

185-
public TerminalCraftingPlanFlatStatic.Entry get(IPrototypedIngredient<?, ?> prototypedIngredient) {
186-
IPrototypedIngredient<?, ?> prototype = getPrototype(prototypedIngredient);
187-
return indexedEntries.computeIfAbsent(prototype, k -> new TerminalCraftingPlanFlatStatic.Entry(new PrototypedIngredient(prototypedIngredient.getComponent(), prototype.getPrototype(), prototypedIngredient.getCondition())));
188-
}
189-
190-
protected <T, M> IPrototypedIngredient<T, M> getPrototype(IPrototypedIngredient<T, M> prototypedIngredient) {
191-
IIngredientMatcher<T, M> matcher = prototypedIngredient.getComponent().getMatcher();
192-
return new PrototypedIngredient(prototypedIngredient.getComponent(), matcher.withQuantity(prototypedIngredient.getPrototype(), 1L), matcher.getExactMatchNoQuantityCondition());
193-
}
194-
195-
public static long getQuantity(IPrototypedIngredient<?, ?> prototypedIngredient) {
196-
IIngredientMatcher matcher = prototypedIngredient.getComponent().getMatcher();
197-
return matcher.getQuantity(prototypedIngredient.getPrototype());
196+
/**
197+
* Get (or create) the entry corresponding to the given list of alternative ingredients.
198+
*
199+
* @param prototypedIngredients A non-empty list of alternatives.
200+
* @return The corresponding flat plan entry.
201+
*/
202+
public TerminalCraftingPlanFlatStatic.Entry get(List<IPrototypedIngredient<?, ?>> prototypedIngredients) {
203+
List<IPrototypedIngredient<?, ?>> key = getPrototypes(prototypedIngredients);
204+
return indexedEntries.computeIfAbsent(key,
205+
k -> new TerminalCraftingPlanFlatStatic.Entry(k));
206+
}
207+
208+
/**
209+
* Build a canonical list of prototype ingredients for the given alternatives.
210+
* Quantities are normalized to 1 and the exact-match-no-quantity condition is used.
211+
*/
212+
protected List<IPrototypedIngredient<?, ?>> getPrototypes(List<IPrototypedIngredient<?, ?>> prototypedIngredients) {
213+
List<IPrototypedIngredient<?, ?>> result = new ArrayList<>(prototypedIngredients.size());
214+
for (IPrototypedIngredient<?, ?> ingredient : prototypedIngredients) {
215+
IIngredientMatcher matcher = ingredient.getComponent().getMatcher();
216+
result.add(new PrototypedIngredient(
217+
ingredient.getComponent(),
218+
matcher.withQuantity(ingredient.getPrototype(), 1L),
219+
matcher.getExactMatchNoQuantityCondition()));
220+
}
221+
return result;
222+
}
223+
224+
/**
225+
* Get the quantity associated with the given alternatives list.
226+
* <p>
227+
* This is derived from the first element, which is consistent with prior behaviour
228+
* when only a single prototype was available.
229+
*/
230+
public static long getQuantity(List<IPrototypedIngredient<?, ?>> prototypedIngredients) {
231+
if (prototypedIngredients.isEmpty()) {
232+
return 0;
233+
}
234+
IPrototypedIngredient<?, ?> first = prototypedIngredients.get(0);
235+
IIngredientMatcher matcher = first.getComponent().getMatcher();
236+
return matcher.getQuantity(first.getPrototype());
198237
}
199238

200239
public Collection<TerminalCraftingPlanFlatStatic.Entry> getEntries() {
@@ -211,8 +250,9 @@ protected static <I> void groupDependenciesByPrototype(IndexedEntries indexedEnt
211250

212251
// Determine outputs that are invalid or will be crafted
213252
for (IPrototypedIngredient<?, ?> output : plan.getOutputs()) {
214-
TerminalCraftingPlanFlatStatic.Entry entry = indexedEntries.get(output);
215-
long quantity = IndexedEntries.getQuantity(output);
253+
List<IPrototypedIngredient<?, ?>> outputs = List.of(output);
254+
TerminalCraftingPlanFlatStatic.Entry entry = indexedEntries.get(outputs);
255+
long quantity = IndexedEntries.getQuantity(outputs);
216256

217257
if (plan.getStatus() == TerminalCraftingJobStatus.ERROR
218258
|| plan.getStatus() == TerminalCraftingJobStatus.INVALID
@@ -237,16 +277,16 @@ protected static <I> void groupDependenciesByPrototype(IndexedEntries indexedEnt
237277

238278
// Determine storage ingredients
239279
for (IPrototypedIngredient<?, ?> output : plan.getStorageIngredients()) {
240-
TerminalCraftingPlanFlatStatic.Entry entry = indexedEntries.get(output);
241-
long quantity = IndexedEntries.getQuantity(output);
280+
List<IPrototypedIngredient<?, ?>> outputs = List.of(output);
281+
TerminalCraftingPlanFlatStatic.Entry entry = indexedEntries.get(outputs);
282+
long quantity = IndexedEntries.getQuantity(outputs);
242283
entry.setQuantityInStorage(entry.getQuantityInStorage() + quantity);
243284
}
244285

245286
// Determine missing ingredients
246287
for (List<IPrototypedIngredient<?, ?>> outputVariants : plan.getLastMissingIngredients()) {
247-
IPrototypedIngredient<?, ?> output = outputVariants.stream().findFirst().get();
248-
TerminalCraftingPlanFlatStatic.Entry entry = indexedEntries.get(output);
249-
long quantity = IndexedEntries.getQuantity(output);
288+
TerminalCraftingPlanFlatStatic.Entry entry = indexedEntries.get(outputVariants);
289+
long quantity = IndexedEntries.getQuantity(outputVariants);
250290
entry.setQuantityMissing(entry.getQuantityMissing() + quantity * plan.getCraftingQuantity());
251291
}
252292

src/main/java/org/cyclops/integratedterminals/client/gui/container/component/GuiCraftingPlanFlat.java

Lines changed: 23 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -102,6 +102,10 @@ public void setFirstRow(int firstRow) {
102102
this.firstRow = Math.max(0, firstRow);
103103
}
104104

105+
protected int getTick() {
106+
return (int) Minecraft.getInstance().level.getGameTime() / TICK_DELAY;
107+
}
108+
105109
@Override
106110
public void render(PoseStack matrixStack, int mouseX, int mouseY, float partialTicks) {
107111

@@ -133,15 +137,19 @@ private void drawElement(PoseStack matrixStack, Element element, int x, int y, i
133137

134138
int xOriginal = x;
135139

136-
// Draw instance
137-
IPrototypedIngredient<?, ?> output = element.getInstance();
138-
IngredientComponent<?, ?> ingredientComponent = output.getComponent();
139-
long quantity = ((IngredientComponent) ingredientComponent).getMatcher().getQuantity(output.getPrototype());
140-
int finalX = x;
141-
int finalY = y;
142-
ingredientComponent.getCapability(IngredientComponentTerminalStorageHandlerConfig.CAPABILITY)
143-
.ifPresent(h -> h.drawInstance(matrixStack, output.getPrototype(), quantity,
144-
"", this.parentGui, layer, partialTick, finalX, finalY, mouseX, mouseY, null));
140+
// Draw instance (rotate over alternatives if present)
141+
List<IPrototypedIngredient<?, ?>> instances = element.getInstances();
142+
if (!instances.isEmpty()) {
143+
int tick = getTick();
144+
IPrototypedIngredient<?, ?> output = instances.get(tick % instances.size());
145+
IngredientComponent<?, ?> ingredientComponent = output.getComponent();
146+
long quantity = ((IngredientComponent) ingredientComponent).getMatcher().getQuantity(output.getPrototype());
147+
int finalX = x;
148+
int finalY = y;
149+
ingredientComponent.getCapability(IngredientComponentTerminalStorageHandlerConfig.CAPABILITY)
150+
.ifPresent(h -> h.drawInstance(matrixStack, output.getPrototype(), quantity,
151+
"", this.parentGui, layer, partialTick, finalX, finalY, mouseX, mouseY, null));
152+
}
145153

146154
x = xOriginal + width - 50;
147155
if (layer == ContainerScreenTerminalStorage.DrawLayer.BACKGROUND) {
@@ -264,7 +272,7 @@ public static List<GuiCraftingPlanFlat.Element> getElements(ITerminalCraftingPla
264272

265273
protected static void addElements(ITerminalCraftingPlanFlat.IEntry craftingPlan, List<GuiCraftingPlanFlat.Element> elements) {
266274
elements.add(new Element(
267-
craftingPlan.getInstance(),
275+
craftingPlan.getInstances(),
268276
craftingPlan.getQuantityInStorage(),
269277
craftingPlan.getQuantityToCraft(),
270278
craftingPlan.getQuantityCrafting(),
@@ -283,23 +291,23 @@ public void updateNarration(NarrationElementOutput p_169152_) {
283291

284292
public static class Element {
285293

286-
private final IPrototypedIngredient<?, ?> instance;
294+
private final List<IPrototypedIngredient<?, ?>> instances;
287295
private final long storageQuantity;
288296
private final long toCraftQuantity;
289297
private final long craftingQuantity;
290298
private final long missingQuantity;
291299

292-
public Element(IPrototypedIngredient<?, ?> instance,
300+
public Element(List<IPrototypedIngredient<?, ?>> instances,
293301
long storageQuantity, long toCraftQuantity, long craftingQuantity, long missingQuantity) {
294-
this.instance = instance;
302+
this.instances = instances;
295303
this.storageQuantity = storageQuantity;
296304
this.toCraftQuantity = toCraftQuantity;
297305
this.craftingQuantity = craftingQuantity;
298306
this.missingQuantity = missingQuantity;
299307
}
300308

301-
public IPrototypedIngredient<?, ?> getInstance() {
302-
return instance;
309+
public List<IPrototypedIngredient<?, ?>> getInstances() {
310+
return instances;
303311
}
304312

305313
public long getStorageQuantity() {

0 commit comments

Comments
 (0)