Create/src/main/java/com/simibubi/create/content/contraptions/actors/roller/RollerBlockEntity.java
simibubi eea8bb2607 Filter code caused global warming
- Contents of a filter are no longer deserialised from item nbt each time a stack is tested
- FilteringBehaviour.getFilter() no longer creates a copy of the item
- MovementContext for contraption actors now have a shortcut to a cached filter from their corresponding BlockEntity
2023-11-02 00:08:34 +01:00

208 lines
6.3 KiB
Java

package com.simibubi.create.content.contraptions.actors.roller;
import java.util.List;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import com.simibubi.create.foundation.blockEntity.SmartBlockEntity;
import com.simibubi.create.foundation.blockEntity.behaviour.BlockEntityBehaviour;
import com.simibubi.create.foundation.blockEntity.behaviour.ValueBoxTransform;
import com.simibubi.create.foundation.blockEntity.behaviour.filtering.FilteringBehaviour;
import com.simibubi.create.foundation.blockEntity.behaviour.scrollValue.INamedIconOptions;
import com.simibubi.create.foundation.blockEntity.behaviour.scrollValue.ScrollOptionBehaviour;
import com.simibubi.create.foundation.gui.AllIcons;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.StairBlock;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
public class RollerBlockEntity extends SmartBlockEntity {
// For simulations such as Ponder
private float manuallyAnimatedSpeed;
public FilteringBehaviour filtering;
public ScrollOptionBehaviour<RollingMode> mode;
private boolean dontPropagate;
public RollerBlockEntity(BlockEntityType<?> type, BlockPos pos, BlockState state) {
super(type, pos, state);
dontPropagate = false;
}
@Override
public void addBehaviours(List<BlockEntityBehaviour> behaviours) {
behaviours.add(filtering = new FilteringBehaviour(this, new RollerValueBox(3)));
behaviours.add(mode = new ScrollOptionBehaviour<RollingMode>(RollingMode.class,
Lang.translateDirect("contraptions.roller_mode"), this, new RollerValueBox(-3)));
filtering.setLabel(Lang.translateDirect("contraptions.mechanical_roller.pave_material"));
filtering.withCallback(this::onFilterChanged);
filtering.withPredicate(this::isValidMaterial);
mode.withCallback(this::onModeChanged);
}
protected void onModeChanged(int mode) {
shareValuesToAdjacent();
}
protected void onFilterChanged(ItemStack newFilter) {
shareValuesToAdjacent();
}
protected boolean isValidMaterial(ItemStack newFilter) {
if (newFilter.isEmpty())
return true;
BlockState appliedState = RollerMovementBehaviour.getStateToPaveWith(newFilter);
if (appliedState.isAir())
return false;
if (appliedState.getBlock() instanceof EntityBlock)
return false;
if (appliedState.getBlock() instanceof StairBlock)
return false;
VoxelShape shape = appliedState.getShape(level, worldPosition);
if (shape.isEmpty() || !shape.bounds()
.equals(Shapes.block()
.bounds()))
return false;
VoxelShape collisionShape = appliedState.getCollisionShape(level, worldPosition);
if (collisionShape.isEmpty())
return false;
return true;
}
@Override
protected AABB createRenderBoundingBox() {
return new AABB(worldPosition).inflate(1);
}
public float getAnimatedSpeed() {
return manuallyAnimatedSpeed;
}
public void setAnimatedSpeed(float speed) {
manuallyAnimatedSpeed = speed;
}
public void searchForSharedValues() {
BlockState blockState = getBlockState();
Direction facing = blockState.getOptionalValue(RollerBlock.FACING)
.orElse(Direction.SOUTH);
for (int side : Iterate.positiveAndNegative) {
BlockPos pos = worldPosition.relative(facing.getClockWise(), side);
if (level.getBlockState(pos) != blockState)
continue;
if (!(level.getBlockEntity(pos) instanceof RollerBlockEntity otherRoller))
continue;
acceptSharedValues(otherRoller.mode.getValue(), otherRoller.filtering.getFilter());
shareValuesToAdjacent();
break;
}
}
protected void acceptSharedValues(int mode, ItemStack filter) {
dontPropagate = true;
this.filtering.setFilter(filter.copy());
this.mode.setValue(mode);
dontPropagate = false;
notifyUpdate();
}
public void shareValuesToAdjacent() {
if (dontPropagate || level.isClientSide())
return;
BlockState blockState = getBlockState();
Direction facing = blockState.getOptionalValue(RollerBlock.FACING)
.orElse(Direction.SOUTH);
for (int side : Iterate.positiveAndNegative) {
for (int i = 1; i < 100; i++) {
BlockPos pos = worldPosition.relative(facing.getClockWise(), side * i);
if (level.getBlockState(pos) != blockState)
break;
if (!(level.getBlockEntity(pos) instanceof RollerBlockEntity otherRoller))
break;
otherRoller.acceptSharedValues(mode.getValue(), filtering.getFilter());
}
}
}
static enum RollingMode implements INamedIconOptions {
TUNNEL_PAVE(AllIcons.I_ROLLER_PAVE),
STRAIGHT_FILL(AllIcons.I_ROLLER_FILL),
WIDE_FILL(AllIcons.I_ROLLER_WIDE_FILL),
;
private String translationKey;
private AllIcons icon;
private RollingMode(AllIcons icon) {
this.icon = icon;
translationKey = "contraptions.roller_mode." + Lang.asId(name());
}
@Override
public AllIcons getIcon() {
return icon;
}
@Override
public String getTranslationKey() {
return translationKey;
}
}
private final class RollerValueBox extends ValueBoxTransform {
private int hOffset;
public RollerValueBox(int hOffset) {
this.hOffset = hOffset;
}
@Override
public void rotate(BlockState state, PoseStack ms) {
Direction facing = state.getValue(RollerBlock.FACING);
float yRot = AngleHelper.horizontalAngle(facing) + 180;
TransformStack.cast(ms)
.rotateY(yRot)
.rotateX(90);
}
@Override
public boolean testHit(BlockState state, Vec3 localHit) {
Vec3 offset = getLocalOffset(state);
if (offset == null)
return false;
return localHit.distanceTo(offset) < scale / 3;
}
@Override
public Vec3 getLocalOffset(BlockState state) {
Direction facing = state.getValue(RollerBlock.FACING);
float stateAngle = AngleHelper.horizontalAngle(facing) + 180;
return VecHelper.rotateCentered(VecHelper.voxelSpace(8 + hOffset, 15.5f, 11), stateAngle, Axis.Y);
}
}
}