Skip to content

Commit 0d3437e

Browse files
committed
Port Chanced Outputs TOP Enhancements from NomiLabs
Chanced outputs no longer display as normal outputs in TOP when rolled, which allowed players to see the outcome before the recipe completed. Instead, all potential outputs are now displayed with an indicator overlaid on each chanced output to display the percent chance (including applicable overclocking bonuses) via a new ElementFactory. Support for getting this information to TOP has been added to AbstractRecipeLogic.
1 parent 38aca71 commit 0d3437e

File tree

4 files changed

+209
-26
lines changed

4 files changed

+209
-26
lines changed

src/main/java/gregtech/api/capability/impl/AbstractRecipeLogic.java

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,7 @@
2424
import net.minecraftforge.fluids.IFluidTank;
2525
import net.minecraftforge.items.IItemHandler;
2626
import net.minecraftforge.items.IItemHandlerModifiable;
27+
import org.apache.commons.lang3.tuple.Pair;
2728
import org.jetbrains.annotations.NotNull;
2829

2930
import java.util.ArrayList;
@@ -42,6 +43,9 @@ public abstract class AbstractRecipeLogic extends MTETrait implements IWorkable
4243
private static final String RECIPE_EUT = "RecipeEUt";
4344
private static final String ITEM_OUTPUTS = "ItemOutputs";
4445
private static final String FLUID_OUTPUTS = "FluidOutputs";
46+
private static final String CHANCE = "Chance";
47+
private static final String CHANCED_ITEM_OUTPUTS = "ChancedItemOutputs";
48+
private static final String NON_CHANCED_ITEM_AMOUNT = "NonChancedItemAmt";
4549

4650
public final RecipeMap<?> recipeMap;
4751

@@ -63,6 +67,14 @@ public abstract class AbstractRecipeLogic extends MTETrait implements IWorkable
6367
protected boolean wasActiveAndNeedsUpdate;
6468
protected boolean isOutputsFull;
6569
protected boolean invalidInputsForRecipes;
70+
protected int nonChancedItemAmt;
71+
72+
/**
73+
* A list containing all possible chanced item outputs from the active recipe, paired with their
74+
* overclock-boosted chance value.
75+
*/
76+
protected List<Pair<ItemStack, Integer>> chancedItemOutputs;
77+
6678

