Entering glue zone

- Reimplemented super glue as area-entities instead of individual connections
This commit is contained in:
simibubi 2022-05-05 01:35:08 +02:00
parent 6ae6c4878f
commit e1c3ad1a9b
25 changed files with 455 additions and 868 deletions

View file

@ -6,7 +6,6 @@ import com.simibubi.create.content.contraptions.components.structureMovement.Con
import com.simibubi.create.content.contraptions.components.structureMovement.OrientedContraptionEntity; import com.simibubi.create.content.contraptions.components.structureMovement.OrientedContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.OrientedContraptionEntityRenderer; import com.simibubi.create.content.contraptions.components.structureMovement.OrientedContraptionEntityRenderer;
import com.simibubi.create.content.contraptions.components.structureMovement.gantry.GantryContraptionEntity; import com.simibubi.create.content.contraptions.components.structureMovement.gantry.GantryContraptionEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.GlueInstance;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity; import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueRenderer; import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueRenderer;
import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionEntityRenderer; import com.simibubi.create.content.contraptions.components.structureMovement.render.ContraptionEntityRenderer;
@ -34,25 +33,20 @@ import net.minecraft.world.entity.MobCategory;
public class AllEntityTypes { public class AllEntityTypes {
public static final EntityEntry<OrientedContraptionEntity> ORIENTED_CONTRAPTION = contraption("contraption", public static final EntityEntry<OrientedContraptionEntity> ORIENTED_CONTRAPTION = contraption("contraption",
OrientedContraptionEntity::new, () -> OrientedContraptionEntityRenderer::new, 5, 3, true) OrientedContraptionEntity::new, () -> OrientedContraptionEntityRenderer::new, 5, 3, true).register();
.register();
public static final EntityEntry<ControlledContraptionEntity> CONTROLLED_CONTRAPTION = public static final EntityEntry<ControlledContraptionEntity> CONTROLLED_CONTRAPTION =
contraption("stationary_contraption", ControlledContraptionEntity::new, () -> ContraptionEntityRenderer::new, contraption("stationary_contraption", ControlledContraptionEntity::new, () -> ContraptionEntityRenderer::new,
20, 40, false) 20, 40, false).register();
.register();
public static final EntityEntry<GantryContraptionEntity> GANTRY_CONTRAPTION = contraption("gantry_contraption", public static final EntityEntry<GantryContraptionEntity> GANTRY_CONTRAPTION = contraption("gantry_contraption",
GantryContraptionEntity::new, () -> ContraptionEntityRenderer::new, 10, 40, false) GantryContraptionEntity::new, () -> ContraptionEntityRenderer::new, 10, 40, false).register();
.register();
public static final EntityEntry<CarriageContraptionEntity> CARRIAGE_CONTRAPTION = public static final EntityEntry<CarriageContraptionEntity> CARRIAGE_CONTRAPTION =
contraption("carriage_contraption", CarriageContraptionEntity::new, contraption("carriage_contraption", CarriageContraptionEntity::new,
() -> CarriageContraptionEntityRenderer::new, 15, 3, true) () -> CarriageContraptionEntityRenderer::new, 15, 3, true).instance(() -> CarriageContraptionInstance::new)
.instance(() -> CarriageContraptionInstance::new) .register();
.register();
public static final EntityEntry<SuperGlueEntity> SUPER_GLUE = public static final EntityEntry<SuperGlueEntity> SUPER_GLUE =
register("super_glue", SuperGlueEntity::new, () -> SuperGlueRenderer::new, MobCategory.MISC, 10, register("super_glue", SuperGlueEntity::new, () -> SuperGlueRenderer::new, MobCategory.MISC, 10,
Integer.MAX_VALUE, false, true, SuperGlueEntity::build).instance(() -> GlueInstance::new, false) Integer.MAX_VALUE, false, true, SuperGlueEntity::build).register();
.register();
public static final EntityEntry<BlueprintEntity> CRAFTING_BLUEPRINT = public static final EntityEntry<BlueprintEntity> CRAFTING_BLUEPRINT =
register("crafting_blueprint", BlueprintEntity::new, () -> BlueprintRenderer::new, MobCategory.MISC, 10, register("crafting_blueprint", BlueprintEntity::new, () -> BlueprintRenderer::new, MobCategory.MISC, 10,

View file

@ -12,6 +12,7 @@ public enum AllSpecialTextures {
CUTOUT_CHECKERED("cutout_checkerboard.png"), CUTOUT_CHECKERED("cutout_checkerboard.png"),
HIGHLIGHT_CHECKERED("highlighted_checkerboard.png"), HIGHLIGHT_CHECKERED("highlighted_checkerboard.png"),
SELECTION("selection.png"), SELECTION("selection.png"),
GLUE("glue.png"),
; ;

View file

@ -11,7 +11,6 @@ import com.simibubi.create.api.behaviour.BlockSpoutingBehaviour;
import com.simibubi.create.content.CreateItemGroup; import com.simibubi.create.content.CreateItemGroup;
import com.simibubi.create.content.contraptions.TorquePropagator; import com.simibubi.create.content.contraptions.TorquePropagator;
import com.simibubi.create.content.contraptions.components.flywheel.engine.FurnaceEngineInteractions; import com.simibubi.create.content.contraptions.components.flywheel.engine.FurnaceEngineInteractions;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.GlueQueue;
import com.simibubi.create.content.curiosities.weapons.BuiltinPotatoProjectileTypes; import com.simibubi.create.content.curiosities.weapons.BuiltinPotatoProjectileTypes;
import com.simibubi.create.content.logistics.RedstoneLinkNetworkHandler; import com.simibubi.create.content.logistics.RedstoneLinkNetworkHandler;
import com.simibubi.create.content.logistics.block.data.AllDataGathererBehaviours; import com.simibubi.create.content.logistics.block.data.AllDataGathererBehaviours;
@ -77,7 +76,6 @@ public class Create {
public static final TorquePropagator TORQUE_PROPAGATOR = new TorquePropagator(); public static final TorquePropagator TORQUE_PROPAGATOR = new TorquePropagator();
public static final GlobalRailwayManager RAILWAYS = new GlobalRailwayManager(); public static final GlobalRailwayManager RAILWAYS = new GlobalRailwayManager();
public static final ServerLagger LAGGER = new ServerLagger(); public static final ServerLagger LAGGER = new ServerLagger();
public static final GlueQueue GLUE_QUEUE = new GlueQueue();
public static final Random RANDOM = new Random(); public static final Random RANDOM = new Random();
private static final NonNullSupplier<CreateRegistrate> REGISTRATE = CreateRegistrate.lazy(ID); private static final NonNullSupplier<CreateRegistrate> REGISTRATE = CreateRegistrate.lazy(ID);

View file

@ -42,7 +42,6 @@ import com.simibubi.create.content.contraptions.components.structureMovement.cha
import com.simibubi.create.content.contraptions.components.structureMovement.chassis.StickerBlock; import com.simibubi.create.content.contraptions.components.structureMovement.chassis.StickerBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.gantry.GantryCarriageBlock; import com.simibubi.create.content.contraptions.components.structureMovement.gantry.GantryCarriageBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity; import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueHandler;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsBlock; import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock; import com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock;
import com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.PistonState; import com.simibubi.create.content.contraptions.components.structureMovement.piston.MechanicalPistonBlock.PistonState;
@ -101,6 +100,7 @@ import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.ChestType; import net.minecraft.world.level.block.state.properties.ChestType;
import net.minecraft.world.level.block.state.properties.PistonType; import net.minecraft.world.level.block.state.properties.PistonType;
import net.minecraft.world.level.chunk.HashMapPalette; import net.minecraft.world.level.chunk.HashMapPalette;
import net.minecraft.world.level.levelgen.structure.BoundingBox;
import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo; import net.minecraft.world.level.levelgen.structure.templatesystem.StructureTemplate.StructureBlockInfo;
import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids; import net.minecraft.world.level.material.Fluids;
@ -137,12 +137,12 @@ public abstract class Contraption {
protected Map<BlockPos, MountedFluidStorage> fluidStorage; protected Map<BlockPos, MountedFluidStorage> fluidStorage;
protected List<MutablePair<StructureBlockInfo, MovementContext>> actors; protected List<MutablePair<StructureBlockInfo, MovementContext>> actors;
protected Map<BlockPos, MovingInteractionBehaviour> interactors; protected Map<BlockPos, MovingInteractionBehaviour> interactors;
protected Set<Pair<BlockPos, Direction>> superglue; protected List<AABB> superglue;
protected List<BlockPos> seats; protected List<BlockPos> seats;
protected Map<UUID, Integer> seatMapping; protected Map<UUID, Integer> seatMapping;
protected Map<UUID, BlockFace> stabilizedSubContraptions; protected Map<UUID, BlockFace> stabilizedSubContraptions;
private List<SuperGlueEntity> glueToRemove; private Set<SuperGlueEntity> glueToRemove;
private Map<BlockPos, Entity> initialPassengers; private Map<BlockPos, Entity> initialPassengers;
private List<BlockFace> pendingSubContraptions; private List<BlockFace> pendingSubContraptions;
@ -161,10 +161,10 @@ public abstract class Contraption {
seats = new ArrayList<>(); seats = new ArrayList<>();
actors = new ArrayList<>(); actors = new ArrayList<>();
interactors = new HashMap<>(); interactors = new HashMap<>();
superglue = new HashSet<>(); superglue = new ArrayList<>();
seatMapping = new HashMap<>(); seatMapping = new HashMap<>();
fluidStorage = new HashMap<>(); fluidStorage = new HashMap<>();
glueToRemove = new ArrayList<>(); glueToRemove = new HashSet<>();
initialPassengers = new HashMap<>(); initialPassengers = new HashMap<>();
presentTileEntities = new HashMap<>(); presentTileEntities = new HashMap<>();
maybeInstancedTileEntities = new ArrayList<>(); maybeInstancedTileEntities = new ArrayList<>();
@ -389,8 +389,6 @@ public abstract class Contraption {
if (!visited.contains(posDown) && AllBlocks.CART_ASSEMBLER.has(stateBelow)) if (!visited.contains(posDown) && AllBlocks.CART_ASSEMBLER.has(stateBelow))
frontier.add(posDown); frontier.add(posDown);
Map<Direction, SuperGlueEntity> superglue = SuperGlueHandler.gatherGlue(world, pos);
// Slime blocks and super glue drag adjacent blocks if possible // Slime blocks and super glue drag adjacent blocks if possible
for (Direction offset : Iterate.directions) { for (Direction offset : Iterate.directions) {
BlockPos offsetPos = pos.relative(offset); BlockPos offsetPos = pos.relative(offset);
@ -404,7 +402,7 @@ public abstract class Contraption {
} }
boolean wasVisited = visited.contains(offsetPos); boolean wasVisited = visited.contains(offsetPos);
boolean faceHasGlue = superglue.containsKey(offset); boolean faceHasGlue = SuperGlueEntity.isGlued(world, pos, offset, glueToRemove);
boolean blockAttachedTowardsFace = boolean blockAttachedTowardsFace =
BlockMovementChecks.isBlockAttachedTowards(blockState, world, offsetPos, offset.getOpposite()); BlockMovementChecks.isBlockAttachedTowards(blockState, world, offsetPos, offset.getOpposite());
boolean brittle = BlockMovementChecks.isBrittle(blockState); boolean brittle = BlockMovementChecks.isBrittle(blockState);
@ -425,8 +423,6 @@ public abstract class Contraption {
if (!wasVisited && (canStick || blockAttachedTowardsFace || faceHasGlue if (!wasVisited && (canStick || blockAttachedTowardsFace || faceHasGlue
|| (offset == forcedDirection && !BlockMovementChecks.isNotSupportive(state, forcedDirection)))) || (offset == forcedDirection && !BlockMovementChecks.isNotSupportive(state, forcedDirection))))
frontier.add(offsetPos); frontier.add(offsetPos);
if (faceHasGlue)
addGlue(superglue.get(offset));
} }
addBlock(pos, capture(world, pos)); addBlock(pos, capture(world, pos));
@ -678,13 +674,6 @@ public abstract class Contraption {
return nbt; return nbt;
} }
protected void addGlue(SuperGlueEntity entity) {
BlockPos pos = entity.getHangingPosition();
Direction direction = entity.getFacingDirection();
this.superglue.add(Pair.of(toLocalPos(pos), direction));
glueToRemove.add(entity);
}
protected BlockPos toLocalPos(BlockPos globalPos) { protected BlockPos toLocalPos(BlockPos globalPos) {
return globalPos.subtract(anchor); return globalPos.subtract(anchor);
} }
@ -718,8 +707,8 @@ public abstract class Contraption {
}); });
superglue.clear(); superglue.clear();
NBTHelper.iterateCompoundList(nbt.getList("Superglue", Tag.TAG_COMPOUND), c -> superglue.add( NBTHelper.iterateCompoundList(nbt.getList("Superglue", Tag.TAG_COMPOUND),
Pair.of(NbtUtils.readBlockPos(c.getCompound("Pos")), Direction.from3DDataValue(c.getByte("Direction"))))); c -> superglue.add(SuperGlueEntity.readBoundingBox(c)));
seats.clear(); seats.clear();
NBTHelper.iterateCompoundList(nbt.getList("Seats", Tag.TAG_COMPOUND), c -> seats.add(NbtUtils.readBlockPos(c))); NBTHelper.iterateCompoundList(nbt.getList("Seats", Tag.TAG_COMPOUND), c -> seats.add(NbtUtils.readBlockPos(c)));
@ -802,11 +791,9 @@ public abstract class Contraption {
ListTag superglueNBT = new ListTag(); ListTag superglueNBT = new ListTag();
ListTag storageNBT = new ListTag(); ListTag storageNBT = new ListTag();
if (!spawnPacket) { if (!spawnPacket) {
for (Pair<BlockPos, Direction> glueEntry : superglue) { for (AABB glueEntry : superglue) {
CompoundTag c = new CompoundTag(); CompoundTag c = new CompoundTag();
c.put("Pos", NbtUtils.writeBlockPos(glueEntry.getKey())); SuperGlueEntity.writeBoundingBox(c, glueEntry);
c.putByte("Direction", (byte) glueEntry.getValue()
.get3DDataValue());
superglueNBT.add(c); superglueNBT.add(c);
} }
@ -978,7 +965,17 @@ public abstract class Contraption {
.forEach(MountedStorage::removeStorageFromWorld); .forEach(MountedStorage::removeStorageFromWorld);
fluidStorage.values() fluidStorage.values()
.forEach(MountedFluidStorage::removeStorageFromWorld); .forEach(MountedFluidStorage::removeStorageFromWorld);
glueToRemove.forEach(SuperGlueEntity::discard);
glueToRemove.forEach(glue -> {
superglue.add(glue.getBoundingBox()
.move(Vec3.atLowerCornerOf(offset.offset(anchor))
.scale(-1)));
glue.discard();
});
List<BoundingBox> minimisedGlue = new ArrayList<>();
for (int i = 0; i < superglue.size(); i++)
minimisedGlue.add(null);
for (boolean brittles : Iterate.trueAndFalse) { for (boolean brittles : Iterate.trueAndFalse) {
for (Iterator<StructureBlockInfo> iterator = blocks.values() for (Iterator<StructureBlockInfo> iterator = blocks.values()
@ -987,6 +984,18 @@ public abstract class Contraption {
if (brittles != BlockMovementChecks.isBrittle(block.state)) if (brittles != BlockMovementChecks.isBrittle(block.state))
continue; continue;
for (int i = 0; i < superglue.size(); i++) {
AABB aabb = superglue.get(i);
if (aabb == null
|| !aabb.contains(block.pos.getX() + .5, block.pos.getY() + .5, block.pos.getZ() + .5))
continue;
if (minimisedGlue.get(i) == null)
minimisedGlue.set(i, new BoundingBox(block.pos));
else
minimisedGlue.get(i)
.encapsulate(block.pos);
}
BlockPos add = block.pos.offset(anchor) BlockPos add = block.pos.offset(anchor)
.offset(offset); .offset(offset);
if (customBlockRemoval(world, add, block.state)) if (customBlockRemoval(world, add, block.state))
@ -1008,6 +1017,16 @@ public abstract class Contraption {
world.setBlock(add, Blocks.AIR.defaultBlockState(), flags); world.setBlock(add, Blocks.AIR.defaultBlockState(), flags);
} }
} }
superglue.clear();
for (BoundingBox box : minimisedGlue) {
if (box == null)
continue;
AABB bb = new AABB(box.minX(), box.minY(), box.minZ(), box.maxX() + 1, box.maxY() + 1, box.maxZ() + 1);
if (bb.getSize() > 1.01)
superglue.add(bb);
}
for (StructureBlockInfo block : blocks.values()) { for (StructureBlockInfo block : blocks.values()) {
BlockPos add = block.pos.offset(anchor) BlockPos add = block.pos.offset(anchor)
.offset(offset); .offset(offset);
@ -1137,15 +1156,11 @@ public abstract class Contraption {
for (int i = 0; i < fluidInventory.getTanks(); i++) for (int i = 0; i < fluidInventory.getTanks(); i++)
fluidInventory.drain(fluidInventory.getFluidInTank(i), FluidAction.EXECUTE); fluidInventory.drain(fluidInventory.getFluidInTank(i), FluidAction.EXECUTE);
for (Pair<BlockPos, Direction> pair : superglue) { for (AABB box : superglue) {
BlockPos targetPos = transform.apply(pair.getKey()); box = new AABB(transform.apply(new Vec3(box.minX, box.minY, box.minZ)),
Direction targetFacing = transform.transformFacing(pair.getValue()); transform.apply(new Vec3(box.maxX, box.maxY, box.maxZ)));
if (!world.isClientSide)
SuperGlueEntity entity = new SuperGlueEntity(world, targetPos, targetFacing); world.addFreshEntity(new SuperGlueEntity(world, box));
if (entity.onValidSurface()) {
if (!world.isClientSide)
world.addFreshEntity(entity);
}
} }
} }

View file

@ -1,89 +0,0 @@
package com.simibubi.create.content.contraptions.components.structureMovement.glue;
import com.jozufozu.flywheel.api.Instancer;
import com.jozufozu.flywheel.api.MaterialGroup;
import com.jozufozu.flywheel.api.MaterialManager;
import com.jozufozu.flywheel.api.instance.TickableInstance;
import com.jozufozu.flywheel.backend.instancing.entity.EntityInstance;
import com.jozufozu.flywheel.core.Materials;
import com.jozufozu.flywheel.core.instancing.ConditionalInstance;
import com.jozufozu.flywheel.core.materials.oriented.OrientedData;
import com.mojang.math.Quaternion;
import com.simibubi.create.AllItems;
import com.simibubi.create.Create;
import com.simibubi.create.foundation.utility.AngleHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.LightLayer;
public class GlueInstance extends EntityInstance<SuperGlueEntity> implements TickableInstance {
private static final ResourceLocation TEXTURE = Create.asResource("textures/entity/super_glue/slime.png");
private final Quaternion rotation;
protected ConditionalInstance<OrientedData> model;
public GlueInstance(MaterialManager materialManager, SuperGlueEntity entity) {
super(materialManager, entity);
Instancer<OrientedData> instancer = getInstancer(materialManager, entity);
Direction face = entity.getFacingDirection();
rotation = new Quaternion(AngleHelper.verticalAngle(face), AngleHelper.horizontalAngle(face), 0, true);
model = new ConditionalInstance<>(instancer)
.withCondition(this::shouldShow)
.withSetupFunc(this::positionModel)
.update();
}
private Instancer<OrientedData> getInstancer(MaterialManager materialManager, SuperGlueEntity entity) {
MaterialGroup group = GlueModel.USE_ATLAS ? materialManager.defaultCutout() : materialManager.cutout(RenderType.entityCutout(TEXTURE));
return group.material(Materials.ORIENTED).model(entity.getType(), GlueModel::get);
}
@Override
public void tick() {
model.update();
}
@Override
public void remove() {
model.delete();
}
private void positionModel(OrientedData model) {
model.setPosition(getInstancePosition())
.setPivot(0, 0, 0)
.setRotation(rotation);
updateLight(model);
}
@Override
public void updateLight() {
model.get().ifPresent(this::updateLight);
}
private void updateLight(OrientedData model) {
BlockPos pos = entity.getHangingPosition();
model.setBlockLight(world.getBrightness(LightLayer.BLOCK, pos))
.setSkyLight(world.getBrightness(LightLayer.SKY, pos));
}
private boolean shouldShow() {
Player player = Minecraft.getInstance().player;
return entity.isVisible()
|| AllItems.SUPER_GLUE.isIn(player.getMainHandItem())
|| AllItems.SUPER_GLUE.isIn(player.getOffhandItem());
}
}

View file

@ -1,99 +0,0 @@
package com.simibubi.create.content.contraptions.components.structureMovement.glue;
import com.jozufozu.flywheel.api.vertex.VertexList;
import com.jozufozu.flywheel.core.Formats;
import com.jozufozu.flywheel.core.model.Model;
import com.jozufozu.flywheel.core.vertex.PosTexNormalWriterUnsafe;
import com.mojang.blaze3d.platform.MemoryTracker;
import com.simibubi.create.AllStitchedTextures;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.Direction;
import net.minecraft.world.phys.Vec3;
public class GlueModel implements Model {
public static final GlueModel INSTANCE = new GlueModel();
static final boolean USE_ATLAS = false;
public static GlueModel get() {
return INSTANCE;
}
private final VertexList reader;
private GlueModel() {
PosTexNormalWriterUnsafe writer = Formats.POS_TEX_NORMAL.createWriter(MemoryTracker.create(size()));
createGlueModel(writer);
reader = writer.intoReader();
}
@Override
public String name() {
return "glue";
}
@Override
public int vertexCount() {
return 8;
}
@Override
public VertexList getReader() {
return reader;
}
public static void createGlueModel(PosTexNormalWriterUnsafe buffer) {
Vec3 diff = Vec3.atLowerCornerOf(Direction.SOUTH.getNormal());
Vec3 extension = diff.normalize()
.scale(1 / 32f - 1 / 128f);
Vec3 plane = VecHelper.axisAlingedPlaneOf(diff);
Direction.Axis axis = Direction.getNearest(diff.x, diff.y, diff.z)
.getAxis();
Vec3 start = Vec3.ZERO.subtract(extension);
Vec3 end = Vec3.ZERO.add(extension);
plane = plane.scale(1 / 2f);
Vec3 a1 = plane.add(start);
Vec3 b1 = plane.add(end);
plane = VecHelper.rotate(plane, -90, axis);
Vec3 a2 = plane.add(start);
Vec3 b2 = plane.add(end);
plane = VecHelper.rotate(plane, -90, axis);
Vec3 a3 = plane.add(start);
Vec3 b3 = plane.add(end);
plane = VecHelper.rotate(plane, -90, axis);
Vec3 a4 = plane.add(start);
Vec3 b4 = plane.add(end);
float minU;
float maxU;
float minV;
float maxV;
if (USE_ATLAS) {
TextureAtlasSprite sprite = AllStitchedTextures.SUPER_GLUE.get();
minU = sprite.getU0();
maxU = sprite.getU1();
minV = sprite.getV0();
maxV = sprite.getV1();
} else {
minU = minV = 0;
maxU = maxV = 1;
}
// inside quad
buffer.putVertex((float) a1.x, (float) a1.y, (float) a1.z, 0, 0, -1, maxU, minV);
buffer.putVertex((float) a2.x, (float) a2.y, (float) a2.z, 0, 0, -1, maxU, maxV);
buffer.putVertex((float) a3.x, (float) a3.y, (float) a3.z, 0, 0, -1, minU, maxV);
buffer.putVertex((float) a4.x, (float) a4.y, (float) a4.z, 0, 0, -1, minU, minV);
// outside quad
buffer.putVertex((float) b4.x, (float) b4.y, (float) b4.z, 0, 0, 1f, minU, minV);
buffer.putVertex((float) b3.x, (float) b3.y, (float) b3.z, 0, 0, 1f, minU, maxV);
buffer.putVertex((float) b2.x, (float) b2.y, (float) b2.z, 0, 0, 1f, maxU, maxV);
buffer.putVertex((float) b1.x, (float) b1.y, (float) b1.z, 0, 0, 1f, maxU, minV);
}
}

View file

@ -1,42 +0,0 @@
package com.simibubi.create.content.contraptions.components.structureMovement.glue;
import java.util.Collection;
import java.util.LinkedList;
import java.util.List;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.BlockFace;
import com.simibubi.create.foundation.utility.WorldAttached;
import net.minecraft.util.Mth;
import net.minecraft.world.level.Level;
import net.minecraftforge.network.PacketDistributor;
public class GlueQueue {
private WorldAttached<List<BlockFace>> QUEUED_GLUE = new WorldAttached<>(level -> new LinkedList<>());
public void tick(Level level) {
List<BlockFace> list = QUEUED_GLUE.get(level);
if (list.isEmpty())
return;
BlockFace next = list.remove(0);
if (!level.isLoaded(next.getPos()))
return;
SuperGlueEntity entity = new SuperGlueEntity(level, next.getPos(), next.getFace());
level.addFreshEntity(entity);
AllSoundEvents.SLIME_ADDED.playFrom(entity, 0.125F, Mth.clamp(8f / (list.size() + 1), 0.75f, 1f));
AllPackets.channel.send(PacketDistributor.ALL.noArg(),
new GlueEffectPacket(entity.getHangingPosition(), entity.getFacingDirection()
.getOpposite(), false));
}
public void add(Level level, Collection<BlockFace> entries) {
QUEUED_GLUE.get(level)
.addAll(entries);
}
}

View file

@ -1,8 +1,6 @@
package com.simibubi.create.content.contraptions.components.structureMovement.glue; package com.simibubi.create.content.contraptions.components.structureMovement.glue;
import javax.annotation.Nullable; import java.util.Set;
import org.apache.commons.lang3.Validate;
import com.simibubi.create.AllBlocks; import com.simibubi.create.AllBlocks;
import com.simibubi.create.AllEntityTypes; import com.simibubi.create.AllEntityTypes;
@ -15,23 +13,19 @@ import com.simibubi.create.content.contraptions.components.structureMovement.cha
import com.simibubi.create.content.schematics.ISpecialEntityItemRequirement; import com.simibubi.create.content.schematics.ISpecialEntityItemRequirement;
import com.simibubi.create.content.schematics.ItemRequirement; import com.simibubi.create.content.schematics.ItemRequirement;
import com.simibubi.create.content.schematics.ItemRequirement.ItemUseType; import com.simibubi.create.content.schematics.ItemRequirement.ItemUseType;
import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.AnimationTickHolder; import com.simibubi.create.foundation.utility.VecHelper;
import com.simibubi.create.foundation.utility.BlockFace;
import com.simibubi.create.foundation.utility.worldWrappers.WrappedWorld;
import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis; import net.minecraft.core.Direction.Axis;
import net.minecraft.core.Direction.AxisDirection;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.Tag;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.protocol.Packet; import net.minecraft.network.protocol.Packet;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.util.Mth;
import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult; import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.damagesource.DamageSource;
@ -39,14 +33,11 @@ import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityDimensions; import net.minecraft.world.entity.EntityDimensions;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LightningBolt; import net.minecraft.world.entity.LightningBolt;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.MoverType; import net.minecraft.world.entity.MoverType;
import net.minecraft.world.entity.Pose; import net.minecraft.world.entity.Pose;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.GameRules;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.DirectionalBlock; import net.minecraft.world.level.block.DirectionalBlock;
import net.minecraft.world.level.block.Mirror; import net.minecraft.world.level.block.Mirror;
@ -54,145 +45,51 @@ import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.HitResult.Type;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.entity.IEntityAdditionalSpawnData; import net.minecraftforge.entity.IEntityAdditionalSpawnData;
import net.minecraftforge.fml.DistExecutor;
import net.minecraftforge.network.NetworkHooks; import net.minecraftforge.network.NetworkHooks;
import net.minecraftforge.network.PacketDistributor;
public class SuperGlueEntity extends Entity public class SuperGlueEntity extends Entity implements IEntityAdditionalSpawnData, ISpecialEntityItemRequirement {
implements IEntityAdditionalSpawnData, ISpecialEntityItemRequirement {
private int validationTimer; public static AABB span(BlockPos startPos, BlockPos endPos) {
protected BlockPos hangingPosition; return new AABB(startPos, endPos).expandTowards(1, 1, 1);
protected Direction facingDirection = Direction.SOUTH; }
public static boolean isGlued(LevelAccessor level, BlockPos blockPos, Direction direction,
Set<SuperGlueEntity> cached) {
BlockPos targetPos = blockPos.relative(direction);
if (cached != null)
for (SuperGlueEntity glueEntity : cached)
if (glueEntity.contains(blockPos) && glueEntity.contains(targetPos))
return true;
for (SuperGlueEntity glueEntity : level.getEntitiesOfClass(SuperGlueEntity.class, span(blockPos, targetPos))) {
if (!glueEntity.contains(blockPos) || !glueEntity.contains(targetPos))
continue;
if (cached != null)
cached.add(glueEntity);
return true;
}
return false;
}
public SuperGlueEntity(EntityType<?> type, Level world) { public SuperGlueEntity(EntityType<?> type, Level world) {
super(type, world); super(type, world);
} }
public SuperGlueEntity(Level world, BlockPos pos, Direction direction) { public SuperGlueEntity(Level world, AABB boundingBox) {
this(AllEntityTypes.SUPER_GLUE.get(), world); this(AllEntityTypes.SUPER_GLUE.get(), world);
hangingPosition = pos; setBoundingBox(boundingBox);
facingDirection = direction; resetPositionToBB();
updateFacingWithBoundingBox(); }
public void resetPositionToBB() {
AABB bb = getBoundingBox();
setPosRaw(bb.getCenter().x, bb.minY, bb.getCenter().z);
} }
@Override @Override
protected void defineSynchedData() {} protected void defineSynchedData() {}
public int getWidthPixels() {
return 12;
}
public int getHeightPixels() {
return 12;
}
public void onBroken(@Nullable Entity breaker) {
playSound(SoundEvents.SLIME_SQUISH_SMALL, 1.0F, 1.0F);
if (onValidSurface()) {
AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> this),
new GlueEffectPacket(getHangingPosition(), getFacingDirection().getOpposite(), false));
AllSoundEvents.SLIME_ADDED.playFrom(this, 0.5F, 0.5F);
}
}
public void playPlaceSound() {
AllSoundEvents.SLIME_ADDED.playFrom(this, 0.5F, 0.75F);
}
protected void updateFacingWithBoundingBox() {
Validate.notNull(getFacingDirection());
if (getFacingDirection().getAxis()
.isHorizontal()) {
setXRot(0);
setYRot(getFacingDirection().get2DDataValue() * 90);
} else {
setXRot(-90 * getFacingDirection().getAxisDirection()
.getStep());
setYRot(0);
}
this.xRotO = this.getXRot();
this.yRotO = this.getYRot();
this.updateBoundingBox();
}
protected void updateBoundingBox() {
if (this.getFacingDirection() != null) {
double offset = 0.5 - 1 / 256d;
double x = hangingPosition.getX() + 0.5 - facingDirection.getStepX() * offset;
double y = hangingPosition.getY() + 0.5 - facingDirection.getStepY() * offset;
double z = hangingPosition.getZ() + 0.5 - facingDirection.getStepZ() * offset;
this.setPosRaw(x, y, z);
double w = getWidthPixels();
double h = getHeightPixels();
double l = getWidthPixels();
Axis axis = this.getFacingDirection()
.getAxis();
double depth = 2 - 1 / 128f;
switch (axis) {
case X -> w = depth;
case Y -> h = depth;
case Z -> l = depth;
}
w = w / 32.0D;
h = h / 32.0D;
l = l / 32.0D;
this.setBoundingBox(new AABB(x - w, y - h, z - l, x + w, y + h, z + l));
}
}
@Override
public void tick() {
if (this.validationTimer++ == 10 && !this.level.isClientSide) {
this.validationTimer = 0;
if (isAlive() && !this.onValidSurface()) {
kill();
onBroken(null);
}
}
}
public boolean isVisible() {
if (!isAlive())
return false;
if (level instanceof WrappedWorld)
return true;
BlockPos pos = hangingPosition;
BlockPos pos2 = pos.relative(getFacingDirection().getOpposite());
return isValidFace(level, pos2, getFacingDirection()) != isValidFace(level, pos,
getFacingDirection().getOpposite());
}
public boolean onValidSurface() {
BlockPos pos = hangingPosition;
BlockPos pos2 = hangingPosition.relative(getFacingDirection().getOpposite());
if (level.isOutsideBuildHeight(pos2))
return false;
if (!level.isLoaded(pos) || !level.isLoaded(pos2))
return true;
if (!isValidFace(level, pos2, getFacingDirection())
&& !isValidFace(level, pos, getFacingDirection().getOpposite()))
return false;
if (isSideSticky(level, pos2, getFacingDirection())
|| isSideSticky(level, pos, getFacingDirection().getOpposite()))
return false;
return level.getEntities(this, getBoundingBox(), e -> e instanceof SuperGlueEntity)
.isEmpty();
}
public static boolean isValidFace(Level world, BlockPos pos, Direction direction) { public static boolean isValidFace(Level world, BlockPos pos, Direction direction) {
BlockState state = world.getBlockState(pos); BlockState state = world.getBlockState(pos);
if (BlockMovementChecks.isBlockAttachedTowards(state, world, pos, direction)) if (BlockMovementChecks.isBlockAttachedTowards(state, world, pos, direction))
@ -237,61 +134,36 @@ public class SuperGlueEntity extends Entity
return false; return false;
} }
@Override
public boolean isPickable() {
return true;
}
@Override
public boolean skipAttackInteraction(Entity entity) {
return entity instanceof Player ? hurt(DamageSource.playerAttack((Player) entity), 0) : false;
}
@Override
public Direction getDirection() {
return this.getFacingDirection();
}
@Override @Override
public boolean hurt(DamageSource source, float amount) { public boolean hurt(DamageSource source, float amount) {
if (this.isInvulnerableTo(source)) return false;
return false; }
boolean mobGriefing = level.getGameRules() @Override
.getBoolean(GameRules.RULE_MOBGRIEFING); public void tick() {
Entity trueSource = source.getEntity(); super.tick();
if (!mobGriefing && trueSource instanceof Mob) if (getBoundingBox().getXsize() == 0)
return false; discard();
}
Entity immediateSource = source.getDirectEntity(); @Override
if (!isVisible() && immediateSource instanceof Player) { public void setPos(double x, double y, double z) {
if (!AllItems.SUPER_GLUE.isIn(((Player) immediateSource).getMainHandItem())) setPosRaw(x, y, z);
return true; getBoundingBox().move(getBoundingBox().getCenter()
} .scale(-1)
.add(x, y, z));
if (isAlive() && !level.isClientSide) {
onBroken(source.getEntity());
kill();
markHurt();
}
return true;
} }
@Override @Override
public void move(MoverType typeIn, Vec3 pos) { public void move(MoverType typeIn, Vec3 pos) {
if (!level.isClientSide && isAlive() && pos.lengthSqr() > 0.0D) { if (!level.isClientSide && isAlive() && pos.lengthSqr() > 0.0D)
discard(); discard();
onBroken(null);
}
} }
@Override @Override
public void push(double x, double y, double z) { public void push(double x, double y, double z) {
if (!level.isClientSide && isAlive() && x * x + y * y + z * z > 0.0D) { if (!level.isClientSide && isAlive() && x * x + y * y + z * z > 0.0D)
discard(); discard();
onBroken(null);
}
} }
@Override @Override
@ -299,9 +171,8 @@ public class SuperGlueEntity extends Entity
return 0.0F; return 0.0F;
} }
@Override public void playPlaceSound() {
public ItemStack getPickedResult(HitResult target) { AllSoundEvents.SLIME_ADDED.playFrom(this, 0.5F, 0.75F);
return AllItems.SUPER_GLUE.asStack();
} }
@Override @Override
@ -311,79 +182,30 @@ public class SuperGlueEntity extends Entity
@Override @Override
public InteractionResult interact(Player player, InteractionHand hand) { public InteractionResult interact(Player player, InteractionHand hand) {
if (player instanceof FakePlayer) return InteractionResult.PASS;
return InteractionResult.PASS;
DistExecutor.unsafeRunWhenOn(Dist.CLIENT, () -> () -> {
triggerPlaceBlock(player, hand);
});
return InteractionResult.CONSUME;
}
@OnlyIn(Dist.CLIENT)
private void triggerPlaceBlock(Player player, InteractionHand hand) {
if (!(player instanceof LocalPlayer))
return;
if (!(player.level instanceof ClientLevel))
return;
LocalPlayer cPlayer = (LocalPlayer) player;
Minecraft mc = Minecraft.getInstance();
HitResult ray = cPlayer.pick(mc.gameMode.getPickRange(), AnimationTickHolder.getPartialTicks(), false);
if (!(ray instanceof BlockHitResult))
return;
if (ray.getType() == Type.MISS)
return;
BlockHitResult blockRay = (BlockHitResult) ray;
BlockFace rayFace = new BlockFace(blockRay.getBlockPos(), blockRay.getDirection());
BlockFace hangingFace = new BlockFace(getHangingPosition(), getFacingDirection().getOpposite());
if (!rayFace.isEquivalent(hangingFace))
return;
for (InteractionHand handIn : InteractionHand.values()) {
ItemStack itemstack = cPlayer.getItemInHand(handIn);
int countBefore = itemstack.getCount();
InteractionResult actionResultType =
mc.gameMode.useItemOn(cPlayer, (ClientLevel) cPlayer.level, handIn, blockRay);
if (actionResultType != InteractionResult.SUCCESS)
return;
cPlayer.swing(handIn);
if (!itemstack.isEmpty() && (itemstack.getCount() != countBefore || mc.gameMode.hasInfiniteItems()))
mc.gameRenderer.itemInHandRenderer.itemUsed(handIn);
return;
}
} }
@Override @Override
public void addAdditionalSaveData(CompoundTag compound) { public void addAdditionalSaveData(CompoundTag compound) {
compound.putByte("Facing", (byte) this.getFacingDirection() Vec3 position = position();
.get3DDataValue()); writeBoundingBox(compound, getBoundingBox().move(position.scale(-1)));
BlockPos blockpos = this.getHangingPosition();
compound.putInt("TileX", blockpos.getX());
compound.putInt("TileY", blockpos.getY());
compound.putInt("TileZ", blockpos.getZ());
} }
@Override @Override
public void readAdditionalSaveData(CompoundTag compound) { public void readAdditionalSaveData(CompoundTag compound) {
this.hangingPosition = Vec3 position = position();
new BlockPos(compound.getInt("TileX"), compound.getInt("TileY"), compound.getInt("TileZ")); setBoundingBox(readBoundingBox(compound).move(position));
this.facingDirection = Direction.from3DDataValue(compound.getByte("Facing"));
updateFacingWithBoundingBox();
} }
@Override public static void writeBoundingBox(CompoundTag compound, AABB bb) {
public ItemEntity spawnAtLocation(ItemStack stack, float yOffset) { compound.put("From", VecHelper.writeNBT(new Vec3(bb.minX, bb.minY, bb.minZ)));
float xOffset = (float) this.getFacingDirection() compound.put("To", VecHelper.writeNBT(new Vec3(bb.maxX, bb.maxY, bb.maxZ)));
.getStepX() * 0.15F; }
float zOffset = (float) this.getFacingDirection()
.getStepZ() * 0.15F; public static AABB readBoundingBox(CompoundTag compound) {
ItemEntity itementity = Vec3 from = VecHelper.readNBT(compound.getList("From", Tag.TAG_DOUBLE));
new ItemEntity(this.level, this.getX() + xOffset, this.getY() + yOffset, this.getZ() + zOffset, stack); Vec3 to = VecHelper.readNBT(compound.getList("To", Tag.TAG_DOUBLE));
itementity.setDefaultPickUpDelay(); return new AABB(from, to);
this.level.addFreshEntity(itementity);
return itementity;
} }
@Override @Override
@ -391,51 +213,17 @@ public class SuperGlueEntity extends Entity
return false; return false;
} }
@Override
public void setPos(double x, double y, double z) {
hangingPosition = new BlockPos(x, y, z);
updateBoundingBox();
hasImpulse = true;
}
@Override @Override
public float rotate(Rotation transformRotation) { public float rotate(Rotation transformRotation) {
if (this.getFacingDirection() AABB bb = getBoundingBox().move(position().scale(-1));
.getAxis() != Direction.Axis.Y) { if (transformRotation == Rotation.CLOCKWISE_90 || transformRotation == Rotation.COUNTERCLOCKWISE_90)
switch (transformRotation) { setBoundingBox(new AABB(bb.minZ, bb.minY, bb.minX, bb.maxZ, bb.maxY, bb.maxX).move(position()));
case CLOCKWISE_180: return super.rotate(transformRotation);
facingDirection = facingDirection.getOpposite();
break;
case COUNTERCLOCKWISE_90:
facingDirection = facingDirection.getCounterClockWise();
break;
case CLOCKWISE_90:
facingDirection = facingDirection.getClockWise();
default:
break;
}
}
float f = Mth.wrapDegrees(this.getYRot());
return switch (transformRotation) {
case CLOCKWISE_180 -> f + 180.0F;
case COUNTERCLOCKWISE_90 -> f + 90.0F;
case CLOCKWISE_90 -> f + 270.0F;
default -> f;
};
}
public BlockPos getHangingPosition() {
return this.hangingPosition;
} }
@Override @Override
public float mirror(Mirror transformMirror) { public float mirror(Mirror transformMirror) {
return this.rotate(transformMirror.getRotation(this.getFacingDirection())); return super.mirror(transformMirror);
}
public Direction getAttachedDirection(BlockPos pos) {
return !pos.equals(hangingPosition) ? getFacingDirection() : getFacingDirection().getOpposite();
} }
@Override @Override
@ -467,10 +255,6 @@ public class SuperGlueEntity extends Entity
readAdditionalSaveData(additionalData.readNbt()); readAdditionalSaveData(additionalData.readNbt());
} }
public Direction getFacingDirection() {
return facingDirection;
}
@Override @Override
public ItemRequirement getRequiredItems() { public ItemRequirement getRequiredItems() {
return new ItemRequirement(ItemUseType.DAMAGE, AllItems.SUPER_GLUE.get()); return new ItemRequirement(ItemUseType.DAMAGE, AllItems.SUPER_GLUE.get());
@ -480,4 +264,53 @@ public class SuperGlueEntity extends Entity
public boolean isIgnoringBlockTriggers() { public boolean isIgnoringBlockTriggers() {
return true; return true;
} }
public boolean contains(BlockPos pos) {
return getBoundingBox().contains(Vec3.atCenterOf(pos));
}
public void spawnParticles() {
AABB bb = getBoundingBox();
Vec3 origin = new Vec3(bb.minX, bb.minY, bb.minZ);
Vec3 extents = new Vec3(bb.getXsize(), bb.getYsize(), bb.getZsize());
if (!(level instanceof ServerLevel slevel))
return;
for (Axis axis : Iterate.axes) {
AxisDirection positive = AxisDirection.POSITIVE;
double max = axis.choose(extents.x, extents.y, extents.z);
Vec3 normal = Vec3.atLowerCornerOf(Direction.fromAxisAndDirection(axis, positive)
.getNormal());
for (Axis axis2 : Iterate.axes) {
if (axis2 == axis)
continue;
double max2 = axis2.choose(extents.x, extents.y, extents.z);
Vec3 normal2 = Vec3.atLowerCornerOf(Direction.fromAxisAndDirection(axis2, positive)
.getNormal());
for (Axis axis3 : Iterate.axes) {
if (axis3 == axis2 || axis3 == axis)
continue;
double max3 = axis3.choose(extents.x, extents.y, extents.z);
Vec3 normal3 = Vec3.atLowerCornerOf(Direction.fromAxisAndDirection(axis3, positive)
.getNormal());
for (int i = 0; i <= max * 2; i++) {
for (int o1 : Iterate.zeroAndOne) {
for (int o2 : Iterate.zeroAndOne) {
Vec3 v = origin.add(normal.scale(i / 2f))
.add(normal2.scale(max2 * o1))
.add(normal3.scale(max3 * o2));
slevel.sendParticles(ParticleTypes.ITEM_SLIME, v.x, v.y, v.z, 1, 0, 0, 0, 0);
}
}
}
break;
}
break;
}
}
}
} }

View file

@ -1,11 +1,12 @@
package com.simibubi.create.content.contraptions.components.structureMovement.glue; package com.simibubi.create.content.contraptions.components.structureMovement.glue;
import java.util.HashMap; import java.util.HashSet;
import java.util.List; import java.util.Set;
import java.util.Map;
import com.simibubi.create.AllItems; import com.simibubi.create.AllItems;
import com.simibubi.create.content.contraptions.components.structureMovement.BlockMovementChecks;
import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.placement.IPlacementHelper; import com.simibubi.create.foundation.utility.placement.IPlacementHelper;
import com.simibubi.create.foundation.utility.worldWrappers.RayTraceWorld; import com.simibubi.create.foundation.utility.worldWrappers.RayTraceWorld;
@ -21,7 +22,6 @@ import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor; import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult.Type; import net.minecraft.world.phys.HitResult.Type;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@ -34,14 +34,6 @@ import net.minecraftforge.network.PacketDistributor;
@EventBusSubscriber @EventBusSubscriber
public class SuperGlueHandler { public class SuperGlueHandler {
public static Map<Direction, SuperGlueEntity> gatherGlue(LevelAccessor world, BlockPos pos) {
List<SuperGlueEntity> entities = world.getEntitiesOfClass(SuperGlueEntity.class, new AABB(pos));
Map<Direction, SuperGlueEntity> map = new HashMap<>();
for (SuperGlueEntity entity : entities)
map.put(entity.getAttachedDirection(pos), entity);
return map;
}
@SubscribeEvent @SubscribeEvent
public static void glueListensForBlockPlacement(EntityPlaceEvent event) { public static void glueListensForBlockPlacement(EntityPlaceEvent event) {
LevelAccessor world = event.getWorld(); LevelAccessor world = event.getWorld();
@ -53,10 +45,14 @@ public class SuperGlueHandler {
if (world.isClientSide()) if (world.isClientSide())
return; return;
Map<Direction, SuperGlueEntity> gatheredGlue = gatherGlue(world, pos); Set<SuperGlueEntity> cached = new HashSet<>();
for (Direction direction : gatheredGlue.keySet()) for (Direction direction : Iterate.directions) {
AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY_AND_SELF.with(() -> entity), BlockPos relative = pos.relative(direction);
new GlueEffectPacket(pos, direction, true)); if (SuperGlueEntity.isGlued(world, pos, direction, cached)
&& BlockMovementChecks.isMovementNecessary(world.getBlockState(relative), entity.level, relative))
AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY_AND_SELF.with(() -> entity),
new GlueEffectPacket(pos, direction, true));
}
if (entity instanceof Player) if (entity instanceof Player)
glueInOffHandAppliesOnBlockPlace(event, pos, (Player) entity); glueInOffHandAppliesOnBlockPlace(event, pos, (Player) entity);
@ -80,31 +76,33 @@ public class SuperGlueHandler {
RayTraceWorld rayTraceWorld = RayTraceWorld rayTraceWorld =
new RayTraceWorld(world, (p, state) -> p.equals(pos) ? Blocks.AIR.defaultBlockState() : state); new RayTraceWorld(world, (p, state) -> p.equals(pos) ? Blocks.AIR.defaultBlockState() : state);
BlockHitResult ray = rayTraceWorld.clip( BlockHitResult ray =
new ClipContext(start, end, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, placer)); rayTraceWorld.clip(new ClipContext(start, end, ClipContext.Block.OUTLINE, ClipContext.Fluid.NONE, placer));
Direction face = ray.getDirection(); Direction face = ray.getDirection();
if (face == null || ray.getType() == Type.MISS) if (face == null || ray.getType() == Type.MISS)
return; return;
if (!ray.getBlockPos() BlockPos gluePos = ray.getBlockPos();
.relative(face) if (!gluePos.relative(face)
.equals(pos)) { .equals(pos)) {
event.setCanceled(true); event.setCanceled(true);
return; return;
} }
SuperGlueEntity entity = new SuperGlueEntity(world, ray.getBlockPos(), face.getOpposite()); if (SuperGlueEntity.isGlued(world, gluePos, face, null))
return;
SuperGlueEntity entity = new SuperGlueEntity(world, SuperGlueEntity.span(gluePos, gluePos.relative(face)));
CompoundTag compoundnbt = itemstack.getTag(); CompoundTag compoundnbt = itemstack.getTag();
if (compoundnbt != null) if (compoundnbt != null)
EntityType.updateCustomEntityTag(world, placer, entity, compoundnbt); EntityType.updateCustomEntityTag(world, placer, entity, compoundnbt);
if (entity.onValidSurface()) { if (SuperGlueEntity.isValidFace(world, gluePos, face)) {
if (!world.isClientSide) { if (!world.isClientSide) {
entity.playPlaceSound();
world.addFreshEntity(entity); world.addFreshEntity(entity);
AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> entity), AllPackets.channel.send(PacketDistributor.TRACKING_ENTITY.with(() -> entity),
new GlueEffectPacket(ray.getBlockPos(), face, true)); new GlueEffectPacket(gluePos, face, true));
} }
itemstack.hurtAndBreak(1, placer, SuperGlueItem::onBroken); itemstack.hurtAndBreak(1, placer, SuperGlueItem::onBroken);
} }

View file

@ -7,15 +7,11 @@ import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
import net.minecraft.core.particles.ItemParticleOption; import net.minecraft.core.particles.ItemParticleOption;
import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.Mth; import net.minecraft.util.Mth;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.player.Player; import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items; import net.minecraft.world.item.Items;
import net.minecraft.world.item.context.UseOnContext;
import net.minecraft.world.level.Level; import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
@ -49,47 +45,17 @@ public class SuperGlueItem extends Item {
super(properties); super(properties);
} }
@Override
public boolean canAttackBlock(BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer) {
return false;
}
@Override @Override
public boolean canBeDepleted() { public boolean canBeDepleted() {
return true; return true;
} }
@Override public static void onBroken(Player player) {}
public InteractionResult useOn(UseOnContext context) {
BlockPos blockpos = context.getClickedPos();
Direction direction = context.getClickedFace();
BlockPos blockpos1 = blockpos.relative(direction);
Player playerentity = context.getPlayer();
ItemStack itemstack = context.getItemInHand();
if (playerentity == null || !this.canPlace(playerentity, direction, itemstack, blockpos1))
return InteractionResult.FAIL;
Level world = context.getLevel();
SuperGlueEntity entity = new SuperGlueEntity(world, blockpos1, direction);
CompoundTag compoundnbt = itemstack.getTag();
if (compoundnbt != null)
EntityType.updateCustomEntityTag(world, playerentity, entity, compoundnbt);
if (!entity.onValidSurface())
return InteractionResult.FAIL;
if (!world.isClientSide) {
entity.playPlaceSound();
world.addFreshEntity(entity);
}
itemstack.hurtAndBreak(1, playerentity, SuperGlueItem::onBroken);
return InteractionResult.SUCCESS;
}
public static void onBroken(Player player) {
}
protected boolean canPlace(Player entity, Direction facing, ItemStack stack, BlockPos pos) {
return !entity.level.isOutsideBuildHeight(pos) && entity.mayUseItemAt(pos, facing, stack);
}
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public static void spawnParticles(Level world, BlockPos pos, Direction direction, boolean fullBlock) { public static void spawnParticles(Level world, BlockPos pos, Direction direction, boolean fullBlock) {

View file

@ -0,0 +1,53 @@
package com.simibubi.create.content.contraptions.components.structureMovement.glue;
import java.util.function.Supplier;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.foundation.networking.SimplePacketBase;
import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraftforge.network.NetworkEvent.Context;
public class SuperGlueRemovalPacket extends SimplePacketBase {
private int entityId;
private BlockPos soundSource;
public SuperGlueRemovalPacket(int id, BlockPos soundSource) {
entityId = id;
this.soundSource = soundSource;
}
public SuperGlueRemovalPacket(FriendlyByteBuf buffer) {
entityId = buffer.readInt();
soundSource = buffer.readBlockPos();
}
@Override
public void write(FriendlyByteBuf buffer) {
buffer.writeInt(entityId);
buffer.writeBlockPos(soundSource);
}
@Override
public void handle(Supplier<Context> context) {
Context ctx = context.get();
ctx.enqueueWork(() -> {
ServerPlayer player = ctx.getSender();
Entity entity = player.level.getEntity(entityId);
if (!(entity instanceof SuperGlueEntity superGlue))
return;
double range = 32;
if (player.distanceToSqr(superGlue.position()) > range * range)
return;
AllSoundEvents.SLIME_ADDED.play(player.level, null, soundSource, 0.5F, 0.5F);
superGlue.spawnParticles();
entity.discard();
});
ctx.setPacketHandled(true);
}
}

View file

@ -1,146 +1,27 @@
package com.simibubi.create.content.contraptions.components.structureMovement.glue; package com.simibubi.create.content.contraptions.components.structureMovement.glue;
import com.jozufozu.flywheel.util.transform.TransformStack;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.PoseStack.Pose;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.simibubi.create.AllItems;
import com.simibubi.create.Create;
import com.simibubi.create.foundation.utility.AngleHelper;
import com.simibubi.create.foundation.utility.VecHelper;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.LevelRenderer;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.RenderType;
import net.minecraft.client.renderer.culling.Frustum; import net.minecraft.client.renderer.culling.Frustum;
import net.minecraft.client.renderer.entity.EntityRenderer; import net.minecraft.client.renderer.entity.EntityRenderer;
import net.minecraft.client.renderer.entity.EntityRendererProvider; import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.client.renderer.texture.OverlayTexture;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Direction.Axis;
import net.minecraft.resources.ResourceLocation; import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn; import net.minecraftforge.api.distmarker.OnlyIn;
@OnlyIn(Dist.CLIENT) @OnlyIn(Dist.CLIENT)
public class SuperGlueRenderer extends EntityRenderer<SuperGlueEntity> { public class SuperGlueRenderer extends EntityRenderer<SuperGlueEntity> {
private ResourceLocation regular = Create.asResource("textures/entity/super_glue/slime.png");
private float[] insideQuad;
private float[] outsideQuad;
public SuperGlueRenderer(EntityRendererProvider.Context context) { public SuperGlueRenderer(EntityRendererProvider.Context context) {
super(context); super(context);
initQuads();
} }
@Override @Override
public ResourceLocation getTextureLocation(SuperGlueEntity entity) { public ResourceLocation getTextureLocation(SuperGlueEntity entity) {
return regular; return null;
} }
@Override @Override
public boolean shouldRender(SuperGlueEntity entity, Frustum frustum, double x, double y, double z) { public boolean shouldRender(SuperGlueEntity entity, Frustum frustum, double x, double y, double z) {
if (super.shouldRender(entity, frustum, x, y, z)) {
Player player = Minecraft.getInstance().player;
boolean visible = entity.isVisible();
boolean holdingGlue = AllItems.SUPER_GLUE.isIn(player.getMainHandItem())
|| AllItems.SUPER_GLUE.isIn(player.getOffhandItem());
if (visible || holdingGlue)
return true;
}
return false; return false;
} }
@Override
public void render(SuperGlueEntity entity, float yaw, float partialTicks, PoseStack ms,
MultiBufferSource buffer, int light) {
super.render(entity, yaw, partialTicks, ms, buffer, light);
VertexConsumer builder = buffer.getBuffer(RenderType.entityCutout(getTextureLocation(entity)));
light = getBrightnessForRender(entity);
Direction face = entity.getFacingDirection();
ms.pushPose();
TransformStack.cast(ms)
.rotateY(AngleHelper.horizontalAngle(face))
.rotateX(AngleHelper.verticalAngle(face));
Pose peek = ms.last();
renderQuad(builder, peek, insideQuad, light, -1);
renderQuad(builder, peek, outsideQuad, light, 1);
ms.popPose();
}
private void initQuads() {
Vec3 diff = Vec3.atLowerCornerOf(Direction.SOUTH.getNormal());
Vec3 extension = diff.normalize()
.scale(1 / 32f - 1 / 128f);
Vec3 plane = VecHelper.axisAlingedPlaneOf(diff);
Axis axis = Direction.getNearest(diff.x, diff.y, diff.z)
.getAxis();
Vec3 start = Vec3.ZERO.subtract(extension);
Vec3 end = Vec3.ZERO.add(extension);
plane = plane.scale(1 / 2f);
Vec3 a1 = plane.add(start);
Vec3 b1 = plane.add(end);
plane = VecHelper.rotate(plane, -90, axis);
Vec3 a2 = plane.add(start);
Vec3 b2 = plane.add(end);
plane = VecHelper.rotate(plane, -90, axis);
Vec3 a3 = plane.add(start);
Vec3 b3 = plane.add(end);
plane = VecHelper.rotate(plane, -90, axis);
Vec3 a4 = plane.add(start);
Vec3 b4 = plane.add(end);
insideQuad = new float[] {
(float) a1.x, (float) a1.y, (float) a1.z, 1, 0,
(float) a2.x, (float) a2.y, (float) a2.z, 1, 1,
(float) a3.x, (float) a3.y, (float) a3.z, 0, 1,
(float) a4.x, (float) a4.y, (float) a4.z, 0, 0,
};
outsideQuad = new float[] {
(float) b4.x, (float) b4.y, (float) b4.z, 0, 0,
(float) b3.x, (float) b3.y, (float) b3.z, 0, 1,
(float) b2.x, (float) b2.y, (float) b2.z, 1, 1,
(float) b1.x, (float) b1.y, (float) b1.z, 1, 0,
};
}
private int getBrightnessForRender(SuperGlueEntity entity) {
BlockPos blockpos = entity.getHangingPosition();
BlockPos blockpos2 = blockpos.relative(entity.getFacingDirection()
.getOpposite());
Level world = entity.getCommandSenderWorld();
int light = world.isLoaded(blockpos) ? LevelRenderer.getLightColor(world, blockpos) : 15;
int light2 = world.isLoaded(blockpos2) ? LevelRenderer.getLightColor(world, blockpos2) : 15;
return Math.max(light, light2);
}
// Vertex format: pos x, pos y, pos z, u, v
private void renderQuad(VertexConsumer builder, Pose matrix, float[] data, int light, float normalZ) {
for (int i = 0; i < 4; i++) {
builder.vertex(matrix.pose(), data[5 * i], data[5 * i + 1], data[5 * i + 2])
.color(255, 255, 255, 255)
.uv(data[5 * i + 3], data[5 * i + 4])
.overlayCoords(OverlayTexture.NO_OVERLAY)
.uv2(light)
.normal(matrix.normal(), 0.0f, 0.0f, normalZ)
.endVertex();
}
}
} }

View file

@ -1,42 +1,53 @@
package com.simibubi.create.content.contraptions.components.structureMovement.glue; package com.simibubi.create.content.contraptions.components.structureMovement.glue;
import java.util.List; import java.util.List;
import java.util.Optional;
import java.util.Set; import java.util.Set;
import com.google.common.base.Objects; import com.google.common.base.Objects;
import com.simibubi.create.AllSoundEvents;
import com.simibubi.create.AllSpecialTextures; import com.simibubi.create.AllSpecialTextures;
import com.simibubi.create.CreateClient; import com.simibubi.create.CreateClient;
import com.simibubi.create.content.contraptions.components.structureMovement.chassis.AbstractChassisBlock; import com.simibubi.create.content.contraptions.components.structureMovement.chassis.AbstractChassisBlock;
import com.simibubi.create.foundation.networking.AllPackets; import com.simibubi.create.foundation.networking.AllPackets;
import com.simibubi.create.foundation.utility.BlockFace;
import com.simibubi.create.foundation.utility.Lang; import com.simibubi.create.foundation.utility.Lang;
import com.simibubi.create.foundation.utility.Pair; import com.simibubi.create.foundation.utility.RaycastHelper;
import com.simibubi.create.foundation.utility.outliner.Outline.OutlineParams;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.LocalPlayer; import net.minecraft.client.player.LocalPlayer;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.network.chat.TextComponent;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult; import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.HitResult.Type; import net.minecraft.world.phys.HitResult.Type;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.ForgeMod;
public class SuperGlueSelectionHandler { public class SuperGlueSelectionHandler {
private static final int SUCCESS = 0x68c586; private static final int PASSIVE = 0x4D9162;
private static final int HIGHLIGHT = 0x68c586;
private static final int FAIL = 0xc5b548; private static final int FAIL = 0xc5b548;
private Object clusterOutlineSlot = new Object(); private Object clusterOutlineSlot = new Object();
private Object bbOutlineSlot = new Object(); private Object bbOutlineSlot = new Object();
private int clusterCooldown;
private BlockPos firstPos; private BlockPos firstPos;
private BlockPos hoveredPos; private BlockPos hoveredPos;
private Set<BlockPos> currentCluster; private Set<BlockPos> currentCluster;
private int glueRequired; private int glueRequired;
private SuperGlueEntity selected;
private BlockPos soundSourceForRemoval;
public void tick() { public void tick() {
Minecraft mc = Minecraft.getInstance(); Minecraft mc = Minecraft.getInstance();
LocalPlayer player = mc.player; LocalPlayer player = mc.player;
@ -49,6 +60,51 @@ public class SuperGlueSelectionHandler {
return; return;
} }
if (clusterCooldown > 0) {
if (clusterCooldown == 25)
player.displayClientMessage(TextComponent.EMPTY, true);
CreateClient.OUTLINER.keep(clusterOutlineSlot);
clusterCooldown--;
}
AABB scanArea = player.getBoundingBox()
.inflate(32, 16, 32);
List<SuperGlueEntity> glueNearby = mc.level.getEntitiesOfClass(SuperGlueEntity.class, scanArea);
selected = null;
if (firstPos == null) {
double range = player.getAttribute(ForgeMod.REACH_DISTANCE.get())
.getValue() + 1;
Vec3 traceOrigin = RaycastHelper.getTraceOrigin(player);
Vec3 traceTarget = RaycastHelper.getTraceTarget(player, range, traceOrigin);
double bestDistance = Double.MAX_VALUE;
for (SuperGlueEntity glueEntity : glueNearby) {
Optional<Vec3> clip = glueEntity.getBoundingBox()
.clip(traceOrigin, traceTarget);
if (clip.isEmpty())
continue;
Vec3 vec3 = clip.get();
double distanceToSqr = vec3.distanceToSqr(traceOrigin);
if (distanceToSqr > bestDistance)
continue;
selected = glueEntity;
soundSourceForRemoval = new BlockPos(vec3);
bestDistance = distanceToSqr;
}
for (SuperGlueEntity glueEntity : glueNearby) {
boolean h = clusterCooldown == 0 && glueEntity == selected;
AllSpecialTextures faceTex = h ? AllSpecialTextures.GLUE : null;
CreateClient.OUTLINER.showAABB(glueEntity, glueEntity.getBoundingBox())
.colored(h ? HIGHLIGHT : PASSIVE)
.withFaceTextures(faceTex, faceTex)
.disableNormals()
.lineWidth(h ? 1 / 16f : 1 / 64f);
}
}
HitResult hitResult = mc.hitResult; HitResult hitResult = mc.hitResult;
if (hitResult != null && hitResult.getType() == Type.BLOCK) if (hitResult != null && hitResult.getType() == Type.BLOCK)
hovered = ((BlockHitResult) hitResult).getBlockPos(); hovered = ((BlockHitResult) hitResult).getBlockPos();
@ -83,33 +139,29 @@ public class SuperGlueSelectionHandler {
else if (cancel) else if (cancel)
Lang.sendStatus(player, FAIL, "super_glue.click_to_discard"); Lang.sendStatus(player, FAIL, "super_glue.click_to_discard");
else else
Lang.sendStatus(player, SUCCESS, "super_glue.click_to_confirm"); Lang.sendStatus(player, HIGHLIGHT, "super_glue.click_to_confirm");
if (currentSelectionBox != null)
CreateClient.OUTLINER.showAABB(bbOutlineSlot, currentSelectionBox)
.colored(canReach && canAfford && !cancel ? HIGHLIGHT : FAIL)
.withFaceTextures(AllSpecialTextures.GLUE, AllSpecialTextures.GLUE)
.disableNormals()
.lineWidth(1 / 16f);
CreateClient.OUTLINER.showCluster(clusterOutlineSlot, currentCluster) CreateClient.OUTLINER.showCluster(clusterOutlineSlot, currentCluster)
.colored(canReach && canAfford && !cancel ? SUCCESS : FAIL) .colored(0x4D9162)
.withFaceTextures(AllSpecialTextures.CHECKERED, AllSpecialTextures.HIGHLIGHT_CHECKERED)
.lineWidth(1 / 16f);
}
if (currentSelectionBox != null) {
OutlineParams params =
firstPos == null ? CreateClient.OUTLINER.showAABB(bbOutlineSlot, currentSelectionBox)
: CreateClient.OUTLINER.chaseAABB(bbOutlineSlot, currentSelectionBox);
params.colored(0x111111)
.disableNormals() .disableNormals()
.lineWidth(1 / 128f); .lineWidth(1 / 64f);
} }
return; return;
} }
hoveredPos = hovered; hoveredPos = hovered;
Pair<Set<BlockPos>, List<BlockFace>> pair = Set<BlockPos> cluster = SuperGlueSelectionHelper.searchGlueGroup(mc.level, firstPos, hoveredPos, true);
SuperGlueSelectionHelper.searchGlueGroup(mc.level, firstPos, hoveredPos); currentCluster = cluster;
glueRequired = 1;
currentCluster = pair == null ? null : pair.getFirst();
glueRequired = pair == null ? 0
: pair.getSecond()
.size();
} }
private boolean isGlue(ItemStack stack) { private boolean isGlue(ItemStack stack) {
@ -120,7 +172,7 @@ public class SuperGlueSelectionHandler {
return firstPos == null || hoveredPos == null ? null : new AABB(firstPos, hoveredPos).expandTowards(1, 1, 1); return firstPos == null || hoveredPos == null ? null : new AABB(firstPos, hoveredPos).expandTowards(1, 1, 1);
} }
public boolean onMouseInput() { public boolean onMouseInput(boolean attack) {
Minecraft mc = Minecraft.getInstance(); Minecraft mc = Minecraft.getInstance();
LocalPlayer player = mc.player; LocalPlayer player = mc.player;
ClientLevel level = mc.level; ClientLevel level = mc.level;
@ -128,6 +180,15 @@ public class SuperGlueSelectionHandler {
if (!isGlue(player.getMainHandItem())) if (!isGlue(player.getMainHandItem()))
return false; return false;
if (attack) {
if (selected == null)
return false;
AllPackets.channel.sendToServer(new SuperGlueRemovalPacket(selected.getId(), soundSourceForRemoval));
selected = null;
clusterCooldown = 0;
return true;
}
if (player.isSteppingCarefully()) { if (player.isSteppingCarefully()) {
if (firstPos != null) { if (firstPos != null) {
discard(); discard();
@ -139,7 +200,9 @@ public class SuperGlueSelectionHandler {
if (hoveredPos == null) if (hoveredPos == null)
return false; return false;
Direction face = null;
if (mc.hitResult instanceof BlockHitResult bhr) { if (mc.hitResult instanceof BlockHitResult bhr) {
face = bhr.getDirection();
BlockState blockState = level.getBlockState(hoveredPos); BlockState blockState = level.getBlockState(hoveredPos);
if (blockState.getBlock()instanceof AbstractChassisBlock cb) if (blockState.getBlock()instanceof AbstractChassisBlock cb)
if (cb.getGlueableSide(blockState, bhr.getDirection()) != null) if (cb.getGlueableSide(blockState, bhr.getDirection()) != null)
@ -158,7 +221,11 @@ public class SuperGlueSelectionHandler {
} }
firstPos = hoveredPos; firstPos = hoveredPos;
if (face != null)
SuperGlueItem.spawnParticles(level, firstPos, face, true);
Lang.sendStatus(player, "super_glue.first_pos"); Lang.sendStatus(player, "super_glue.first_pos");
AllSoundEvents.SLIME_ADDED.playAt(level, firstPos, 0.5F, 0.85F, false);
level.playSound(player, firstPos, SoundEvents.ITEM_FRAME_ADD_ITEM, SoundSource.BLOCKS, 0.75f, 1);
return true; return true;
} }
@ -167,13 +234,25 @@ public class SuperGlueSelectionHandler {
currentCluster = null; currentCluster = null;
firstPos = null; firstPos = null;
Lang.sendStatus(player, "super_glue.abort"); Lang.sendStatus(player, "super_glue.abort");
clusterCooldown = 0;
} }
public void confirm() { public void confirm() {
LocalPlayer player = Minecraft.getInstance().player; LocalPlayer player = Minecraft.getInstance().player;
AllPackets.channel.sendToServer(new SuperGlueSelectionPacket(firstPos, hoveredPos)); AllPackets.channel.sendToServer(new SuperGlueSelectionPacket(firstPos, hoveredPos));
AllSoundEvents.SLIME_ADDED.playAt(player.level, hoveredPos, 0.5F, 0.95F, false);
player.level.playSound(player, hoveredPos, SoundEvents.ITEM_FRAME_ADD_ITEM, SoundSource.BLOCKS, 0.75f, 1);
if (currentCluster != null)
CreateClient.OUTLINER.showCluster(clusterOutlineSlot, currentCluster)
.colored(0xB5F2C6)
.withFaceTextures(AllSpecialTextures.GLUE, AllSpecialTextures.HIGHLIGHT_CHECKERED)
.disableNormals()
.lineWidth(1 / 24f);
discard(); discard();
Lang.sendStatus(player, "super_glue.sucess"); Lang.sendStatus(player, "super_glue.sucess");
clusterCooldown = 40;
} }
} }

View file

@ -3,13 +3,10 @@ package com.simibubi.create.content.contraptions.components.structureMovement.gl
import java.util.ArrayList; import java.util.ArrayList;
import java.util.HashSet; import java.util.HashSet;
import java.util.List; import java.util.List;
import java.util.Map;
import java.util.Set; import java.util.Set;
import com.simibubi.create.content.contraptions.components.structureMovement.BlockMovementChecks; import com.simibubi.create.content.contraptions.components.structureMovement.BlockMovementChecks;
import com.simibubi.create.foundation.utility.BlockFace;
import com.simibubi.create.foundation.utility.Iterate; import com.simibubi.create.foundation.utility.Iterate;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction; import net.minecraft.core.Direction;
@ -22,17 +19,16 @@ import net.minecraft.world.phys.Vec3;
public class SuperGlueSelectionHelper { public class SuperGlueSelectionHelper {
public static Pair<Set<BlockPos>, List<BlockFace>> searchGlueGroup(Level level, BlockPos startPos, public static Set<BlockPos> searchGlueGroup(Level level, BlockPos startPos, BlockPos endPos, boolean includeOther) {
BlockPos endPos) {
if (endPos == null || startPos == null) if (endPos == null || startPos == null)
return null; return null;
AABB bb = new AABB(startPos, endPos).expandTowards(1, 1, 1); AABB bb = SuperGlueEntity.span(startPos, endPos);
List<BlockPos> frontier = new ArrayList<>(); List<BlockPos> frontier = new ArrayList<>();
Set<BlockPos> visited = new HashSet<>(); Set<BlockPos> visited = new HashSet<>();
Set<BlockPos> attached = new HashSet<>(); Set<BlockPos> attached = new HashSet<>();
List<BlockFace> glue = new ArrayList<>(); Set<SuperGlueEntity> cachedOther = new HashSet<>();
visited.add(startPos); visited.add(startPos);
frontier.add(startPos); frontier.add(startPos);
@ -41,11 +37,10 @@ public class SuperGlueSelectionHelper {
BlockPos currentPos = frontier.remove(0); BlockPos currentPos = frontier.remove(0);
attached.add(currentPos); attached.add(currentPos);
Map<Direction, SuperGlueEntity> gatheredGlue = SuperGlueHandler.gatherGlue(level, currentPos);
for (Direction d : Iterate.directions) { for (Direction d : Iterate.directions) {
BlockPos offset = currentPos.relative(d); BlockPos offset = currentPos.relative(d);
boolean gluePresent = gatheredGlue.containsKey(d); boolean gluePresent = includeOther && SuperGlueEntity.isGlued(level, currentPos, d, cachedOther);
boolean alreadySticky = SuperGlueEntity.isSideSticky(level, currentPos, d) boolean alreadySticky = includeOther && SuperGlueEntity.isSideSticky(level, currentPos, d)
|| SuperGlueEntity.isSideSticky(level, offset, d.getOpposite()); || SuperGlueEntity.isSideSticky(level, offset, d.getOpposite());
if (!alreadySticky && !gluePresent && !bb.contains(Vec3.atCenterOf(offset))) if (!alreadySticky && !gluePresent && !bb.contains(Vec3.atCenterOf(offset)))
@ -56,20 +51,15 @@ public class SuperGlueSelectionHelper {
|| !SuperGlueEntity.isValidFace(level, offset, d.getOpposite())) || !SuperGlueEntity.isValidFace(level, offset, d.getOpposite()))
continue; continue;
boolean glueNecessary = !gluePresent && !alreadySticky; if (visited.add(offset))
if (visited.add(offset)) {
frontier.add(offset); frontier.add(offset);
if (glueNecessary)
glue.add(new BlockFace(offset, d));
}
} }
} }
if (attached.size() < 2 && attached.contains(endPos)) if (attached.size() < 2 && attached.contains(endPos))
return null; return null;
return Pair.of(attached, glue); return attached;
} }
public static boolean collectGlueFromInventory(Player player, int requiredAmount, boolean simulate) { public static boolean collectGlueFromInventory(Player player, int requiredAmount, boolean simulate) {

View file

@ -1,17 +1,14 @@
package com.simibubi.create.content.contraptions.components.structureMovement.glue; package com.simibubi.create.content.contraptions.components.structureMovement.glue;
import java.util.List;
import java.util.Set; import java.util.Set;
import java.util.function.Supplier; import java.util.function.Supplier;
import com.simibubi.create.Create;
import com.simibubi.create.foundation.networking.SimplePacketBase; import com.simibubi.create.foundation.networking.SimplePacketBase;
import com.simibubi.create.foundation.utility.BlockFace;
import com.simibubi.create.foundation.utility.Pair;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.network.FriendlyByteBuf; import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.server.level.ServerPlayer; import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.Vec3;
import net.minecraftforge.common.ForgeMod; import net.minecraftforge.common.ForgeMod;
import net.minecraftforge.network.NetworkEvent.Context; import net.minecraftforge.network.NetworkEvent.Context;
@ -50,19 +47,19 @@ public class SuperGlueSelectionPacket extends SimplePacketBase {
if (!to.closerThan(from, 25)) if (!to.closerThan(from, 25))
return; return;
Pair<Set<BlockPos>, List<BlockFace>> group = Set<BlockPos> group = SuperGlueSelectionHelper.searchGlueGroup(player.level, from, to, false);
SuperGlueSelectionHelper.searchGlueGroup(player.level, from, to);
if (group == null) if (group == null)
return; return;
if (!group.getFirst() if (!group.contains(to))
.contains(to))
return; return;
List<BlockFace> glue = group.getSecond(); if (!SuperGlueSelectionHelper.collectGlueFromInventory(player, 1, true))
if (!SuperGlueSelectionHelper.collectGlueFromInventory(player, glue.size(), true))
return; return;
SuperGlueSelectionHelper.collectGlueFromInventory(player, glue.size(), false); AABB bb = SuperGlueEntity.span(from, to);
Create.GLUE_QUEUE.add(player.level, glue); SuperGlueSelectionHelper.collectGlueFromInventory(player, 1, false);
SuperGlueEntity entity = new SuperGlueEntity(player.level, bb);
player.level.addFreshEntity(entity);
entity.spawnParticles();
}); });
ctx.setPacketHandled(true); ctx.setPacketHandled(true);
} }

View file

@ -1,6 +0,0 @@
@ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault
package com.simibubi.create.content.contraptions.components.structureMovement.glue;
import javax.annotation.ParametersAreNonnullByDefault;
import net.minecraft.MethodsReturnNonnullByDefault;

View file

@ -122,7 +122,6 @@ public class CommonEvents {
LinkedControllerServerHandler.tick(world); LinkedControllerServerHandler.tick(world);
ControlsServerHandler.tick(world); ControlsServerHandler.tick(world);
Create.RAILWAYS.tick(world); Create.RAILWAYS.tick(world);
Create.GLUE_QUEUE.tick(world);
} }
@SubscribeEvent @SubscribeEvent

View file

@ -8,6 +8,7 @@ import com.simibubi.create.content.logistics.trains.track.CurvedTrackInteraction
import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringHandler; import com.simibubi.create.foundation.tileEntity.behaviour.filtering.FilteringHandler;
import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollValueHandler; import com.simibubi.create.foundation.tileEntity.behaviour.scrollvalue.ScrollValueHandler;
import net.minecraft.client.KeyMapping;
import net.minecraft.client.Minecraft; import net.minecraft.client.Minecraft;
import net.minecraftforge.api.distmarker.Dist; import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.client.event.InputEvent.ClickInputEvent; import net.minecraftforge.client.event.InputEvent.ClickInputEvent;
@ -68,12 +69,14 @@ public class InputEvents {
return; return;
} }
if (event.getKeyMapping() == mc.options.keyUse) { KeyMapping key = event.getKeyMapping();
if (CreateClient.GLUE_HANDLER.onMouseInput())
if (key == mc.options.keyUse || key == mc.options.keyAttack) {
if (CreateClient.GLUE_HANDLER.onMouseInput(key == mc.options.keyAttack))
event.setCanceled(true); event.setCanceled(true);
} }
if (event.getKeyMapping() == mc.options.keyPickItem) { if (key == mc.options.keyPickItem) {
if (ToolboxHandlerClient.onPickItem()) if (ToolboxHandlerClient.onPickItem())
event.setCanceled(true); event.setCanceled(true);
return; return;

View file

@ -92,19 +92,19 @@ public class CloneCommand {
List<SuperGlueEntity> glue = world.getEntitiesOfClass(SuperGlueEntity.class, AABB.of(sourceArea)); List<SuperGlueEntity> glue = world.getEntitiesOfClass(SuperGlueEntity.class, AABB.of(sourceArea));
List<Pair<BlockPos, Direction>> newGlue = Lists.newArrayList(); List<Pair<BlockPos, Direction>> newGlue = Lists.newArrayList();
for (SuperGlueEntity g : glue) { // for (SuperGlueEntity g : glue) {TODO
BlockPos pos = g.getHangingPosition(); // BlockPos pos = g.getHangingPosition();
Direction direction = g.getFacingDirection(); // Direction direction = g.getFacingDirection();
newGlue.add(Pair.of(pos.offset(diffToTarget), direction)); // newGlue.add(Pair.of(pos.offset(diffToTarget), direction));
} // }
//
for (Pair<BlockPos, Direction> p : newGlue) { // for (Pair<BlockPos, Direction> p : newGlue) {
SuperGlueEntity g = new SuperGlueEntity(world, p.getFirst(), p.getSecond()); // SuperGlueEntity g = new SuperGlueEntity(world, p.getFirst(), p.getSecond());
if (g.onValidSurface()) { // if (g.onValidSurface()) {
world.addFreshEntity(g); // world.addFreshEntity(g);
gluePastes++; // gluePastes++;
} // }
} // }
return gluePastes; return gluePastes;
} }

View file

@ -7,26 +7,26 @@ import net.minecraft.commands.CommandSourceStack;
import net.minecraft.commands.Commands; import net.minecraft.commands.Commands;
import net.minecraft.commands.arguments.coordinates.BlockPosArgument; import net.minecraft.commands.arguments.coordinates.BlockPosArgument;
import net.minecraft.core.BlockPos; import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.server.level.ServerLevel; import net.minecraft.server.level.ServerLevel;
public class GlueCommand { public class GlueCommand {
public static ArgumentBuilder<CommandSourceStack, ?> register() { public static ArgumentBuilder<CommandSourceStack, ?> register() {
return Commands.literal("glue") return Commands.literal("glue")
.requires(cs -> cs.hasPermission(2)) .requires(cs -> cs.hasPermission(2))
.then(Commands.argument("pos", BlockPosArgument.blockPos()) .then(Commands.argument("from", BlockPosArgument.blockPos())
//.then(Commands.argument("direction", EnumArgument.enumArgument(Direction.class)) .then(Commands.argument("to", BlockPosArgument.blockPos())
.executes(ctx -> { .executes(ctx -> {
BlockPos pos = BlockPosArgument.getLoadedBlockPos(ctx, "pos"); BlockPos from = BlockPosArgument.getLoadedBlockPos(ctx, "from");
BlockPos to = BlockPosArgument.getLoadedBlockPos(ctx, "to");
ServerLevel world = ctx.getSource().getLevel(); ServerLevel world = ctx.getSource()
SuperGlueEntity entity = new SuperGlueEntity(world, pos, Direction.UP); .getLevel();
entity.playPlaceSound(); SuperGlueEntity entity = new SuperGlueEntity(world, SuperGlueEntity.span(from, to));
world.addFreshEntity(entity); entity.playPlaceSound();
world.addFreshEntity(entity);
return 1; return 1;
})); })));
} }
} }

View file

@ -12,6 +12,7 @@ import com.simibubi.create.content.contraptions.components.structureMovement.Con
import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionStallPacket; import com.simibubi.create.content.contraptions.components.structureMovement.ContraptionStallPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.gantry.GantryContraptionUpdatePacket; import com.simibubi.create.content.contraptions.components.structureMovement.gantry.GantryContraptionUpdatePacket;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.GlueEffectPacket; import com.simibubi.create.content.contraptions.components.structureMovement.glue.GlueEffectPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueRemovalPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueSelectionPacket; import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueSelectionPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsInputPacket; import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsInputPacket;
import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsStopControllingPacket; import com.simibubi.create.content.contraptions.components.structureMovement.interaction.controls.ControlsStopControllingPacket;
@ -130,6 +131,7 @@ public enum AllPackets {
SELECT_CURVED_TRACK(CurvedTrackSelectionPacket.class, CurvedTrackSelectionPacket::new, PLAY_TO_SERVER), SELECT_CURVED_TRACK(CurvedTrackSelectionPacket.class, CurvedTrackSelectionPacket::new, PLAY_TO_SERVER),
PLACE_CURVED_TRACK(PlaceExtendedCurvePacket.class, PlaceExtendedCurvePacket::new, PLAY_TO_SERVER), PLACE_CURVED_TRACK(PlaceExtendedCurvePacket.class, PlaceExtendedCurvePacket::new, PLAY_TO_SERVER),
GLUE_IN_AREA(SuperGlueSelectionPacket.class, SuperGlueSelectionPacket::new, PLAY_TO_SERVER), GLUE_IN_AREA(SuperGlueSelectionPacket.class, SuperGlueSelectionPacket::new, PLAY_TO_SERVER),
GLUE_REMOVED(SuperGlueRemovalPacket.class, SuperGlueRemovalPacket::new, PLAY_TO_SERVER),
// Server to Client // Server to Client
SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new, PLAY_TO_CLIENT), SYMMETRY_EFFECT(SymmetryEffectPacket.class, SymmetryEffectPacket::new, PLAY_TO_CLIENT),

View file

@ -13,7 +13,6 @@ import com.simibubi.create.content.contraptions.base.KineticBlock;
import com.simibubi.create.content.contraptions.base.KineticTileEntity; import com.simibubi.create.content.contraptions.base.KineticTileEntity;
import com.simibubi.create.content.contraptions.components.crafter.ConnectedInputHandler; import com.simibubi.create.content.contraptions.components.crafter.ConnectedInputHandler;
import com.simibubi.create.content.contraptions.components.crafter.MechanicalCrafterTileEntity; import com.simibubi.create.content.contraptions.components.crafter.MechanicalCrafterTileEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueEntity;
import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueItem; import com.simibubi.create.content.contraptions.components.structureMovement.glue.SuperGlueItem;
import com.simibubi.create.content.contraptions.fluids.PumpTileEntity; import com.simibubi.create.content.contraptions.fluids.PumpTileEntity;
import com.simibubi.create.content.contraptions.particle.RotationIndicatorParticleData; import com.simibubi.create.content.contraptions.particle.RotationIndicatorParticleData;
@ -661,11 +660,6 @@ public class SceneBuilder {
}); });
} }
public ElementLink<EntityElement> createGlueEntity(BlockPos pos, Direction face) {
effects.superGlue(pos, face, false);
return createEntity(world -> new SuperGlueEntity(world, pos, face.getOpposite()));
}
public void createItemOnBeltLike(BlockPos location, Direction insertionSide, ItemStack stack) { public void createItemOnBeltLike(BlockPos location, Direction insertionSide, ItemStack stack) {
addInstruction(scene -> { addInstruction(scene -> {
PonderWorld world = scene.getWorld(); PonderWorld world = scene.getWorld();

View file

@ -253,7 +253,7 @@ public class ChassisScenes {
scene.overlay.showControls(new InputWindowElement(glueSurface, Pointing.DOWN).rightClick() scene.overlay.showControls(new InputWindowElement(glueSurface, Pointing.DOWN).rightClick()
.withItem(AllItems.SUPER_GLUE.asStack()), 30); .withItem(AllItems.SUPER_GLUE.asStack()), 30);
scene.idle(7); scene.idle(7);
ElementLink<EntityElement> glueEntity = scene.world.createGlueEntity(chassisPos.west(), Direction.NORTH); // ElementLink<EntityElement> glueEntity = scene.world.createGlueEntity(chassisPos.west(), Direction.NORTH);TODO
scene.idle(20); scene.idle(20);
ElementLink<WorldSectionElement> gluedPlank = ElementLink<WorldSectionElement> gluedPlank =
scene.world.showIndependentSection(util.select.position(3, 3, 1), Direction.SOUTH); scene.world.showIndependentSection(util.select.position(3, 3, 1), Direction.SOUTH);
@ -262,7 +262,7 @@ public class ChassisScenes {
scene.effects.superGlue(chassisPos.west(), Direction.NORTH, true); scene.effects.superGlue(chassisPos.west(), Direction.NORTH, true);
scene.idle(20); scene.idle(20);
scene.world.modifyEntity(glueEntity, Entity::discard); // scene.world.modifyEntity(glueEntity, Entity::discard);
scene.world.hideIndependentSection(glassSection, Direction.UP); scene.world.hideIndependentSection(glassSection, Direction.UP);
scene.world.hideIndependentSection(gluedPlank, Direction.UP); scene.world.hideIndependentSection(gluedPlank, Direction.UP);
scene.world.hideIndependentSection(topGlassSection, Direction.UP); scene.world.hideIndependentSection(topGlassSection, Direction.UP);
@ -499,7 +499,7 @@ public class ChassisScenes {
scene.overlay.showControls(new InputWindowElement(blockSurface, Pointing.DOWN).rightClick() scene.overlay.showControls(new InputWindowElement(blockSurface, Pointing.DOWN).rightClick()
.withItem(AllItems.SUPER_GLUE.asStack()), 40); .withItem(AllItems.SUPER_GLUE.asStack()), 40);
scene.idle(7); scene.idle(7);
ElementLink<EntityElement> glueEntity = scene.world.createGlueEntity(central, Direction.NORTH); // ElementLink<EntityElement> glueEntity = scene.world.createGlueEntity(central, Direction.NORTH);TODO
scene.idle(10); scene.idle(10);
scene.overlay.showText(60) scene.overlay.showText(60)
.pointAt(blockSurface) .pointAt(blockSurface)
@ -510,7 +510,7 @@ public class ChassisScenes {
scene.world.glueBlockOnto(central.north(), Direction.SOUTH, plank); scene.world.glueBlockOnto(central.north(), Direction.SOUTH, plank);
scene.idle(20); scene.idle(20);
scene.world.modifyEntity(glueEntity, Entity::discard); // scene.world.modifyEntity(glueEntity, Entity::discard);
BlockPos bearingPos = util.grid.at(2, 1, 2); BlockPos bearingPos = util.grid.at(2, 1, 2);
scene.world.configureCenterOfRotation(plank, util.vector.centerOf(bearingPos)); scene.world.configureCenterOfRotation(plank, util.vector.centerOf(bearingPos));
@ -553,13 +553,13 @@ public class ChassisScenes {
scene.world.rotateSection(plank, 0, 360, 0, 80); scene.world.rotateSection(plank, 0, 360, 0, 80);
scene.idle(90); scene.idle(90);
glueEntity = scene.world.createGlueEntity(central, Direction.UP); // glueEntity = scene.world.createGlueEntity(central, Direction.UP);TODO
scene.world.destroyBlock(central.above()); scene.world.destroyBlock(central.above());
scene.idle(20); scene.idle(20);
scene.addKeyframe(); scene.addKeyframe();
scene.overlay.showControls(new InputWindowElement(util.vector.topOf(central), Pointing.DOWN).leftClick(), 40); scene.overlay.showControls(new InputWindowElement(util.vector.topOf(central), Pointing.DOWN).leftClick(), 40);
scene.idle(7); scene.idle(7);
scene.world.modifyEntity(glueEntity, Entity::discard); // scene.world.modifyEntity(glueEntity, Entity::discard);
scene.effects.superGlue(central, Direction.UP, false); scene.effects.superGlue(central, Direction.UP, false);
scene.idle(10); scene.idle(10);
scene.overlay.showText(60) scene.overlay.showText(60)

View file

@ -55,27 +55,47 @@ public class BlockClusterOutline extends Outline {
}); });
} }
static Vec3 xyz = new Vec3(-.5, -.5, -.5);
static Vec3 Xyz = new Vec3(.5, -.5, -.5);
static Vec3 xYz = new Vec3(-.5, .5, -.5);
static Vec3 XYz = new Vec3(.5, .5, -.5);
static Vec3 xyZ = new Vec3(-.5, -.5, .5);
static Vec3 XyZ = new Vec3(.5, -.5, .5);
static Vec3 xYZ = new Vec3(-.5, .5, .5);
static Vec3 XYZ = new Vec3(.5, .5, .5);
protected void renderBlockFace(PoseStack ms, VertexConsumer builder, BlockPos pos, Direction face) { protected void renderBlockFace(PoseStack ms, VertexConsumer builder, BlockPos pos, Direction face) {
Vec3 center = VecHelper.getCenterOf(pos); Vec3 center = VecHelper.getCenterOf(pos);
Vec3 offset = Vec3.atLowerCornerOf(face.getNormal()); Vec3 offset = Vec3.atLowerCornerOf(face.getNormal());
Vec3 plane = VecHelper.axisAlingedPlaneOf(offset); offset = offset.scale(1 / 128d);
Axis axis = face.getAxis(); center = center.add(offset);
offset = offset.scale(1 / 2f + 1 / 128d); ms.pushPose();
plane = plane.scale(1 / 2f) ms.translate(center.x, center.y, center.z);
.add(offset);
int deg = face.getAxisDirection() switch (face) {
.getStep() * 90; case DOWN:
Vec3 a1 = plane.add(center); putQuad(ms, builder, xyz, Xyz, XyZ, xyZ, face);
plane = VecHelper.rotate(plane, deg, axis); break;
Vec3 a2 = plane.add(center); case EAST:
plane = VecHelper.rotate(plane, deg, axis); putQuad(ms, builder, XYz, XYZ, XyZ, Xyz, face);
Vec3 a3 = plane.add(center); break;
plane = VecHelper.rotate(plane, deg, axis); case NORTH:
Vec3 a4 = plane.add(center); putQuad(ms, builder, xYz, XYz, Xyz, xyz, face);
break;
case SOUTH:
putQuad(ms, builder, XYZ, xYZ, xyZ, XyZ, face);
break;
case UP:
putQuad(ms, builder, xYZ, XYZ, XYz, xYz, face);
break;
case WEST:
putQuad(ms, builder, xYZ, xYz, xyz, xyZ, face);
default:
break;
}
putQuad(ms, builder, a1, a2, a3, a4, face); ms.popPose();
} }
private static class Cluster { private static class Cluster {

Binary file not shown.

After

Width:  |  Height:  |  Size: 275 B