package speedytools.clientside.tools;
import net.minecraft.client.entity.EntityPlayerSP;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.*;
import net.minecraft.util.BlockPos;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import speedytools.clientside.UndoManagerClient;
import speedytools.clientside.network.PacketSenderClient;
import speedytools.clientside.rendering.RendererElement;
import speedytools.clientside.rendering.RendererHotbarCurrentItem;
import speedytools.clientside.rendering.RendererWireframeSelection;
import speedytools.clientside.rendering.SpeedyToolRenderers;
import speedytools.clientside.selections.BlockMultiSelector;
import speedytools.clientside.sound.SoundController;
import speedytools.clientside.userinput.UserInput;
import speedytools.common.blocks.BlockWithMetadata;
import speedytools.common.items.ItemSpeedyTool;
import speedytools.common.network.Packet250SpeedyToolUse;
import speedytools.common.utilities.Pair;
import speedytools.common.utilities.UsefulConstants;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;
/**
* User: The Grey Ghost
* Date: 18/04/2014
*/
public abstract class SpeedyToolSimple extends SpeedyTool
{
public SpeedyToolSimple(ItemSpeedyTool i_parentItem, SpeedyToolRenderers i_renderers, SoundController i_speedyToolSounds,
UndoManagerClient i_undoManagerClient, PacketSenderClient i_packetSenderClient)
{
super(i_parentItem, i_renderers, i_speedyToolSounds, i_undoManagerClient, i_packetSenderClient);
wireframeRendererUpdateLink = this.new SimpleWireframeRendererLink();
hotbarRenderInfoUpdateLink = this.new HotbarRenderInfoUpdateLink();
}
/**
* Process user input
* no effect if the tool is not active.
*
* @param player
* @param userInput
* @return
*/
public boolean processUserInput(EntityPlayerSP player, float partialTick, UserInput userInput) {
if (!iAmActive) return false;
controlKeyIsDown = userInput.isControlKeyDown();
UserInput.InputEvent nextEvent;
while (null != (nextEvent = userInput.poll())) {
switch (nextEvent.eventType) {
case LEFT_CLICK_DOWN: {
undoManagerClient.performUndo(player.getPositionEyes(partialTick));
break;
}
case RIGHT_CLICK_DOWN: {
boolean successfulSend = sendPlaceCommand();
if (successfulSend) {
undoManagerClient.addUndoableAction(new SpeedyToolUndoCallback());
playPlacementSound(player.getPositionEyes(partialTick));
}
break;
}
case WHEEL_MOVE: {
if (currentToolItemStack != null) {
int newCount = parentItem.getPlacementCount(currentToolItemStack) + nextEvent.count;
parentItem.setPlacementCount(currentToolItemStack, newCount);
}
break;
}
}
}
return true;
}
/**
* update the tool state based on the player selected items; where the player is looking; etc
* No effect if not active.
* @param world
* @param player
* @param partialTick
* @return
*/
public boolean updateForThisFrame(World world, EntityPlayerSP player, float partialTick)
{
if (!iAmActive) return false;
// ItemStack currentItem = player.inventory.getCurrentItem();
if (currentToolItemStack == null) return false; // can be null if the user has just moved the active tool out of hotbar
parentItem.revalidatePlacementCount(currentToolItemStack);
int maxSelectionSize = currentToolItemStack.stackSize;
// the block to be placed is the one to the right of the tool in the hotbar
int currentlySelectedHotbarSlot = player.inventory.currentItem;
final int MAX_HOTBAR_SLOT = 8;
ItemStack itemStackToPlace = (currentlySelectedHotbarSlot == MAX_HOTBAR_SLOT) ? null : player.inventory.getStackInSlot(currentlySelectedHotbarSlot + 1);
currentBlockToPlace = getPlacedBlockFromItemStack(itemStackToPlace);
// MovingObjectPosition target = parentItem.rayTraceLineOfSight(player.worldObj, player);
MovingObjectPosition blockUnderCursor = selectBlockUnderCursor(player, itemStackToPlace, partialTick);
Pair<List<BlockPos>, EnumFacing> retval = selectBlocks(blockUnderCursor, player, maxSelectionSize, partialTick);
currentlySelectedBlocks = retval.getFirst();
currentSideToBePlaced = retval.getSecond();
return true;
}
/** The user is now holding this tool, prepare it
* @return
* @param newToolItemStack
*/
@Override
public boolean activateTool(ItemStack newToolItemStack)
{
currentToolItemStack = newToolItemStack;
LinkedList<RendererElement> rendererElements = new LinkedList<RendererElement>();
rendererElements.add(new RendererWireframeSelection(wireframeRendererUpdateLink));
rendererElements.add(new RendererHotbarCurrentItem(hotbarRenderInfoUpdateLink));
speedyToolRenderers.setRenderers(rendererElements);
iAmActive = true;
return true;
}
/** The user has unequipped this tool, deactivate it, stop any effects, etc
* @return
*/
@Override
public boolean deactivateTool()
{
speedyToolRenderers.setRenderers(null);
currentlySelectedBlocks.clear();
iAmActive = false;
return true;
}
@Override
public void resetTool()
{
// nothing - no state information stored
}
/**
* This class is used to provide information to the WireFrame Renderer when it needs it:
* The Renderer calls refreshRenderInfo, which copies the relevant information from the tool.
*/
public class SimpleWireframeRendererLink implements RendererWireframeSelection.WireframeRenderInfoUpdateLink
{
@Override
public boolean refreshRenderInfo(RendererWireframeSelection.WireframeRenderInfo infoToUpdate)
{
infoToUpdate.currentlySelectedBlocks = currentlySelectedBlocks;
return true;
}
}
/**
* This class is used to provide information to the Boundary Field Renderer when it needs it.
* The information is taken from the reference to the SpeedyToolBoundary.
*/
public class HotbarRenderInfoUpdateLink implements RendererHotbarCurrentItem.HotbarRenderInfoUpdateLink
{
@Override
public boolean refreshRenderInfo(RendererHotbarCurrentItem.HotbarRenderInfo infoToUpdate, ItemStack currentlyHeldItem) {
if (currentlyHeldItem == null || !(currentlyHeldItem.getItem() instanceof ItemSpeedyTool)) {
return false;
}
ItemSpeedyTool itemSpeedyTool = (ItemSpeedyTool) currentlyHeldItem.getItem();
return itemSpeedyTool.usesAdjacentBlockInHotbar();
}
}
public class SpeedyToolUndoCallback implements UndoManagerClient.UndoCallback
{
@Override
public boolean performUndo(Vec3 playerPosition)
{
boolean successfulSend = sendUndoCommand();
if (successfulSend) playUndoSound(playerPosition);
return successfulSend;
}
}
/**
* Selects the a straight line of Blocks that will be affected by the tool when the player presses right-click
* @param blockUnderCursor the position of the cursor
* @param player the player
* @param maxSelectionSize the maximum number of blocks in the selection
* @param stopWhenCollide if true, stop when a "solid" block such as stone is encountered. "non-solid" is blocks such as air, grass, etc
* @param partialTick partial tick time.
* @return returns the list of blocks in the selection (may be zero length)
*/
protected Pair<List<BlockPos>, EnumFacing> selectLineOfBlocks(MovingObjectPosition blockUnderCursor, EntityPlayer player, int maxSelectionSize,
BlockMultiSelector.CollisionOptions stopWhenCollide, float partialTick)
{
// MovingObjectPosition startBlock = BlockMultiSelector.selectStartingBlock(blockUnderCursor, BlockMultiSelector.BlockTypeToSelect.NON_SOLID_OK, player, partialTick);
if (blockUnderCursor == null) return new Pair<List<BlockPos>, EnumFacing>(new ArrayList<BlockPos>(), EnumFacing.UP);
BlockPos startBlockCoordinates = blockUnderCursor.getBlockPos();
boolean diagonalOK = controlKeyIsDown;
List<BlockPos> selection = BlockMultiSelector.selectLine(startBlockCoordinates, player.worldObj, blockUnderCursor.hitVec,
maxSelectionSize, diagonalOK, stopWhenCollide);
return new Pair<List<BlockPos>, EnumFacing> (selection, blockUnderCursor.sideHit);
}
protected boolean sendPlaceCommand()
{
if (currentlySelectedBlocks == null || currentlySelectedBlocks.isEmpty()) return false;
final int RIGHT_BUTTON = 1;
Packet250SpeedyToolUse packet = new Packet250SpeedyToolUse(RIGHT_BUTTON, currentBlockToPlace, currentSideToBePlaced, currentlySelectedBlocks);
packetSenderClient.sendPacket(packet);
return true;
}
protected boolean sendUndoCommand()
{
final int LEFT_BUTTON = 0;
final EnumFacing DUMMY_SIDE = EnumFacing.DOWN;
Packet250SpeedyToolUse packet = new Packet250SpeedyToolUse(LEFT_BUTTON, currentBlockToPlace, DUMMY_SIDE, currentlySelectedBlocks);
packetSenderClient.sendPacket(packet);
return true;
}
protected abstract void playPlacementSound(Vec3 playerPosition);
protected abstract void playUndoSound(Vec3 playerPosition);
/**
* Selects the Blocks that will be affected by the tool when the player presses right-click
* default method just selects the first block.
* @param blockUnderCursor the position of the cursor
* @param player the player
* @param maxSelectionSize the maximum number of blocks in the selection
* @param partialTick partial tick time.
* @return returns the list of blocks in the selection (may be zero length)
*/
protected Pair<List<BlockPos>, EnumFacing> selectBlocks(MovingObjectPosition blockUnderCursor, EntityPlayer player,
int maxSelectionSize, float partialTick)
{
ArrayList<BlockPos> retval = new ArrayList<BlockPos>();
// MovingObjectPosition startBlock = BlockMultiSelector.selectStartingBlock(target, BlockMultiSelector.BlockTypeToSelect.SOLID_OK, player, partialTick);
EnumFacing sideToPlace = EnumFacing.UP;
if (blockUnderCursor != null) {
BlockPos startBlockCoordinates = new BlockPos(blockUnderCursor.getBlockPos());
retval.add(startBlockCoordinates);
sideToPlace = blockUnderCursor.sideHit;
}
return new Pair<List<BlockPos>, EnumFacing> (retval, sideToPlace);
}
protected List<BlockPos> currentlySelectedBlocks = new LinkedList<BlockPos>();
protected BlockWithMetadata currentBlockToPlace;
protected EnumFacing currentSideToBePlaced;
private RendererHotbarCurrentItem.HotbarRenderInfoUpdateLink hotbarRenderInfoUpdateLink;
}