PrettyPipes/src/main/java/de/ellpeck/prettypipes/network/PipeItem.java

367 lines
14 KiB
Java
Raw Normal View History

2020-04-14 17:14:24 +02:00
package de.ellpeck.prettypipes.network;
2021-12-02 16:55:04 +01:00
import com.mojang.blaze3d.vertex.PoseStack;
2020-10-16 22:36:44 +02:00
import de.ellpeck.prettypipes.PrettyPipes;
2020-04-14 18:51:43 +02:00
import de.ellpeck.prettypipes.Utility;
2020-10-18 02:32:28 +02:00
import de.ellpeck.prettypipes.pipe.IPipeItem;
2021-12-02 14:44:26 +01:00
import de.ellpeck.prettypipes.pipe.PipeBlockEntity;
2020-10-16 22:36:44 +02:00
import net.minecraft.client.Minecraft;
2021-12-02 16:55:04 +01:00
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.block.model.ItemTransforms;
import net.minecraft.core.BlockPos;
2021-12-02 12:31:04 +01:00
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.ListTag;
2021-12-02 16:55:04 +01:00
import net.minecraft.nbt.NbtUtils;
import net.minecraft.nbt.Tag;
2021-12-02 12:31:04 +01:00
import net.minecraft.resources.ResourceLocation;
2021-12-02 16:55:04 +01:00
import net.minecraft.util.Mth;
import net.minecraft.world.entity.item.ItemEntity;
import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.phys.Vec3;
2020-10-16 22:36:44 +02:00
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
2020-04-14 18:51:43 +02:00
import net.minecraftforge.items.ItemHandlerHelper;
2020-04-14 17:14:24 +02:00
import org.jgrapht.GraphPath;
import java.util.ArrayList;
import java.util.List;
import java.util.Random;
2020-04-14 18:51:43 +02:00
import java.util.function.Consumer;
2020-04-14 17:14:24 +02:00
2020-10-18 02:32:28 +02:00
public class PipeItem implements IPipeItem {
2020-10-16 22:36:44 +02:00
public static final ResourceLocation TYPE = new ResourceLocation(PrettyPipes.ID, "pipe_item");
2020-04-14 17:14:24 +02:00
public ItemStack stack;
2020-04-16 23:40:35 +02:00
public float speed;
2020-04-14 17:14:24 +02:00
public float x;
public float y;
public float z;
2020-04-14 18:51:43 +02:00
public float lastX;
public float lastY;
public float lastZ;
2020-04-14 17:14:24 +02:00
2020-08-28 00:29:44 +02:00
protected List<BlockPos> path;
protected BlockPos startInventory;
protected BlockPos destInventory;
protected BlockPos currGoalPos;
protected int currentTile;
protected boolean retryOnObstruction;
2020-08-28 00:29:44 +02:00
protected long lastWorldTick;
2020-10-16 22:36:44 +02:00
protected ResourceLocation type;
2020-04-14 17:14:24 +02:00
2020-10-16 22:36:44 +02:00
public PipeItem(ResourceLocation type, ItemStack stack, float speed) {
this.type = type;
2020-04-14 17:14:24 +02:00
this.stack = stack;
2020-04-16 23:40:35 +02:00
this.speed = speed;
2020-04-14 17:14:24 +02:00
}
2020-10-16 22:36:44 +02:00
public PipeItem(ItemStack stack, float speed) {
this(TYPE, stack, speed);
}
2021-12-02 12:31:04 +01:00
public PipeItem(ResourceLocation type, CompoundTag nbt) {
2020-10-16 22:36:44 +02:00
this.type = type;
2020-04-14 18:51:43 +02:00
this.path = new ArrayList<>();
2020-04-14 17:14:24 +02:00
this.deserializeNBT(nbt);
}
2020-10-18 02:32:28 +02:00
@Override
public ItemStack getContent() {
return this.stack;
}
@Override
public void setDestination(BlockPos startInventory, BlockPos destInventory, GraphPath<BlockPos, NetworkEdge> path) {
2020-04-15 23:13:05 +02:00
this.startInventory = startInventory;
2020-04-14 21:04:41 +02:00
this.destInventory = destInventory;
this.path = compilePath(path);
this.currGoalPos = this.getStartPipe();
2020-04-14 21:04:41 +02:00
this.currentTile = 0;
2020-04-15 23:13:05 +02:00
// initialize position if new
if (this.x == 0 && this.y == 0 && this.z == 0) {
2021-12-02 16:55:04 +01:00
this.x = Mth.lerp(0.5F, startInventory.getX(), this.currGoalPos.getX()) + 0.5F;
this.y = Mth.lerp(0.5F, startInventory.getY(), this.currGoalPos.getY()) + 0.5F;
this.z = Mth.lerp(0.5F, startInventory.getZ(), this.currGoalPos.getZ()) + 0.5F;
2020-04-15 23:13:05 +02:00
}
2020-04-14 21:04:41 +02:00
}
2020-10-18 02:32:28 +02:00
@Override
2021-12-02 14:44:26 +01:00
public void updateInPipe(PipeBlockEntity currPipe) {
// this prevents pipes being updated after one another
// causing an item that just switched to tick twice
2021-12-02 16:55:04 +01:00
var worldTick = currPipe.getLevel().getGameTime();
if (this.lastWorldTick == worldTick)
return;
this.lastWorldTick = worldTick;
2021-12-02 16:55:04 +01:00
var motionLeft = this.speed;
2020-10-13 18:42:45 +02:00
while (motionLeft > 0) {
2021-12-02 16:55:04 +01:00
var currSpeed = Math.min(0.25F, motionLeft);
2020-10-13 18:42:45 +02:00
motionLeft -= currSpeed;
2021-12-02 16:55:04 +01:00
var myPos = new BlockPos(this.x, this.y, this.z);
if (!myPos.equals(currPipe.getBlockPos()) && (currPipe.getBlockPos().equals(this.getDestPipe()) || !myPos.equals(this.startInventory))) {
2020-10-13 18:42:45 +02:00
// we're done with the current pipe, so switch to the next one
currPipe.getItems().remove(this);
2021-12-02 16:55:04 +01:00
var next = this.getNextTile(currPipe, true);
2020-04-15 23:13:05 +02:00
if (next == null) {
2021-12-02 16:55:04 +01:00
if (!currPipe.getLevel().isClientSide) {
if (currPipe.getBlockPos().equals(this.getDestPipe())) {
// ...or store in our destination container if we reached our destination
2020-10-13 18:42:45 +02:00
this.stack = this.store(currPipe);
if (!this.stack.isEmpty())
this.onPathObstructed(currPipe, true);
} else {
2020-04-15 23:13:05 +02:00
this.onPathObstructed(currPipe, false);
2020-10-13 18:42:45 +02:00
}
2020-04-15 23:13:05 +02:00
}
2020-10-13 18:42:45 +02:00
return;
2020-04-14 18:51:43 +02:00
} else {
2020-10-13 18:42:45 +02:00
next.getItems().add(this);
currPipe = next;
}
2020-10-13 18:42:45 +02:00
} else {
2022-03-04 15:15:47 +01:00
var dist = (this.currGoalPos).distToLowCornerSqr(this.x - 0.5F, this.y - 0.5F, this.z - 0.5F);
2020-10-13 18:42:45 +02:00
if (dist < currSpeed * currSpeed) {
// we're past the start of the pipe, so move to the center of the next pipe
BlockPos nextPos;
2021-12-02 16:55:04 +01:00
var next = this.getNextTile(currPipe, false);
if (next == null || next == currPipe) {
2021-12-02 16:55:04 +01:00
if (currPipe.getBlockPos().equals(this.getDestPipe())) {
2020-10-13 18:42:45 +02:00
nextPos = this.destInventory;
} else {
currPipe.getItems().remove(this);
2021-12-02 16:55:04 +01:00
if (!currPipe.getLevel().isClientSide)
2020-10-13 18:42:45 +02:00
this.onPathObstructed(currPipe, false);
return;
}
} else {
2021-12-02 16:55:04 +01:00
nextPos = next.getBlockPos();
2020-10-13 18:42:45 +02:00
}
2021-12-02 16:55:04 +01:00
var tolerance = 0.001F;
2020-10-13 18:42:45 +02:00
if (dist >= tolerance * tolerance) {
// when going around corners, we want to move right up to the corner
2021-12-02 16:55:04 +01:00
var motion = new Vec3(this.x - this.lastX, this.y - this.lastY, this.z - this.lastZ);
var diff = new Vec3(nextPos.getX() + 0.5F - this.x, nextPos.getY() + 0.5F - this.y, nextPos.getZ() + 0.5F - this.z);
if (motion.cross(diff).length() >= tolerance) {
2020-10-13 18:42:45 +02:00
currSpeed = (float) Math.sqrt(dist);
} else {
// we're not going around a corner, so continue
this.currGoalPos = nextPos;
}
2020-04-16 23:40:35 +02:00
} else {
2020-10-13 18:42:45 +02:00
// distance is very small, so continue
2020-04-16 23:40:35 +02:00
this.currGoalPos = nextPos;
}
2020-04-14 18:51:43 +02:00
}
2020-04-14 17:14:24 +02:00
}
2020-10-13 18:42:45 +02:00
this.lastX = this.x;
this.lastY = this.y;
this.lastZ = this.z;
2020-04-14 18:51:43 +02:00
2021-12-02 16:55:04 +01:00
var dist = new Vec3(this.currGoalPos.getX() + 0.5F - this.x, this.currGoalPos.getY() + 0.5F - this.y, this.currGoalPos.getZ() + 0.5F - this.z);
2020-10-13 18:42:45 +02:00
dist = dist.normalize();
this.x += dist.x * currSpeed;
this.y += dist.y * currSpeed;
this.z += dist.z * currSpeed;
}
2020-04-14 17:14:24 +02:00
}
2021-12-02 14:44:26 +01:00
protected void onPathObstructed(PipeBlockEntity currPipe, boolean tryReturn) {
2021-12-02 16:55:04 +01:00
if (currPipe.getLevel().isClientSide)
return;
2021-12-02 16:55:04 +01:00
var network = PipeNetwork.get(currPipe.getLevel());
if (tryReturn) {
// first time: we try to return to our input chest
2021-12-02 16:55:04 +01:00
if (!this.retryOnObstruction && network.routeItemToLocation(currPipe.getBlockPos(), this.destInventory, this.getStartPipe(), this.startInventory, this.stack, speed -> this)) {
this.retryOnObstruction = true;
2020-04-14 21:04:41 +02:00
return;
}
// second time: we arrived at our input chest, it is full, so we try to find a different goal location
2021-12-02 16:55:04 +01:00
var remain = network.routeItem(currPipe.getBlockPos(), this.destInventory, this.stack, (stack, speed) -> this, false);
2020-10-13 18:11:40 +02:00
if (!remain.isEmpty())
2021-12-02 16:55:04 +01:00
this.drop(currPipe.getLevel(), remain.copy());
2020-10-13 18:11:40 +02:00
} else {
// if all re-routing attempts fail, we drop
2021-12-02 16:55:04 +01:00
this.drop(currPipe.getLevel(), this.stack);
2020-04-14 21:04:41 +02:00
}
2020-04-14 17:14:24 +02:00
}
2020-10-18 02:32:28 +02:00
@Override
2021-12-02 16:55:04 +01:00
public void drop(Level world, ItemStack stack) {
var item = new ItemEntity(world, this.x, this.y, this.z, stack.copy());
item.level.addFreshEntity(item);
2020-04-14 18:51:43 +02:00
}
2020-04-14 17:14:24 +02:00
2021-12-02 14:44:26 +01:00
protected ItemStack store(PipeBlockEntity currPipe) {
2021-12-02 16:55:04 +01:00
var dir = Utility.getDirectionFromOffset(this.destInventory, this.getDestPipe());
var connectable = currPipe.getPipeConnectable(dir);
if (connectable != null)
2021-12-02 16:55:04 +01:00
return connectable.insertItem(currPipe.getBlockPos(), dir, this.stack, false);
var handler = currPipe.getItemHandler(dir);
if (handler != null)
return ItemHandlerHelper.insertItemStacked(handler, this.stack, false);
return this.stack;
2020-04-14 18:51:43 +02:00
}
2020-04-14 17:14:24 +02:00
2021-12-02 14:44:26 +01:00
protected PipeBlockEntity getNextTile(PipeBlockEntity currPipe, boolean progress) {
2020-04-14 18:51:43 +02:00
if (this.path.size() <= this.currentTile + 1)
return null;
2021-12-02 16:55:04 +01:00
var pos = this.path.get(this.currentTile + 1);
2020-04-14 18:51:43 +02:00
if (progress)
this.currentTile++;
2021-12-02 16:55:04 +01:00
var network = PipeNetwork.get(currPipe.getLevel());
2020-04-14 18:51:43 +02:00
return network.getPipe(pos);
2020-04-14 17:14:24 +02:00
}
2020-08-28 00:29:44 +02:00
protected BlockPos getStartPipe() {
return this.path.get(0);
}
2020-10-18 02:32:28 +02:00
@Override
2020-04-17 22:29:02 +02:00
public BlockPos getDestPipe() {
return this.path.get(this.path.size() - 1);
}
2020-10-18 02:32:28 +02:00
@Override
public BlockPos getCurrentPipe() {
return this.path.get(this.currentTile);
}
2020-10-18 02:32:28 +02:00
@Override
public BlockPos getDestInventory() {
return this.destInventory;
}
2020-04-14 17:14:24 +02:00
@Override
2021-12-02 12:31:04 +01:00
public CompoundTag serializeNBT() {
2021-12-02 16:55:04 +01:00
var nbt = new CompoundTag();
2020-10-16 22:36:44 +02:00
nbt.putString("type", this.type.toString());
2020-04-14 17:14:24 +02:00
nbt.put("stack", this.stack.serializeNBT());
2020-04-16 23:40:35 +02:00
nbt.putFloat("speed", this.speed);
2021-12-02 16:55:04 +01:00
nbt.put("start_inv", NbtUtils.writeBlockPos(this.startInventory));
nbt.put("dest_inv", NbtUtils.writeBlockPos(this.destInventory));
nbt.put("curr_goal", NbtUtils.writeBlockPos(this.currGoalPos));
nbt.putBoolean("drop_on_obstruction", this.retryOnObstruction);
2020-04-14 17:14:24 +02:00
nbt.putInt("tile", this.currentTile);
nbt.putFloat("x", this.x);
nbt.putFloat("y", this.y);
nbt.putFloat("z", this.z);
2021-12-02 16:55:04 +01:00
var list = new ListTag();
for (var pos : this.path)
list.add(NbtUtils.writeBlockPos(pos));
2020-04-14 17:14:24 +02:00
nbt.put("path", list);
return nbt;
}
@Override
2021-12-02 12:31:04 +01:00
public void deserializeNBT(CompoundTag nbt) {
2021-12-02 16:55:04 +01:00
this.stack = ItemStack.of(nbt.getCompound("stack"));
2020-04-16 23:40:35 +02:00
this.speed = nbt.getFloat("speed");
2021-12-02 16:55:04 +01:00
this.startInventory = NbtUtils.readBlockPos(nbt.getCompound("start_inv"));
this.destInventory = NbtUtils.readBlockPos(nbt.getCompound("dest_inv"));
this.currGoalPos = NbtUtils.readBlockPos(nbt.getCompound("curr_goal"));
this.retryOnObstruction = nbt.getBoolean("drop_on_obstruction");
2020-04-14 17:14:24 +02:00
this.currentTile = nbt.getInt("tile");
this.x = nbt.getFloat("x");
this.y = nbt.getFloat("y");
this.z = nbt.getFloat("z");
2020-04-14 18:51:43 +02:00
this.path.clear();
2021-12-02 16:55:04 +01:00
var list = nbt.getList("path", Tag.TAG_COMPOUND);
for (var i = 0; i < list.size(); i++)
this.path.add(NbtUtils.readBlockPos(list.getCompound(i)));
2020-04-14 18:51:43 +02:00
}
2020-10-18 02:32:28 +02:00
@Override
public int getItemsOnTheWay(BlockPos goalInv) {
2020-10-18 01:57:51 +02:00
return this.stack.getCount();
}
2020-10-18 02:32:28 +02:00
@Override
2020-10-16 22:36:44 +02:00
@OnlyIn(Dist.CLIENT)
2021-12-02 16:55:04 +01:00
public void render(PipeBlockEntity tile, PoseStack matrixStack, Random random, float partialTicks, int light, int overlay, MultiBufferSource source) {
2020-10-16 22:36:44 +02:00
matrixStack.translate(
2021-12-02 16:55:04 +01:00
Mth.lerp(partialTicks, this.lastX, this.x),
Mth.lerp(partialTicks, this.lastY, this.y),
Mth.lerp(partialTicks, this.lastZ, this.z));
2020-10-16 22:36:44 +02:00
if (this.stack.getItem() instanceof BlockItem) {
2021-12-02 16:55:04 +01:00
var scale = 0.7F;
2020-10-16 22:36:44 +02:00
matrixStack.scale(scale, scale, scale);
matrixStack.translate(0, -0.2F, 0);
} else {
2021-12-02 16:55:04 +01:00
var scale = 0.45F;
2020-10-16 22:36:44 +02:00
matrixStack.scale(scale, scale, scale);
matrixStack.translate(0, -0.1F, 0);
}
2021-12-02 16:55:04 +01:00
random.setSeed(Item.getId(this.stack.getItem()) + this.stack.getDamageValue());
var amount = this.getModelCount();
2020-10-16 22:36:44 +02:00
2021-12-02 16:55:04 +01:00
for (var i = 0; i < amount; i++) {
matrixStack.pushPose();
2020-10-16 22:36:44 +02:00
if (amount > 1) {
matrixStack.translate(
(random.nextFloat() * 2.0F - 1.0F) * 0.25F * 0.5F,
(random.nextFloat() * 2.0F - 1.0F) * 0.25F * 0.5F,
(random.nextFloat() * 2.0F - 1.0F) * 0.25F * 0.5F);
}
2021-12-02 16:55:04 +01:00
Minecraft.getInstance().getItemRenderer().renderStatic(this.stack, ItemTransforms.TransformType.GROUND, light, overlay, matrixStack, source, 0);
matrixStack.popPose();
2020-10-16 22:36:44 +02:00
}
}
protected int getModelCount() {
2021-12-02 16:55:04 +01:00
var i = 1;
2020-10-16 22:36:44 +02:00
if (this.stack.getCount() > 48) {
i = 5;
} else if (this.stack.getCount() > 32) {
i = 4;
} else if (this.stack.getCount() > 16) {
i = 3;
} else if (this.stack.getCount() > 1) {
i = 2;
}
return i;
}
2020-08-28 00:29:44 +02:00
protected static List<BlockPos> compilePath(GraphPath<BlockPos, NetworkEdge> path) {
2021-12-02 16:55:04 +01:00
var graph = path.getGraph();
2020-04-14 18:51:43 +02:00
List<BlockPos> ret = new ArrayList<>();
2021-12-02 16:55:04 +01:00
var nodes = path.getVertexList();
if (nodes.size() == 1) {
// add the single pipe twice if there's only one
2021-12-02 16:55:04 +01:00
// this is a dirty hack, but it works fine so eh
for (var i = 0; i < 2; i++)
ret.add(nodes.get(0));
return ret;
}
2021-12-02 16:55:04 +01:00
for (var i = 0; i < nodes.size() - 1; i++) {
var first = nodes.get(i);
var second = nodes.get(i + 1);
var edge = graph.getEdge(first, second);
var add = (Consumer<Integer>) j -> {
var pos = edge.pipes.get(j);
2020-04-14 18:51:43 +02:00
if (!ret.contains(pos))
ret.add(pos);
};
// if the edge is the other way around, we need to loop through tiles
// the other way also
if (!graph.getEdgeSource(edge).equals(first)) {
2021-12-02 16:55:04 +01:00
for (var j = edge.pipes.size() - 1; j >= 0; j--)
2020-04-14 18:51:43 +02:00
add.accept(j);
} else {
2021-12-02 16:55:04 +01:00
for (var j = 0; j < edge.pipes.size(); j++)
2020-04-14 18:51:43 +02:00
add.accept(j);
}
}
return ret;
2020-04-14 17:14:24 +02:00
}
}