Skip to content
Open
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 @@ -18,6 +18,7 @@
import gregtech.api.metatileentity.multiblock.FuelMultiblockController;
import gregtech.api.metatileentity.multiblock.ICleanroomProvider;
import gregtech.api.metatileentity.multiblock.ICleanroomReceiver;
import gregtech.api.metatileentity.multiblock.IMultiblockAbilityPart;
import gregtech.api.metatileentity.multiblock.IMultiblockPart;
import gregtech.api.metatileentity.multiblock.MultiblockAbility;
import gregtech.api.metatileentity.multiblock.MultiblockDisplayText;
Expand Down Expand Up @@ -50,6 +51,7 @@
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.resources.I18n;
import net.minecraft.creativetab.CreativeTabs;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
Expand All @@ -66,6 +68,7 @@
import net.minecraft.util.text.TextComponentTranslation;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.capabilities.Capability;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
Expand All @@ -75,6 +78,7 @@
import codechicken.lib.render.CCRenderState;
import codechicken.lib.render.pipeline.IVertexOperation;
import codechicken.lib.vec.Matrix4;
import it.unimi.dsi.fastutil.objects.ObjectOpenHashSet;
import org.jetbrains.annotations.NotNull;
import org.jetbrains.annotations.Nullable;

Expand All @@ -86,6 +90,7 @@
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Set;

public class MetaTileEntityCleanroom extends MultiblockWithDisplayBase
implements ICleanroomProvider, IWorkable, IDataInfoProvider {
Expand All @@ -111,6 +116,9 @@ public class MetaTileEntityCleanroom extends MultiblockWithDisplayBase
private final CleanroomLogic cleanroomLogic;
private final Collection<ICleanroomReceiver> cleanroomReceivers = new HashSet<>();

private Set<BlockPos> doors = Collections.emptySet();
private int openBlocks = 0;

public MetaTileEntityCleanroom(ResourceLocation metaTileEntityId) {
super(metaTileEntityId);
this.cleanroomLogic = new CleanroomLogic(this, GTValues.LV);
Expand Down Expand Up @@ -142,6 +150,7 @@ protected void formStructure(PatternMatchContext context) {
this.cleanroomLogic.setMaxProgress(Math.max(100,
((lDist + rDist + 1) * (bDist + fDist + 1) * hDist) - ((lDist + rDist + 1) * (bDist + fDist + 1))));
this.cleanroomLogic.setMinEnergyTier(cleanroomFilter.getMinTier());
this.doors = context.get("Doors");
}

@Override
Expand All @@ -156,6 +165,8 @@ public void invalidateStructure() {
}
});
cleanroomReceivers.clear();
this.doors = Collections.emptySet();
this.openBlocks = 0;
}

@Override
Expand All @@ -175,6 +186,149 @@ public void checkStructurePattern() {
reinitializeStructurePattern();
}
super.checkStructurePattern();
if (isStructureFormed()) {
checkDoors();
}
}

protected static class DoorCheckingContext {

private World world;
private BlockPos doorPos;
private IBlockState doorState;
private EnumFacing doorFacing;
private EnumFacing actualDoorFacing;
private boolean doorOpen;
private int openDoors;
private int checkX;
private int checkZ;
private boolean doorOnPositive;
private boolean doorOnNegative;

public void init(BlockPos pos, IBlockState state) {
this.doorPos = pos;
this.doorState = state.getActualState(this.world, this.doorPos);
this.doorFacing = this.doorState.getValue(BlockDoor.FACING);
this.doorOpen = this.doorState.getValue(BlockDoor.OPEN);
this.actualDoorFacing = getActualDoorFacing(this.doorFacing, this.doorState.getValue(BlockDoor.HINGE),
this.doorOpen);
this.checkX = this.doorOpen ? Math.abs(this.doorFacing.getXOffset()) :
1 - Math.abs(this.doorFacing.getXOffset()); // 1 or 0
this.checkZ = 1 - this.checkX; // inversion of x since facing can only face in x or z
this.doorOnPositive = false;
this.doorOnNegative = false;
}

public void setDoor(boolean positive) {
if (positive) this.doorOnPositive = true;
else this.doorOnNegative = true;
}

public boolean isDoor(boolean positive) {
return positive ? this.doorOnPositive : this.doorOnNegative;
}
}

public void checkDoors() {
DoorCheckingContext context = new DoorCheckingContext();
context.world = getWorld();
context.openDoors = 0;
for (BlockPos pos : this.doors) {
IBlockState state = getWorld().getBlockState(pos);
if (!(state.getBlock() instanceof BlockDoor)) {
invalidateStructure();
return;
}
context.init(pos, state);
determineOpenDoors(context);
}
if (this.openBlocks != context.openDoors && context.world instanceof WorldServer worldServer) {
List<EntityPlayerMP> players = worldServer.getMinecraftServer().getPlayerList().getPlayers();
if (!players.isEmpty()) {
// for debug
players.get(0).sendMessage(new TextComponentString("Open blocks: " + context.openDoors));
}
}
this.openBlocks = context.openDoors;
}

