Clean up raytracing

This commit is contained in:
Sollace 2020-09-27 20:07:55 +02:00
parent ac74b1fbd3
commit ea34a93bbc
9 changed files with 200 additions and 211 deletions

View file

@ -3,9 +3,7 @@ package com.minelittlepony.unicopia.ability;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.util.VecHelper;
import net.minecraft.entity.Entity;
import com.minelittlepony.unicopia.util.RayTraceHelper;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket;
@ -38,15 +36,9 @@ public class CarryAbility implements Ability<Hit> {
}
protected LivingEntity findRider(PlayerEntity player, World w) {
Entity hit = VecHelper.getLookedAtEntity(player, 10);
if (hit instanceof LivingEntity && !player.isConnectedThroughVehicle(hit)) {
if (!(hit instanceof IPickupImmuned)) {
return (LivingEntity)hit;
}
}
return null;
return RayTraceHelper.<LivingEntity>findEntity(player, 10, 1, hit -> {
return hit instanceof LivingEntity && !player.isConnectedThroughVehicle(hit) && !(hit instanceof IPickupImmuned);
}).orElse(null);
}
@Override

View file

@ -8,19 +8,14 @@ import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.entity.behaviour.Disguise;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.util.VecHelper;
import net.minecraft.block.BlockState;
import com.minelittlepony.unicopia.util.RayTraceHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.FallingBlockEntity;
import net.minecraft.entity.LightningEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
/**
* Changeling ability to disguise themselves as other players.
@ -36,33 +31,21 @@ public class ChangelingDisguiseAbility extends ChangelingFeedAbility {
@Override
public void apply(Pony iplayer, Hit data) {
PlayerEntity player = iplayer.getOwner();
HitResult trace = VecHelper.getObjectMouseOver(player, 10, 1);
Entity looked = null;
if (trace.getType() == HitResult.Type.BLOCK) {
BlockPos pos = ((BlockHitResult)trace).getBlockPos();
RayTraceHelper.Trace trace = RayTraceHelper.doTrace(player, 10, 1, EntityPredicates.EXCEPT_SPECTATOR.and(e -> !(e instanceof LightningEntity)));
Entity looked = trace.getEntity().map(e -> {
return e instanceof PlayerEntity ? Pony.of((PlayerEntity)e)
.getSpellOrEmpty(DisguiseSpell.class)
.map(DisguiseSpell::getDisguise)
.map(Disguise::getAppearance)
.orElse(e) : e;
}).orElseGet(() -> trace.getBlockPos().map(pos -> {
if (!iplayer.getWorld().isAir(pos)) {
BlockState state = iplayer.getWorld().getBlockState(pos);
looked = new FallingBlockEntity(player.getEntityWorld(), 0, 0, 0, state);
return new FallingBlockEntity(player.getEntityWorld(), 0, 0, 0, iplayer.getWorld().getBlockState(pos));
}
} else if (trace.getType() == HitResult.Type.ENTITY) {
looked = ((EntityHitResult)trace).getEntity();
if (looked instanceof PlayerEntity) {
looked = Pony.of((PlayerEntity)looked)
.getSpellOrEmpty(DisguiseSpell.class)
.map(DisguiseSpell::getDisguise)
.map(Disguise::getAppearance)
.orElse(looked);
}
if (looked instanceof LightningEntity) {
looked = null;
}
}
return null;
}).orElse(null));
player.getEntityWorld().playSound(null, player.getBlockPos(), SoundEvents.ENTITY_PARROT_IMITATE_RAVAGER, SoundCategory.PLAYERS, 1.4F, 0.4F);
@ -72,8 +55,8 @@ public class ChangelingDisguiseAbility extends ChangelingFeedAbility {
iplayer.setSpell(disc);
return disc;
}).setDisguise(looked);
player.calculateDimensions();
player.calculateDimensions();
iplayer.setDirty();
}

View file

@ -11,6 +11,7 @@ import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.util.MagicalDamageSource;
import com.minelittlepony.unicopia.util.RayTraceHelper;
import com.minelittlepony.unicopia.util.VecHelper;
import net.minecraft.entity.Entity;
@ -78,12 +79,11 @@ public class ChangelingFeedAbility implements Ability<Hit> {
}
protected List<LivingEntity> getTargets(Pony player) {
List<Entity> list = VecHelper.getWithinRange(player.getOwner(), 3, this::canDrain);
List<Entity> list = VecHelper.getWithinReach(player.getOwner(), 3, this::canDrain);
Entity looked = VecHelper.getLookedAtEntity(player.getOwner(), 17);
if (looked != null && !list.contains(looked) && canDrain(looked)) {
list.add(looked);
}
RayTraceHelper.<LivingEntity>findEntity(player.getOwner(), 17, 1,
looked -> looked instanceof LivingEntity && !list.contains(looked) && canDrain(looked))
.ifPresent(list::add);
return list.stream().map(i -> (LivingEntity)i).collect(Collectors.toList());
}

View file

@ -5,14 +5,11 @@ import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.data.Pos;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.util.VecHelper;
import com.minelittlepony.unicopia.util.RayTraceHelper;
import net.minecraft.block.BlockState;
import net.minecraft.item.BoneMealItem;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World;
@ -39,13 +36,7 @@ public class EarthPonyGrowAbility implements Ability<Pos> {
@Override
public Pos tryActivate(Pony player) {
HitResult ray = VecHelper.getObjectMouseOver(player.getOwner(), 3, 1);
if (ray instanceof BlockHitResult && ray.getType() == HitResult.Type.BLOCK) {
return new Pos(((BlockHitResult)ray).getBlockPos());
}
return null;
return RayTraceHelper.doTrace(player.getOwner(), 3, 1).getBlockPos().map(Pos::new).orElse(null);
}
@Override

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.ability;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import javax.annotation.Nullable;
@ -14,7 +15,7 @@ import com.minelittlepony.unicopia.ability.data.Multi;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.util.MagicalDamageSource;
import com.minelittlepony.unicopia.util.PosHelper;
import com.minelittlepony.unicopia.util.VecHelper;
import com.minelittlepony.unicopia.util.RayTraceHelper;
import com.minelittlepony.unicopia.util.WorldEvent;
import com.minelittlepony.unicopia.util.shape.Shape;
import com.minelittlepony.unicopia.util.shape.Sphere;
@ -31,8 +32,6 @@ import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.particle.BlockStateParticleEffect;
import net.minecraft.particle.ParticleTypes;
import net.minecraft.tag.BlockTags;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Direction;
@ -70,10 +69,10 @@ public class EarthPonyStompAbility implements Ability<Multi> {
@Nullable
@Override
public Multi tryActivate(Pony player) {
HitResult mop = VecHelper.getObjectMouseOver(player.getOwner(), 6, 1);
Optional<BlockPos> p = RayTraceHelper.doTrace(player.getOwner(), 6, 1).getBlockPos();
if (mop instanceof BlockHitResult && mop.getType() == HitResult.Type.BLOCK) {
BlockPos pos = ((BlockHitResult)mop).getBlockPos();
if (p.isPresent()) {
BlockPos pos = p.get();
BlockState state = player.getWorld().getBlockState(pos);
if (state.getBlock().isIn(BlockTags.LOGS)) {

View file

@ -5,8 +5,7 @@ import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.data.Pos;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.util.VecHelper;
import com.minelittlepony.unicopia.util.RayTraceHelper;
import net.minecraft.block.Block;
import net.minecraft.block.BlockState;
import net.minecraft.block.FenceBlock;
@ -15,6 +14,7 @@ import net.minecraft.block.WallBlock;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.packet.s2c.play.EntityPassengersSetS2CPacket;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.server.network.ServerPlayerEntity;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvents;
@ -45,7 +45,7 @@ public class UnicornTeleportAbility implements Ability<Pos> {
@Override
public Pos tryActivate(Pony player) {
HitResult ray = VecHelper.getObjectMouseOver(player.getOwner(), 100, 1);
HitResult ray = RayTraceHelper.doTrace(player.getOwner(), 100, 1, EntityPredicates.EXCEPT_SPECTATOR).getResult();
World w = player.getWorld();

View file

@ -1,11 +1,12 @@
package com.minelittlepony.unicopia.item;
import java.util.Optional;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.item.toxin.Toxicity;
import com.minelittlepony.unicopia.util.MagicalDamageSource;
import com.minelittlepony.unicopia.util.VecHelper;
import com.minelittlepony.unicopia.util.RayTraceHelper;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LightningEntity;
@ -18,6 +19,7 @@ import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.ActionResult;
import net.minecraft.util.collection.DefaultedList;
@ -25,8 +27,6 @@ import net.minecraft.util.Hand;
import net.minecraft.util.Identifier;
import net.minecraft.util.Rarity;
import net.minecraft.util.TypedActionResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.World;
@ -38,16 +38,12 @@ public class ZapAppleItem extends AppleItem {
@Override
public TypedActionResult<ItemStack> use(World world, PlayerEntity player, Hand hand) {
HitResult mop = VecHelper.getObjectMouseOver(player, 5, 0);
ItemStack stack = player.getStackInHand(hand);
if (mop != null && mop.getType() == HitResult.Type.ENTITY) {
ItemStack stack = player.getStackInHand(hand);
Optional<Entity> entity = RayTraceHelper.doTrace(player, 5, 1, EntityPredicates.EXCEPT_SPECTATOR.and(e -> canFeedTo(stack, e))).getEntity();
EntityHitResult ehr = ((EntityHitResult)mop);
if (canFeedTo(stack, ehr.getEntity())) {
return onFedTo(stack, player, ehr.getEntity());
}
if (entity.isPresent()) {
return onFedTo(stack, player, entity.get());
}
return super.use(world, player, hand);

View file

@ -0,0 +1,158 @@
package com.minelittlepony.unicopia.util;
import java.util.List;
import java.util.Optional;
import java.util.function.Consumer;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import net.minecraft.entity.Entity;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.RayTraceContext;
public class RayTraceHelper {
public static <T extends Entity> Optional<T> findEntity(Entity e, double distance) {
return doTrace(e, distance, 1).getEntity();
}
public static <T extends Entity> Optional<T> findEntity(Entity e, double distance, float partialTick, Predicate<Entity> predicate) {
return doTrace(e, distance, partialTick, EntityPredicates.EXCEPT_SPECTATOR.and(predicate)).getEntity();
}
public static Trace doTrace(Entity e, double distance, float partialTick) {
return doTrace(e, distance, partialTick, EntityPredicates.EXCEPT_SPECTATOR);
}
/**
* Performs a ray trace from the given entity and returns a result for the first Entity that passing the given predicate or block that the ray intercepts.
* <p>
*
*
* @param e Entity to start from
* @param distance Maximum distance
* @param partialTick Client partial ticks
* @param predicate Predicate test to filter entities
*
* @return A OptionalHit describing what was found.
*/
public static Trace doTrace(Entity e, double distance, float partialTick, Predicate<Entity> predicate) {
HitResult tracedBlock = traceBlocks(e, distance, partialTick, false);
double totalTraceDistance = distance;
Vec3d pos = e.getCameraPosVec(partialTick);
if (tracedBlock != null) {
totalTraceDistance = tracedBlock.getPos().distanceTo(pos);
}
Vec3d look = e.getRotationVec(partialTick);
Vec3d ray = pos.add(look.multiply(distance));
Vec3d hit = null;
Entity pointedEntity = null;
List<Entity> entitiesWithinRange = e.world.getOtherEntities(e, e.getBoundingBox()
.expand(look.x * distance, look.y * distance, look.z * distance)
.expand(1, 1, 1), predicate);
double traceDistance = totalTraceDistance;
for (Entity entity : entitiesWithinRange) {
if (entity.collides()) {
Box entityAABB = entity.getBoundingBox().expand(entity.getTargetingMargin());
Optional<Vec3d> intercept = entityAABB.rayTrace(pos, ray);
if (entityAABB.contains(pos)) {
if (traceDistance <= 0) {
pointedEntity = entity;
hit = intercept.orElse(null);
traceDistance = 0;
}
} else if (intercept.isPresent()) {
Vec3d inter = intercept.get();
double distanceToHit = pos.distanceTo(inter);
if (distanceToHit < traceDistance || traceDistance == 0) {
if (entity.getRootVehicle() == e.getRootVehicle()) {
if (traceDistance == 0) {
pointedEntity = entity;
hit = inter;
}
} else {
pointedEntity = entity;
hit = inter;
traceDistance = distanceToHit;
}
}
}
}
}
if (pointedEntity != null && (traceDistance < totalTraceDistance || tracedBlock == null)) {
return new Trace(new EntityHitResult(pointedEntity, hit));
}
return new Trace(tracedBlock);
}
/**
* Server-available version of Entity.rayTrace
*/
public static HitResult traceBlocks(Entity e, double maxDistance, float tickDelta, boolean includeFluids) {
Vec3d start = e.getCameraPosVec(tickDelta);
Vec3d end = e.getRotationVec(tickDelta).multiply(maxDistance).add(start);
return e.world.rayTrace(new RayTraceContext(start, end,
RayTraceContext.ShapeType.OUTLINE,
includeFluids ? RayTraceContext.FluidHandling.ANY : RayTraceContext.FluidHandling.NONE,
e)
);
}
public static class Trace {
@Nullable
private final HitResult result;
Trace(@Nullable HitResult result) {
this.result = result;
}
@Nullable
public HitResult getResult() {
return result;
}
@SuppressWarnings("unchecked")
public <T extends Entity> Optional<T> getEntity() {
if (result != null && result.getType() == HitResult.Type.ENTITY) {
return Optional.of((T)((EntityHitResult)result).getEntity());
}
return Optional.empty();
}
public Optional<BlockPos> getBlockPos() {
if (result != null && result.getType() == HitResult.Type.BLOCK) {
return Optional.of(((BlockHitResult)result).getBlockPos());
}
return Optional.empty();
}
public Trace ifEntity(Consumer<Entity> consumer) {
getEntity().ifPresent(consumer);
return this;
}
public Trace ifBlock(Consumer<BlockPos> consumer) {
getBlockPos().ifPresent(consumer);
return this;
}
}
}

