diff --git a/common/src/main/java/com/hammy275/immersivemc/client/config/screen/ImmersivesConfigScreen.java b/common/src/main/java/com/hammy275/immersivemc/client/config/screen/ImmersivesConfigScreen.java index 9d50b248..ddfca222 100644 --- a/common/src/main/java/com/hammy275/immersivemc/client/config/screen/ImmersivesConfigScreen.java +++ b/common/src/main/java/com/hammy275/immersivemc/client/config/screen/ImmersivesConfigScreen.java @@ -72,6 +72,7 @@ protected void initOptionsList() { ScreenUtils.addOption("backpack_button", ImmersiveMCConfig.useBackpack, this.list); ScreenUtils.addOption("button", ImmersiveMCConfig.useButton, this.list); ScreenUtils.addOption("campfire", ImmersiveMCConfig.useCampfireImmersion, this.list); + ScreenUtils.addOption("cauldron", ImmersiveMCConfig.useCauldronImmersion, this.list); ScreenUtils.addOption("door", ImmersiveMCConfig.useDoorImmersion, this.list); ScreenUtils.addOption("jukebox", ImmersiveMCConfig.useJukeboxImmersion, this.list); ScreenUtils.addOption("lever", ImmersiveMCConfig.useLever, this.list); diff --git a/common/src/main/java/com/hammy275/immersivemc/common/config/ActiveConfig.java b/common/src/main/java/com/hammy275/immersivemc/common/config/ActiveConfig.java index 1b00e64c..835a207b 100644 --- a/common/src/main/java/com/hammy275/immersivemc/common/config/ActiveConfig.java +++ b/common/src/main/java/com/hammy275/immersivemc/common/config/ActiveConfig.java @@ -38,6 +38,7 @@ public class ActiveConfig { public static boolean useHopperImmersion = false; public static boolean useSmithingTableImmersion = false; public static boolean useWrittenBookImmersion = false; + public static boolean useCauldronImmersion = false; // C2S Synced values public static boolean crouchBypassImmersion = false; @@ -121,6 +122,7 @@ private static void loadFromByteBuffer(FriendlyByteBuf buffer) { useHopperImmersion = buffer.readBoolean() && useHopperImmersion; useSmithingTableImmersion = buffer.readBoolean() && useSmithingTableImmersion; useWrittenBookImmersion = buffer.readBoolean() && useWrittenBookImmersion; + useCauldronImmersion = buffer.readBoolean() && useCauldronImmersion; } @@ -160,6 +162,7 @@ public static void loadConfigFromFile(boolean forceLoadServerSettings) { useHopperImmersion = ImmersiveMCConfig.useHopperImmersion.get(); useSmithingTableImmersion = ImmersiveMCConfig.useSmithingTableImmersion.get(); useWrittenBookImmersion = ImmersiveMCConfig.useWrittenBookImmersion.get(); + useCauldronImmersion = ImmersiveMCConfig.useCauldronImmersion.get(); } else { ImmersiveMC.LOGGER.debug("Not re-loading immersive options since we're in a world!"); } @@ -218,6 +221,7 @@ public static void loadOffConfig() { useHopperImmersion = false; useSmithingTableImmersion = false; useWrittenBookImmersion = false; + useCauldronImmersion = false; ImmersiveMC.LOGGER.debug("Loaded 'disabled' config: \n" + asString()); } @@ -228,7 +232,8 @@ public static FriendlyByteBuf encodeServerOnlyConfig(FriendlyByteBuf buffer) { .writeBoolean(ActiveConfig.canPet).writeBoolean(ActiveConfig.useArmorImmersion) .writeBoolean(ActiveConfig.canFeedAnimals).writeBoolean(ActiveConfig.canPetAnyLiving) .writeInt(ActiveConfig.rangedGrabRange).writeBoolean(ActiveConfig.crouchBypassImmersion) - .writeBoolean(ActiveConfig.doRumble).writeBoolean(ActiveConfig.returnItems); + .writeBoolean(ActiveConfig.doRumble).writeBoolean(ActiveConfig.returnItems) + .writeBoolean(ActiveConfig.useCauldronImmersion); return buffer; } @@ -278,7 +283,8 @@ public static String asString() { "Use Smithing Table Immersion: " + useSmithingTableImmersion + "\n" + "Do Rumble: " + doRumble + "\n" + "Return Items: " + returnItems + "\n" + - "Use Written Book Immersion: " + useWrittenBookImmersion; + "Use Written Book Immersion: " + useWrittenBookImmersion + "\n" + + "Use Cauldron Immersion: " + useCauldronImmersion; return stringOut; } diff --git a/common/src/main/java/com/hammy275/immersivemc/common/config/ImmersiveMCConfig.java b/common/src/main/java/com/hammy275/immersivemc/common/config/ImmersiveMCConfig.java index a4f90922..e58dc8cb 100644 --- a/common/src/main/java/com/hammy275/immersivemc/common/config/ImmersiveMCConfig.java +++ b/common/src/main/java/com/hammy275/immersivemc/common/config/ImmersiveMCConfig.java @@ -41,6 +41,7 @@ public class ImmersiveMCConfig { public static ForgeConfigSpec.BooleanValue useHopperImmersion; public static ForgeConfigSpec.BooleanValue useSmithingTableImmersion; public static ForgeConfigSpec.BooleanValue useWrittenBookImmersion; + public static ForgeConfigSpec.BooleanValue useCauldronImmersion; // C2S Only Sync public static ForgeConfigSpec.BooleanValue crouchBypassImmersion; @@ -162,6 +163,9 @@ protected static void setupConfig(ForgeConfigSpec.Builder builder) { useWrittenBookImmersion = builder .comment("Whether immersives for written books should be allowed.") .define("written_book_immersion", true); + useCauldronImmersion = builder + .comment("Whether immersives for cauldrons should be allowed.") + .define("cauldron_immersion", true); // C2S Only Sync crouchBypassImmersion = builder @@ -260,7 +264,8 @@ public static void encode(FriendlyByteBuf buffer) { .writeBoolean(allowThrowingBeyondMax.get()) .writeBoolean(useHopperImmersion.get()) .writeBoolean(useSmithingTableImmersion.get()) - .writeBoolean(useWrittenBookImmersion.get()); + .writeBoolean(useWrittenBookImmersion.get()) + .writeBoolean(useCauldronImmersion.get()); } public static void resetToDefault() { @@ -293,6 +298,7 @@ public static void resetToDefault() { useHopperImmersion.set(true); useSmithingTableImmersion.set(true); useWrittenBookImmersion.set(true); + useCauldronImmersion.set(true); // C2S Synced Values crouchBypassImmersion.set(true); diff --git a/common/src/main/java/com/hammy275/immersivemc/common/config/ServerPlayerConfig.java b/common/src/main/java/com/hammy275/immersivemc/common/config/ServerPlayerConfig.java index d305ec48..d53591b9 100644 --- a/common/src/main/java/com/hammy275/immersivemc/common/config/ServerPlayerConfig.java +++ b/common/src/main/java/com/hammy275/immersivemc/common/config/ServerPlayerConfig.java @@ -7,7 +7,7 @@ public class ServerPlayerConfig { public static final ServerPlayerConfig EMPTY_CONFIG = new ServerPlayerConfig(false, false, false, false, false, false, false, false, false, 0, - false, false, false); + false, false, false, false); public boolean useButtons; public boolean useCampfire; @@ -22,12 +22,13 @@ public class ServerPlayerConfig { public boolean crouchBypassImmersion; public boolean doRumble; public boolean returnItems; + public boolean useCauldronImmersion; public ServerPlayerConfig(boolean useButtons, boolean useCampfire, boolean useLevers, boolean useRangedGrab, boolean useDoorImmersion, boolean canPet, boolean useArmorImmersion, boolean canFeedAnimals, boolean canPetAnyLiving, int rangedGrabRange, boolean crouchBypassImmersion, - boolean doRumble, boolean returnItems) { + boolean doRumble, boolean returnItems, boolean useCauldronImmersion) { this.useButtons = useButtons && ActiveConfig.useButton; this.useCampfire = useCampfire && ActiveConfig.useCampfireImmersion; this.useLevers = useLevers && ActiveConfig.useLever; @@ -41,13 +42,14 @@ public ServerPlayerConfig(boolean useButtons, boolean useCampfire, boolean useLe this.crouchBypassImmersion = crouchBypassImmersion && ActiveConfig.crouchBypassImmersion; this.doRumble = doRumble; // Don't check server for this one, no need to allow admins to disable rumble. this.returnItems = returnItems; + this.useCauldronImmersion = useCauldronImmersion && ActiveConfig.useCauldronImmersion; } public ServerPlayerConfig(FriendlyByteBuf buffer) { this(buffer.readBoolean(), buffer.readBoolean(), buffer.readBoolean(), buffer.readBoolean(), buffer.readBoolean(), buffer.readBoolean(), buffer.readBoolean(), buffer.readBoolean(), buffer.readBoolean(), buffer.readInt(), buffer.readBoolean(), - buffer.readBoolean(), buffer.readBoolean()); + buffer.readBoolean(), buffer.readBoolean(), buffer.readBoolean()); buffer.release(); } } diff --git a/common/src/main/java/com/hammy275/immersivemc/mixin/AbstractCauldronBlockAccessor.java b/common/src/main/java/com/hammy275/immersivemc/mixin/AbstractCauldronBlockAccessor.java new file mode 100644 index 00000000..b24290bf --- /dev/null +++ b/common/src/main/java/com/hammy275/immersivemc/mixin/AbstractCauldronBlockAccessor.java @@ -0,0 +1,16 @@ +package com.hammy275.immersivemc.mixin; + +import net.minecraft.core.cauldron.CauldronInteraction; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.AbstractCauldronBlock; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Accessor; + +import java.util.Map; + +@Mixin(AbstractCauldronBlock.class) +public interface AbstractCauldronBlockAccessor { + + @Accessor("interactions") + public Map getInteractions(); +} diff --git a/common/src/main/java/com/hammy275/immersivemc/server/tracker/ServerTrackerInit.java b/common/src/main/java/com/hammy275/immersivemc/server/tracker/ServerTrackerInit.java index 1aa3e82a..bdfbf8b6 100644 --- a/common/src/main/java/com/hammy275/immersivemc/server/tracker/ServerTrackerInit.java +++ b/common/src/main/java/com/hammy275/immersivemc/server/tracker/ServerTrackerInit.java @@ -21,4 +21,5 @@ public class ServerTrackerInit { public static final PetTracker petTracker = new PetTracker(); public static final ArmorTracker armorTracker = new ArmorTracker(); public static final FeedAnimalsTracker feedAnimalsTracker = new FeedAnimalsTracker(); + public static final CauldronTracker cauldronTracker = new CauldronTracker(); } diff --git a/common/src/main/java/com/hammy275/immersivemc/server/tracker/vrhand/CauldronTracker.java b/common/src/main/java/com/hammy275/immersivemc/server/tracker/vrhand/CauldronTracker.java new file mode 100644 index 00000000..e672ed9b --- /dev/null +++ b/common/src/main/java/com/hammy275/immersivemc/server/tracker/vrhand/CauldronTracker.java @@ -0,0 +1,97 @@ +package com.hammy275.immersivemc.server.tracker.vrhand; + +import com.hammy275.immersivemc.common.config.ServerPlayerConfig; +import com.hammy275.immersivemc.mixin.AbstractCauldronBlockAccessor; +import com.hammy275.immersivemc.server.data.LastTickData; +import net.blf02.vrapi.api.data.IVRData; +import net.blf02.vrapi.api.data.IVRPlayer; +import net.minecraft.core.BlockPos; +import net.minecraft.core.cauldron.CauldronInteraction; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.*; +import net.minecraft.world.item.alchemy.Potion; +import net.minecraft.world.item.alchemy.PotionUtils; +import net.minecraft.world.item.alchemy.Potions; +import net.minecraft.world.level.block.AbstractCauldronBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluids; + +import java.util.HashMap; +import java.util.Map; +import java.util.UUID; + +public class CauldronTracker extends AbstractVRHandTracker { + + private Map cooldown = new HashMap<>(); + + @Override + public void preTick(Player player) { + super.preTick(player); + int newCooldown = cooldown.getOrDefault(player.getUUID(), 0) - 1; + if (newCooldown <= 0) { + cooldown.remove(player.getUUID()); + } else { + cooldown.put(player.getUUID(), newCooldown); + } + } + + @Override + protected boolean shouldRunForHand(Player player, InteractionHand hand, ItemStack stackInHand, IVRPlayer currentVRData, LastTickData lastVRData) { + if (cooldown.getOrDefault(player.getUUID(), 0) > 0) return false; + IVRData data = currentVRData.getController(hand.ordinal()); + // If block at hand pos is cauldron or block at hand pos is air and block below is cauldron. + return player.level.getBlockState(new BlockPos(data.position())).getBlock() instanceof AbstractCauldronBlock || + (player.level.getBlockState(new BlockPos(data.position()).below()).getBlock() instanceof AbstractCauldronBlock + && player.level.getBlockState(new BlockPos(data.position())).isAir()); + } + + @Override + protected void runForHand(Player player, InteractionHand hand, ItemStack stackInHand, IVRPlayer currentVRData, LastTickData lastVRData) { + IVRData data = currentVRData.getController(hand.ordinal()); + BlockState handState = player.level.getBlockState(new BlockPos(data.position())); + + BlockPos cauldronPos = handState.getBlock() instanceof AbstractCauldronBlock ? new BlockPos(data.position()) : + new BlockPos(data.position()).below(); + BlockState cauldron = player.level.getBlockState(cauldronPos); + AbstractCauldronBlock cauldronBlock = (AbstractCauldronBlock) cauldron.getBlock(); + boolean inCauldronBlock = handState.getBlock() instanceof AbstractCauldronBlock; + ItemStack handStack = player.getItemInHand(hand); + Item handItem = handStack.getItem(); + Potion heldPotion = PotionUtils.getPotion(handStack); + CauldronInteraction interaction = ((AbstractCauldronBlockAccessor) cauldronBlock).getInteractions().get(handItem); + + if (interaction == null) return; + + // If holding an empty bucket or glass bottle, see if we can fill it. + if (inCauldronBlock && (handItem instanceof BottleItem || (handItem instanceof BucketItem bucketItem && bucketItem.arch$getFluid().isSame(Fluids.EMPTY)))) { + // Pointing up in any way + if (Math.abs(data.getRoll()) < 90) { + possiblySetCooldown(player, interaction.interact(cauldron, player.level, cauldronPos, player, hand, handStack)); + } + } else if ((handItem instanceof PotionItem && heldPotion == Potions.WATER) || + (handItem instanceof BucketItem bucketItem && !bucketItem.arch$getFluid().isSame(Fluids.EMPTY)) || + handItem instanceof SolidBucketItem) { + // 20-degrees in either direction from straight down + if (Math.abs(data.getRoll()) > 160) { + possiblySetCooldown(player, interaction.interact(cauldron, player.level, cauldronPos, player, hand, handStack)); + } + } else if (inCauldronBlock) { + possiblySetCooldown(player, interaction.interact(cauldron, player.level, cauldronPos, player, hand, handStack)); + } + + + } + + @Override + public boolean isEnabledInConfig(ServerPlayerConfig config) { + return config.useCauldronImmersion; + } + + private void possiblySetCooldown(Player player, InteractionResult res) { + if (res.consumesAction()) { + cooldown.put(player.getUUID(), 5); + } + } +} diff --git a/common/src/main/resources/assets/immersivemc/lang/en_us.json b/common/src/main/resources/assets/immersivemc/lang/en_us.json index 1662658a..c3bd5636 100644 --- a/common/src/main/resources/assets/immersivemc/lang/en_us.json +++ b/common/src/main/resources/assets/immersivemc/lang/en_us.json @@ -50,8 +50,10 @@ "config.immersivemc.brewing.desc": "Enables/disables the ability to use the brewing stand immersive.", "config.immersivemc.button": "Use Button Immersion [VR Only]", "config.immersivemc.button.desc": "Enables/disables the ability for VR players to push buttons with their hand.", - "config.immersivemc.campfire": "Use Campfire Immmersion [VR Only]", + "config.immersivemc.campfire": "Use Campfire Immersion [VR Only]", "config.immersivemc.campfire.desc": "Enables/disables the ability for VR players to hold food above a campfire to cook it.", + "config.immersivemc.cauldron": "Use Cauldron Immersion [VR Only]", + "config.immersivemc.cauldron.desc": "Enables/disables the ability to interact with a cauldron without button presses. Turn an item filled with liquid upside-down over a cauldron to empty it, place an empty item that can be filled with liquid into a cauldron to fill it up, or place an item that interacts with a cauldron inside to perform the interaction! VR Only.", "config.immersivemc.chest": "Use Chest Immersion", "config.immersivemc.chest.desc": "Enables/disables the ability for players to open/close chests without the GUI. Make sure to enable 'Right Click Chests' to use the chest outside of VR! You can punch the chest to cycle which items are on top!", "config.immersivemc.door": "Use Door Immersion [VR Only]", diff --git a/common/src/main/resources/mixins.immersivemc.json b/common/src/main/resources/mixins.immersivemc.json index 80a0d338..daa6aa1a 100644 --- a/common/src/main/resources/mixins.immersivemc.json +++ b/common/src/main/resources/mixins.immersivemc.json @@ -6,6 +6,7 @@ "refmap": "immersivemc-common-refmap.json", "plugin": "com.hammy275.immersivemc.MixinConfigPlugin", "mixins": [ + "AbstractCauldronBlockAccessor", "AnvilMenuMixin", "BeaconBlockEntityMixin", "ButtonBlockMixin", @@ -30,7 +31,7 @@ "MinecraftMixin", "MinecraftMixinAccessor", "MultiPlayerGameModeMixin", - "throw_render_helpers.vivecraft.VivecraftItemRenderingMixin", - "ScreenMixin" + "ScreenMixin", + "throw_render_helpers.vivecraft.VivecraftItemRenderingMixin" ] } \ No newline at end of file