diff options
Diffstat (limited to 'src/main/java/gregtech/api/multitileentity/multiblock')
11 files changed, 2304 insertions, 0 deletions
diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/base/ComplexParallelController.java b/src/main/java/gregtech/api/multitileentity/multiblock/base/ComplexParallelController.java new file mode 100644 index 0000000000..cdcb77d6e5 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/base/ComplexParallelController.java @@ -0,0 +1,111 @@ +package gregtech.api.multitileentity.multiblock.base; + +import java.util.ArrayList; +import java.util.List; + +import javax.annotation.Nonnull; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.EnumChatFormatting; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; + +import gregtech.api.logic.ComplexParallelProcessingLogic; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.GT_Waila; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +public abstract class ComplexParallelController<C extends ComplexParallelController<C, P>, P extends ComplexParallelProcessingLogic<P>> + extends Controller<C, P> { + + protected int maxComplexParallels = 0; + protected int currentComplexParallels = 0; + + public ComplexParallelController() { + isSimpleMachine = false; + } + + protected void setMaxComplexParallels(int parallel, boolean stopMachine) { + if (parallel != maxComplexParallels && maxComplexParallels != 0 && stopMachine) { + stopMachine(false); + } + maxComplexParallels = parallel; + setProcessingUpdate(true); + } + + @Override + protected void stopMachine(boolean powerShutDown) { + super.stopMachine(powerShutDown); + } + + protected boolean hasPerfectOverclock() { + return false; + } + + @Override + protected void addProgressStringToScanner(EntityPlayer player, int logLevel, ArrayList<String> list) { + P processing = getProcessingLogic(); + for (int i = 0; i < maxComplexParallels; i++) { + list.add( + StatCollector.translateToLocal("GT5U.multiblock.Progress") + " " + + (i + 1) + + ": " + + EnumChatFormatting.GREEN + + GT_Utility.formatNumbers( + processing.getProgress(i) > 20 ? processing.getProgress(i) / 20 : processing.getProgress(i)) + + EnumChatFormatting.RESET + + (processing.getProgress(i) > 20 ? " s / " : " ticks / ") + + EnumChatFormatting.YELLOW + + GT_Utility.formatNumbers( + processing.getDuration(i) > 20 ? processing.getDuration(i) / 20 : processing.getDuration(i)) + + EnumChatFormatting.RESET + + (processing.getDuration(i) > 20 ? " s" : " ticks")); + } + } + + @Override + public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y, + int z) { + super.getWailaNBTData(player, tile, tag, world, x, y, z); + P processing = getProcessingLogic(); + tag.setInteger("maxComplexParallels", maxComplexParallels); + for (int i = 0; i < maxComplexParallels; i++) { + tag.setInteger("maxProgress" + i, processing.getDuration(i)); + tag.setInteger("progress" + i, processing.getProgress(i)); + } + } + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + super.getWailaBody(itemStack, currentTip, accessor, config); + final NBTTagCompound tag = accessor.getNBTData(); + maxComplexParallels = tag.getInteger("maxComplexParallels"); + for (int i = 0; i < maxComplexParallels; i++) { + long maxProgress = tag.getInteger("maxProgress" + i); + long progress = tag.getInteger("progress" + i); + currentTip.add( + "Process " + (i + 1) + + ": " + + GT_Waila + .getMachineProgressString(maxProgress > 0 && maxProgress >= progress, maxProgress, progress)); + } + } + + @Override + public void setProcessingLogicPower(@Nonnull P processingLogic) { + processingLogic.setAmperageOC(true); + processingLogic.setAvailableAmperage(getPowerLogic().getMaxAmperage() / maxComplexParallels); + processingLogic.setAvailableVoltage(getPowerLogic().getVoltage() / maxComplexParallels); + } + + @Override + public void updateProcessingLogic(@Nonnull P processingLogic) { + processingLogic.setMaxComplexParallel(maxComplexParallels); + } +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/base/Controller.java b/src/main/java/gregtech/api/multitileentity/multiblock/base/Controller.java new file mode 100644 index 0000000000..552cf6d94e --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/base/Controller.java @@ -0,0 +1,1082 @@ +package gregtech.api.multitileentity.multiblock.base; + +import static gregtech.api.util.GT_Utility.moveMultipleItemStacks; +import static gregtech.common.misc.WirelessNetworkManager.strongCheckOrAddUser; +import static mcp.mobius.waila.api.SpecialChars.*; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.inventory.IInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.StatCollector; +import net.minecraft.world.World; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.FluidStack; + +import org.jetbrains.annotations.NotNull; +import org.lwjgl.input.Keyboard; + +import com.gtnewhorizon.structurelib.StructureLibAPI; +import com.gtnewhorizon.structurelib.alignment.IAlignment; +import com.gtnewhorizon.structurelib.alignment.IAlignmentLimits; +import com.gtnewhorizon.structurelib.alignment.constructable.ISurvivalConstructable; +import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing; +import com.gtnewhorizon.structurelib.alignment.enumerable.Flip; +import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation; +import com.gtnewhorizon.structurelib.structure.IStructureDefinition; +import com.gtnewhorizon.structurelib.structure.ISurvivalBuildEnvironment; +import com.gtnewhorizon.structurelib.util.Vec3Impl; +import com.gtnewhorizons.modularui.api.screen.ModularWindow; + +import cpw.mods.fml.common.network.NetworkRegistry; +import gregtech.api.enums.GT_Values.NBT; +import gregtech.api.enums.InventoryType; +import gregtech.api.enums.VoidingMode; +import gregtech.api.interfaces.IDescribable; +import gregtech.api.interfaces.fluid.IFluidStore; +import gregtech.api.logic.ControllerFluidLogic; +import gregtech.api.logic.ControllerItemLogic; +import gregtech.api.logic.FluidInventoryLogic; +import gregtech.api.logic.ItemInventoryLogic; +import gregtech.api.logic.MuTEProcessingLogic; +import gregtech.api.logic.PowerLogic; +import gregtech.api.multitileentity.WeakTargetRef; +import gregtech.api.multitileentity.enums.MultiTileCasingPurpose; +import gregtech.api.multitileentity.interfaces.IMultiBlockController; +import gregtech.api.multitileentity.interfaces.IMultiBlockPart; +import gregtech.api.multitileentity.machine.MultiTileBasicMachine; +import gregtech.api.multitileentity.multiblock.casing.FunctionalCasing; +import gregtech.api.multitileentity.multiblock.casing.UpgradeCasing; +import gregtech.api.net.GT_Packet_MultiTileEntity; +import gregtech.api.objects.GT_ItemStack; +import gregtech.api.util.GT_Multiblock_Tooltip_Builder; +import gregtech.api.util.GT_Utility; +import gregtech.api.util.GT_Waila; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +/** + * Multi Tile Entities - or MuTEs - don't have dedicated hatches, but their casings can become hatches. + */ +public abstract class Controller<C extends Controller<C, P>, P extends MuTEProcessingLogic<P>> extends + MultiTileBasicMachine<P> implements IAlignment, IMultiBlockController, IDescribable, ISurvivalConstructable { + + public static final String ALL_INVENTORIES_NAME = "all"; + protected static final int AUTO_OUTPUT_FREQUENCY_TICK = 20; + + private static final Map<Integer, GT_Multiblock_Tooltip_Builder> tooltip = new ConcurrentHashMap<>(); + private final List<WeakTargetRef<UpgradeCasing>> upgradeCasings = new ArrayList<>(); + private final List<WeakTargetRef<FunctionalCasing>> functionalCasings = new ArrayList<>(); + protected BuildState buildState = new BuildState(); + + private boolean structureOkay = false, structureChanged = false; + private ExtendedFacing extendedFacing = ExtendedFacing.DEFAULT; + private IAlignmentLimits limits = getInitialAlignmentLimits(); + protected boolean separateInputs = getDefaultInputSeparationMode(); + protected VoidingMode voidingMode = getDefaultVoidingMode(); + protected boolean batchMode = getDefaultBatchMode(); + protected boolean recipeLock = getDefaultRecipeLockingMode(); + protected boolean shouldSort = false; + /** If this is set to true, the machine will get default WAILA behavior */ + protected boolean isSimpleMachine = true; + + protected boolean isCleanroom = false; + protected ControllerItemLogic controllerItemInput = new ControllerItemLogic(); + protected ControllerItemLogic controllerItemOutput = new ControllerItemLogic(); + protected ControllerFluidLogic controllerFluidInput = new ControllerFluidLogic(); + protected ControllerFluidLogic controllerFluidOutput = new ControllerFluidLogic(); + + // A list of sides + // Each side has a list of parts that have a cover that need to be ticked + protected List<List<WeakTargetRef<IMultiBlockPart>>> registeredCoveredParts = Arrays.asList( + new ArrayList<>(), + new ArrayList<>(), + new ArrayList<>(), + new ArrayList<>(), + new ArrayList<>(), + new ArrayList<>()); + + // A list for each purpose that a casing can register to, to be ticked + protected List<List<WeakTargetRef<IMultiBlockPart>>> registeredTickableParts = new ArrayList<>(); + + public Controller() { + for (int i = 0; i < MultiTileCasingPurpose.values().length; i++) { + registeredTickableParts.add(new ArrayList<>()); + } + } + + /** Registry ID of the required casing */ + public abstract short getCasingRegistryID(); + + /** Meta ID of the required casing */ + public abstract int getCasingMeta(); + + /** + * Create the tooltip for this multi block controller. + */ + protected abstract GT_Multiblock_Tooltip_Builder createTooltip(); + + /** + * @return The starting offset for the structure builder + */ + public abstract Vec3Impl getStartingStructureOffset(); + + /** + * Due to limitation of Java type system, you might need to do an unchecked cast. HOWEVER, the returned + * IStructureDefinition is expected to be evaluated against current instance only, and should not be used against + * other instances, even for those of the same class. + */ + @Override + public abstract IStructureDefinition<C> getStructureDefinition(); + + /** + * Checks the Machine. + * <p> + * NOTE: If using `buildState` be sure to `startBuilding()` and either `endBuilding()` or `failBuilding()` + */ + public boolean checkMachine() { + calculateTier(); + updatePowerLogic(); + return tier > 0; + } + + protected void calculateTier() { + if (functionalCasings.size() == 0) { + return; + } + double sum = 0; + for (WeakTargetRef<FunctionalCasing> casingRef : functionalCasings) { + final FunctionalCasing casing = casingRef.get(); + if (casing == null) continue; + sum += casing.getPartTier() * casing.getPartModifier(); + } + tier = (int) Math.min(Math.floor(sum / functionalCasings.size()), 14); + } + + @Override + public void writeMultiTileNBT(NBTTagCompound nbt) { + super.writeMultiTileNBT(nbt); + + nbt.setBoolean(NBT.STRUCTURE_OK, structureOkay); + nbt.setByte( + NBT.ROTATION, + (byte) extendedFacing.getRotation() + .getIndex()); + nbt.setByte( + NBT.FLIP, + (byte) extendedFacing.getFlip() + .getIndex()); + + nbt.setString(NBT.VOIDING_MODE, voidingMode.name); + nbt.setBoolean(NBT.SEPARATE_INPUTS, separateInputs); + nbt.setBoolean(NBT.RECIPE_LOCK, recipeLock); + nbt.setBoolean(NBT.BATCH_MODE, batchMode); + } + + @Override + protected void saveItemLogic(NBTTagCompound nbt) { + NBTTagCompound itemInputNBT = controllerItemInput.saveToNBT(); + nbt.setTag(NBT.INV_INPUT_LIST, itemInputNBT); + NBTTagCompound itemOutputNBT = controllerItemOutput.saveToNBT(); + nbt.setTag(NBT.INV_OUTPUT_LIST, itemOutputNBT); + } + + @Override + protected void saveFluidLogic(NBTTagCompound nbt) { + NBTTagCompound fluidInputNBT = controllerFluidInput.saveToNBT(); + nbt.setTag(NBT.TANK_IN, fluidInputNBT); + NBTTagCompound fluidOutputNBT = controllerFluidOutput.saveToNBT(); + nbt.setTag(NBT.TANK_OUT, fluidOutputNBT); + } + + @Override + public void readMultiTileNBT(NBTTagCompound nbt) { + super.readMultiTileNBT(nbt); + + // Multiblock inventories are a collection of inventories. The first inventory is the default internal + // inventory, and the others are added by inventory extending blocks. + + structureOkay = nbt.getBoolean(NBT.STRUCTURE_OK); + extendedFacing = ExtendedFacing + .of(getFrontFacing(), Rotation.byIndex(nbt.getByte(NBT.ROTATION)), Flip.byIndex(nbt.getByte(NBT.FLIP))); + + voidingMode = VoidingMode.fromName(nbt.getString(NBT.VOIDING_MODE)); + separateInputs = nbt.getBoolean(NBT.SEPARATE_INPUTS); + recipeLock = nbt.getBoolean(NBT.RECIPE_LOCK); + batchMode = nbt.getBoolean(NBT.BATCH_MODE); + } + + @Override + protected void loadItemLogic(NBTTagCompound nbt) { + if (!nbt.hasKey(NBT.INV_INPUT_LIST) && !nbt.hasKey(NBT.INV_OUTPUT_LIST)) { + controllerItemInput.addInventory(new ItemInventoryLogic(16)); + controllerItemOutput.addInventory(new ItemInventoryLogic(16)); + return; + } + controllerItemInput.loadFromNBT(nbt.getCompoundTag(NBT.INV_INPUT_LIST)); + controllerItemOutput.loadFromNBT(nbt.getCompoundTag(NBT.INV_OUTPUT_LIST)); + } + + @Override + protected void loadFluidLogic(NBTTagCompound nbt) { + if (!nbt.hasKey(NBT.TANK_IN) && !nbt.hasKey(NBT.TANK_OUT)) { + controllerFluidInput.addInventory(new FluidInventoryLogic(16, 32000)); + controllerFluidOutput.addInventory(new FluidInventoryLogic(16, 32000)); + return; + } + controllerFluidInput.loadFromNBT(nbt.getCompoundTag(NBT.TANK_IN)); + controllerFluidOutput.loadFromNBT(nbt.getCompoundTag(NBT.TANK_OUT)); + } + + @Override + public void addToolTips(List<String> aList, ItemStack aStack, boolean aF3_H) { + aList.addAll(Arrays.asList(getDescription())); + } + + @Override + public String[] getDescription() { + if (Keyboard.isKeyDown(Keyboard.KEY_LSHIFT)) { + return getTooltip().getStructureInformation(); + } + + return getTooltip().getInformation(); + } + + @Override + protected void addDebugInfo(EntityPlayer aPlayer, int aLogLevel, ArrayList<String> tList) { + super.addDebugInfo(aPlayer, aLogLevel, tList); + tList.add("Structure ok: " + checkStructure(false)); + } + + protected int getToolTipID() { + return getMultiTileEntityRegistryID() << 16 + getMultiTileEntityID(); + } + + protected GT_Multiblock_Tooltip_Builder getTooltip() { + GT_Multiblock_Tooltip_Builder builder = tooltip.get(getToolTipID()); + if (builder == null) { + builder = createTooltip(); + tooltip.put(getToolTipID(), builder); + } + return builder; + } + + @Override + public boolean checkStructure(boolean aForceReset) { + if (!isServerSide()) return structureOkay; + + // Only trigger an update if forced (from onPostTick, generally), or if the structure has changed + if ((structureChanged || aForceReset)) { + clearSpecialLists(); + structureOkay = checkMachine(); + } + structureChanged = false; + return structureOkay; + } + + @Override + public void onStructureChange() { + structureChanged = true; + } + + public final boolean checkPiece(String piece, Vec3Impl offset) { + return checkPiece(piece, offset.get0(), offset.get1(), offset.get2()); + } + + /** + * Explanation of the world coordinate these offset means: + * <p> + * Imagine you stand in front of the controller, with controller facing towards you not rotated or flipped. + * <p> + * The horizontalOffset would be the number of blocks on the left side of the controller, not counting controller + * itself. The verticalOffset would be the number of blocks on the top side of the controller, not counting + * controller itself. The depthOffset would be the number of blocks between you and controller, not counting + * controller itself. + * <p> + * All these offsets can be negative. + */ + protected final boolean checkPiece(String piece, int horizontalOffset, int verticalOffset, int depthOffset) { + return getCastedStructureDefinition().check( + this, + piece, + getWorld(), + getExtendedFacing(), + getXCoord(), + getYCoord(), + getZCoord(), + horizontalOffset, + verticalOffset, + depthOffset, + !structureOkay); + } + + public final boolean buildPiece(String piece, ItemStack trigger, boolean hintsOnly, Vec3Impl offset) { + return buildPiece(piece, trigger, hintsOnly, offset.get0(), offset.get1(), offset.get2()); + } + + protected final boolean buildPiece(String piece, ItemStack trigger, boolean hintOnly, int horizontalOffset, + int verticalOffset, int depthOffset) { + return getCastedStructureDefinition().buildOrHints( + this, + trigger, + piece, + getWorld(), + getExtendedFacing(), + getXCoord(), + getYCoord(), + getZCoord(), + horizontalOffset, + verticalOffset, + depthOffset, + hintOnly); + } + + protected final int survivalBuildPiece(String piece, ItemStack trigger, Vec3Impl offset, int elementBudget, + ISurvivalBuildEnvironment env, boolean check) { + return survivalBuildPiece( + piece, + trigger, + offset.get0(), + offset.get1(), + offset.get2(), + elementBudget, + env, + check); + } + + protected final Integer survivalBuildPiece(String piece, ItemStack trigger, int horizontalOffset, + int verticalOffset, int depthOffset, int elementBudget, ISurvivalBuildEnvironment env, boolean check) { + return getCastedStructureDefinition().survivalBuild( + this, + trigger, + piece, + getWorld(), + getExtendedFacing(), + getXCoord(), + getYCoord(), + getZCoord(), + horizontalOffset, + verticalOffset, + depthOffset, + elementBudget, + env, + check); + } + + @SuppressWarnings("unchecked") + private IStructureDefinition<Controller<C, P>> getCastedStructureDefinition() { + return (IStructureDefinition<Controller<C, P>>) getStructureDefinition(); + } + + @Override + public ExtendedFacing getExtendedFacing() { + return extendedFacing; + } + + @Override + public void setExtendedFacing(ExtendedFacing newExtendedFacing) { + if (extendedFacing == newExtendedFacing) { + return; + } + + onStructureChange(); + if (structureOkay) stopMachine(false); + extendedFacing = newExtendedFacing; + structureOkay = false; + if (isServerSide()) { + StructureLibAPI.sendAlignment( + this, + new NetworkRegistry.TargetPoint( + getWorld().provider.dimensionId, + getXCoord(), + getYCoord(), + getZCoord(), + 512)); + } else { + issueTextureUpdate(); + } + + } + + @Override + public boolean onWrenchRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, float aX, + float aY, float aZ, ItemStack aTool) { + if (wrenchSide != getFrontFacing()) + return super.onWrenchRightClick(aPlayer, tCurrentItem, wrenchSide, aX, aY, aZ); + if (aPlayer.isSneaking()) { + // we won't be allowing horizontal flips, as it can be perfectly emulated by rotating twice and flipping + // horizontally allowing an extra round of flip make it hard to draw meaningful flip markers in + // GT_Proxy#drawGrid + toolSetFlip(getFlip().isHorizontallyFlipped() ? Flip.NONE : Flip.HORIZONTAL); + } else { + toolSetRotation(null); + } + return true; + } + + @Override + public void registerCoveredPartOnSide(final ForgeDirection side, IMultiBlockPart part) { + if (side == ForgeDirection.UNKNOWN) return; + + final List<WeakTargetRef<IMultiBlockPart>> registeredCovers = registeredCoveredParts.get(side.ordinal()); + // TODO: Make sure that we're not already registered on this side + registeredCovers.add(new WeakTargetRef<>(part, true)); + } + + @Override + public void unregisterCoveredPartOnSide(final ForgeDirection side, IMultiBlockPart aPart) { + if (side == ForgeDirection.UNKNOWN) return; + + final List<WeakTargetRef<IMultiBlockPart>> coveredParts = registeredCoveredParts.get(side.ordinal()); + final Iterator<WeakTargetRef<IMultiBlockPart>> it = coveredParts.listIterator(); + while (it.hasNext()) { + final IMultiBlockPart part = (it.next()).get(); + if (part == null || part == aPart) it.remove(); + } + } + + @Override + public void registerCaseWithPurpose(MultiTileCasingPurpose purpose, IMultiBlockPart part) { + final List<WeakTargetRef<IMultiBlockPart>> tickableParts = registeredTickableParts.get(purpose.ordinal()); + final Iterator<WeakTargetRef<IMultiBlockPart>> it = tickableParts.listIterator(); + while (it.hasNext()) { + final IMultiBlockPart next = (it.next()).get(); + if (next == null) { + it.remove(); + } else if (next == part) { + return; + } + } + tickableParts.add(new WeakTargetRef<>(part, true)); + } + + @Override + public void unregisterCaseWithPurpose(MultiTileCasingPurpose purpose, IMultiBlockPart part) { + final List<WeakTargetRef<IMultiBlockPart>> tickableParts = registeredTickableParts.get(purpose.ordinal()); + final Iterator<WeakTargetRef<IMultiBlockPart>> it = tickableParts.listIterator(); + while (it.hasNext()) { + final IMultiBlockPart next = (it.next()).get(); + if (next == null || next == part) it.remove(); + } + } + + @Override + public void onFirstTick(boolean isServerSide) { + super.onFirstTick(isServerSide); + if (isServerSide) { + checkStructure(true); + } else { + StructureLibAPI.queryAlignment(this); + } + } + + private boolean tickCovers() { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + // TODO: Tick controller covers, if any + final List<WeakTargetRef<IMultiBlockPart>> coveredParts = this.registeredCoveredParts.get(side.ordinal()); + final Iterator<WeakTargetRef<IMultiBlockPart>> it = coveredParts.listIterator(); + while (it.hasNext()) { + final IMultiBlockPart part = (it.next()).get(); + if (part == null) { + it.remove(); + continue; + } + if (!part.tickCoverAtSide(side, mTickTimer)) it.remove(); + } + } + + return true; + } + + @Override + public void onTick(long tick, boolean isServerSide) { + tickCovers(); + } + + @Override + public void onPostTick(long tick, boolean isServerSide) { + if (!isServerSide) { // client side + doActivitySound(getActivitySoundLoop()); + return; + } + + // server side + if (tick % 600 == 5) { + // Recheck the structure every 30 seconds or so + if (!checkStructure(false)) checkStructure(true); + } + if (checkStructure(false)) { + runMachine(tick); + pushItemOutputs(tick); + pushFluidOutputs(tick); + + } else { + stopMachine(false); + } + + } + + protected void pushItemOutputs(long tick) { + if (tick % AUTO_OUTPUT_FREQUENCY_TICK != 0) return; + final List<WeakTargetRef<IMultiBlockPart>> registeredItemOutputs = registeredTickableParts + .get(MultiTileCasingPurpose.ItemOutput.ordinal()); + final Iterator<WeakTargetRef<IMultiBlockPart>> itemOutputIterator = registeredItemOutputs.listIterator(); + while (itemOutputIterator.hasNext()) { + final IMultiBlockPart part = (itemOutputIterator.next()).get(); + if (part == null) { + itemOutputIterator.remove(); + continue; + } + if (!part.shouldTick(mTickTimer)) { + itemOutputIterator.remove(); + continue; + } + + final IInventory facingInventory = part.getIInventoryAtSide(part.getFrontFacing()); + if (facingInventory == null) { + continue; + } + + moveMultipleItemStacks( + part, + facingInventory, + part.getFrontFacing(), + part.getBackFacing(), + null, + false, + (byte) 64, + (byte) 1, + (byte) 64, + (byte) 1, + part.getSizeInventory()); + for (int i = 0; i < part.getSizeInventory(); i++) { + final ItemStack stack = part.getStackInSlot(i); + if (stack != null && stack.stackSize <= 0) { + part.setInventorySlotContents(i, null); + } + } + + } + } + + protected void pushFluidOutputs(long tick) { + if (tick % AUTO_OUTPUT_FREQUENCY_TICK != 0) return; + final List<WeakTargetRef<IMultiBlockPart>> registeredFluidOutputs = registeredTickableParts + .get(MultiTileCasingPurpose.FluidOutput.ordinal()); + final Iterator<WeakTargetRef<IMultiBlockPart>> fluidOutputIterator = registeredFluidOutputs.listIterator(); + while (fluidOutputIterator.hasNext()) { + final IMultiBlockPart part = (fluidOutputIterator.next()).get(); + if (part == null) { + fluidOutputIterator.remove(); + continue; + } + if (!part.shouldTick(mTickTimer)) { + fluidOutputIterator.remove(); + } + } + } + + @Override + public void setCleanroom(boolean cleanroom) { + isCleanroom = cleanroom; + } + + protected void clearSpecialLists() { + upgradeCasings.clear(); + functionalCasings.clear(); + } + + @Override + public final boolean isFacingValid(ForgeDirection facing) { + return canSetToDirectionAny(facing); + } + + @Override + public void onFacingChange() { + toolSetDirection(getFrontFacing()); + onStructureChange(); + } + + @Override + public boolean allowCoverOnSide(ForgeDirection side, GT_ItemStack aCoverID) { + return side != facing; + } + + @Override + public String[] getStructureDescription(ItemStack stackSize) { + return getTooltip().getStructureHint(); + } + + @Override + public IAlignmentLimits getAlignmentLimits() { + return limits; + } + + protected void setAlignmentLimits(IAlignmentLimits mLimits) { + this.limits = mLimits; + } + + public boolean isSeparateInputs() { + return separateInputs; + } + + public void setSeparateInputs(boolean aSeparateInputs) { + separateInputs = aSeparateInputs; + } + + protected IAlignmentLimits getInitialAlignmentLimits() { + return (d, r, f) -> !f.isVerticallyFliped(); + } + + public static class BuildState { + + /** + * Utility class to keep track of the build state of a multiblock + */ + boolean building = false; + + Vec3Impl currentOffset; + + public void startBuilding(Vec3Impl structureOffset) { + if (building) throw new IllegalStateException("Already building!"); + building = true; + setCurrentOffset(structureOffset); + } + + public Vec3Impl setCurrentOffset(Vec3Impl structureOffset) { + verifyBuilding(); + return (currentOffset = structureOffset); + } + + private void verifyBuilding() { + if (!building) throw new IllegalStateException("Not building!"); + } + + public boolean failBuilding() { + building = false; + currentOffset = null; + return false; + } + + public Vec3Impl stopBuilding() { + final Vec3Impl toReturn = getCurrentOffset(); + building = false; + currentOffset = null; + + return toReturn; + } + + public Vec3Impl getCurrentOffset() { + verifyBuilding(); + return currentOffset; + } + + public Vec3Impl addOffset(Vec3Impl offset) { + verifyBuilding(); + return setCurrentOffset(currentOffset.add(offset)); + } + } + + public void registerSpecialCasings(MultiBlockPart part) { + if (part instanceof UpgradeCasing upgradeCasing) { + upgradeCasings.add(new WeakTargetRef<>(upgradeCasing, true)); + } + if (part instanceof FunctionalCasing functionalCasing) { + functionalCasings.add(new WeakTargetRef<>(functionalCasing, true)); + } + } + + // #region Fluid - MultiBlock related Fluid Tank behaviour. + + @Override + @Nullable + public FluidInventoryLogic getFluidLogic(@Nonnull ForgeDirection side, @Nonnull InventoryType type) { + if (side == facing) return null; + return switch (type) { + case Input -> controllerFluidInput.getAllInventoryLogics(); + case Output -> controllerFluidOutput.getAllInventoryLogics(); + default -> null; + }; + } + + @Override + @Nonnull + public @NotNull FluidInventoryLogic getFluidLogic(@Nonnull InventoryType type, @Nullable UUID id) { + return switch (type) { + case Input -> controllerFluidInput.getInventoryLogic(id); + case Output -> controllerFluidOutput.getInventoryLogic(id); + default -> throw new IllegalStateException("Unexpected value: " + type); + }; + } + + @Override + @Nonnull + public UUID registerFluidInventory(int tanks, long capacity, int tier, @Nonnull InventoryType type, + boolean isUpgradeInventory) { + return switch (type) { + case Input -> controllerFluidInput + .addInventory(new FluidInventoryLogic(tanks, capacity, tier, isUpgradeInventory)); + case Output -> controllerFluidOutput + .addInventory(new FluidInventoryLogic(tanks, capacity, tier, isUpgradeInventory)); + case Both -> { + UUID id = controllerFluidInput + .addInventory(new FluidInventoryLogic(tanks, capacity, tier, isUpgradeInventory)); + controllerFluidOutput + .addInventory(id, new FluidInventoryLogic(tanks, capacity, tier, isUpgradeInventory)); + yield id; + } + }; + } + + @Override + @Nonnull + public FluidInventoryLogic unregisterFluidInventory(@Nonnull UUID id, @Nonnull InventoryType type) { + return switch (type) { + case Input -> controllerFluidInput.removeInventory(id); + case Output -> controllerFluidOutput.removeInventory(id); + case Both -> { + FluidInventoryLogic input = controllerFluidInput.removeInventory(id); + FluidInventoryLogic output = controllerFluidOutput.removeInventory(id); + yield new FluidInventoryLogic( + Stream.of(input, output) + .map(FluidInventoryLogic::getInventory) + .collect(Collectors.toList())); + } + }; + } + + @Override + public void changeFluidInventoryDisplayName(@Nullable UUID id, @Nullable String displayName, + @Nonnull InventoryType type) { + switch (type) { + case Input: + controllerFluidInput.setInventoryDisplayName(id, displayName); + break; + case Output: + controllerFluidOutput.setInventoryDisplayName(id, displayName); + break; + case Both: + controllerFluidInput.setInventoryDisplayName(id, displayName); + controllerFluidOutput.setInventoryDisplayName(id, displayName); + break; + } + } + + // #endregion Fluid + + // #region Item - MultiBlock related Item behaviour. + + @Override + @Nullable + public ItemInventoryLogic getItemLogic(@Nonnull ForgeDirection side, @Nonnull InventoryType type) { + if (side == facing) return null; + return switch (type) { + case Input -> controllerItemInput.getAllInventoryLogics(); + case Output -> controllerItemOutput.getAllInventoryLogics(); + default -> null; + }; + } + + @Override + @Nonnull + public ItemInventoryLogic getItemLogic(@Nonnull InventoryType type, @Nullable UUID id) { + return switch (type) { + case Input -> controllerItemInput.getInventoryLogic(id); + case Output -> controllerItemOutput.getInventoryLogic(id); + default -> throw new IllegalStateException("Unexpected value: " + type); + }; + } + + @Override + @Nonnull + public UUID registerItemInventory(int slots, int tier, @Nonnull InventoryType type, boolean isUpgradeInventory) { + return switch (type) { + case Input -> controllerItemInput.addInventory(new ItemInventoryLogic(slots, tier, isUpgradeInventory)); + case Output -> controllerItemOutput.addInventory(new ItemInventoryLogic(slots, tier, isUpgradeInventory)); + case Both -> { + UUID id = controllerItemInput.addInventory(new ItemInventoryLogic(slots, tier, isUpgradeInventory)); + controllerItemOutput.addInventory(id, new ItemInventoryLogic(slots, tier, isUpgradeInventory)); + yield id; + } + }; + } + + @Override + public ItemInventoryLogic unregisterItemInventory(@Nonnull UUID id, @Nonnull InventoryType type) { + return switch (type) { + case Input -> controllerItemInput.removeInventory(id); + case Output -> controllerItemOutput.removeInventory(id); + case Both -> { + ItemInventoryLogic input = controllerItemInput.removeInventory(id); + ItemInventoryLogic output = controllerItemOutput.removeInventory(id); + yield new ItemInventoryLogic( + Arrays.asList(input, output) + .stream() + .map(inv -> inv.getInventory()) + .collect(Collectors.toList())); + } + }; + } + + @Override + public void changeItemInventoryDisplayName(@Nullable UUID id, @Nullable String displayName, + @Nonnull InventoryType type) { + switch (type) { + case Input: + controllerItemInput.setInventoryDisplayName(id, displayName); + break; + case Output: + controllerItemOutput.setInventoryDisplayName(id, displayName); + break; + case Both: + controllerItemInput.setInventoryDisplayName(id, displayName); + controllerItemOutput.setInventoryDisplayName(id, displayName); + break; + } + } + + // #endregion Item + + // #region Energy + + @Nonnull + @Override + public PowerLogic getPowerLogic() { + return getPowerLogic(ForgeDirection.UNKNOWN); + } + + // #endregion Energy + + @Override + protected void updateSlots() { + controllerItemInput.getAllInventoryLogics() + .update(shouldSort); + controllerItemOutput.getAllInventoryLogics() + .update(shouldSort); + controllerFluidInput.getAllInventoryLogics() + .update(); + controllerFluidOutput.getAllInventoryLogics() + .update(); + } + + /* + * GUI Work - Multiblock GUI related methods + */ + @Override + public boolean useModularUI() { + return true; + } + + @Override + public boolean hasGui(ForgeDirection side) { + return true; + } + + @Override + protected void addTitleTextStyle(ModularWindow.Builder builder, String title) { + // leave empty + } + + @Override + public boolean supportsVoidProtection() { + return true; + } + + @Override + public VoidingMode getVoidingMode() { + return voidingMode; + } + + @Override + public void setVoidingMode(VoidingMode mode) { + this.voidingMode = mode; + } + + @Override + public boolean canDumpItemToME() { + return false; + } + + @Override + public boolean canDumpFluidToME() { + return false; + } + + @Override + public boolean supportsInputSeparation() { + return true; + } + + @Override + public boolean isInputSeparated() { + return separateInputs; + } + + @Override + public void setInputSeparation(Boolean enabled) { + this.separateInputs = enabled; + } + + @Override + public boolean supportsBatchMode() { + return true; + } + + @Override + public boolean isBatchModeEnabled() { + return batchMode; + } + + @Override + public void setBatchMode(Boolean mode) { + this.batchMode = mode; + } + + @Override + public boolean supportsSingleRecipeLocking() { + return false; + } + + @Override + public boolean isRecipeLockingEnabled() { + return recipeLock; + } + + @Override + public void setRecipeLocking(Boolean enabled) { + this.recipeLock = enabled; + } + + @Override + public void getWailaNBTData(EntityPlayerMP player, TileEntity tile, NBTTagCompound tag, World world, int x, int y, + int z) { + super.getWailaNBTData(player, tile, tag, world, x, y, z); + P processing = getProcessingLogic(); + tag.setInteger("progress", processing.getProgress()); + tag.setInteger("maxProgress", processing.getDuration()); + tag.setBoolean("structureOkay", structureOkay); + tag.setBoolean("isActive", isActive()); + if (isActive()) { + tag.setLong("energyUsage", getProcessingLogic().getCalculatedEut()); + tag.setLong("energyTier", tier); + } + } + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + super.getWailaBody(itemStack, currentTip, accessor, config); + final NBTTagCompound tag = accessor.getNBTData(); + if (!tag.getBoolean("structureOkay")) { + currentTip.add(RED + "** INCOMPLETE STRUCTURE **" + RESET); + } else { + currentTip.add((GREEN + "Running Fine") + RESET); + } + if (isSimpleMachine) { + boolean isActive = tag.getBoolean("isActive"); + currentTip.add( + GT_Waila.getMachineProgressString(isActive, tag.getInteger("maxProgress"), tag.getInteger("progress"))); + } + boolean isActive = tag.getBoolean("isActive"); + if (isActive) { + long energyTier = tag.getLong("energyTier"); + long actualEnergyUsage = tag.getLong("energyUsage"); + if (actualEnergyUsage > 0) { + currentTip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.energy.use_with_amperage", + GT_Utility.formatNumbers(actualEnergyUsage), + GT_Utility.getAmperageForTier(actualEnergyUsage, (byte) energyTier), + GT_Utility.getColoredTierNameFromTier((byte) energyTier))); + } else if (actualEnergyUsage < 0) { + currentTip.add( + StatCollector.translateToLocalFormatted( + "GT5U.waila.energy.produce_with_amperage", + GT_Utility.formatNumbers(-actualEnergyUsage), + GT_Utility.getAmperageForTier(-actualEnergyUsage, (byte) energyTier), + GT_Utility.getColoredTierNameFromTier((byte) energyTier))); + } + } + } + + @Override + public GT_Packet_MultiTileEntity getClientDataPacket() { + final GT_Packet_MultiTileEntity packet = super.getClientDataPacket(); + + return packet; + + } + + @Override + public void enableWorking() { + super.enableWorking(); + if (!structureOkay) { + checkStructure(true); + } + } + + @Override + public List<ItemStack> getItemOutputSlots(ItemStack[] toOutput) { + return new ArrayList<>(0); + } + + @Override + public List<? extends IFluidStore> getFluidOutputSlots(FluidStack[] toOutput) { + return new ArrayList<>(0); + } + + @Override + @Nonnull + public Set<Entry<UUID, FluidInventoryLogic>> getAllFluidInventoryLogics(@Nonnull InventoryType type) { + return switch (type) { + case Input -> controllerFluidInput.getAllInventoryLogicsAsEntrySet(); + case Output -> controllerFluidOutput.getAllInventoryLogicsAsEntrySet(); + default -> super.getAllFluidInventoryLogics(type); + }; + } + + @Override + @Nonnull + public Set<Entry<UUID, ItemInventoryLogic>> getAllItemInventoryLogics(@Nonnull InventoryType type) { + return switch (type) { + case Input -> controllerItemInput.getAllInventoryLogicsAsEntrySet(); + case Output -> controllerItemOutput.getAllInventoryLogicsAsEntrySet(); + default -> super.getAllItemInventoryLogics(type); + }; + } + + @Override + public void setWirelessSupport(boolean canUse) { + if (canUse) { + strongCheckOrAddUser(getOwnerUuid()); + } + power.setCanUseWireless(canUse, getOwnerUuid()); + } + + @Override + public void setLaserSupport(boolean canUse) { + power.setCanUseLaser(canUse); + } + + @Override + public void setMaxAmperage(long amperage) { + power.setMaxAmperage(amperage); + } +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/base/MultiBlockPart.java b/src/main/java/gregtech/api/multitileentity/multiblock/base/MultiBlockPart.java new file mode 100644 index 0000000000..5a16ed4b38 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/base/MultiBlockPart.java @@ -0,0 +1,693 @@ +package gregtech.api.multitileentity.multiblock.base; + +import static com.google.common.math.LongMath.log2; +import static gregtech.api.enums.GT_Values.B; +import static gregtech.api.enums.Textures.BlockIcons.FLUID_IN_SIGN; +import static gregtech.api.enums.Textures.BlockIcons.FLUID_OUT_SIGN; +import static gregtech.api.enums.Textures.BlockIcons.ITEM_IN_SIGN; +import static gregtech.api.enums.Textures.BlockIcons.ITEM_OUT_SIGN; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ENERGY_IN_MULTI; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_ENERGY_OUT_MULTI; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_IN; +import static gregtech.api.enums.Textures.BlockIcons.OVERLAY_PIPE_OUT; + +import java.math.RoundingMode; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.HashSet; +import java.util.List; +import java.util.Set; +import java.util.UUID; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChunkCoordinates; +import net.minecraft.util.StatCollector; +import net.minecraftforge.common.util.ForgeDirection; +import net.minecraftforge.fluids.Fluid; + +import com.gtnewhorizons.modularui.api.screen.ModularWindow.Builder; +import com.gtnewhorizons.modularui.api.screen.UIBuildContext; +import com.gtnewhorizons.modularui.common.widget.DrawableWidget; + +import gregtech.api.enums.GT_Values.NBT; +import gregtech.api.enums.InventoryType; +import gregtech.api.fluid.FluidTankGT; +import gregtech.api.gui.GUIHost; +import gregtech.api.gui.GUIProvider; +import gregtech.api.interfaces.ITexture; +import gregtech.api.logic.FluidInventoryLogic; +import gregtech.api.logic.ItemInventoryLogic; +import gregtech.api.logic.NullPowerLogic; +import gregtech.api.logic.PowerLogic; +import gregtech.api.logic.interfaces.PowerLogicHost; +import gregtech.api.multitileentity.MultiTileEntityRegistry; +import gregtech.api.multitileentity.WeakTargetRef; +import gregtech.api.multitileentity.base.NonTickableMultiTileEntity; +import gregtech.api.multitileentity.enums.MultiTileCasingPurpose; +import gregtech.api.multitileentity.interfaces.IMultiBlockController; +import gregtech.api.multitileentity.interfaces.IMultiBlockPart; +import gregtech.api.render.TextureFactory; +import gregtech.api.util.GT_Utility; +import gregtech.common.covers.CoverInfo; +import gregtech.common.gui.PartGUIProvider; +import mcp.mobius.waila.api.IWailaConfigHandler; +import mcp.mobius.waila.api.IWailaDataAccessor; + +public abstract class MultiBlockPart extends NonTickableMultiTileEntity + implements IMultiBlockPart, PowerLogicHost, GUIHost { + + public static final int NOTHING = 0, ENERGY_IN = B[0], ENERGY_OUT = B[1], FLUID_IN = B[2], FLUID_OUT = B[3], + ITEM_IN = B[4], ITEM_OUT = B[5]; + + protected final List<Integer> BASIC_MODES = new ArrayList<>( + Arrays.asList(NOTHING, ENERGY_IN, ENERGY_OUT, FLUID_IN, FLUID_OUT, ITEM_IN, ITEM_OUT)); + + protected Set<MultiTileCasingPurpose> registeredPurposes = new HashSet<>(); + + protected final WeakTargetRef<IMultiBlockController> controller = new WeakTargetRef<>( + IMultiBlockController.class, + false); + + protected int allowedModes = NOTHING; // BITMASK - Modes allowed for this part + protected int mode = 0; // Mode selected for this part + + protected UUID lockedInventory; + protected int mLockedInventoryIndex = 0; + protected FluidTankGT configurationTank = new FluidTankGT(); + + @Nonnull + protected final GUIProvider<?> guiProvider = createGUIProvider(); + + /** + * What Part Tier is this part? All Basic Casings are Tier 1, and will allow: Energy, Item, Fluid input/output. Some + * of the more advanced modes can be set to require a higher tier part. + */ + public int getPartTier() { + return 1; + } + + @Override + public UUID getLockedInventory() { + return lockedInventory; + } + + public void setTarget(IMultiBlockController newController, int aAllowedModes) { + final IMultiBlockController currentController = getTarget(false); + if (currentController != null && currentController != newController) { + for (MultiTileCasingPurpose purpose : registeredPurposes) { + unregisterPurpose(purpose); + } + } + + allowedModes = aAllowedModes; + if (newController != currentController) { + registerCovers(newController); + registerPurposes(); + } + } + + protected void registerPurpose(MultiTileCasingPurpose purpose) { + IMultiBlockController target = getTarget(false); + if (target != null) { + target.registerCaseWithPurpose(purpose, this); + registeredPurposes.add(purpose); + } + } + + protected void unregisterPurpose(MultiTileCasingPurpose purpose) { + IMultiBlockController target = getTarget(false); + if (target != null) { + target.unregisterCaseWithPurpose(purpose, this); + } + registeredPurposes.remove(purpose); + } + + @Override + protected void addDebugInfo(EntityPlayer aPlayer, int aLogLevel, ArrayList<String> tList) { + final IMultiBlockController controller = getTarget(false); + if (controller != null) { + tList.add("Has controller"); + } else { + tList.add("No Controller"); + } + tList.add("Casing Mode: " + getModeName(mode)); + } + + @Override + public void getWailaBody(ItemStack itemStack, List<String> currentTip, IWailaDataAccessor accessor, + IWailaConfigHandler config) { + super.getWailaBody(itemStack, currentTip, accessor, config); + currentTip.add(String.format("Mode: %s", getModeName(mode))); + if (modeSelected(FLUID_OUT)) { + if (configurationTank != null && configurationTank.get() != null) { + currentTip.add( + String.format( + "Locked to: %s", + configurationTank.get() + .getLocalizedName())); + } else { + currentTip.add("Locked to: Nothing"); + } + } + } + + public IMultiBlockController getTarget(boolean aCheckValidity) { + final IMultiBlockController res = controller.get(); + if (res != null && aCheckValidity) { + return res.checkStructure(false) ? res : null; + } + return res; + } + + public void registerCovers(IMultiBlockController controller) { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (coverInfo.isValid() && coverInfo.getTickRate() > 0) { + controller.registerCoveredPartOnSide(side, this); + } + } + } + + protected void registerPurposes() { + for (MultiTileCasingPurpose purpose : registeredPurposes) { + registerPurpose(purpose); + } + } + + @Override + public void setCoverItemAtSide(ForgeDirection side, ItemStack aCover) { + super.setCoverItemAtSide(side, aCover); + // TODO: Filter on tickable covers + final IMultiBlockController tTarget = getTarget(true); + if (tTarget == null) { + return; + } + + final CoverInfo coverInfo = getCoverInfoAtSide(side); + if (coverInfo.isValid() && coverInfo.getTickRate() > 0) { + tTarget.registerCoveredPartOnSide(side, this); + } + + } + + public void unregisterCovers(IMultiBlockController controller) { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + if (getCoverInfoAtSide(side).isValid()) { + controller.unregisterCoveredPartOnSide(side, this); + } + } + } + + @Override + public boolean dropCover(ForgeDirection side, ForgeDirection droppedSide, boolean aForced) { + final boolean res = super.dropCover(side, droppedSide, aForced); + final IMultiBlockController tTarget = getTarget(true); + if (tTarget != null) { + tTarget.unregisterCoveredPartOnSide(side, this); + } + return res; + } + + @Override + public void readMultiTileNBT(NBTTagCompound aNBT) { + if (aNBT.hasKey(NBT.ALLOWED_MODES)) allowedModes = aNBT.getInteger(NBT.ALLOWED_MODES); + if (aNBT.hasKey(NBT.MODE)) setMode(aNBT.getByte(NBT.MODE)); + if (aNBT.hasKey(NBT.TARGET)) { + controller + .setPosition(aNBT.getInteger(NBT.TARGET_X), aNBT.getShort(NBT.TARGET_Y), aNBT.getInteger(NBT.TARGET_Z)); + controller.setWorld(worldObj); + } + if (aNBT.hasKey(NBT.LOCKED_INVENTORY)) { + lockedInventory = UUID.fromString(aNBT.getString(NBT.LOCKED_INVENTORY)); + } + if (aNBT.hasKey(NBT.LOCKED_INVENTORY_INDEX)) { + mLockedInventoryIndex = aNBT.getInteger(NBT.LOCKED_INVENTORY_INDEX); + } + if (aNBT.hasKey(NBT.LOCKED_FLUID)) { + configurationTank.readFromNBT(aNBT, NBT.LOCKED_FLUID); + } + if (modeSelected(ITEM_OUT)) { + registeredPurposes.add(MultiTileCasingPurpose.ItemOutput); + } + if (modeSelected(FLUID_OUT)) { + registeredPurposes.add(MultiTileCasingPurpose.FluidOutput); + } + } + + @Override + public void writeMultiTileNBT(NBTTagCompound nbt) { + if (allowedModes != NOTHING) nbt.setInteger(NBT.ALLOWED_MODES, allowedModes); + if (mode != 0) nbt.setInteger(NBT.MODE, mode); + + final ChunkCoordinates pos = controller.getPosition(); + if (pos.posY >= 0) { + // Valid position + nbt.setBoolean(NBT.TARGET, true); + nbt.setInteger(NBT.TARGET_X, pos.posX); + nbt.setShort(NBT.TARGET_Y, (short) pos.posY); + nbt.setInteger(NBT.TARGET_Z, pos.posZ); + } + + if (lockedInventory != null) { + nbt.setString(NBT.LOCKED_INVENTORY, lockedInventory.toString()); + } + if (mLockedInventoryIndex != 0) { + nbt.setInteger(NBT.LOCKED_INVENTORY_INDEX, mLockedInventoryIndex); + } + configurationTank.writeToNBT(nbt, NBT.LOCKED_FLUID); + } + + @Override + public void setLockedInventoryIndex(int index) { + mLockedInventoryIndex = index; + } + + @Override + public int getLockedInventoryIndex() { + return mLockedInventoryIndex; + } + + @Override + public ChunkCoordinates getTargetPos() { + return controller.getPosition(); + } + + @Override + public void setMode(int mode) { + if (this.mode == mode) return; + if (modeSelected(FLUID_OUT)) { + unregisterPurpose(MultiTileCasingPurpose.FluidOutput); + } + if (modeSelected(ITEM_OUT)) { + unregisterPurpose(MultiTileCasingPurpose.ItemOutput); + } + this.mode = mode; + if (modeSelected(FLUID_OUT)) { + registerPurpose(MultiTileCasingPurpose.FluidOutput); + } + if (modeSelected(ITEM_OUT)) { + registerPurpose(MultiTileCasingPurpose.ItemOutput); + } + } + + @Override + public int getMode() { + return mode; + } + + @Override + public int getAllowedModes() { + return allowedModes; + } + + @Override + public void setAllowedModes(int aAllowedModes) { + allowedModes = aAllowedModes; + } + + /** + * True if `mode` is one of the allowed modes + */ + public boolean hasMode(int mode) { + // This is not sent to the client + return (allowedModes & mode) != 0; + } + + /** + * Returns true if the part has any of the modes provided, and that mode is the currently selected mode + */ + public boolean modeSelected(int... modes) { + for (int mode : modes) { + if (hasMode(mode) && this.mode == getModeOrdinal(mode)) return true; + } + return false; + } + + @Override + public boolean onBlockBroken() { + final IMultiBlockController tTarget = getTarget(false); + if (tTarget != null) { + unregisterCovers(tTarget); + tTarget.onStructureChange(); + } + return false; + } + + @Override + public void onBlockAdded() { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + final TileEntity te = getTileEntityAtSide(side); + if (te instanceof MultiBlockPart part) { + final IMultiBlockController tController = part.getTarget(false); + if (tController != null) tController.onStructureChange(); + } else if (te instanceof IMultiBlockController controller) { + controller.onStructureChange(); + } + } + } + + @Override + public ITexture getTexture(ForgeDirection side) { + ITexture texture = super.getTexture(side); + if (mode != 0 && side == facing) { + if (mode == getModeOrdinal(ITEM_IN)) { + return TextureFactory.of( + texture, + TextureFactory.of(OVERLAY_PIPE_IN), + TextureFactory.of(ITEM_IN_SIGN), + getCoverTexture(side)); + } + if (mode == getModeOrdinal(ITEM_OUT)) { + return TextureFactory.of( + texture, + TextureFactory.of(OVERLAY_PIPE_OUT), + TextureFactory.of(ITEM_OUT_SIGN), + getCoverTexture(side)); + } + if (mode == getModeOrdinal(FLUID_IN)) { + return TextureFactory.of( + texture, + TextureFactory.of(OVERLAY_PIPE_IN), + TextureFactory.of(FLUID_IN_SIGN), + getCoverTexture(side)); + } + if (mode == getModeOrdinal(FLUID_OUT)) { + return TextureFactory.of( + texture, + TextureFactory.of(OVERLAY_PIPE_OUT), + TextureFactory.of(FLUID_OUT_SIGN), + getCoverTexture(side)); + } + if (mode == getModeOrdinal(ENERGY_IN)) { + return TextureFactory.of(texture, TextureFactory.of(OVERLAY_ENERGY_IN_MULTI), getCoverTexture(side)); + } + if (mode == getModeOrdinal(ENERGY_OUT)) { + return TextureFactory.of(texture, TextureFactory.of(OVERLAY_ENERGY_OUT_MULTI), getCoverTexture(side)); + } + } + + return TextureFactory.of(texture, getCoverTexture(side)); + } + + protected String getModeName(int aMode) { + if (aMode == NOTHING) return "Nothing"; + if (aMode == getModeOrdinal(ITEM_IN)) return "Item Input"; + if (aMode == getModeOrdinal(ITEM_OUT)) return "Item Output"; + if (aMode == getModeOrdinal(FLUID_IN)) return "Fluid Input"; + if (aMode == getModeOrdinal(FLUID_OUT)) return "Fluid Output"; + if (aMode == getModeOrdinal(ENERGY_IN)) return "Energy Input"; + if (aMode == getModeOrdinal(ENERGY_OUT)) return "Energy Output"; + return "Unknown"; + } + + protected byte getModeOrdinal(int aMode) { + // log2 returns the bit position of the only bit set, add 1 to account for 0 being NOTHING + // NOTE: Must be a power of 2 (single bit) + return (byte) (log2(aMode, RoundingMode.UNNECESSARY) + 1); + } + + protected byte getNextAllowedMode(List<Integer> allowedModes) { + if (this.allowedModes == NOTHING) return NOTHING; + + final int numModes = allowedModes.size(); + for (byte i = 1; i <= numModes; i++) { + final byte curMode = (byte) ((mode + i) % numModes); + if (curMode == NOTHING || hasMode(1 << (curMode - 1))) return curMode; + } + // Nothing valid found + return 0; + } + + @Override + public boolean onMalletRightClick(EntityPlayer aPlayer, ItemStack tCurrentItem, ForgeDirection wrenchSide, float aX, + float aY, float aZ) { + if (allowedModes == NOTHING) return true; + if (mode == NOTHING) { + facing = wrenchSide; + } + setMode(getNextAllowedMode(BASIC_MODES)); + if (aPlayer.isSneaking()) { + facing = wrenchSide; + } + GT_Utility.sendChatToPlayer(aPlayer, "Mode set to `" + getModeName(mode) + "' (" + mode + ")"); + sendClientData((EntityPlayerMP) aPlayer); + return true; + } + + @Override + public void setLightValue(byte aLightValue) {} + + @Override + public byte getComparatorValue(ForgeDirection side) { + return 0; + } + + @Override + public String getTileEntityName() { + return "gt.multitileentity.multiblock.part"; + } + + @Override + public boolean shouldTick(long tickTimer) { + return modeSelected(ITEM_OUT, FLUID_OUT); + } + + /** + * TODO: Make sure the energy/item/fluid hatch is facing that way! or has that mode enabled on that side Check + * SIDE_UNKNOWN for or coverbehavior + */ + + // #region Fluid - Depending on the part type - proxy it to the multiblock controller, if we have one + @Override + @Nullable + public FluidInventoryLogic getFluidLogic(@Nonnull ForgeDirection side, @Nonnull InventoryType type) { + if (side != facing && side != ForgeDirection.UNKNOWN) return null; + + if (!modeSelected(FLUID_IN, FLUID_OUT)) return null; + + IMultiBlockController controller = getTarget(false); + if (controller == null) return null; + return controller + .getFluidLogic(modeSelected(FLUID_IN) ? InventoryType.Input : InventoryType.Output, lockedInventory); + } + + // #endregion Fluid + + // #region Energy - Depending on the part type - proxy to the multiblock controller, if we have one + + @Override + @Nonnull + public PowerLogic getPowerLogic(@Nonnull ForgeDirection side) { + if (side != facing && side != ForgeDirection.UNKNOWN) { + return new NullPowerLogic(); + } + + if (!modeSelected(ENERGY_IN, ENERGY_OUT)) { + return new NullPowerLogic(); + } + + final IMultiBlockController controller = getTarget(true); + if (controller == null) { + return new NullPowerLogic(); + } + return controller.getPowerLogic(); + } + + @Override + public boolean isEnetInput() { + return modeSelected(ENERGY_IN); + } + + @Override + public boolean isEnetOutput() { + return modeSelected(ENERGY_OUT); + } + + // #endregion Energy + + // #region Item - Depending on the part type - proxy to the multiblock controller, if we have one + + @Override + @Nullable + public ItemInventoryLogic getItemLogic(@Nonnull ForgeDirection side, @Nonnull InventoryType unused) { + if (side != facing && side != ForgeDirection.UNKNOWN) return null; + + if (!modeSelected(ITEM_IN, ITEM_OUT)) return null; + + final IMultiBlockController controller = getTarget(false); + if (controller == null) return null; + + return controller + .getItemLogic(modeSelected(ITEM_IN) ? InventoryType.Input : InventoryType.Output, lockedInventory); + } + + @Override + @Nullable + public InventoryType getItemInventoryType() { + if (!modeSelected(ITEM_IN, ITEM_OUT)) return InventoryType.Both; + return modeSelected(ITEM_IN) ? InventoryType.Input : InventoryType.Output; + } + + // #endregion Item + + // === Modular UI === + @Override + public boolean useModularUI() { + return true; + } + + @Override + public String getLocalName() { + if (modeSelected(ITEM_IN)) return "Input Inventory"; + if (modeSelected(ITEM_OUT)) return "Output Inventory"; + if (modeSelected(FLUID_IN)) return "Fluid Input Hatch"; + if (modeSelected(FLUID_OUT)) return "Fluid Output Hatch"; + + return "Unknown"; + } + + @Override + public boolean hasGui(ForgeDirection side) { + if (modeSelected(ENERGY_IN, ENERGY_OUT) && facing == side) { + return false; + } + return getTarget(true) != null; + } + + protected boolean isWrongFluid(Fluid fluid) { + if (fluid == null) { + return true; + } + Fluid lockedFluid = getLockedFluid(); + if (lockedFluid != null) { + return !fluid.equals(lockedFluid); + } + return false; + } + + protected Fluid getLockedFluid() { + if (configurationTank.get() != null && configurationTank.get() + .getFluid() != null) { + return configurationTank.get() + .getFluid(); + } + return null; + } + + @Override + public void addUIWidgets(Builder builder, UIBuildContext buildContext) { + super.addUIWidgets(builder, buildContext); + IMultiBlockController controller = getTarget(false); + if (controller == null) { + return; + } + if ((modeSelected(ITEM_IN, ITEM_OUT))) { + builder.widget( + controller + .getItemLogic(modeSelected(ITEM_IN) ? InventoryType.Input : InventoryType.Output, lockedInventory) + .getGuiPart() + .setSize(18 * 4 + 4, 18 * 5) + .setPos(52, 7)); + } + + if ((modeSelected(FLUID_IN, FLUID_OUT))) { + builder.widget( + controller + .getFluidLogic(modeSelected(FLUID_IN) ? InventoryType.Input : InventoryType.Output, lockedInventory) + .getGuiPart() + .setSize(18 * 4 + 4, 18 * 5) + .setPos(52, 7)); + } + } + + protected boolean canOpenControllerGui() { + return true; + } + + @Override + protected int getGUIHeight() { + return super.getGUIHeight() + 20; + } + + @Override + public void addGregTechLogo(Builder builder) { + if (modeSelected(ITEM_IN, ITEM_OUT)) { + builder.widget( + new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo()) + .setSize(17, 17) + .setPos(152, 74)); + } else if (modeSelected(FLUID_IN, FLUID_OUT)) { + builder.widget( + new DrawableWidget().setDrawable(getGUITextureSet().getGregTechLogo()) + .setSize(17, 17) + .setPos(152, 82)); + } else { + super.addGregTechLogo(builder); + } + } + + @Override + public void addToolTips(List<String> list, ItemStack stack, boolean f3_h) { + list.add("A MultiTileEntity Casing"); + } + + public String getInventoryName() { + IMultiBlockController controller = getTarget(false); + if (controller == null) return ""; + if (modeSelected(ITEM_IN, ITEM_OUT)) { + InventoryType type = modeSelected(ITEM_IN) ? InventoryType.Input : InventoryType.Output; + ItemInventoryLogic itemLogic = controller.getItemLogic(type, lockedInventory); + return itemLogic.getDisplayName(); + } + if (modeSelected(FLUID_IN, FLUID_OUT)) { + InventoryType type = modeSelected(FLUID_IN) ? InventoryType.Input : InventoryType.Output; + FluidInventoryLogic fluidLogic = controller.getFluidLogic(type, lockedInventory); + return fluidLogic.getDisplayName(); + } + return ""; + } + + @Override + @Nonnull + public ForgeDirection getPowerOutputSide() { + if (!modeSelected(ENERGY_OUT)) return ForgeDirection.UNKNOWN; + return facing; + } + + @Nonnull + protected GUIProvider<?> createGUIProvider() { + return new PartGUIProvider<>(this); + } + + @Override + @Nonnull + public GUIProvider<?> getGUI(@Nonnull UIBuildContext uiContext) { + IMultiBlockController controller = getTarget(false); + if (controller == null) return guiProvider; + if (!modeSelected(NOTHING, ENERGY_IN, ENERGY_OUT)) return guiProvider; + if (!canOpenControllerGui()) return guiProvider; + if (uiContext.getPlayer() + .isSneaking()) return guiProvider; + GUIProvider<?> controllerGUI = controller.getGUI(uiContext); + return controllerGUI; + } + + @Override + public ItemStack getAsItem() { + return MultiTileEntityRegistry.getRegistry(getMultiTileEntityRegistryID()) + .getItem(getMultiTileEntityID()); + } + + @Override + public String getMachineName() { + return StatCollector.translateToLocal(getAsItem().getUnlocalizedName()); + } + +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/base/StackableController.java b/src/main/java/gregtech/api/multitileentity/multiblock/base/StackableController.java new file mode 100644 index 0000000000..51feb363dd --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/base/StackableController.java @@ -0,0 +1,128 @@ +package gregtech.api.multitileentity.multiblock.base; + +import net.minecraft.item.ItemStack; + +import com.gtnewhorizon.structurelib.util.Vec3Impl; + +import gregtech.api.logic.MuTEProcessingLogic; + +public abstract class StackableController<C extends StackableController<C, P>, P extends MuTEProcessingLogic<P>> + extends Controller<C, P> { + + protected static String STACKABLE_STOP = "STACKABLE_STOP"; + protected static String STACKABLE_MIDDLE = "STACKABLE_MIDDLE"; + protected static String STACKABLE_START = "STACKABLE_START"; + protected int stackCount = 0; + + /** + * construct implementation for stackable multi-blocks + */ + @Override + public void construct(ItemStack trigger, boolean hintsOnly) { + final int blueprintCount = (trigger.stackSize - 1) + getMinStacks(); + final int stackCount = Math.min(blueprintCount, getMaxStacks()); + + buildState.startBuilding(getStartingStructureOffset()); + buildPiece(getStackableStart(), trigger, hintsOnly, buildState.getCurrentOffset()); + buildState.addOffset(getStartingStackOffset()); + + for (int i = 0; i < stackCount; i++) { + buildPiece(getStackableMiddle(i), trigger, hintsOnly, buildState.getCurrentOffset()); + buildState.addOffset(getPerStackOffset()); + } + if (hasTop()) { + buildState.addOffset(getAfterLastStackOffset()); + buildPiece(getStackableStop(), trigger, hintsOnly, buildState.stopBuilding()); + } else { + buildState.stopBuilding(); + } + } + + /** + * Stackable + * + * @return The minimum number of stacks required for this multi-block to form + */ + public abstract int getMinStacks(); + + /** + * Stackable + * + * @return The maximum number of stacks allowed for this multi-block to form + */ + public abstract int getMaxStacks(); + + /** + * Stackable + * + * @return The starting offset for the first stack + */ + public abstract Vec3Impl getStartingStackOffset(); + + /** + * Stackable + * + * @return The per stack offset + */ + public abstract Vec3Impl getPerStackOffset(); + + /** + * Stackable + * + * @return Whether this structure has a Top/Cap. Defaults to true. + */ + public boolean hasTop() { + return true; + } + + /** + * Stackable + * + * @return Any offset needed after the last stack + */ + public Vec3Impl getAfterLastStackOffset() { + return new Vec3Impl(0, 0, 0); + } + + /** + * checkMachine implementation for stackable multi-blocks + */ + @Override + public boolean checkMachine() { + stackCount = 0; + + buildState.startBuilding(getStartingStructureOffset()); + if (!checkPiece(getStackableStart(), buildState.getCurrentOffset())) return buildState.failBuilding(); + + buildState.addOffset(getStartingStackOffset()); + + for (int i = 0; i < getMaxStacks(); i++) { + if (!checkPiece(getStackableMiddle(i), buildState.getCurrentOffset())) { + break; + } + + buildState.addOffset(getPerStackOffset()); + stackCount++; + + } + if (stackCount < getMinStacks()) return buildState.failBuilding(); + + buildState.addOffset(getAfterLastStackOffset()); + if (!checkPiece(getStackableStop(), buildState.stopBuilding())) { + return buildState.failBuilding(); + } + return super.checkMachine(); + } + + protected String getStackableStop() { + return STACKABLE_STOP; + } + + protected String getStackableMiddle(int stackIndex) { + return STACKABLE_MIDDLE; + } + + protected String getStackableStart() { + return STACKABLE_START; + } +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/base/StackableModularController.java b/src/main/java/gregtech/api/multitileentity/multiblock/base/StackableModularController.java new file mode 100644 index 0000000000..1dfd497151 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/base/StackableModularController.java @@ -0,0 +1,77 @@ +package gregtech.api.multitileentity.multiblock.base; + +import java.util.Arrays; +import java.util.HashMap; +import java.util.Map; + +import org.jetbrains.annotations.NotNull; + +import gregtech.api.logic.MuTEProcessingLogic; +import gregtech.api.multitileentity.interfaces.UpgradableModularMuTE; +import gregtech.api.util.GT_StructureUtilityMuTE.UpgradeCasings; + +public abstract class StackableModularController<C extends StackableModularController<C, P>, P extends MuTEProcessingLogic<P>> + extends StackableController<C, P> implements UpgradableModularMuTE { + + protected double durationMultiplier = 1; + protected double euTickMultiplier = 1; + + private Map<UpgradeCasings, int[]> mucMap; + + protected @NotNull Map<UpgradeCasings, int[]> getMucMap() { + if (mucMap == null) { + mucMap = createMucMap(); + } + return mucMap; + } + + protected static @NotNull Map<UpgradeCasings, int[]> createMucMap() { + Map<UpgradeCasings, int[]> mucCount = new HashMap<>(); + mucCount.put(UpgradeCasings.Heater, new int[] { 0, 0, 0, 0, 0 }); + mucCount.put(UpgradeCasings.Insulator, new int[] { 0, 0, 0, 0, 0 }); + return mucCount; + } + + @Override + public void increaseMucCount(UpgradeCasings casingType, int tier) { + Map<UpgradeCasings, int[]> mucCounters = getMucMap(); + int[] casingCount = mucCounters.get(casingType); + + switch (tier) { + case 0, 1, 2 -> casingCount[0] += 1; + case 3, 4, 5 -> casingCount[1] += 1; + case 6, 7, 8 -> casingCount[2] += 1; + case 9, 10, 11 -> casingCount[3] += 1; + default -> casingCount[4] += 1; + } + } + + @Override + public void resetMucCount() { + Map<UpgradeCasings, int[]> mucCounters = getMucMap(); + mucCounters.forEach((type, casingCount) -> { Arrays.fill(casingCount, 0); }); + } + + // Returns the cheapest MUC that is possible for the multi, which gets the minimum bonuses. + protected abstract UpgradeCasings getBaseMucType(); + + // Minimum parallel bonus per MUC. Higher tier MUCs multiply with this value for even more parallels. + protected abstract int getParallelFactor(); + + protected void calculateParallels() { + int parallelCount = 0; + int parallelFactor = getParallelFactor(); + int[] parallelCasingList = mucMap.get(getBaseMucType()); + + for (int i = 0; i < 5; i++) { + // (i * 3 + 1) -> Convert MUC tier into minimum GT tier, in groups of 3 (LV, EV, LuV, UHV, UMV) + // If higher than multi tier, upgrade casing has no effect + if (i * 3 + 1 <= tier) { + parallelCount += parallelCasingList[i] * (i + 1) * parallelFactor; + } + } + maxParallel = parallelCount == 0 ? 1 : parallelCount; + } + + protected abstract boolean calculateMucMultipliers(); +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/base/WallShareablePart.java b/src/main/java/gregtech/api/multitileentity/multiblock/base/WallShareablePart.java new file mode 100644 index 0000000000..238ce1eb2d --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/base/WallShareablePart.java @@ -0,0 +1,100 @@ +package gregtech.api.multitileentity.multiblock.base; + +import java.util.ArrayList; +import java.util.List; +import java.util.UUID; + +import net.minecraft.tileentity.TileEntity; +import net.minecraft.util.ChunkCoordinates; +import net.minecraftforge.common.util.ForgeDirection; + +import gregtech.api.multitileentity.WeakTargetRef; +import gregtech.api.multitileentity.interfaces.IMultiBlockController; + +public class WallShareablePart extends MultiBlockPart { + + protected List<WeakTargetRef<IMultiBlockController>> targets = new ArrayList<>(); + + @Override + public void setTarget(IMultiBlockController newController, int allowedModes) { + if (targets.size() >= 1) { + this.allowedModes = 0; + setMode((byte) 0); + controller.invalidate(); + } else { + this.allowedModes = allowedModes; + controller.setTarget(newController); + } + + if (newController == null) { + return; + } + + targets.add(new WeakTargetRef<IMultiBlockController>(IMultiBlockController.class, true)); + } + + @Override + public UUID getLockedInventory() { + if (targets.size() > 1) { + return null; + } + return super.getLockedInventory(); + } + + @Override + public IMultiBlockController getTarget(boolean aCheckValidity) { + if (targets.size() != 1) { + return null; + } + + controller.setTarget( + targets.get(0) + .get()); + return super.getTarget(aCheckValidity); + } + + @Override + public String getTileEntityName() { + return "gt.multiTileEntity.casing.wallSharable"; + } + + @Override + public boolean onBlockBroken() { + for (final WeakTargetRef<IMultiBlockController> tar : targets) { + IMultiBlockController target = getTarget(tar.getPosition(), false); + if (target == null) { + continue; + } + target.onStructureChange(); + } + return false; + } + + @Override + public void onBlockAdded() { + for (final ForgeDirection side : ForgeDirection.VALID_DIRECTIONS) { + final TileEntity te = getTileEntityAtSide(side); + if (te instanceof MultiBlockPart part) { + final IMultiBlockController tController = part.getTarget(false); + if (tController != null) tController.onStructureChange(); + } else if (te instanceof IMultiBlockController tController) { + tController.onStructureChange(); + } + } + } + + public IMultiBlockController getTarget(ChunkCoordinates coordinates, boolean aCheckValidity) { + IMultiBlockController target = null; + if (coordinates == null) return null; + if (worldObj.blockExists(coordinates.posX, coordinates.posY, coordinates.posZ)) { + final TileEntity te = worldObj.getTileEntity(coordinates.posX, coordinates.posY, coordinates.posZ); + if (te instanceof IMultiBlockController) { + target = (IMultiBlockController) te; + } + } + if (aCheckValidity) { + return target != null && target.checkStructure(false) ? target : null; + } + return target; + } +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/casing/BasicCasing.java b/src/main/java/gregtech/api/multitileentity/multiblock/casing/BasicCasing.java new file mode 100644 index 0000000000..84f1442a88 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/casing/BasicCasing.java @@ -0,0 +1,7 @@ +package gregtech.api.multitileentity.multiblock.casing; + +import gregtech.api.multitileentity.multiblock.base.MultiBlockPart; + +public class BasicCasing extends MultiBlockPart { + /* Nothing */ +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/casing/CasingBehaviorBase.java b/src/main/java/gregtech/api/multitileentity/multiblock/casing/CasingBehaviorBase.java new file mode 100644 index 0000000000..9f0d9bd2d1 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/casing/CasingBehaviorBase.java @@ -0,0 +1,10 @@ +package gregtech.api.multitileentity.multiblock.casing; + +/** + * Allows for functional casings that influence the multiblock structure's behavior Examples include: - Extra Byproducts + * - Extra Speed - More parallels - Upgraded internal item/energy/fluid storage - Faster output - Voiding/Anti-Voiding + * upgrade - Ender Upgrades - etc, etc. + * + */ +public class CasingBehaviorBase { +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/casing/FunctionalCasing.java b/src/main/java/gregtech/api/multitileentity/multiblock/casing/FunctionalCasing.java new file mode 100644 index 0000000000..bc3c857fd6 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/casing/FunctionalCasing.java @@ -0,0 +1,29 @@ +package gregtech.api.multitileentity.multiblock.casing; + +import net.minecraft.nbt.NBTTagCompound; + +import gregtech.api.enums.GT_Values; +import gregtech.api.multitileentity.multiblock.base.MultiBlockPart; + +public abstract class FunctionalCasing extends MultiBlockPart { + + private int tier = 0; + + @Override + public int getPartTier() { + return tier; + } + + public abstract float getPartModifier(); + + @Override + public void readMultiTileNBT(NBTTagCompound nbt) { + super.readMultiTileNBT(nbt); + tier = nbt.getInteger(GT_Values.NBT.TIER); + } + + @Override + public void writeMultiTileNBT(NBTTagCompound nbt) { + super.writeMultiTileNBT(nbt); + } +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/casing/Glasses.java b/src/main/java/gregtech/api/multitileentity/multiblock/casing/Glasses.java new file mode 100644 index 0000000000..edc1bd0e5b --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/casing/Glasses.java @@ -0,0 +1,32 @@ +package gregtech.api.multitileentity.multiblock.casing; + +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofBlockUnlocalizedName; +import static com.gtnewhorizon.structurelib.structure.StructureUtility.ofChain; +import static gregtech.api.enums.Mods.BartWorks; +import static gregtech.api.enums.Mods.Botania; +import static gregtech.api.enums.Mods.IndustrialCraft2; +import static gregtech.api.enums.Mods.Thaumcraft; + +import com.gtnewhorizon.structurelib.structure.IStructureElementChain; + +public class Glasses { + + /** support all Bart, Botania, Ic2, Thaumcraft glasses for multiblock structure **/ + public static <T> IStructureElementChain<T> chainAllGlasses() { + return ofChain( + // IndustrialCraft2 glass + ofBlockUnlocalizedName(IndustrialCraft2.ID, "blockAlloyGlass", 0, true), + + // Botania glass + ofBlockUnlocalizedName(Botania.ID, "manaGlass", 0, false), + ofBlockUnlocalizedName(Botania.ID, "elfGlass", 0, false), + + // BartWorks glass + ofBlockUnlocalizedName(BartWorks.ID, "BW_GlasBlocks", 0, true), + ofBlockUnlocalizedName(BartWorks.ID, "BW_GlasBlocks2", 0, true), + + // warded glass + ofBlockUnlocalizedName(Thaumcraft.ID, "blockCosmeticOpaque", 2, false)); + } + +} diff --git a/src/main/java/gregtech/api/multitileentity/multiblock/casing/UpgradeCasing.java b/src/main/java/gregtech/api/multitileentity/multiblock/casing/UpgradeCasing.java new file mode 100644 index 0000000000..566afcd770 --- /dev/null +++ b/src/main/java/gregtech/api/multitileentity/multiblock/casing/UpgradeCasing.java @@ -0,0 +1,35 @@ +package gregtech.api.multitileentity.multiblock.casing; + +import net.minecraft.nbt.NBTTagCompound; + +import gregtech.api.enums.GT_Values; +import gregtech.api.multitileentity.interfaces.IMultiBlockController; +import gregtech.api.multitileentity.multiblock.base.MultiBlockPart; + +public abstract class UpgradeCasing extends MultiBlockPart { + + protected int tier = 0; + + @Override + public int getPartTier() { + return tier; + } + + @Override + public void setTarget(IMultiBlockController newController, int aAllowedModes) { + super.setTarget(newController, aAllowedModes); + + if (getTarget(false) != null) { + customWork(getTarget(false)); + } + } + + @Override + public void readMultiTileNBT(NBTTagCompound aNBT) { + super.readMultiTileNBT(aNBT); + tier = aNBT.getInteger(GT_Values.NBT.TIER); + } + + protected abstract void customWork(IMultiBlockController aTarget); + +} |