6779
public AbstractRecipeLogic(MetaTileEntity tileEntity, RecipeMap<?> recipeMap) {
6880
super(tileEntity);
@@ -350,6 +362,8 @@ protected void setupRecipe(Recipe recipe) {
350362
this.recipeEUt = resultOverclock[0];
351363
this.fluidOutputs = GTUtility.copyFluidList(recipe.getFluidOutputs());
352364
int overclocks = getMachineTierForRecipe(recipe) - recipe.getBaseTier();
365+
this.nonChancedItemAmt = recipe.getOutputs().size();
366+
this.chancedItemOutputs = recipe.getChancedRecipeOutputsAtTier(overclocks);
353367
this.itemOutputs = GTUtility.copyStackList(recipe.getResultItemOutputs(getOutputInventory().getSlots(), random, overclocks));
354368
if (this.wasActiveAndNeedsUpdate) {
355369
this.wasActiveAndNeedsUpdate = false;
@@ -376,6 +390,15 @@ protected void setupRecipe(Recipe recipe) {
376390
return GTUtility.copyFluidList(fluidOutputs);
377391
}
378392

393+
/**
394+
* @return all possible chanced item outputs paired with their computed chances for the currently active recipe
395+
*/
396+
public List<Pair<ItemStack, Integer>> getChancedItemOutputs() {
397+
if(itemOutputs == null)
398+
return Collections.emptyList();
399+
return chancedItemOutputs;
400+
}
401+
379402
protected int getMachineTierForRecipe(Recipe recipe) {
380403
return GTUtility.getTierByVoltage(getMaxVoltage());
381404
}
@@ -388,6 +411,8 @@ protected void completeRecipe() {
388411
this.recipeEUt = 0;
389412
this.fluidOutputs = null;
390413
this.itemOutputs = null;
414+
this.chancedItemOutputs = null;
415+
this.nonChancedItemAmt = 0;
391416
setHasNotEnoughEnergy(false);
392417
this.wasActiveAndNeedsUpdate = true;
393418
}
@@ -414,6 +439,13 @@ public int getRecipeEUt() {
414439
return recipeEUt;
415440
}
416441

442+
/**
443+
* @return the number of non-chanced item outputs this recipe produces
444+
*/
445+
public int getNonChancedItemAmt() {
446+
return nonChancedItemAmt;
447+
}
448+
417449
public void setMaxProgress(int maxProgress) {
418450
this.maxProgressTime = maxProgress;
419451
metaTileEntity.markDirty();
@@ -562,6 +594,16 @@ public NBTTagCompound serializeNBT() {
562594
}
563595
compound.setTag(ITEM_OUTPUTS, itemOutputsList);
564596
compound.setTag(FLUID_OUTPUTS, fluidOutputsList);
597+
598+
NBTTagList chancedItemOutputsList = new NBTTagList();
599+
for(var entry : chancedItemOutputs) {
600+
NBTTagCompound tag = entry.getKey().writeToNBT(new NBTTagCompound());
601+
tag.setInteger(CHANCE, entry.getValue());
602+
chancedItemOutputsList.appendTag(tag);
603+
}
604+
605+
compound.setTag(CHANCED_ITEM_OUTPUTS, chancedItemOutputsList);
606+
compound.setInteger(NON_CHANCED_ITEM_AMOUNT, nonChancedItemAmt);
565607
}
566608
return compound;
567609
}
@@ -596,6 +638,14 @@ public void deserializeNBT(NBTTagCompound compound) {
596638
for(var f : fluidOutputsList)
597639
if(f instanceof NBTTagCompound tag)
598640
this.fluidOutputs.add(FluidStack.loadFluidStackFromNBT(tag));
641+
642+
NBTTagList chancedItemOutputsList = compound.getTagList(CHANCED_ITEM_OUTPUTS, Constants.NBT.TAG_COMPOUND);
643+
this.chancedItemOutputs = new ArrayList<>();
644+
for(var ci : chancedItemOutputsList)
645+
if(ci instanceof NBTTagCompound tag)
646+
this.chancedItemOutputs.add(Pair.of(new ItemStack(tag), tag.getInteger(CHANCE)));
647+
648+
this.nonChancedItemAmt = compound.getInteger(NON_CHANCED_ITEM_AMOUNT);
599649
}
600650
}
601651
}
Lines changed: 75 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,75 @@
1+
/*
2+
Code adapted from Nomi-Labs https://github.com/Nomi-CEu/Nomi-Labs @ d2d6e89
3+
*/
4+
package gregtech.integration.nomilabs.element;
5+
6+
import gregtech.integration.theoneprobe.TheOneProbeCompatibility;
7+
import io.netty.buffer.ByteBuf;
8+
import mcjty.theoneprobe.api.IItemStyle;
9+
import mcjty.theoneprobe.apiimpl.elements.ElementItemStack;
10+
import net.minecraft.client.Minecraft;
11+
import net.minecraft.client.renderer.GlStateManager;
12+
import net.minecraft.item.ItemStack;
13+
import net.minecraftforge.fml.relauncher.Side;
14+
import net.minecraftforge.fml.relauncher.SideOnly;
15+
16+
import java.text.DecimalFormat;
17+
18+
public class LabsChancedItemStackElement extends ElementItemStack {
19+
20+
private static final DecimalFormat format = new DecimalFormat("#.#");
21+
22+
private final int chance;
23+
24+
public LabsChancedItemStackElement(ItemStack itemStack, int chance, IItemStyle style) {
25+
super(itemStack, style);
26+
this.chance = chance;
27+
}
28+
29+
public LabsChancedItemStackElement(ByteBuf buf) {
30+
super(buf);
31+
chance = buf.readInt();
32+
}
33+
34+
@Override
35+
public void render(int x, int y) {
36+
super.render(x, y);
37+
renderChance(chance, x, y);
38+
}
39+
40+
@Override
41+
public void toBytes(ByteBuf buf) {
42+
super.toBytes(buf);
43+
buf.writeInt(chance);
44+
}
45+
46+
@Override
47+
public int getID() {
48+
return TheOneProbeCompatibility.CHANCED_ITEM_STACK_ELEMENT;
49+
}
50+
51+
@SideOnly(Side.CLIENT)
52+
public static void renderChance(int chance, int x, int y) {
53+
GlStateManager.disableLighting();
54+
GlStateManager.disableDepth();
55+
GlStateManager.disableBlend();
56+
57+
GlStateManager.pushMatrix();
58+
GlStateManager.scale(0.5f, 0.5f, 1);
59+
Minecraft mc = Minecraft.getMinecraft();
60+
61+
String chanceTxt = formatChance(chance);
62+
mc.fontRenderer.drawStringWithShadow(chanceTxt, (x + 17) * 2 - 1 - mc.fontRenderer.getStringWidth(chanceTxt),
63+
y * 2, 0xffffffff);
64+
65+
GlStateManager.popMatrix();
66+
67+
GlStateManager.enableLighting();
68+
GlStateManager.enableDepth();
69+
GlStateManager.enableBlend();
70+
}
71+
72+
public static String formatChance(int chance) {
73+
return format.format(chance / 100.0) + "%";
74+
}
75+
}

