From 7dd29e9ffde9aa196299e880f4a48b634be35435 Mon Sep 17 00:00:00 2001 From: simibubi <31564874+simibubi@users.noreply.github.com> Date: Thu, 13 Feb 2020 00:12:05 +0100 Subject: [PATCH] Portable Deployers - Server speed sync no longer goes nuts when server speed increased from low tps - Flimsy attempts at better syncing a bearings' current angle - Portable Storage abilities are (for now) available to the Barrel only - Deployers can now be mounted on moving structures and will activate at every visited block position --- .../utility/ServerSpeedProvider.java | 2 +- .../components/contraptions/Contraption.java | 15 +- .../contraptions/ContraptionEntity.java | 16 +- .../contraptions/ContraptionRenderer.java | 19 +- .../contraptions/MountedStorage.java | 12 +- .../contraptions/MovementBehaviour.java | 12 + .../contraptions/MovementContext.java | 15 +- .../bearing/MechanicalBearingTileEntity.java | 33 +- .../mounted/CartAssemblerBlock.java | 2 +- .../piston/MechanicalPistonTileEntity.java | 3 +- .../components/deployer/DeployerBlock.java | 26 +- .../deployer/DeployerFakePlayer.java | 21 +- .../components/deployer/DeployerHandler.java | 263 +++++++++++++ .../deployer/DeployerMovementBehaviour.java | 175 +++++++++ .../deployer/DeployerTileEntity.java | 355 ++++-------------- .../deployer/DeployerTileEntityRenderer.java | 50 ++- .../block/inventories/FlexcrateBlock.java | 3 +- .../logistics/item/filter/FilterItem.java | 3 + 18 files changed, 681 insertions(+), 344 deletions(-) create mode 100644 src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerHandler.java create mode 100644 src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerMovementBehaviour.java diff --git a/src/main/java/com/simibubi/create/foundation/utility/ServerSpeedProvider.java b/src/main/java/com/simibubi/create/foundation/utility/ServerSpeedProvider.java index e243cea21..176d824d8 100644 --- a/src/main/java/com/simibubi/create/foundation/utility/ServerSpeedProvider.java +++ b/src/main/java/com/simibubi/create/foundation/utility/ServerSpeedProvider.java @@ -71,7 +71,7 @@ public class ServerSpeedProvider { return; } float target = ((float) getSyncInterval()) / Math.max(clientTimer, 1); - modifier.target(target); + modifier.target(Math.min(target, 1)); clientTimer = 0; }); diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/Contraption.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/Contraption.java index 464d22a5a..4ebda5c84 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/Contraption.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/Contraption.java @@ -150,17 +150,15 @@ public class Contraption { return false; for (int limit = 1000; limit > 0; limit--) { - if (frontier.isEmpty()) { - onAssembled(world, pos); + if (frontier.isEmpty()) return true; - } if (!moveBlock(world, frontier.remove(0), direction, frontier, visited)) return false; } return false; } - protected void onAssembled(World world, BlockPos pos) { + public void gatherStoredItems() { List list = storage.values().stream().map(MountedStorage::getItemHandler).collect(Collectors.toList()); inventory = new CombinedInvWrapper(Arrays.copyOf(list.toArray(), list.size(), IItemHandlerModifiable[].class)); @@ -504,7 +502,7 @@ public class Contraption { nbt.getList("Actors", 10).forEach(c -> { CompoundNBT comp = (CompoundNBT) c; BlockInfo info = blocks.get(NBTUtil.readBlockPos(comp.getCompound("Pos"))); - MovementContext context = MovementContext.readNBT(world, comp); + MovementContext context = MovementContext.readNBT(world, info, comp); context.contraption = this; getActors().add(MutablePair.of(info, context)); }); @@ -549,6 +547,7 @@ public class Contraption { for (MutablePair actor : getActors()) { CompoundNBT compound = new CompoundNBT(); compound.put("Pos", NBTUtil.writeBlockPos(actor.left.pos)); + getMovement(actor.left.state).writeExtraData(actor.right); actor.right.writeToNBT(compound); actorsNBT.add(compound); } @@ -591,6 +590,7 @@ public class Contraption { } public void removeBlocksFromWorld(IWorld world, BlockPos offset, BiPredicate customRemoval) { + storage.values().forEach(MountedStorage::empty); for (BlockInfo block : blocks.values()) { BlockPos add = block.pos.add(anchor).add(offset); if (customRemoval.test(add, block.state)) @@ -635,10 +635,9 @@ public class Contraption { public void initActors(World world) { for (MutablePair pair : actors) { - BlockState blockState = pair.left.state; - MovementContext context = new MovementContext(world, blockState); + MovementContext context = new MovementContext(world, pair.left); context.contraption = this; - getMovement(blockState).startMoving(context); + getMovement(pair.left.state).startMoving(context); pair.setRight(context); } } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/ContraptionEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/ContraptionEntity.java index 9af7ae893..63c9fa456 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/ContraptionEntity.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/ContraptionEntity.java @@ -70,6 +70,8 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD this.prevYaw = initialAngle; this.yaw = initialAngle; this.targetYaw = initialAngle; + if (contraption != null) + contraption.gatherStoredItems(); } public ContraptionEntity controlledBy(T controller) { @@ -84,15 +86,15 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD remove(); return; } - + attachToController(); - + Entity e = getRidingEntity(); if (e != null) { Vec3d movementVector = e.getMotion(); Vec3d motion = movementVector.normalize(); if (motion.length() > 0) { - targetYaw = yawFromMotion(motion); + targetYaw = yawFromVector(motion); targetPitch = (float) ((Math.atan(motion.y) * 73.0D) / Math.PI * 180); if (targetYaw < 0) targetYaw += 360; @@ -254,8 +256,12 @@ public class ContraptionEntity extends Entity implements IEntityAdditionalSpawnD disassemble(); } - public static float yawFromMotion(Vec3d motion) { - return (float) ((3 * Math.PI / 2 + Math.atan2(motion.z, motion.x)) / Math.PI * 180); + public static float yawFromVector(Vec3d vec) { + return (float) ((3 * Math.PI / 2 + Math.atan2(vec.z, vec.x)) / Math.PI * 180); + } + + public static float pitchFromVector(Vec3d vec) { + return (float) ((Math.acos(vec.y)) / Math.PI * 180); } public float getYaw(float partialTicks) { diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/ContraptionRenderer.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/ContraptionRenderer.java index 6d62fcf59..5d6d9f8cf 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/ContraptionRenderer.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/ContraptionRenderer.java @@ -69,17 +69,18 @@ public class ContraptionRenderer { context.world = world; BlockInfo blockInfo = actor.getLeft(); - SuperByteBuffer render = Contraption.getMovement(blockInfo.state).renderInContraption(context); - if (render == null) - continue; + for (SuperByteBuffer render : Contraption.getMovement(blockInfo.state).renderListInContraption(context)) { + if (render == null) + continue; - int posX = blockInfo.pos.getX(); - int posY = blockInfo.pos.getY(); - int posZ = blockInfo.pos.getZ(); + int posX = blockInfo.pos.getX(); + int posY = blockInfo.pos.getY(); + int posZ = blockInfo.pos.getZ(); - render.translate(posX, posY, posZ); - transform.accept(render); - render.light((lx, ly, lz) -> getLight(world, lx, ly, lz)).renderInto(buffer); + render.translate(posX, posY, posZ); + transform.accept(render); + render.light((lx, ly, lz) -> getLight(world, lx, ly, lz)).renderInto(buffer); + } } } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/MountedStorage.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/MountedStorage.java index e9eb4ff87..b5573d494 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/MountedStorage.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/MountedStorage.java @@ -1,7 +1,5 @@ package com.simibubi.create.modules.contraptions.components.contraptions; -import com.simibubi.create.AllTileEntities; - import net.minecraft.item.ItemStack; import net.minecraft.nbt.CompoundNBT; import net.minecraft.tileentity.TileEntity; @@ -17,9 +15,14 @@ public class MountedStorage { ItemStackHandler handler; boolean working; + private TileEntity te; public MountedStorage(TileEntity te) { + this.te = te; handler = dummyHandler; + } + + public void empty() { if (te != null) { IItemHandler teHandler = te.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY).orElse(dummyHandler); @@ -68,11 +71,8 @@ public class MountedStorage { if (te == null) return false; TileEntityType type = te.getType(); - if (type == TileEntityType.CHEST || type == TileEntityType.SHULKER_BOX || type == TileEntityType.BARREL) + if (type == TileEntityType.BARREL) return true; - if (type == AllTileEntities.FLEXCRATE.type) - return true; - return false; } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/MovementBehaviour.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/MovementBehaviour.java index 0f3c8bf0a..72c55bd2e 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/MovementBehaviour.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/MovementBehaviour.java @@ -1,5 +1,8 @@ package com.simibubi.create.modules.contraptions.components.contraptions; +import java.util.Arrays; +import java.util.List; + import com.simibubi.create.foundation.utility.SuperByteBuffer; import net.minecraft.entity.item.ItemEntity; @@ -45,8 +48,17 @@ public abstract class MovementBehaviour { return null; } + @OnlyIn(value = Dist.CLIENT) + public List renderListInContraption(MovementContext context) { + return Arrays.asList(renderInContraption(context)); + } + public void stopMoving(MovementContext context) { } + + public void writeExtraData(MovementContext context) { + + } } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/MovementContext.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/MovementContext.java index a6c8f5661..1e7e81e0c 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/MovementContext.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/MovementContext.java @@ -4,9 +4,9 @@ import com.simibubi.create.foundation.utility.VecHelper; import net.minecraft.block.BlockState; import net.minecraft.nbt.CompoundNBT; -import net.minecraft.nbt.NBTUtil; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; +import net.minecraft.world.gen.feature.template.Template.BlockInfo; import net.minecraftforge.common.util.Constants.NBT; public class MovementContext { @@ -17,14 +17,17 @@ public class MovementContext { public Vec3d rotation; public World world; public BlockState state; + public CompoundNBT tileData; public boolean stall; public CompoundNBT data; public Contraption contraption; + public Object temporaryData; - public MovementContext(World world, BlockState state) { + public MovementContext(World world, BlockInfo info) { this.world = world; - this.state = state; + this.state = info.state; + this.tileData = info.nbt; motion = Vec3d.ZERO; relativeMotion = Vec3d.ZERO; @@ -44,9 +47,8 @@ public class MovementContext { return (((int) (length * modifier + 100 * Math.signum(length))) / 100) * 100; } - public static MovementContext readNBT(World world, CompoundNBT nbt) { - BlockState state = NBTUtil.readBlockState(nbt.getCompound("State")); - MovementContext context = new MovementContext(world, state); + public static MovementContext readNBT(World world, BlockInfo info, CompoundNBT nbt) { + MovementContext context = new MovementContext(world, info); context.motion = VecHelper.readNBT(nbt.getList("Motion", NBT.TAG_DOUBLE)); context.relativeMotion = VecHelper.readNBT(nbt.getList("RelativeMotion", NBT.TAG_DOUBLE)); context.rotation = VecHelper.readNBT(nbt.getList("Rotation", NBT.TAG_DOUBLE)); @@ -58,7 +60,6 @@ public class MovementContext { } public CompoundNBT writeToNBT(CompoundNBT nbt) { - nbt.put("State", NBTUtil.writeBlockState(state)); nbt.put("Motion", VecHelper.writeNBT(motion)); nbt.put("RelativeMotion", VecHelper.writeNBT(relativeMotion)); nbt.put("Rotation", VecHelper.writeNBT(rotation)); diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/bearing/MechanicalBearingTileEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/bearing/MechanicalBearingTileEntity.java index cf61ead0a..ffdeb7048 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/bearing/MechanicalBearingTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/bearing/MechanicalBearingTileEntity.java @@ -24,9 +24,12 @@ public class MechanicalBearingTileEntity extends GeneratingKineticTileEntity imp protected boolean assembleNextTick; protected boolean isWindmill; + protected float clientAngleDiff; + public MechanicalBearingTileEntity() { super(AllTileEntities.MECHANICAL_BEARING.type); isWindmill = false; + setLazyTickRate(3); } @Override @@ -92,6 +95,16 @@ public class MechanicalBearingTileEntity extends GeneratingKineticTileEntity imp super.read(tag); } + @Override + public void readClientUpdate(CompoundNBT tag) { + float angleBefore = angle; + super.readClientUpdate(tag); + clientAngleDiff = angle - angleBefore; + if (Math.abs(clientAngleDiff) > 20) + clientAngleDiff = 0; + angle = angleBefore; + } + public float getInterpolatedAngle(float partialTicks) { if (movedContraption != null && movedContraption.isStalled()) partialTicks = 0; @@ -106,8 +119,10 @@ public class MechanicalBearingTileEntity extends GeneratingKineticTileEntity imp public float getAngularSpeed() { float speed = getSpeed() * 3 / 10f; - if (world.isRemote) + if (world.isRemote) { speed *= ServerSpeedProvider.get(); + speed += clientAngleDiff / 3f; + } return speed; } @@ -120,9 +135,9 @@ public class MechanicalBearingTileEntity extends GeneratingKineticTileEntity imp return; if (isWindmill && contraption.getSailBlocks() == 0) return; + contraption.removeBlocksFromWorld(world, BlockPos.ZERO); movedContraption = new ContraptionEntity(world, contraption, 0).controlledBy(this); BlockPos anchor = pos.offset(direction); - contraption.removeBlocksFromWorld(world, BlockPos.ZERO); movedContraption.setPosition(anchor.getX(), anchor.getY(), anchor.getZ()); world.addEntity(movedContraption); @@ -150,17 +165,20 @@ public class MechanicalBearingTileEntity extends GeneratingKineticTileEntity imp public void tick() { super.tick(); + if (world.isRemote) + clientAngleDiff /= 2; + if (running && Contraption.isFrozen()) disassembleConstruct(); if (!world.isRemote && assembleNextTick) { assembleNextTick = false; if (running) { - if (movedContraption != null) - movedContraption.getContraption().stop(world); boolean canDisassemble = Math.abs(angle) < 45 || Math.abs(angle) > 7 * 45; if (speed == 0 && (canDisassemble || movedContraption == null || movedContraption.getContraption().blocks.isEmpty())) { + if (movedContraption != null) + movedContraption.getContraption().stop(world); disassembleConstruct(); } return; @@ -184,6 +202,13 @@ public class MechanicalBearingTileEntity extends GeneratingKineticTileEntity imp applyRotation(); } + @Override + public void lazyTick() { + super.lazyTick(); + if (movedContraption != null && !world.isRemote) + sendData(); + } + private void applyRotation() { if (movedContraption != null) { Axis axis = getBlockState().get(BlockStateProperties.FACING).getAxis(); diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/CartAssemblerBlock.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/CartAssemblerBlock.java index 66557fa6c..811c0ea7f 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/CartAssemblerBlock.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/mounted/CartAssemblerBlock.java @@ -76,7 +76,7 @@ public class CartAssemblerBlock extends AbstractRailBlock { if (contraption == null) return; ContraptionEntity entity = new ContraptionEntity(world, contraption, - ContraptionEntity.yawFromMotion(cart.getMotion())); + ContraptionEntity.yawFromVector(cart.getMotion())); entity.setPosition(pos.getX(), pos.getY(), pos.getZ()); world.addEntity(entity); entity.startRiding(cart); diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/piston/MechanicalPistonTileEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/piston/MechanicalPistonTileEntity.java index 34df9aadf..39d766e9e 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/piston/MechanicalPistonTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/contraptions/piston/MechanicalPistonTileEntity.java @@ -93,7 +93,8 @@ public class MechanicalPistonTileEntity extends KineticTileEntity implements ICo if (!removed) getWorld().setBlockState(pos, getBlockState().with(MechanicalPistonBlock.STATE, PistonState.EXTENDED), 3); - movedContraption.disassemble(); + if (movedContraption != null) + movedContraption.disassemble(); running = false; movedContraption = null; sendData(); diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerBlock.java b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerBlock.java index 8a6305a00..e852ccc20 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerBlock.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerBlock.java @@ -6,9 +6,12 @@ import com.simibubi.create.foundation.utility.AllShapes; import com.simibubi.create.foundation.utility.AngleHelper; import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.modules.contraptions.base.DirectionalAxisKineticBlock; +import com.simibubi.create.modules.contraptions.components.contraptions.IPortableBlock; +import com.simibubi.create.modules.contraptions.components.contraptions.MovementBehaviour; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; +import net.minecraft.block.material.PushReaction; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemUseContext; @@ -25,7 +28,10 @@ import net.minecraft.util.math.shapes.VoxelShape; import net.minecraft.world.IBlockReader; import net.minecraft.world.World; -public class DeployerBlock extends DirectionalAxisKineticBlock implements IWithTileEntity { +public class DeployerBlock extends DirectionalAxisKineticBlock + implements IWithTileEntity, IPortableBlock { + + public static MovementBehaviour MOVEMENT = new DeployerMovementBehaviour(); public DeployerBlock() { super(Properties.from(Blocks.ANDESITE)); @@ -41,6 +47,11 @@ public class DeployerBlock extends DirectionalAxisKineticBlock implements IWithT return true; } + @Override + public PushReaction getPushReaction(BlockState state) { + return PushReaction.PUSH_ONLY; + } + @Override public VoxelShape getShape(BlockState state, IBlockReader worldIn, BlockPos pos, ISelectionContext context) { return AllShapes.SHORT_CASING_12_VOXEL.get(state.get(FACING)); @@ -60,8 +71,12 @@ public class DeployerBlock extends DirectionalAxisKineticBlock implements IWithT public void onReplaced(BlockState state, World worldIn, BlockPos pos, BlockState newState, boolean isMoving) { if (state.hasTileEntity() && state.getBlock() != newState.getBlock()) { withTileEntityDo(worldIn, pos, te -> { - te.player.inventory.dropAllItems(); - te.overflowItems.forEach(itemstack -> te.player.dropItem(itemstack, true, false)); + if (te.player != null && !isMoving) { + te.player.inventory.dropAllItems(); + te.overflowItems.forEach(itemstack -> te.player.dropItem(itemstack, true, false)); + te.player.remove(); + te.player = null; + } }); worldIn.removeTileEntity(pos); @@ -112,4 +127,9 @@ public class DeployerBlock extends DirectionalAxisKineticBlock implements IWithT return new Vec3d(0, yRot, zRot); } + @Override + public MovementBehaviour getMovementBehaviour() { + return MOVEMENT; + } + } diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerFakePlayer.java b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerFakePlayer.java index d8852fece..cefd52f8d 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerFakePlayer.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerFakePlayer.java @@ -3,6 +3,8 @@ package com.simibubi.create.modules.contraptions.components.deployer; import java.util.OptionalInt; import java.util.UUID; +import org.apache.commons.lang3.tuple.Pair; + import com.mojang.authlib.GameProfile; import com.simibubi.create.foundation.utility.Lang; @@ -12,12 +14,14 @@ import net.minecraft.entity.Entity; import net.minecraft.entity.Pose; import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.inventory.container.INamedContainerProvider; +import net.minecraft.item.ItemStack; import net.minecraft.network.IPacket; import net.minecraft.network.NetworkManager; import net.minecraft.network.PacketDirection; import net.minecraft.network.play.ServerPlayNetHandler; import net.minecraft.server.MinecraftServer; import net.minecraft.util.EntityDamageSource; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.Vec3d; import net.minecraft.util.text.ITextComponent; import net.minecraft.util.text.StringTextComponent; @@ -35,14 +39,16 @@ import net.minecraftforge.fml.common.Mod.EventBusSubscriber; public class DeployerFakePlayer extends FakePlayer { private static final NetworkManager NETWORK_MANAGER = new NetworkManager(PacketDirection.CLIENTBOUND); - public static final GameProfile DEPLOYER_PROFILE = new GameProfile( - UUID.fromString("9e2faded-cafe-4ec2-c314-dad129ae971d"), "Deployer"); + public static final GameProfile DEPLOYER_PROFILE = + new GameProfile(UUID.fromString("9e2faded-cafe-4ec2-c314-dad129ae971d"), "Deployer"); + Pair blockBreakingProgress; + ItemStack spawnedItemEffects; public DeployerFakePlayer(ServerWorld world) { super(world, DEPLOYER_PROFILE); connection = new FakePlayNetHandler(world.getServer(), this); } - + @Override public OptionalInt openContainer(INamedContainerProvider container) { return OptionalInt.empty(); @@ -73,7 +79,7 @@ public class DeployerFakePlayer extends FakePlayer { if (event.getEntity() instanceof DeployerFakePlayer) event.setNewHeight(0); } - + @SubscribeEvent public static void deployerCollectsDropsFromKilledEntities(LivingDropsEvent event) { if (!(event.getSource() instanceof EntityDamageSource)) @@ -88,6 +94,13 @@ public class DeployerFakePlayer extends FakePlayer { } } + @Override + public void remove(boolean keepData) { + if (blockBreakingProgress != null && !world.isRemote) + world.sendBlockBreakProgress(getEntityId(), blockBreakingProgress.getKey(), -1); + super.remove(keepData); + } + @SubscribeEvent public static void deployerKillsDoNotSpawnXP(LivingExperienceDropEvent event) { if (event.getAttackingPlayer() instanceof DeployerFakePlayer) diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerHandler.java b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerHandler.java new file mode 100644 index 000000000..bcdabadbe --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerHandler.java @@ -0,0 +1,263 @@ +package com.simibubi.create.modules.contraptions.components.deployer; + +import static net.minecraftforge.eventbus.api.Event.Result.DENY; + +import java.util.ArrayList; +import java.util.List; + +import org.apache.commons.lang3.tuple.Pair; + +import com.google.common.collect.Multimap; +import com.simibubi.create.foundation.utility.WrappedWorld; +import com.simibubi.create.modules.contraptions.components.deployer.DeployerTileEntity.Mode; +import com.simibubi.create.modules.curiosities.tools.SandPaperItem; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.block.material.Material; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.attributes.AttributeModifier; +import net.minecraft.entity.item.ItemEntity; +import net.minecraft.fluid.Fluid; +import net.minecraft.fluid.Fluids; +import net.minecraft.inventory.EquipmentSlotType; +import net.minecraft.item.BlockItem; +import net.minecraft.item.BlockItemUseContext; +import net.minecraft.item.BucketItem; +import net.minecraft.item.FlintAndSteelItem; +import net.minecraft.item.Item; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemUseContext; +import net.minecraft.item.Items; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.ActionResult; +import net.minecraft.util.ActionResultType; +import net.minecraft.util.Direction; +import net.minecraft.util.Hand; +import net.minecraft.util.math.AxisAlignedBB; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.BlockRayTraceResult; +import net.minecraft.util.math.RayTraceContext; +import net.minecraft.util.math.RayTraceContext.BlockMode; +import net.minecraft.util.math.RayTraceContext.FluidMode; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.common.ForgeHooks; +import net.minecraftforge.event.entity.player.PlayerInteractEvent.LeftClickBlock; +import net.minecraftforge.event.entity.player.PlayerInteractEvent.RightClickBlock; +import net.minecraftforge.eventbus.api.Event.Result; + +public class DeployerHandler { + + private static final class ItemUseWorld extends WrappedWorld { + private final Direction face; + private final BlockPos pos; + boolean rayMode = false; + + private ItemUseWorld(World world, Direction face, BlockPos pos) { + super(world); + this.face = face; + this.pos = pos; + } + + @Override + public BlockRayTraceResult rayTraceBlocks(RayTraceContext context) { + rayMode = true; + BlockRayTraceResult rayTraceBlocks = super.rayTraceBlocks(context); + rayMode = false; + return rayTraceBlocks; + } + + @Override + public BlockState getBlockState(BlockPos position) { + if (rayMode && (pos.offset(face.getOpposite(), 3).equals(position) + || pos.offset(face.getOpposite(), 1).equals(position))) + return Blocks.BEDROCK.getDefaultState(); + return world.getBlockState(position); + } + } + + static boolean shouldActivate(ItemStack held, World world, BlockPos targetPos) { + if (held.getItem() instanceof BlockItem) + if (!world.getBlockState(targetPos).getMaterial().isReplaceable()) + return false; + + if (held.getItem() instanceof BucketItem) { + BucketItem bucketItem = (BucketItem) held.getItem(); + Fluid fluid = bucketItem.getFluid(); + if (fluid != Fluids.EMPTY && world.getFluidState(targetPos).getFluid() == fluid) + return false; + } + + return true; + } + + static void activate(DeployerFakePlayer player, Vec3d vec, BlockPos clickedPos, Vec3d extensionVector, Mode mode) { + Multimap attributeModifiers = + player.getHeldItemMainhand().getAttributeModifiers(EquipmentSlotType.MAINHAND); + player.getAttributes().applyAttributeModifiers(attributeModifiers); + activateInner(player, vec, clickedPos, extensionVector, mode); + player.getAttributes().removeAttributeModifiers(attributeModifiers); + } + + private static void activateInner(DeployerFakePlayer player, Vec3d vec, BlockPos clickedPos, Vec3d extensionVector, + Mode mode) { + + Vec3d rayOrigin = vec.add(extensionVector.scale(3 / 2f + 1 / 64f)); + Vec3d rayTarget = vec.add(extensionVector.scale(5 / 2f - 1 / 64f)); + player.setPosition(rayOrigin.x, rayOrigin.y, rayOrigin.z); + BlockPos pos = new BlockPos(vec); + ItemStack stack = player.getHeldItemMainhand(); + Item item = stack.getItem(); + + // Check for entities + final ServerWorld world = player.getServerWorld(); + List entities = world.getEntitiesWithinAABB(LivingEntity.class, new AxisAlignedBB(clickedPos)); + Hand hand = Hand.MAIN_HAND; + if (!entities.isEmpty()) { + LivingEntity entity = entities.get(world.rand.nextInt(entities.size())); + List capturedDrops = new ArrayList<>(); + boolean success = false; + entity.captureDrops(capturedDrops); + + // Use on entity + if (mode == Mode.USE) { + ActionResultType cancelResult = ForgeHooks.onInteractEntity(player, entity, hand); + if (cancelResult == ActionResultType.FAIL) { + entity.captureDrops(null); + return; + } + if (cancelResult == null) { + if (entity.processInitialInteract(player, hand)) + success = true; + else if (stack.interactWithEntity(player, entity, hand)) + success = true; + } + } + + // Punch entity + if (mode == Mode.PUNCH) { + player.resetCooldown(); + player.attackTargetEntityWithCurrentItem(entity); + success = true; + } + + entity.captureDrops(null); + capturedDrops.forEach(e -> player.inventory.placeItemBackInInventory(world, e.getItem())); + if (success) + return; + } + + // Shoot ray + RayTraceContext rayTraceContext = + new RayTraceContext(rayOrigin, rayTarget, BlockMode.OUTLINE, FluidMode.NONE, player); + BlockRayTraceResult result = world.rayTraceBlocks(rayTraceContext); + BlockState clickedState = world.getBlockState(clickedPos); + Direction face = result.getFace(); + if (face == null) + face = Direction.getFacingFromVector(extensionVector.x, extensionVector.y, extensionVector.z).getOpposite(); + + // Left click + if (mode == Mode.PUNCH) { + LeftClickBlock event = ForgeHooks.onLeftClickBlock(player, clickedPos, face); + if (event.isCanceled()) + return; + if (!world.isBlockModifiable(player, clickedPos)) + return; + if (world.extinguishFire(player, clickedPos, face)) + return; + if (clickedState.isAir(world, clickedPos)) { + player.blockBreakingProgress = null; + return; + } + if (event.getUseBlock() != Result.DENY) + clickedState.onBlockClicked(world, clickedPos, player); + if (stack.isEmpty()) + return; + + float progress = clickedState.getPlayerRelativeBlockHardness(player, world, clickedPos) * 16; + float before = 0; + Pair blockBreakingProgress = player.blockBreakingProgress; + if (blockBreakingProgress != null) + before = blockBreakingProgress.getValue(); + progress += before; + + if (progress >= 1) { + player.interactionManager.tryHarvestBlock(clickedPos); + world.sendBlockBreakProgress(player.getEntityId(), clickedPos, -1); + player.blockBreakingProgress = null; + return; + } + + if ((int) (before * 10) != (int) (progress * 10)) + world.sendBlockBreakProgress(player.getEntityId(), clickedPos, (int) (progress * 10)); + player.blockBreakingProgress = Pair.of(clickedPos, progress); + return; + } + + // Right click + ItemUseContext itemusecontext = new ItemUseContext(player, hand, result); + RightClickBlock event = ForgeHooks.onRightClickBlock(player, hand, clickedPos, face); + + // Item has custom active use + if (event.getUseItem() != DENY) { + ActionResultType actionresult = stack.onItemUseFirst(itemusecontext); + if (actionresult != ActionResultType.PASS) + return; + } + + boolean holdingSomething = !player.getHeldItemMainhand().isEmpty(); + boolean flag1 = + !(player.isSneaking() && holdingSomething) || (stack.doesSneakBypassUse(world, clickedPos, player)); + + // Use on block + if (event.getUseBlock() != DENY && flag1 && clickedState.onBlockActivated(world, player, hand, result)) + return; + if (stack.isEmpty()) + return; + if (event.getUseItem() == DENY) + return; + if (item instanceof BlockItem && !clickedState.isReplaceable(new BlockItemUseContext(itemusecontext))) + return; + + // Reposition fire placement for convenience + if (item == Items.FLINT_AND_STEEL) { + Direction newFace = result.getFace(); + BlockPos newPos = result.getPos(); + if (!FlintAndSteelItem.canSetFire(clickedState, world, clickedPos)) + newFace = Direction.UP; + if (clickedState.getMaterial() == Material.AIR) + newPos = newPos.offset(face.getOpposite()); + result = new BlockRayTraceResult(result.getHitVec(), newFace, newPos, result.isInside()); + itemusecontext = new ItemUseContext(player, hand, result); + } + + // 'Inert' item use behaviour & block placement + ActionResultType onItemUse = stack.onItemUse(itemusecontext); + if (onItemUse == ActionResultType.SUCCESS) + return; + if (item == Items.ENDER_PEARL) + return; + + // buckets create their own ray, We use a fake wall to contain the active area + World itemUseWorld = world; + if (item instanceof BucketItem || item instanceof SandPaperItem) + itemUseWorld = new ItemUseWorld(world, face, pos); + + ActionResult onItemRightClick = item.onItemRightClick(itemUseWorld, player, hand); + player.setHeldItem(hand, onItemRightClick.getResult()); + + CompoundNBT tag = stack.getOrCreateTag(); + if (stack.getItem() instanceof SandPaperItem && tag.contains("Polishing")) + player.spawnedItemEffects = ItemStack.read(tag.getCompound("Polishing")); + if (stack.isFood()) + player.spawnedItemEffects = stack.copy(); + + if (!player.getActiveItemStack().isEmpty()) + player.setHeldItem(hand, stack.onItemUseFinish(world, player)); + + player.resetActiveHand(); + } + +} diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerMovementBehaviour.java b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerMovementBehaviour.java new file mode 100644 index 000000000..f46165b24 --- /dev/null +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerMovementBehaviour.java @@ -0,0 +1,175 @@ +package com.simibubi.create.modules.contraptions.components.deployer; + +import java.util.Arrays; +import java.util.List; + +import org.apache.commons.lang3.tuple.Pair; + +import com.simibubi.create.foundation.item.ItemHelper; +import com.simibubi.create.foundation.utility.NBTHelper; +import com.simibubi.create.foundation.utility.SuperByteBuffer; +import com.simibubi.create.foundation.utility.VecHelper; +import com.simibubi.create.modules.contraptions.components.contraptions.ContraptionEntity; +import com.simibubi.create.modules.contraptions.components.contraptions.MovementBehaviour; +import com.simibubi.create.modules.contraptions.components.contraptions.MovementContext; +import com.simibubi.create.modules.contraptions.components.deployer.DeployerTileEntity.Mode; +import com.simibubi.create.modules.logistics.item.filter.FilterItem; + +import net.minecraft.entity.player.PlayerInventory; +import net.minecraft.item.ItemStack; +import net.minecraft.util.Hand; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.Vec3d; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; +import net.minecraftforge.common.util.Constants.NBT; + +public class DeployerMovementBehaviour extends MovementBehaviour { + + @Override + public Vec3d getActiveAreaOffset(MovementContext context) { + return new Vec3d(context.state.get(DeployerBlock.FACING).getDirectionVec()).scale(2); + } + + @Override + public void visitNewPosition(MovementContext context, BlockPos pos) { + if (context.world.isRemote) + return; + + tryGrabbingItem(context); + DeployerFakePlayer player = getPlayer(context); + Mode mode = getMode(context); + if (mode == Mode.USE && !DeployerHandler.shouldActivate(player.getHeldItemMainhand(), context.world, pos)) + return; + + activate(context, pos, player, mode); + tryDisposeOfExcess(context); + context.stall = player.blockBreakingProgress != null; + } + + public void activate(MovementContext context, BlockPos pos, DeployerFakePlayer player, Mode mode) { + Vec3d facingVec = new Vec3d(context.state.get(DeployerBlock.FACING).getDirectionVec()); + facingVec = VecHelper.rotate(facingVec, context.rotation.x, context.rotation.y, context.rotation.z); + Vec3d vec = context.position.subtract(facingVec.scale(2)); + + player.rotationYaw = ContraptionEntity.yawFromVector(facingVec); + player.rotationPitch = ContraptionEntity.pitchFromVector(facingVec) - 90; + + DeployerHandler.activate(player, vec, pos, facingVec, mode); + } + + @Override + public void tick(MovementContext context) { + if (context.world.isRemote) + return; + if (!context.stall) + return; + + DeployerFakePlayer player = getPlayer(context); + Mode mode = getMode(context); + + Pair blockBreakingProgress = player.blockBreakingProgress; + if (blockBreakingProgress != null) { + int timer = context.data.getInt("Timer"); + if (timer < 20) { + timer++; + context.data.putInt("Timer", timer); + return; + } + + context.data.remove("Timer"); + activate(context, blockBreakingProgress.getKey(), player, mode); + tryDisposeOfExcess(context); + } + + context.stall = player.blockBreakingProgress != null; + } + + @Override + public void stopMoving(MovementContext context) { + if (context.world.isRemote) + return; + tryDisposeOfEverything(context); + } + + private void tryGrabbingItem(MovementContext context) { + DeployerFakePlayer player = getPlayer(context); + if (player == null) + return; + if (player.getHeldItemMainhand().isEmpty()) { + ItemStack held = ItemHelper.extract(context.contraption.inventory, + stack -> FilterItem.test(stack, getFilter(context)), 1, false); + player.setHeldItem(Hand.MAIN_HAND, held); + } + } + + private void tryDisposeOfEverything(MovementContext context) { + DeployerFakePlayer player = getPlayer(context); + if (player == null) + return; + ItemStack held = player.getHeldItemMainhand(); + if (!held.isEmpty()) { + dropItem(context, held); + player.setHeldItem(Hand.MAIN_HAND, ItemStack.EMPTY); + } + tryDisposeOfExcess(context); + } + + private void tryDisposeOfExcess(MovementContext context) { + DeployerFakePlayer player = getPlayer(context); + if (player == null) + return; + PlayerInventory inv = player.inventory; + ItemStack filter = getFilter(context); + + for (List list : Arrays.asList(inv.armorInventory, inv.offHandInventory, inv.mainInventory)) { + for (int i = 0; i < list.size(); ++i) { + ItemStack itemstack = list.get(i); + if (itemstack.isEmpty()) + continue; + + if (list == inv.mainInventory && i == inv.currentItem && FilterItem.test(itemstack, filter)) + continue; + + dropItem(context, itemstack); + list.set(i, ItemStack.EMPTY); + } + } + } + + @Override + public void writeExtraData(MovementContext context) { + DeployerFakePlayer player = getPlayer(context); + if (player == null) + return; + context.data.put("HeldItem", player.getHeldItemMainhand().serializeNBT()); + } + + private DeployerFakePlayer getPlayer(MovementContext context) { + if (!(context.temporaryData instanceof DeployerFakePlayer) && context.world instanceof ServerWorld) { + DeployerFakePlayer deployerFakePlayer = new DeployerFakePlayer((ServerWorld) context.world); + deployerFakePlayer.inventory.read(context.tileData.getList("Inventory", NBT.TAG_COMPOUND)); + if (context.data.contains("HeldItem")) + deployerFakePlayer.setHeldItem(Hand.MAIN_HAND, ItemStack.read(context.data.getCompound("HeldItem"))); + context.tileData.remove("Inventory"); + context.temporaryData = deployerFakePlayer; + } + return (DeployerFakePlayer) context.temporaryData; + } + + private ItemStack getFilter(MovementContext context) { + return ItemStack.read(context.tileData.getCompound("Filter")); + } + + private Mode getMode(MovementContext context) { + return NBTHelper.readEnum(context.tileData.getString("Mode"), Mode.class); + } + + @Override + @OnlyIn(Dist.CLIENT) + public List renderListInContraption(MovementContext context) { + return DeployerTileEntityRenderer.renderListInContraption(context); + } + +} diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntity.java b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntity.java index 1c2f3b85b..ad2f0eb09 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntity.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntity.java @@ -1,7 +1,6 @@ package com.simibubi.create.modules.contraptions.components.deployer; import static com.simibubi.create.modules.contraptions.base.DirectionalKineticBlock.FACING; -import static net.minecraftforge.eventbus.api.Event.Result.DENY; import java.util.ArrayList; import java.util.Arrays; @@ -9,15 +8,12 @@ import java.util.Iterator; import java.util.List; import java.util.stream.Collectors; -import com.simibubi.create.foundation.advancement.AllCriterionTriggers; -import net.minecraft.entity.player.ServerPlayerEntity; -import net.minecraft.tileentity.TileEntity; import org.apache.commons.lang3.tuple.Pair; -import com.google.common.collect.Multimap; import com.simibubi.create.AllBlockPartials; import com.simibubi.create.AllBlocks; import com.simibubi.create.AllTileEntities; +import com.simibubi.create.foundation.advancement.AllCriterionTriggers; import com.simibubi.create.foundation.behaviour.base.TileEntityBehaviour; import com.simibubi.create.foundation.behaviour.filtering.FilteringBehaviour; import com.simibubi.create.foundation.behaviour.filtering.FilteringBehaviour.SlotPositioning; @@ -25,32 +21,16 @@ import com.simibubi.create.foundation.behaviour.inventory.ExtractingBehaviour; import com.simibubi.create.foundation.item.ItemHelper; import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.VecHelper; -import com.simibubi.create.foundation.utility.WrappedWorld; import com.simibubi.create.modules.contraptions.base.KineticTileEntity; import com.simibubi.create.modules.curiosities.tools.SandPaperItem; -import net.minecraft.block.BlockState; -import net.minecraft.block.Blocks; -import net.minecraft.block.material.Material; -import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.entity.item.ItemEntity; import net.minecraft.entity.player.PlayerInventory; -import net.minecraft.fluid.Fluid; -import net.minecraft.fluid.Fluids; -import net.minecraft.inventory.EquipmentSlotType; -import net.minecraft.item.BlockItem; -import net.minecraft.item.BlockItemUseContext; -import net.minecraft.item.BucketItem; -import net.minecraft.item.FlintAndSteelItem; -import net.minecraft.item.Item; +import net.minecraft.entity.player.ServerPlayerEntity; import net.minecraft.item.ItemStack; -import net.minecraft.item.ItemUseContext; -import net.minecraft.item.Items; import net.minecraft.nbt.CompoundNBT; import net.minecraft.nbt.ListNBT; -import net.minecraft.util.ActionResult; -import net.minecraft.util.ActionResultType; +import net.minecraft.tileentity.TileEntity; import net.minecraft.util.Direction; import net.minecraft.util.Hand; import net.minecraft.util.math.AxisAlignedBB; @@ -61,13 +41,8 @@ import net.minecraft.util.math.RayTraceContext; import net.minecraft.util.math.RayTraceContext.BlockMode; import net.minecraft.util.math.RayTraceContext.FluidMode; import net.minecraft.util.math.Vec3d; -import net.minecraft.world.World; import net.minecraft.world.server.ServerWorld; -import net.minecraftforge.common.ForgeHooks; import net.minecraftforge.common.util.Constants.NBT; -import net.minecraftforge.event.entity.player.PlayerInteractEvent.LeftClickBlock; -import net.minecraftforge.event.entity.player.PlayerInteractEvent.RightClickBlock; -import net.minecraftforge.eventbus.api.Event.Result; import net.minecraftforge.items.IItemHandler; import net.minecraftforge.items.ItemHandlerHelper; @@ -86,10 +61,7 @@ public class DeployerTileEntity extends KineticTileEntity { protected float reach; protected boolean boop = false; protected List overflowItems = new ArrayList<>(); - protected Pair blockBreakingProgress; - private ListNBT deferredInventoryList; - private ItemStack spawnItemEffects; enum State { WAITING, EXPANDING, RETRACTING, DUMPING; @@ -152,10 +124,10 @@ public class DeployerTileEntity extends KineticTileEntity { if (getSpeed() == 0) return; - if (!world.isRemote && blockBreakingProgress != null) { - if (world.isAirBlock(blockBreakingProgress.getKey())) { - world.sendBlockBreakProgress(player.getEntityId(), blockBreakingProgress.getKey(), -1); - blockBreakingProgress = null; + if (!world.isRemote && player != null && player.blockBreakingProgress != null) { + if (world.isAirBlock(player.blockBreakingProgress.getKey())) { + world.sendBlockBreakProgress(player.getEntityId(), player.blockBreakingProgress.getKey(), -1); + player.blockBreakingProgress = null; } } if (timer > 0) { @@ -191,50 +163,21 @@ public class DeployerTileEntity extends KineticTileEntity { extracting.extract(1); Direction facing = getBlockState().get(FACING); - if (stack.getItem() instanceof BlockItem) { - if (!world.getBlockState(pos.offset(facing, 2)).getMaterial().isReplaceable()) { - timer = getTimerSpeed() * 10; - return; - } + if (mode == Mode.USE && !DeployerHandler.shouldActivate(stack, world, pos.offset(facing, 2))) { + timer = getTimerSpeed() * 10; + return; } - if (stack.getItem() instanceof BucketItem) { - BucketItem bucketItem = (BucketItem) stack.getItem(); - Fluid fluid = bucketItem.getFluid(); - if (fluid != Fluids.EMPTY && world.getFluidState(pos.offset(facing, 2)).getFluid() == fluid) { - timer = getTimerSpeed() * 10; - return; - } - } - - //Check for advancement conditions - if (mode == Mode.PUNCH && !boop) { - if (world.isAirBlock(pos.offset(facing,1)) && world.isAirBlock(pos.offset(facing,2))) { - BlockPos otherDeployer = pos.offset(facing, 4); - if (world.isBlockPresent(otherDeployer)){ - TileEntity otherTile = world.getTileEntity(otherDeployer); - if (otherTile instanceof DeployerTileEntity){ - DeployerTileEntity deployerTile = (DeployerTileEntity) otherTile; - if (world.getBlockState(otherDeployer).get(FACING).getOpposite() == facing && deployerTile.mode == Mode.PUNCH) { - //two facing deployer - boop = true; - reach = 1f; - timer = 1000; - state = State.EXPANDING; - sendData(); - return; - } - } - } - } - } + // Check for advancement conditions + if (mode == Mode.PUNCH && !boop && startBoop(facing)) + return; state = State.EXPANDING; Vec3d movementVector = getMovementVector(); Vec3d rayOrigin = VecHelper.getCenterOf(pos).add(movementVector.scale(3 / 2f)); Vec3d rayTarget = VecHelper.getCenterOf(pos).add(movementVector.scale(5 / 2f)); - RayTraceContext rayTraceContext = new RayTraceContext(rayOrigin, rayTarget, BlockMode.OUTLINE, - FluidMode.NONE, player); + RayTraceContext rayTraceContext = + new RayTraceContext(rayOrigin, rayTarget, BlockMode.OUTLINE, FluidMode.NONE, player); BlockRayTraceResult result = world.rayTraceBlocks(rayTraceContext); reach = (float) (.5f + Math.min(result.getHitVec().subtract(rayOrigin).length(), .75f)); @@ -244,37 +187,10 @@ public class DeployerTileEntity extends KineticTileEntity { } if (state == State.EXPANDING) { - if (boop){ - TileEntity otherTile = world.getTileEntity(pos.offset(getBlockState().get(FACING),4)); - if (otherTile instanceof DeployerTileEntity){ - DeployerTileEntity deployerTile = (DeployerTileEntity) otherTile; - if (deployerTile.boop && deployerTile.state == State.EXPANDING){ - if(deployerTile.timer <= 0){ - //everything should be met - boop = false; - state = State.RETRACTING; - timer = 1000; - deployerTile.boop = false; - deployerTile.state = State.RETRACTING; - deployerTile.timer = 1000; - - deployerTile.sendData(); - sendData(); - - //award nearby players - List players = world.getEntitiesWithinAABB(ServerPlayerEntity.class, new AxisAlignedBB(pos).grow(9)); - players.forEach(AllCriterionTriggers.DEPLOYER_BOOP::trigger); - } - } - } - return; - } - Multimap attributeModifiers = stack - .getAttributeModifiers(EquipmentSlotType.MAINHAND); - player.getAttributes().applyAttributeModifiers(attributeModifiers); - activate(); - player.getAttributes().removeAttributeModifiers(attributeModifiers); - heldItem = player.getHeldItemMainhand(); + if (boop) + triggerBoop(); + else + activate(); state = State.RETRACTING; timer = 1000; @@ -292,183 +208,61 @@ public class DeployerTileEntity extends KineticTileEntity { } + public boolean startBoop(Direction facing) { + if (!world.isAirBlock(pos.offset(facing, 1)) || !world.isAirBlock(pos.offset(facing, 2))) + return false; + BlockPos otherDeployer = pos.offset(facing, 4); + if (!world.isBlockPresent(otherDeployer)) + return false; + TileEntity otherTile = world.getTileEntity(otherDeployer); + if (!(otherTile instanceof DeployerTileEntity)) + return false; + DeployerTileEntity deployerTile = (DeployerTileEntity) otherTile; + if (world.getBlockState(otherDeployer).get(FACING).getOpposite() != facing || deployerTile.mode != Mode.PUNCH) + return false; + + boop = true; + reach = 1f; + timer = 1000; + state = State.EXPANDING; + sendData(); + return true; + } + + public void triggerBoop() { + TileEntity otherTile = world.getTileEntity(pos.offset(getBlockState().get(FACING), 4)); + if (!(otherTile instanceof DeployerTileEntity)) + return; + + DeployerTileEntity deployerTile = (DeployerTileEntity) otherTile; + if (!deployerTile.boop || deployerTile.state != State.EXPANDING) + return; + if (deployerTile.timer > 0) + return; + + // everything should be met + boop = false; + deployerTile.boop = false; + deployerTile.state = State.RETRACTING; + deployerTile.timer = 1000; + deployerTile.sendData(); + + // award nearby players + List players = + world.getEntitiesWithinAABB(ServerPlayerEntity.class, new AxisAlignedBB(pos).grow(9)); + players.forEach(AllCriterionTriggers.DEPLOYER_BOOP::trigger); + } + protected void activate() { - // Update player position and angle Vec3d movementVector = getMovementVector(); Direction direction = getBlockState().get(FACING); Vec3d center = VecHelper.getCenterOf(pos); - Vec3d rayOrigin = center.add(movementVector.scale(3 / 2f + 1 / 64f)); - Vec3d rayTarget = center.add(movementVector.scale(5 / 2f - 1 / 64f)); BlockPos clickedPos = pos.offset(direction, 2); - player.rotationYaw = direction.getHorizontalAngle(); player.rotationPitch = direction == Direction.UP ? -90 : direction == Direction.DOWN ? 90 : 0; - player.setPosition(rayOrigin.x, rayOrigin.y, rayOrigin.z); - ItemStack stack = player.getHeldItemMainhand(); - Item item = stack.getItem(); - - // Check for entities - World world = this.world; - List entities = world.getEntitiesWithinAABB(LivingEntity.class, new AxisAlignedBB(clickedPos)); - Hand hand = Hand.MAIN_HAND; - if (!entities.isEmpty()) { - LivingEntity entity = entities.get(world.rand.nextInt(entities.size())); - List capturedDrops = new ArrayList<>(); - boolean success = false; - entity.captureDrops(capturedDrops); - - // Use on entity - if (mode == Mode.USE) { - ActionResultType cancelResult = ForgeHooks.onInteractEntity(player, entity, hand); - if (cancelResult == ActionResultType.FAIL) { - entity.captureDrops(null); - return; - } - if (cancelResult == null) { - if (entity.processInitialInteract(player, hand)) - success = true; - else if (stack.interactWithEntity(player, entity, hand)) - success = true; - } - } - - // Punch entity - if (mode == Mode.PUNCH) { - player.resetCooldown(); - player.attackTargetEntityWithCurrentItem(entity); - success = true; - } - - entity.captureDrops(null); - capturedDrops.forEach(e -> player.inventory.placeItemBackInInventory(this.world, e.getItem())); - if (success) - return; - } - - // Shoot ray - RayTraceContext rayTraceContext = new RayTraceContext(rayOrigin, rayTarget, BlockMode.OUTLINE, FluidMode.NONE, - player); - BlockRayTraceResult result = world.rayTraceBlocks(rayTraceContext); - BlockState clickedState = world.getBlockState(clickedPos); - - // Left click - if (mode == Mode.PUNCH) { - LeftClickBlock event = ForgeHooks.onLeftClickBlock(player, clickedPos, direction.getOpposite()); - if (event.isCanceled()) - return; - if (!world.isBlockModifiable(player, clickedPos)) - return; - if (world.extinguishFire(player, clickedPos, direction.getOpposite())) - return; - if (clickedState.isAir(world, clickedPos)) - return; - if (event.getUseBlock() != Result.DENY) - clickedState.onBlockClicked(world, clickedPos, player); - if (stack.isEmpty()) - return; - - float progress = clickedState.getPlayerRelativeBlockHardness(player, world, clickedPos) * 16; - float before = 0; - if (blockBreakingProgress != null) - before = blockBreakingProgress.getValue(); - progress += before; - - if (progress >= 1) { - player.interactionManager.tryHarvestBlock(clickedPos); - world.sendBlockBreakProgress(player.getEntityId(), clickedPos, -1); - blockBreakingProgress = null; - return; - } - - if ((int) (before * 10) != (int) (progress * 10)) - world.sendBlockBreakProgress(player.getEntityId(), clickedPos, (int) (progress * 10)); - blockBreakingProgress = Pair.of(clickedPos, progress); - return; - } - - // Right click - ItemUseContext itemusecontext = new ItemUseContext(player, hand, result); - RightClickBlock event = ForgeHooks.onRightClickBlock(player, hand, clickedPos, direction.getOpposite()); - - // Item has custom active use - if (event.getUseItem() != DENY) { - ActionResultType actionresult = stack.onItemUseFirst(itemusecontext); - if (actionresult != ActionResultType.PASS) - return; - } - - boolean holdingSomething = !player.getHeldItemMainhand().isEmpty(); - boolean flag1 = !(player.isSneaking() && holdingSomething) - || (stack.doesSneakBypassUse(world, clickedPos, player)); - - // Use on block - if (event.getUseBlock() != DENY && flag1 && clickedState.onBlockActivated(world, player, hand, result)) - return; - if (stack.isEmpty()) - return; - if (event.getUseItem() == DENY) - return; - if (item instanceof BlockItem && !clickedState.isReplaceable(new BlockItemUseContext(itemusecontext))) - return; - - // Reposition fire placement for convenience - if (item == Items.FLINT_AND_STEEL) { - Direction newFace = result.getFace(); - BlockPos newPos = result.getPos(); - if (!FlintAndSteelItem.canSetFire(clickedState, world, clickedPos)) - newFace = Direction.UP; - if (clickedState.getMaterial() == Material.AIR) - newPos = newPos.offset(direction); - result = new BlockRayTraceResult(result.getHitVec(), newFace, newPos, result.isInside()); - itemusecontext = new ItemUseContext(player, hand, result); - } - - // 'Inert' item use behaviour & block placement - ActionResultType onItemUse = stack.onItemUse(itemusecontext); - if (onItemUse == ActionResultType.SUCCESS) - return; - if (item == Items.ENDER_PEARL) - return; - - // buckets create their own ray, We use a fake wall to contain the active area - if (item instanceof BucketItem || item instanceof SandPaperItem) { - world = new WrappedWorld(world) { - - boolean rayMode = false; - - @Override - public BlockRayTraceResult rayTraceBlocks(RayTraceContext context) { - rayMode = true; - BlockRayTraceResult rayTraceBlocks = super.rayTraceBlocks(context); - rayMode = false; - return rayTraceBlocks; - }; - - @Override - public BlockState getBlockState(BlockPos position) { - if (rayMode - && (pos.offset(direction, 3).equals(position) || pos.offset(direction, 1).equals(position))) - return Blocks.BEDROCK.getDefaultState(); - return world.getBlockState(position); - } - - }; - } - - ActionResult onItemRightClick = item.onItemRightClick(world, player, hand); - player.setHeldItem(hand, onItemRightClick.getResult()); - - CompoundNBT tag = stack.getOrCreateTag(); - if (stack.getItem() instanceof SandPaperItem && tag.contains("Polishing")) - spawnItemEffects = ItemStack.read(tag.getCompound("Polishing")); - if (stack.isFood()) - spawnItemEffects = stack.copy(); - - if (!player.getActiveItemStack().isEmpty()) - player.setHeldItem(hand, stack.onItemUseFinish(world, player)); - - player.resetActiveHand(); + DeployerHandler.activate(player, center, clickedPos, movementVector, mode); + heldItem = player.getHeldItemMainhand(); } protected void returnAndDeposit() { @@ -556,11 +350,12 @@ public class DeployerTileEntity extends KineticTileEntity { @Override public CompoundNBT writeToClient(CompoundNBT compound) { compound.putFloat("Reach", reach); - if (player != null) + if (player != null) { compound.put("HeldItem", player.getHeldItemMainhand().serializeNBT()); - if (spawnItemEffects != null) { - compound.put("Particle", spawnItemEffects.serializeNBT()); - spawnItemEffects = null; + if (player.spawnedItemEffects != null) { + compound.put("Particle", player.spawnedItemEffects.serializeNBT()); + player.spawnedItemEffects = null; + } } return super.writeToClient(compound); } @@ -584,14 +379,6 @@ public class DeployerTileEntity extends KineticTileEntity { return false; } - @Override - public void remove() { - if (!world.isRemote && blockBreakingProgress != null) - world.sendBlockBreakProgress(player.getEntityId(), blockBreakingProgress.getKey(), -1); - super.remove(); - player = null; - } - public AllBlockPartials getHandPose() { return mode == Mode.PUNCH ? AllBlockPartials.DEPLOYER_HAND_PUNCHING : heldItem.isEmpty() ? AllBlockPartials.DEPLOYER_HAND_POINTING : AllBlockPartials.DEPLOYER_HAND_HOLDING; diff --git a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntityRenderer.java b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntityRenderer.java index a32e6a4ac..63f200861 100644 --- a/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntityRenderer.java +++ b/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerTileEntityRenderer.java @@ -4,18 +4,23 @@ import static com.simibubi.create.modules.contraptions.base.DirectionalAxisKinet import static com.simibubi.create.modules.contraptions.base.DirectionalKineticBlock.FACING; import static net.minecraft.state.properties.BlockStateProperties.AXIS; +import java.util.Arrays; +import java.util.List; + import com.mojang.blaze3d.platform.GlStateManager; import com.simibubi.create.AllBlockPartials; import com.simibubi.create.AllBlocks; import com.simibubi.create.foundation.behaviour.filtering.FilteringRenderer; import com.simibubi.create.foundation.block.SafeTileEntityRenderer; import com.simibubi.create.foundation.utility.AngleHelper; +import com.simibubi.create.foundation.utility.NBTHelper; import com.simibubi.create.foundation.utility.SuperByteBuffer; import com.simibubi.create.foundation.utility.TessellatorHelper; import com.simibubi.create.foundation.utility.VecHelper; import com.simibubi.create.modules.contraptions.base.IRotate; import com.simibubi.create.modules.contraptions.base.KineticTileEntity; import com.simibubi.create.modules.contraptions.base.KineticTileEntityRenderer; +import com.simibubi.create.modules.contraptions.components.contraptions.MovementContext; import com.simibubi.create.modules.contraptions.components.deployer.DeployerTileEntity.Mode; import com.simibubi.create.modules.contraptions.components.deployer.DeployerTileEntity.State; @@ -33,12 +38,14 @@ import net.minecraft.util.Direction.Axis; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; +import net.minecraft.world.World; @SuppressWarnings("deprecation") public class DeployerTileEntityRenderer extends SafeTileEntityRenderer { @Override - public void renderWithGL(DeployerTileEntity te, double x, double y, double z, float partialTicks, int destroyStage) { + public void renderWithGL(DeployerTileEntity te, double x, double y, double z, float partialTicks, + int destroyStage) { renderItem(te, x, y, z, partialTicks); FilteringRenderer.renderOnTileEntity(te, x, y, z, partialTicks, destroyStage); renderComponents(te, x, y, z, partialTicks); @@ -64,8 +71,8 @@ public class DeployerTileEntityRenderer extends SafeTileEntityRenderer renderListInContraption(MovementContext context) { + BlockState blockState = context.state; + BlockPos pos = BlockPos.ZERO; + Mode mode = NBTHelper.readEnum(context.tileData.getString("Mode"), Mode.class); + World world = context.world; + AllBlockPartials handPose = + mode == Mode.PUNCH ? AllBlockPartials.DEPLOYER_HAND_PUNCHING : AllBlockPartials.DEPLOYER_HAND_POINTING; + + SuperByteBuffer pole = renderAndTransform(world, AllBlockPartials.DEPLOYER_POLE, blockState, pos, true); + SuperByteBuffer hand = renderAndTransform(world, handPose, blockState, pos, false); + + Vec3d center = VecHelper.getCenterOf(new BlockPos(context.position)); + double distance = context.position.distanceTo(center); + double nextDistance = context.position.add(context.motion).distanceTo(center); + Vec3d offset = new Vec3d(blockState.get(FACING).getDirectionVec()) + .scale(.5f - MathHelper.lerp(Minecraft.getInstance().getRenderPartialTicks(), distance, nextDistance)); + pole.translate(offset.x, offset.y, offset.z); + hand.translate(offset.x, offset.y, offset.z); + + return Arrays.asList(pole, hand); + } + } diff --git a/src/main/java/com/simibubi/create/modules/logistics/block/inventories/FlexcrateBlock.java b/src/main/java/com/simibubi/create/modules/logistics/block/inventories/FlexcrateBlock.java index 2d376ebff..4f71e546a 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/block/inventories/FlexcrateBlock.java +++ b/src/main/java/com/simibubi/create/modules/logistics/block/inventories/FlexcrateBlock.java @@ -138,7 +138,8 @@ public class FlexcrateBlock extends ProperDirectionalBlock { if (state.hasTileEntity() && state.getBlock() != newState.getBlock()) { FlexcrateTileEntity te = (FlexcrateTileEntity) worldIn.getTileEntity(pos); - te.onDestroyed(); + if (!isMoving) + te.onDestroyed(); worldIn.removeTileEntity(pos); } diff --git a/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterItem.java b/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterItem.java index f5de44166..bd9d2e4f2 100644 --- a/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterItem.java +++ b/src/main/java/com/simibubi/create/modules/logistics/item/filter/FilterItem.java @@ -157,6 +157,9 @@ public class FilterItem extends Item implements INamedContainerProvider { } private static boolean test(ItemStack stack, ItemStack filter, boolean matchNBT) { + if (filter.isEmpty()) + return true; + if (!(filter.getItem() instanceof FilterItem)) return (matchNBT ? ItemHandlerHelper.canItemStacksStack(filter, stack) : ItemStack.areItemsEqual(filter, stack));