Jars can now hold fluids

This commit is contained in:
Sollace 2024-03-31 22:34:31 +01:00
parent a81336ef88
commit 7d63797d4d
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
11 changed files with 772 additions and 346 deletions

View file

@ -1,42 +1,34 @@
package com.minelittlepony.unicopia.block; package com.minelittlepony.unicopia.block;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.mixin.MixinEntityBucketItem; import com.minelittlepony.unicopia.block.jar.EntityJarContents;
import com.minelittlepony.unicopia.util.NbtSerialisable; import com.minelittlepony.unicopia.block.jar.FluidOnlyJarContents;
import com.minelittlepony.unicopia.block.jar.ItemsJarContents;
import com.minelittlepony.unicopia.block.jar.FakeFluidJarContents;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
import net.minecraft.block.BlockEntityProvider; import net.minecraft.block.BlockEntityProvider;
import net.minecraft.block.BlockRenderType; import net.minecraft.block.BlockRenderType;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.block.InventoryProvider; import net.minecraft.block.InventoryProvider;
import net.minecraft.block.entity.BlockEntity; import net.minecraft.block.entity.BlockEntity;
import net.minecraft.entity.Bucketable;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.SidedInventory; import net.minecraft.inventory.SidedInventory;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.item.Items; import net.minecraft.item.ItemUsage;
import net.minecraft.nbt.NbtCompound; import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement; import net.minecraft.nbt.NbtElement;
import net.minecraft.network.listener.ClientPlayPacketListener; import net.minecraft.network.listener.ClientPlayPacketListener;
import net.minecraft.network.packet.Packet; import net.minecraft.network.packet.Packet;
import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket; import net.minecraft.network.packet.s2c.play.BlockEntityUpdateS2CPacket;
import net.minecraft.registry.Registries;
import net.minecraft.server.world.ServerWorld; import net.minecraft.server.world.ServerWorld;
import net.minecraft.util.ActionResult; import net.minecraft.util.ActionResult;
import net.minecraft.util.Hand; import net.minecraft.util.Hand;
import net.minecraft.util.Identifier;
import net.minecraft.util.TypedActionResult; import net.minecraft.util.TypedActionResult;
import net.minecraft.util.hit.BlockHitResult; import net.minecraft.util.hit.BlockHitResult;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.world.World; import net.minecraft.world.World;
import net.minecraft.world.WorldAccess; import net.minecraft.world.WorldAccess;
@ -79,7 +71,7 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
public int getComparatorOutput(BlockState state, World world, BlockPos pos) { public int getComparatorOutput(BlockState state, World world, BlockPos pos) {
return world.getBlockEntity(pos, UBlockEntities.ITEM_JAR) return world.getBlockEntity(pos, UBlockEntities.ITEM_JAR)
.map(TileData::getItems) .map(TileData::getItems)
.map(data -> Math.min(16, data.getStacks().size())) .map(data -> Math.min(16, data.stacks().size()))
.orElse(0); .orElse(0);
} }
@ -96,7 +88,7 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
return new TileData(pos, state); return new TileData(pos, state);
} }
@Nullable
@Override @Override
public SidedInventory getInventory(BlockState state, WorldAccess world, BlockPos pos) { public SidedInventory getInventory(BlockState state, WorldAccess world, BlockPos pos) {
return world.getBlockEntity(pos, UBlockEntities.ITEM_JAR).map(TileData::getItems).orElse(null); return world.getBlockEntity(pos, UBlockEntities.ITEM_JAR).map(TileData::getItems).orElse(null);
@ -130,6 +122,16 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
return getContents() instanceof EntityJarContents c ? c : null; return getContents() instanceof EntityJarContents c ? c : null;
} }
@Nullable
public FluidJarContents getFluid() {
return getContents() instanceof FluidJarContents c ? c : null;
}
@Nullable
public FakeFluidJarContents getFakeFluid() {
return getContents() instanceof FakeFluidJarContents c ? c : null;
}
@Override @Override
public Packet<ClientPlayPacketListener> toUpdatePacket() { public Packet<ClientPlayPacketListener> toUpdatePacket() {
return BlockEntityUpdateS2CPacket.create(this); return BlockEntityUpdateS2CPacket.create(this);
@ -151,11 +153,13 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
@Override @Override
public void readNbt(NbtCompound nbt) { public void readNbt(NbtCompound nbt) {
if (nbt.contains("items", NbtElement.COMPOUND_TYPE)) { if (nbt.contains("items", NbtElement.COMPOUND_TYPE)) {
contents = new ItemsJarContents(this); contents = new ItemsJarContents(this, nbt.getCompound("items"));
contents.fromNBT(nbt.getCompound("items"));
} else if (nbt.contains("entity", NbtElement.COMPOUND_TYPE)) { } else if (nbt.contains("entity", NbtElement.COMPOUND_TYPE)) {
contents = new EntityJarContents(this); contents = new EntityJarContents(this, nbt.getCompound("entity"));
contents.fromNBT(nbt.getCompound("entity")); } else if (nbt.contains("fluid", NbtElement.COMPOUND_TYPE)) {
contents = new FluidOnlyJarContents(this, nbt.getCompound("fluid"));
} else if (nbt.contains("fakeFluid", NbtElement.COMPOUND_TYPE)) {
contents = new FakeFluidJarContents(this, nbt.getCompound("fakeFluid"));
} }
} }
@ -163,249 +167,34 @@ public class ItemJarBlock extends JarBlock implements BlockEntityProvider, Inven
protected void writeNbt(NbtCompound nbt) { protected void writeNbt(NbtCompound nbt) {
var items = getItems(); var items = getItems();
if (items != null) { if (items != null) {
nbt.put("items", items.toNBT()); nbt.put("items", items.toNBT(new NbtCompound()));
} else if (getEntity() != null) { } else if (getEntity() != null) {
nbt.put("entity", getEntity().toNBT()); nbt.put("entity", getEntity().toNBT(new NbtCompound()));
} else if (getFluid() != null) {
nbt.put("fluid", getFluid().toNBT(new NbtCompound()));
} else if (getFakeFluid() != null) {
nbt.put("fakeFluid", getFakeFluid().toNBT(new NbtCompound()));
} }
} }
} }
public interface JarContents {
public interface JarContents extends NbtSerialisable {
TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand); TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand);
void onDestroyed(); void onDestroyed();
NbtCompound toNBT(NbtCompound compound);
default void consumeAndSwap(PlayerEntity player, Hand hand, ItemStack output) {
player.setStackInHand(hand, ItemUsage.exchangeStack(player.getStackInHand(hand), player, output.copy()));
}
} }
public static class EntityJarContents implements JarContents { public interface FluidJarContents extends JarContents {
@Nullable FluidVariant fluid();
private EntityType<?> entityType;
@Nullable
private Entity renderEntity;
private final TileData tile; default long amount() {
return FluidConstants.BUCKET;
public EntityJarContents(TileData tile) {
this(tile, null);
}
public EntityJarContents(TileData tile, EntityType<?> entityType) {
this.tile = tile;
this.entityType = entityType;
}
@Nullable
public Entity getOrCreateEntity() {
if (entityType == null && tile.getWorld() != null) {
return null;
}
if (renderEntity == null || renderEntity.getType() != entityType) {
renderEntity = entityType.create(tile.getWorld());
}
return renderEntity;
}
@Override
public TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand) {
ItemStack stack = player.getStackInHand(hand);
if (stack.isOf(Items.BUCKET)) {
if (getOrCreateEntity() instanceof Bucketable bucketable) {
if (!player.isCreative()) {
stack.decrement(1);
if (stack.isEmpty()) {
player.setStackInHand(hand, bucketable.getBucketItem());
} else {
player.giveItemStack(bucketable.getBucketItem());
}
}
player.playSound(bucketable.getBucketFillSound(), 1, 1);
}
tile.markDirty();
return TypedActionResult.success(new ItemsJarContents(tile));
}
return TypedActionResult.pass(this);
}
@Override
public void onDestroyed() {
tile.getWorld().setBlockState(tile.getPos(), Blocks.WATER.getDefaultState());
Entity entity = getOrCreateEntity();
if (entity != null) {
entity.refreshPositionAfterTeleport(tile.getPos().toCenterPos());
tile.getWorld().spawnEntity(entity);
}
}
@Override
public void toNBT(NbtCompound compound) {
compound.putString("entity", EntityType.getId(entityType).toString());
}
@Override
public void fromNBT(NbtCompound compound) {
entityType = Registries.ENTITY_TYPE.getOrEmpty(Identifier.tryParse(compound.getString("entity"))).orElse(null);
}
}
public static class ItemsJarContents implements JarContents, SidedInventory {
private static final int[] SLOTS = IntStream.range(0, 16).toArray();
private final TileData tile;
private List<ItemStack> stacks = new ArrayList<>();
public ItemsJarContents(TileData tile) {
this.tile = tile;
}
@Override
public TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand) {
ItemStack handStack = player.getStackInHand(hand);
if (handStack.isEmpty()) {
if (stacks.isEmpty()) {
return TypedActionResult.fail(this);
}
dropStack(tile.getWorld(), tile.getPos(), stacks.remove(0));
markDirty();
return TypedActionResult.success(this);
}
if (stacks.isEmpty()) {
if (handStack.getItem() instanceof MixinEntityBucketItem bucket) {
if (!player.isCreative()) {
handStack.decrement(1);
if (handStack.isEmpty()) {
player.setStackInHand(hand, Items.BUCKET.getDefaultStack());
} else {
player.giveItemStack(Items.BUCKET.getDefaultStack());
}
}
player.playSound(bucket.getEmptyingSound(), 1, 1);
markDirty();
return TypedActionResult.success(new EntityJarContents(tile, bucket.getEntityType()));
}
}
if (stacks.size() >= size()) {
return TypedActionResult.fail(this);
}
stacks.add(player.isCreative() ? handStack.copyWithCount(1) : handStack.split(1));
markDirty();
return TypedActionResult.success(this);
}
@Override
public void onDestroyed() {
stacks.forEach(stack -> {
dropStack(tile.getWorld(), tile.getPos(), stack);
});
}
public List<ItemStack> getStacks() {
return stacks;
}
@Override
public int size() {
return 15;
}
@Override
public boolean isEmpty() {
return stacks.isEmpty();
}
@Override
public ItemStack getStack(int slot) {
return slot < 0 || slot >= stacks.size() ? ItemStack.EMPTY : stacks.get(slot);
}
@Override
public ItemStack removeStack(int slot, int amount) {
if (slot < 0 || slot >= stacks.size()) {
return ItemStack.EMPTY;
}
try {
ItemStack stack = stacks.get(slot);
ItemStack removed = stack.split(1);
if (stack.isEmpty()) {
stacks.remove(slot);
}
return removed;
} finally {
markDirty();
}
}
@Override
public ItemStack removeStack(int slot) {
if (slot < 0 || slot >= stacks.size()) {
return ItemStack.EMPTY;
}
try {
return stacks.remove(slot);
} finally {
markDirty();
}
}
@Override
public void setStack(int slot, ItemStack stack) {
if (slot >= stacks.size()) {
stacks.add(stack);
} else {
stacks.set(slot, stack);
}
markDirty();
}
@Override
public boolean canPlayerUse(PlayerEntity player) {
return false;
}
@Override
public void clear() {
stacks.clear();
markDirty();
}
@Override
public int[] getAvailableSlots(Direction side) {
return SLOTS;
}
@Override
public boolean canInsert(int slot, ItemStack stack, Direction dir) {
return slot >= 0 && slot < size() && slot >= stacks.size();
}
@Override
public boolean canExtract(int slot, ItemStack stack, Direction dir) {
return slot >= 0 && slot < size() && slot < stacks.size();
}
@Override
public void toNBT(NbtCompound compound) {
compound.put("items", NbtSerialisable.ITEM_STACK.writeAll(stacks));
}
@Override
public void fromNBT(NbtCompound compound) {
stacks = NbtSerialisable.ITEM_STACK.readAll(compound.getList("items", NbtElement.COMPOUND_TYPE))
.limit(size())
.collect(Collectors.toList());
}
@Override
public void markDirty() {
tile.markDirty();
} }
} }
} }

