diff --git a/build.gradle b/build.gradle index b528eb386..69b831065 100644 --- a/build.gradle +++ b/build.gradle @@ -38,8 +38,8 @@ dependencies { // Minecraft, MCP, Forge, and Java versions sourceCompatibility = targetCompatibility = "1.8" ext.mcversion = "1.12.2" -ext.forgeversion = "14.23.2.2623" -String mcpversion = "snapshot_20180227" +ext.forgeversion = "14.23.4.2707" +String mcpversion = "snapshot_20180703" // Mod version version = "3.0.9" diff --git a/src/main/java/org/dimdev/dimdoors/shared/items/ItemRiftBlade.java b/src/main/java/org/dimdev/dimdoors/shared/items/ItemRiftBlade.java index 5e5913340..617248733 100644 --- a/src/main/java/org/dimdev/dimdoors/shared/items/ItemRiftBlade.java +++ b/src/main/java/org/dimdev/dimdoors/shared/items/ItemRiftBlade.java @@ -48,10 +48,13 @@ public boolean getIsRepairable(ItemStack toRepair, ItemStack repair) { @Override public ActionResult onItemRightClick(World world, EntityPlayer player, EnumHand hand) { ItemStack stack = player.getHeldItem(hand); - RayTraceResult hit = RayTraceHelper.rayTraceForRiftTools(world, player); + RayTraceResult hit = RayTraceHelper.rayTraceEntity(world, player, 16, 1.0F); //TODO: make the range of the Rift Blade configurable + if (hit == null) { + hit = RayTraceHelper.rayTraceForRiftTools(world, player); + } if (world.isRemote) { - if (RayTraceHelper.isRift(hit, world) || RayTraceHelper.isLivingEntity(hit)) { + if (RayTraceHelper.isLivingEntity(hit) || RayTraceHelper.isRift(hit, world)) { return new ActionResult<>(EnumActionResult.SUCCESS, stack); } else { player.sendStatusMessage(new TextComponentTranslation(getUnlocalizedName() + ".rift_miss"), true); @@ -60,25 +63,30 @@ public ActionResult onItemRightClick(World world, EntityPlayer player } } - if (RayTraceHelper.isRift(hit, world)) { - TileEntityRift rift = (TileEntityRift) world.getTileEntity(hit.getBlockPos()); - rift.teleport(player); + if (RayTraceHelper.isLivingEntity(hit)) { + double damageMultiplier = (double) stack.getItemDamage() / (double) stack.getMaxDamage(); + // TODO: gaussian, instead or random + double offsetDistance = Math.random() * damageMultiplier * 7 + 2; //TODO: make these offset distances configurable + double offsetRotationYaw = (Math.random() - 0.5) * damageMultiplier * 360; + + Vec3d playerVec = player.getPositionVector(); + Vec3d entityVec = hit.hitVec; + Vec3d offsetDirection = playerVec.subtract(entityVec).normalize(); + offsetDirection = offsetDirection.rotateYaw((float) (offsetRotationYaw * Math.PI) / 180); + BlockPos tpPos = new BlockPos(entityVec.add(offsetDirection.scale(offsetDistance))); + while (world.getBlockState(tpPos).getMaterial().blocksMovement()) tpPos = tpPos.up(); // TODO: move to ddutils + TeleportUtils.teleport(player, new Location(world, tpPos), (player.rotationYaw - (float) offsetRotationYaw) % 360, player.rotationPitch); + stack.damageItem(1, player); return new ActionResult<>(EnumActionResult.SUCCESS, stack); + } else if (RayTraceHelper.isRift(hit, world)) { + TileEntityRift rift = (TileEntityRift) world.getTileEntity(hit.getBlockPos()); + rift.teleport(player); - } else if (RayTraceHelper.isLivingEntity(hit)) { - BlockPos hitPos = hit.getBlockPos(); - // TODO: gaussian, and depend on rift blade's wear - int xDiff = (int) (5 * (Math.random() - 0.5)); - int zDiff = (int) (5 * (Math.random() - 0.5)); - BlockPos tpPos = new BlockPos(hitPos.getX() + xDiff, hitPos.getY(), hitPos.getZ() + zDiff); - while (world.getBlockState(tpPos).getMaterial().blocksMovement()) tpPos = tpPos.up(); // TODO: move to ddutils - TeleportUtils.teleport(player, new Location(world, tpPos), player.rotationYaw, player.rotationPitch); stack.damageItem(1, player); return new ActionResult<>(EnumActionResult.SUCCESS, stack); } - return new ActionResult<>(EnumActionResult.FAIL, stack); } diff --git a/src/main/java/org/dimdev/dimdoors/shared/items/RayTraceHelper.java b/src/main/java/org/dimdev/dimdoors/shared/items/RayTraceHelper.java index 3efca7fe1..16f3c705a 100644 --- a/src/main/java/org/dimdev/dimdoors/shared/items/RayTraceHelper.java +++ b/src/main/java/org/dimdev/dimdoors/shared/items/RayTraceHelper.java @@ -2,18 +2,23 @@ import net.minecraft.block.Block; import net.minecraft.block.state.IBlockState; +import net.minecraft.entity.EntityLiving; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.util.EnumFacing; +import net.minecraft.util.math.AxisAlignedBB; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.RayTraceResult; import net.minecraft.util.math.Vec3d; import net.minecraft.world.World; +import org.dimdev.dimdoors.DimDoors; import org.dimdev.dimdoors.shared.blocks.IRiftProvider; import org.dimdev.dimdoors.shared.tileentities.TileEntityFloatingRift; import org.dimdev.dimdoors.shared.tileentities.TileEntityRift; +import java.util.List; + public final class RayTraceHelper { public static boolean isFloatingRift(RayTraceResult hit, World world) { @@ -28,142 +33,182 @@ public static boolean isLivingEntity(RayTraceResult hit) { return hit != null && hit.typeOfHit == RayTraceResult.Type.ENTITY && hit.entityHit instanceof EntityLivingBase; } - //copied from MC source code, because Vanilla MC assumes that everything without a hitbox either can never be hit OR is a liquid - //TODO: clean this up? + /** + * returns a {@code RayTraceResult} for the first {@code EntityLiving} the look vector of + * {@code player} hits within {@code range} if it can find any. If the look + * vector is blocked by a solid block, before it collides with an {@code EntityLiving}, a + * {@code RayTraceResult} for this solid block is returned instead. If it fails to find + * either, this method returns null. + * + * @param world + * @param player + * @param range + * @param partialTicks + * @return + */ + public static RayTraceResult rayTraceEntity(World world, EntityPlayer player, double range, float partialTicks) { + //TODO: make this work on a server + Vec3d playerEyesVector = player.getPositionEyes(partialTicks); + Vec3d playerLookVector = player.getLookVec(); + + //Make sure that the raytrace stops if there's a block inbetween + RayTraceResult firstHitBlock = RayTraceHelper.rayTraceForRiftTools(world, player, range); + if (firstHitBlock != null) { + Vec3d hitVector = firstHitBlock.hitVec; + double distance = hitVector.distanceTo(playerEyesVector); + if (distance < range) { + range = distance; + } + } + + Vec3d tempVec = playerEyesVector; + Vec3d tempVec2; + for (double d = 0.2; d < range; d += 0.2) { + tempVec2 = playerEyesVector.add(playerLookVector.scale(d)); + List entities = world.getEntitiesWithinAABB(EntityLiving.class, new AxisAlignedBB(tempVec.x, tempVec.y, tempVec.z, tempVec2.x, tempVec2.y, tempVec2.z)); + for (EntityLiving entity : entities) { + if (entity == null) { //should never happen, but just in case + continue; + } + AxisAlignedBB entityHitbox = entity.getEntityBoundingBox(); + if (entityHitbox == null) { + DimDoors.log.info("The hitbox of Entity: " + entity + " was null, somehow"); + continue; + } + if (entityHitbox.contains(tempVec2)) { + return new RayTraceResult(entity); + } + } + tempVec = tempVec2; + } + return firstHitBlock; + } + public static RayTraceResult rayTraceForRiftTools(World worldIn, EntityPlayer playerIn) { - float f = playerIn.rotationPitch; - float f1 = playerIn.rotationYaw; - double d0 = playerIn.posX; - double d1 = playerIn.posY + (double) playerIn.getEyeHeight(); - double d2 = playerIn.posZ; - Vec3d playerVector = new Vec3d(d0, d1, d2); - float f2 = MathHelper.cos(-f1 * 0.017453292F - (float) Math.PI); - float f3 = MathHelper.sin(-f1 * 0.017453292F - (float) Math.PI); - float f4 = -MathHelper.cos(-f * 0.017453292F); - float f5 = MathHelper.sin(-f * 0.017453292F); - float f6 = f3 * f4; - float f7 = f2 * f4; - double d3 = playerIn.getEntityAttribute(EntityPlayer.REACH_DISTANCE).getAttributeValue(); - Vec3d cumulativeLookVector = playerVector.addVector((double) f6 * d3, (double) f5 * d3, (double) f7 * d3); - - if (!Double.isNaN(playerVector.x) && !Double.isNaN(playerVector.y) && !Double.isNaN(playerVector.z)) { - if (!Double.isNaN(cumulativeLookVector.x) && !Double.isNaN(cumulativeLookVector.y) && !Double.isNaN(cumulativeLookVector.z)) { - int i = MathHelper.floor(cumulativeLookVector.x); - int j = MathHelper.floor(cumulativeLookVector.y); - int k = MathHelper.floor(cumulativeLookVector.z); - int l = MathHelper.floor(playerVector.x); - int i1 = MathHelper.floor(playerVector.y); - int j1 = MathHelper.floor(playerVector.z); - BlockPos blockpos = new BlockPos(l, i1, j1); + return rayTraceForRiftTools(worldIn, playerIn, playerIn.getEntityAttribute(EntityPlayer.REACH_DISTANCE).getAttributeValue()); + } + + //partially copied from MC source code, because Vanilla MC assumes that everything without a hitbox either can never be hit OR is a liquid + //TODO: clean this up? + public static RayTraceResult rayTraceForRiftTools(World worldIn, EntityPlayer player, double range) { + + Vec3d playerLookVector = player.getLookVec(); + Vec3d tempVector = player.getPositionEyes(1.0F); //this is what constantly gets shifted in the while-loop later and it starts at the eyes + Vec3d endVector = tempVector.add(playerLookVector.scale(range)); + + if (!Double.isNaN(tempVector.x) && !Double.isNaN(tempVector.y) && !Double.isNaN(tempVector.z)) { + if (!Double.isNaN(endVector.x) && !Double.isNaN(endVector.y) && !Double.isNaN(endVector.z)) { + int endX = MathHelper.floor(endVector.x); + int endY = MathHelper.floor(endVector.y); + int endZ = MathHelper.floor(endVector.z); + int playerX = MathHelper.floor(tempVector.x); + int playerY = MathHelper.floor(tempVector.y); + int playerZ = MathHelper.floor(tempVector.z); + BlockPos blockpos = new BlockPos(playerX, playerY, playerZ); IBlockState iblockstate = worldIn.getBlockState(blockpos); Block block = iblockstate.getBlock(); - if (block instanceof IRiftProvider) { - RayTraceResult raytraceresult = iblockstate.collisionRayTrace(worldIn, blockpos, playerVector, cumulativeLookVector); - + if (block.canCollideCheck(iblockstate, false) || block instanceof IRiftProvider) { + RayTraceResult raytraceresult = iblockstate.collisionRayTrace(worldIn, blockpos, tempVector, endVector); if (raytraceresult != null) { return raytraceresult; } } - - RayTraceResult raytraceresult2 = null; - int k1 = 200; - - while (k1-- >= 0) { - if (Double.isNaN(playerVector.x) || Double.isNaN(playerVector.y) || Double.isNaN(playerVector.z) || (l == i && i1 == j && j1 == k)) { + + int counter = 200; //TODO: should this really be a hardcoded value? + while (counter-- >= 0) { + if (Double.isNaN(tempVector.x) || Double.isNaN(tempVector.y) || Double.isNaN(tempVector.z) || (playerX == endX && playerY == endY && playerZ == endZ)) { return null; } - boolean flag0 = true; - boolean flag1 = true; - boolean flag2 = true; - double d4 = 999.0D; - double d5 = 999.0D; - double d6 = 999.0D; - - if (i > l) { - d4 = (double) l + 1.0D; - } else if (i < l) { - d4 = (double) l + 0.0D; + boolean xFlag = true; + boolean yFlag = true; + boolean zFlag = true; + double x1 = 999.0D; + double y1 = 999.0D; + double z1 = 999.0D; + + if (endX > playerX) { + x1 = (double) playerX + 1.0D; + } else if (endX < playerX) { + x1 = (double) playerX; } else { - flag0 = false; + xFlag = false; } - if (j > i1) { - d5 = (double) i1 + 1.0D; - } else if (j < i1) { - d5 = (double) i1 + 0.0D; + if (endY > playerY) { + y1 = (double) playerY + 1.0D; + } else if (endY < playerY) { + y1 = (double) playerY; } else { - flag1 = false; + yFlag = false; } - if (k > j1) { - d6 = (double) j1 + 1.0D; - } else if (k < j1) { - d6 = (double) j1 + 0.0D; + if (endZ > playerZ) { + z1 = (double) playerZ + 1.0D; + } else if (endZ < playerZ) { + z1 = (double) playerZ; } else { - flag2 = false; + zFlag = false; } - double d7 = 999.0D; - double d8 = 999.0D; - double d9 = 999.0D; - double d10 = cumulativeLookVector.x - playerVector.x; - double d11 = cumulativeLookVector.y - playerVector.y; - double d12 = cumulativeLookVector.z - playerVector.z; + double x2 = endVector.x - tempVector.x; + double y2 = endVector.y - tempVector.y; + double z2 = endVector.z - tempVector.z; + double x3 = 999.0D; + double y3 = 999.0D; + double z3 = 999.0D; - if (flag0) { - d7 = (d4 - playerVector.x) / d10; + if (xFlag) { + x3 = (x1 - tempVector.x) / x2; } - if (flag1) { - d8 = (d5 - playerVector.y) / d11; + if (yFlag) { + y3 = (y1 - tempVector.y) / y2; } - if (flag2) { - d9 = (d6 - playerVector.z) / d12; + if (zFlag) { + z3 = (z1 - tempVector.z) / z2; } - if (d7 == -0.0D) { - d7 = -1.0E-4D; + if (x3 == -0.0D) { + x3 = -1.0E-4D; } - if (d8 == -0.0D) { - d8 = -1.0E-4D; + if (y3 == -0.0D) { + y3 = -1.0E-4D; } - if (d9 == -0.0D) { - d9 = -1.0E-4D; + if (z3 == -0.0D) { + z3 = -1.0E-4D; } EnumFacing enumfacing; - if (d7 < d8 && d7 < d9) { - enumfacing = i > l ? EnumFacing.WEST : EnumFacing.EAST; - playerVector = new Vec3d(d4, playerVector.y + d11 * d7, playerVector.z + d12 * d7); - } else if (d8 < d9) { - enumfacing = j > i1 ? EnumFacing.DOWN : EnumFacing.UP; - playerVector = new Vec3d(playerVector.x + d10 * d8, d5, playerVector.z + d12 * d8); + if (x3 < y3 && x3 < z3) { + enumfacing = endX > playerX ? EnumFacing.WEST : EnumFacing.EAST; + tempVector = new Vec3d(x1, tempVector.y + y2 * x3, tempVector.z + z2 * x3); + } else if (y3 < z3) { + enumfacing = endY > playerY ? EnumFacing.DOWN : EnumFacing.UP; + tempVector = new Vec3d(tempVector.x + x2 * y3, y1, tempVector.z + z2 * y3); } else { - enumfacing = k > j1 ? EnumFacing.NORTH : EnumFacing.SOUTH; - playerVector = new Vec3d(playerVector.x + d10 * d9, playerVector.y + d11 * d9, d6); + enumfacing = endZ > playerZ ? EnumFacing.NORTH : EnumFacing.SOUTH; + tempVector = new Vec3d(tempVector.x + x2 * z3, tempVector.y + y2 * z3, z1); } - l = MathHelper.floor(playerVector.x) - (enumfacing == EnumFacing.EAST ? 1 : 0); - i1 = MathHelper.floor(playerVector.y) - (enumfacing == EnumFacing.UP ? 1 : 0); - j1 = MathHelper.floor(playerVector.z) - (enumfacing == EnumFacing.SOUTH ? 1 : 0); - blockpos = new BlockPos(l, i1, j1); + playerX = MathHelper.floor(tempVector.x) - (enumfacing == EnumFacing.EAST ? 1 : 0); + playerY = MathHelper.floor(tempVector.y) - (enumfacing == EnumFacing.UP ? 1 : 0); + playerZ = MathHelper.floor(tempVector.z) - (enumfacing == EnumFacing.SOUTH ? 1 : 0); + blockpos = new BlockPos(playerX, playerY, playerZ); IBlockState iblockstate1 = worldIn.getBlockState(blockpos); Block block1 = iblockstate1.getBlock(); if (block1.canCollideCheck(iblockstate1, false) || block1 instanceof IRiftProvider) { - RayTraceResult raytraceresult1 = iblockstate1.collisionRayTrace(worldIn, blockpos, playerVector, cumulativeLookVector); + RayTraceResult raytraceresult1 = iblockstate1.collisionRayTrace(worldIn, blockpos, tempVector, endVector); if (raytraceresult1 != null) { return raytraceresult1; } - } else { - raytraceresult2 = new RayTraceResult(RayTraceResult.Type.MISS, playerVector, enumfacing, blockpos); } } }