1
0
mirror of https://github.com/SquidDev-CC/CC-Tweaked synced 2024-06-24 06:03:28 +00:00

Add turtle events

The main aim of this is to allow for greater extensibility for other
mods. For instance, you can now prevent turtles placing dirt blocks, or
turning when on gravel.
This commit is contained in:
SquidDev 2018-02-16 10:33:32 +00:00
parent 3b3dd8071b
commit ecff23d027
19 changed files with 801 additions and 16 deletions

View File

@ -8,11 +8,15 @@
import dan200.computercraft.api.ComputerCraftAPI;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.event.TurtleAttackEvent;
import dan200.computercraft.api.turtle.event.TurtleBlockEvent;
import net.minecraft.client.renderer.block.model.IBakedModel;
import net.minecraft.client.renderer.block.model.ModelResourceLocation;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.event.entity.player.AttackEntityEvent;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import org.apache.commons.lang3.tuple.Pair;
@ -100,6 +104,9 @@ public interface ITurtleUpgrade
* Will only be called for Tool turtle. Called when turtle.dig() or turtle.attack() is called
* by the turtle, and the tool is required to do some work.
*
* Conforming implementations should fire {@link BlockEvent.BreakEvent} and {@link TurtleBlockEvent.Dig}for digging,
* {@link AttackEntityEvent} and {@link TurtleAttackEvent} for attacking.
*
* @param turtle Access to the turtle that the tool resides on.
* @param side Which side of the turtle (left or right) the tool resides on.
* @param verb Which action (dig or attack) the turtle is being called on to perform.

View File

@ -0,0 +1,84 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.turtle.event;
/**
* A basic action that a turtle may perform, as accessed by the {@code turtle} API.
*
* @see TurtleActionEvent
*/
public enum TurtleAction
{
/**
* A turtle moves to a new position.
*
* @see TurtleBlockEvent.Move
*/
MOVE,
/**
* A turtle turns in a specific direction.
*/
TURN,
/**
* A turtle attempts to dig a block.
*
* @see TurtleBlockEvent.Dig
*/
DIG,
/**
* A turtle attempts to place a block or item in the world.
*
* @see TurtleBlockEvent.Place
*/
PLACE,
/**
* A turtle attempts to attack an entity.
*
* @see TurtleActionEvent
*/
ATTACK,
/**
* Drop an item into an inventory/the world.
*
* @see TurtleInventoryEvent.Drop
*/
DROP,
/**
* Suck an item from an inventory or the world.
*
* @see TurtleInventoryEvent.Suck
*/
SUCK,
/**
* Refuel the turtle's fuel levels.
*/
REFUEL,
/**
* Equip or unequip an item.
*/
EQUIP,
/**
* Inspect a block in world
*
* @see TurtleBlockEvent.Inspect
*/
INSPECT,
/**
* Gather metdata about an item in the turtle's inventory.
*/
INSPECT_ITEM,
}

View File

@ -0,0 +1,76 @@
package dan200.computercraft.api.turtle.event;
import com.google.common.base.Preconditions;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.TurtleCommandResult;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* An event fired when a turtle is performing a known action.
*/
@Cancelable
public class TurtleActionEvent extends TurtleEvent
{
private final TurtleAction action;
private String failureMessage;
public TurtleActionEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action )
{
super( turtle );
Preconditions.checkNotNull( action, "action cannot be null" );
this.action = action;
}
public TurtleAction getAction()
{
return action;
}
/**
* Sets the cancellation state of this action.
*
* If {@code cancel} is {@code true}, this action will not be carried out.
*
* @param cancel The new canceled value.
* @see TurtleCommandResult#failure()
* @deprecated Use {@link #setCanceled(boolean, String)} instead.
*/
@Override
@Deprecated
public void setCanceled( boolean cancel )
{
setCanceled( cancel, null );
}
/**
* Set the cancellation state of this action, setting a failure message if required.
*
* If {@code cancel} is {@code true}, this action will not be carried out.
*
* @param cancel The new canceled value.
* @param failureMessage The message to return to the user explaining the failure.
* @see TurtleCommandResult#failure(String)
*/
public void setCanceled( boolean cancel, @Nullable String failureMessage )
{
super.setCanceled( cancel );
this.failureMessage = cancel ? failureMessage : null;
}
/**
* Get the message with which this will fail.
*
* @return The failure message.
* @see TurtleCommandResult#failure()
* @see #setCanceled(boolean, String)
*/
@Nullable
public String getFailureMessage()
{
return failureMessage;
}
}

