Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement Distinct Buses #124

Merged
merged 7 commits into from
Sep 9, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ protected void trySearchNewRecipe() {
this.invalidInputsForRecipes = (currentRecipe == null);

// proceed if we have a usable recipe.
if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe))
if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe, importInventory))
setupRecipe(currentRecipe);
// Inputs have been inspected.
metaTileEntity.getNotifiedItemInputList().clear();
Expand Down Expand Up @@ -236,10 +236,21 @@ protected static boolean areItemStacksEqual(ItemStack stackA, ItemStack stackB)
ItemStack.areItemStackTagsEqual(stackA, stackB));
}

protected boolean setupAndConsumeRecipeInputs(Recipe recipe) {
/**
* Determines if the provided recipe is possible to run from the provided inventory, or if there is anything preventing
* the Recipe from being completed.
*
* Will consume the inputs of the Recipe if it is possible to run.
*
* @param recipe - The Recipe that will be consumed from the inputs and ran in the machine
* @param importInventory - The inventory that the recipe should be consumed from.
* Used mainly for Distinct bus implementation for multiblocks to specify
* a specific bus
* @return - true if the recipe is successful, false if the recipe is not successful
*/
protected boolean setupAndConsumeRecipeInputs(Recipe recipe, IItemHandlerModifiable importInventory) {
int[] resultOverclock = calculateOverclock(recipe.getEUt(), recipe.getDuration());
int totalEUt = resultOverclock[0] * resultOverclock[1];
IItemHandlerModifiable importInventory = getInputInventory();
IItemHandlerModifiable exportInventory = getOutputInventory();
IMultipleTankHandler importFluids = getInputTank();
IMultipleTankHandler exportFluids = getOutputTank();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,13 +2,22 @@

import gregtech.api.capability.IEnergyContainer;
import gregtech.api.capability.IMultipleTankHandler;
import gregtech.api.metatileentity.multiblock.MultiblockAbility;
import gregtech.api.metatileentity.multiblock.MultiblockWithDisplayBase;
import gregtech.api.metatileentity.multiblock.RecipeMapMultiblockController;
import gregtech.api.recipes.MatchingMode;
import gregtech.api.recipes.Recipe;
import net.minecraftforge.items.IItemHandlerModifiable;

import java.util.ArrayList;
import java.util.List;

public class MultiblockRecipeLogic extends AbstractRecipeLogic {

// Used for distinct mode
protected int lastRecipeIndex = 0;
protected List<IItemHandlerModifiable> invalidatedInputList = new ArrayList<>();


public MultiblockRecipeLogic(RecipeMapMultiblockController tileEntity) {
super(tileEntity, tileEntity.recipeMap);
Expand Down Expand Up @@ -36,9 +45,14 @@ public void invalidate() {
recipeEUt = 0;
fluidOutputs = null;
itemOutputs = null;
lastRecipeIndex = 0;
setActive(false); // this marks dirty for us
}

public void onDistinctChanged() {
this.lastRecipeIndex = 0;
}

public IEnergyContainer getEnergyContainer() {
RecipeMapMultiblockController controller = (RecipeMapMultiblockController) metaTileEntity;
return controller.getEnergyContainer();
Expand All @@ -50,6 +64,12 @@ protected IItemHandlerModifiable getInputInventory() {
return controller.getInputInventory();
}

// Used for distinct bus recipe checking
protected List<IItemHandlerModifiable> getInputBuses() {
RecipeMapMultiblockController controller = (RecipeMapMultiblockController) metaTileEntity;
return controller.getAbilities(MultiblockAbility.IMPORT_ITEMS);
}

@Override
protected IItemHandlerModifiable getOutputInventory() {
RecipeMapMultiblockController controller = (RecipeMapMultiblockController) metaTileEntity;
Expand All @@ -71,13 +91,86 @@ protected IMultipleTankHandler getOutputTank() {
@Override
protected void trySearchNewRecipe() {
// do not run recipes when there are more than 5 maintenance problems
// Maintenance can apply to all multiblocks, so cast to a base multiblock class
MultiblockWithDisplayBase controller = (MultiblockWithDisplayBase) metaTileEntity;
if (controller.hasMaintenanceMechanics() && controller.getNumMaintenanceProblems() > 5)
if (controller.hasMaintenanceMechanics() && controller.getNumMaintenanceProblems() > 5) {
return;
}

// Distinct buses only apply to some of the multiblocks, so check the controller against a lower class
if(controller instanceof RecipeMapMultiblockController) {
ALongStringOfNumbers marked this conversation as resolved.
Show resolved Hide resolved
RecipeMapMultiblockController distinctController = (RecipeMapMultiblockController) controller;

if(distinctController.canBeDistinct() && distinctController.isDistinct()) {
trySearchNewRecipeDistinct();
return;
}
}

super.trySearchNewRecipe();
}

protected void trySearchNewRecipeDistinct() {
long maxVoltage = getMaxVoltage();
Recipe currentRecipe = null;
List<IItemHandlerModifiable> importInventory = getInputBuses();
IMultipleTankHandler importFluids = getInputTank();

//if fluids changed, iterate all input busses again
if (metaTileEntity.getNotifiedFluidInputList().size() > 0) {
for (IItemHandlerModifiable ihm : importInventory){
if (!metaTileEntity.getNotifiedItemInputList().contains(ihm)){
metaTileEntity.getNotifiedItemInputList().add(ihm);
}
}
metaTileEntity.getNotifiedFluidInputList().clear();
}

// 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 a valid recipe is found, immediately attempt to return it to prevent inventory scanning
if (setupAndConsumeRecipeInputs(currentRecipe, importInventory.get(lastRecipeIndex))) {
setupRecipe(currentRecipe);
metaTileEntity.getNotifiedItemInputList().remove(importInventory.get(lastRecipeIndex));

// No need to cache the previous recipe here, as it is not null and matched by the current recipe,
// so it will always be the same
return;
}
}

// 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);
// Skip this bus if no recipe was found last time and the inventory did not change
if (invalidatedInputList.contains(bus) && !metaTileEntity.getNotifiedItemInputList().contains(bus)) {
continue;
} else {
invalidatedInputList.remove(bus);
}
// Look for a new recipe after a cache miss
currentRecipe = findRecipe(maxVoltage, bus, importFluids, MatchingMode.DEFAULT);
// Cache the current recipe, if one is found
if (currentRecipe != null) {
this.previousRecipe = currentRecipe;
if (setupAndConsumeRecipeInputs(currentRecipe, importInventory.get(i))) {
lastRecipeIndex = i;
setupRecipe(currentRecipe);
metaTileEntity.getNotifiedItemInputList().remove(bus);
return;
}
} else {
invalidatedInputList.add(bus);
}
}

//If no matching recipes are found, clear the notified inputs so we know when new items are given
metaTileEntity.getNotifiedItemInputList().clear();
}

@Override
protected int[] calculateOverclock(int EUt, long voltage, int duration) {
// apply maintenance penalties
Expand All @@ -91,10 +184,10 @@ protected int[] calculateOverclock(int EUt, long voltage, int duration) {
}

@Override
protected boolean setupAndConsumeRecipeInputs(Recipe recipe) {
protected boolean setupAndConsumeRecipeInputs(Recipe recipe, IItemHandlerModifiable importInventory) {
RecipeMapMultiblockController controller = (RecipeMapMultiblockController) metaTileEntity;
if (controller.checkRecipe(recipe, false) &&
super.setupAndConsumeRecipeInputs(recipe)) {
super.setupAndConsumeRecipeInputs(recipe, importInventory)) {
controller.checkRecipe(recipe, true);
return true;
} else return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.WorldServer;
import net.minecraftforge.fluids.IFluidTank;
import net.minecraftforge.items.IItemHandlerModifiable;

public class RecipeLogicSteam extends AbstractRecipeLogic {

Expand Down Expand Up @@ -155,8 +156,8 @@ public void update() {
}

@Override
protected boolean setupAndConsumeRecipeInputs(Recipe recipe) {
return !this.needsVenting && super.setupAndConsumeRecipeInputs(recipe);
protected boolean setupAndConsumeRecipeInputs(Recipe recipe, IItemHandlerModifiable importInventory) {
return !this.needsVenting && super.setupAndConsumeRecipeInputs(recipe, importInventory);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ protected void trySearchNewRecipe() {
// replace old recipe with new one
this.previousRecipe = currentRecipe;
// proceed if we have a usable recipe.
if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe))
if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe, importInventory))
setupRecipe(currentRecipe);
// Inputs have been inspected.
metaTileEntity.getNotifiedItemInputList().clear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -107,10 +107,10 @@ protected long getMaxVoltage() {
}

@Override
protected boolean setupAndConsumeRecipeInputs(Recipe recipe) {
protected boolean setupAndConsumeRecipeInputs(Recipe recipe, IItemHandlerModifiable importInventory) {
RecipeMapSteamMultiblockController controller = (RecipeMapSteamMultiblockController) metaTileEntity;
if (controller.checkRecipe(recipe, false) &&
super.setupAndConsumeRecipeInputs(recipe)) {
super.setupAndConsumeRecipeInputs(recipe, importInventory)) {
controller.checkRecipe(recipe, true);
return true;
} else return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,9 @@ public AdvancedTextWidget(int xPosition, int yPosition, Consumer<List<ITextCompo
public static ITextComponent withButton(ITextComponent textComponent, String componentData) {
Style style = textComponent.getStyle();
style.setClickEvent(new ClickEvent(Action.OPEN_URL, "@!" + componentData));
style.setColor(TextFormatting.YELLOW);
if(style.getColor() == null) {
style.setColor(TextFormatting.YELLOW);
}
return textComponent;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,11 +12,15 @@
import gregtech.api.capability.impl.FluidTankList;
import gregtech.api.capability.impl.ItemHandlerList;
import gregtech.api.capability.impl.MultiblockRecipeLogic;
import gregtech.api.gui.Widget;
import gregtech.api.gui.widgets.AdvancedTextWidget;
import gregtech.api.multiblock.PatternMatchContext;
import gregtech.api.recipes.Recipe;
import gregtech.api.recipes.RecipeMap;
import gregtech.api.util.GTUtility;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.network.PacketBuffer;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.util.text.Style;
Expand All @@ -42,6 +46,8 @@ public abstract class RecipeMapMultiblockController extends MultiblockWithDispla
protected IMultipleTankHandler outputFluidInventory;
protected IEnergyContainer energyContainer;

private boolean isDistinct = false;

public RecipeMapMultiblockController(ResourceLocation metaTileEntityId, RecipeMap<?> recipeMap) {
super(metaTileEntityId);
this.recipeMap = recipeMap;
Expand Down Expand Up @@ -136,6 +142,17 @@ protected void addDisplayText(List<ITextComponent> textList) {
textList.add(new TextComponentTranslation("gregtech.multiblock.max_energy_per_tick", maxVoltage, voltageName));
}

if(canBeDistinct()) {
ITextComponent buttonText = new TextComponentTranslation("gregtech.multiblock.universal.distinct");
buttonText.appendText(" ");
ITextComponent button = AdvancedTextWidget.withButton(isDistinct() ?
new TextComponentTranslation("gregtech.multiblock.universal.distinct.yes").setStyle(new Style().setColor(TextFormatting.GREEN)) :
new TextComponentTranslation("gregtech.multiblock.universal.distinct.no").setStyle(new Style().setColor(TextFormatting.RED)), "distinct");
AdvancedTextWidget.withHoverTextTranslate(button, "gregtech.multiblock.universal.distinct.info");
buttonText.appendSibling(button);
textList.add(buttonText);
}

if (!recipeMapWorkable.isWorkingEnabled()) {
textList.add(new TextComponentTranslation("gregtech.multiblock.work_paused"));

Expand All @@ -153,19 +170,22 @@ protected void addDisplayText(List<ITextComponent> textList) {
}
}

@Override
protected void handleDisplayClick(String componentData, Widget.ClickData clickData) {
super.handleDisplayClick(componentData, clickData);
toggleDistinct();
}

@Override
protected boolean checkStructureComponents(List<IMultiblockPart> parts, Map<MultiblockAbility<Object>, List<Object>> abilities) {
boolean canForm = super.checkStructureComponents(parts, abilities);
if (!canForm)
return false;

//basically check minimal requirements for inputs count
//noinspection SuspiciousMethodCalls
int itemInputsCount = abilities.getOrDefault(MultiblockAbility.IMPORT_ITEMS, Collections.emptyList())
.stream().map(it -> (IItemHandler) it).mapToInt(IItemHandler::getSlots).sum();
//noinspection SuspiciousMethodCalls
int fluidInputsCount = abilities.getOrDefault(MultiblockAbility.IMPORT_FLUIDS, Collections.emptyList()).size();
//noinspection SuspiciousMethodCalls
return itemInputsCount >= recipeMap.getMinInputs() &&
fluidInputsCount >= recipeMap.getMinFluidInputs() &&
abilities.containsKey(MultiblockAbility.INPUT_ENERGY);
Expand All @@ -176,4 +196,48 @@ public void renderMetaTileEntity(CCRenderState renderState, Matrix4 translation,
super.renderMetaTileEntity(renderState, translation, pipeline);
this.getFrontOverlay().render(renderState, translation, pipeline, getFrontFacing(), recipeMapWorkable.isActive());
}

@Override
public NBTTagCompound writeToNBT(NBTTagCompound data) {
super.writeToNBT(data);
data.setBoolean("isDistinct", isDistinct);
return data;
}

@Override
public void readFromNBT(NBTTagCompound data) {
super.readFromNBT(data);
isDistinct = data.getBoolean("isDistinct");
}

@Override
public void writeInitialSyncData(PacketBuffer buf) {
super.writeInitialSyncData(buf);
buf.writeBoolean(isDistinct);
}

@Override
public void receiveInitialSyncData(PacketBuffer buf) {
super.receiveInitialSyncData(buf);
isDistinct = buf.readBoolean();
}

public boolean canBeDistinct() {
return false;
}

public boolean isDistinct() {
return isDistinct;
}

protected void toggleDistinct() {
isDistinct = !isDistinct;
recipeMapWorkable.onDistinctChanged();
//mark buses as changed on distinct toggle
if (isDistinct) {
this.notifiedItemInputList.addAll(this.getAbilities(MultiblockAbility.IMPORT_ITEMS));
} else {
this.notifiedItemInputList.add(this.inputInventory);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ protected void trySearchNewRecipe() {
this.invalidInputsForRecipes = (currentRecipe == null);

// proceed if we have a usable recipe.
if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe))
if (currentRecipe != null && setupAndConsumeRecipeInputs(currentRecipe, importInventory))
setupRecipe(currentRecipe);
// Inputs have been inspected.
metaTileEntity.getNotifiedItemInputList().clear();
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -147,6 +147,11 @@ protected OrientedOverlayRenderer getFrontOverlay() {
return Textures.BLAST_FURNACE_OVERLAY;
}

@Override
public boolean canBeDistinct() {
return true;
}

@Override
public boolean hasMufflerMechanics() {
return true;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -231,12 +231,12 @@ protected Recipe findRecipe(long maxVoltage, IItemHandlerModifiable inputs, IMul
}

@Override
protected boolean setupAndConsumeRecipeInputs(Recipe recipe) {
protected boolean setupAndConsumeRecipeInputs(Recipe recipe, IItemHandlerModifiable importInventory) {
long heatDiff = recipe.getProperty(FusionEUToStartProperty.getInstance(), 0L) - heat;
if (heatDiff <= 0) {
return super.setupAndConsumeRecipeInputs(recipe);
return super.setupAndConsumeRecipeInputs(recipe, importInventory);
}
if (energyContainer.getEnergyStored() < heatDiff || !super.setupAndConsumeRecipeInputs(recipe)) {
if (energyContainer.getEnergyStored() < heatDiff || !super.setupAndConsumeRecipeInputs(recipe, importInventory)) {
return false;
}
energyContainer.removeEnergy(heatDiff);
Expand Down
Loading