View file

@ -1,63 +1,19 @@
package com.minelittlepony.unicopia.util;
import java.util.List;
import java.util.Optional;
import java.util.function.Predicate;
import java.util.stream.Stream;
import javax.annotation.Nullable;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.predicate.entity.EntityPredicates;
import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.hit.EntityHitResult;
import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.RayTraceContext;
import net.minecraft.world.World;
public interface VecHelper {
/**
* Performs a ray cast from the given entity and returns a result for the first block that ray intercepts.
*
* Server-available version of Entity.rayTrace
*
* @param e Entity to start from
* @param distance Maximum distance
* @param partialTick Client partial ticks
*
* @return RayTraceResult result or null
*/
static HitResult rayTrace(Entity e, double distance, float partialTick) {
Vec3d cam = e.getCameraPosVec(partialTick);
Vec3d look = e.getRotationVec(partialTick).multiply(distance);
return e.world.rayTrace(
new RayTraceContext(cam, cam.add(look),
RayTraceContext.ShapeType.OUTLINE,
RayTraceContext.FluidHandling.NONE,
e)
);
}
/**
* Gets the entity the player is currently looking at, or null.
*/
@Nullable
static Entity getLookedAtEntity(LivingEntity e, int reach) {
HitResult objectMouseOver = getObjectMouseOver(e, reach, 1);
if (objectMouseOver instanceof EntityHitResult && objectMouseOver.getType() == BlockHitResult.Type.ENTITY) {
return ((EntityHitResult)objectMouseOver).getEntity();
}
return null;
}
static Stream<Entity> findAllEntitiesInRange(@Nullable Entity origin, World w, BlockPos pos, double radius) {
return w.getOtherEntities(origin, new Box(pos).expand(radius), e -> {
double dist = Math.sqrt(e.squaredDistanceTo(pos.getX(), pos.getY(), pos.getZ()));
@ -70,7 +26,7 @@ public interface VecHelper {
/**
* Gets all entities within a given range from the player.
*/
static List<Entity> getWithinRange(PlayerEntity player, double reach, @Nullable Predicate<? super Entity> predicate) {
static List<Entity> getWithinReach(PlayerEntity player, double reach, @Nullable Predicate<? super Entity> predicate) {
Vec3d look = player.getCameraPosVec(1).multiply(reach);
return player.world.getOtherEntities(player, player
@ -78,90 +34,4 @@ public interface VecHelper {
.expand(look.x, look.y, look.z)
.expand(1, 1, 1), predicate);
}
/**
* Performs a ray trace from the given entity and returns a result for the first Entity or block that the ray intercepts.
*
* @param e Entity to start from
* @param distance Maximum distance
* @param partialTick Client partial ticks
*
* @return RayTraceResult result or null
*/
static HitResult getObjectMouseOver(Entity e, double distance, float partialTick) {
return getObjectMouseOver(e, distance, partialTick, EntityPredicates.EXCEPT_SPECTATOR);
}
/**
* Performs a ray trace from the given entity and returns a result for the first Entity that passing the given predicate or block that the ray intercepts.
* <p>
*
*
* @param e Entity to start from
* @param distance Maximum distance
* @param partialTick Client partial ticks
* @param predicate Predicate test to filter entities
*
* @return RayTraceResult result or null
*/
static HitResult getObjectMouseOver(Entity e, double distance, float partialTick, Predicate<Entity> predicate) {
HitResult tracedBlock = rayTrace(e, distance, partialTick);
double totalTraceDistance = distance;
Vec3d pos = e.getCameraPosVec(partialTick);
if (tracedBlock != null) {
totalTraceDistance = tracedBlock.getPos().distanceTo(pos);
}
Vec3d look = e.getRotationVec(partialTick);
Vec3d ray = pos.add(look.multiply(distance));
Vec3d hit = null;
Entity pointedEntity = null;
List<Entity> entitiesWithinRange = e.world.getOtherEntities(e, e.getBoundingBox()
.expand(look.x * distance, look.y * distance, look.z * distance)
.expand(1, 1, 1), predicate);
double traceDistance = totalTraceDistance;
for (Entity entity : entitiesWithinRange) {
if (entity.collides()) {
Box entityAABB = entity.getBoundingBox().expand(entity.getTargetingMargin());
Optional<Vec3d> intercept = entityAABB.rayTrace(pos, ray);
if (entityAABB.contains(pos)) {
if (traceDistance <= 0) {
pointedEntity = entity;
hit = intercept.orElse(null);
traceDistance = 0;
}
} else if (intercept.isPresent()) {
Vec3d inter = intercept.get();
double distanceToHit = pos.distanceTo(inter);
if (distanceToHit < traceDistance || traceDistance == 0) {
if (entity.getRootVehicle() == e.getRootVehicle()) {
if (traceDistance == 0) {
pointedEntity = entity;
hit = inter;
}
} else {
pointedEntity = entity;
hit = inter;
traceDistance = distanceToHit;
}
}
}
}
}
if (pointedEntity != null && (traceDistance < totalTraceDistance || tracedBlock == null)) {
return new EntityHitResult(pointedEntity, hit);
}
return tracedBlock;
}
}