Add archivist's eyeglass and spectral tether, plus some improvements to teleportation

This commit is contained in:
Electroblob77
2020-06-23 22:09:06 +01:00
parent 0ca86eb01b
commit 33c5f79ed7
16 changed files with 224 additions and 103 deletions
@@ -1,5 +1,6 @@
package electroblob.wizardry.util;
import com.google.common.collect.Streams;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.data.WizardData;
import electroblob.wizardry.entity.living.ISpellCaster;
@@ -17,17 +18,24 @@ import net.minecraft.item.ItemStack;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.server.MinecraftServer;
import net.minecraft.util.DamageSource;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.SoundCategory;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.EnumDifficulty;
import net.minecraft.world.World;
import net.minecraftforge.event.ForgeEventFactory;
import net.minecraftforge.fml.common.ObfuscationReflectionHelper;
import javax.annotation.Nullable;
import java.util.Comparator;
import java.util.List;
import java.util.UUID;
import java.util.function.Predicate;
import java.util.stream.Collectors;
/**
* Contains useful static methods for retrieving and interacting with players, mobs and other entities. These methods
@@ -182,6 +190,83 @@ public final class EntityUtils {
target.knockBack(attacker, 0.4f, dx, dz);
}
/**
* Finds the nearest space to the specified position that the given entity can teleport to without being inside one
* or more solid blocks. The search volume is twice the size of the entity's bounding box (meaning that when
* teleported to the returned position, the original destination remains within the entity's bounding box).
* @param entity The entity being teleported
* @param destination The target position to search around
* @param accountForPassengers True to take passengers into account when searching for a space, false to ignore them
* @return The resulting position, or null if no space was found.
*/
public static Vec3d findSpaceForTeleport(Entity entity, Vec3d destination, boolean accountForPassengers){
World world = entity.world;
AxisAlignedBB box = entity.getEntityBoundingBox();
if(accountForPassengers){
for(Entity passenger : entity.getPassengers()){
box = box.union(passenger.getEntityBoundingBox());
}
}
box = box.offset(destination.subtract(entity.posX, box.minY, entity.posZ));
// All the parameters of this method are INCLUSIVE, so even the max coordinates should be rounded down
Iterable<BlockPos> cuboid = BlockPos.getAllInBox(MathHelper.floor(box.minX), MathHelper.floor(box.minY),
MathHelper.floor(box.minZ), MathHelper.floor(box.maxX), MathHelper.floor(box.maxY), MathHelper.floor(box.maxZ));
if(Streams.stream(cuboid).noneMatch(b -> world.collidesWithAnyBlock(new AxisAlignedBB(b)))){
// Nothing in the way
return destination;
}else{
// Nearby position search
double dx = box.maxX - box.minX;
double dy = box.maxY - box.minY;
double dz = box.maxZ - box.minZ;
// Minimum space required is (nx + px) blocks * (ny + py) blocks * (nz + pz) blocks
int nx = MathHelper.ceil(dx) / 2;
int px = MathHelper.ceil(dx) - nx;
int ny = MathHelper.ceil(dy) / 2;
int py = MathHelper.ceil(dy) - ny;
int nz = MathHelper.ceil(dz) / 2;
int pz = MathHelper.ceil(dz) - nz;
// Check all the blocks in and around the bounding box...
List<BlockPos> nearby = Streams.stream(BlockPos.getAllInBox(MathHelper.floor(box.minX) - 1,
MathHelper.floor(box.minY) - 1, MathHelper.floor(box.minZ) - 1,
MathHelper.floor(box.maxX) + 1, MathHelper.floor(box.maxY) + 1,
MathHelper.floor(box.maxZ) + 1)).collect(Collectors.toList());
// ... but only return positions actually inside the box
List<BlockPos> possiblePositions = Streams.stream(cuboid).collect(Collectors.toList());
// Rather than iterate over each position and check if the box fits, find all solid blocks and cut out all
// positions whose corresponding box would include them - this is waaay more efficient!
while(!nearby.isEmpty()){
BlockPos pos = nearby.remove(0);
if(world.collidesWithAnyBlock(new AxisAlignedBB(pos))){
Predicate<BlockPos> nearSolidBlock = b -> b.getX() >= pos.getX() - nx && b.getX() <= pos.getX() + px
&& b.getY() >= pos.getY() - ny && b.getY() <= pos.getY() + py
&& b.getZ() >= pos.getZ() - nz && b.getZ() <= pos.getZ() + pz;
nearby.removeIf(nearSolidBlock);
possiblePositions.removeIf(nearSolidBlock);
}
}
if(possiblePositions.isEmpty()) return null; // No space nearby
BlockPos nearest = possiblePositions.stream().min(Comparator.comparingDouble(b -> destination.squareDistanceTo(
b.getX() + 0.5, b.getY() + 0.5, b.getZ() + 0.5))).get(); // The list can't be empty
return GeometryUtils.getFaceCentre(nearest, EnumFacing.DOWN);
}
}
// Damage
// ===============================================================================================================