View file

@ -0,0 +1,80 @@
package com.minelittlepony.unicopia.block.jar;
import java.util.function.Supplier;
import org.jetbrains.annotations.Nullable;
import com.google.common.base.Suppliers;
import com.minelittlepony.unicopia.block.ItemJarBlock.FluidJarContents;
import com.minelittlepony.unicopia.block.ItemJarBlock.JarContents;
import com.minelittlepony.unicopia.block.ItemJarBlock.TileData;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Bucketable;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.registry.Registries;
import net.minecraft.util.Hand;
import net.minecraft.util.Identifier;
import net.minecraft.util.TypedActionResult;
public record EntityJarContents (
TileData tile,
@Nullable EntityType<?> entityType,
Supplier<@Nullable Entity> entity
) implements FluidJarContents {
public EntityJarContents(TileData tile, NbtCompound compound) {
this(tile, Registries.ENTITY_TYPE.getOrEmpty(Identifier.tryParse(compound.getString("entity"))).orElse(null));
}
public EntityJarContents(TileData tile) {
this(tile, (EntityType<?>)null);
}
public EntityJarContents(TileData tile, EntityType<?> entityType) {
this(tile, entityType, Suppliers.memoize(() -> {
return entityType == null ? null : entityType.create(tile.getWorld());
}));
}
@Override
public TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand) {
ItemStack stack = player.getStackInHand(hand);
if (stack.isOf(Items.BUCKET)) {
if (entity().get() instanceof Bucketable bucketable) {
consumeAndSwap(player, hand, bucketable.getBucketItem());
player.playSound(bucketable.getBucketFillSound(), 1, 1);
}
tile.markDirty();
return TypedActionResult.success(new ItemsJarContents(tile));
}
return TypedActionResult.pass(this);
}
@Override
public void onDestroyed() {
tile.getWorld().setBlockState(tile.getPos(), Blocks.WATER.getDefaultState());
Entity entity = entity().get();
if (entity != null) {
entity.refreshPositionAfterTeleport(tile.getPos().toCenterPos());
tile.getWorld().spawnEntity(entity);
}
}
@Override
public NbtCompound toNBT(NbtCompound compound) {
compound.putString("entity", EntityType.getId(entityType).toString());
return compound;
}
@Override
public FluidVariant fluid() {
return FluidVariant.of(Fluids.WATER);
}
}

