-
Notifications
You must be signed in to change notification settings - Fork 58
Distinct Input Buses #451
Distinct Input Buses #451
Changes from all commits
16e685e
d9f79bc
4a78b11
2e131db
475e0b1
a2388e9
165ce9a
97ea73d
f2b1171
e90f81f
7aaaaae
7e8f3e9
3134a5e
4910e88
dc705e1
97d26f3
d1240c8
6bb4a5f
949caf8
2e699bd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -5,8 +5,9 @@ | |
import gregicadditions.capabilities.impl.GARecipeMapMultiblockController; | ||
import gregicadditions.item.components.*; | ||
import gregicadditions.utils.GALog; | ||
import gregicadditions.utils.Tuple; | ||
import gregtech.api.capability.IMultipleTankHandler; | ||
import gregtech.api.gui.Widget; | ||
import gregtech.api.metatileentity.MetaTileEntity; | ||
import gregtech.api.metatileentity.multiblock.MultiblockAbility; | ||
import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController; | ||
import gregtech.api.multiblock.BlockWorldState; | ||
|
@@ -20,6 +21,7 @@ | |
import net.minecraft.block.state.IBlockState; | ||
import net.minecraft.client.resources.I18n; | ||
import net.minecraft.item.ItemStack; | ||
import net.minecraft.nbt.NBTTagCompound; | ||
import net.minecraft.util.ResourceLocation; | ||
import net.minecraft.util.text.ITextComponent; | ||
import net.minecraft.util.text.TextComponentTranslation; | ||
|
@@ -28,6 +30,7 @@ | |
import net.minecraftforge.fluids.IFluidTank; | ||
import net.minecraftforge.fml.relauncher.Side; | ||
import net.minecraftforge.fml.relauncher.SideOnly; | ||
import net.minecraftforge.items.IItemHandler; | ||
import net.minecraftforge.items.IItemHandlerModifiable; | ||
|
||
import javax.annotation.Nullable; | ||
|
@@ -36,6 +39,9 @@ | |
import java.util.function.Predicate; | ||
import java.util.stream.IntStream; | ||
|
||
import static gregtech.api.gui.widgets.AdvancedTextWidget.withButton; | ||
import static gregtech.api.gui.widgets.AdvancedTextWidget.withHoverTextTranslate; | ||
|
||
abstract public class LargeSimpleRecipeMapMultiblockController extends GARecipeMapMultiblockController { | ||
|
||
private int EUtPercentage = 100; | ||
|
@@ -44,6 +50,15 @@ abstract public class LargeSimpleRecipeMapMultiblockController extends GARecipeM | |
private int stack = 1; | ||
public long maxVoltage = 0; | ||
|
||
/** | ||
* When false, this multiblock will behave like any other. | ||
* When true, this multiblock will treat each of its input buses as distinct, | ||
* checking recipes for them independently. This is useful for many machines, for example the | ||
* Large Extruder, where the player may want to put one extruder shape per bus, rather than | ||
* one machine per extruder shape. | ||
*/ | ||
protected boolean isDistinct = false; | ||
|
||
DecimalFormat formatter = new DecimalFormat("#0.0"); | ||
|
||
/** | ||
|
@@ -216,16 +231,48 @@ public boolean checkRecipe(Recipe recipe, boolean consumeIfSuccess) { | |
protected void addDisplayText(List<ITextComponent> textList) { | ||
super.addDisplayText(textList); | ||
textList.add(new TextComponentTranslation("gregtech.multiblock.universal.framework", this.maxVoltage)); | ||
|
||
ITextComponent buttonText = new TextComponentTranslation("gtadditions.multiblock.universal.distinct"); | ||
buttonText.appendText(" "); | ||
ITextComponent button = withButton((isDistinct ? | ||
new TextComponentTranslation("gtadditions.multiblock.universal.distinct.yes") : | ||
new TextComponentTranslation("gtadditions.multiblock.universal.distinct.no")), "distinct"); | ||
withHoverTextTranslate(button, "gtadditions.multiblock.universal.distinct.info"); | ||
buttonText.appendSibling(button); | ||
textList.add(buttonText); | ||
} | ||
|
||
@Override | ||
protected void handleDisplayClick(String componentData, Widget.ClickData clickData) { | ||
super.handleDisplayClick(componentData, clickData); | ||
isDistinct = !isDistinct; | ||
} | ||
|
||
@Override | ||
public NBTTagCompound writeToNBT(NBTTagCompound data) { | ||
super.writeToNBT(data); | ||
data.setBoolean("Distinct", isDistinct); | ||
return data; | ||
} | ||
|
||
@Override | ||
public void readFromNBT(NBTTagCompound data) { | ||
super.readFromNBT(data); | ||
isDistinct = data.getBoolean("Distinct"); | ||
} | ||
|
||
public static class LargeSimpleMultiblockRecipeLogic extends GAMultiblockRecipeLogic { | ||
|
||
private int EUtPercentage = 100; | ||
private int durationPercentage = 100; | ||
private int chancePercentage = 100; | ||
private int stack = 1; | ||
private final int EUtPercentage; | ||
private final int durationPercentage; | ||
private final int chancePercentage; | ||
private final int stack; | ||
public RecipeMap<?> recipeMap; | ||
|
||
// Fields used for distinct mode | ||
protected int lastRecipeIndex = 0; | ||
protected ItemStack[][] lastItemInputsMatrix; | ||
|
||
|
||
public LargeSimpleMultiblockRecipeLogic(RecipeMapMultiblockController tileEntity, int EUtPercentage, int durationPercentage, int chancePercentage, int stack) { | ||
super(tileEntity); | ||
|
@@ -252,12 +299,21 @@ public int getStack() { | |
return stack; | ||
} | ||
|
||
protected List<IItemHandlerModifiable> getInputBuses() { | ||
RecipeMapMultiblockController controller = (RecipeMapMultiblockController) metaTileEntity; | ||
return controller.getAbilities(MultiblockAbility.IMPORT_ITEMS); | ||
} | ||
|
||
@Override | ||
/** | ||
* From multi-smelter. | ||
* | ||
*/ | ||
protected void trySearchNewRecipe() { | ||
if (metaTileEntity instanceof LargeSimpleRecipeMapMultiblockController && ((LargeSimpleRecipeMapMultiblockController) metaTileEntity).isDistinct) { | ||
trySearchNewRecipeDistinct(); | ||
} else trySearchNewRecipeCombined(); | ||
} | ||
|
||
// Combined buses code ========================================================================================= | ||
|
||
private void trySearchNewRecipeCombined() { | ||
long maxVoltage = getMaxVoltage(); | ||
if (metaTileEntity instanceof LargeSimpleRecipeMapMultiblockController) | ||
maxVoltage = ((LargeSimpleRecipeMapMultiblockController) metaTileEntity).maxVoltage; | ||
|
@@ -285,24 +341,119 @@ protected void trySearchNewRecipe() { | |
} | ||
} | ||
|
||
@Override | ||
protected Recipe findRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMultipleTankHandler fluidInputs) { | ||
List<IItemHandlerModifiable> itemInputs = ((RecipeMapMultiblockController) this.getMetaTileEntity()).getAbilities(MultiblockAbility.IMPORT_ITEMS); | ||
// Distinct buses code ========================================================================================= | ||
|
||
private void trySearchNewRecipeDistinct() { | ||
long maxVoltage = getMaxVoltage(); | ||
Recipe currentRecipe = null; | ||
List<IItemHandlerModifiable> importInventory = getInputBuses(); | ||
IMultipleTankHandler importFluids = getInputTank(); | ||
|
||
Tuple<Recipe, IItemHandlerModifiable> recipePerInput = itemInputs.stream() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. you rewrite lots of code, but the only thing to change is that single line There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Its possible that it would be "functional" with a single change there, but there would be many places where the code is largely inefficient. I rewrote Though in my testing, the separate buses feature from before was not functional, and would often use molds/shapes from other buses rather than the one in the current bus. But if I am wrong on this, and the single change in |
||
.map(iItemHandlerModifiable -> new Tuple<>(recipeMap.findRecipe(maxVoltage, iItemHandlerModifiable, fluidInputs, 0), iItemHandlerModifiable)) | ||
.filter(tuple -> tuple.getKey() != null) | ||
.findFirst().orElse(new Tuple<>(recipeMap.findRecipe(maxVoltage, inputs, fluidInputs, 0), inputs)); | ||
// Our caching implementation | ||
// This guarantees that if we get a recipe cache hit, our efficiency is no different from other machines | ||
if (previousRecipe != null && previousRecipe.matches(false, importInventory.get(lastRecipeIndex), importFluids)) { | ||
currentRecipe = previousRecipe; | ||
if (setupAndConsumeRecipeInputs(currentRecipe, lastRecipeIndex)) { | ||
setupRecipe(currentRecipe); | ||
return; | ||
} | ||
} | ||
|
||
if (recipePerInput.getKey() == null) { | ||
return null; | ||
// On a cache miss, our efficiency is much worse, as it will check | ||
// each bus individually instead of the combined inventory all at once. | ||
for (int i = 0; i < importInventory.size(); i++) { | ||
IItemHandlerModifiable bus = importInventory.get(i); | ||
boolean dirty = checkRecipeInputsDirty(bus, importFluids, i); | ||
if (dirty || forceRecipeRecheck) { | ||
this.forceRecipeRecheck = false; | ||
currentRecipe = findRecipe(maxVoltage, bus, importFluids); | ||
if (currentRecipe != null) { | ||
this.previousRecipe = currentRecipe; | ||
} | ||
} | ||
if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe, i)) { | ||
lastRecipeIndex = i; | ||
setupRecipe(currentRecipe); | ||
break; | ||
} | ||
} | ||
} | ||
|
||
return createRecipe(maxVoltage, recipePerInput.getValue(), fluidInputs, recipePerInput.getKey()); | ||
// Replacing this for optimization reasons | ||
protected boolean checkRecipeInputsDirty(IItemHandler inputs, IMultipleTankHandler fluidInputs, int index) { | ||
boolean shouldRecheckRecipe = false; | ||
|
||
if (lastItemInputsMatrix == null || lastItemInputsMatrix.length != getInputBuses().size()) { | ||
lastItemInputsMatrix = new ItemStack[getInputBuses().size()][]; | ||
GALog.logger.info("Num buses: " + getInputBuses().size()); | ||
} | ||
if (lastItemInputsMatrix[index] == null || lastItemInputsMatrix[index].length != inputs.getSlots()) { | ||
this.lastItemInputsMatrix[index] = new ItemStack[inputs.getSlots()]; | ||
Arrays.fill(lastItemInputsMatrix[index], ItemStack.EMPTY); | ||
} | ||
if (lastFluidInputs == null || lastFluidInputs.length != fluidInputs.getTanks()) { | ||
this.lastFluidInputs = new FluidStack[fluidInputs.getTanks()]; | ||
} | ||
for (int i = 0; i < lastItemInputsMatrix[index].length; i++) { | ||
ItemStack currentStack = inputs.getStackInSlot(i); | ||
ItemStack lastStack = lastItemInputsMatrix[index][i]; | ||
if (!areItemStacksEqual(currentStack, lastStack)) { | ||
this.lastItemInputsMatrix[index][i] = currentStack.isEmpty() ? ItemStack.EMPTY : currentStack.copy(); | ||
shouldRecheckRecipe = true; | ||
} else if (currentStack.getCount() != lastStack.getCount()) { | ||
lastStack.setCount(currentStack.getCount()); | ||
shouldRecheckRecipe = true; | ||
} | ||
} | ||
for (int i = 0; i < lastFluidInputs.length; i++) { | ||
FluidStack currentStack = fluidInputs.getTankAt(i).getFluid(); | ||
FluidStack lastStack = lastFluidInputs[i]; | ||
if ((currentStack == null && lastStack != null) || | ||
(currentStack != null && !currentStack.isFluidEqual(lastStack))) { | ||
this.lastFluidInputs[i] = currentStack == null ? null : currentStack.copy(); | ||
shouldRecheckRecipe = true; | ||
} else if (currentStack != null && lastStack != null && | ||
currentStack.amount != lastStack.amount) { | ||
lastStack.amount = currentStack.amount; | ||
shouldRecheckRecipe = true; | ||
} | ||
} | ||
return shouldRecheckRecipe; | ||
} | ||
|
||
protected boolean setupAndConsumeRecipeInputs(Recipe recipe, int index) { | ||
RecipeMapMultiblockController controller = (RecipeMapMultiblockController) metaTileEntity; | ||
if (controller.checkRecipe(recipe, false)) { | ||
|
||
int[] resultOverclock = calculateOverclock(recipe.getEUt(), recipe.getDuration()); | ||
int totalEUt = resultOverclock[0] * resultOverclock[1]; | ||
IItemHandlerModifiable importInventory = getInputBuses().get(index); | ||
IItemHandlerModifiable exportInventory = getOutputInventory(); | ||
IMultipleTankHandler importFluids = getInputTank(); | ||
IMultipleTankHandler exportFluids = getOutputTank(); | ||
boolean setup = (totalEUt >= 0 ? getEnergyStored() >= (totalEUt > getEnergyCapacity() / 2 ? resultOverclock[0] : totalEUt) : | ||
(getEnergyStored() - resultOverclock[0] <= getEnergyCapacity())) && | ||
MetaTileEntity.addItemsToItemHandler(exportInventory, true, recipe.getAllItemOutputs(exportInventory.getSlots())) && | ||
MetaTileEntity.addFluidsToFluidHandler(exportFluids, true, recipe.getFluidOutputs()) && | ||
recipe.matches(true, importInventory, importFluids); | ||
|
||
if (setup) { | ||
controller.checkRecipe(recipe, true); | ||
return true; | ||
} | ||
} | ||
return false; | ||
} | ||
|
||
// Shared recipe generation code =============================================================================== | ||
|
||
@Override | ||
protected Recipe findRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMultipleTankHandler fluidInputs) { | ||
Recipe recipe = super.findRecipe(maxVoltage, inputs, fluidInputs); | ||
if (recipe != null) | ||
return createRecipe(maxVoltage, inputs, fluidInputs, recipe); | ||
return null; | ||
} | ||
|
||
protected Recipe createRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMultipleTankHandler fluidInputs, Recipe matchingRecipe) { | ||
int maxItemsLimit = this.stack; | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I dont understand why I used
universal
instead ofcommon
-_-