CreateMod/src/main/java/com/simibubi/create/modules/contraptions/components/deployer/DeployerHandler.java
2020-05-15 02:06:28 +02:00

291 lines
10 KiB
Java

package com.simibubi.create.modules.contraptions.components.deployer;
import static net.minecraftforge.eventbus.api.Event.Result.DENY;
import static net.minecraftforge.eventbus.api.Event.Result.DEFAULT;
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.Entity;
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;
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<String, AttributeModifier> 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<Entity> entities = world.getEntitiesWithinAABB(Entity.class, new AxisAlignedBB(clickedPos));
Hand hand = Hand.MAIN_HAND;
if (!entities.isEmpty()) {
Entity entity = entities.get(world.rand.nextInt(entities.size()));
List<ItemEntity> 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 (entity instanceof LivingEntity
&& stack.interactWithEntity(player, (LivingEntity) 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);
if (result.getPos() != clickedPos)
result = new BlockRayTraceResult(result.getHitVec(), result.getFace(), clickedPos, result.isInside());
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) {
if (!world.isBlockModifiable(player, clickedPos))
return;
if (clickedState.getRenderShape(world, clickedPos)
.isEmpty()) {
player.blockBreakingProgress = null;
return;
}
LeftClickBlock event = ForgeHooks.onLeftClickBlock(player, clickedPos, face);
if (event.isCanceled())
return;
if (world.extinguishFire(player, clickedPos, face))
return;
if (event.getUseBlock() != DENY)
clickedState.onBlockClicked(world, clickedPos, player);
if (stack.isEmpty())
return;
float progress = clickedState.getPlayerRelativeBlockHardness(player, world, clickedPos) * 16;
float before = 0;
Pair<BlockPos, Float> 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 (progress <= 0) {
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);
Event.Result useBlock = DENY;
Event.Result useItem = DEFAULT;
if (!clickedState.getRenderShape(world, clickedPos)
.isEmpty()) {
RightClickBlock event = ForgeHooks.onRightClickBlock(player, hand, clickedPos, face);
useBlock = event.getUseBlock();
useItem = event.getUseItem();
}
// Item has custom active use
if (useItem != 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 (useBlock != DENY && flag1 && clickedState.onUse(world, player, hand, result) == ActionResultType.SUCCESS)
return;
if (stack.isEmpty())
return;
if (useItem == 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<ItemStack> 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();
}
}