src/main/java/gregtech/integration/theoneprobe/TheOneProbeCompatibility.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,12 +10,14 @@ public class TheOneProbeCompatibility {
1010
public static int FLUID_NAME_ELEMENT;
1111
public static int FLUID_STACK_ELEMENT;
1212
public static int CUSTOM_NAME_ELEMENT;
13+
public static int CHANCED_ITEM_STACK_ELEMENT;
1314

1415
public static void registerCompatibility() {
1516
ITheOneProbe oneProbe = TheOneProbe.theOneProbeImp;
1617
FLUID_NAME_ELEMENT = oneProbe.registerElementFactory(LabsFluidNameElement::new);
1718
FLUID_STACK_ELEMENT = oneProbe.registerElementFactory(LabsFluidStackElement::new);
1819
CUSTOM_NAME_ELEMENT = oneProbe.registerElementFactory(CustomNameElement::new);
20+
CHANCED_ITEM_STACK_ELEMENT = oneProbe.registerElementFactory(LabsChancedItemStackElement::new);
1921

2022
oneProbe.registerProvider(new ElectricContainerInfoProvider());
2123
oneProbe.registerProvider(new FuelableInfoProvider());

src/main/java/gregtech/integration/theoneprobe/provider/RecipeOutputsProvider.java

Lines changed: 82 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
/*
2-
Code adapted from Nomi-Labs https://github.com/Nomi-CEu/Nomi-Labs @ 6e14c06
2+
Code adapted from Nomi-Labs https://github.com/Nomi-CEu/Nomi-Labs @ d2d6e89
33
*/
44
package gregtech.integration.theoneprobe.provider;
55

@@ -10,20 +10,23 @@
1010
import gregtech.integration.nomilabs.element.*;
1111
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
1212
import mcjty.theoneprobe.api.*;
13+
import mcjty.theoneprobe.apiimpl.elements.ElementItemStack;
1314
import mcjty.theoneprobe.apiimpl.styles.*;
1415
import mcjty.theoneprobe.config.Config;
1516
import net.minecraft.entity.player.EntityPlayer;
1617
import net.minecraft.item.ItemStack;
1718
import net.minecraft.tileentity.TileEntity;
1819
import net.minecraftforge.common.capabilities.Capability;
1920
import net.minecraftforge.fluids.*;
21+
import org.apache.commons.lang3.tuple.Pair;
2022
import org.jetbrains.annotations.NotNull;
2123

2224
import java.util.*;
23-
import java.util.function.BiConsumer;
25+
import java.util.function.Function;
2426

2527
import static mcjty.theoneprobe.api.ElementAlignment.*;
2628
import static mcjty.theoneprobe.api.TextStyleClass.*;
29+
import static gregtech.integration.nomilabs.element.LabsChancedItemStackElement.formatChance;
2730

2831
public class RecipeOutputsProvider extends CapabilityInfoProvider<IWorkable> {
2932

@@ -48,71 +51,67 @@ protected void addProbeInfo(IWorkable capability, IProbeInfo info, EntityPlayer
4851
// Generators, Ignore
4952
if (recipe.getRecipeEUt() < 0) return;
5053

51-
var outputs = getUniqueItems(recipe.getItemOutputs());
52-
var fluidOutputs = getUniqueFluids(recipe.getFluidOutputs());
54+
var lists = createItemFluidElementLists(recipe);
55+
var items = lists.getLeft();
56+
var fluids = lists.getRight();
5357

54-
if (outputs.isEmpty() && fluidOutputs.isEmpty()) return;
58+
if (items.isEmpty() && fluids.isEmpty()) return;
5559

56-
boolean showDetailed = outputs.size() + fluidOutputs.size() <= Config.showItemDetailThresshold &&
57-
player.isSneaking();
60+
boolean showDetailed = items.size() + fluids.size() <= Config.showItemDetailThresshold && player.isSneaking();
5861
IProbeInfo mainPanel = info.vertical()
5962
.text("{*gregtech.top.crafting*}")
6063
.vertical(info.defaultLayoutStyle().borderColor(Config.chestContentsBorderColor)
6164
.spacing(5));
6265

6366
if (showDetailed) {
64-
for (var entry : outputs.entrySet()) {
65-
ItemStack stack = entry.getKey().toStack(entry.getValue());
67+
for (var entry : items)
6668
mainPanel.horizontal(new LayoutStyle().spacing(10).alignment(ALIGN_CENTER))
67-
.item(stack, new ItemStyle().width(16).height(16))
68-
.text(INFO + stack.getDisplayName());
69-
}
69+
.element(entry.getValue())
70+
.text(INFO + entry.getKey());
7071

71-
for (var entry : fluidOutputs.entrySet()) {
72-
FluidStack stack = new FluidStack(entry.getKey(), entry.getValue());
72+
for (var entry : fluids)
7373
mainPanel.horizontal(new LayoutStyle().spacing(10).alignment(ALIGN_CENTER))
74-
.element(new LabsFluidStackElement(stack))
75-
.element(new LabsFluidNameElement(stack, false));
76-
}
74+
.element(entry.getValue())
75+
.element(entry.getKey());
76+
7777
return;
7878
}
7979

8080
// If outputs and fluid outputs are both of size 1, show on same row instead of over two rows
81-
boolean condense = outputs.size() == 1 && fluidOutputs.size() == 1;
81+
boolean condense = items.size() == 1 && fluids.size() == 1;
8282
IProbeInfo sharedHorizontal = null;
8383

8484
if (condense)
8585
sharedHorizontal = createHorizontalLayout(mainPanel);
8686

87-
if (!outputs.isEmpty()) {
87+
if (!items.isEmpty()) {
8888
IProbeInfo panel;
8989
if (condense)
9090
panel = sharedHorizontal;
9191
else
9292
panel = createHorizontalLayout(mainPanel);
9393

94-
addOutputs(outputs, (meta, amt) -> panel.item(meta.toStack(amt), new ItemStyle().width(16).height(16)));
94+
addOutputs(items, panel, Pair::getValue);
9595
}
9696

97-
if (!fluidOutputs.isEmpty()) {
97+
if (!fluids.isEmpty()) {
9898
IProbeInfo panel;
9999
if (condense)
100100
panel = sharedHorizontal;
101101
else
102102
panel = createHorizontalLayout(mainPanel);
103103

104-
addOutputs(fluidOutputs,
105-
(fluid, amount) -> panel.element(new LabsFluidStackElement(new FluidStack(fluid, amount))));
104+
addOutputs(fluids, panel, Pair::getValue);
106105
}
107106
}
108107

109-
private <T> void addOutputs(Map<T, Integer> outputs, BiConsumer<T, Integer> addToPanel) {
108+
private <T> void addOutputs(List<T> list, IProbeInfo panel, Function<T, IElement> getElement) {
110109
int idx = 0;
111110

112-
for (var output : outputs.entrySet()) {
111+
for (var entry : list) {
113112
if (idx >= AMT_IN_ROW) break;
114113

115-
addToPanel.accept(output.getKey(), output.getValue());
114+
panel.element(getElement.apply(entry));
116115
idx++;
117116
}
118117
}
@@ -121,6 +120,63 @@ private IProbeInfo createHorizontalLayout(IProbeInfo mainPanel) {
121120
return mainPanel.horizontal(new LayoutStyle().spacing(4));
122121
}
123122

123+
private Pair<List<Pair<String, ElementItemStack>>, List<Pair<LabsFluidNameElement, LabsFluidStackElement>>> createItemFluidElementLists(AbstractRecipeLogic recipe) {
124+
// Items
125+
var outputs =
126+
getUnique(recipe.getItemOutputs().subList(0, recipe.getNonChancedItemAmt()),
127+
ItemStack::isEmpty, ItemMeta::new, ItemStack::getCount);
128+
129+
var chancedOutputs =
130+
getUnique(recipe.getChancedItemOutputs(),
131+
(chanced) -> chanced.getKey().isEmpty() || chanced.getValue() == 0,
132+
(chanced) -> Pair.of(new ItemMeta(chanced.getKey()), chanced.getValue()),
133+
(chanced) -> chanced.getKey().getCount());
134+
135+
IItemStyle style = new ItemStyle().width(16).height(16);
136+
List<Pair<String, ElementItemStack>> items = new ArrayList<>();
137+
138+
for (var output : outputs.entrySet()) {
139+
ItemStack stack = output.getKey().toStack(output.getValue());
140+
items.add(Pair.of(stack.getDisplayName(), new ElementItemStack(stack, style)));
141+
}
142+
143+
for (var chanced : chancedOutputs.entrySet()) {
144+
ItemStack stack = chanced.getKey().getKey().toStack(chanced.getValue());
145+
String display = stack.getDisplayName() + " (" + formatChance(chanced.getKey().getValue()) + ")";
146+
items.add(Pair.of(display, new LabsChancedItemStackElement(stack, chanced.getKey().getValue(), style)));
147+
}
148+
149+
// Fluids
150+
var fluidOutputs =
151+
getUnique(recipe.getFluidOutputs(),
152+
(stack) -> stack.amount == 0, FluidStack::getFluid, (stack) -> stack.amount);
153+
154+
List<Pair<LabsFluidNameElement, LabsFluidStackElement>> fluids = new ArrayList<>();
155+
156+
for (var output : fluidOutputs.entrySet()) {
157+
FluidStack stack = new FluidStack(output.getKey(), output.getValue());
158+
fluids.add(Pair.of(new LabsFluidNameElement(stack, false), new LabsFluidStackElement(stack)));
159+
}
160+
161+
return Pair.of(items, fluids);
162+
}
163+
164+
private <T, K> Map<K, Integer> getUnique(List<T> stacks, Function<T, Boolean> emptyCheck, Function<T, K> getKey,
165+
Function<T, Integer> getCount) {
166+
Map<K, Integer> map = new Object2ObjectLinkedOpenHashMap<>();
167+
168+
for (T stack : stacks) {
169+
if (emptyCheck.apply(stack)) continue;
170+
171+
map.compute(getKey.apply(stack), (key, count) -> {
172+
if (count == null) count = 0;
173+
return count + getCount.apply(stack);
174+
});
175+
}
176+
177+
return map;
178+
}
179+
124180
private Map<ItemMeta, Integer> getUniqueItems(List<ItemStack> stacks) {
125181
Map<ItemMeta, Integer> map = new Object2ObjectLinkedOpenHashMap<>();
126182

0 commit comments

Comments
 (0)