Ssome more cleanup

This commit is contained in:
Sollace 2020-10-10 16:25:49 +02:00
parent 312afe3d46
commit 5c2e156392
4 changed files with 174 additions and 178 deletions

View file

@ -16,125 +16,138 @@ import net.minecraft.world.World;
public class TreeTraverser { public class TreeTraverser {
public static void removeTree(World w, BlockPos pos) { public static class Remover {
BlockState log = w.getBlockState(pos); /**
* Removes the tree located at the given position.
*/
public static void removeTree(World w, BlockPos pos) {
BlockState log = w.getBlockState(pos);
int size = measureTree(w, log, pos); if (Measurer.measureTree(w, log, pos) > 0) {
removeTreePart(w, log, Ascender.ascendTrunk(new HashSet<BlockPos>(), w, pos, log, 0).get(), 0);
if (size > 0) {
removeTreePart(w, log, ascendTrunk(new HashSet<BlockPos>(), w, pos, log, 0).get(), 0);
}
}
private static void removeTreePart(World w, BlockState log, BlockPos pos, int level) {
if (level < 10 && isWoodOrLeaf(w, log, pos)) {
if (level < 5) {
w.breakBlock(pos, true);
} else {
Block.dropStacks(w.getBlockState(pos), w, pos);
w.setBlockState(pos, Blocks.AIR.getDefaultState(), 3);
} }
}
PosHelper.all(pos, p -> { private static void removeTreePart(World w, BlockState log, BlockPos pos, int level) {
removeTreePart(w, log, p, level + 1); if (level < 10 && isWoodOrLeaf(w, log, pos)) {
}, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST); breakBlock(w, pos, level < 5);
PosHelper.all(pos, p -> {
removeTreePart(w, log, p, level + 1);
}, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST);
}
} }
} }
private static Optional<BlockPos> ascendTrunk(Set<BlockPos> done, World w, BlockPos pos, BlockState log, int level) { public static class Ascender {
if (level < 3 && !done.contains(pos)) { /**
done.add(pos); * Locates the top of the tree's trunk. Usually the point where wood meets leaves.
*/
BlockPos.Mutable result = new BlockPos.Mutable(); public static BlockPos ascendTree(World w, BlockState log, BlockPos pos, boolean remove) {
result.set(ascendTree(w, log, pos, true)); int breaks = 0;
while (variantAndBlockEquals(w.getBlockState(pos.up()), log)) {
PosHelper.all(pos, p -> { if (PosHelper.any(pos, p -> isLeaves(w.getBlockState(p), log), Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST)) {
if (variantAndBlockEquals(w.getBlockState(pos.east()), log)) { break;
ascendTrunk(done, w, pos.east(), log, level + 1).filter(a -> a.getY() > result.getY()).ifPresent(result::set);
} }
}, Direction.EAST, Direction.WEST, Direction.NORTH, Direction.SOUTH);
done.add(result.toImmutable()); if (remove) {
return Optional.of(result.toImmutable()); breakBlock(w, pos, breaks++ < 10);
}
return Optional.of(pos);
}
public static BlockPos ascendTree(World w, BlockState log, BlockPos pos, boolean remove) {
int breaks = 0;
BlockState state;
while (variantAndBlockEquals(w.getBlockState(pos.up()), log)) {
if (PosHelper.some(pos, p -> isLeaves(w.getBlockState(p), log), Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST)) {
break;
}
if (remove) {
if (breaks < 10) {
w.breakBlock(pos, true);
} else {
state = w.getBlockState(pos);
Block.dropStacks(state, w, pos);
w.setBlockState(pos, Blocks.AIR.getDefaultState(), 3);
} }
breaks++; pos = pos.up();
} }
pos = pos.up(); return pos;
}
static Optional<BlockPos> ascendTrunk(Set<BlockPos> done, World w, BlockPos pos, BlockState log, int level) {
if (level < 3 && !done.contains(pos)) {
done.add(pos);
BlockPos.Mutable result = new BlockPos.Mutable();
result.set(ascendTree(w, log, pos, true));
PosHelper.all(pos, p -> {
if (variantAndBlockEquals(w.getBlockState(pos.east()), log)) {
ascendTrunk(done, w, pos.east(), log, level + 1).filter(a -> a.getY() > result.getY()).ifPresent(result::set);
}
}, Direction.EAST, Direction.WEST, Direction.NORTH, Direction.SOUTH);
done.add(result.toImmutable());
return Optional.of(result.toImmutable());
}
return Optional.of(pos);
} }
return pos;
} }
public static Optional<BlockPos> descendTree(World w, BlockState log, BlockPos pos) { public static class Descender {
return descendTreePart(new HashSet<BlockPos>(), w, log, new BlockPos.Mutable(pos.getX(), pos.getY(), pos.getZ())); /**
* Recursively locates the base of the tree.
*/
public static Optional<BlockPos> descendTree(World w, BlockState log, BlockPos pos) {
return descendTreePart(new HashSet<BlockPos>(), w, log, new BlockPos.Mutable(pos.getX(), pos.getY(), pos.getZ()));
}
private static Optional<BlockPos> descendTreePart(Set<BlockPos> done, World w, BlockState log, BlockPos.Mutable pos) {
if (done.contains(pos) || !variantAndBlockEquals(w.getBlockState(pos), log)) {
return Optional.empty();
}
done.add(pos.toImmutable());
while (variantAndBlockEquals(w.getBlockState(pos.down()), log)) {
done.add(pos.move(Direction.DOWN).toImmutable());
}
PosHelper.all(pos.toImmutable(), p -> {
descendTreePart(done, w, log, new BlockPos.Mutable(p.getX(), p.getY(), p.getZ())).filter(a -> a.getY() < pos.getY()).ifPresent(pos::set);
}, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST);
done.add(pos.toImmutable());
return Optional.of(pos.toImmutable());
}
} }
private static Optional<BlockPos> descendTreePart(Set<BlockPos> done, World w, BlockState log, BlockPos.Mutable pos) { public static class Measurer {
if (done.contains(pos) || !variantAndBlockEquals(w.getBlockState(pos), log)) { /**
return Optional.empty(); * Counts the number of logs and leaves present in the targeted tree.
*/
public static int measureTree(World w, BlockState log, BlockPos pos) {
Set<BlockPos> logs = new HashSet<>();
Set<BlockPos> leaves = new HashSet<>();
countParts(logs, leaves, w, log, pos);
return logs.size() <= (leaves.size() / 2) ? logs.size() + leaves.size() : 0;
} }
done.add(pos.toImmutable()); private static void countParts(Set<BlockPos> logs, Set<BlockPos> leaves, World w, BlockState log, BlockPos pos) {
while (variantAndBlockEquals(w.getBlockState(pos.down()), log)) { if (logs.contains(pos) || leaves.contains(pos)) {
done.add(pos.move(Direction.DOWN).toImmutable()); return;
}
BlockState state = w.getBlockState(pos);
boolean yay = false;
if (isLeaves(state, log) && !state.get(LeavesBlock.PERSISTENT)) {
leaves.add(pos);
yay = true;
} else if (variantAndBlockEquals(state, log)) {
logs.add(pos);
yay = true;
}
if (yay) {
PosHelper.all(pos, p -> {
countParts(logs, leaves, w, log, p);
}, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST);
}
} }
PosHelper.all(pos.toImmutable(), p -> {
descendTreePart(done, w, log, new BlockPos.Mutable(p.getX(), p.getY(), p.getZ())).filter(a -> a.getY() < pos.getY()).ifPresent(pos::set);
}, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST);
done.add(pos.toImmutable());
return Optional.of(pos.toImmutable());
} }
public static int measureTree(World w, BlockState log, BlockPos pos) { private static void breakBlock(World w, BlockPos pos, boolean destroy) {
Set<BlockPos> logs = new HashSet<>(); if (destroy) {
Set<BlockPos> leaves = new HashSet<>(); w.breakBlock(pos, true);
} else {
countParts(logs, leaves, w, log, pos); Block.dropStacks(w.getBlockState(pos), w, pos);
w.setBlockState(pos, Blocks.AIR.getDefaultState(), 3);
return logs.size() <= (leaves.size() / 2) ? logs.size() + leaves.size() : 0;
}
private static void countParts(Set<BlockPos> logs, Set<BlockPos> leaves, World w, BlockState log, BlockPos pos) {
if (logs.contains(pos) || leaves.contains(pos)) {
return;
}
BlockState state = w.getBlockState(pos);
boolean yay = false;
if (isLeaves(state, log) && !state.get(LeavesBlock.PERSISTENT)) {
leaves.add(pos);
yay = true;
} else if (variantAndBlockEquals(state, log)) {
logs.add(pos);
yay = true;
}
if (yay) {
PosHelper.all(pos, p -> {
countParts(logs, leaves, w, log, p);
}, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST);
} }
} }

View file

@ -81,8 +81,8 @@ public class EarthPonyStompAbility implements Ability<Multi> {
BlockState state = player.getWorld().getBlockState(pos); BlockState state = player.getWorld().getBlockState(pos);
if (state.getBlock().isIn(BlockTags.LOGS)) { if (state.getBlock().isIn(BlockTags.LOGS)) {
pos = TreeTraverser.descendTree(player.getWorld(), state, pos).get(); pos = TreeTraverser.Descender.descendTree(player.getWorld(), state, pos).get();
if (TreeTraverser.measureTree(player.getWorld(), state, pos) > 0) { if (TreeTraverser.Measurer.measureTree(player.getWorld(), state, pos) > 0) {
return new Multi(pos, 1); return new Multi(pos, 1);
} }
} }
@ -164,7 +164,7 @@ public class EarthPonyStompAbility implements Ability<Multi> {
if (harmed || player.world.random.nextInt(5) == 0) { if (harmed || player.world.random.nextInt(5) == 0) {
if (!harmed || player.world.random.nextInt(30) == 0) { if (!harmed || player.world.random.nextInt(30) == 0) {
TreeTraverser.removeTree(player.world, data.pos()); TreeTraverser.Remover.removeTree(player.world, data.pos());
} }
iplayer.subtractEnergyCost(3); iplayer.subtractEnergyCost(3);
@ -223,7 +223,7 @@ public class EarthPonyStompAbility implements Ability<Multi> {
private int dropApples(World w, BlockPos pos) { private int dropApples(World w, BlockPos pos) {
BlockState log = w.getBlockState(pos); BlockState log = w.getBlockState(pos);
int size = TreeTraverser.measureTree(w, log, pos); int size = TreeTraverser.Measurer.measureTree(w, log, pos);
if (size > 0) { if (size > 0) {
List<ItemEntity> capturedDrops = Lists.newArrayList(); List<ItemEntity> capturedDrops = Lists.newArrayList();
@ -244,7 +244,7 @@ public class EarthPonyStompAbility implements Ability<Multi> {
private static void dropApplesPart(List<ItemEntity> drops, List<BlockPos> done, World w, BlockState log, BlockPos pos, int level) { private static void dropApplesPart(List<ItemEntity> drops, List<BlockPos> done, World w, BlockState log, BlockPos pos, int level) {
if (!done.contains(pos)) { if (!done.contains(pos)) {
done.add(pos); done.add(pos);
pos = TreeTraverser.ascendTree(w, log, pos, false); pos = TreeTraverser.Ascender.ascendTree(w, log, pos, false);
if (level < 10 && TreeTraverser.isWoodOrLeaf(w, log, pos)) { if (level < 10 && TreeTraverser.isWoodOrLeaf(w, log, pos)) {
BlockState state = w.getBlockState(pos); BlockState state = w.getBlockState(pos);

View file

@ -1,7 +1,6 @@
package com.minelittlepony.unicopia.util; package com.minelittlepony.unicopia.util;
import java.util.Iterator; import java.util.Iterator;
import java.util.List;
import java.util.Spliterator; import java.util.Spliterator;
import java.util.Spliterators.AbstractSpliterator; import java.util.Spliterators.AbstractSpliterator;
import java.util.function.Consumer; import java.util.function.Consumer;
@ -39,7 +38,7 @@ public interface PosHelper {
} }
} }
static boolean some(BlockPos origin, Predicate<BlockPos> consumer, Direction... directions) { static boolean any(BlockPos origin, Predicate<BlockPos> consumer, Direction... directions) {
for (Direction facing : directions) { for (Direction facing : directions) {
if (consumer.test(origin.offset(facing))) { if (consumer.test(origin.offset(facing))) {
return true; return true;
@ -49,17 +48,15 @@ public interface PosHelper {
} }
static Stream<BlockPos> adjacentNeighbours(BlockPos origin) { static Stream<BlockPos> adjacentNeighbours(BlockPos origin) {
BlockPos.Mutable pos = new BlockPos.Mutable(origin.getX(), origin.getY(), origin.getZ()); return StreamSupport.stream(new AbstractSpliterator<BlockPos>(Direction.values().length, Spliterator.SIZED) {
List<Direction> directions = Lists.newArrayList(Direction.values()); private final BlockPos.Mutable pos = new BlockPos.Mutable();
Iterator<Direction> iter = directions.iterator(); private final Iterator<Direction> iter = Lists.newArrayList(Direction.values()).iterator();
return StreamSupport.stream(new AbstractSpliterator<BlockPos>(directions.size(), Spliterator.SIZED) {
@Override @Override
public boolean tryAdvance(Consumer<? super BlockPos> consumer) { public boolean tryAdvance(Consumer<? super BlockPos> consumer) {
if (iter.hasNext()) { if (iter.hasNext()) {
Direction next = iter.next(); Direction next = iter.next();
consumer.accept(pos.set(origin.getX() + next.getOffsetX(), origin.getY() + next.getOffsetY(), origin.getZ() + next.getOffsetZ()));
pos.set(origin.getX() + next.getOffsetX(), origin.getY() + next.getOffsetY(), origin.getZ() + next.getOffsetZ());
consumer.accept(pos);
return true; return true;
} }
return false; return false;

View file

@ -1,6 +1,5 @@
package com.minelittlepony.unicopia.util; package com.minelittlepony.unicopia.util;
import java.util.List;
import java.util.Optional; import java.util.Optional;
import java.util.function.Consumer; import java.util.function.Consumer;
import java.util.function.Predicate; import java.util.function.Predicate;
@ -15,82 +14,83 @@ import net.minecraft.util.hit.HitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Box; import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.RayTraceContext;
public class RayTraceHelper { public class RayTraceHelper {
public static <T extends Entity> Optional<T> findEntity(Entity e, double distance) { public static <T extends Entity> Optional<T> findEntity(Entity e, double distance, float tickDelta, Predicate<Entity> predicate) {
return doTrace(e, distance, 1).getEntity(); return doTrace(e, distance, tickDelta, EntityPredicates.EXCEPT_SPECTATOR.and(predicate)).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. * Performs a ray trace from the given entity and returns
* <p> * a result for the first Entity or block that the ray intercepts.
* *
* *
* @param e Entity to start from * @param e Entity to start from
* @param distance Maximum distance * @param distance Maximum distance
* @param partialTick Client partial ticks * @param tickDelta Client partial ticks
* @param predicate Predicate test to filter entities
* *
* @return A OptionalHit describing what was found. * @return A Trace describing what was found.
*/ */
public static Trace doTrace(Entity e, double distance, float partialTick, Predicate<Entity> predicate) { public static Trace doTrace(Entity e, double distance, float tickDelta) {
HitResult tracedBlock = traceBlocks(e, distance, partialTick, false); return doTrace(e, distance, tickDelta, EntityPredicates.EXCEPT_SPECTATOR);
}
double totalTraceDistance = distance; /**
* Performs a ray trace from the given entity and returns
* a result for the first Entity that passes the given predicate
* or block that the ray intercepts.
*
*
* @param e Entity to start from
* @param distance Maximum distance
* @param tickDelta Client partial ticks
* @param predicate Predicate test to filter entities
*
* @return A Trace describing what was found.
*/
public static Trace doTrace(Entity e, double distance, float tickDelta, Predicate<Entity> predicate) {
HitResult tracedBlock = e.rayTrace(distance, tickDelta, false);
Vec3d pos = e.getCameraPosVec(partialTick); final Vec3d start = e.getCameraPosVec(tickDelta);
if (tracedBlock != null) { final double totalTraceDistance = tracedBlock == null ? distance : tracedBlock.getPos().distanceTo(start);
totalTraceDistance = tracedBlock.getPos().distanceTo(pos);
}
Vec3d look = e.getRotationVec(partialTick); final Vec3d ray = e.getRotationVec(tickDelta).multiply(distance);
Vec3d ray = pos.add(look.multiply(distance)); final Vec3d end = start.add(ray);
Vec3d hit = null; Vec3d hit = null;
Entity pointedEntity = 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; double traceDistance = totalTraceDistance;
for (Entity entity : entitiesWithinRange) { for (Entity entity : e.world.getOtherEntities(e,
if (entity.collides()) { e.getBoundingBox().expand(ray.x + 1, ray.y + 1, ray.z + 1),
Box entityAABB = entity.getBoundingBox().expand(entity.getTargetingMargin()); predicate.and(Entity::collides)
)) {
Box entityAABB = entity.getBoundingBox().expand(entity.getTargetingMargin());
Optional<Vec3d> intercept = entityAABB.rayTrace(pos, ray); Optional<Vec3d> intercept = entityAABB.rayTrace(start, end);
if (entityAABB.contains(pos)) { if (entityAABB.contains(start)) {
if (traceDistance <= 0) { if (traceDistance <= 0) {
pointedEntity = entity; pointedEntity = entity;
hit = intercept.orElse(null); hit = intercept.orElse(null);
traceDistance = 0; traceDistance = 0;
} }
} else if (intercept.isPresent()) { } else if (intercept.isPresent()) {
Vec3d inter = intercept.get(); Vec3d inter = intercept.get();
double distanceToHit = pos.distanceTo(inter); double distanceToHit = start.distanceTo(inter);
if (distanceToHit < traceDistance || traceDistance == 0) { if (distanceToHit < traceDistance || traceDistance == 0) {
if (entity.getRootVehicle() == e.getRootVehicle()) { if (entity.getRootVehicle() == e.getRootVehicle()) {
if (traceDistance == 0) { if (traceDistance == 0) {
pointedEntity = entity;
hit = inter;
}
} else {
pointedEntity = entity; pointedEntity = entity;
hit = inter; hit = inter;
traceDistance = distanceToHit;
} }
} else {
pointedEntity = entity;
hit = inter;
traceDistance = distanceToHit;
} }
} }
} }
@ -103,20 +103,6 @@ public class RayTraceHelper {
return new Trace(tracedBlock); 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 { public static class Trace {
@Nullable @Nullable
private final HitResult result; private final HitResult result;