View File

@ -0,0 +1,80 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.turtle.event;
import com.google.common.base.Preconditions;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.turtle.TurtleVerb;
import net.minecraft.entity.Entity;
import net.minecraft.util.EnumFacing;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.event.entity.player.AttackEntityEvent;
import javax.annotation.Nonnull;
/**
* Fired when a turtle attempts to attack an entity.
*
* This must be fired by {@link ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, EnumFacing)},
* as the base {@code turtle.attack()} command does not fire it.
*
* Note that such commands should also fire {@link AttackEntityEvent}, so you do not need to listen to both.
*
* @see TurtleAction#ATTACK
*/
public class TurtleAttackEvent extends TurtlePlayerEvent
{
private final Entity target;
private final ITurtleUpgrade upgrade;
private final TurtleSide side;
public TurtleAttackEvent( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull Entity target, @Nonnull ITurtleUpgrade upgrade, @Nonnull TurtleSide side )
{
super( turtle, TurtleAction.ATTACK, player );
Preconditions.checkNotNull( target, "target cannot be null" );
Preconditions.checkNotNull( upgrade, "upgrade cannot be null" );
Preconditions.checkNotNull( side, "side cannot be null" );
this.target = target;
this.upgrade = upgrade;
this.side = side;
}
/**
* Get the entity being attacked by this turtle.
*
* @return The entity being attacked.
*/
@Nonnull
public Entity getTarget()
{
return target;
}
/**
* Get the upgrade responsible for attacking.
*
* @return The upgrade responsible for attacking.
*/
@Nonnull
public ITurtleUpgrade getUpgrade()
{
return upgrade;
}
/**
* Get the side the attacking upgrade is on.
*
* @return The upgrade's side.
*/
@Nonnull
public TurtleSide getSide()
{
return side;
}
}

View File

