mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2024-12-14 12:10:30 +00:00
Allow placing items against some blocks
We define a tag which allows specifying which blocks can be used. Right now this is is just cauldrons and hives, as they have "placing into" semantics. Closes #1305. Many thanks to Lindsay-Needs-Sleep for their initial work on this! Fixes #1008. I believe also fixes #854.
This commit is contained in:
parent
566315947b
commit
118d04f018
@ -14,6 +14,8 @@ import net.minecraft.world.item.Item;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
|
||||
/**
|
||||
* Tags provided by ComputerCraft.
|
||||
@ -65,6 +67,12 @@ public class ComputerCraftTags {
|
||||
*/
|
||||
public static final TagKey<Block> TURTLE_HOE_BREAKABLE = make("turtle_hoe_harvestable");
|
||||
|
||||
/**
|
||||
* Block which can be {@linkplain BlockState#use(Level, Player, InteractionHand, BlockHitResult) used} when
|
||||
* calling {@code turtle.place()}.
|
||||
*/
|
||||
public static final TagKey<Block> TURTLE_CAN_USE = make("turtle_can_use");
|
||||
|
||||
private static TagKey<Block> make(String name) {
|
||||
return TagKey.create(Registries.BLOCK, new ResourceLocation(ComputerCraftAPI.MOD_ID, name));
|
||||
}
|
||||
|
@ -57,6 +57,8 @@ class TagProvider {
|
||||
|
||||
tags.tag(ComputerCraftTags.Blocks.TURTLE_SWORD_BREAKABLE).addTag(BlockTags.WOOL).add(Blocks.COBWEB);
|
||||
|
||||
tags.tag(ComputerCraftTags.Blocks.TURTLE_CAN_USE).addTag(BlockTags.CAULDRONS).addTag(BlockTags.BEEHIVES);
|
||||
|
||||
// Make all blocks aside from command computer mineable.
|
||||
tags.tag(BlockTags.MINEABLE_WITH_PICKAXE).add(
|
||||
ModRegistry.Blocks.COMPUTER_NORMAL.get(),
|
||||
|
@ -53,6 +53,7 @@ import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
/**
|
||||
* This extends {@linkplain dan200.computercraft.impl.PlatformHelper the API's loader abstraction layer}, adding
|
||||
@ -385,13 +386,15 @@ public interface PlatformHelper extends dan200.computercraft.impl.PlatformHelper
|
||||
* Place an item against a block.
|
||||
* <p>
|
||||
* Implementations should largely mirror {@link ServerPlayerGameMode#useItemOn(ServerPlayer, Level, ItemStack, InteractionHand, BlockHitResult)}
|
||||
* (including any loader-specific modifications), except they should skip the call to {@link BlockState#use(Level, Player, InteractionHand, BlockHitResult)}.
|
||||
* (including any loader-specific modifications), except the call to {@link BlockState#use(Level, Player, InteractionHand, BlockHitResult)}
|
||||
* should only be evaluated when {@code canUseBlock} evaluates to true.
|
||||
*
|
||||
* @param player The player which is placing this item.
|
||||
* @param stack The item to place.
|
||||
* @param hit The collision with the block we're placing against.
|
||||
* @param canUseBlock Test whether the block should be interacted with first.
|
||||
* @return Whether any interaction occurred.
|
||||
* @see ServerPlayerGameMode#useItemOn(ServerPlayer, Level, ItemStack, InteractionHand, BlockHitResult)
|
||||
*/
|
||||
InteractionResult useOn(ServerPlayer player, ItemStack stack, BlockHitResult hit);
|
||||
InteractionResult useOn(ServerPlayer player, ItemStack stack, BlockHitResult hit, Predicate<BlockState> canUseBlock);
|
||||
}
|
||||
|
@ -22,6 +22,7 @@ import net.minecraft.network.chat.Component;
|
||||
import net.minecraft.server.level.ServerLevel;
|
||||
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.context.BlockPlaceContext;
|
||||
import net.minecraft.world.item.context.UseOnContext;
|
||||
@ -29,6 +30,7 @@ import net.minecraft.world.level.Level;
|
||||
import net.minecraft.world.level.block.Block;
|
||||
import net.minecraft.world.level.block.entity.BlockEntity;
|
||||
import net.minecraft.world.level.block.entity.SignBlockEntity;
|
||||
import net.minecraft.world.level.block.state.BlockState;
|
||||
import net.minecraft.world.phys.BlockHitResult;
|
||||
import net.minecraft.world.phys.EntityHitResult;
|
||||
import net.minecraft.world.phys.Vec3;
|
||||
@ -153,7 +155,7 @@ public class TurtlePlaceCommand implements TurtleCommand {
|
||||
|
||||
private static boolean deployOnBlock(
|
||||
ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, BlockPos position, Direction side,
|
||||
@Nullable Object[] extraArguments, boolean allowReplace, @Nullable ErrorMessage outErrorMessage
|
||||
@Nullable Object[] extraArguments, boolean adjacent, @Nullable ErrorMessage outErrorMessage
|
||||
) {
|
||||
// Re-orient the fake player
|
||||
var playerDir = side.getOpposite();
|
||||
@ -169,14 +171,14 @@ public class TurtlePlaceCommand implements TurtleCommand {
|
||||
// Check if there's something suitable to place onto
|
||||
var hit = new BlockHitResult(new Vec3(hitX, hitY, hitZ), side, position, false);
|
||||
var context = new UseOnContext(turtlePlayer.player(), InteractionHand.MAIN_HAND, hit);
|
||||
if (!canDeployOnBlock(new BlockPlaceContext(context), turtle, turtlePlayer, position, side, allowReplace, outErrorMessage)) {
|
||||
if (!canDeployOnBlock(new BlockPlaceContext(context), turtle, turtlePlayer, position, side, adjacent, outErrorMessage)) {
|
||||
return false;
|
||||
}
|
||||
|
||||
var item = stack.getItem();
|
||||
var existingTile = turtle.getLevel().getBlockEntity(position);
|
||||
|
||||
var placed = doDeployOnBlock(stack, turtlePlayer, hit).consumesAction();
|
||||
var placed = doDeployOnBlock(stack, turtlePlayer, hit, adjacent).consumesAction();
|
||||
|
||||
// Set text on signs
|
||||
if (placed && item instanceof SignItem && extraArguments != null && extraArguments.length >= 1 && extraArguments[0] instanceof String message) {
|
||||
@ -198,18 +200,23 @@ public class TurtlePlaceCommand implements TurtleCommand {
|
||||
* @param stack The stack the player is using.
|
||||
* @param turtlePlayer The player which represents the turtle
|
||||
* @param hit Where the block we're placing against was clicked.
|
||||
* @param adjacent If the block is directly adjacent to the turtle, and so can be interacted with via
|
||||
* {@link BlockState#use(Level, Player, InteractionHand, BlockHitResult)}.
|
||||
* @return If this item was deployed.
|
||||
*/
|
||||
private static InteractionResult doDeployOnBlock(
|
||||
ItemStack stack, TurtlePlayer turtlePlayer, BlockHitResult hit
|
||||
) {
|
||||
var result = PlatformHelper.get().useOn(turtlePlayer.player(), stack, hit);
|
||||
private static InteractionResult doDeployOnBlock(ItemStack stack, TurtlePlayer turtlePlayer, BlockHitResult hit, boolean adjacent) {
|
||||
var result = PlatformHelper.get().useOn(
|
||||
turtlePlayer.player(), stack, hit,
|
||||
adjacent ? x -> x.is(ComputerCraftTags.Blocks.TURTLE_CAN_USE) : x -> false
|
||||
);
|
||||
if (result != InteractionResult.PASS) return result;
|
||||
|
||||
var level = turtlePlayer.player().level;
|
||||
|
||||
// We special case some items which we allow to place "normally". Yes, this is very ugly.
|
||||
var item = stack.getItem();
|
||||
if (item instanceof BucketItem || item instanceof PlaceOnWaterBlockItem || stack.is(ComputerCraftTags.Items.TURTLE_CAN_PLACE)) {
|
||||
return turtlePlayer.player().gameMode.useItem(turtlePlayer.player(), turtlePlayer.player().level, stack, InteractionHand.MAIN_HAND);
|
||||
return turtlePlayer.player().gameMode.useItem(turtlePlayer.player(), level, stack, InteractionHand.MAIN_HAND);
|
||||
}
|
||||
|
||||
return InteractionResult.PASS;
|
||||
|
@ -55,6 +55,7 @@ import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Predicate;
|
||||
|
||||
@AutoService({ PlatformHelper.class, dan200.computercraft.impl.PlatformHelper.class, ComputerCraftAPIService.class })
|
||||
public class TestPlatformHelper extends AbstractComputerCraftAPI implements PlatformHelper {
|
||||
@ -202,7 +203,7 @@ public class TestPlatformHelper extends AbstractComputerCraftAPI implements Plat
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult useOn(ServerPlayer player, ItemStack stack, BlockHitResult hit) {
|
||||
public InteractionResult useOn(ServerPlayer player, ItemStack stack, BlockHitResult hit, Predicate<BlockState> canUseBlock) {
|
||||
throw new UnsupportedOperationException("Cannot interact with the world inside tests");
|
||||
}
|
||||
|
||||
|
@ -185,7 +185,7 @@ class Turtle_Test {
|
||||
*
|
||||
* Currently not required as turtles can no longer right-click cauldrons.
|
||||
*/
|
||||
@GameTest(required = false)
|
||||
@GameTest
|
||||
fun Cleaned_with_cauldrons(helper: GameTestHelper) = helper.sequence {
|
||||
thenOnComputer {
|
||||
val details = getTurtleItemDetail(1, true)
|
||||
|
@ -23,6 +23,7 @@ import net.minecraft.gametest.framework.GameTestSequence
|
||||
import org.apache.logging.log4j.LogManager
|
||||
import java.io.InputStream
|
||||
import java.util.*
|
||||
import java.util.concurrent.CancellationException
|
||||
import java.util.concurrent.ConcurrentLinkedDeque
|
||||
import java.util.concurrent.atomic.AtomicReference
|
||||
|
||||
@ -46,7 +47,7 @@ object ManagedComputers : ILuaMachine.Factory {
|
||||
task()
|
||||
monitor.result.set(Result.success(Unit))
|
||||
} catch (e: Throwable) {
|
||||
if (e !is AssertionError) LOGGER.error("Computer $label failed", e)
|
||||
if (e !is AssertionError && e !is CancellationException) LOGGER.error("Computer $label failed", e)
|
||||
monitor.result.set(Result.failure(e))
|
||||
throw e
|
||||
} finally {
|
||||
|
@ -1,5 +1,5 @@
|
||||
{
|
||||
DataVersion: 2730,
|
||||
DataVersion: 3218,
|
||||
size: [3, 3, 3],
|
||||
data: [
|
||||
{pos: [0, 0, 0], state: "minecraft:polished_andesite"},
|
||||
@ -15,7 +15,7 @@
|
||||
{pos: [0, 1, 1], state: "minecraft:air"},
|
||||
{pos: [0, 1, 2], state: "minecraft:air"},
|
||||
{pos: [1, 1, 0], state: "computercraft:turtle_normal{facing:south,waterlogged:false}", nbt: {ComputerId: 1, Fuel: 0, Items: [{Count: 1b, Slot: 0b, id: "computercraft:turtle_normal", tag: {Color: 13388876, ComputerId: 0, display: {Name: '{"text":"Clean turtle"}'}}}], Label: "turtle_test.cleaned_with_cauldrons", On: 1b, Owner: {LowerId: -6876936588741668278L, Name: "Dev", UpperId: 4039158846114182220L}, Slot: 0, id: "computercraft:turtle_normal"}},
|
||||
{pos: [1, 1, 1], state: "minecraft:cauldron{level:3}"},
|
||||
{pos: [1, 1, 1], state: "minecraft:water_cauldron{level:3}"},
|
||||
{pos: [1, 1, 2], state: "minecraft:air"},
|
||||
{pos: [2, 1, 0], state: "minecraft:air"},
|
||||
{pos: [2, 1, 1], state: "minecraft:air"},
|
||||
@ -33,8 +33,8 @@
|
||||
entities: [],
|
||||
palette: [
|
||||
"minecraft:polished_andesite",
|
||||
"computercraft:turtle_normal{facing:south,waterlogged:false}",
|
||||
"minecraft:air",
|
||||
"minecraft:cauldron{level:3}"
|
||||
"minecraft:water_cauldron{level:3}",
|
||||
"computercraft:turtle_normal{facing:south,waterlogged:false}"
|
||||
]
|
||||
}
|
||||
|
1
projects/fabric/src/generated/resources/data/computercraft/tags/blocks/turtle_can_use.json
generated
Normal file
1
projects/fabric/src/generated/resources/data/computercraft/tags/blocks/turtle_can_use.json
generated
Normal file
@ -0,0 +1 @@
|
||||
{"replace": false, "values": ["#minecraft:cauldrons", "#minecraft:beehives"]}
|
@ -77,10 +77,7 @@ import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.*;
|
||||
|
||||
@AutoService(dan200.computercraft.impl.PlatformHelper.class)
|
||||
public class PlatformHelperImpl implements PlatformHelper {
|
||||
@ -303,10 +300,16 @@ public class PlatformHelperImpl implements PlatformHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult useOn(ServerPlayer player, ItemStack stack, BlockHitResult hit) {
|
||||
public InteractionResult useOn(ServerPlayer player, ItemStack stack, BlockHitResult hit, Predicate<BlockState> canUseBlock) {
|
||||
var result = UseBlockCallback.EVENT.invoker().interact(player, player.level, InteractionHand.MAIN_HAND, hit);
|
||||
if (result != InteractionResult.PASS) return result;
|
||||
|
||||
var block = player.level.getBlockState(hit.getBlockPos());
|
||||
if (!block.isAir() && canUseBlock.test(block)) {
|
||||
var useResult = block.use(player.level, player, InteractionHand.MAIN_HAND, hit);
|
||||
if (useResult.consumesAction()) return useResult;
|
||||
}
|
||||
|
||||
return stack.useOn(new UseOnContext(player, InteractionHand.MAIN_HAND, hit));
|
||||
}
|
||||
|
||||
|
1
projects/forge/src/generated/resources/data/computercraft/tags/blocks/turtle_can_use.json
generated
Normal file
1
projects/forge/src/generated/resources/data/computercraft/tags/blocks/turtle_can_use.json
generated
Normal file
@ -0,0 +1 @@
|
||||
{"values": ["#minecraft:cauldrons", "#minecraft:beehives"]}
|
@ -74,10 +74,7 @@ import net.minecraftforge.registries.RegistryObject;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.*;
|
||||
import java.util.function.BiFunction;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.function.Supplier;
|
||||
import java.util.function.*;
|
||||
|
||||
@AutoService(dan200.computercraft.impl.PlatformHelper.class)
|
||||
public class PlatformHelperImpl implements PlatformHelper {
|
||||
@ -318,17 +315,25 @@ public class PlatformHelperImpl implements PlatformHelper {
|
||||
}
|
||||
|
||||
@Override
|
||||
public InteractionResult useOn(ServerPlayer player, ItemStack stack, BlockHitResult hit) {
|
||||
public InteractionResult useOn(ServerPlayer player, ItemStack stack, BlockHitResult hit, Predicate<BlockState> canUseBlock) {
|
||||
var level = player.level;
|
||||
var pos = hit.getBlockPos();
|
||||
var event = ForgeHooks.onRightClickBlock(player, InteractionHand.MAIN_HAND, pos, hit);
|
||||
if (event.isCanceled()) return event.getCancellationResult();
|
||||
|
||||
var context = new UseOnContext(player, InteractionHand.MAIN_HAND, hit);
|
||||
if (event.getUseItem() == Event.Result.DENY) return InteractionResult.PASS;
|
||||
|
||||
if (event.getUseItem() != Event.Result.DENY) {
|
||||
var result = stack.onItemUseFirst(context);
|
||||
return result != InteractionResult.PASS ? result : stack.useOn(context);
|
||||
if (result != InteractionResult.PASS) return result;
|
||||
}
|
||||
|
||||
var block = level.getBlockState(hit.getBlockPos());
|
||||
if (event.getUseBlock() != Event.Result.DENY && !block.isAir() && canUseBlock.test(block)) {
|
||||
var useResult = block.use(level, player, InteractionHand.MAIN_HAND, hit);
|
||||
if (useResult.consumesAction()) return useResult;
|
||||
}
|
||||
|
||||
return event.getUseItem() == Event.Result.DENY ? InteractionResult.PASS : stack.useOn(context);
|
||||
}
|
||||
|
||||
private record RegistryWrapperImpl<T>(
|
||||
|
Loading…
Reference in New Issue
Block a user