mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-27 15:17:59 +01:00
fixed tree kicking and improved performance when traversing trees
This commit is contained in:
parent
c7cdca3ff4
commit
226e80a89b
22 changed files with 278 additions and 231 deletions
|
@ -149,43 +149,38 @@ public class EarthPonyKickAbility implements Ability<Pos> {
|
|||
public boolean apply(Pony iplayer, Pos data) {
|
||||
|
||||
BlockPos pos = data.pos();
|
||||
TreeType tree = TreeType.at(pos, iplayer.asWorld());
|
||||
|
||||
if (tree == TreeType.NONE || tree.findBase(iplayer.asWorld(), pos)
|
||||
.map(base -> tree.countBlocks(iplayer.asWorld(), pos) > 0)
|
||||
.orElse(false)) {
|
||||
return false;
|
||||
}
|
||||
TreeType treeType = TreeType.at(pos, iplayer.asWorld());
|
||||
|
||||
iplayer.setAnimation(Animation.KICK, Animation.Recipient.ANYONE);
|
||||
iplayer.subtractEnergyCost(tree == TreeType.NONE ? 1 : 3);
|
||||
iplayer.subtractEnergyCost(treeType == TreeType.NONE ? 1 : 3);
|
||||
|
||||
ParticleUtils.spawnParticle(iplayer.asWorld(), UParticles.GROUND_POUND, data.vec(), Vec3d.ZERO);
|
||||
return treeType.collectBlocks(iplayer.asWorld(), pos).filter(tree -> {
|
||||
ParticleUtils.spawnParticle(iplayer.asWorld(), UParticles.GROUND_POUND, data.vec(), Vec3d.ZERO);
|
||||
|
||||
PlayerEntity player = iplayer.asEntity();
|
||||
PlayerEntity player = iplayer.asEntity();
|
||||
|
||||
if (BlockDestructionManager.of(player.getWorld()).getBlockDestruction(pos) + 4 >= BlockDestructionManager.MAX_DAMAGE) {
|
||||
if (player.getWorld().random.nextInt(30) == 0) {
|
||||
tree.traverse(player.getWorld(), pos, (w, state, p, recurseLevel) -> {
|
||||
if (recurseLevel < 5) {
|
||||
if (BlockDestructionManager.of(player.getWorld()).getBlockDestruction(pos) + 4 >= BlockDestructionManager.MAX_DAMAGE) {
|
||||
if (player.getWorld().random.nextInt(30) == 0) {
|
||||
tree.logs().forEach(player.getWorld(), (w, state, p) -> {
|
||||
w.breakBlock(p, true);
|
||||
} else {
|
||||
});
|
||||
tree.leaves().forEach(player.getWorld(), (w, state, p) -> {
|
||||
Block.dropStacks(w.getBlockState(p), w, p);
|
||||
w.setBlockState(p, Blocks.AIR.getDefaultState(), 3);
|
||||
}
|
||||
});
|
||||
w.setBlockState(p, Blocks.AIR.getDefaultState(), Block.NOTIFY_ALL);
|
||||
});
|
||||
}
|
||||
|
||||
iplayer.subtractEnergyCost(3);
|
||||
} else {
|
||||
int cost = dropApples(player, pos);
|
||||
|
||||
if (cost > 0) {
|
||||
iplayer.subtractEnergyCost(cost / 7F);
|
||||
}
|
||||
}
|
||||
|
||||
iplayer.subtractEnergyCost(3);
|
||||
} else {
|
||||
int cost = dropApples(player, pos);
|
||||
|
||||
if (cost > 0) {
|
||||
iplayer.subtractEnergyCost(cost / 7F);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
return true;
|
||||
}).isPresent();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -195,37 +190,32 @@ public class EarthPonyKickAbility implements Ability<Pos> {
|
|||
|
||||
@Override
|
||||
public void coolDown(Pony player, AbilitySlot slot) {
|
||||
player.asEntity().getHungerManager().addExhaustion(0.1F);
|
||||
}
|
||||
|
||||
private int dropApples(PlayerEntity player, BlockPos pos) {
|
||||
TreeType tree = TreeType.at(pos, player.getWorld());
|
||||
|
||||
if (tree.countBlocks(player.getWorld(), pos) > 0) {
|
||||
List<ItemEntity> capturedDrops = new ArrayList<>();
|
||||
|
||||
tree.traverse(player.getWorld(), pos, (world, state, position, recurse) -> {
|
||||
TreeType treeType = TreeType.at(pos, player.getWorld());
|
||||
return treeType.collectBlocks(player.getWorld(), pos).map(tree -> {
|
||||
tree.logs().forEach(player.getWorld(), (world, state, position) -> {
|
||||
affectBlockChange(player, position);
|
||||
}, (world, state, position, recurse) -> {
|
||||
});
|
||||
|
||||
int[] dropCount = {0};
|
||||
tree.leaves().forEach(player.getWorld(), (world, state, position) -> {
|
||||
affectBlockChange(player, position);
|
||||
List<ItemEntity> drops = buckBlock(tree, state, world, position)
|
||||
if (!buckBlock(treeType, state, world, position)
|
||||
.filter(i -> !i.isEmpty())
|
||||
.map(stack -> createDrop(stack, position, world))
|
||||
.toList();
|
||||
if (!drops.isEmpty()) {
|
||||
.map(stack -> createDrop(stack, position, world, dropCount))
|
||||
.toList().isEmpty()) {
|
||||
world.syncWorldEvent(WorldEvents.BLOCK_BROKEN, position, Block.getRawIdFromState(state));
|
||||
capturedDrops.addAll(drops);
|
||||
}
|
||||
});
|
||||
|
||||
capturedDrops.forEach(player.getWorld()::spawnEntity);
|
||||
|
||||
return capturedDrops.size() / 3;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return dropCount[0] / 3;
|
||||
}).orElse(0);
|
||||
}
|
||||
|
||||
private ItemEntity createDrop(ItemStack stack, BlockPos pos, World world) {
|
||||
private ItemEntity createDrop(ItemStack stack, BlockPos pos, World world, int[] dropCount) {
|
||||
ItemEntity entity = new ItemEntity(world,
|
||||
pos.getX() + world.random.nextFloat(),
|
||||
pos.getY() - 0.5,
|
||||
|
@ -233,10 +223,12 @@ public class EarthPonyKickAbility implements Ability<Pos> {
|
|||
stack
|
||||
);
|
||||
entity.setToDefaultPickupDelay();
|
||||
world.spawnEntity(entity);
|
||||
dropCount[0]++;
|
||||
return entity;
|
||||
}
|
||||
|
||||
private Stream<ItemStack> buckBlock(TreeType tree, BlockState treeState, World world, BlockPos position) {
|
||||
private Stream<ItemStack> buckBlock(TreeType treeType, BlockState treeState, World world, BlockPos position) {
|
||||
|
||||
if (treeState.getBlock() instanceof Buckable buckable) {
|
||||
return buckable.onBucked((ServerWorld)world, treeState, position).stream();
|
||||
|
@ -246,7 +238,7 @@ public class EarthPonyKickAbility implements Ability<Pos> {
|
|||
BlockState below = world.getBlockState(down);
|
||||
|
||||
if (below.isAir()) {
|
||||
return Stream.of(tree.pickRandomStack(world.random, treeState));
|
||||
return Stream.of(treeType.pickRandomStack(world.random, treeState));
|
||||
}
|
||||
|
||||
if (below.getBlock() instanceof Buckable buckable) {
|
||||
|
@ -259,7 +251,7 @@ public class EarthPonyKickAbility implements Ability<Pos> {
|
|||
private void affectBlockChange(PlayerEntity player, BlockPos position) {
|
||||
BlockDestructionManager.of(player.getWorld()).damageBlock(position, 4);
|
||||
|
||||
PosHelper.all(position, p -> {
|
||||
PosHelper.fastAll(position, p -> {
|
||||
BlockState s = player.getWorld().getBlockState(p);
|
||||
|
||||
if (s.getBlock() instanceof BeehiveBlock) {
|
||||
|
|
|
@ -0,0 +1,105 @@
|
|||
package com.minelittlepony.unicopia.ability.data.tree;
|
||||
|
||||
import java.util.Optional;
|
||||
|
||||
import com.minelittlepony.unicopia.util.PosHelper;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public class TreeTraverser {
|
||||
private static final Direction[] WIDE_DIRS = new Direction[] {
|
||||
Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST
|
||||
};
|
||||
|
||||
private final TreeType type;
|
||||
|
||||
private PosHelper.PositionRecord logs = new PosHelper.PositionRecord();
|
||||
private PosHelper.PositionRecord leaves = new PosHelper.PositionRecord();
|
||||
|
||||
private final int maxRecurse = 50;
|
||||
|
||||
public TreeTraverser(TreeType type) {
|
||||
this.type = type;
|
||||
}
|
||||
|
||||
public PosHelper.PositionRecord collectLogs(World w, BlockPos pos) {
|
||||
traverse(w, pos.mutableCopy());
|
||||
return logs;
|
||||
}
|
||||
|
||||
public PosHelper.PositionRecord collectLeaves(World w, BlockPos pos) {
|
||||
traverse(w, findCanopy(w, pos));
|
||||
return leaves;
|
||||
}
|
||||
|
||||
public void traverse(World w, BlockPos pos) {
|
||||
traverse(w, pos.mutableCopy());
|
||||
}
|
||||
|
||||
private void traverse(World w, BlockPos.Mutable pos) {
|
||||
logs = new PosHelper.PositionRecord();
|
||||
leaves = new PosHelper.PositionRecord();
|
||||
innerTraverse(w, pos, 0);
|
||||
}
|
||||
|
||||
private void innerTraverse(World w, BlockPos.Mutable pos, int recurseLevel) {
|
||||
if (recurseLevel >= maxRecurse || logs.hasVisited(pos) || leaves.hasVisited(pos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
BlockState state = w.getBlockState(pos);
|
||||
boolean yay = false;
|
||||
|
||||
if (type.isLeaves(state)) {
|
||||
leaves.visit(pos);
|
||||
yay = true;
|
||||
} else if (type.isLog(state)) {
|
||||
logs.visit(pos);
|
||||
yay = true;
|
||||
}
|
||||
|
||||
if (yay) {
|
||||
PosHelper.fastAll(pos, p -> innerTraverse(w, p, recurseLevel + 1), WIDE_DIRS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the top of the tree's trunk. Usually the point where wood meets leaves.
|
||||
*/
|
||||
private BlockPos.Mutable findCanopy(World w, BlockPos pos) {
|
||||
BlockPos.Mutable mutable = pos.mutableCopy();
|
||||
while (type.isLog(w.getBlockState(mutable.move(Direction.UP)))) {
|
||||
if (PosHelper.fastAny(mutable, p -> type.isLeaves(w.getBlockState(p)), PosHelper.HORIZONTAL)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return mutable.move(Direction.DOWN);
|
||||
}
|
||||
|
||||
public Optional<BlockPos> findBase(World w, BlockPos pos) {
|
||||
logs.clear();
|
||||
return findBase(w, pos.mutableCopy());
|
||||
}
|
||||
|
||||
private Optional<BlockPos> findBase(World w, BlockPos.Mutable pos) {
|
||||
if (logs.hasVisited(pos) || !type.isLog(w.getBlockState(pos))) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
do {
|
||||
logs.visit(pos);
|
||||
} while (type.isLog(w.getBlockState(pos.move(Direction.DOWN))));
|
||||
pos.move(Direction.UP);
|
||||
|
||||
if (type.isWide()) {
|
||||
PosHelper.fastAll(pos, p -> findBase(w, p)
|
||||
.filter(a -> a.getY() < pos.getY())
|
||||
.ifPresent(pos::set), PosHelper.HORIZONTAL);
|
||||
}
|
||||
|
||||
return Optional.of(logs.visit(pos).toImmutable());
|
||||
}
|
||||
}
|
|
@ -4,7 +4,6 @@ import com.minelittlepony.unicopia.Unicopia;
|
|||
import com.minelittlepony.unicopia.util.PosHelper;
|
||||
import com.minelittlepony.unicopia.util.Weighted;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
|
@ -12,159 +11,11 @@ import java.util.Set;
|
|||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public interface TreeType {
|
||||
TreeType NONE = new TreeTypeImpl(
|
||||
Unicopia.id("none"),
|
||||
false,
|
||||
Set.of(),
|
||||
Set.of(),
|
||||
Weighted.of(),
|
||||
0
|
||||
);
|
||||
Direction[] WIDE_DIRS = new Direction[] { Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST };
|
||||
|
||||
default void traverse(World w, BlockPos start, Reactor consumer) {
|
||||
traverse(w, start, consumer, consumer);
|
||||
}
|
||||
|
||||
default void traverse(World w, BlockPos start, Reactor logConsumer, Reactor leavesConsumer) {
|
||||
traverse(new HashSet<>(), new HashSet<>(), w, start, 0, 50, logConsumer, leavesConsumer);
|
||||
}
|
||||
|
||||
default void traverse(Set<BlockPos> logs, Set<BlockPos> leaves, World w, BlockPos start, int recurseLevel, int maxRecurse, Reactor logConsumer, Reactor leavesConsumer) {
|
||||
if (this == NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
findBase(w, start).ifPresent(base -> {
|
||||
traverseInner(logs, leaves, w, base, recurseLevel, maxRecurse, logConsumer, leavesConsumer);
|
||||
});
|
||||
}
|
||||
|
||||
private void traverseInner(Set<BlockPos> logs, Set<BlockPos> leaves, World w, BlockPos pos, int recurseLevel, int maxRecurse, Reactor logConsumer, Reactor leavesConsumer) {
|
||||
|
||||
if (this == NONE || (maxRecurse > 0 && recurseLevel >= maxRecurse) || logs.contains(pos) || leaves.contains(pos)) {
|
||||
return;
|
||||
}
|
||||
|
||||
BlockState state = w.getBlockState(pos);
|
||||
boolean yay = false;
|
||||
|
||||
if (isLeaves(state)) {
|
||||
leaves.add(pos);
|
||||
yay = true;
|
||||
if (leavesConsumer != null) {
|
||||
leavesConsumer.react(w, state, pos, recurseLevel);
|
||||
}
|
||||
} else if (isLog(state)) {
|
||||
logs.add(pos);
|
||||
yay = true;
|
||||
if (logConsumer != null) {
|
||||
logConsumer.react(w, state, pos, recurseLevel);
|
||||
}
|
||||
}
|
||||
|
||||
if (yay) {
|
||||
PosHelper.all(pos, p -> traverseInner(logs, leaves, w, p, recurseLevel + 1, maxRecurse, logConsumer, leavesConsumer), WIDE_DIRS);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recursively locates the base of the tree.
|
||||
*/
|
||||
default Optional<BlockPos> findBase(World w, BlockPos pos) {
|
||||
return findBase(new HashSet<>(), w, new BlockPos.Mutable(pos.getX(), pos.getY(), pos.getZ()));
|
||||
}
|
||||
|
||||
private Optional<BlockPos> findBase(Set<BlockPos> done, World w, BlockPos.Mutable pos) {
|
||||
if (done.contains(pos) || !isLog(w.getBlockState(pos))) {
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
done.add(pos.toImmutable());
|
||||
while (isLog(w.getBlockState(pos.down()))) {
|
||||
done.add(pos.move(Direction.DOWN).toImmutable());
|
||||
}
|
||||
|
||||
if (isWide()) {
|
||||
PosHelper.all(pos.toImmutable(), p -> findBase(done, w, p.mutableCopy())
|
||||
.filter(a -> a.getY() < pos.getY())
|
||||
.ifPresent(pos::set), PosHelper.HORIZONTAL);
|
||||
}
|
||||
|
||||
done.add(pos.toImmutable());
|
||||
return Optional.of(pos.toImmutable());
|
||||
}
|
||||
|
||||
/**
|
||||
* Counts the number of logs and leaves present in the targeted tree.
|
||||
*/
|
||||
default int countBlocks(World w, BlockPos pos) {
|
||||
if (this == NONE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Set<BlockPos> logs = new HashSet<>();
|
||||
Set<BlockPos> leaves = new HashSet<>();
|
||||
|
||||
findBase(w, pos).ifPresent(base -> traverseInner(logs, leaves, w, base, 0, 50, null, null));
|
||||
|
||||
int logCount = logs.size();
|
||||
|
||||
logs.clear();
|
||||
leaves.clear();
|
||||
|
||||
traverseInner(logs, leaves, w, findCanopy(w, pos), 0, 50, null, null);
|
||||
|
||||
int leafCount = leaves.size();
|
||||
|
||||
return logCount <= (leafCount / 2) ? logCount + leafCount : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the top of the tree's trunk. Usually the point where wood meets leaves.
|
||||
*/
|
||||
default BlockPos findCanopy(World w, BlockPos pos) {
|
||||
while (isLog(w.getBlockState(pos.up()))) {
|
||||
if (PosHelper.any(pos, p -> isLeaves(w.getBlockState(p)), PosHelper.HORIZONTAL)) {
|
||||
break;
|
||||
}
|
||||
|
||||
pos = pos.up();
|
||||
}
|
||||
return pos;
|
||||
}
|
||||
|
||||
/**
|
||||
* Finds the tree type of the leaves on top of this tree, independent of what this tree expects for its leaves.
|
||||
*/
|
||||
default TreeType findLeavesType(World w, BlockPos pos) {
|
||||
while (isLog(w.getBlockState(pos.up()))) {
|
||||
if (PosHelper.any(pos, p -> isLeaves(w.getBlockState(p)), PosHelper.HORIZONTAL)) {
|
||||
return this;
|
||||
}
|
||||
|
||||
pos = pos.up();
|
||||
}
|
||||
|
||||
return of(w.getBlockState(pos.up()));
|
||||
}
|
||||
|
||||
boolean isLeaves(BlockState state);
|
||||
|
||||
boolean isLog(BlockState state);
|
||||
|
||||
default boolean matches(BlockState state) {
|
||||
return isLeaves(state) || isLog(state);
|
||||
}
|
||||
|
||||
ItemStack pickRandomStack(Random random, BlockState state);
|
||||
|
||||
boolean isWide();
|
||||
TreeType NONE = new TreeTypeImpl(Unicopia.id("none"), false, Set.of(), Set.of(), Weighted.of(), 0, 0.5F);
|
||||
|
||||
static TreeType at(BlockPos pos, World world) {
|
||||
return TreeTypes.get(world.getBlockState(pos), pos, world);
|
||||
|
@ -198,10 +49,45 @@ public interface TreeType {
|
|||
public boolean isWide() {
|
||||
return logs.isWide();
|
||||
}
|
||||
|
||||
@Override
|
||||
public float leavesRatio() {
|
||||
return logs.leavesRatio();
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
public interface Reactor {
|
||||
void react(World w, BlockState state, BlockPos pos, int recurseLevel);
|
||||
boolean isLeaves(BlockState state);
|
||||
|
||||
boolean isLog(BlockState state);
|
||||
|
||||
default boolean matches(BlockState state) {
|
||||
return isLeaves(state) || isLog(state);
|
||||
}
|
||||
|
||||
ItemStack pickRandomStack(Random random, BlockState state);
|
||||
|
||||
boolean isWide();
|
||||
|
||||
/**
|
||||
* The minimum leaves to logs ratio for this tree.
|
||||
*/
|
||||
float leavesRatio();
|
||||
|
||||
/**
|
||||
* Counts the number of logs and leaves present in the targeted tree.
|
||||
*/
|
||||
default Optional<Tree> collectBlocks(World w, BlockPos pos) {
|
||||
if (this == NONE) {
|
||||
return Optional.empty();
|
||||
}
|
||||
TreeTraverser traverser = new TreeTraverser(this);
|
||||
return traverser.findBase(w, pos).map(base -> {
|
||||
PosHelper.PositionRecord logs = traverser.collectLogs(w, base);
|
||||
PosHelper.PositionRecord leaves = traverser.collectLeaves(w, base);
|
||||
return logs.size() <= (leaves.size() * leavesRatio()) ? new Tree(logs, leaves) : null;
|
||||
});
|
||||
}
|
||||
|
||||
record Tree(PosHelper.PositionRecord logs, PosHelper.PositionRecord leaves) { }
|
||||
}
|
||||
|
|
|
@ -17,7 +17,8 @@ public record TreeTypeImpl (
|
|||
Set<Identifier> logs,
|
||||
Set<Identifier> leaves,
|
||||
Supplier<Optional<Supplier<ItemStack>>> pool,
|
||||
int rarity
|
||||
int rarity,
|
||||
float leavesRatio
|
||||
) implements TreeType {
|
||||
@Override
|
||||
public boolean isLeaves(BlockState state) {
|
||||
|
|
|
@ -64,6 +64,7 @@ public class TreeTypeLoader extends JsonDataLoader implements IdentifiableResour
|
|||
final Set<Drop> drops;
|
||||
final boolean wideTrunk;
|
||||
final int rarity;
|
||||
final float leavesRatio;
|
||||
|
||||
public TreeTypeDef(PacketByteBuf buffer) {
|
||||
logs = new HashSet<>(buffer.readList(PacketByteBuf::readIdentifier));
|
||||
|
@ -71,6 +72,7 @@ public class TreeTypeLoader extends JsonDataLoader implements IdentifiableResour
|
|||
drops = new HashSet<>(buffer.readList(Drop::new));
|
||||
wideTrunk = buffer.readBoolean();
|
||||
rarity = buffer.readInt();
|
||||
leavesRatio = buffer.readFloat();
|
||||
}
|
||||
|
||||
public TreeType toTreeType(Identifier id) {
|
||||
|
@ -80,7 +82,8 @@ public class TreeTypeLoader extends JsonDataLoader implements IdentifiableResour
|
|||
Objects.requireNonNull(logs, "TreeType must have logs"),
|
||||
Objects.requireNonNull(leaves, "TreeType must have leaves"),
|
||||
Weighted.of(drops),
|
||||
rarity
|
||||
rarity,
|
||||
leavesRatio
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -90,6 +93,7 @@ public class TreeTypeLoader extends JsonDataLoader implements IdentifiableResour
|
|||
buffer.writeCollection(drops, (a, b) -> b.write(a));
|
||||
buffer.writeBoolean(wideTrunk);
|
||||
buffer.writeInt(rarity);
|
||||
buffer.writeFloat(leavesRatio);
|
||||
}
|
||||
|
||||
static class Drop implements Weighted.Buildable<Supplier<ItemStack>> {
|
||||
|
|
|
@ -12,6 +12,7 @@ import net.minecraft.item.ItemStack;
|
|||
import net.minecraft.registry.tag.BlockTags;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.random.Random;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
|
@ -29,18 +30,8 @@ public class TreeTypes {
|
|||
return entries.stream()
|
||||
.filter(type -> type.matches(state))
|
||||
.findFirst()
|
||||
.map(type -> TreeType.of(type, type.findLeavesType(world, pos)))
|
||||
.orElseGet(() -> {
|
||||
if (any1x.matches(state)) {
|
||||
if (PosHelper.any(pos, p -> world.getBlockState(p).isOf(state.getBlock()), PosHelper.HORIZONTAL)) {
|
||||
return any2x;
|
||||
}
|
||||
|
||||
return any1x;
|
||||
}
|
||||
|
||||
return TreeType.NONE;
|
||||
});
|
||||
.map(type -> TreeType.of(type, findLeavesType(type, world, pos)))
|
||||
.orElseGet(() -> any1x.matches(state) ? (PosHelper.fastAny(pos, p -> world.getBlockState(p).isOf(state.getBlock()), PosHelper.HORIZONTAL) ? any2x : any1x) : TreeType.NONE);
|
||||
}
|
||||
|
||||
static TreeType get(BlockState state) {
|
||||
|
@ -50,6 +41,16 @@ public class TreeTypes {
|
|||
.orElse(TreeType.NONE);
|
||||
}
|
||||
|
||||
private static TreeType findLeavesType(TreeType baseType, World w, BlockPos pos) {
|
||||
BlockPos.Mutable mutable = pos.mutableCopy();
|
||||
while (baseType.isLog(w.getBlockState(mutable.move(Direction.UP)))) {
|
||||
if (PosHelper.fastAny(mutable, p -> baseType.isLeaves(w.getBlockState(p)), PosHelper.HORIZONTAL)) {
|
||||
return baseType;
|
||||
}
|
||||
}
|
||||
return TreeType.of(w.getBlockState(mutable));
|
||||
}
|
||||
|
||||
private static TreeType createDynamic(boolean wide) {
|
||||
return new TreeType() {
|
||||
@Override
|
||||
|
@ -75,6 +76,11 @@ public class TreeTypes {
|
|||
public boolean isWide() {
|
||||
return wide;
|
||||
}
|
||||
|
||||
@Override
|
||||
public float leavesRatio() {
|
||||
return 0.5F;
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -97,7 +97,7 @@ public record Altar(BlockPos origin, Set<BlockPos> pillars) {
|
|||
}
|
||||
|
||||
private static boolean checkSlab(World world, BlockPos pos) {
|
||||
return !PosHelper.any(pos, p -> !isObsidian(world, p), HORIZONTALS);
|
||||
return !PosHelper.fastAny(pos, p -> !isObsidian(world, p), HORIZONTALS);
|
||||
}
|
||||
|
||||
private static boolean checkPillarPair(World world, BlockPos.Mutable center, BlockRotation rotation, Consumer<BlockPos> pillarPosCollector) {
|
||||
|
|
|
@ -11,10 +11,10 @@ import java.util.stream.StreamSupport;
|
|||
|
||||
import com.google.common.collect.Lists;
|
||||
|
||||
import it.unimi.dsi.fastutil.longs.LongOpenHashSet;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.Direction.Axis;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.util.math.Vec3i;
|
||||
import net.minecraft.world.BlockView;
|
||||
|
@ -29,30 +29,36 @@ public interface PosHelper {
|
|||
}
|
||||
|
||||
static BlockPos findSolidGroundAt(World world, BlockPos pos, int signum) {
|
||||
while (world.isInBuildLimit(pos) && (world.isAir(pos) || !world.getBlockState(pos).canPlaceAt(world, pos))) {
|
||||
pos = pos.offset(Axis.Y, -signum);
|
||||
BlockPos.Mutable mutable = pos.mutableCopy();
|
||||
while (world.isInBuildLimit(mutable) && (world.isAir(mutable) || !world.getBlockState(mutable).canPlaceAt(world, mutable))) {
|
||||
mutable.move(Direction.DOWN, signum);
|
||||
}
|
||||
|
||||
return pos;
|
||||
return mutable.toImmutable();
|
||||
}
|
||||
|
||||
static void all(BlockPos origin, Consumer<BlockPos> consumer, Direction... directions) {
|
||||
BlockPos.Mutable mutable = origin.mutableCopy();
|
||||
static void fastAll(BlockPos origin, Consumer<BlockPos.Mutable> consumer, Direction... directions) {
|
||||
final BlockPos immutable = origin instanceof BlockPos.Mutable m ? m.toImmutable() : origin;
|
||||
final BlockPos.Mutable mutable = origin instanceof BlockPos.Mutable m ? m : origin.mutableCopy();
|
||||
for (Direction facing : directions) {
|
||||
mutable.set(origin);
|
||||
consumer.accept(mutable.move(facing));
|
||||
mutable.set(immutable);
|
||||
}
|
||||
}
|
||||
|
||||
static boolean any(BlockPos origin, Predicate<BlockPos> consumer, Direction... directions) {
|
||||
BlockPos.Mutable mutable = origin.mutableCopy();
|
||||
for (Direction facing : directions) {
|
||||
mutable.set(origin);
|
||||
if (consumer.test(mutable.move(facing))) {
|
||||
return true;
|
||||
static boolean fastAny(BlockPos origin, Predicate<BlockPos> consumer, Direction... directions) {
|
||||
final BlockPos immutable = origin instanceof BlockPos.Mutable m ? m.toImmutable() : origin;
|
||||
final BlockPos.Mutable mutable = origin instanceof BlockPos.Mutable m ? m : origin.mutableCopy();
|
||||
try {
|
||||
for (Direction facing : directions) {
|
||||
if (consumer.test(mutable.set(immutable).move(facing))) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
} finally {
|
||||
mutable.set(immutable);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static Stream<BlockPos> adjacentNeighbours(BlockPos origin) {
|
||||
|
@ -82,4 +88,37 @@ public interface PosHelper {
|
|||
mutablePos.move(chainDirection.getOpposite());
|
||||
return mutablePos.toImmutable();
|
||||
}
|
||||
|
||||
public final class PositionRecord {
|
||||
private final LongOpenHashSet visitedPositions = new LongOpenHashSet();
|
||||
|
||||
public BlockPos visit(BlockPos pos) {
|
||||
visitedPositions.add(pos.asLong());
|
||||
return pos;
|
||||
}
|
||||
|
||||
public boolean hasVisited(BlockPos pos) {
|
||||
return visitedPositions.contains(pos.asLong());
|
||||
}
|
||||
|
||||
public int size() {
|
||||
return visitedPositions.size();
|
||||
}
|
||||
|
||||
public void clear() {
|
||||
visitedPositions.clear();
|
||||
}
|
||||
|
||||
public void forEach(World world, Reactor reactor) {
|
||||
forEach(new BlockPos.Mutable(), pos -> reactor.react(world, world.getBlockState(pos), pos));
|
||||
}
|
||||
|
||||
public void forEach(BlockPos.Mutable mutable, Consumer<BlockPos> consumer) {
|
||||
visitedPositions.forEach(l -> consumer.accept(mutable.set(l)));
|
||||
}
|
||||
|
||||
public interface Reactor {
|
||||
void react(World w, BlockState state, BlockPos pos);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"logs": [ "minecraft:acacia_log", "minecraft:acacia_wood" ],
|
||||
"leaves": [ "minecraft:acacia_leaves" ],
|
||||
"rarity": 5,
|
||||
"leavesRatio": 0.5,
|
||||
"drops": [
|
||||
{ "weight": 1, "item": "unicopia:rotten_apple" },
|
||||
{ "weight": 2, "item": "unicopia:sweet_apple" },
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"logs": [ "minecraft:oak_log", "minecraft:oak_wood" ],
|
||||
"leaves": [ "minecraft:azalea_leaves", "minecraft:flowering_azalea_leaves" ],
|
||||
"rarity": 5,
|
||||
"leavesRatio": 0.5,
|
||||
"drops": [
|
||||
{ "weight": 1, "item": "unicopia:sour_apple" },
|
||||
{ "weight": 2, "item": "unicopia:green_apple" },
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"logs": [ "minecraft:birch_log", "minecraft:birch_wood" ],
|
||||
"leaves": [ "minecraft:birch_leaves" ],
|
||||
"rarity": 5,
|
||||
"leavesRatio": 0.5,
|
||||
"drops": [
|
||||
{ "weight": 1, "item": "unicopia:rotten_apple" },
|
||||
{ "weight": 2, "item": "unicopia:sweet_apple" },
|
||||
|
|
|
@ -2,6 +2,7 @@
|
|||
"logs": [ "minecraft:cherry_log", "minecraft:cherry_wood" ],
|
||||
"leaves": [ "minecraft:cherry_leaves" ],
|
||||
"rarity": 7,
|
||||
"leavesRatio": 0.5,
|
||||
"drops": [
|
||||
{ "weight": 1, "item": "minecraft:pink_petals" }
|
||||
]
|
||||
|
|
|
@ -3,6 +3,7 @@
|
|||
"leaves": [ "minecraft:dark_oak_leaves" ],
|
||||
"wideTrunk": true,
|
||||
"rarity": 5,
|
||||
"leavesRatio": 0.5,
|
||||
"drops": [
|
||||
{ "weight": 1, "item": "unicopia:rotten_apple" },
|
||||
{ "weight": 2, "item": "unicopia:sweet_apple" },
|
||||
|
|
|
@ -2,5 +2,6 @@
|
|||
"logs": [ "minecraft:oak_log", "minecraft:oak_wood" ],
|
||||
"leaves": [ "unicopia:green_apple_leaves" ],
|
||||
"wideTrunk": false,
|
||||
"leavesRatio": 0.5,
|
||||
"drops": []
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
"leaves": [ "minecraft:jungle_leaves" ],
|
||||
"wideTrunk": true,
|
||||
"rarity": 5,
|
||||
"leavesRatio": 0.5,
|
||||
"drops": [
|
||||
{ "weight": 5, "item": "unicopia:green_apple" },
|
||||
{ "weight": 2, "item": "unicopia:sweet_apple" },
|
||||
|
|
|
@ -2,5 +2,6 @@
|
|||
"logs": [ "minecraft:jungle_log", "minecraft:jungle_wood" ],
|
||||
"leaves": [ "unicopia:mango_leaves" ],
|
||||
"wideTrunk": false,
|
||||
"leavesRatio": 0.5,
|
||||
"drops": []
|
||||
}
|
|
@ -2,6 +2,7 @@
|
|||
"logs": [ "minecraft:oak_log", "minecraft:oak_wood" ],
|
||||
"leaves": [ "minecraft:oak_leaves" ],
|
||||
"rarity": 3,
|
||||
"leavesRatio": 0.5,
|
||||
"drops": [
|
||||
{ "weight": 1, "item": "unicopia:rotten_apple" },
|
||||
{ "weight": 2, "tag": "c:acorns" },
|
||||
|
|
|
@ -2,5 +2,6 @@
|
|||
"logs": [ "unicopia:palm_log", "unicopia:palm_wood" ],
|
||||
"leaves": [ "unicopia:palm_leaves" ],
|
||||
"wideTrunk": false,
|
||||
"leavesRatio": 0.5,
|
||||
"drops": []
|
||||
}
|
|
@ -2,5 +2,6 @@
|
|||
"logs": [ "minecraft:oak_log", "minecraft:oak_wood" ],
|
||||
"leaves": [ "unicopia:sour_apple_leaves" ],
|
||||
"wideTrunk": false,
|
||||
"leavesRatio": 0.5,
|
||||
"drops": []
|
||||
}
|
|
@ -3,6 +3,7 @@
|
|||
"leaves": [ "minecraft:spruce_leaves" ],
|
||||
"wideTrunk": true,
|
||||
"rarity": 3,
|
||||
"leavesRatio": 0.5,
|
||||
"drops": [
|
||||
{ "weight": 1, "tag": "c:pinecones" },
|
||||
{ "weight": 4, "tag": "c:sticks" }
|
||||
|
|
|
@ -2,5 +2,6 @@
|
|||
"logs": [ "minecraft:oak_log", "minecraft:oak_wood" ],
|
||||
"leaves": [ "unicopia:sweet_apple_leaves" ],
|
||||
"wideTrunk": false,
|
||||
"leavesRatio": 0.5,
|
||||
"drops": []
|
||||
}
|
|
@ -2,5 +2,6 @@
|
|||
"logs": [ "unicopia:zap_log" ],
|
||||
"leaves": [ "unicopia:zap_leaves" ],
|
||||
"wideTrunk": false,
|
||||
"leavesRatio": 0.5,
|
||||
"drops": []
|
||||
}
|
Loading…
Reference in a new issue