@ -0,0 +1,241 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.turtle.event;
import com.google.common.base.Preconditions;
import dan200.computercraft.api.lua.ILuaContext;
import dan200.computercraft.api.peripheral.IComputerAccess;
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleUpgrade;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.turtle.TurtleVerb;
import net.minecraft.block.state.IBlockState;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import javax.annotation.Nonnull;
import java.util.Map;
/**
* A general event for when a turtle interacts with a block or region.
*
* You should generally listen to one of the sub-events instead, cancelling them where
* appropriate.
*
* Note that you are not guaranteed to receive this event, if it has been cancelled by other
* mechanisms, such as block protection systems.
*
* Be aware that some events (such as {@link TurtleInventoryEvent}) do not necessarily interact
* with a block, simply objects within that block space.
*/
@Cancelable
public abstract class TurtleBlockEvent extends TurtlePlayerEvent
{
private final World world;
private final BlockPos pos;
protected TurtleBlockEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos )
{
super( turtle, action, player );
Preconditions.checkNotNull( world, "world cannot be null" );
Preconditions.checkNotNull( pos, "pos cannot be null" );
this.world = world;
this.pos = pos;
}
/**
* Get the world the turtle is interacting in.
*
* @return The world the turtle is interacting in.
*/
public World getWorld()
{
return world;
}
/**
* Get the position the turtle is interacting with. Note that this is different
* to {@link ITurtleAccess#getPosition()}.
*
* @return The position the turtle is interacting with.
*/
public BlockPos getPos()
{
return pos;
}
/**
* Fired when a turtle attempts to dig a block.
*
* This must be fired by {@link ITurtleUpgrade#useTool(ITurtleAccess, TurtleSide, TurtleVerb, EnumFacing)},
* as the base {@code turtle.dig()} command does not fire it.
*
* Note that such commands should also fire {@link BlockEvent.BreakEvent}, so you do not need to listen to both.
*
* @see TurtleAction#DIG
*/
@Cancelable
public static class Dig extends TurtleBlockEvent
{
private final IBlockState block;
private final ITurtleUpgrade upgrade;
private final TurtleSide side;
public Dig( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState block, @Nonnull ITurtleUpgrade upgrade, @Nonnull TurtleSide side )
{
super( turtle, TurtleAction.DIG, player, world, pos );
Preconditions.checkNotNull( block, "block cannot be null" );
Preconditions.checkNotNull( upgrade, "upgrade cannot be null" );
Preconditions.checkNotNull( side, "side cannot be null" );
this.block = block;
this.upgrade = upgrade;
this.side = side;
}
/**
* Get the block which is about to be broken.
*
* @return The block which is going to be broken.
*/
@Nonnull
public IBlockState getBlock()
{
return block;
}
/**
* Get the upgrade doing the digging
*
* @return The upgrade doing the digging.
*/
@Nonnull
public ITurtleUpgrade getUpgrade()
{
return upgrade;
}
/**
* Get the side the upgrade doing the digging is on.
*
* @return The upgrade's side.
*/
@Nonnull
public TurtleSide getSide()
{
return side;
}
}
/**
* Fired when a turtle attempts to move into a block.
*
* @see TurtleAction#MOVE
*/
@Cancelable
public static class Move extends TurtleBlockEvent
{
public Move( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos )
{
super( turtle, TurtleAction.MOVE, player, world, pos );
}
}
/**
* Fired when a turtle attempts to place a block in the world.
*
* @see TurtleAction#PLACE
*/
@Cancelable
public static class Place extends TurtleBlockEvent
{
private final ItemStack stack;
public Place( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull ItemStack stack )
{
super( turtle, TurtleAction.PLACE, player, world, pos );
Preconditions.checkNotNull( stack, "stack cannot be null" );
this.stack = stack;
}
/**
* Get the item stack that will be placed. This should not be modified.
*
* @return The item stack to be placed.
*/
@Nonnull
public ItemStack getStack()
{
return stack;
}
}
/**
* Fired when a turtle gathers data on a block in world.
*
* You may prevent blocks being inspected, or add additional information to the result.
*
* @see TurtleAction#INSPECT
*/
@Cancelable
public static class Inspect extends TurtleBlockEvent
{
private final IBlockState state;
private final Map<String, Object> data;
public Inspect( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nonnull IBlockState state, @Nonnull Map<String, Object> data )
{
super( turtle, TurtleAction.INSPECT, player, world, pos );
Preconditions.checkNotNull( state, "state cannot be null" );
Preconditions.checkNotNull( data, "data cannot be null" );
this.data = data;
this.state = state;
}
/**
* Get the block state which is being inspected.
*
* @return The inspected block state.
*/
@Nonnull
public IBlockState getState()
{
return state;
}
/**
* Get the "inspection data" from this block, which will be returned to the user.
*
* @return This block's inspection data.
*/
@Nonnull
public Map<String, Object> getData()
{
return data;
}
/**
* Add new information to the inspection result. Note this will override fields with the same name.
*
* @param newData The data to add. Note all values should be convertable to Lua (see
* {@link dan200.computercraft.api.peripheral.IPeripheral#callMethod(IComputerAccess, ILuaContext, int, Object[])}).
*/
public void addData( @Nonnull Map<String, ?> newData )
{
Preconditions.checkNotNull( newData, "newData cannot be null" );
data.putAll( newData );
}
}
}

View File