View file

@ -0,0 +1,79 @@
package com.minelittlepony.unicopia.block.jar;
import java.util.Optional;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.block.ItemJarBlock.JarContents;
import com.minelittlepony.unicopia.block.ItemJarBlock.TileData;
import com.minelittlepony.unicopia.util.FluidHelper;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.registry.Registries;
import net.minecraft.util.Hand;
import net.minecraft.util.Identifier;
import net.minecraft.util.TypedActionResult;
public record FakeFluidJarContents (
TileData tile,
String fluid,
int color,
Item empty,
Item filled
) implements JarContents {
public FakeFluidJarContents(TileData tile, NbtCompound compound) {
this(tile, compound.getString("fluid"), compound.getInt("color"),
Registries.ITEM.get(new Identifier(compound.getString("empty"))),
Registries.ITEM.get(new Identifier(compound.getString("filled"))));
}
@Override
public TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand) {
ItemStack stack = player.getStackInHand(hand);
tile.markDirty();
return getRealFluid().map(FluidVariant::of).<TypedActionResult<JarContents>>map(fluid -> {
long remainder = FluidHelper.deposit(stack, player, hand, fluid, FluidConstants.BUCKET);
fluid.getFluid().getBucketFillSound().ifPresent(sound -> player.playSound(sound, 1, 1));
if (remainder > 0) {
return TypedActionResult.success(new FluidOnlyJarContents(tile, remainder, fluid));
}
return TypedActionResult.success(new ItemsJarContents(tile));
}).orElseGet(() -> {
if (!stack.isOf(empty)) {
return TypedActionResult.pass(this);
}
consumeAndSwap(player, hand, filled.getDefaultStack());
player.playSound("powder_snow".equalsIgnoreCase(fluid) ? USounds.Vanilla.ITEM_BUCKET_FILL_POWDER_SNOW : USounds.Vanilla.ITEM_BUCKET_FILL, 1, 1);
return TypedActionResult.success(new ItemsJarContents(tile));
});
}
@Override
public void onDestroyed() {
getRealFluid().ifPresent(fluid -> {
tile.getWorld().setBlockState(tile.getPos(), FluidHelper.getFullFluidState(FluidVariant.of(fluid)).getBlockState());
});
}
@Override
public NbtCompound toNBT(NbtCompound compound) {
compound.putString("fluid", fluid);
compound.putInt("color", color);
compound.putString("empty", Registries.ITEM.getId(empty).toString());
compound.putString("filled", Registries.ITEM.getId(filled).toString());
return compound;
}
private Optional<Fluid> getRealFluid() {
return Registries.FLUID.getIds().stream()
.filter(id -> id.getPath().equalsIgnoreCase(fluid))
.findFirst()
.map(Registries.FLUID::get);
}
}

View file

@ -0,0 +1,55 @@
package com.minelittlepony.unicopia.block.jar;
import com.minelittlepony.unicopia.block.ItemJarBlock.FluidJarContents;
import com.minelittlepony.unicopia.block.ItemJarBlock.JarContents;
import com.minelittlepony.unicopia.block.ItemJarBlock.TileData;
import com.minelittlepony.unicopia.util.FluidHelper;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.Hand;
import net.minecraft.util.TypedActionResult;
public record FluidOnlyJarContents (
TileData tile,
long amount,
FluidVariant fluid
) implements FluidJarContents {
public FluidOnlyJarContents(TileData tile, NbtCompound compound) {
this(tile, compound.getLong("amount"), FluidVariant.fromNbt(compound.getCompound("fluid")));
}
@Override
public TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand) {
ItemStack stack = player.getStackInHand(hand);
if (stack.isOf(Items.BUCKET)) {
long remainder = FluidHelper.deposit(stack, player, hand, fluid, amount);
tile.markDirty();
fluid.getFluid().getBucketFillSound().ifPresent(sound -> player.playSound(sound, 1, 1));
if (remainder > 0) {
return TypedActionResult.success(new FluidOnlyJarContents(tile, remainder, fluid));
}
return TypedActionResult.success(new ItemsJarContents(tile));
}
return TypedActionResult.pass(this);
}
@Override
public void onDestroyed() {
if (amount >= FluidConstants.BUCKET) {
tile.getWorld().setBlockState(tile.getPos(), FluidHelper.getFullFluidState(fluid).getBlockState());
}
}
@Override
public NbtCompound toNBT(NbtCompound compound) {
compound.put("fluid", fluid.toNbt());
compound.putLong("amount", amount);
return compound;
}
}

View file

