2020-04-26 19:33:10 +02:00
|
|
|
package com.minelittlepony.unicopia;
|
|
|
|
|
|
|
|
import java.util.HashSet;
|
|
|
|
import java.util.Optional;
|
|
|
|
import java.util.Set;
|
|
|
|
|
|
|
|
import com.minelittlepony.unicopia.util.PosHelper;
|
|
|
|
|
|
|
|
import net.minecraft.block.Block;
|
|
|
|
import net.minecraft.block.BlockState;
|
|
|
|
import net.minecraft.block.Blocks;
|
|
|
|
import net.minecraft.block.LeavesBlock;
|
|
|
|
import net.minecraft.util.math.BlockPos;
|
|
|
|
import net.minecraft.util.math.Direction;
|
|
|
|
import net.minecraft.world.World;
|
|
|
|
|
|
|
|
public class TreeTraverser {
|
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
public static class Remover {
|
|
|
|
/**
|
|
|
|
* Removes the tree located at the given position.
|
|
|
|
*/
|
|
|
|
public static void removeTree(World w, BlockPos pos) {
|
|
|
|
BlockState log = w.getBlockState(pos);
|
|
|
|
|
|
|
|
if (Measurer.measureTree(w, log, pos) > 0) {
|
|
|
|
removeTreePart(w, log, Ascender.ascendTrunk(new HashSet<BlockPos>(), w, pos, log, 0).get(), 0);
|
|
|
|
}
|
|
|
|
}
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
private static void removeTreePart(World w, BlockState log, BlockPos pos, int level) {
|
|
|
|
if (level < 10 && isWoodOrLeaf(w, log, pos)) {
|
|
|
|
breakBlock(w, pos, level < 5);
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
PosHelper.all(pos, p -> {
|
|
|
|
removeTreePart(w, log, p, level + 1);
|
|
|
|
}, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST);
|
|
|
|
}
|
2020-04-26 19:33:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
public static class Ascender {
|
|
|
|
/**
|
|
|
|
* Locates the top of the tree's trunk. Usually the point where wood meets leaves.
|
|
|
|
*/
|
|
|
|
public static BlockPos ascendTree(World w, BlockState log, BlockPos pos, boolean remove) {
|
|
|
|
int breaks = 0;
|
|
|
|
while (variantAndBlockEquals(w.getBlockState(pos.up()), log)) {
|
|
|
|
if (PosHelper.any(pos, p -> isLeaves(w.getBlockState(p), log), Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST)) {
|
|
|
|
break;
|
|
|
|
}
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
if (remove) {
|
|
|
|
breakBlock(w, pos, breaks++ < 10);
|
|
|
|
}
|
|
|
|
pos = pos.up();
|
|
|
|
}
|
|
|
|
return pos;
|
2020-04-26 19:33:10 +02:00
|
|
|
}
|
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
static Optional<BlockPos> ascendTrunk(Set<BlockPos> done, World w, BlockPos pos, BlockState log, int level) {
|
|
|
|
if (level < 3 && !done.contains(pos)) {
|
|
|
|
done.add(pos);
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
BlockPos.Mutable result = new BlockPos.Mutable();
|
|
|
|
result.set(ascendTree(w, log, pos, true));
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
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);
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
done.add(result.toImmutable());
|
|
|
|
return Optional.of(result.toImmutable());
|
|
|
|
}
|
|
|
|
return Optional.of(pos);
|
2020-04-26 19:33:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
public static class Descender {
|
|
|
|
/**
|
|
|
|
* 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()));
|
|
|
|
}
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
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();
|
2020-04-26 19:33:10 +02:00
|
|
|
}
|
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
done.add(pos.toImmutable());
|
|
|
|
while (variantAndBlockEquals(w.getBlockState(pos.down()), log)) {
|
|
|
|
done.add(pos.move(Direction.DOWN).toImmutable());
|
|
|
|
}
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
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);
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
done.add(pos.toImmutable());
|
|
|
|
return Optional.of(pos.toImmutable());
|
2020-04-26 19:33:10 +02:00
|
|
|
}
|
2020-10-10 16:25:49 +02:00
|
|
|
}
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
public static class Measurer {
|
|
|
|
/**
|
|
|
|
* 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<>();
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
countParts(logs, leaves, w, log, pos);
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
return logs.size() <= (leaves.size() / 2) ? logs.size() + leaves.size() : 0;
|
|
|
|
}
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2021-01-26 21:33:26 +01:00
|
|
|
/**
|
|
|
|
* Counts the number of logs and leaves present in the targeted tree.
|
|
|
|
*/
|
|
|
|
public static Set<BlockPos> getParts(World w, BlockState log, BlockPos pos) {
|
|
|
|
Set<BlockPos> parts = new HashSet<>();
|
|
|
|
|
|
|
|
countParts(parts, parts, w, log, pos);
|
|
|
|
|
|
|
|
return parts;
|
|
|
|
}
|
|
|
|
|
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
private static void countParts(Set<BlockPos> logs, Set<BlockPos> leaves, World w, BlockState log, BlockPos pos) {
|
|
|
|
if (logs.contains(pos) || leaves.contains(pos)) {
|
|
|
|
return;
|
|
|
|
}
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
BlockState state = w.getBlockState(pos);
|
|
|
|
boolean yay = false;
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
if (isLeaves(state, log) && !state.get(LeavesBlock.PERSISTENT)) {
|
|
|
|
leaves.add(pos);
|
|
|
|
yay = true;
|
|
|
|
} else if (variantAndBlockEquals(state, log)) {
|
|
|
|
logs.add(pos);
|
|
|
|
yay = true;
|
|
|
|
}
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
if (yay) {
|
|
|
|
PosHelper.all(pos, p -> {
|
|
|
|
countParts(logs, leaves, w, log, p);
|
|
|
|
}, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST);
|
|
|
|
}
|
2020-04-26 19:33:10 +02:00
|
|
|
}
|
2020-10-10 16:25:49 +02:00
|
|
|
}
|
2020-04-26 19:33:10 +02:00
|
|
|
|
2020-10-10 16:25:49 +02:00
|
|
|
private static void breakBlock(World w, BlockPos pos, boolean destroy) {
|
|
|
|
if (destroy) {
|
|
|
|
w.breakBlock(pos, true);
|
|
|
|
} else {
|
|
|
|
Block.dropStacks(w.getBlockState(pos), w, pos);
|
|
|
|
w.setBlockState(pos, Blocks.AIR.getDefaultState(), 3);
|
2020-04-26 19:33:10 +02:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
public static boolean isWoodOrLeaf(World w, BlockState log, BlockPos pos) {
|
|
|
|
BlockState state = w.getBlockState(pos);
|
2020-05-29 18:11:34 +02:00
|
|
|
return variantAndBlockEquals(state, log) || (isLeaves(state, log) && !state.get(LeavesBlock.PERSISTENT));
|
2020-04-26 19:33:10 +02:00
|
|
|
}
|
|
|
|
|
|
|
|
private static boolean isLeaves(BlockState state, BlockState log) {
|
|
|
|
return state.getBlock() instanceof LeavesBlock && variantEquals(state, log);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static boolean variantAndBlockEquals(BlockState one, BlockState two) {
|
|
|
|
return (one.getBlock() == two.getBlock()) && variantEquals(one, two);
|
|
|
|
}
|
|
|
|
|
|
|
|
private static boolean variantEquals(BlockState one, BlockState two) {
|
|
|
|
return TreeType.get(one).equals(TreeType.get(two));
|
|
|
|
}
|
|
|
|
}
|