@ -0,0 +1,43 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.turtle.event;
import com.google.common.base.Preconditions;
import dan200.computercraft.api.turtle.ITurtleAccess;
import net.minecraftforge.fml.common.eventhandler.Event;
import javax.annotation.Nonnull;
/**
* A base class for all events concerning a turtle. This will only ever constructed and fired on the server side,
* so sever specific methods on {@link ITurtleAccess} are safe to use.
*
* You should generally not need to subscribe to this event, preferring one of the more specific classes.
*
* @see TurtleActionEvent
*/
public abstract class TurtleEvent extends Event
{
private final ITurtleAccess turtle;
protected TurtleEvent( @Nonnull ITurtleAccess turtle )
{
Preconditions.checkNotNull( turtle, "turtle cannot be null" );
this.turtle = turtle;
}
/**
* Get the turtle which is performing this action.
*
* @return The access for this turtle.
*/
@Nonnull
public ITurtleAccess getTurtle()
{
return turtle;
}
}

View File

@ -0,0 +1,84 @@
package dan200.computercraft.api.turtle.event;
import com.google.common.base.Preconditions;
import dan200.computercraft.api.turtle.ITurtleAccess;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.util.FakePlayer;
import net.minecraftforge.fml.common.eventhandler.Cancelable;
import net.minecraftforge.items.IItemHandler;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Fired when a turtle attempts to interact with an inventory.
*/
@Cancelable
public abstract class TurtleInventoryEvent extends TurtleBlockEvent
{
private final IItemHandler handler;
protected TurtleInventoryEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable IItemHandler handler )
{
super( turtle, action, player, world, pos );
this.handler = handler;
}
/**
* Get the inventory being interacted with
*
* @return The inventory being interacted with, {@code null} if the item will be dropped to/sucked from the world.
*/
@Nullable
public IItemHandler getItemHandler()
{
return handler;
}
/**
* Fired when a turtle attempts to suck from an inventory.
*
* @see TurtleAction#SUCK
*/
@Cancelable
public static class Suck extends TurtleInventoryEvent
{
public Suck( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable IItemHandler handler )
{
super( turtle, TurtleAction.SUCK, player, world, pos, handler );
}
}
/**
* Fired when a turtle attempts to drop an item into an inventory.
*
* @see TurtleAction#DROP
*/
@Cancelable
public static class Drop extends TurtleInventoryEvent
{
private final ItemStack stack;
public Drop( @Nonnull ITurtleAccess turtle, @Nonnull FakePlayer player, @Nonnull World world, @Nonnull BlockPos pos, @Nullable IItemHandler handler, @Nonnull ItemStack stack )
{
super( turtle, TurtleAction.DROP, player, world, pos, handler );
Preconditions.checkNotNull( stack, "stack cannot be null" );
this.stack = stack;
}
/**
* The item which will be inserted into the inventory/dropped on the ground.
*
* Note that this is a copy of the original stack, and so should not be modified, as that will have no effect.
*
* @return The item stack which will be dropped.
*/
public ItemStack getStack()
{
return stack.copy();
}
}
}

View File

@ -0,0 +1,44 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
package dan200.computercraft.api.turtle.event;
import com.google.common.base.Preconditions;
import dan200.computercraft.api.turtle.ITurtleAccess;
import net.minecraftforge.common.util.FakePlayer;
import javax.annotation.Nonnull;
/**
* An action done by a turtle which is normally done by a player.
*
* {@link #getPlayer()} may be used to modify the player's attributes or perform permission checks.
*/
public abstract class TurtlePlayerEvent extends TurtleActionEvent
{
private final FakePlayer player;
protected TurtlePlayerEvent( @Nonnull ITurtleAccess turtle, @Nonnull TurtleAction action, @Nonnull FakePlayer player )
{
super( turtle, action );
Preconditions.checkNotNull( player, "player cannot be null" );
this.player = player;
}
/**
* A fake player, representing this turtle.
*
* This may be used for triggering permission checks.
*
* @return A {@link FakePlayer} representing this turtle.
*/
@Nonnull
public FakePlayer getPlayer()
{
return player;
}
}

View File