protected void determineOpenDoors(DoorCheckingContext context) {
int x = context.doorPos.getX();
int z = context.doorPos.getZ();
int y = context.doorPos.getY();
int cx = context.checkX;
int cz = context.checkZ;

BlockPos.MutableBlockPos pos = new BlockPos.MutableBlockPos();
// use negative facing on positive side since we are considering the neighboring block
if (!isBlockBlockingDoor(context, pos.setPos(x + cx, y, z + cz), false) ||
!isBlockBlockingDoor(context, pos.setPos(x - cx, y, z - cz), true)) {
context.openDoors++;
}
if ((!context.doorOnPositive &&
!isBlockBlockingDoor(context, pos.setPos(x + cx, y + 1, z + cz), false)) ||
(!context.doorOnNegative &&
!isBlockBlockingDoor(context, pos.setPos(x - cx, y + 1, z - cz), true))) {
context.openDoors++;
}
}

private static EnumFacing getActualDoorFacing(EnumFacing facing, BlockDoor.EnumHingePosition hinge, boolean open) {
if (!open) return facing;
return hinge == BlockDoor.EnumHingePosition.LEFT ? facing.rotateY() : facing.rotateYCCW();
}

protected boolean isBlockBlockingDoor(DoorCheckingContext context, BlockPos neighborPos, boolean positive) {
// we could make a generalized check with bounding box here but this would leave room for bypassing this check
// simply checking if the block is potentially part of the wall is enough
IBlockState state = context.world.getBlockState(neighborPos);
// casing and glass
if (state.getBlock() instanceof BlockCleanroomCasing cleanroomCasing) {
return cleanroomCasing.getState(state) == BlockCleanroomCasing.CasingType.PLASCRETE;
}
if (state.getBlock() instanceof BlockGlassCasing cleanroomCasing) {
return cleanroomCasing.getState(state) == BlockGlassCasing.CasingType.CLEANROOM_GLASS;
}
// multiblock abilities
MetaTileEntity mte = GTUtility.getMetaTileEntity(context.world, neighborPos);
if (mte instanceof IMultiblockAbilityPart<?>multiblockAbilityPart) {
List<MultiblockAbility<?>> abilities = multiblockAbilityPart.getAbilities();
if (abilities.isEmpty()) return false;
return abilities.contains(MultiblockAbility.MUFFLER_HATCH) ||
abilities.contains(MultiblockAbility.MAINTENANCE_HATCH) ||
abilities.contains(MultiblockAbility.PASSTHROUGH_HATCH) ||
abilities.contains(MultiblockAbility.INPUT_ENERGY);
} else if (mte != null) {
return false;
}
// double doors
if (state.getBlock() instanceof BlockDoor) {
if (context.isDoor(positive)) {
// the bottom already had doors, and we don't need to check again
return true;
}
if (!this.doors.contains(neighborPos)) {
// don't worry about doors which are not part of the structure
return false;
}
state = state.getActualState(context.world, neighborPos);
BlockDoor.EnumDoorHalf half = state.getValue(BlockDoor.HALF);
BlockDoor.EnumHingePosition hinge = state.getValue(BlockDoor.HINGE);
EnumFacing facing = state.getValue(BlockDoor.FACING);
boolean open = state.getValue(BlockDoor.OPEN);
EnumFacing actualFacing = getActualDoorFacing(facing, hinge, open);
if (half == BlockDoor.EnumDoorHalf.LOWER) {
context.setDoor(positive);
}
if (context.actualDoorFacing == actualFacing) {
// if door face the same direction and the other door is open it will count that by itself so we accept
return true;
}
// I can't really explain why, but this needed
return context.actualDoorFacing.rotateY() == actualFacing ||
context.actualDoorFacing.rotateYCCW() == actualFacing;
}
return false;
}

@Override
Expand Down Expand Up @@ -378,10 +532,10 @@ protected BlockPattern createStructurePattern() {
.where('S', selfPredicate())
.where('B', states(getCasingState()).or(basePredicate))
.where('X', wallPredicate.or(basePredicate)
.or(doorPredicate().setMaxGlobalLimited(8))
.or(improvedDoorPredicate().setMaxGlobalLimited(8))
.or(abilities(MultiblockAbility.PASSTHROUGH_HATCH).setMaxGlobalLimited(30)))
.where('K', wallPredicate) // the block beneath the controller must only be a casing for structure
// dimension checks
// dimension checks
.where('F', filterPredicate())
.where(' ', innerPredicate())
.build();
Expand Down Expand Up @@ -436,6 +590,23 @@ protected static TraceabilityPredicate doorPredicate() {
blockWorldState -> blockWorldState.getBlockState().getBlock() instanceof BlockDoor);
}

@NotNull
protected static TraceabilityPredicate improvedDoorPredicate() {
return new TraceabilityPredicate(blockWorldState -> {
IBlockState state = blockWorldState.getBlockState();
if (state.getBlock() instanceof BlockDoor) {
BlockDoor.EnumDoorHalf half = state.getValue(BlockDoor.HALF);
if (half == BlockDoor.EnumDoorHalf.LOWER) {
// we only need the door once
blockWorldState.getMatchContext().getOrCreate("Doors", () -> new ObjectOpenHashSet<BlockPos>())
.add(blockWorldState.getPos().toImmutable());
}
return true;
}
return false;
});
}

@NotNull
protected TraceabilityPredicate innerPredicate() {
return new TraceabilityPredicate(blockWorldState -> {
Expand Down