mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-23 21:38:00 +01:00
Rewrite and improve tree traversal
This commit is contained in:
parent
bbe7f67b7b
commit
0dc3860966
12 changed files with 344 additions and 277 deletions
|
@ -1,182 +0,0 @@
|
|||
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 {
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
private static void removeTreePart(World w, BlockState log, BlockPos pos, int level) {
|
||||
if (level < 10 && isWoodOrLeaf(w, log, pos)) {
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (remove) {
|
||||
breakBlock(w, pos, breaks++ < 10);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
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()));
|
||||
}
|
||||
|
||||
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());
|
||||
}
|
||||
}
|
||||
|
||||
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<>();
|
||||
|
||||
countParts(logs, leaves, w, log, pos);
|
||||
|
||||
return logs.size() <= (leaves.size() / 2) ? logs.size() + leaves.size() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* 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;
|
||||
}
|
||||
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean isWoodOrLeaf(World w, BlockState log, BlockPos pos) {
|
||||
BlockState state = w.getBlockState(pos);
|
||||
return variantAndBlockEquals(state, log) || (isLeaves(state, log) && !state.get(LeavesBlock.PERSISTENT));
|
||||
}
|
||||
|
||||
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));
|
||||
}
|
||||
}
|
|
@ -1,75 +1,165 @@
|
|||
package com.minelittlepony.unicopia;
|
||||
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import com.minelittlepony.unicopia.item.UItems;
|
||||
import com.minelittlepony.unicopia.util.PosHelper;
|
||||
import com.minelittlepony.unicopia.util.Weighted;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.HashSet;
|
||||
import java.util.Optional;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.LeavesBlock;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.item.Items;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
public final class TreeType {
|
||||
// TODO: move to datapack
|
||||
private static final Set<TreeType> REGISTRY = new HashSet<>();
|
||||
public static final TreeType NONE = new TreeType(
|
||||
new Identifier("unicopia", "none"),
|
||||
false,
|
||||
new Weighted<Supplier<ItemStack>>(),
|
||||
Collections.emptySet(),
|
||||
Collections.emptySet()
|
||||
);
|
||||
private static final Direction[] WIDE_DIRS = new Direction[] { Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST };
|
||||
|
||||
private static final Supplier<ItemStack> ROTTEN = () -> new ItemStack(UItems.ROTTEN_APPLE);
|
||||
private static final Supplier<ItemStack> SWEET = () -> new ItemStack(UItems.SWEET_APPLE);
|
||||
private static final Supplier<ItemStack> GREEN = () -> new ItemStack(UItems.GREEN_APPLE);
|
||||
private static final Supplier<ItemStack> ZAP = () -> new ItemStack(UItems.ZAP_APPLE);
|
||||
private static final Supplier<ItemStack> SOUR = () -> new ItemStack(UItems.SOUR_APPLE);
|
||||
private static final Supplier<ItemStack> RED = () -> new ItemStack(Items.APPLE);
|
||||
|
||||
public static final TreeType NONE = new TreeType("none", new Weighted<Supplier<ItemStack>>());
|
||||
public static final TreeType OAK = new TreeType("oak", new Weighted<Supplier<ItemStack>>()
|
||||
.put(1, ROTTEN)
|
||||
.put(2, GREEN)
|
||||
.put(3, RED), Blocks.OAK_LOG, Blocks.OAK_LEAVES);
|
||||
public static final TreeType BIRCH = new TreeType("birch", new Weighted<Supplier<ItemStack>>()
|
||||
.put(1, ROTTEN)
|
||||
.put(2, SWEET)
|
||||
.put(5, GREEN), Blocks.BIRCH_LOG, Blocks.BIRCH_LEAVES);
|
||||
public static final TreeType SPRUCE = new TreeType("spruce", new Weighted<Supplier<ItemStack>>()
|
||||
.put(1, SOUR)
|
||||
.put(2, GREEN)
|
||||
.put(3, SWEET)
|
||||
.put(4, ROTTEN), Blocks.SPRUCE_LOG, Blocks.SPRUCE_LEAVES);
|
||||
public static final TreeType ACACIA = new TreeType("acacia", new Weighted<Supplier<ItemStack>>()
|
||||
.put(1, ROTTEN)
|
||||
.put(2, SWEET)
|
||||
.put(5, GREEN), Blocks.ACACIA_LOG, Blocks.ACACIA_LEAVES);
|
||||
public static final TreeType JUNGLE = new TreeType("jungle", new Weighted<Supplier<ItemStack>>()
|
||||
.put(5, GREEN)
|
||||
.put(2, SWEET)
|
||||
.put(1, ZAP), Blocks.JUNGLE_LOG, Blocks.JUNGLE_LEAVES);
|
||||
public static final TreeType DARK_OAK = new TreeType("dark_oak", new Weighted<Supplier<ItemStack>>()
|
||||
.put(1, ROTTEN)
|
||||
.put(2, SWEET)
|
||||
.put(5, ZAP), Blocks.DARK_OAK_LOG, Blocks.DARK_OAK_LEAVES);
|
||||
|
||||
private final String name;
|
||||
private final Set<Identifier> blocks;
|
||||
private final Identifier name;
|
||||
private final boolean wideTrunk;
|
||||
private final Set<Identifier> logs;
|
||||
private final Set<Identifier> leaves;
|
||||
private final Weighted<Supplier<ItemStack>> pool;
|
||||
|
||||
private TreeType(String name, Weighted<Supplier<ItemStack>> pool, Block...blocks) {
|
||||
TreeType(Identifier name, boolean wideTrunk, Weighted<Supplier<ItemStack>> pool, Set<Identifier> logs, Set<Identifier> leaves) {
|
||||
this.name = name;
|
||||
this.wideTrunk = wideTrunk;
|
||||
this.pool = pool;
|
||||
this.blocks = Arrays.stream(blocks).map(Registry.BLOCK::getId)
|
||||
.collect(Collectors.toSet());
|
||||
REGISTRY.add(this);
|
||||
this.logs = logs;
|
||||
this.leaves = leaves;
|
||||
}
|
||||
|
||||
public void traverse(World w, BlockPos start, Reactor consumer) {
|
||||
traverse(w, start, consumer, consumer);
|
||||
}
|
||||
|
||||
public void traverse(World w, BlockPos start, Reactor logConsumer, Reactor leavesConsumer) {
|
||||
traverse(new HashSet<>(), new HashSet<>(), w, start, 0, 50, logConsumer, leavesConsumer);
|
||||
}
|
||||
|
||||
public void traverse(Set<BlockPos> logs, Set<BlockPos> leaves, World w, BlockPos start, int recurseLevel, int maxRecurse, Reactor logConsumer, Reactor leavesConsumer) {
|
||||
if (this == NONE) {
|
||||
return;
|
||||
}
|
||||
|
||||
traverseInner(logs, leaves, w, findBase(w, start), 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.
|
||||
*/
|
||||
public BlockPos findBase(World w, BlockPos pos) {
|
||||
return findBase(new HashSet<BlockPos>(), w, new BlockPos.Mutable(pos.getX(), pos.getY(), pos.getZ())).get();
|
||||
}
|
||||
|
||||
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 (wideTrunk) {
|
||||
PosHelper.all(pos.toImmutable(), p -> findBase(done, w, new BlockPos.Mutable(p.getX(), p.getY(), p.getZ()))
|
||||
.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.
|
||||
*/
|
||||
public int countBlocks(World w, BlockPos pos) {
|
||||
if (this == NONE) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
Set<BlockPos> logs = new HashSet<>();
|
||||
Set<BlockPos> leaves = new HashSet<>();
|
||||
|
||||
traverseInner(logs, leaves, w, findBase(w, pos), 0, 50, null, null);
|
||||
|
||||
int logCount = logs.size();
|
||||
|
||||
logs.clear();
|
||||
leaves.clear();
|
||||
|
||||
traverseInner(logs, leaves, w, findCanopy(w, pos), 0, 50, null, null);
|
||||
|
||||
return logCount <= (leaves.size() / 2) ? logCount + leaves.size() : 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Locates the top of the tree's trunk. Usually the point where wood meets leaves.
|
||||
*/
|
||||
public 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;
|
||||
}
|
||||
|
||||
public boolean isLeaves(BlockState state) {
|
||||
return findMatch(leaves, state) && (!state.contains(LeavesBlock.PERSISTENT) || !state.get(LeavesBlock.PERSISTENT));
|
||||
}
|
||||
|
||||
public boolean isLog(BlockState state) {
|
||||
return findMatch(logs, state);
|
||||
}
|
||||
|
||||
public boolean matches(BlockState state) {
|
||||
return blocks.contains(Registry.BLOCK.getId(state.getBlock()));
|
||||
return isLeaves(state) || isLog(state);
|
||||
}
|
||||
|
||||
public ItemStack pickRandomStack() {
|
||||
|
@ -77,7 +167,7 @@ public final class TreeType {
|
|||
}
|
||||
|
||||
public static TreeType get(BlockState state) {
|
||||
return REGISTRY.stream().filter(type -> type.matches(state)).findFirst().orElse(TreeType.NONE);
|
||||
return TreeTypeLoader.INSTANCE.get(state);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -85,8 +175,16 @@ public final class TreeType {
|
|||
return o instanceof TreeType && name.compareTo(((TreeType)o).name) == 0;
|
||||
}
|
||||
|
||||
private static boolean findMatch(Set<Identifier> ids, BlockState state) {
|
||||
return ids.contains(Registry.BLOCK.getId(state.getBlock()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return name.hashCode();
|
||||
}
|
||||
|
||||
public interface Reactor {
|
||||
void react(World w, BlockState state, BlockPos pos, int recurseLevel);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,95 @@
|
|||
package com.minelittlepony.unicopia;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Objects;
|
||||
import java.util.Set;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
import com.google.gson.Gson;
|
||||
import com.google.gson.GsonBuilder;
|
||||
import com.google.gson.JsonElement;
|
||||
import com.google.gson.JsonParseException;
|
||||
import com.minelittlepony.common.util.settings.ToStringAdapter;
|
||||
import com.minelittlepony.unicopia.util.Weighted;
|
||||
|
||||
import net.fabricmc.fabric.api.resource.IdentifiableResourceReloadListener;
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.resource.JsonDataLoader;
|
||||
import net.minecraft.resource.ResourceManager;
|
||||
import net.minecraft.util.Identifier;
|
||||
import net.minecraft.util.profiler.Profiler;
|
||||
import net.minecraft.util.registry.Registry;
|
||||
|
||||
public class TreeTypeLoader extends JsonDataLoader implements IdentifiableResourceReloadListener {
|
||||
|
||||
private static final Identifier ID = new Identifier("unicopia", "data/tree_type");
|
||||
|
||||
private static final Gson GSON = new GsonBuilder()
|
||||
.registerTypeAdapter(Identifier.class, new ToStringAdapter<>(Identifier::new))
|
||||
.create();
|
||||
|
||||
static final TreeTypeLoader INSTANCE = new TreeTypeLoader();
|
||||
|
||||
private final Set<TreeType> entries = new HashSet<>();
|
||||
|
||||
TreeTypeLoader() {
|
||||
super(GSON, "tree_types");
|
||||
}
|
||||
|
||||
@Override
|
||||
public Identifier getFabricId() {
|
||||
return ID;
|
||||
}
|
||||
|
||||
public TreeType get(BlockState state) {
|
||||
return entries.stream().filter(type -> type.matches(state)).findFirst().orElse(TreeType.NONE);
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void apply(Map<Identifier, JsonElement> resources, ResourceManager manager, Profiler profiler) {
|
||||
entries.clear();
|
||||
|
||||
for (Map.Entry<Identifier, JsonElement> entry : resources.entrySet()) {
|
||||
try {
|
||||
TreeTypeDef typeDef = GSON.fromJson(entry.getValue(), TreeTypeDef.class);
|
||||
|
||||
if (typeDef != null) {
|
||||
entries.add(new TreeType(
|
||||
entry.getKey(),
|
||||
typeDef.wideTrunk,
|
||||
typeDef.getWeighted(new Weighted<Supplier<ItemStack>>()),
|
||||
Objects.requireNonNull(typeDef.logs, "TreeType must have logs"),
|
||||
Objects.requireNonNull(typeDef.leaves, "TreeType must have leaves")
|
||||
));
|
||||
}
|
||||
} catch (IllegalArgumentException | JsonParseException e) {
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static class TreeTypeDef {
|
||||
Set<Identifier> logs;
|
||||
Set<Identifier> leaves;
|
||||
Set<Drop> drops;
|
||||
boolean wideTrunk;
|
||||
|
||||
Weighted<Supplier<ItemStack>> getWeighted(Weighted<Supplier<ItemStack>> weighted) {
|
||||
drops.forEach(drop -> drop.appendDrop(weighted));
|
||||
return weighted;
|
||||
}
|
||||
|
||||
static class Drop {
|
||||
int weight;
|
||||
Identifier item;
|
||||
|
||||
void appendDrop(Weighted<Supplier<ItemStack>> weighted) {
|
||||
Registry.ITEM.getOrEmpty(item).ifPresent(item -> {
|
||||
weighted.put(weight, item::getDefaultStack);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,6 +2,9 @@ package com.minelittlepony.unicopia;
|
|||
|
||||
import net.fabricmc.api.ModInitializer;
|
||||
import net.fabricmc.fabric.api.event.lifecycle.v1.ServerTickEvents;
|
||||
import net.fabricmc.fabric.api.resource.ResourceManagerHelper;
|
||||
import net.minecraft.resource.ResourceType;
|
||||
|
||||
import org.apache.logging.log4j.LogManager;
|
||||
import org.apache.logging.log4j.Logger;
|
||||
|
||||
|
@ -37,6 +40,7 @@ public class Unicopia implements ModInitializer {
|
|||
AwaitTickQueue.tick(w);
|
||||
((BlockDestructionManager.Source)w).getDestructionManager().tick();
|
||||
});
|
||||
ResourceManagerHelper.get(ResourceType.SERVER_DATA).registerReloadListener(TreeTypeLoader.INSTANCE);
|
||||
|
||||
UItems.bootstrap();
|
||||
UPotions.bootstrap();
|
||||
|
|
|
@ -1,6 +1,5 @@
|
|||
package com.minelittlepony.unicopia.ability;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
|
@ -9,29 +8,23 @@ import javax.annotation.Nullable;
|
|||
import com.google.common.collect.Lists;
|
||||
import com.minelittlepony.unicopia.BlockDestructionManager;
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.TreeTraverser;
|
||||
import com.minelittlepony.unicopia.TreeType;
|
||||
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.util.PosHelper;
|
||||
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;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
import net.minecraft.block.Block;
|
||||
import net.minecraft.block.Blocks;
|
||||
import net.minecraft.block.LeavesBlock;
|
||||
import net.minecraft.entity.ItemEntity;
|
||||
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.math.BlockPos;
|
||||
import net.minecraft.util.math.Direction;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/**
|
||||
* Earth Pony kicking ability
|
||||
|
@ -65,11 +58,11 @@ public class EarthPonyKickAbility implements Ability<Pos> {
|
|||
|
||||
if (p.isPresent()) {
|
||||
BlockPos pos = p.get();
|
||||
BlockState state = player.getWorld().getBlockState(pos);
|
||||
TreeType tree = TreeType.get(player.getWorld().getBlockState(pos));
|
||||
|
||||
if (state.getBlock().isIn(BlockTags.LOGS)) {
|
||||
pos = TreeTraverser.Descender.descendTree(player.getWorld(), state, pos).get();
|
||||
if (TreeTraverser.Measurer.measureTree(player.getWorld(), state, pos) > 0) {
|
||||
if (tree != TreeType.NONE) {
|
||||
pos = tree.findBase(player.getWorld(), pos);
|
||||
if (tree.countBlocks(player.getWorld(), pos) > 0) {
|
||||
return new Pos(pos);
|
||||
}
|
||||
}
|
||||
|
@ -101,12 +94,19 @@ public class EarthPonyKickAbility implements Ability<Pos> {
|
|||
|
||||
if (destr.getBlockDestruction(pos) + 4 >= BlockDestructionManager.MAX_DAMAGE) {
|
||||
if (!harmed || player.world.random.nextInt(30) == 0) {
|
||||
TreeTraverser.Remover.removeTree(player.world, pos);
|
||||
TreeType.get(player.world.getBlockState(pos)).traverse(player.world, pos, (w, state, p, recurseLevel) -> {
|
||||
if (recurseLevel < 5) {
|
||||
w.breakBlock(p, true);
|
||||
} else {
|
||||
Block.dropStacks(w.getBlockState(p), w, p);
|
||||
w.setBlockState(p, Blocks.AIR.getDefaultState(), 3);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
iplayer.subtractEnergyCost(3);
|
||||
} else {
|
||||
int cost = dropApples(player.world, pos);
|
||||
int cost = dropApples(player, pos);
|
||||
|
||||
if (cost > 0) {
|
||||
iplayer.subtractEnergyCost(cost * 3);
|
||||
|
@ -149,23 +149,32 @@ public class EarthPonyKickAbility implements Ability<Pos> {
|
|||
}
|
||||
}
|
||||
|
||||
private int dropApples(World w, BlockPos pos) {
|
||||
BlockState log = w.getBlockState(pos);
|
||||
int size = TreeTraverser.Measurer.measureTree(w, log, pos);
|
||||
private int dropApples(PlayerEntity player, BlockPos pos) {
|
||||
TreeType tree = TreeType.get(player.world.getBlockState(pos));
|
||||
|
||||
if (size > 0) {
|
||||
BlockDestructionManager destr = ((BlockDestructionManager.Source)w).getDestructionManager();
|
||||
TreeTraverser.Measurer.getParts(w, log, pos).forEach(position -> {
|
||||
destr.damageBlock(position, 4);
|
||||
});
|
||||
if (tree.countBlocks(player.world, pos) > 0) {
|
||||
|
||||
List<ItemEntity> capturedDrops = Lists.newArrayList();
|
||||
|
||||
dropApplesPart(capturedDrops, new ArrayList<BlockPos>(), w, log, pos, 0);
|
||||
tree.traverse(player.world, pos, (world, state, position, recurse) -> {
|
||||
affectBlockChange(player, position);
|
||||
}, (world, state, position, recurse) -> {
|
||||
affectBlockChange(player, position);
|
||||
|
||||
if (world.getBlockState(position.down()).isAir()) {
|
||||
WorldEvent.play(WorldEvent.DESTROY_BLOCK, world, position, state);
|
||||
capturedDrops.add(new ItemEntity(world,
|
||||
position.getX() + world.random.nextFloat(),
|
||||
position.getY() - 0.5,
|
||||
position.getZ() + world.random.nextFloat(),
|
||||
tree.pickRandomStack()
|
||||
));
|
||||
}
|
||||
});
|
||||
|
||||
capturedDrops.forEach(item -> {
|
||||
item.setToDefaultPickupDelay();
|
||||
w.spawnEntity(item);
|
||||
player.world.spawnEntity(item);
|
||||
});
|
||||
|
||||
return capturedDrops.size() / 3;
|
||||
|
@ -174,27 +183,10 @@ public class EarthPonyKickAbility implements Ability<Pos> {
|
|||
return 0;
|
||||
}
|
||||
|
||||
private static void dropApplesPart(List<ItemEntity> drops, List<BlockPos> done, World w, BlockState log, BlockPos pos, int level) {
|
||||
if (!done.contains(pos)) {
|
||||
done.add(pos);
|
||||
pos = TreeTraverser.Ascender.ascendTree(w, log, pos, false);
|
||||
if (level < 10 && TreeTraverser.isWoodOrLeaf(w, log, pos)) {
|
||||
BlockState state = w.getBlockState(pos);
|
||||
private void affectBlockChange(PlayerEntity player, BlockPos position) {
|
||||
BlockDestructionManager destr = ((BlockDestructionManager.Source)player.world).getDestructionManager();
|
||||
|
||||
if (state.getBlock() instanceof LeavesBlock && w.getBlockState(pos.down()).isAir()) {
|
||||
WorldEvent.play(WorldEvent.DESTROY_BLOCK, w, pos, state);
|
||||
drops.add(new ItemEntity(w,
|
||||
pos.getX() + w.random.nextFloat(),
|
||||
pos.getY() - 0.5,
|
||||
pos.getZ() + w.random.nextFloat(),
|
||||
TreeType.get(log).pickRandomStack()
|
||||
));
|
||||
}
|
||||
destr.damageBlock(position, 4);
|
||||
|
||||
PosHelper.all(pos, p -> {
|
||||
dropApplesPart(drops, done, w, log, p, level + 1);
|
||||
}, Direction.UP, Direction.NORTH, Direction.SOUTH, Direction.EAST, Direction.WEST);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,6 @@
|
|||
package com.minelittlepony.unicopia.util;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.Iterator;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators.AbstractSpliterator;
|
||||
|
@ -19,6 +20,8 @@ import net.minecraft.world.World;
|
|||
|
||||
public interface PosHelper {
|
||||
|
||||
Direction[] HORIZONTAL = Arrays.stream(Direction.values()).filter(d -> d.getAxis().isHorizontal()).toArray(Direction[]::new);
|
||||
|
||||
static Vec3d offset(Vec3d a, Vec3i b) {
|
||||
return a.add(b.getX(), b.getY(), b.getZ());
|
||||
}
|
||||
|
|
9
src/main/resources/data/unicopia/tree_types/acacia.json
Normal file
9
src/main/resources/data/unicopia/tree_types/acacia.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"logs": [ "minecraft:acacia_log", "minecraft:acacia_wood" ],
|
||||
"leaves": [ "minecraft:acacia_leaves" ],
|
||||
"drops": [
|
||||
{ "weight": 1, "item": "unicopia:rotten_apple" },
|
||||
{ "weight": 2, "item": "unicopia:sweet_apple" },
|
||||
{ "weight": 5, "item": "unicopia:green_apple" }
|
||||
]
|
||||
}
|
9
src/main/resources/data/unicopia/tree_types/birch.json
Normal file
9
src/main/resources/data/unicopia/tree_types/birch.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"logs": [ "minecraft:birch_log", "minecraft:birch_wood" ],
|
||||
"leaves": [ "minecraft:birch_leaves" ],
|
||||
"drops": [
|
||||
{ "weight": 1, "item": "unicopia:rotten_apple" },
|
||||
{ "weight": 2, "item": "unicopia:sweet_apple" },
|
||||
{ "weight": 5, "item": "unicopia:green_apple" }
|
||||
]
|
||||
}
|
10
src/main/resources/data/unicopia/tree_types/dark_oak.json
Normal file
10
src/main/resources/data/unicopia/tree_types/dark_oak.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"logs": [ "minecraft:dark_oak_log", "minecraft:dark_oak_wood" ],
|
||||
"leaves": [ "minecraft:dark_oak_leaves" ],
|
||||
"wideTrunk": true,
|
||||
"drops": [
|
||||
{ "weight": 1, "item": "unicopia:rottenn_apple" },
|
||||
{ "weight": 2, "item": "unicopia:sweet_apple" },
|
||||
{ "weight": 5, "item": "unicopia:zap_apple" }
|
||||
]
|
||||
}
|
10
src/main/resources/data/unicopia/tree_types/jungle.json
Normal file
10
src/main/resources/data/unicopia/tree_types/jungle.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"logs": [ "minecraft:jungle_log", "minecraft:jungle_wood" ],
|
||||
"leaves": [ "minecraft:jungle_leaves" ],
|
||||
"wideTrunk": true,
|
||||
"drops": [
|
||||
{ "weight": 5, "item": "unicopia:green_apple" },
|
||||
{ "weight": 2, "item": "unicopia:sweet_apple" },
|
||||
{ "weight": 1, "item": "unicopia:zap_apple" }
|
||||
]
|
||||
}
|
9
src/main/resources/data/unicopia/tree_types/oak.json
Normal file
9
src/main/resources/data/unicopia/tree_types/oak.json
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"logs": [ "minecraft:oak_log", "minecraft:oak_wood" ],
|
||||
"leaves": [ "minecraft:oak_leaves" ],
|
||||
"drops": [
|
||||
{ "weight": 1, "item": "unicopia:rotten_apple" },
|
||||
{ "weight": 2, "item": "unicopia:green_apple" },
|
||||
{ "weight": 3, "item": "minecraft:apple" }
|
||||
]
|
||||
}
|
10
src/main/resources/data/unicopia/tree_types/spruce.json
Normal file
10
src/main/resources/data/unicopia/tree_types/spruce.json
Normal file
|
@ -0,0 +1,10 @@
|
|||
{
|
||||
"logs": [ "minecraft:spruce_log", "minecraft:spruce_wood" ],
|
||||
"leaves": [ "minecraft:spruce_leaves" ],
|
||||
"wideTrunk": true,
|
||||
"drops": [
|
||||
{ "weight": 1, "item": "unicopia:sour_apple" },
|
||||
{ "weight": 2, "item": "unicopia:green_apple" },
|
||||
{ "weight": 4, "item": "unicopia:rotten_apple" }
|
||||
]
|
||||
}
|
Loading…
Reference in a new issue