@ -0,0 +1,10 @@
/*
* This file is part of the public ComputerCraft API - http://www.computercraft.info
* Copyright Daniel Ratcliffe, 2011-2017. This API may be redistributed unmodified and in full only.
* For help using the API, and posting your mods, visit the forums at computercraft.info.
*/
@API(owner = "ComputerCraft", provides = "ComputerCraft|API|Turtle|Event", apiVersion = "${version}")
package dan200.computercraft.api.turtle.event;
import net.minecraftforge.fml.common.API;

View File

@ -11,11 +11,14 @@
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleCommand;
import dan200.computercraft.api.turtle.TurtleSide;
import dan200.computercraft.api.turtle.event.TurtleAction;
import dan200.computercraft.api.turtle.event.TurtleActionEvent;
import dan200.computercraft.core.apis.IAPIEnvironment;
import dan200.computercraft.core.apis.ILuaAPI;
import dan200.computercraft.shared.turtle.core.*;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraftforge.common.MinecraftForge;
import javax.annotation.Nonnull;
import java.util.HashMap;
@ -439,6 +442,13 @@ public Object[] callMethod( @Nonnull ILuaContext context, int method, @Nonnull O
table.put( "name", name );
table.put( "damage", damage );
table.put( "count", count );
TurtleActionEvent event = new TurtleActionEvent( m_turtle, TurtleAction.INSPECT_ITEM );
if( MinecraftForge.EVENT_BUS.post( event ) )
{
return new Object[] { false, event.getFailureMessage() };
}
return new Object[] { table };
}
else

View File

@ -10,12 +10,14 @@
import dan200.computercraft.api.turtle.ITurtleCommand;
import dan200.computercraft.api.turtle.TurtleAnimation;
import dan200.computercraft.api.turtle.TurtleCommandResult;
import dan200.computercraft.api.turtle.event.TurtleInventoryEvent;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.item.ItemStack;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.items.IItemHandler;
import javax.annotation.Nonnull;
@ -59,6 +61,16 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
EnumFacing side = direction.getOpposite();
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 );
TurtleInventoryEvent.Drop event = new TurtleInventoryEvent.Drop( turtle, player, world, newPosition, inventory, stack );
if( MinecraftForge.EVENT_BUS.post( event ) )
{
InventoryUtil.storeItems( stack, turtle.getItemHandler(), turtle.getSelectedSlot() );
return TurtleCommandResult.failure( event.getFailureMessage() );
}
if( inventory != null )
{
// Drop the item into the inventory

View File

@ -8,11 +8,14 @@
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.turtle.*;
import dan200.computercraft.api.turtle.event.TurtleAction;
import dan200.computercraft.api.turtle.event.TurtleActionEvent;
import dan200.computercraft.shared.proxy.CCTurtleProxyCommon;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.item.ItemStack;
import net.minecraft.util.math.BlockPos;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.items.IItemHandler;
import javax.annotation.Nonnull;
@ -63,6 +66,12 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
oldUpgradeStack = null;
}
TurtleActionEvent event = new TurtleActionEvent( turtle, TurtleAction.EQUIP );
if( MinecraftForge.EVENT_BUS.post( event ) )
{
return TurtleCommandResult.failure( event.getFailureMessage() );
}
// Do the swapping:
if( newUpgradeStack != null )
{

View File

@ -10,13 +10,15 @@
import dan200.computercraft.api.turtle.ITurtleAccess;
import dan200.computercraft.api.turtle.ITurtleCommand;
import dan200.computercraft.api.turtle.TurtleCommandResult;
import dan200.computercraft.api.turtle.event.TurtleBlockEvent;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.block.Block;
import net.minecraft.block.properties.IProperty;
import net.minecraft.block.state.IBlockState;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import javax.annotation.Nonnull;
import java.util.HashMap;
@ -46,14 +48,14 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
if( WorldUtil.isBlockInWorld( world, newPosition ) )
{
if( !FAIL_ON_AIR || !world.isAirBlock( newPosition ) )
IBlockState state = world.getBlockState( newPosition );
if( !FAIL_ON_AIR || !state.getBlock().isAir( state, world, newPosition ) )
{
IBlockState state = world.getBlockState( newPosition );
Block block = state.getBlock();
String name = Block.REGISTRY.getNameForObject( block ).toString();
int metadata = block.getMetaFromState( state );
Map<Object, Object> table = new HashMap<>();
Map<String, Object> table = new HashMap<>();
table.put( "name", name );
table.put( "metadata", metadata );
@ -73,7 +75,15 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
}
table.put( "state", stateTable );
return TurtleCommandResult.success( new Object[]{ table } );
// Fire the event, exiting if it is cancelled
TurtlePlayer turtlePlayer = TurtlePlaceCommand.createPlayer( 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() );
}
return TurtleCommandResult.success( new Object[] { table } );
}
}
@ -83,7 +93,7 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
table.put( "name", "minecraft:air" );
table.put( "metadata", 0 );
table.put( "state", new HashMap<>() );
return TurtleCommandResult.success( new Object[]{ table } );
return TurtleCommandResult.success( new Object[] { table } );
}
return TurtleCommandResult.failure( "No block to inspect" );
}

