Skip to content

Commit f9a37fe

Browse files
authored
Allow teapots to be filled from fluid containers, and allow milk pickup (#57)
* Add FluidHandler to teapot * Allow teapots to be filled from fluid containers, and allow milk pickup. * Added configurable teapot capacity
1 parent 903302b commit f9a37fe

4 files changed

Lines changed: 182 additions & 36 deletions

File tree

src/main/java/knightminer/simplytea/core/Registration.java

Lines changed: 21 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -35,13 +35,18 @@
3535
import net.minecraft.resources.ResourceKey;
3636
import net.minecraft.resources.ResourceLocation;
3737
import net.minecraft.sounds.SoundEvents;
38+
import net.minecraft.sounds.SoundSource;
39+
import net.minecraft.stats.Stats;
3840
import net.minecraft.world.InteractionResult;
3941
import net.minecraft.world.effect.MobEffect;
42+
import net.minecraft.world.entity.Entity;
43+
import net.minecraft.world.entity.player.Player;
4044
import net.minecraft.world.item.BlockItem;
4145
import net.minecraft.world.item.CreativeModeTab;
4246
import net.minecraft.world.item.CreativeModeTabs;
4347
import net.minecraft.world.item.Item;
4448
import net.minecraft.world.item.ItemStack;
49+
import net.minecraft.world.item.ItemUtils;
4550
import net.minecraft.world.item.crafting.RecipeSerializer;
4651
import net.minecraft.world.level.block.Block;
4752
import net.minecraft.world.level.block.Blocks;
@@ -53,8 +58,10 @@
5358
import net.minecraft.world.level.block.LayeredCauldronBlock;
5459
import net.minecraft.world.level.block.SaplingBlock;
5560
import net.minecraft.world.level.block.SoundType;
61+
import net.minecraft.world.level.block.state.BlockState;
5662
import net.minecraft.world.level.block.state.properties.NoteBlockInstrument;
5763
import net.minecraft.world.level.block.state.properties.WoodType;
64+
import net.minecraft.world.level.gameevent.GameEvent;
5865
import net.minecraft.world.level.levelgen.feature.ConfiguredFeature;
5966
import net.minecraft.world.level.levelgen.feature.Feature;
6067
import net.minecraft.world.level.levelgen.feature.configurations.NoneFeatureConfiguration;
@@ -265,9 +272,21 @@ static void registerMisc(final FMLCommonSetupEvent event) {
265272
RestfulEffect.addConflict(caffeinated);
266273
RestfulEffect.addConflict(invigorated);
267274

275+
// Lowers the cauldron by a variable amound depending on configured teapot size
276+
// Based on GLASS_BOTTLE interaction and LayeredCauldronBlock.lowerFillLevel()
268277
CauldronInteraction.WATER.put(teapot, (state, level, pos, player, hand, stack) -> {
269-
if (Config.SERVER.teapot.fillFromCauldron()) {
270-
return CauldronInteraction.fillBucket(state, level, pos, player, hand, stack, new ItemStack(teapot_water), s -> s.getValue(LayeredCauldronBlock.LEVEL) == 3, SoundEvents.BUCKET_FILL);
278+
// always consume at least one cauldron level, even for tiny teapots
279+
int teapotLevels = Math.max(1, Math.round(Config.SERVER.teapot.teapotCapacity() / 333.0f));
280+
if (Config.SERVER.teapot.fillFromCauldron() && state.getValue(LayeredCauldronBlock.LEVEL) >= teapotLevels && !level.isClientSide()) {
281+
player.setItemInHand(hand, ItemUtils.createFilledResult(stack, player, new ItemStack(teapot_water)));
282+
player.awardStat(Stats.USE_CAULDRON);
283+
player.awardStat(Stats.ITEM_USED.get(teapot_water));
284+
int cauldronLevel = state.getValue(LayeredCauldronBlock.LEVEL) - teapotLevels;
285+
BlockState cauldronstate = cauldronLevel == 0 ? Blocks.CAULDRON.defaultBlockState() : state.setValue(LayeredCauldronBlock.LEVEL, Integer.valueOf(cauldronLevel));
286+
level.setBlockAndUpdate(pos, cauldronstate);
287+
level.gameEvent(GameEvent.BLOCK_CHANGE, pos, GameEvent.Context.of(cauldronstate));
288+
level.playSound((Player)null, pos, SoundEvents.BUCKET_FILL, SoundSource.BLOCKS, 1.0F, 1.0F);
289+
level.gameEvent((Entity)null, GameEvent.FLUID_PICKUP, pos);
271290
}
272291
return InteractionResult.PASS;
273292
});

src/main/java/knightminer/simplytea/core/config/Teapot.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,11 +2,14 @@
22

33
import net.minecraftforge.common.ForgeConfigSpec;
44
import net.minecraftforge.common.ForgeConfigSpec.BooleanValue;
5+
import net.minecraftforge.common.ForgeConfigSpec.IntValue;
6+
import net.minecraftforge.fluids.FluidType;
57

68
public class Teapot {
79
private BooleanValue infinite_water;
810
private BooleanValue fill_from_cauldron;
911
private BooleanValue milk_cow;
12+
private IntValue teapot_capacity;
1013
public Teapot(ForgeConfigSpec.Builder builder) {
1114
builder.comment("Options related to filling the teapot").push("teapot");
1215
infinite_water = builder.comment("If true, the teapot will not consume water source blocks when filling. It will still consume water from tank and cauldrons.")
@@ -18,6 +21,9 @@ public Teapot(ForgeConfigSpec.Builder builder) {
1821
milk_cow = builder.comment("If true, cows can be milked using a teapot to fill it with milk")
1922
.translation("simplytea.config.teapot.milk_cow")
2023
.define("milk_cow", true);
24+
teapot_capacity = builder.comment("Amount of fluid consumed when filling a teapot from a tank")
25+
.translation("simplytea.config.teapot.teapot_capacity")
26+
.defineInRange("teapot_capacity", FluidType.BUCKET_VOLUME, 1, FluidType.BUCKET_VOLUME * 256);
2127
builder.pop();
2228
}
2329

@@ -35,4 +41,9 @@ public boolean fillFromCauldron() {
3541
public boolean canMilkCows() {
3642
return milk_cow.get();
3743
}
44+
45+
/** Amount of fluid a teapot can contain */
46+
public int teapotCapacity() {
47+
return teapot_capacity.get();
48+
}
3849
}
Lines changed: 99 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,99 @@
1+
package knightminer.simplytea.fluid;
2+
3+
import org.jetbrains.annotations.NotNull;
4+
import org.jetbrains.annotations.Nullable;
5+
6+
import knightminer.simplytea.core.Config;
7+
import knightminer.simplytea.core.Registration;
8+
import net.minecraft.core.Direction;
9+
import net.minecraft.world.item.ItemStack;
10+
import net.minecraft.world.item.Items;
11+
import net.minecraft.world.level.material.Fluid;
12+
import net.minecraft.world.level.material.Fluids;
13+
import net.minecraftforge.common.Tags;
14+
import net.minecraftforge.common.capabilities.Capability;
15+
import net.minecraftforge.common.capabilities.ForgeCapabilities;
16+
import net.minecraftforge.common.capabilities.ICapabilityProvider;
17+
import net.minecraftforge.common.util.LazyOptional;
18+
import net.minecraftforge.fluids.FluidStack;
19+
import net.minecraftforge.fluids.capability.IFluidHandlerItem;
20+
21+
public class FluidTeapotWrapper implements IFluidHandlerItem, ICapabilityProvider {
22+
private final LazyOptional<IFluidHandlerItem> holder = LazyOptional.of(() -> this);
23+
24+
@NotNull
25+
protected ItemStack container;
26+
protected FluidStack fluid = FluidStack.EMPTY;
27+
28+
public FluidTeapotWrapper(ItemStack container) {
29+
this.container = container;
30+
}
31+
32+
@Override
33+
public int getTanks() {
34+
return 1;
35+
}
36+
37+
@Override
38+
public @NotNull FluidStack getFluidInTank(int tank) {
39+
return FluidStack.EMPTY;
40+
}
41+
42+
@Override
43+
public int getTankCapacity(int tank) {
44+
return Config.SERVER.teapot.teapotCapacity();
45+
}
46+
47+
public static boolean isWater(Fluid fluid) {
48+
// Many modded fluids use the WATER tag to give the fluids entity interaction
49+
// Only accept Fluids.WATER and not any other fluid tagged as WATER
50+
return fluid.isSame(Fluids.WATER) || fluid.isSame(Fluids.FLOWING_WATER);
51+
}
52+
53+
public static boolean isMilk(Fluid fluid) {
54+
// Be more flexible with MILK because if it's tagged as MILK then it should really be milk
55+
return fluid.is(Tags.Fluids.MILK) || fluid.getBucket() == Items.MILK_BUCKET;
56+
}
57+
58+
@Override
59+
public boolean isFluidValid(int tank, @NotNull FluidStack fluid) {
60+
return isWater(fluid.getFluid()) || isMilk(fluid.getFluid());
61+
}
62+
63+
@Override
64+
public int fill(FluidStack resource, FluidAction action) {
65+
if (container.getCount() != 1 || !isFluidValid(0, resource) || resource.getAmount() < Config.SERVER.teapot.teapotCapacity()) {
66+
return 0;
67+
}
68+
69+
if (action.execute()) {
70+
if (isWater(resource.getFluid())) {
71+
this.container = new ItemStack(Registration.teapot_water);
72+
} else if (isMilk(resource.getFluid())) {
73+
this.container = new ItemStack(Registration.teapot_milk);
74+
}
75+
}
76+
77+
return Config.SERVER.teapot.teapotCapacity();
78+
}
79+
80+
@Override
81+
public @NotNull FluidStack drain(FluidStack resource, FluidAction action) {
82+
return FluidStack.EMPTY;
83+
}
84+
85+
@Override
86+
public @NotNull FluidStack drain(int maxDrain, FluidAction action) {
87+
return FluidStack.EMPTY;
88+
}
89+
90+
@Override
91+
public @NotNull ItemStack getContainer() {
92+
return container;
93+
}
94+
95+
@Override
96+
public <T> @NotNull LazyOptional<T> getCapability(@NotNull Capability<T> capability, @Nullable Direction side) {
97+
return ForgeCapabilities.FLUID_HANDLER_ITEM.orEmpty(capability, holder);
98+
}
99+
}
Lines changed: 51 additions & 34 deletions
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,35 @@
11
package knightminer.simplytea.item;
22

3+
import org.jetbrains.annotations.Nullable;
4+
35
import knightminer.simplytea.core.Config;
46
import knightminer.simplytea.core.Registration;
7+
import knightminer.simplytea.fluid.FluidTeapotWrapper;
58
import net.minecraft.core.BlockPos;
69
import net.minecraft.core.Direction;
10+
import net.minecraft.nbt.CompoundTag;
11+
import net.minecraft.sounds.SoundEvent;
712
import net.minecraft.sounds.SoundEvents;
8-
import net.minecraft.tags.FluidTags;
913
import net.minecraft.world.InteractionHand;
1014
import net.minecraft.world.InteractionResult;
1115
import net.minecraft.world.InteractionResultHolder;
1216
import net.minecraft.world.entity.LivingEntity;
1317
import net.minecraft.world.entity.animal.Cow;
1418
import net.minecraft.world.entity.player.Player;
15-
import net.minecraft.world.item.Item;
1619
import net.minecraft.world.item.ItemStack;
1720
import net.minecraft.world.item.ItemUtils;
1821
import net.minecraft.world.level.ClipContext;
1922
import net.minecraft.world.level.Level;
2023
import net.minecraft.world.level.block.BucketPickup;
2124
import net.minecraft.world.level.block.state.BlockState;
22-
import net.minecraft.world.level.material.Fluid;
23-
import net.minecraft.world.level.material.Fluids;
2425
import net.minecraft.world.phys.BlockHitResult;
2526
import net.minecraft.world.phys.HitResult.Type;
27+
import net.minecraftforge.common.capabilities.ICapabilityProvider;
28+
import net.minecraftforge.fluids.FluidActionResult;
29+
import net.minecraftforge.fluids.FluidUtil;
30+
import net.minecraftforge.fluids.capability.IFluidHandler;
31+
32+
import java.util.Optional;
2633

2734
public class TeapotItem extends TooltipItem {
2835
public TeapotItem(Properties props) {
@@ -33,40 +40,44 @@ public TeapotItem(Properties props) {
3340
public InteractionResultHolder<ItemStack> use(Level world, Player player, InteractionHand hand) {
3441
ItemStack stack = player.getItemInHand(hand);
3542
BlockHitResult rayTrace = getPlayerPOVHitResult(world, player, ClipContext.Fluid.SOURCE_ONLY);
36-
if (rayTrace.getType() == Type.BLOCK) {
37-
BlockPos pos = rayTrace.getBlockPos();
38-
BlockState state = world.getBlockState(pos);
39-
40-
// we use name for lookup to prevent default fluid conflicts
41-
Fluid fluid = state.getFluidState().getType();
42-
if(fluid != Fluids.EMPTY) {
43-
// try for water or milk using the config lists
44-
Item item = null;
45-
if (fluid.is(FluidTags.WATER)) {
46-
item = Registration.teapot_water;
47-
} // TODO: milk when mods make a standard
48-
49-
// if either one is found, update the stack
50-
if(item != null) {
51-
// water is considered infinite unless disabled in the config
52-
if(!Config.SERVER.teapot.infiniteWater()) {
53-
Direction side = rayTrace.getDirection();
54-
// unable to modify the block, fail
55-
if (!world.mayInteract(player, pos) || !player.mayUseItemAt(pos.relative(side), side, stack) || !(state.getBlock() instanceof BucketPickup)) {
56-
return new InteractionResultHolder<>(InteractionResult.FAIL, stack);
57-
}
58-
((BucketPickup)state.getBlock()).pickupBlock(world, pos, state);
59-
}
43+
if (rayTrace.getType() != Type.BLOCK) {
44+
return InteractionResultHolder.pass(stack);
45+
}
46+
47+
BlockPos pos = rayTrace.getBlockPos();
48+
Direction side = rayTrace.getDirection();
49+
BlockState state = world.getBlockState(pos);
6050

61-
stack = ItemUtils.createFilledResult(stack, player, new ItemStack(item));
51+
if (!world.mayInteract(player, pos) || !player.mayUseItemAt(pos.relative(side), side, stack)) {
52+
return InteractionResultHolder.fail(stack);
53+
}
6254

63-
// TODO: fluid sound based on fluid
64-
player.playSound(SoundEvents.BUCKET_FILL, 1.0f, 1.0f);
65-
return new InteractionResultHolder<>(InteractionResult.SUCCESS, stack);
55+
ItemStack filledStack = ItemStack.EMPTY;
56+
if (state.getBlock() instanceof BucketPickup bucketPickup) {
57+
// special case for infinite water
58+
if (FluidTeapotWrapper.isWater(state.getFluidState().getType()) && Config.SERVER.teapot.infiniteWater()) {
59+
filledStack = new ItemStack(Registration.teapot_water);
60+
Optional<SoundEvent> sound = bucketPickup.getPickupSound(state);
61+
if (sound.isPresent()) {
62+
player.playSound(sound.get(), 1.0f, 1.0f);
63+
}
64+
}
65+
66+
// use teapot like a bucket to get fluid, should work in most cases
67+
if (filledStack.isEmpty()) {
68+
FluidActionResult actionResult = FluidUtil.tryPickUpFluid(stack, player, world, pos, side);
69+
if (actionResult.isSuccess()) {
70+
filledStack = actionResult.getResult();
6671
}
6772
}
6873
}
69-
return new InteractionResultHolder<>(InteractionResult.FAIL, stack);
74+
75+
if (!filledStack.isEmpty()) {
76+
ItemStack filledResult = ItemUtils.createFilledResult(stack, player, filledStack);
77+
return InteractionResultHolder.sidedSuccess(filledResult, world.isClientSide());
78+
}
79+
80+
return InteractionResultHolder.fail(stack);
7081
}
7182

7283
@Override
@@ -82,4 +93,10 @@ public InteractionResult interactLivingEntity(ItemStack stack, Player player, Li
8293
}
8394
return InteractionResult.PASS;
8495
}
85-
}
96+
97+
@Nullable
98+
@Override
99+
public ICapabilityProvider initCapabilities(ItemStack stack, @Nullable CompoundTag nbt) {
100+
return new FluidTeapotWrapper(stack);
101+
}
102+
}

0 commit comments

Comments
 (0)