@ -0,0 +1,208 @@
package com.minelittlepony.unicopia.block.jar;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.IntStream;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.block.ItemJarBlock.JarContents;
import com.minelittlepony.unicopia.block.ItemJarBlock.TileData;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.mixin.MixinEntityBucketItem;
import com.minelittlepony.unicopia.util.FluidHelper;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
import net.minecraft.block.Block;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.inventory.SidedInventory;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.util.Hand;
import net.minecraft.util.Pair;
import net.minecraft.util.TypedActionResult;
import net.minecraft.util.math.Direction;
public record ItemsJarContents (
TileData tile,
List<ItemStack> stacks
) implements JarContents, SidedInventory {
private static final int[] SLOTS = IntStream.range(0, 16).toArray();
public ItemsJarContents(TileData tile) {
this(tile, new ArrayList<>());
}
public ItemsJarContents(TileData tile, NbtCompound compound) {
this(tile, NbtSerialisable.ITEM_STACK.readAll(compound.getList("items", NbtElement.COMPOUND_TYPE))
.limit(15)
.collect(Collectors.toList()));
}
@Override
public TypedActionResult<JarContents> interact(PlayerEntity player, Hand hand) {
ItemStack handStack = player.getStackInHand(hand);
if (handStack.isEmpty()) {
if (stacks.isEmpty()) {
return TypedActionResult.fail(this);
}
Block.dropStack(tile.getWorld(), tile.getPos(), stacks.remove(0));
markDirty();
return TypedActionResult.success(this);
}
if (stacks.isEmpty()) {
if (handStack.getItem() instanceof MixinEntityBucketItem bucket) {
consumeAndSwap(player, hand, Items.BUCKET.getDefaultStack());
player.playSound(bucket.getEmptyingSound(), 1, 1);
markDirty();
return TypedActionResult.success(new EntityJarContents(tile, bucket.getEntityType()));
}
Pair<Long, FluidVariant> fluid = FluidHelper.extract(handStack, player, hand).orElse(null);
if (fluid != null) {
fluid.getRight().getFluid().getBucketFillSound().ifPresent(sound -> player.playSound(sound, 1, 1));
markDirty();
return TypedActionResult.success(new FluidOnlyJarContents(tile, fluid.getLeft(), fluid.getRight()));
}
if (handStack.isOf(Items.MILK_BUCKET)) {
consumeAndSwap(player, hand, handStack.getRecipeRemainder());
player.playSound(USounds.Vanilla.ITEM_BUCKET_EMPTY, 1, 1);
markDirty();
return TypedActionResult.success(new FakeFluidJarContents(tile, "milk", 0xFFFFFFFF, Items.BUCKET, Items.MILK_BUCKET));
}
if (handStack.isOf(Items.POWDER_SNOW_BUCKET)) {
consumeAndSwap(player, hand, Items.BUCKET.getDefaultStack());
player.playSound(USounds.Vanilla.ITEM_BUCKET_EMPTY_POWDER_SNOW, 1, 1);
markDirty();
return TypedActionResult.success(new FakeFluidJarContents(tile, "powder_snow", 0xFFFFFFFF, Items.BUCKET, Items.POWDER_SNOW_BUCKET));
}
if (handStack.isOf(UItems.LOVE_BUCKET)) {
consumeAndSwap(player, hand, handStack.getRecipeRemainder());
player.playSound(USounds.Vanilla.ITEM_BUCKET_EMPTY, 1, 1);
markDirty();
return TypedActionResult.success(new FakeFluidJarContents(tile, "love", 0xFF3030, Items.BUCKET, UItems.LOVE_BUCKET));
}
if (handStack.isOf(UItems.JUICE)) {
consumeAndSwap(player, hand, handStack.getRecipeRemainder());
player.playSound(USounds.Vanilla.ITEM_BUCKET_EMPTY, 1, 1);
markDirty();
return TypedActionResult.success(new FakeFluidJarContents(tile, "apple_juice", 0x30FF30, Items.GLASS_BOTTLE, UItems.JUICE));
}
}
if (stacks.size() >= size()) {
return TypedActionResult.fail(this);
}
stacks.add(player.isCreative() ? handStack.copyWithCount(1) : handStack.split(1));
markDirty();
return TypedActionResult.success(this);
}
@Override
public void onDestroyed() {
stacks.forEach(stack -> Block.dropStack(tile.getWorld(), tile.getPos(), stack));
}
@Override
public int size() {
return 15;
}
@Override
public boolean isEmpty() {
return stacks.isEmpty();
}
@Override
public ItemStack getStack(int slot) {
return slot < 0 || slot >= stacks.size() ? ItemStack.EMPTY : stacks.get(slot);
}
@Override
public ItemStack removeStack(int slot, int amount) {
if (slot < 0 || slot >= stacks.size()) {
return ItemStack.EMPTY;
}
try {
ItemStack stack = stacks.get(slot);
ItemStack removed = stack.split(1);
if (stack.isEmpty()) {
stacks.remove(slot);
}
return removed;
} finally {
markDirty();
}
}
@Override
public ItemStack removeStack(int slot) {
if (slot < 0 || slot >= stacks.size()) {
return ItemStack.EMPTY;
}
try {
return stacks.remove(slot);
} finally {
markDirty();
}
}
@Override
public void setStack(int slot, ItemStack stack) {
if (slot >= stacks.size()) {
stacks.add(stack);
} else {
stacks.set(slot, stack);
}
markDirty();
}
@Override
public boolean canPlayerUse(PlayerEntity player) {
return false;
}
@Override
public void clear() {
stacks.clear();
markDirty();
}
@Override
public int[] getAvailableSlots(Direction side) {
return SLOTS;
}
@Override
public boolean canInsert(int slot, ItemStack stack, Direction dir) {
return slot >= 0 && slot < size() && slot >= stacks.size();
}
@Override
public boolean canExtract(int slot, ItemStack stack, Direction dir) {
return slot >= 0 && slot < size() && slot < stacks.size();
}
@Override
public NbtCompound toNBT(NbtCompound compound) {
compound.put("items", NbtSerialisable.ITEM_STACK.writeAll(stacks));
return compound;
}
@Override
public void markDirty() {
tile.markDirty();
}
}

View file

