|
4 | 4 | import dan200.computercraft.api.lua.LuaException; |
5 | 5 | import dan200.computercraft.api.lua.LuaFunction; |
6 | 6 | import dan200.computercraft.api.lua.MethodResult; |
| 7 | +import dan200.computercraft.api.turtle.ITurtleAccess; |
| 8 | +import dan200.computercraft.api.turtle.TurtleSide; |
7 | 9 | import dan200.computercraft.core.apis.TableHelper; |
| 10 | +import dan200.computercraft.shared.turtle.core.TurtlePlayer; |
| 11 | +import de.srendi.advancedperipherals.common.addons.computercraft.operations.SingleOperationContext; |
8 | 12 | import de.srendi.advancedperipherals.common.addons.computercraft.owner.TurtlePeripheralOwner; |
| 13 | +import de.srendi.advancedperipherals.common.addons.computercraft.peripheral.CompassPeripheral; |
| 14 | +import de.srendi.advancedperipherals.common.configuration.APConfig; |
9 | 15 | import de.srendi.advancedperipherals.common.util.Pair; |
| 16 | +import de.srendi.advancedperipherals.common.util.StringUtil; |
10 | 17 | import de.srendi.advancedperipherals.common.util.fakeplayer.APFakePlayer; |
11 | 18 | import de.srendi.advancedperipherals.lib.peripherals.AutomataCorePeripheral; |
12 | 19 | import de.srendi.advancedperipherals.lib.peripherals.IPeripheralOperation; |
| 20 | +import net.minecraft.core.BlockPos; |
| 21 | +import net.minecraft.core.Direction; |
| 22 | +import net.minecraft.network.chat.Component; |
| 23 | +import net.minecraft.world.InteractionHand; |
13 | 24 | import net.minecraft.world.InteractionResult; |
| 25 | +import net.minecraft.world.item.BlockItem; |
| 26 | +import net.minecraft.world.item.Item; |
14 | 27 | import net.minecraft.world.item.ItemStack; |
| 28 | +import net.minecraft.world.item.SignItem; |
| 29 | +import net.minecraft.world.item.context.DirectionalPlaceContext; |
| 30 | +import net.minecraft.world.level.block.Block; |
| 31 | +import net.minecraft.world.level.block.entity.BlockEntity; |
| 32 | +import net.minecraft.world.level.block.entity.SignBlockEntity; |
| 33 | +import net.minecraft.world.level.Level; |
| 34 | +import net.minecraft.world.phys.BlockHitResult; |
| 35 | +import net.minecraft.world.phys.Vec3; |
| 36 | +import net.minecraftforge.common.ForgeHooks; |
| 37 | +import net.minecraftforge.event.entity.player.PlayerInteractEvent; |
15 | 38 |
|
16 | 39 | import org.jetbrains.annotations.NotNull; |
17 | 40 | import org.jetbrains.annotations.Nullable; |
18 | 41 | import java.util.Collections; |
19 | 42 | import java.util.Map; |
| 43 | +import java.util.stream.Stream; |
20 | 44 |
|
21 | 45 | import static de.srendi.advancedperipherals.common.addons.computercraft.operations.SingleOperation.DIG; |
22 | 46 | import static de.srendi.advancedperipherals.common.addons.computercraft.operations.SingleOperation.USE_ON_BLOCK; |
| 47 | +import static de.srendi.advancedperipherals.common.addons.computercraft.operations.SingleOperation.ACCURE_PLACE; |
23 | 48 |
|
24 | 49 | public class AutomataBlockHandPlugin extends AutomataCorePlugin { |
25 | 50 |
|
@@ -71,4 +96,139 @@ public final MethodResult useOnBlock(@NotNull IArguments arguments) throws LuaEx |
71 | 96 | }); |
72 | 97 | } |
73 | 98 |
|
| 99 | + /** |
| 100 | + * placeBlock method will let turtle place a block with more details when compass has equipped. |
| 101 | + * It should not able to place fluids / use any item, because compass do not recognize them. |
| 102 | + * |
| 103 | + * @param options A table contains how to place the block: |
| 104 | + * x: the x offset relative to the turtle. Default 0 |
| 105 | + * y: the y offset relative to the turtle. Default 0 |
| 106 | + * z: the z offset relative to the turtle. Default 0 |
| 107 | + * anchor: the direction the block is going to hanging on. Default is the direction of the turtle |
| 108 | + * front: the direction the block is going to facing. Default is same as anchor |
| 109 | + * top: the direction the block's top is going to facing. Default is TOP |
| 110 | + * text: the text going to write on the sign. Default is null |
| 111 | + */ |
| 112 | + @LuaFunction(mainThread = true) |
| 113 | + public MethodResult placeBlock(@NotNull Map<?, ?> options) throws LuaException { |
| 114 | + ITurtleAccess turtle = automataCore.getPeripheralOwner().getTurtle(); |
| 115 | + CompassPeripheral compassPeripheral = Stream.of(TurtleSide.values()).map(side -> turtle.getPeripheral(side) instanceof CompassPeripheral compass ? compass : null).filter(peripheral -> peripheral != null).findFirst().orElse(null); |
| 116 | + if (compassPeripheral == null || !compassPeripheral.isEnabled()) { |
| 117 | + return MethodResult.of(false, "COMPASS_NOT_EQUIPPED"); |
| 118 | + } |
| 119 | + int x = TableHelper.optIntField(options, "x", 0); |
| 120 | + int y = TableHelper.optIntField(options, "y", 0); |
| 121 | + int z = TableHelper.optIntField(options, "z", 0); |
| 122 | + final int maxDist = APConfig.PERIPHERALS_CONFIG.compassAccurePlaceRadius.get(); |
| 123 | + final int freeDist = APConfig.PERIPHERALS_CONFIG.compassAccurePlaceFreeRadius.get(); |
| 124 | + if (Math.abs(x) > maxDist || Math.abs(y) > maxDist || Math.abs(z) > maxDist) { |
| 125 | + return MethodResult.of(null, "OUT_OF_RANGE"); |
| 126 | + } |
| 127 | + String anchor = TableHelper.optStringField(options, "anchor", null); |
| 128 | + String front = TableHelper.optStringField(options, "front", null); |
| 129 | + String top = TableHelper.optStringField(options, "top", null); |
| 130 | + Direction anchorDir = anchor != null ? automataCore.validateSide(anchor) : null; |
| 131 | + Direction frontDir = front != null ? automataCore.validateSide(front) : null; |
| 132 | + Direction topDir = top != null ? automataCore.validateSide(top) : null; |
| 133 | + |
| 134 | + int distance = |
| 135 | + Math.max(0, Math.abs(x) - freeDist) + |
| 136 | + Math.max(0, Math.abs(y) - freeDist) + |
| 137 | + Math.max(0, Math.abs(z) - freeDist); |
| 138 | + return automataCore.withOperation(ACCURE_PLACE, new SingleOperationContext(1, distance), context -> { |
| 139 | + ItemStack stack = turtle.getInventory().getItem(turtle.getSelectedSlot()); |
| 140 | + if (stack.isEmpty()) { |
| 141 | + return MethodResult.of(null, "EMPTY_SLOT"); |
| 142 | + } |
| 143 | + BlockPos position = turtle.getPosition().offset(x, y, z); |
| 144 | + String err = deployOn(stack, position, anchorDir, frontDir, topDir, options); |
| 145 | + if (err != null) { |
| 146 | + return MethodResult.of(null, err); |
| 147 | + } |
| 148 | + return MethodResult.of(true); |
| 149 | + }, null); |
| 150 | + } |
| 151 | + |
| 152 | + /** |
| 153 | + * @return A nullable string of the error. <code>null</code> means the operation is successful |
| 154 | + */ |
| 155 | + @Nullable |
| 156 | + private String deployOn(ItemStack stack, BlockPos position, Direction anchor, Direction front, Direction top, Map<?, ?> options) throws LuaException { |
| 157 | + ITurtleAccess turtle = automataCore.getPeripheralOwner().getTurtle(); |
| 158 | + Level world = turtle.getLevel(); |
| 159 | + if (anchor == null) { |
| 160 | + anchor = turtle.getDirection(); |
| 161 | + } |
| 162 | + if (front == null) { |
| 163 | + front = anchor; |
| 164 | + } |
| 165 | + if (top == null) { |
| 166 | + top = Direction.UP; |
| 167 | + } |
| 168 | + TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition(turtle, position, front.getOpposite()); |
| 169 | + BlockHitResult hit = BlockHitResult.miss(Vec3.atCenterOf(position), top, position); |
| 170 | + AdvanceDirectionalPlaceContext context = new AdvanceDirectionalPlaceContext(world, position, anchor, front, stack, top); |
| 171 | + PlayerInteractEvent.RightClickBlock event = ForgeHooks.onRightClickBlock(turtlePlayer, InteractionHand.MAIN_HAND, position, hit); |
| 172 | + if (event.isCanceled()) { |
| 173 | + return "EVENT_CANCELED"; |
| 174 | + } |
| 175 | + Item item = stack.getItem(); |
| 176 | + if (!(item instanceof BlockItem)) { |
| 177 | + return "NOT_BLOCK"; |
| 178 | + } |
| 179 | + BlockItem block = (BlockItem) item; |
| 180 | + InteractionResult res = block.place(context); |
| 181 | + if (!res.consumesAction()) { |
| 182 | + return "CANNOT_PLACE"; |
| 183 | + } |
| 184 | + if (block instanceof SignItem) { |
| 185 | + BlockEntity blockEntity = world.getBlockEntity(position); |
| 186 | + if (blockEntity instanceof SignBlockEntity sign) { |
| 187 | + String text = StringUtil.convertAndToSectionMark(TableHelper.optStringField(options, "text", null)); |
| 188 | + setSignText(world, sign, text); |
| 189 | + } |
| 190 | + } |
| 191 | + return null; |
| 192 | + } |
| 193 | + |
| 194 | + private static void setSignText(Level world, SignBlockEntity sign, String text) { |
| 195 | + if (text == null) { |
| 196 | + for (int i = 0; i < SignBlockEntity.LINES; i++) { |
| 197 | + sign.setMessage(i, Component.literal("")); |
| 198 | + } |
| 199 | + } else { |
| 200 | + String[] lines = text.split("\n"); |
| 201 | + for (int i = 0; i < SignBlockEntity.LINES; i++) { |
| 202 | + sign.setMessage(i, Component.literal(i < lines.length ? lines[i] : "")); |
| 203 | + } |
| 204 | + } |
| 205 | + sign.setChanged(); |
| 206 | + world.sendBlockUpdated(sign.getBlockPos(), sign.getBlockState(), sign.getBlockState(), Block.UPDATE_ALL); |
| 207 | + } |
| 208 | + |
| 209 | + private static class AdvanceDirectionalPlaceContext extends DirectionalPlaceContext { |
| 210 | + private final Direction anchor; |
| 211 | + |
| 212 | + AdvanceDirectionalPlaceContext(Level world, BlockPos pos, Direction anchor, Direction front, ItemStack stack, Direction top) { |
| 213 | + super(world, pos, front, stack, top); |
| 214 | + this.anchor = anchor; |
| 215 | + } |
| 216 | + |
| 217 | + @Override |
| 218 | + public Direction getNearestLookingDirection() { |
| 219 | + return this.anchor; |
| 220 | + } |
| 221 | + |
| 222 | + @Override |
| 223 | + public Direction[] getNearestLookingDirections() { |
| 224 | + return switch (this.anchor) { |
| 225 | + case DOWN -> new Direction[]{Direction.DOWN, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.UP}; |
| 226 | + case UP -> new Direction[]{Direction.UP, Direction.NORTH, Direction.EAST, Direction.SOUTH, Direction.WEST, Direction.DOWN}; |
| 227 | + case NORTH -> new Direction[]{Direction.NORTH, Direction.EAST, Direction.WEST, Direction.UP, Direction.DOWN, Direction.SOUTH}; |
| 228 | + case SOUTH -> new Direction[]{Direction.SOUTH, Direction.EAST, Direction.WEST, Direction.UP, Direction.DOWN, Direction.NORTH}; |
| 229 | + case WEST -> new Direction[]{Direction.WEST, Direction.SOUTH, Direction.UP, Direction.NORTH, Direction.DOWN, Direction.EAST}; |
| 230 | + case EAST -> new Direction[]{Direction.EAST, Direction.SOUTH, Direction.UP, Direction.NORTH, Direction.DOWN, Direction.WEST}; |
| 231 | + }; |
| 232 | + } |
| 233 | + } |
74 | 234 | } |
0 commit comments