/**
* This class was created by <Vazkii>. It's distributed as
* part of the Botania Mod. Get the Source Code in github:
* https://github.com/Vazkii/Botania
*
* Botania is Open Source and distributed under the
* Botania License: http://botaniamod.net/license.php
*
* File Created @ [Aug 20, 2015, 8:08:34 PM (GMT)]
*/
package vazkii.botania.common.item.rod;
import java.util.ArrayList;
import java.util.List;
import javax.annotation.Nonnull;
import com.google.common.collect.ImmutableList;
import net.minecraft.block.Block;
import net.minecraft.block.state.IBlockState;
import net.minecraft.client.Minecraft;
import net.minecraft.entity.Entity;
import net.minecraft.entity.item.EntityItem;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.inventory.EntityEquipmentSlot;
import net.minecraft.inventory.IInventory;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.World;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import vazkii.botania.api.BotaniaAPI;
import vazkii.botania.api.item.IBlockProvider;
import vazkii.botania.api.item.IManaProficiencyArmor;
import vazkii.botania.api.item.IWireframeCoordinateListProvider;
import vazkii.botania.api.mana.IManaUsingItem;
import vazkii.botania.api.mana.ManaItemHandler;
import vazkii.botania.client.core.handler.ItemsRemainingRenderHandler;
import vazkii.botania.common.block.BlockCamo;
import vazkii.botania.common.core.helper.ItemNBTHelper;
import vazkii.botania.common.item.ItemMod;
import vazkii.botania.common.lib.LibItemNames;
public class ItemExchangeRod extends ItemMod implements IManaUsingItem, IWireframeCoordinateListProvider {
private static final int RANGE = 3;
private static final int COST = 40;
private static final String TAG_BLOCK_NAME = "blockName";
private static final String TAG_BLOCK_META = "blockMeta";
private static final String TAG_TARGET_BLOCK_NAME = "targetBlockName";
private static final String TAG_TARGET_BLOCK_META = "targetBlockMeta";
private static final String TAG_SWAPPING = "swapping";
private static final String TAG_SELECT_X = "selectX";
private static final String TAG_SELECT_Y = "selectY";
private static final String TAG_SELECT_Z = "selectZ";
private static final String TAG_EXTRA_RANGE = "extraRange";
public ItemExchangeRod() {
super(LibItemNames.EXCHANGE_ROD);
setMaxStackSize(1);
MinecraftForge.EVENT_BUS.register(this);
}
@Nonnull
@Override
public EnumActionResult onItemUse(EntityPlayer player, World world, BlockPos pos, EnumHand hand, EnumFacing side, float par8, float par9, float par10) {
ItemStack stack = player.getHeldItem(hand);
IBlockState wstate = world.getBlockState(pos);
if(player.isSneaking()) {
TileEntity tile = world.getTileEntity(pos);
if(tile == null) {
if(BlockCamo.isValidBlock(wstate)) {
Item item = Item.getItemFromBlock(wstate.getBlock());
setBlock(stack, wstate.getBlock(), !item.getHasSubtypes() ? 0 : wstate.getBlock().getMetaFromState(wstate));
player.setItemStackToSlot(hand == EnumHand.MAIN_HAND ? EntityEquipmentSlot.MAINHAND : EntityEquipmentSlot.OFFHAND, stack);
displayRemainderCounter(player, stack);
return EnumActionResult.SUCCESS;
}
}
} else if(canExchange(stack) && !ItemNBTHelper.getBoolean(stack, TAG_SWAPPING, false)) {
Block block = getBlock(stack);
int meta = getBlockMeta(stack);
List<BlockPos> swap = getBlocksToSwap(world, stack, block.getStateFromMeta(meta), pos, null);
if(swap.size() > 0) {
ItemNBTHelper.setBoolean(stack, TAG_SWAPPING, true);
ItemNBTHelper.setInt(stack, TAG_SELECT_X, pos.getX());
ItemNBTHelper.setInt(stack, TAG_SELECT_Y, pos.getY());
ItemNBTHelper.setInt(stack, TAG_SELECT_Z, pos.getZ());
setTargetBlock(stack, wstate.getBlock(), wstate.getBlock().getMetaFromState(wstate));
if(world.isRemote)
player.swingArm(hand);
}
}
return EnumActionResult.SUCCESS;
}
@SubscribeEvent
public void onLeftClick(PlayerInteractEvent.LeftClickBlock event) {
ItemStack stack = event.getItemStack();
if(!stack.isEmpty() && stack.getItem() == this && canExchange(stack) && ManaItemHandler.requestManaExactForTool(stack, event.getEntityPlayer(), COST, false)) {
if(exchange(event.getWorld(), event.getEntityPlayer(), event.getPos(), stack, getBlock(stack).getStateFromMeta(getBlockMeta(stack))))
ManaItemHandler.requestManaExactForTool(stack, event.getEntityPlayer(), COST, true);
}
}
@Override
public void onUpdate(ItemStack stack, World world, Entity entity, int something, boolean somethingelse) {
if(!canExchange(stack) || !(entity instanceof EntityPlayer))
return;
EntityPlayer player = (EntityPlayer) entity;
int extraRange = ItemNBTHelper.getInt(stack, TAG_EXTRA_RANGE, 1);
int extraRangeNew = IManaProficiencyArmor.Helper.hasProficiency(player, stack) ? 3 : 1;
if(extraRange != extraRangeNew)
ItemNBTHelper.setInt(stack, TAG_EXTRA_RANGE, extraRangeNew);
Block block = getBlock(stack);
int meta = getBlockMeta(stack);
if(ItemNBTHelper.getBoolean(stack, TAG_SWAPPING, false)) {
if(!ManaItemHandler.requestManaExactForTool(stack, player, COST, false)) {
ItemNBTHelper.setBoolean(stack, TAG_SWAPPING, false);
return;
}
int x = ItemNBTHelper.getInt(stack, TAG_SELECT_X, 0);
int y = ItemNBTHelper.getInt(stack, TAG_SELECT_Y, 0);
int z = ItemNBTHelper.getInt(stack, TAG_SELECT_Z, 0);
Block targetBlock = getTargetBlock(stack);
int targetMeta = getTargetBlockMeta(stack);
List<BlockPos> swap = getBlocksToSwap(world, stack, block.getStateFromMeta(meta), new BlockPos(x, y, z), targetBlock.getStateFromMeta(targetMeta));
if(swap.size() == 0) {
ItemNBTHelper.setBoolean(stack, TAG_SWAPPING, false);
return;
}
BlockPos coords = swap.get(world.rand.nextInt(swap.size()));
boolean exchange = exchange(world, player, coords, stack, block.getStateFromMeta(meta));
if(exchange)
ManaItemHandler.requestManaExactForTool(stack, player, COST, true);
else ItemNBTHelper.setBoolean(stack, TAG_SWAPPING, false);
}
}
public List<BlockPos> getBlocksToSwap(World world, ItemStack stack, IBlockState swapState, BlockPos pos, IBlockState targetState) {
// If we have no target block passed in, infer it to be
// the block which the swapping is centered on (presumably the block
// which the player is looking at)
if(targetState == null) {
targetState = world.getBlockState(pos);
}
// Our result list
List<BlockPos> coordsList = new ArrayList<>();
// We subtract 1 from the effective range as the center tile is included
// So, with a range of 3, we are visiting tiles at -2, -1, 0, 1, 2
int effRange = RANGE + ItemNBTHelper.getInt(stack, TAG_EXTRA_RANGE, 1) - 1;
// Iterate in all 3 dimensions through our possible positions.
for(int offsetX = -effRange; offsetX <= effRange; offsetX++)
for(int offsetY = -effRange; offsetY <= effRange; offsetY++)
for(int offsetZ = -effRange; offsetZ <= effRange; offsetZ++) {
BlockPos pos_ = pos.add(offsetX, offsetY, offsetZ);
IBlockState currentState = world.getBlockState(pos_);
// If this block is not our target, ignore it, as we don't need
// to consider replacing it
if(currentState != targetState)
continue;
// If this block is already the block we're swapping to,
// we don't need to swap again
if(currentState == swapState)
continue;
// Check to see if the block is visible on any side:
for(EnumFacing dir : EnumFacing.VALUES) {
BlockPos adjPos = pos_.offset(dir);
IBlockState adjState = world.getBlockState(adjPos);
// If the side of the adjacent block facing this block is
// _not_ solid, then this block is considered "visible"
// and should be replaced.
// If there is a rendering-specific way to check for this,
// that should be placed in preference to this.
if(!adjState.isSideSolid(world, adjPos, dir.getOpposite())) {
coordsList.add(pos_);
break;
}
}
}
return coordsList;
}
public boolean exchange(World world, EntityPlayer player, BlockPos pos, ItemStack stack, IBlockState state) {
TileEntity tile = world.getTileEntity(pos);
if(tile != null)
return false;
ItemStack placeStack = removeFromInventory(player, stack, state.getBlock(), state.getBlock().getMetaFromState(state), false);
if(!placeStack.isEmpty()) {
IBlockState stateAt = world.getBlockState(pos);
Block blockAt = stateAt.getBlock();
if(!blockAt.isAir(world.getBlockState(pos), world, pos) && stateAt.getPlayerRelativeBlockHardness(player, world, pos) > 0 && stateAt != state) {
if(!world.isRemote) {
if(!player.capabilities.isCreativeMode) {
List<ItemStack> drops = blockAt.getDrops(world, pos, stateAt, 0);
for(ItemStack drop : drops)
world.spawnEntity(new EntityItem(world, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, drop));
removeFromInventory(player, stack, state.getBlock(), state.getBlock().getMetaFromState(state), true);
}
world.playEvent(2001, pos, Block.getStateId(state));
world.setBlockState(pos, state, 1 | 2);
state.getBlock().onBlockPlacedBy(world, pos, state, player, placeStack);
}
displayRemainderCounter(player, stack);
return true;
}
}
return false;
}
public boolean canExchange(ItemStack stack) {
Block block = getBlock(stack);
return block != null && block != Blocks.AIR;
}
public static ItemStack removeFromInventory(EntityPlayer player, IItemHandler inv, ItemStack stack, Block block, int meta, boolean doit) {
List<ItemStack> providers = new ArrayList<>();
for(int i = inv.getSlots() - 1; i >= 0; i--) {
ItemStack invStack = inv.getStackInSlot(i);
if(invStack.isEmpty())
continue;
Item item = invStack.getItem();
if(item == Item.getItemFromBlock(block) && invStack.getItemDamage() == meta) {
return inv.extractItem(i, 1, !doit);
}
if(item instanceof IBlockProvider)
providers.add(invStack);
}
for(ItemStack provStack : providers) {
IBlockProvider prov = (IBlockProvider) provStack.getItem();
if(prov.provideBlock(player, stack, provStack, block, meta, doit))
return new ItemStack(block, 1, meta);
}
return ItemStack.EMPTY;
}
public static ItemStack removeFromInventory(EntityPlayer player, ItemStack stack, Block block, int meta, boolean doit) {
if(player.capabilities.isCreativeMode)
return new ItemStack(block, 1, meta);
ItemStack outStack = removeFromInventory(player, BotaniaAPI.internalHandler.getBaublesInventoryWrapped(player), stack, block, meta, doit);
if (outStack.isEmpty())
outStack = removeFromInventory(player, player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null), stack, block, meta, doit);
return outStack;
}
public static int getInventoryItemCount(EntityPlayer player, ItemStack stack, Block block, int meta) {
if(player.capabilities.isCreativeMode)
return -1;
int baubleCount = getInventoryItemCount(player, BotaniaAPI.internalHandler.getBaublesInventoryWrapped(player), stack, block, meta);
if (baubleCount == -1) return -1;
int count = getInventoryItemCount(player, player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, null), stack, block, meta);
if (count == -1) return -1;
return count+baubleCount;
}
public static int getInventoryItemCount(EntityPlayer player, IItemHandler inv, ItemStack stack, Block block, int meta) {
if(player.capabilities.isCreativeMode)
return -1;
int count = 0;
for(int i = 0; i < inv.getSlots(); i++) {
ItemStack invStack = inv.getStackInSlot(i);
if(invStack.isEmpty())
continue;
Item item = invStack.getItem();
if(item == Item.getItemFromBlock(block) && invStack.getItemDamage() == meta)
count += invStack.getCount();
if(item instanceof IBlockProvider) {
IBlockProvider prov = (IBlockProvider) item;
int provCount = prov.getBlockCount(player, stack, invStack, block, meta);
if(provCount == -1)
return -1;
count += provCount;
}
}
return count;
}
public void displayRemainderCounter(EntityPlayer player, ItemStack stack) {
Block block = getBlock(stack);
int meta = getBlockMeta(stack);
int count = getInventoryItemCount(player, stack, block, meta);
if(!player.world.isRemote)
ItemsRemainingRenderHandler.set(new ItemStack(block, 1, meta), count);
}
@Override
public boolean usesMana(ItemStack stack) {
return true;
}
private boolean setBlock(ItemStack stack, Block block, int meta) {
ItemNBTHelper.setString(stack, TAG_BLOCK_NAME, Block.REGISTRY.getNameForObject(block).toString());
ItemNBTHelper.setInt(stack, TAG_BLOCK_META, meta);
return true;
}
@Nonnull
@Override
public String getItemStackDisplayName(@Nonnull ItemStack par1ItemStack) {
Block block = getBlock(par1ItemStack);
int meta = getBlockMeta(par1ItemStack);
return super.getItemStackDisplayName(par1ItemStack) + (block == null ? "" : " (" + TextFormatting.GREEN + new ItemStack(block, 1, meta).getDisplayName() + TextFormatting.RESET + ")");
}
public static String getBlockName(ItemStack stack) {
return ItemNBTHelper.getString(stack, TAG_BLOCK_NAME, "");
}
public static Block getBlock(ItemStack stack) {
Block block = Block.getBlockFromName(getBlockName(stack));
return block;
}
public static int getBlockMeta(ItemStack stack) {
return ItemNBTHelper.getInt(stack, TAG_BLOCK_META, 0);
}
private void setTargetBlock(ItemStack stack, Block block, int meta) {
ItemNBTHelper.setString(stack, TAG_TARGET_BLOCK_NAME, Block.REGISTRY.getNameForObject(block).toString());
ItemNBTHelper.setInt(stack, TAG_TARGET_BLOCK_META, meta);
}
public static String getTargetBlockName(ItemStack stack) {
return ItemNBTHelper.getString(stack, TAG_TARGET_BLOCK_NAME, "");
}
public static Block getTargetBlock(ItemStack stack) {
Block block = Block.getBlockFromName(getTargetBlockName(stack));
return block;
}
public static int getTargetBlockMeta(ItemStack stack) {
return ItemNBTHelper.getInt(stack, TAG_TARGET_BLOCK_META, 0);
}
@Override
@SideOnly(Side.CLIENT)
public List<BlockPos> getWireframesToDraw(EntityPlayer player, ItemStack stack) {
ItemStack holding = player.getHeldItemMainhand();
if(holding != stack || !canExchange(stack))
return ImmutableList.of();
Block block = getBlock(stack);
int meta = getBlockMeta(stack);
RayTraceResult pos = Minecraft.getMinecraft().objectMouseOver;
if(pos != null && pos.getBlockPos() != null) {
BlockPos bPos = pos.getBlockPos();
Block targetBlock = null;
int targetMeta = 0;
if(ItemNBTHelper.getBoolean(stack, TAG_SWAPPING, false)) {
bPos = new BlockPos(
ItemNBTHelper.getInt(stack, TAG_SELECT_X, 0),
ItemNBTHelper.getInt(stack, TAG_SELECT_Y, 0),
ItemNBTHelper.getInt(stack, TAG_SELECT_Z, 0)
);
targetBlock = getTargetBlock(stack);
targetMeta = getTargetBlockMeta(stack);
}
if(!player.world.isAirBlock(bPos)) {
List<BlockPos> coordsList = getBlocksToSwap(player.world, stack, block.getStateFromMeta(meta), bPos, targetBlock == null ? null : targetBlock.getStateFromMeta(targetMeta));
for(BlockPos coords : coordsList)
if(coords.equals(bPos)) {
coordsList.remove(coords);
break;
}
return coordsList;
}
}
return ImmutableList.of();
}
@Override
public BlockPos getSourceWireframe(EntityPlayer player, ItemStack stack) {
return null;
}
}