@ -5,20 +5,15 @@ import org.joml.Vector3f;
import org.joml.Vector4f; import org.joml.Vector4f;
import net.minecraft.client.render.BufferBuilder; import net.minecraft.client.render.BufferBuilder;
import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.Tessellator; import net.minecraft.client.render.Tessellator;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.VertexFormat; import net.minecraft.client.render.VertexFormat;
import net.minecraft.client.render.VertexFormats; import net.minecraft.client.render.VertexFormats;
import net.minecraft.client.texture.Sprite;
import net.minecraft.client.util.math.MatrixStack; import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.ColorHelper;
import net.minecraft.util.math.Direction;
public class RenderUtil { public class RenderUtil {
public static final Vector4f TEMP_VECTOR = new Vector4f(); public static final Vector4f TEMP_VECTOR = new Vector4f();
private static final Vector4f TEMP_UV_VECTOR = new Vector4f(); public static final Vector4f TEMP_UV_VECTOR = new Vector4f();
public static final Vector3f TEMP_NORMAL_VECTOR = new Vector3f();
public static final Vertex[] UNIT_FACE = new Vertex[] { public static final Vertex[] UNIT_FACE = new Vertex[] {
new Vertex(0, 0, 0, 1, 1), new Vertex(0, 0, 0, 1, 1),
new Vertex(0, 1, 0, 1, 0), new Vertex(0, 1, 0, 1, 0),
@ -31,72 +26,9 @@ public class RenderUtil {
new Vertex(1, 0, 0, 1, 1), new Vertex(1, 0, 0, 1, 1),
new Vertex(0, 0, 0, 0, 1) new Vertex(0, 0, 0, 0, 1)
}; };
private static final Vertex[][] CUBE_VERTICES = {
new Vertex[] { // down
new Vertex(0, 0, 0, 0, 0),
new Vertex(1, 0, 0, 1, 0),
new Vertex(1, 0, 1, 1, 1),
new Vertex(0, 0, 1, 0, 1)
},
new Vertex[] { //up
new Vertex(0, 1, 0, 0, 0),
new Vertex(0, 1, 1, 0, 1),
new Vertex(1, 1, 1, 1, 1),
new Vertex(1, 1, 0, 1, 0)
},
new Vertex[] { //north
new Vertex(0, 0, 0, 0, 0),
new Vertex(0, 1, 0, 0, 1),
new Vertex(1, 1, 0, 1, 1),
new Vertex(1, 0, 0, 1, 0)
},
new Vertex[] { //south
new Vertex(0, 0, 1, 0, 0),
new Vertex(1, 0, 1, 1, 0),
new Vertex(1, 1, 1, 1, 1),
new Vertex(0, 1, 1, 0, 1)
},
new Vertex[] { //west
new Vertex(0, 0, 0, 0, 0),
new Vertex(0, 0, 1, 1, 0),
new Vertex(0, 1, 1, 1, 1),
new Vertex(0, 1, 0, 0, 1)
},
new Vertex[] { //east
new Vertex(1, 0, 0, 0, 0),
new Vertex(1, 1, 0, 1, 0),
new Vertex(1, 1, 1, 1, 1),
new Vertex(1, 0, 1, 0, 1)
}
};
public static void renderSpriteCubeFaces(MatrixStack matrices, VertexConsumerProvider provider, Sprite sprite,
float width, float height, float length,
int color, int light, int overlay,
Direction... directions) {
float r = ColorHelper.Abgr.getRed(color),
g = ColorHelper.Abgr.getGreen(color),
b = ColorHelper.Abgr.getBlue(color),
a = ColorHelper.Abgr.getAlpha(color);
float u0 = sprite.getMinU(), uDelta = sprite.getMaxU() - u0;
float v0 = sprite.getMinV(), vDelta = sprite.getMaxV() - v0;
RenderLayer layer = RenderLayer.getEntitySolid(sprite.getAtlasId());
VertexConsumer buffer = provider.getBuffer(layer);
Matrix4f position = matrices.peek().getPositionMatrix();
for (Direction direction : directions) {
for (Vertex vertex : CUBE_VERTICES[direction.ordinal()]) {
Vector4f pos = position.transform(TEMP_VECTOR.set(vertex.position(), 1).mul(width, height, length, 1));
buffer.vertex(
pos.x, pos.y, pos.z,
r, g, b, a,
u0 + vertex.texture().x * uDelta,
v0 + vertex.texture().y * vDelta,
overlay, light,
direction.getOffsetX(), direction.getOffsetY(), direction.getOffsetZ()
);
}
}
}
public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light) { public static void renderFace(MatrixStack matrices, Tessellator te, BufferBuilder buffer, float r, float g, float b, float a, int light) {
renderFace(matrices, te, buffer, r, g, b, a, light, 1, 1); renderFace(matrices, te, buffer, r, g, b, a, light, 1, 1);

View file

@ -1,14 +1,24 @@
package com.minelittlepony.unicopia.client.render.entity; package com.minelittlepony.unicopia.client.render.entity;
import java.util.Arrays;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.common.util.Color;
import com.minelittlepony.unicopia.block.ItemJarBlock; import com.minelittlepony.unicopia.block.ItemJarBlock;
import com.minelittlepony.unicopia.block.ItemJarBlock.EntityJarContents; import com.minelittlepony.unicopia.block.ItemJarBlock.FluidJarContents;
import com.minelittlepony.unicopia.block.ItemJarBlock.ItemsJarContents; import com.minelittlepony.unicopia.block.jar.ItemsJarContents;
import com.minelittlepony.unicopia.client.render.RenderUtil; import com.minelittlepony.unicopia.block.jar.FakeFluidJarContents;
import com.minelittlepony.unicopia.block.jar.EntityJarContents;
import com.minelittlepony.unicopia.client.render.model.CubeModel;
import com.minelittlepony.unicopia.util.FluidHelper;
import com.minelittlepony.unicopia.util.PosHelper; import com.minelittlepony.unicopia.util.PosHelper;
import net.minecraft.block.Blocks; import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandler;
import net.fabricmc.fabric.api.client.render.fluid.v1.FluidRenderHandlerRegistry;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.color.world.BiomeColors; import net.minecraft.client.render.RenderLayer;
import net.minecraft.client.render.VertexConsumerProvider; import net.minecraft.client.render.VertexConsumerProvider;
import net.minecraft.client.render.block.entity.BlockEntityRenderer; import net.minecraft.client.render.block.entity.BlockEntityRenderer;
import net.minecraft.client.render.block.entity.BlockEntityRendererFactory; import net.minecraft.client.render.block.entity.BlockEntityRendererFactory;
@ -20,8 +30,12 @@ import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.command.argument.EntityAnchorArgumentType.EntityAnchor; import net.minecraft.command.argument.EntityAnchorArgumentType.EntityAnchor;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.Fluid;
import net.minecraft.fluid.FluidState;
import net.minecraft.fluid.Fluids;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Direction;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RotationAxis; import net.minecraft.util.math.RotationAxis;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@ -29,16 +43,13 @@ import net.minecraft.util.math.random.Random;
import net.minecraft.world.World; import net.minecraft.world.World;
public class ItemJarBlockEntityRenderer implements BlockEntityRenderer<ItemJarBlock.TileData> { public class ItemJarBlockEntityRenderer implements BlockEntityRenderer<ItemJarBlock.TileData> {
private static final Direction[] GLASS_SIDES = Arrays.stream(PosHelper.ALL).filter(i -> i != Direction.UP).toArray(Direction[]::new);
private final ItemRenderer itemRenderer; private final ItemRenderer itemRenderer;
private final EntityRenderDispatcher dispatcher; private final EntityRenderDispatcher dispatcher;
private final Sprite waterSprite;
public ItemJarBlockEntityRenderer(BlockEntityRendererFactory.Context ctx) { public ItemJarBlockEntityRenderer(BlockEntityRendererFactory.Context ctx) {
itemRenderer = ctx.getItemRenderer(); itemRenderer = ctx.getItemRenderer();
dispatcher = ctx.getEntityRenderDispatcher(); dispatcher = ctx.getEntityRenderDispatcher();
waterSprite = MinecraftClient.getInstance().getBakedModelManager().getBlockModels().getModel(Blocks.WATER.getDefaultState()).getParticleSprite();
} }
@Override @Override
@ -53,6 +64,16 @@ public class ItemJarBlockEntityRenderer implements BlockEntityRenderer<ItemJarBl
if (entity != null) { if (entity != null) {
renderEntity(data, entity, tickDelta, matrices, vertices, light, overlay); renderEntity(data, entity, tickDelta, matrices, vertices, light, overlay);
} }
FluidJarContents fluid = data.getFluid();
if (fluid != null) {
renderFluid(data, fluid, tickDelta, matrices, vertices, light, overlay);
}
FakeFluidJarContents milk = data.getFakeFluid();
if (milk != null) {
renderFluid(data, Fluids.WATER.getDefaultState(), milk.color(), FluidConstants.BUCKET, tickDelta, matrices, vertices, light, overlay);
}
} }
private void renderItemStacks(ItemJarBlock.TileData data, ItemsJarContents items, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) { private void renderItemStacks(ItemJarBlock.TileData data, ItemsJarContents items, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
@ -66,7 +87,7 @@ public class ItemJarBlockEntityRenderer implements BlockEntityRenderer<ItemJarBl
Random rng = Random.create(data.getPos().asLong()); Random rng = Random.create(data.getPos().asLong());
float y = 0; float y = 0;
for (ItemStack stack : items.getStacks()) { for (ItemStack stack : items.stacks()) {
matrices.push(); matrices.push();
matrices.translate((rng.nextFloat() - 0.5F) * 0.5F, (rng.nextFloat() - 0.5F) * 0.8F, -0.05 + y); matrices.translate((rng.nextFloat() - 0.5F) * 0.5F, (rng.nextFloat() - 0.5F) * 0.8F, -0.05 + y);
@ -80,10 +101,9 @@ public class ItemJarBlockEntityRenderer implements BlockEntityRenderer<ItemJarBl
} }
private void renderEntity(ItemJarBlock.TileData data, EntityJarContents entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) { private void renderEntity(ItemJarBlock.TileData data, EntityJarContents entity, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
Entity e = entity.getOrCreateEntity(); Entity e = entity.entity().get();
if (e != null) { if (e != null) {
PlayerEntity player = MinecraftClient.getInstance().player; PlayerEntity player = MinecraftClient.getInstance().player;
int age = player == null ? 0 : player.age; int age = player == null ? 0 : player.age;
@ -109,20 +129,57 @@ public class ItemJarBlockEntityRenderer implements BlockEntityRenderer<ItemJarBl
dispatcher.render(e, 0, 0, 0, yaw * MathHelper.RADIANS_PER_DEGREE, tickDelta, matrices, vertices, light); dispatcher.render(e, 0, 0, 0, yaw * MathHelper.RADIANS_PER_DEGREE, tickDelta, matrices, vertices, light);
matrices.pop(); matrices.pop();
} }
renderFluid(data.getWorld(), data.getPos(), tickDelta, matrices, vertices, light, overlay);
} }
private void renderFluid(World world, BlockPos pos, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) { private void renderFluid(ItemJarBlock.TileData data, FluidJarContents fluid, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
FluidState state = FluidHelper.getFullFluidState(fluid.fluid());
int color = getFluidColor(data.getWorld(), data.getPos(), state);
renderFluid(data, state, color, fluid.amount(), tickDelta, matrices, vertices, light, overlay);
}
private void renderFluid(ItemJarBlock.TileData data, FluidState state, int color, long amount, float tickDelta, MatrixStack matrices, VertexConsumerProvider vertices, int light, int overlay) {
Sprite[] sprite = getFluidSprite(data.getWorld(), data.getPos(), state);
matrices.push(); matrices.push();
matrices.translate(0.3F, 0, 0.3F); Sprite topSprite = sprite[0];
RenderUtil.renderSpriteCubeFaces( float height = 0.6F * (amount / (float)FluidConstants.BUCKET);
boolean opaque = Color.a(color) >= 1;
CubeModel.render(
matrices, matrices,
vertices, vertices.getBuffer(opaque ? RenderLayer.getEntitySolid(topSprite.getAtlasId()) : RenderLayer.getEntityTranslucent(topSprite.getAtlasId())),
waterSprite, topSprite.getMinU(), topSprite.getMinV(),
0.4F, 0.4F, 0.4F, topSprite.getMaxU(), topSprite.getMaxV(),
BiomeColors.getWaterColor(world, pos), 0.28F, 0.01F, 0.28F,
light, overlay, PosHelper.ALL 0.73F, 0.01F + height, 0.73F,
color,
light, overlay, Direction.UP
);
Sprite sideSprite = sprite[sprite.length - 1];
CubeModel.render(
matrices,
vertices.getBuffer(opaque ? RenderLayer.getEntitySolid(sideSprite.getAtlasId()) : RenderLayer.getEntityTranslucent(sideSprite.getAtlasId())),
sideSprite.getMinU(), sideSprite.getMinV(),
sideSprite.getMaxU(), sideSprite.getMaxV(),
0.28F, 0.01F, 0.28F,
0.73F, 0.01F + height, 0.73F,
color,
light, overlay, GLASS_SIDES
); );
matrices.pop(); matrices.pop();
} }
private int getFluidColor(World world, BlockPos pos, FluidState state) {
return getFluidHandler(state.getFluid()).getFluidColor(world, pos, state);
}
private Sprite[] getFluidSprite(@Nullable World world, BlockPos pos, FluidState state) {
return getFluidHandler(state.getFluid()).getFluidSprites(world, pos, state);
}
private FluidRenderHandler getFluidHandler(Fluid fluid) {
FluidRenderHandler handler = FluidRenderHandlerRegistry.INSTANCE.get(fluid);
if (handler == null) {
return FluidRenderHandlerRegistry.INSTANCE.get(Fluids.WATER);
}
return handler;
}
} }

View file

@ -0,0 +1,77 @@
package com.minelittlepony.unicopia.client.render.model;
import org.joml.Matrix3f;
import org.joml.Matrix4f;
import org.joml.Vector2f;
import org.joml.Vector3f;
import org.joml.Vector4f;
import com.minelittlepony.common.util.Color;
import com.minelittlepony.unicopia.client.render.RenderUtil;
import com.minelittlepony.unicopia.client.render.RenderUtil.Vertex;
import net.minecraft.client.render.VertexConsumer;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.util.math.Direction;
public class CubeModel {
private static final Vector2f TEMP_UV_VECTOR = new Vector2f();
private static final Vertex[][] CUBE_VERTICES = {
new Vertex[] {
new Vertex(0, 0, 0, 0, 0),
new Vertex(1, 0, 0, 1, 0),
new Vertex(1, 0, 1, 1, 1),
new Vertex(0, 0, 1, 0, 1)
},
new Vertex[] {
new Vertex(0, 1, 0, 0, 0),
new Vertex(0, 1, 1, 0, 1),
new Vertex(1, 1, 1, 1, 1),
new Vertex(1, 1, 0, 1, 0)
},
new Vertex[] {
new Vertex(0, 0, 0, 0, 0),
new Vertex(0, 1, 0, 0, 1),
new Vertex(1, 1, 0, 1, 1),
new Vertex(1, 0, 0, 1, 0)
},
new Vertex[] {
new Vertex(0, 0, 1, 0, 0),
new Vertex(1, 0, 1, 1, 0),
new Vertex(1, 1, 1, 1, 1),
new Vertex(0, 1, 1, 0, 1)
},
new Vertex[] {
new Vertex(0, 0, 0, 0, 0),
new Vertex(0, 0, 1, 1, 0),
new Vertex(0, 1, 1, 1, 1),
new Vertex(0, 1, 0, 0, 1)
},
new Vertex[] {
new Vertex(1, 0, 0, 0, 0),
new Vertex(1, 1, 0, 1, 0),
new Vertex(1, 1, 1, 1, 1),
new Vertex(1, 0, 1, 0, 1)
}
};
public static void render(MatrixStack matrices, VertexConsumer buffer,
float u0, float v0, float u1, float v1,
float x0, float y0, float z0, float x1, float y1, float z1,
int color, int light, int overlay,
Direction... directions) {
float r = Color.r(color), g = Color.g(color), b = Color.b(color);
float du = u1 - u0, dv = v1 - v0;
float dx = x1 - x0, dy = y1 - y0, dz = z1 - z0;
Matrix4f position = matrices.peek().getPositionMatrix();
Matrix3f normal = matrices.peek().getNormalMatrix();
for (Direction direction : directions) {
for (Vertex vertex : CUBE_VERTICES[direction.ordinal()]) {
Vector4f pos = position.transform(RenderUtil.TEMP_VECTOR.set(vertex.position(), 1).mul(dx, dy, dz, 1).add(x0, y0, z0, 0));
Vector2f tex = TEMP_UV_VECTOR.set(vertex.texture().x, vertex.texture().y).mul(du, dv).add(u0, v0);
Vector3f norm = normal.transform(RenderUtil.TEMP_NORMAL_VECTOR.set(direction.getOffsetX(), direction.getOffsetY(), direction.getOffsetZ()));
buffer.vertex(pos.x, pos.y, pos.z, r, g, b, 1, tex.x, tex.y, overlay, light, norm.x, norm.y, norm.z);
}
}
}
}

View file

@ -1,6 +1,8 @@
package com.minelittlepony.unicopia.datagen.providers.loot; package com.minelittlepony.unicopia.datagen.providers.loot;
import java.util.List; import java.util.List;
import com.minelittlepony.unicopia.block.PieBlock;
import com.minelittlepony.unicopia.block.UBlocks; import com.minelittlepony.unicopia.block.UBlocks;
import com.minelittlepony.unicopia.datagen.providers.UModelProvider; import com.minelittlepony.unicopia.datagen.providers.UModelProvider;
import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.item.UItems;
@ -14,14 +16,21 @@ import net.minecraft.block.Block;
import net.minecraft.block.Blocks; import net.minecraft.block.Blocks;
import net.minecraft.block.enums.BedPart; import net.minecraft.block.enums.BedPart;
import net.minecraft.enchantment.Enchantments; import net.minecraft.enchantment.Enchantments;
import net.minecraft.item.Item;
import net.minecraft.item.Items; import net.minecraft.item.Items;
import net.minecraft.loot.LootPool; import net.minecraft.loot.LootPool;
import net.minecraft.loot.LootTable; import net.minecraft.loot.LootTable;
import net.minecraft.loot.condition.BlockStatePropertyLootCondition;
import net.minecraft.loot.condition.LootConditionConsumingBuilder;
import net.minecraft.loot.condition.TableBonusLootCondition; import net.minecraft.loot.condition.TableBonusLootCondition;
import net.minecraft.loot.entry.ItemEntry; import net.minecraft.loot.entry.ItemEntry;
import net.minecraft.loot.function.SetCountLootFunction; import net.minecraft.loot.function.SetCountLootFunction;
import net.minecraft.loot.provider.number.ConstantLootNumberProvider; import net.minecraft.loot.provider.number.ConstantLootNumberProvider;
import net.minecraft.loot.provider.number.UniformLootNumberProvider; import net.minecraft.loot.provider.number.UniformLootNumberProvider;
import net.minecraft.predicate.StatePredicate;
import net.minecraft.state.property.BooleanProperty;
import net.minecraft.state.property.Property;
import net.minecraft.util.StringIdentifiable;
public class UBlockLootTableProvider extends FabricBlockLootTableProvider { public class UBlockLootTableProvider extends FabricBlockLootTableProvider {
@ -116,6 +125,7 @@ public class UBlockLootTableProvider extends FabricBlockLootTableProvider {
addDrop(UBlocks.CLOUD_PILLAR, drops(UBlocks.CLOUD_PILLAR, UBlocks.CLOUD, ConstantLootNumberProvider.create(2))); addDrop(UBlocks.CLOUD_PILLAR, drops(UBlocks.CLOUD_PILLAR, UBlocks.CLOUD, ConstantLootNumberProvider.create(2)));
addDrop(UBlocks.FROSTED_OBSIDIAN, Blocks.OBSIDIAN); addDrop(UBlocks.FROSTED_OBSIDIAN, Blocks.OBSIDIAN);
addDrop(UBlocks.APPLE_PIE, pieDrops(UBlocks.APPLE_PIE, UItems.APPLE_PIE, UItems.APPLE_PIE_HOOF));
} }
private LootTable.Builder fruitLeavesDrops(Block leaves) { private LootTable.Builder fruitLeavesDrops(Block leaves) {
@ -136,4 +146,23 @@ public class UBlockLootTableProvider extends FabricBlockLootTableProvider {
); );
} }
private LootTable.Builder pieDrops(Block block, Item drop, Item stomped) {
return LootTable.builder().pool(LootPool.builder()
.rolls(ConstantLootNumberProvider.create(1)).conditionally(WITH_SILK_TOUCH)
.with(addStateCondition(block, PieBlock.STOMPED, false, applyExplosionDecay(block, ItemEntry.builder(drop))))
.with(addStateCondition(block, PieBlock.STOMPED, true, applyExplosionDecay(block, ItemEntry.builder(stomped))))
);
}
public static <T extends LootConditionConsumingBuilder<T>, P extends Comparable<P> & StringIdentifiable> T addStateCondition(Block block,
Property<P> property, P value,
LootConditionConsumingBuilder<T> builder) {
return builder.conditionally(BlockStatePropertyLootCondition.builder(block).properties(StatePredicate.Builder.create().exactMatch(property, value)));
}
public static <T extends LootConditionConsumingBuilder<T>> T addStateCondition(Block block,
BooleanProperty property, boolean value,
LootConditionConsumingBuilder<T> builder) {
return builder.conditionally(BlockStatePropertyLootCondition.builder(block).properties(StatePredicate.Builder.create().exactMatch(property, value)));
}
} }

View file

@ -0,0 +1,69 @@
package com.minelittlepony.unicopia.util;
import java.util.Iterator;
import java.util.Optional;
import java.util.function.Function;
import net.fabricmc.fabric.api.transfer.v1.context.ContainerItemContext;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidConstants;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidStorage;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
import net.fabricmc.fabric.api.transfer.v1.storage.Storage;
import net.fabricmc.fabric.api.transfer.v1.storage.StorageView;
import net.fabricmc.fabric.api.transfer.v1.transaction.Transaction;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.fluid.FluidState;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Hand;
import net.minecraft.util.Pair;
/**
* Here be dragons
*/
public interface FluidHelper {
static FluidState getFullFluidState(FluidVariant variant) {
return PsyFluidHelper.getFullFluidState(variant);
}
static Optional<Pair<Long, FluidVariant>> extract(ItemStack stack, PlayerEntity player, Hand hand) {
return getAsContainer(stack, ContainerItemContext.forPlayerInteraction(player, hand))
.filter(c -> !c.isResourceBlank())
.map(container -> applyTransaction(t -> {
FluidVariant type = container.getResource();
long amountExtracted = container.extract(type, FluidConstants.BUCKET, t);
if (amountExtracted > 0) {
return new Pair<>(amountExtracted, type);
}
return null;
}));
}
static long deposit(ItemStack stack, PlayerEntity player, Hand hand, FluidVariant variant, long amount) {
return amount - getAsStorage(stack, ContainerItemContext.forPlayerInteraction(player, hand))
.map(storage -> applyTransaction(t -> storage.insert(variant, amount, t)))
.orElse(0L);
}
private static Optional<Storage<FluidVariant>> getAsStorage(ItemStack stack, ContainerItemContext context) {
return Optional.ofNullable(FluidStorage.ITEM.find(stack, context));
}
private static Optional<StorageView<FluidVariant>> getAsContainer(ItemStack stack, ContainerItemContext context) {
return getAsStorage(stack, context).map(storage -> {
Iterator<StorageView<FluidVariant>> iter = storage.iterator();
return iter.hasNext() ? iter.next() : null;
});
}
private static <T> T applyTransaction(Function<Transaction, T> action) {
try (@SuppressWarnings("deprecation") var transaction = Transaction.isOpen()
? Transaction.getCurrentUnsafe().openNested()
: Transaction.openOuter()) {
try {
return action.apply(transaction);
} finally {
transaction.commit();
}
}
}
}

View file

@ -0,0 +1,51 @@
package com.minelittlepony.unicopia.util;
import java.lang.reflect.Method;
import java.util.Optional;
import java.util.function.Function;
import java.util.function.Supplier;
import com.google.common.base.Suppliers;
import net.fabricmc.fabric.api.transfer.v1.fluid.FluidVariant;
import net.minecraft.fluid.FluidState;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.registry.Registries;
final class PsyFluidHelper {
private static final Supplier<Optional<Class<?>>> SIMPLE_FLUID_CLASS = Suppliers.memoize(() -> {
try {
return Optional.ofNullable(Class.forName("ivorius.psychedelicraft.fluid.SimpleFluid"));
} catch (Throwable t) {
return Optional.empty();
}
});
private static final Function<FluidVariant, FluidState> FALLBACK_METHOD = fluid -> fluid.getFluid().getDefaultState();
private static final Supplier<Function<FluidVariant, FluidState>> GET_FULL_FLUID_STATE = Suppliers.memoize(() -> SIMPLE_FLUID_CLASS.get().<Function<FluidVariant, FluidState>>map(type -> {
try {
final Method method = type.getDeclaredMethod("getFluidState", ItemStack.class);
if (method != null) {
return fluid -> {
try {
ItemStack stack = Items.STONE.getDefaultStack();
NbtCompound fluidTag = stack.getOrCreateSubNbt("fluid");
fluidTag.putString("id", Registries.FLUID.getId(fluid.getFluid()).toString());
fluidTag.put("attributes", fluid.getNbt());
return FluidState.class.cast(method.invoke(type.cast(fluid), stack));
} catch (Throwable tt) {}
return FALLBACK_METHOD.apply(fluid);
};
}
} catch (Throwable t) {}
return FALLBACK_METHOD;
}).orElse(FALLBACK_METHOD));
static FluidState getFullFluidState(FluidVariant variant) {
return SIMPLE_FLUID_CLASS.get()
.filter(type -> type.isAssignableFrom(variant.getFluid().getClass()))
.map(type -> GET_FULL_FLUID_STATE.get().apply(variant))
.orElseGet(() -> variant.getFluid().getDefaultState());
}
}