mirror of
https://github.com/SquidDev-CC/CC-Tweaked
synced 2025-01-06 07:20:30 +00:00
Rewrite turtle placing logic
- Simplify how the turtle's inventory is processed. We now copy all items into the player inventory, attempt to place, and then copy the items back. This also fixes the problem where turtle.place() wouldn't (always) update the item which was placed. I'm also hoping this is more "correct" in how we process drops from entities and whatnot. Though I've not had any reports of issues, so it's probably fine. - Replace the "error message" string array with an actual object. It'd be nicer all these functions returned a TurtleCommandResult, but merging error messages from that is a little harder. Fun facts: the test suite was actually helpful here, and caught the fact that hoeing was broken!
This commit is contained in:
parent
9f7cc00fcb
commit
9142ccfc93
@ -343,7 +343,7 @@ task setupServer(type: Copy) {
|
||||
tasks.register('testInGame', JavaExec.class).configure {
|
||||
it.group('test server')
|
||||
it.description("Runs tests on a temporary Minecraft server.")
|
||||
it.dependsOn(setupServer, 'prepareRunTestServer')
|
||||
it.dependsOn(setupServer, 'prepareRunTestServer', 'cleanTestInGame')
|
||||
|
||||
// Copy from runTestServer. We do it in this slightly odd way as runTestServer
|
||||
// isn't created until the task is configured (which is no good for us).
|
||||
|
@ -62,7 +62,7 @@ public class TurtleDropCommand implements ITurtleCommand
|
||||
IItemHandler inventory = InventoryUtil.getInventory( world, newPosition, side );
|
||||
|
||||
// Fire the event, restoring the inventory and exiting if it is cancelled.
|
||||
TurtlePlayer player = TurtlePlaceCommand.createPlayer( turtle, oldPosition, direction );
|
||||
TurtlePlayer player = TurtlePlayer.getWithPosition( turtle, oldPosition, direction );
|
||||
TurtleInventoryEvent.Drop event = new TurtleInventoryEvent.Drop( turtle, player, world, newPosition, inventory, stack );
|
||||
if( MinecraftForge.EVENT_BUS.post( event ) )
|
||||
{
|
||||
|
@ -50,7 +50,7 @@ public class TurtleInspectCommand implements ITurtleCommand
|
||||
Map<String, Object> table = BlockData.fill( new HashMap<>(), state );
|
||||
|
||||
// Fire the event, exiting if it is cancelled
|
||||
TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, oldPosition, direction );
|
||||
TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, oldPosition, direction );
|
||||
TurtleBlockEvent.Inspect event = new TurtleBlockEvent.Inspect( turtle, turtlePlayer, world, newPosition, state, table );
|
||||
if( MinecraftForge.EVENT_BUS.post( event ) ) return TurtleCommandResult.failure( event.getFailureMessage() );
|
||||
|
||||
|
@ -47,7 +47,7 @@ public class TurtleMoveCommand implements ITurtleCommand
|
||||
BlockPos oldPosition = turtle.getPosition();
|
||||
BlockPos newPosition = oldPosition.relative( direction );
|
||||
|
||||
TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, oldPosition, direction );
|
||||
TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, oldPosition, direction );
|
||||
TurtleCommandResult canEnterResult = canEnter( turtlePlayer, oldWorld, newPosition );
|
||||
if( !canEnterResult.isSuccess() )
|
||||
{
|
||||
|
@ -12,7 +12,6 @@ import dan200.computercraft.api.turtle.TurtleAnimation;
|
||||
import dan200.computercraft.api.turtle.TurtleCommandResult;
|
||||
import dan200.computercraft.api.turtle.event.TurtleBlockEvent;
|
||||
import dan200.computercraft.shared.TurtlePermissions;
|
||||
import dan200.computercraft.shared.util.DirectionUtil;
|
||||
import dan200.computercraft.shared.util.DropConsumer;
|
||||
import dan200.computercraft.shared.util.InventoryUtil;
|
||||
import dan200.computercraft.shared.util.WorldUtil;
|
||||
@ -20,6 +19,7 @@ import net.minecraft.block.BlockState;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.item.*;
|
||||
import net.minecraft.network.play.client.CUseEntityPacket;
|
||||
import net.minecraft.tileentity.SignTileEntity;
|
||||
import net.minecraft.tileentity.TileEntity;
|
||||
import net.minecraft.util.ActionResult;
|
||||
@ -34,11 +34,13 @@ import net.minecraft.world.World;
|
||||
import net.minecraftforge.common.ForgeHooks;
|
||||
import net.minecraftforge.common.MinecraftForge;
|
||||
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
|
||||
import net.minecraftforge.eventbus.api.Event;
|
||||
import net.minecraftforge.items.IItemHandler;
|
||||
import net.minecraftforge.items.wrapper.InvWrapper;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
|
||||
import static net.minecraftforge.eventbus.api.Event.Result;
|
||||
|
||||
public class TurtlePlaceCommand implements ITurtleCommand
|
||||
{
|
||||
@ -57,10 +59,7 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
{
|
||||
// Get thing to place
|
||||
ItemStack stack = turtle.getInventory().getItem( turtle.getSelectedSlot() );
|
||||
if( stack.isEmpty() )
|
||||
{
|
||||
return TurtleCommandResult.failure( "No items to place" );
|
||||
}
|
||||
if( stack.isEmpty() ) return TurtleCommandResult.failure( "No items to place" );
|
||||
|
||||
// Remember old block
|
||||
Direction direction = this.direction.toWorldDir( turtle );
|
||||
@ -68,144 +67,63 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
|
||||
// Create a fake player, and orient it appropriately
|
||||
BlockPos playerPosition = turtle.getPosition().relative( direction );
|
||||
TurtlePlayer turtlePlayer = createPlayer( turtle, playerPosition, direction );
|
||||
TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, playerPosition, direction );
|
||||
|
||||
TurtleBlockEvent.Place place = new TurtleBlockEvent.Place( turtle, turtlePlayer, turtle.getWorld(), coordinates, stack );
|
||||
if( MinecraftForge.EVENT_BUS.post( place ) )
|
||||
{
|
||||
return TurtleCommandResult.failure( place.getFailureMessage() );
|
||||
}
|
||||
if( MinecraftForge.EVENT_BUS.post( place ) ) return TurtleCommandResult.failure( place.getFailureMessage() );
|
||||
|
||||
// Do the deploying
|
||||
String[] errorMessage = new String[1];
|
||||
ItemStack remainder = deploy( stack, turtle, turtlePlayer, direction, extraArguments, errorMessage );
|
||||
if( remainder != stack )
|
||||
turtlePlayer.loadInventory( turtle );
|
||||
ErrorMessage message = new ErrorMessage();
|
||||
boolean result = deploy( stack, turtle, turtlePlayer, direction, extraArguments, message );
|
||||
turtlePlayer.unloadInventory( turtle );
|
||||
if( result )
|
||||
{
|
||||
// Put the remaining items back
|
||||
turtle.getInventory().setItem( turtle.getSelectedSlot(), remainder );
|
||||
turtle.getInventory().setChanged();
|
||||
|
||||
// Animate and return success
|
||||
turtle.playAnimation( TurtleAnimation.WAIT );
|
||||
return TurtleCommandResult.success();
|
||||
}
|
||||
else if( message.message != null )
|
||||
{
|
||||
return TurtleCommandResult.failure( message.message );
|
||||
}
|
||||
else
|
||||
{
|
||||
if( errorMessage[0] != null )
|
||||
{
|
||||
return TurtleCommandResult.failure( errorMessage[0] );
|
||||
}
|
||||
else if( stack.getItem() instanceof BlockItem )
|
||||
{
|
||||
return TurtleCommandResult.failure( "Cannot place block here" );
|
||||
}
|
||||
else
|
||||
{
|
||||
return TurtleCommandResult.failure( "Cannot place item here" );
|
||||
}
|
||||
return TurtleCommandResult.failure( stack.getItem() instanceof BlockItem ? "Cannot place block here" : "Cannot place item here" );
|
||||
}
|
||||
}
|
||||
|
||||
public static ItemStack deploy( @Nonnull ItemStack stack, ITurtleAccess turtle, Direction direction, Object[] extraArguments, String[] outErrorMessage )
|
||||
public static boolean deployCopiedItem( @Nonnull ItemStack stack, ITurtleAccess turtle, Direction direction, Object[] extraArguments, ErrorMessage outErrorMessage )
|
||||
{
|
||||
// Create a fake player, and orient it appropriately
|
||||
BlockPos playerPosition = turtle.getPosition().relative( direction );
|
||||
TurtlePlayer turtlePlayer = createPlayer( turtle, playerPosition, direction );
|
||||
|
||||
return deploy( stack, turtle, turtlePlayer, direction, extraArguments, outErrorMessage );
|
||||
TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, playerPosition, direction );
|
||||
turtlePlayer.loadInventory( stack );
|
||||
boolean result = deploy( stack, turtle, turtlePlayer, direction, extraArguments, outErrorMessage );
|
||||
turtlePlayer.inventory.clearContent();
|
||||
return result;
|
||||
}
|
||||
|
||||
public static ItemStack deploy( @Nonnull ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, Direction direction, Object[] extraArguments, String[] outErrorMessage )
|
||||
private static boolean deploy( @Nonnull ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, Direction direction, Object[] extraArguments, ErrorMessage outErrorMessage )
|
||||
{
|
||||
// Deploy on an entity
|
||||
ItemStack remainder = deployOnEntity( stack, turtle, turtlePlayer, direction, extraArguments, outErrorMessage );
|
||||
if( remainder != stack )
|
||||
{
|
||||
return remainder;
|
||||
}
|
||||
if( deployOnEntity( stack, turtle, turtlePlayer ) ) return true;
|
||||
|
||||
// Deploy on the block immediately in front
|
||||
BlockPos position = turtle.getPosition();
|
||||
BlockPos newPosition = position.relative( direction );
|
||||
remainder = deployOnBlock( stack, turtle, turtlePlayer, newPosition, direction.getOpposite(), extraArguments, true, outErrorMessage );
|
||||
if( remainder != stack )
|
||||
{
|
||||
return remainder;
|
||||
}
|
||||
|
||||
// Deploy on the block one block away
|
||||
remainder = deployOnBlock( stack, turtle, turtlePlayer, newPosition.relative( direction ), direction.getOpposite(), extraArguments, false, outErrorMessage );
|
||||
if( remainder != stack )
|
||||
{
|
||||
return remainder;
|
||||
}
|
||||
|
||||
if( direction.getAxis() != Direction.Axis.Y )
|
||||
{
|
||||
// Try to deploy against a block. Tries the following options:
|
||||
// Deploy on the block immediately in front
|
||||
return deployOnBlock( stack, turtle, turtlePlayer, newPosition, direction.getOpposite(), extraArguments, true, outErrorMessage )
|
||||
// Deploy on the block one block away
|
||||
|| deployOnBlock( stack, turtle, turtlePlayer, newPosition.relative( direction ), direction.getOpposite(), extraArguments, false, outErrorMessage )
|
||||
// Deploy down on the block in front
|
||||
remainder = deployOnBlock( stack, turtle, turtlePlayer, newPosition.below(), Direction.UP, extraArguments, false, outErrorMessage );
|
||||
if( remainder != stack )
|
||||
{
|
||||
return remainder;
|
||||
}
|
||||
}
|
||||
|
||||
// Deploy back onto the turtle
|
||||
remainder = deployOnBlock( stack, turtle, turtlePlayer, position, direction, extraArguments, false, outErrorMessage );
|
||||
if( remainder != stack )
|
||||
{
|
||||
return remainder;
|
||||
}
|
||||
|
||||
// If nothing worked, return the original stack unchanged
|
||||
return stack;
|
||||
|| (direction.getAxis() != Direction.Axis.Y && deployOnBlock( stack, turtle, turtlePlayer, newPosition.below(), Direction.UP, extraArguments, false, outErrorMessage ))
|
||||
// Deploy back onto the turtle
|
||||
|| deployOnBlock( stack, turtle, turtlePlayer, position, direction, extraArguments, false, outErrorMessage );
|
||||
}
|
||||
|
||||
public static TurtlePlayer createPlayer( ITurtleAccess turtle, BlockPos position, Direction direction )
|
||||
{
|
||||
TurtlePlayer turtlePlayer = TurtlePlayer.get( turtle );
|
||||
orientPlayer( turtle, turtlePlayer, position, direction );
|
||||
return turtlePlayer;
|
||||
}
|
||||
|
||||
private static void orientPlayer( ITurtleAccess turtle, TurtlePlayer turtlePlayer, BlockPos position, Direction direction )
|
||||
{
|
||||
double posX = position.getX() + 0.5;
|
||||
double posY = position.getY() + 0.5;
|
||||
double posZ = position.getZ() + 0.5;
|
||||
|
||||
// Stop intersection with the turtle itself
|
||||
if( turtle.getPosition().equals( position ) )
|
||||
{
|
||||
posX += 0.48 * direction.getStepX();
|
||||
posY += 0.48 * direction.getStepY();
|
||||
posZ += 0.48 * direction.getStepZ();
|
||||
}
|
||||
|
||||
if( direction.getAxis() != Direction.Axis.Y )
|
||||
{
|
||||
turtlePlayer.yRot = direction.toYRot();
|
||||
turtlePlayer.xRot = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
turtlePlayer.yRot = turtle.getDirection().toYRot();
|
||||
turtlePlayer.xRot = DirectionUtil.toPitchAngle( direction );
|
||||
}
|
||||
|
||||
turtlePlayer.setPosRaw( posX, posY, posZ );
|
||||
turtlePlayer.xo = posX;
|
||||
turtlePlayer.yo = posY;
|
||||
turtlePlayer.zo = posZ;
|
||||
turtlePlayer.xRotO = turtlePlayer.xRot;
|
||||
turtlePlayer.yRotO = turtlePlayer.yRot;
|
||||
|
||||
turtlePlayer.yHeadRot = turtlePlayer.yRot;
|
||||
turtlePlayer.yHeadRotO = turtlePlayer.yHeadRot;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static ItemStack deployOnEntity( @Nonnull ItemStack stack, final ITurtleAccess turtle, TurtlePlayer turtlePlayer, Direction direction, Object[] extraArguments, String[] outErrorMessage )
|
||||
private static boolean deployOnEntity( @Nonnull ItemStack stack, final ITurtleAccess turtle, TurtlePlayer turtlePlayer )
|
||||
{
|
||||
// See if there is an entity present
|
||||
final World world = turtle.getWorld();
|
||||
@ -213,81 +131,57 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
Vec3d turtlePos = turtlePlayer.position();
|
||||
Vec3d rayDir = turtlePlayer.getViewVector( 1.0f );
|
||||
Pair<Entity, Vec3d> hit = WorldUtil.rayTraceEntities( world, turtlePos, rayDir, 1.5 );
|
||||
if( hit == null )
|
||||
{
|
||||
return stack;
|
||||
}
|
||||
|
||||
// Load up the turtle's inventory
|
||||
ItemStack stackCopy = stack.copy();
|
||||
turtlePlayer.loadInventory( stackCopy );
|
||||
if( hit == null ) return false;
|
||||
|
||||
// Start claiming entity drops
|
||||
Entity hitEntity = hit.getKey();
|
||||
Vec3d hitPos = hit.getValue();
|
||||
DropConsumer.set(
|
||||
hitEntity,
|
||||
drop -> InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() )
|
||||
);
|
||||
|
||||
// Place on the entity
|
||||
boolean placed = false;
|
||||
ActionResultType cancelResult = ForgeHooks.onInteractEntityAt( turtlePlayer, hitEntity, hitPos, Hand.MAIN_HAND );
|
||||
if( cancelResult == null )
|
||||
{
|
||||
cancelResult = hitEntity.interactAt( turtlePlayer, hitPos, Hand.MAIN_HAND );
|
||||
}
|
||||
IItemHandler itemHandler = new InvWrapper( turtlePlayer.inventory );
|
||||
DropConsumer.set( hitEntity, drop -> InventoryUtil.storeItems( drop, itemHandler, 1 ) );
|
||||
|
||||
if( cancelResult == ActionResultType.SUCCESS )
|
||||
{
|
||||
placed = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// See EntityPlayer.interactOn
|
||||
cancelResult = ForgeHooks.onInteractEntity( turtlePlayer, hitEntity, Hand.MAIN_HAND );
|
||||
if( cancelResult == ActionResultType.SUCCESS )
|
||||
{
|
||||
placed = true;
|
||||
}
|
||||
else if( cancelResult == null )
|
||||
{
|
||||
if( hitEntity.interact( turtlePlayer, Hand.MAIN_HAND ) )
|
||||
{
|
||||
placed = true;
|
||||
}
|
||||
else if( hitEntity instanceof LivingEntity )
|
||||
{
|
||||
placed = stackCopy.interactEnemy( turtlePlayer, (LivingEntity) hitEntity, Hand.MAIN_HAND );
|
||||
if( placed ) turtlePlayer.loadInventory( stackCopy );
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean placed = doDeployOnEntity( stack, turtlePlayer, hitEntity, hitPos );
|
||||
|
||||
// Stop claiming drops
|
||||
List<ItemStack> remainingDrops = DropConsumer.clear();
|
||||
for( ItemStack remaining : remainingDrops )
|
||||
{
|
||||
WorldUtil.dropItemStack( remaining, world, position, turtle.getDirection().getOpposite() );
|
||||
}
|
||||
|
||||
// Put everything we collected into the turtles inventory, then return
|
||||
ItemStack remainder = turtlePlayer.unloadInventory( turtle );
|
||||
if( !placed && ItemStack.matches( stack, remainder ) )
|
||||
{
|
||||
return stack;
|
||||
}
|
||||
else if( !remainder.isEmpty() )
|
||||
{
|
||||
return remainder;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ItemStack.EMPTY;
|
||||
}
|
||||
DropConsumer.clearAndDrop( world, position, turtle.getDirection().getOpposite() );
|
||||
return placed;
|
||||
}
|
||||
|
||||
private static boolean canDeployOnBlock( @Nonnull BlockItemUseContext context, ITurtleAccess turtle, TurtlePlayer player, BlockPos position, Direction side, boolean allowReplaceable, String[] outErrorMessage )
|
||||
/**
|
||||
* Place a block onto an entity. For instance, feeding cows.
|
||||
*
|
||||
* @param stack The stack we're placing.
|
||||
* @param turtlePlayer The player of the turtle we're placing.
|
||||
* @param hitEntity The entity we're interacting with.
|
||||
* @param hitPos The position our ray trace hit the entity.
|
||||
* @return If this item was deployed.
|
||||
* @see net.minecraft.network.play.ServerPlayNetHandler#handleInteract(CUseEntityPacket)
|
||||
* @see net.minecraft.entity.player.PlayerEntity#interactOn(Entity, Hand)
|
||||
*/
|
||||
private static boolean doDeployOnEntity( @Nonnull ItemStack stack, TurtlePlayer turtlePlayer, @Nonnull Entity hitEntity, @Nonnull Vec3d hitPos )
|
||||
{
|
||||
// Placing "onto" a block follows two flows. First we try to interactAt. If that doesn't succeed, then we try to
|
||||
// call the normal interact path. Cancelling an interactAt *does not* cancel a normal interact path.
|
||||
|
||||
ActionResultType interactAt = ForgeHooks.onInteractEntityAt( turtlePlayer, hitEntity, hitPos, Hand.MAIN_HAND );
|
||||
if( interactAt == null ) interactAt = hitEntity.interactAt( turtlePlayer, hitPos, Hand.MAIN_HAND );
|
||||
if( interactAt.consumesAction() ) return true;
|
||||
|
||||
ActionResultType interact = ForgeHooks.onInteractEntity( turtlePlayer, hitEntity, Hand.MAIN_HAND );
|
||||
if( interact != null ) return interact.consumesAction();
|
||||
|
||||
if( hitEntity.interact( turtlePlayer, Hand.MAIN_HAND ) ) return true;
|
||||
if( hitEntity instanceof LivingEntity )
|
||||
{
|
||||
return stack.interactEnemy( turtlePlayer, (LivingEntity) hitEntity, Hand.MAIN_HAND );
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
private static boolean canDeployOnBlock(
|
||||
@Nonnull BlockItemUseContext context, ITurtleAccess turtle, TurtlePlayer player, BlockPos position,
|
||||
Direction side, boolean allowReplaceable, ErrorMessage outErrorMessage
|
||||
)
|
||||
{
|
||||
World world = turtle.getWorld();
|
||||
if( !World.isInWorldBounds( position ) || world.isEmptyBlock( position ) ||
|
||||
@ -309,7 +203,7 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
: TurtlePermissions.isBlockEditable( world, position.relative( side ), player );
|
||||
if( !editable )
|
||||
{
|
||||
if( outErrorMessage != null ) outErrorMessage[0] = "Cannot place in protected area";
|
||||
if( outErrorMessage != null ) outErrorMessage.message = "Cannot place in protected area";
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -317,131 +211,123 @@ public class TurtlePlaceCommand implements ITurtleCommand
|
||||
return true;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
private static ItemStack deployOnBlock( @Nonnull ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, BlockPos position, Direction side, Object[] extraArguments, boolean allowReplace, String[] outErrorMessage )
|
||||
private static boolean deployOnBlock(
|
||||
@Nonnull ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, BlockPos position, Direction side,
|
||||
Object[] extraArguments, boolean allowReplace, ErrorMessage outErrorMessage
|
||||
)
|
||||
{
|
||||
// Re-orient the fake player
|
||||
Direction playerDir = side.getOpposite();
|
||||
BlockPos playerPosition = position.relative( side );
|
||||
orientPlayer( turtle, turtlePlayer, playerPosition, playerDir );
|
||||
|
||||
ItemStack stackCopy = stack.copy();
|
||||
turtlePlayer.loadInventory( stackCopy );
|
||||
turtlePlayer.setPosition( turtle, playerPosition, playerDir );
|
||||
|
||||
// Calculate where the turtle would hit the block
|
||||
float hitX = 0.5f + side.getStepX() * 0.5f;
|
||||
float hitY = 0.5f + side.getStepY() * 0.5f;
|
||||
float hitZ = 0.5f + side.getStepZ() * 0.5f;
|
||||
if( Math.abs( hitY - 0.5f ) < 0.01f )
|
||||
{
|
||||
hitY = 0.45f;
|
||||
}
|
||||
if( Math.abs( hitY - 0.5f ) < 0.01f ) hitY = 0.45f;
|
||||
|
||||
// Check if there's something suitable to place onto
|
||||
BlockRayTraceResult hit = new BlockRayTraceResult( new Vec3d( hitX, hitY, hitZ ), side, position, false );
|
||||
ItemUseContext context = new ItemUseContext( turtlePlayer, Hand.MAIN_HAND, hit );
|
||||
if( !canDeployOnBlock( new BlockItemUseContext( context ), turtle, turtlePlayer, position, side, allowReplace, outErrorMessage ) )
|
||||
{
|
||||
return stack;
|
||||
return false;
|
||||
}
|
||||
|
||||
// Load up the turtle's inventory
|
||||
Item item = stack.getItem();
|
||||
|
||||
// Do the deploying (put everything in the players inventory)
|
||||
boolean placed = false;
|
||||
TileEntity existingTile = turtle.getWorld().getBlockEntity( position );
|
||||
|
||||
// See PlayerInteractionManager.processRightClickBlock
|
||||
// TODO: ^ Check we're still consistent.
|
||||
PlayerInteractEvent.RightClickBlock event = ForgeHooks.onRightClickBlock( turtlePlayer, Hand.MAIN_HAND, position, side );
|
||||
if( !event.isCanceled() )
|
||||
{
|
||||
if( item.onItemUseFirst( stack, context ) == ActionResultType.SUCCESS )
|
||||
{
|
||||
placed = true;
|
||||
turtlePlayer.loadInventory( stackCopy );
|
||||
}
|
||||
else if( event.getUseItem() != Event.Result.DENY &&
|
||||
stackCopy.useOn( context ) == ActionResultType.SUCCESS )
|
||||
{
|
||||
placed = true;
|
||||
turtlePlayer.loadInventory( stackCopy );
|
||||
}
|
||||
}
|
||||
|
||||
if( !placed && (item instanceof BucketItem || item instanceof BoatItem || item instanceof LilyPadItem || item instanceof GlassBottleItem) )
|
||||
{
|
||||
ActionResultType actionResult = ForgeHooks.onItemRightClick( turtlePlayer, Hand.MAIN_HAND );
|
||||
if( actionResult == ActionResultType.SUCCESS )
|
||||
{
|
||||
placed = true;
|
||||
}
|
||||
else if( actionResult == null )
|
||||
{
|
||||
ActionResult<ItemStack> result = stackCopy.use( turtle.getWorld(), turtlePlayer, Hand.MAIN_HAND );
|
||||
if( result.getResult() == ActionResultType.SUCCESS && !ItemStack.matches( stack, result.getObject() ) )
|
||||
{
|
||||
placed = true;
|
||||
turtlePlayer.loadInventory( result.getObject() );
|
||||
}
|
||||
}
|
||||
}
|
||||
boolean placed = doDeployOnBlock( stack, turtlePlayer, position, side, context ).consumesAction();
|
||||
|
||||
// Set text on signs
|
||||
if( placed && item instanceof SignItem )
|
||||
if( placed && item instanceof SignItem && extraArguments != null && extraArguments.length >= 1 && extraArguments[0] instanceof String )
|
||||
{
|
||||
if( extraArguments != null && extraArguments.length >= 1 && extraArguments[0] instanceof String )
|
||||
World world = turtle.getWorld();
|
||||
TileEntity tile = world.getBlockEntity( position );
|
||||
if( tile == null || tile == existingTile )
|
||||
{
|
||||
World world = turtle.getWorld();
|
||||
TileEntity tile = world.getBlockEntity( position );
|
||||
if( tile == null || tile == existingTile )
|
||||
{
|
||||
tile = world.getBlockEntity( position.relative( side ) );
|
||||
}
|
||||
if( tile instanceof SignTileEntity )
|
||||
{
|
||||
SignTileEntity signTile = (SignTileEntity) tile;
|
||||
String s = (String) extraArguments[0];
|
||||
String[] split = s.split( "\n" );
|
||||
int firstLine = split.length <= 2 ? 1 : 0;
|
||||
for( int i = 0; i < signTile.messages.length; i++ )
|
||||
{
|
||||
if( i >= firstLine && i < firstLine + split.length )
|
||||
{
|
||||
if( split[i - firstLine].length() > 15 )
|
||||
{
|
||||
signTile.messages[i] = new StringTextComponent( split[i - firstLine].substring( 0, 15 ) );
|
||||
}
|
||||
else
|
||||
{
|
||||
signTile.messages[i] = new StringTextComponent( split[i - firstLine] );
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
signTile.messages[i] = new StringTextComponent( "" );
|
||||
}
|
||||
}
|
||||
signTile.setChanged();
|
||||
world.sendBlockUpdated( tile.getBlockPos(), tile.getBlockState(), tile.getBlockState(), 3 );
|
||||
}
|
||||
tile = world.getBlockEntity( position.relative( side ) );
|
||||
}
|
||||
|
||||
if( tile instanceof SignTileEntity ) setSignText( world, tile, (String) extraArguments[0] );
|
||||
}
|
||||
|
||||
return placed;
|
||||
}
|
||||
|
||||
/**
|
||||
* Attempt to place an item into the world. Returns true/false if an item was placed.
|
||||
*
|
||||
* @param stack The stack the player is using.
|
||||
* @param turtlePlayer The player which represents the turtle
|
||||
* @param position The block we're deploying against's position.
|
||||
* @param side The side of the block we're deploying against.
|
||||
* @param context The context of this place action.
|
||||
* @return If this item was deployed.
|
||||
* @see net.minecraft.server.management.PlayerInteractionManager#useItemOn For the original implementation.
|
||||
*/
|
||||
private static ActionResultType doDeployOnBlock(
|
||||
@Nonnull ItemStack stack, TurtlePlayer turtlePlayer, BlockPos position, Direction side, ItemUseContext context
|
||||
)
|
||||
{
|
||||
PlayerInteractEvent.RightClickBlock event = ForgeHooks.onRightClickBlock( turtlePlayer, Hand.MAIN_HAND, position, side );
|
||||
if( event.isCanceled() ) return event.getCancellationResult();
|
||||
|
||||
if( event.getUseItem() != Result.DENY )
|
||||
{
|
||||
ActionResultType result = stack.onItemUseFirst( context );
|
||||
if( result != ActionResultType.PASS ) return result;
|
||||
}
|
||||
|
||||
if( event.getUseItem() != Result.DENY )
|
||||
{
|
||||
ActionResultType result = stack.useOn( context );
|
||||
if( result != ActionResultType.PASS ) return result;
|
||||
}
|
||||
|
||||
Item item = stack.getItem();
|
||||
if( item instanceof BucketItem || item instanceof BoatItem || item instanceof LilyPadItem || item instanceof GlassBottleItem )
|
||||
{
|
||||
ActionResultType actionResult = ForgeHooks.onItemRightClick( turtlePlayer, Hand.MAIN_HAND );
|
||||
if( actionResult != null && actionResult != ActionResultType.PASS ) return actionResult;
|
||||
|
||||
ActionResult<ItemStack> result = stack.use( context.getLevel(), turtlePlayer, Hand.MAIN_HAND );
|
||||
if( result.getResult().consumesAction() && !ItemStack.matches( stack, result.getObject() ) )
|
||||
{
|
||||
turtlePlayer.setItemInHand( Hand.MAIN_HAND, result.getObject() );
|
||||
return result.getResult();
|
||||
}
|
||||
}
|
||||
|
||||
// Put everything we collected into the turtles inventory, then return
|
||||
ItemStack remainder = turtlePlayer.unloadInventory( turtle );
|
||||
if( !placed && ItemStack.matches( stack, remainder ) )
|
||||
return ActionResultType.PASS;
|
||||
}
|
||||
|
||||
private static void setSignText( World world, TileEntity tile, String message )
|
||||
{
|
||||
SignTileEntity signTile = (SignTileEntity) tile;
|
||||
String[] split = message.split( "\n" );
|
||||
int firstLine = split.length <= 2 ? 1 : 0;
|
||||
for( int i = 0; i < signTile.messages.length; i++ )
|
||||
{
|
||||
return stack;
|
||||
}
|
||||
else if( !remainder.isEmpty() )
|
||||
{
|
||||
return remainder;
|
||||
}
|
||||
else
|
||||
{
|
||||
return ItemStack.EMPTY;
|
||||
if( i >= firstLine && i < firstLine + split.length )
|
||||
{
|
||||
String line = split[i - firstLine];
|
||||
signTile.messages[i] = line.length() > 15
|
||||
? new StringTextComponent( line.substring( 0, 15 ) )
|
||||
: new StringTextComponent( line );
|
||||
}
|
||||
else
|
||||
{
|
||||
signTile.messages[i] = new StringTextComponent( "" );
|
||||
}
|
||||
}
|
||||
signTile.setChanged();
|
||||
world.sendBlockUpdated( tile.getBlockPos(), tile.getBlockState(), tile.getBlockState(), 3 );
|
||||
}
|
||||
|
||||
private static class ErrorMessage
|
||||
{
|
||||
String message;
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import com.mojang.authlib.GameProfile;
|
||||
import dan200.computercraft.ComputerCraft;
|
||||
import dan200.computercraft.api.turtle.ITurtleAccess;
|
||||
import dan200.computercraft.shared.Registry;
|
||||
import dan200.computercraft.shared.util.DirectionUtil;
|
||||
import dan200.computercraft.shared.util.FakeNetHandler;
|
||||
import dan200.computercraft.shared.util.InventoryUtil;
|
||||
import dan200.computercraft.shared.util.WorldUtil;
|
||||
@ -73,23 +74,6 @@ public final class TurtlePlayer extends FakePlayer
|
||||
return profile != null && profile.isComplete() ? profile : DEFAULT_PROFILE;
|
||||
}
|
||||
|
||||
private void setState( ITurtleAccess turtle )
|
||||
{
|
||||
if( containerMenu != inventoryMenu )
|
||||
{
|
||||
ComputerCraft.log.warn( "Turtle has open container ({})", containerMenu );
|
||||
doCloseContainer();
|
||||
}
|
||||
|
||||
BlockPos position = turtle.getPosition();
|
||||
setPosRaw( position.getX() + 0.5, position.getY() + 0.5, position.getZ() + 0.5 );
|
||||
|
||||
yRot = turtle.getDirection().toYRot();
|
||||
xRot = 0.0f;
|
||||
|
||||
inventory.clearContent();
|
||||
}
|
||||
|
||||
public static TurtlePlayer get( ITurtleAccess access )
|
||||
{
|
||||
if( !(access instanceof TurtleBrain) ) return create( access );
|
||||
@ -109,37 +93,114 @@ public final class TurtlePlayer extends FakePlayer
|
||||
return player;
|
||||
}
|
||||
|
||||
public void loadInventory( @Nonnull ItemStack currentStack )
|
||||
public static TurtlePlayer getWithPosition( ITurtleAccess turtle, BlockPos position, Direction direction )
|
||||
{
|
||||
// Load up the fake inventory
|
||||
inventory.selected = 0;
|
||||
inventory.setItem( 0, currentStack );
|
||||
TurtlePlayer turtlePlayer = get( turtle );
|
||||
turtlePlayer.setPosition( turtle, position, direction );
|
||||
return turtlePlayer;
|
||||
}
|
||||
|
||||
public ItemStack unloadInventory( ITurtleAccess turtle )
|
||||
private void setState( ITurtleAccess turtle )
|
||||
{
|
||||
// Get the item we placed with
|
||||
ItemStack results = inventory.getItem( 0 );
|
||||
inventory.setItem( 0, ItemStack.EMPTY );
|
||||
if( containerMenu != inventoryMenu )
|
||||
{
|
||||
ComputerCraft.log.warn( "Turtle has open container ({})", containerMenu );
|
||||
doCloseContainer();
|
||||
}
|
||||
|
||||
BlockPos position = turtle.getPosition();
|
||||
setPosRaw( position.getX() + 0.5, position.getY() + 0.5, position.getZ() + 0.5 );
|
||||
|
||||
yRot = turtle.getDirection().toYRot();
|
||||
xRot = 0.0f;
|
||||
|
||||
inventory.clearContent();
|
||||
}
|
||||
|
||||
public void setPosition( ITurtleAccess turtle, BlockPos position, Direction direction )
|
||||
{
|
||||
double posX = position.getX() + 0.5;
|
||||
double posY = position.getY() + 0.5;
|
||||
double posZ = position.getZ() + 0.5;
|
||||
|
||||
// Stop intersection with the turtle itself
|
||||
if( turtle.getPosition().equals( position ) )
|
||||
{
|
||||
posX += 0.48 * direction.getStepX();
|
||||
posY += 0.48 * direction.getStepY();
|
||||
posZ += 0.48 * direction.getStepZ();
|
||||
}
|
||||
|
||||
if( direction.getAxis() != Direction.Axis.Y )
|
||||
{
|
||||
yRot = direction.toYRot();
|
||||
xRot = 0.0f;
|
||||
}
|
||||
else
|
||||
{
|
||||
yRot = turtle.getDirection().toYRot();
|
||||
xRot = DirectionUtil.toPitchAngle( direction );
|
||||
}
|
||||
|
||||
setPosRaw( posX, posY, posZ );
|
||||
xo = posX;
|
||||
yo = posY;
|
||||
zo = posZ;
|
||||
xRotO = xRot;
|
||||
yRotO = yRot;
|
||||
|
||||
yHeadRot = yRot;
|
||||
yHeadRotO = yHeadRot;
|
||||
}
|
||||
|
||||
public void loadInventory( @Nonnull ItemStack stack )
|
||||
{
|
||||
inventory.clearContent();
|
||||
inventory.selected = 0;
|
||||
inventory.setItem( 0, stack );
|
||||
}
|
||||
|
||||
public void loadInventory( @Nonnull ITurtleAccess turtle )
|
||||
{
|
||||
inventory.clearContent();
|
||||
|
||||
int currentSlot = turtle.getSelectedSlot();
|
||||
int slots = turtle.getItemHandler().getSlots();
|
||||
|
||||
// Load up the fake inventory
|
||||
inventory.selected = 0;
|
||||
for( int i = 0; i < slots; i++ )
|
||||
{
|
||||
inventory.setItem( i, turtle.getItemHandler().getStackInSlot( (currentSlot + i) % slots ) );
|
||||
}
|
||||
}
|
||||
|
||||
public void unloadInventory( ITurtleAccess turtle )
|
||||
{
|
||||
int currentSlot = turtle.getSelectedSlot();
|
||||
int slots = turtle.getItemHandler().getSlots();
|
||||
|
||||
// Load up the fake inventory
|
||||
inventory.selected = 0;
|
||||
for( int i = 0; i < slots; i++ )
|
||||
{
|
||||
turtle.getItemHandler().setStackInSlot( (currentSlot + i) % slots, inventory.getItem( i ) );
|
||||
}
|
||||
|
||||
// Store (or drop) anything else we found
|
||||
BlockPos dropPosition = turtle.getPosition();
|
||||
Direction dropDirection = turtle.getDirection().getOpposite();
|
||||
for( int i = 0; i < inventory.getContainerSize(); i++ )
|
||||
int totalSize = inventory.getContainerSize();
|
||||
for( int i = slots; i < totalSize; i++ )
|
||||
{
|
||||
ItemStack stack = inventory.getItem( i );
|
||||
if( !stack.isEmpty() )
|
||||
ItemStack remainder = InventoryUtil.storeItems( inventory.getItem( i ), turtle.getItemHandler(), turtle.getSelectedSlot() );
|
||||
if( !remainder.isEmpty() )
|
||||
{
|
||||
ItemStack remainder = InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot() );
|
||||
if( !remainder.isEmpty() )
|
||||
{
|
||||
WorldUtil.dropItemStack( remainder, turtle.getWorld(), dropPosition, dropDirection );
|
||||
}
|
||||
inventory.setItem( i, ItemStack.EMPTY );
|
||||
WorldUtil.dropItemStack( remainder, turtle.getWorld(), dropPosition, dropDirection );
|
||||
}
|
||||
}
|
||||
|
||||
inventory.setChanged();
|
||||
return results;
|
||||
}
|
||||
|
||||
@Nonnull
|
||||
|
@ -58,7 +58,7 @@ public class TurtleSuckCommand implements ITurtleCommand
|
||||
IItemHandler inventory = InventoryUtil.getInventory( world, blockPosition, side );
|
||||
|
||||
// Fire the event, exiting if it is cancelled.
|
||||
TurtlePlayer player = TurtlePlaceCommand.createPlayer( turtle, turtlePosition, direction );
|
||||
TurtlePlayer player = TurtlePlayer.getWithPosition( turtle, turtlePosition, direction );
|
||||
TurtleInventoryEvent.Suck event = new TurtleInventoryEvent.Suck( turtle, player, world, blockPosition, inventory );
|
||||
if( MinecraftForge.EVENT_BUS.post( event ) )
|
||||
{
|
||||
|
@ -59,9 +59,7 @@ public class TurtleHoe extends TurtleTool
|
||||
{
|
||||
if( verb == TurtleVerb.DIG )
|
||||
{
|
||||
ItemStack hoe = item.copy();
|
||||
ItemStack remainder = TurtlePlaceCommand.deploy( hoe, turtle, direction, null, null );
|
||||
if( remainder != hoe )
|
||||
if( TurtlePlaceCommand.deployCopiedItem( item.copy(), turtle, direction, null, null ) )
|
||||
{
|
||||
return TurtleCommandResult.success();
|
||||
}
|
||||
|
@ -63,9 +63,7 @@ public class TurtleShovel extends TurtleTool
|
||||
{
|
||||
if( verb == TurtleVerb.DIG )
|
||||
{
|
||||
ItemStack shovel = item.copy();
|
||||
ItemStack remainder = TurtlePlaceCommand.deploy( shovel, turtle, direction, null, null );
|
||||
if( remainder != shovel )
|
||||
if( TurtlePlaceCommand.deployCopiedItem( item.copy(), turtle, direction, null, null ) )
|
||||
{
|
||||
return TurtleCommandResult.success();
|
||||
}
|
||||
|
@ -12,7 +12,6 @@ import dan200.computercraft.api.turtle.event.TurtleAttackEvent;
|
||||
import dan200.computercraft.api.turtle.event.TurtleBlockEvent;
|
||||
import dan200.computercraft.shared.TurtlePermissions;
|
||||
import dan200.computercraft.shared.turtle.core.TurtleBrain;
|
||||
import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand;
|
||||
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
|
||||
import dan200.computercraft.shared.util.DropConsumer;
|
||||
import dan200.computercraft.shared.util.InventoryUtil;
|
||||
@ -45,7 +44,6 @@ import net.minecraftforge.event.world.BlockEvent;
|
||||
import org.apache.commons.lang3.tuple.Pair;
|
||||
|
||||
import javax.annotation.Nonnull;
|
||||
import java.util.List;
|
||||
import java.util.function.Function;
|
||||
|
||||
public class TurtleTool extends AbstractTurtleUpgrade
|
||||
@ -140,7 +138,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
|
||||
TileEntity turtleTile = turtle instanceof TurtleBrain ? ((TurtleBrain) turtle).getOwner() : world.getBlockEntity( position );
|
||||
if( turtleTile == null ) return TurtleCommandResult.failure( "Turtle has vanished from existence." );
|
||||
|
||||
final TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, position, direction );
|
||||
final TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, position, direction );
|
||||
|
||||
// See if there is an entity present
|
||||
Vec3d turtlePos = turtlePlayer.position();
|
||||
@ -204,7 +202,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
|
||||
// Put everything we collected into the turtles inventory, then return
|
||||
if( attacked )
|
||||
{
|
||||
turtlePlayer.unloadInventory( turtle );
|
||||
turtlePlayer.inventory.clearContent();
|
||||
return TurtleCommandResult.success();
|
||||
}
|
||||
}
|
||||
@ -229,7 +227,7 @@ public class TurtleTool extends AbstractTurtleUpgrade
|
||||
BlockState state = world.getBlockState( blockPosition );
|
||||
IFluidState fluidState = world.getFluidState( blockPosition );
|
||||
|
||||
TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( turtle, turtlePosition, direction );
|
||||
TurtlePlayer turtlePlayer = TurtlePlayer.getWithPosition( turtle, turtlePosition, direction );
|
||||
turtlePlayer.loadInventory( item.copy() );
|
||||
|
||||
if( ComputerCraft.turtlesObeyBlockProtection )
|
||||
@ -293,10 +291,6 @@ public class TurtleTool extends AbstractTurtleUpgrade
|
||||
private static void stopConsuming( TileEntity tile, ITurtleAccess turtle )
|
||||
{
|
||||
Direction direction = tile.isRemoved() ? null : turtle.getDirection().getOpposite();
|
||||
List<ItemStack> extra = DropConsumer.clear();
|
||||
for( ItemStack remainder : extra )
|
||||
{
|
||||
WorldUtil.dropItemStack( remainder, turtle.getWorld(), turtle.getPosition(), direction );
|
||||
}
|
||||
DropConsumer.clearAndDrop( turtle.getWorld(), turtle.getPosition(), direction );
|
||||
}
|
||||
}
|
||||
|
@ -9,6 +9,7 @@ import dan200.computercraft.ComputerCraft;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.item.ItemEntity;
|
||||
import net.minecraft.item.ItemStack;
|
||||
import net.minecraft.util.Direction;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.BlockPos;
|
||||
import net.minecraft.world.World;
|
||||
@ -66,6 +67,12 @@ public final class DropConsumer
|
||||
return remainingStacks;
|
||||
}
|
||||
|
||||
public static void clearAndDrop( World world, BlockPos pos, Direction direction )
|
||||
{
|
||||
List<ItemStack> remainingDrops = clear();
|
||||
for( ItemStack remaining : remainingDrops ) WorldUtil.dropItemStack( remaining, world, pos, direction );
|
||||
}
|
||||
|
||||
private static void handleDrops( ItemStack stack )
|
||||
{
|
||||
ItemStack remaining = dropConsumer.apply( stack );
|
||||
|
Loading…
Reference in New Issue
Block a user