// Validation code
@Override
protected void isMachineWhole() throws MultiblockValidationException {
if(attachedRotorBearings.size() != 1) {
throw new MultiblockValidationException("Turbines require exactly 1 rotor bearing");
}
// Set up validation caches
foundCoils.clear();
super.isMachineWhole();
// Now do additional validation based on the coils/blades/rotors that were found
// Check that we have a rotor that goes all the way up the bearing
TileEntityTurbinePartBase rotorPart = attachedRotorBearings.iterator().next();
// Rotor bearing must calculate outwards dir, as this is normally only calculated in onMachineAssembled().
rotorPart.recalculateOutwardsDirection(getMinimumCoord(), getMaximumCoord());
// Find out which way the rotor runs. Obv, this is inwards from the bearing.
ForgeDirection rotorDir = rotorPart.getOutwardsDir().getOpposite();
CoordTriplet rotorCoord = rotorPart.getWorldLocation();
CoordTriplet minRotorCoord = getMinimumCoord();
CoordTriplet maxRotorCoord = getMaximumCoord();
// Constrain min/max rotor coords to where the rotor bearing is and the block opposite it
if(rotorDir.offsetX == 0) {
minRotorCoord.x = maxRotorCoord.x = rotorCoord.x;
}
if(rotorDir.offsetY == 0) {
minRotorCoord.y = maxRotorCoord.y = rotorCoord.y;
}
if(rotorDir.offsetZ == 0) {
minRotorCoord.z = maxRotorCoord.z = rotorCoord.z;
}
// Figure out where the rotor ends and which directions are normal to the rotor's 4 faces (this is where blades emit from)
CoordTriplet endRotorCoord = rotorCoord.equals(minRotorCoord) ? maxRotorCoord : minRotorCoord;
endRotorCoord.translate(rotorDir.getOpposite());
ForgeDirection[] bladeDirections;
if(rotorDir.offsetY != 0) {
bladeDirections = StaticUtils.CardinalDirections;
}
else if(rotorDir.offsetX != 0) {
bladeDirections = RotorXBladeDirections;
}
else {
bladeDirections = RotorZBladeDirections;
}
Set<CoordTriplet> rotorShafts = new HashSet<CoordTriplet>(attachedRotorShafts.size());
Set<CoordTriplet> rotorBlades = new HashSet<CoordTriplet>(attachedRotorBlades.size());
for(TileEntityTurbineRotorPart part : attachedRotorShafts) {
rotorShafts.add(part.getWorldLocation());
}
for(TileEntityTurbineRotorPart part : attachedRotorBlades) {
rotorBlades.add(part.getWorldLocation());
}
// Move along the length of the rotor, 1 block at a time
boolean encounteredCoils = false;
while(!rotorShafts.isEmpty() && !rotorCoord.equals(endRotorCoord)) {
rotorCoord.translate(rotorDir);
// Ensure we find a rotor block along the length of the entire rotor
if(!rotorShafts.remove(rotorCoord)) {
throw new MultiblockValidationException(String.format("%s - This block must contain a rotor. The rotor must begin at the bearing and run the entire length of the turbine", rotorCoord));
}
// Now move out in the 4 rotor normals, looking for blades and coils
CoordTriplet checkCoord = rotorCoord.copy();
boolean encounteredBlades = false;
for(ForgeDirection bladeDir : bladeDirections) {
checkCoord.copy(rotorCoord);
boolean foundABlade = false;
checkCoord.translate(bladeDir);
// If we find 1 blade, we can keep moving along the normal to find more blades
while(rotorBlades.remove(checkCoord)) {
// We found a coil already?! NOT ALLOWED.
if(encounteredCoils) {
throw new MultiblockValidationException(String.format("%s - Rotor blades must be placed closer to the rotor bearing than all other parts inside a turbine", checkCoord));
}
foundABlade = encounteredBlades = true;
checkCoord.translate(bladeDir);
}
// If this block wasn't a blade, check to see if it was a coil
if(!foundABlade) {
if(foundCoils.remove(checkCoord)) {
encounteredCoils = true;
// We cannot have blades and coils intermix. This prevents intermixing, depending on eval order.
if(encounteredBlades) {
throw new MultiblockValidationException(String.format("%s - Metal blocks must by placed further from the rotor bearing than all rotor blades", checkCoord));
}
// Check the two coil spots in the 'corners', which are permitted if they're connected to the main rotor coil somehow
CoordTriplet coilCheck = checkCoord.copy();
coilCheck.translate(bladeDir.getRotation(rotorDir));
foundCoils.remove(coilCheck);
coilCheck.copy(checkCoord);
coilCheck.translate(bladeDir.getRotation(rotorDir.getOpposite()));
foundCoils.remove(coilCheck);
}
// Else: It must have been air.
}
}
}
if(!rotorCoord.equals(endRotorCoord)) {
throw new MultiblockValidationException("The rotor shaft must extend the entire length of the turbine interior.");
}
// Ensure that we encountered all the rotor, blade and coil blocks. If not, there's loose stuff inside the turbine.
if(!rotorShafts.isEmpty()) {
throw new MultiblockValidationException(String.format("Found %d rotor blocks that are not attached to the main rotor. All rotor blocks must be in a column extending the entire length of the turbine, starting from the bearing.", rotorShafts.size()));
}
if(!rotorBlades.isEmpty()) {
throw new MultiblockValidationException(String.format("Found %d rotor blades that are not attached to the rotor. All rotor blades must extend continuously from the rotor's shaft.", rotorBlades.size()));
}
if(!foundCoils.isEmpty()) {
throw new MultiblockValidationException(String.format("Found %d metal blocks which were not in a ring around the rotor. All metal blocks must be in rings, or partial rings, around the rotor.", foundCoils.size()));
}
// A-OK!
}