package choonster.testmod3.tweak.spawnerdrops;
import gnu.trove.map.TIntObjectMap;
import gnu.trove.map.hash.TIntObjectHashMap;
import net.minecraft.block.state.IBlockState;
import net.minecraft.enchantment.EnchantmentHelper;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.init.Enchantments;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.tileentity.TileEntityMobSpawner;
import net.minecraft.util.math.BlockPos;
import net.minecraft.world.World;
import net.minecraftforge.event.world.BlockEvent;
import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import javax.annotation.Nullable;
import java.util.HashMap;
import java.util.Map;
/**
* Allows Mob Spawners to be dropped when broken with a Silk Touch pickaxe.
* <p>
* Test for this thread:
* http://www.minecraftforum.net/forums/mapping-and-modding/minecraft-mods/modification-development/2677402-unable-to-get-current-blocks-tile-entity-metadata
*
* @author Choonster
*/
@Mod.EventBusSubscriber
public class SpawnerDrops {
/**
* Stores the TileEntities of broken spawner blocks between {@link BlockEvent.BreakEvent} and {@link BlockEvent.HarvestDropsEvent}.
* <p>
* The outer map uses the dimension ID as the key and the per-dimension map as the value.
* The inner (per-dimension) maps use the broken block's position as the key and its {@link TileEntity} as the value.
*/
private static final TIntObjectMap<Map<BlockPos, TileEntity>> storedSpawners = new TIntObjectHashMap<>();
/**
* Was the event fired on the server for a mob spawner broken by a player holding a pickaxe?
*
* @param world The block's world
* @param state The block state
* @param player The player who broke the block
* @return Is the event valid?
*/
private static boolean isValid(World world, IBlockState state, @Nullable EntityPlayer player) {
if (player == null) return false;
final ItemStack heldItem = player.getHeldItemMainhand();
return !world.isRemote && // Return true if this is the server,
state.getBlock() == Blocks.MOB_SPAWNER && // The block is a mob spawner,
!heldItem.isEmpty() && heldItem.getItem().getHarvestLevel(heldItem, "pickaxe", player, state) > 0 && // The held item is a pickaxe,
EnchantmentHelper.getEnchantmentLevel(Enchantments.SILK_TOUCH, heldItem) > 0; // And the held item has Silk Touch
}
@SubscribeEvent
public static void blockBroken(BlockEvent.BreakEvent event) {
final World world = event.getWorld();
final BlockPos pos = event.getPos();
// If the event isn't valid, do nothing
if (!isValid(world, event.getState(), event.getPlayer())) return;
// If the TileEntity isn't a mob spawner, do nothing
if (!(world.getTileEntity(pos) instanceof TileEntityMobSpawner)) {
return;
}
final int dimensionID = world.provider.getDimension(); // Get this dimension's ID
if (!storedSpawners.containsKey(dimensionID)) { // If the map for this dimension doesn't exist yet, create it
storedSpawners.put(dimensionID, new HashMap<>());
}
final Map<BlockPos, TileEntity> storedSpawnersInDimension = storedSpawners.get(dimensionID); // Get the map for this dimension
storedSpawnersInDimension.put(pos, world.getTileEntity(pos)); // Store the TileEntity in it
event.setExpToDrop(0); // Don't drop any XP
}
@SubscribeEvent
public static void blockHarvested(BlockEvent.HarvestDropsEvent event) {
final World world = event.getWorld();
final BlockPos pos = event.getPos();
final IBlockState state = event.getState();
// If the event isn't valid, do nothing
if (!isValid(world, state, event.getHarvester())) return;
final int dimensionID = world.provider.getDimension(); // Get this dimension's ID
if (!storedSpawners.containsKey(dimensionID)) { // If the map for this dimension doesn't exist yet, do nothing
return;
}
final Map<BlockPos, TileEntity> storedSpawnersInDimension = storedSpawners.get(dimensionID); // Get the map for this dimension
final TileEntity tileEntity = storedSpawnersInDimension.remove(pos); // Get the stored TileEntity for this position and remove it from the map
if (tileEntity == null) return; // If the TileEntity doesn't exist, do nothing
final NBTTagCompound tileData = tileEntity.serializeNBT(); // Write the TileEntity to NBT
tileData.removeTag("x"); // Remove the coordinate tags so spawners of the same type from different positions stack
tileData.removeTag("y");
tileData.removeTag("z");
final ItemStack droppedItem = new ItemStack(state.getBlock()); // Create an ItemStack of the Block
droppedItem.setTagInfo("BlockEntityTag", tileData); // Store the TileEntity data in the ItemStack
event.getDrops().add(droppedItem); // Add the ItemStack to the drops list
}
public static void serverStopped() {
storedSpawners.clear(); // Clear the map when the server stops
}
}