View File

@ -11,13 +11,15 @@
import dan200.computercraft.api.turtle.ITurtleCommand;
import dan200.computercraft.api.turtle.TurtleAnimation;
import dan200.computercraft.api.turtle.TurtleCommandResult;
import dan200.computercraft.api.turtle.event.TurtleBlockEvent;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.block.Block;
import net.minecraft.entity.Entity;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import javax.annotation.Nonnull;
import java.util.List;
@ -103,6 +105,12 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
}
}
TurtleBlockEvent.Move moveEvent = new TurtleBlockEvent.Move( turtle, turtlePlayer, oldWorld, newPosition );
if( MinecraftForge.EVENT_BUS.post( moveEvent ) )
{
return TurtleCommandResult.failure( moveEvent.getFailureMessage() );
}
// Check fuel level
if( turtle.isFuelNeeded() && turtle.getFuelLevel() < 1 )
{

View File

@ -11,6 +11,7 @@
import dan200.computercraft.api.turtle.ITurtleCommand;
import dan200.computercraft.api.turtle.TurtleAnimation;
import dan200.computercraft.api.turtle.TurtleCommandResult;
import dan200.computercraft.api.turtle.event.TurtleBlockEvent;
import dan200.computercraft.shared.util.DirectionUtil;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.WorldUtil;
@ -31,6 +32,7 @@
import net.minecraft.world.World;
import net.minecraft.world.WorldServer;
import net.minecraftforge.common.ForgeHooks;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.fml.common.eventhandler.Event;
import org.apache.commons.lang3.tuple.Pair;
@ -64,6 +66,16 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
World world = turtle.getWorld();
BlockPos coordinates = WorldUtil.moveCoords( turtle.getPosition(), direction );
// Create a fake player, and orient it appropriately
BlockPos playerPosition = WorldUtil.moveCoords( turtle.getPosition(), direction );
TurtlePlayer turtlePlayer = createPlayer( 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() );
}
IBlockState previousState;
if( WorldUtil.isBlockInWorld( world, coordinates ) )
{
@ -75,8 +87,8 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
}
// Do the deploying
String[] errorMessage = new String[1];
ItemStack remainder = deploy( stack, turtle, direction, m_extraArguments, errorMessage );
String[] errorMessage = new String[ 1 ];
ItemStack remainder = deploy( stack, turtle, turtlePlayer, direction, m_extraArguments, errorMessage );
if( remainder != stack )
{
// Put the remaining items back
@ -117,6 +129,11 @@ public static ItemStack deploy( @Nonnull ItemStack stack, ITurtleAccess turtle,
BlockPos playerPosition = WorldUtil.moveCoords( turtle.getPosition(), direction );
TurtlePlayer turtlePlayer = createPlayer( turtle, playerPosition, direction );
return deploy( stack, turtle, turtlePlayer, direction, extraArguments, o_errorMessage );
}
public static ItemStack deploy( @Nonnull ItemStack stack, ITurtleAccess turtle, TurtlePlayer turtlePlayer, EnumFacing direction, Object[] extraArguments, String[] o_errorMessage )
{
// Deploy on an entity
ItemStack remainder = deployOnEntity( stack, turtle, turtlePlayer, direction, extraArguments, o_errorMessage );
if( remainder != stack )

View File

@ -10,9 +10,12 @@
import dan200.computercraft.api.turtle.ITurtleCommand;
import dan200.computercraft.api.turtle.TurtleAnimation;
import dan200.computercraft.api.turtle.TurtleCommandResult;
import dan200.computercraft.api.turtle.event.TurtleAction;
import dan200.computercraft.api.turtle.event.TurtleActionEvent;
import dan200.computercraft.shared.util.InventoryUtil;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntityFurnace;
import net.minecraftforge.common.MinecraftForge;
import javax.annotation.Nonnull;
@ -71,6 +74,12 @@ private TurtleCommandResult refuel( ITurtleAccess turtle, @Nonnull ItemStack sta
return TurtleCommandResult.failure( "Items not combustible" );
}
TurtleActionEvent event = new TurtleActionEvent( turtle, TurtleAction.REFUEL );
if( MinecraftForge.EVENT_BUS.post( event ) )
{
return TurtleCommandResult.failure( event.getFailureMessage() );
}
if( !testOnly )
{
// Determine fuel to give and replacement item to leave behind

View File

@ -10,6 +10,7 @@
import dan200.computercraft.api.turtle.ITurtleCommand;
import dan200.computercraft.api.turtle.TurtleAnimation;
import dan200.computercraft.api.turtle.TurtleCommandResult;
import dan200.computercraft.api.turtle.event.TurtleInventoryEvent;
import dan200.computercraft.shared.util.InventoryUtil;
import dan200.computercraft.shared.util.WorldUtil;
import net.minecraft.entity.Entity;
@ -19,6 +20,7 @@
import net.minecraft.util.math.AxisAlignedBB;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.items.IItemHandler;
import javax.annotation.Nonnull;
@ -56,6 +58,15 @@ public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
EnumFacing side = direction.getOpposite();
IItemHandler inventory = InventoryUtil.getInventory( world, newPosition, side );
// Fire the event, exiting if it is cancelled.
TurtlePlayer player = TurtlePlaceCommand.createPlayer( turtle, oldPosition, direction );
TurtleInventoryEvent.Suck event = new TurtleInventoryEvent.Suck( turtle, player, world, newPosition, inventory );
if( MinecraftForge.EVENT_BUS.post( event ) )
{
return TurtleCommandResult.failure( event.getFailureMessage() );
}
if( inventory != null )
{
// Take from inventory of thing in front

View File

@ -10,7 +10,10 @@
import dan200.computercraft.api.turtle.ITurtleCommand;
import dan200.computercraft.api.turtle.TurtleAnimation;
import dan200.computercraft.api.turtle.TurtleCommandResult;
import dan200.computercraft.api.turtle.event.TurtleAction;
import dan200.computercraft.api.turtle.event.TurtleActionEvent;
import dan200.computercraft.shared.util.DirectionUtil;
import net.minecraftforge.common.MinecraftForge;
import javax.annotation.Nonnull;
@ -27,6 +30,12 @@ public TurtleTurnCommand( TurnDirection direction )
@Override
public TurtleCommandResult execute( @Nonnull ITurtleAccess turtle )
{
TurtleActionEvent event = new TurtleActionEvent( turtle, TurtleAction.TURN );
if( MinecraftForge.EVENT_BUS.post( event ) )
{
return TurtleCommandResult.failure( event.getFailureMessage() );
}
switch( m_direction )
{
case Left:

View File

@ -9,6 +9,8 @@
import dan200.computercraft.ComputerCraft;
import dan200.computercraft.api.peripheral.IPeripheral;
import dan200.computercraft.api.turtle.*;
import dan200.computercraft.api.turtle.event.TurtleAttackEvent;
import dan200.computercraft.api.turtle.event.TurtleBlockEvent;
import dan200.computercraft.shared.turtle.core.TurtleBrain;
import dan200.computercraft.shared.turtle.core.TurtlePlaceCommand;
import dan200.computercraft.shared.turtle.core.TurtlePlayer;
@ -128,11 +130,11 @@ public TurtleCommandResult useTool( @Nonnull ITurtleAccess turtle, @Nonnull Turt
{
case Attack:
{
return attack( turtle, direction );
return attack( turtle, direction, side );
}
case Dig:
{
return dig( turtle, direction );
return dig( turtle, direction, side );
}
default:
{
@ -161,7 +163,7 @@ protected float getDamageMultiplier()
return 3.0f;
}
private TurtleCommandResult attack( final ITurtleAccess turtle, EnumFacing direction )
private TurtleCommandResult attack( final ITurtleAccess turtle, EnumFacing direction, TurtleSide side )
{
// Create a fake player, and orient it appropriately
final World world = turtle.getWorld();
@ -178,8 +180,21 @@ private TurtleCommandResult attack( final ITurtleAccess turtle, EnumFacing direc
ItemStack stackCopy = m_item.copy();
turtlePlayer.loadInventory( stackCopy );
// Start claiming entity drops
Entity hitEntity = hit.getKey();
// Fire several events to ensure we have permissions.
if( MinecraftForge.EVENT_BUS.post( new AttackEntityEvent( turtlePlayer, hitEntity ) ) || !hitEntity.canBeAttackedWithItem() )
{
return TurtleCommandResult.failure( "Nothing to attack here" );
}
TurtleAttackEvent attackEvent = new TurtleAttackEvent( turtle, turtlePlayer, hitEntity, this, side );
if( MinecraftForge.EVENT_BUS.post( attackEvent ) )
{
return TurtleCommandResult.failure( attackEvent.getFailureMessage() );
}
// Start claiming entity drops
ComputerCraft.setEntityDropConsumer( hitEntity, ( entity, drop ) ->
{
ItemStack remainder = InventoryUtil.storeItems( drop, turtle.getItemHandler(), turtle.getSelectedSlot() );
@ -191,8 +206,7 @@ private TurtleCommandResult attack( final ITurtleAccess turtle, EnumFacing direc
// Attack the entity
boolean attacked = false;
if( hitEntity.canBeAttackedWithItem() && !hitEntity.hitByEntity( turtlePlayer )
&& !MinecraftForge.EVENT_BUS.post( new AttackEntityEvent( turtlePlayer, hitEntity ) ) )
if( !hitEntity.hitByEntity( turtlePlayer ) )
{
float damage = (float)turtlePlayer.getEntityAttribute( SharedMonsterAttributes.ATTACK_DAMAGE ).getAttributeValue();
damage *= getDamageMultiplier();
@ -233,7 +247,7 @@ private TurtleCommandResult attack( final ITurtleAccess turtle, EnumFacing direc
return TurtleCommandResult.failure( "Nothing to attack here" );
}
private TurtleCommandResult dig( ITurtleAccess turtle, EnumFacing direction )
private TurtleCommandResult dig( ITurtleAccess turtle, EnumFacing direction, TurtleSide side )
{
// Get ready to dig
World world = turtle.getWorld();
@ -266,6 +280,13 @@ private TurtleCommandResult dig( ITurtleAccess turtle, EnumFacing direction )
return TurtleCommandResult.failure( "Unbreakable block detected" );
}
// Fire the dig event, checking whether it was cancelled.
TurtleBlockEvent.Dig digEvent = new TurtleBlockEvent.Dig( turtle, turtlePlayer, world, newPosition, world.getBlockState( newPosition ), this, side );
if( MinecraftForge.EVENT_BUS.post( digEvent ) )
{
return TurtleCommandResult.failure( digEvent.getFailureMessage() );
}
// Consume the items the block drops
if( canHarvestBlock( world, newPosition ) )
{