Files
StructureLib/src/main/java/com/gtnewhorizon/structurelib/alignment/IAlignmentLimits.java
T
Abdiel Kavash 3b3bc4b9ad Add preset IAlignmentLimits constants. (#43)
Many multiblocks can only be placed or rotated in certain orientations.
This has been always done ad-hoc, with individual checks for each
controller. This is inconsistent and sometimes it results in bugs where
certain orientations are not allowed for no discernible reason; for
example
https://github.com/GTNewHorizons/GT-New-Horizons-Modpack/issues/14206.

This PR introduces a short list of named constants to
`IAlignmentLimits`, which describe common multiplboc orientations. These
are currently:

* `UNLIMITED`: Any orientation is allowed. Most multiblocks.
* `UPRIGHT`: The multiblock must be in an upright position, with the top
side facing up. Example: Distillation Tower, Ore Washing Plant.
* `NOT_UPSIDE_DOWN`: The multiblock can be in any orientation, except
for the top side facing directly downwards. This is currently only the
Large Heat Exchanger and Integrated Ore Factory.
* `FACING_UP`: The front of the controller must face up. Example:
Flotation Cell, XL Turbines.

Currently, these constants describe the allowed facings of *all* GT5U
multiblocks.

A multiblock can then simply return one of these constants from
`getInitialAlignmentLimits()` to describe its valid orientations,
instead of implementing the logic itself. This concentrates the code to
one place, improves readability, and fixes any bugs connected with these
checks being done wrong.

Of course, even though the list is currently complete, if any future
multiblock wants to implement its own strange restrictions on
orientation, it can still implement its own check instead of returning
one of the constants.

A follow-up PR to GT5U implements this for all multiblocks which
restrict valid orientations:
https://github.com/GTNewHorizons/GT5-Unofficial/pull/4595
2025-08-01 10:54:59 +07:00

168 lines
5.8 KiB
Java

package com.gtnewhorizon.structurelib.alignment;
import static com.gtnewhorizon.structurelib.alignment.IAlignment.STATES_COUNT;
import java.util.Arrays;
import java.util.Optional;
import java.util.function.Function;
import net.minecraftforge.common.util.ForgeDirection;
import com.gtnewhorizon.structurelib.alignment.enumerable.ExtendedFacing;
import com.gtnewhorizon.structurelib.alignment.enumerable.Flip;
import com.gtnewhorizon.structurelib.alignment.enumerable.Rotation;
public interface IAlignmentLimits {
/**
* Can be placed in any orientation.
*/
IAlignmentLimits UNLIMITED = (direction, rotation, flip) -> true;
/**
* Must be standing upright; i.e., top side of the block facing up.
*/
IAlignmentLimits UPRIGHT = (direction, rotation, flip) -> direction.offsetY == 0
&& (flip.isVerticallyFliped() ? rotation.isUpsideDown() : rotation.isNotRotated());
/**
* Can be in any orientation except upside down; i.e., top side of the block facing down.
*/
IAlignmentLimits NOT_UPSIDE_DOWN = (direction, rotation, flip) -> direction.offsetY != 0
|| (flip.isVerticallyFliped() ? !rotation.isNotRotated() : !rotation.isUpsideDown());
/**
* Front side of the block must be facing up.
*/
IAlignmentLimits FACING_UP = (direction, rotation, flip) -> direction == ForgeDirection.UP;
boolean isNewExtendedFacingValid(ForgeDirection direction, Rotation rotation, Flip flip);
default boolean isNewExtendedFacingValid(ExtendedFacing alignment) {
return isNewExtendedFacingValid(alignment.getDirection(), alignment.getRotation(), alignment.getFlip());
}
static IAlignmentLimits allowOnly(ExtendedFacing... allowedFacings) {
Builder builder = Builder.denyAll();
for (ExtendedFacing allowedFacing : allowedFacings) {
builder.allow(allowedFacing);
}
return builder.build();
}
static IAlignmentLimits denyOnly(ExtendedFacing... allowedFacings) {
Builder builder = Builder.allowAll();
for (ExtendedFacing allowedFacing : allowedFacings) {
builder.deny(allowedFacing);
}
return builder.build();
}
class Builder {
protected final boolean[] validStates = new boolean[STATES_COUNT];
private Builder() {}
public static Builder allowAll() {
Builder b = new Builder();
Arrays.fill(b.validStates, true);
return b;
}
public static Builder denyAll() {
Builder b = new Builder();
Arrays.fill(b.validStates, true);
return b;
}
public Builder deny(ForgeDirection fd) {
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
.forEach(v -> validStates[v] = false);
return this;
}
public Builder allow(ForgeDirection fd) {
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
.forEach(v -> validStates[v] = true);
return this;
}
public Builder deny(ExtendedFacing o) {
validStates[o.getIndex()] = false;
return this;
}
public Builder allow(ExtendedFacing o) {
validStates[o.getIndex()] = true;
return this;
}
public Builder deny(Rotation fd) {
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
.forEach(v -> validStates[v] = false);
return this;
}
public Builder allow(Rotation fd) {
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
.forEach(v -> validStates[v] = true);
return this;
}
public Builder deny(Flip fd) {
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
.forEach(v -> validStates[v] = false);
return this;
}
public Builder allow(Flip fd) {
ExtendedFacing.getAllWith(fd).stream().mapToInt(ExtendedFacing::getIndex)
.forEach(v -> validStates[v] = true);
return this;
}
public Builder filter(Function<ExtendedFacing, Optional<Boolean>> predicate) {
for (ExtendedFacing value : ExtendedFacing.VALUES) {
predicate.apply(value).ifPresent(bool -> validStates[value.getIndex()] = bool);
}
return this;
}
public Builder ensureDuplicates() {
for (ExtendedFacing value : ExtendedFacing.VALUES) {
if (validStates[value.getIndex()]) {
validStates[value.getDuplicate().getIndex()] = true;
}
}
return this;
}
/**
* Prefers rotation to flip, so both flip will get translated to opposite rotation and no flip
*
* @param flip the preferred flip to be used Horizontal or vertical
* @return this
*/
public Builder ensureNoDuplicates(Flip flip) {
if (flip == Flip.BOTH || flip == Flip.NONE) {
throw new IllegalArgumentException("Preffered Flip must be Horizontal or Vertical");
}
flip = flip.getOpposite();
for (ExtendedFacing value : ExtendedFacing.VALUES) {
if (validStates[value.getIndex()]) {
if (value.getFlip() == Flip.BOTH || value.getFlip() == flip) {
validStates[value.getIndex()] = false;
validStates[value.getDuplicate().getIndex()] = true;
}
}
}
return this;
}
public IAlignmentLimits build() {
return new AlignmentLimits(validStates);
}
}
}