package com.carpentersblocks.util.handler;
import net.minecraft.block.Block;
import net.minecraft.client.Minecraft;
import net.minecraft.client.audio.PositionedSoundRecord;
import net.minecraft.entity.Entity;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.entity.player.EntityPlayerMP;
import net.minecraft.init.Blocks;
import net.minecraft.item.Item;
import net.minecraft.item.ItemBlock;
import net.minecraft.item.ItemStack;
import net.minecraft.tileentity.TileEntity;
import net.minecraft.util.MathHelper;
import net.minecraft.util.MovingObjectPosition;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.Vec3;
import net.minecraft.world.World;
import net.minecraftforge.client.event.GuiOpenEvent;
import net.minecraftforge.client.event.MouseEvent;
import net.minecraftforge.client.event.sound.PlaySoundEvent17;
import net.minecraftforge.common.util.ForgeDirection;
import net.minecraftforge.event.entity.PlaySoundAtEntityEvent;
import net.minecraftforge.event.entity.living.LivingEvent.LivingUpdateEvent;
import net.minecraftforge.event.entity.player.PlayerInteractEvent;
import net.minecraftforge.event.world.BlockEvent;
import com.carpentersblocks.CarpentersBlocks;
import com.carpentersblocks.api.ICarpentersChisel;
import com.carpentersblocks.api.ICarpentersHammer;
import com.carpentersblocks.block.BlockCoverable;
import com.carpentersblocks.network.PacketActivateBlock;
import com.carpentersblocks.network.PacketSlopeSelect;
import com.carpentersblocks.renderer.helper.ParticleHelper;
import com.carpentersblocks.tileentity.TEBase;
import com.carpentersblocks.util.BlockProperties;
import com.carpentersblocks.util.registry.BlockRegistry;
import cpw.mods.fml.client.FMLClientHandler;
import cpw.mods.fml.common.FMLCommonHandler;
import cpw.mods.fml.common.eventhandler.SubscribeEvent;
import cpw.mods.fml.relauncher.Side;
import cpw.mods.fml.relauncher.SideOnly;
public class EventHandler {
public static float hitX;
public static float hitY;
public static float hitZ;
/** Stores face for onBlockClicked(). */
public static int eventFace;
/** Stores entity that hit block. */
public static EntityPlayer eventEntityPlayer;
@SubscribeEvent
@SideOnly(Side.CLIENT)
/**
* Check render settings on GUI open/close event.
*/
public void onGuiOpenEvent(GuiOpenEvent event)
{
if (event.gui == null) {
if (ShadersHandler.enableShadersModCoreIntegration) {
ShadersHandler.update();
}
}
}
@SubscribeEvent
/**
* Used to prevent block destruction if block is a Carpenter's Block
* and player is holding a Carpenter's tool.
*/
public void onBlockBreakEvent(BlockEvent.BreakEvent event)
{
EntityPlayer entityPlayer = event.getPlayer();
ItemStack itemStack = entityPlayer.getHeldItem();
if (entityPlayer == null || itemStack == null) {
return;
}
Item item = itemStack.getItem();
if (item == null) {
return;
}
if (event.block instanceof BlockCoverable) {
if (entityPlayer.capabilities.isCreativeMode && (item instanceof ICarpentersHammer || item instanceof ICarpentersChisel)) {
event.setCanceled(true);
}
}
}
@SubscribeEvent
/**
* Used to store side clicked and also forces onBlockActivated
* event when entityPlayer is sneaking and activates block with the
* Carpenter's Hammer.
*/
public void onPlayerInteractEvent(PlayerInteractEvent event)
{
if (event.isCanceled()) {
return;
}
Block block = event.entity.worldObj.getBlock(event.x, event.y, event.z);
if (block instanceof BlockCoverable) {
eventFace = event.face;
eventEntityPlayer = event.entityPlayer;
ItemStack itemStack = eventEntityPlayer.getHeldItem();
MovingObjectPosition object = getMovingObjectPositionFromPlayer(eventEntityPlayer.worldObj, eventEntityPlayer);
if (object != null) {
hitX = (float)object.hitVec.xCoord - event.x;
hitY = (float)object.hitVec.yCoord - event.y;
hitZ = (float)object.hitVec.zCoord - event.z;
} else {
hitX = hitY = hitZ = 1.0F;
}
switch (event.action) {
case LEFT_CLICK_BLOCK:
boolean toolEquipped = itemStack != null && (itemStack.getItem() instanceof ICarpentersHammer || itemStack.getItem() instanceof ICarpentersChisel);
/*
* Creative mode doesn't normally invoke onBlockClicked(), but rather it tries
* to destroy the block.
*
* We'll invoke it here when a Carpenter's tool is being held.
*/
if (!event.entity.worldObj.isRemote) {
if (toolEquipped && eventEntityPlayer.capabilities.isCreativeMode) {
block.onBlockClicked(eventEntityPlayer.worldObj, event.x, event.y, event.z, eventEntityPlayer);
}
}
break;
case RIGHT_CLICK_BLOCK:
/*
* To enable full functionality with the hammer, we need to override pretty
* much everything that happens on sneak right-click.
*
* In order to invoke onBlockActivated() server-side, we must send a packet
* from the client.
*
* The server will receive the packet and attempt to alter the Carpenter's
* block. If nothing changes, vanilla behavior will resume - the Item(Block)
* in the ItemStack (if applicable) will be created adjacent to block.
*/
if (eventEntityPlayer.isSneaking()) {
if (!(itemStack != null && itemStack.getItem() instanceof ItemBlock && !BlockProperties.isOverlay(itemStack))) {
event.setCanceled(true); // Normally prevents server event, but sometimes it doesn't, so check below
if (event.entity.worldObj.isRemote) {
PacketHandler.sendPacketToServer(new PacketActivateBlock(event.x, event.y, event.z, event.face));
}
}
}
break;
default: {}
}
}
}
@SideOnly(Side.CLIENT)
@SubscribeEvent
/**
* Grabs mouse scroll events for slope selection.
*/
public void onMouseEvent(MouseEvent event)
{
// We only want to process wheel events
if (event.button < 0)
{
EntityPlayer entityPlayer = Minecraft.getMinecraft().thePlayer;
if (entityPlayer != null && entityPlayer.isSneaking()) {
ItemStack itemStack = entityPlayer.getHeldItem();
if (itemStack != null && itemStack.getItem() instanceof ItemBlock && BlockProperties.toBlock(itemStack).equals(BlockRegistry.blockCarpentersSlope)) {
if (event.dwheel != 0) {
PacketHandler.sendPacketToServer(new PacketSlopeSelect(entityPlayer.inventory.currentItem, event.dwheel > 0));
}
event.setCanceled(true);
}
}
}
}
/**
* Returns the MovingObjectPosition of block hit by player.
* Adapted from protected method of same name in Item.class.
*/
private MovingObjectPosition getMovingObjectPositionFromPlayer(World world, EntityPlayer entityPlayer)
{
double xPos = entityPlayer.prevPosX + (entityPlayer.posX - entityPlayer.prevPosX);
double yPos = entityPlayer.prevPosY + (entityPlayer.posY - entityPlayer.prevPosY) + (world.isRemote ? entityPlayer.getEyeHeight() - entityPlayer.getDefaultEyeHeight() : entityPlayer.getEyeHeight());
double zPos = entityPlayer.prevPosZ + (entityPlayer.posZ - entityPlayer.prevPosZ);
float pitch = entityPlayer.prevRotationPitch + (entityPlayer.rotationPitch - entityPlayer.prevRotationPitch);
float yaw = entityPlayer.prevRotationYaw + (entityPlayer.rotationYaw - entityPlayer.prevRotationYaw);
float commonComp = -MathHelper.cos(-pitch * 0.017453292F);
float xComp = MathHelper.sin(-yaw * 0.017453292F - (float)Math.PI) * commonComp;
float yComp = MathHelper.sin(-pitch * 0.017453292F);
float zComp = MathHelper.cos(-yaw * 0.017453292F - (float)Math.PI) * commonComp;
double reachDist = 5.0D;
if (entityPlayer instanceof EntityPlayerMP) {
reachDist = ((EntityPlayerMP)entityPlayer).theItemInWorldManager.getBlockReachDistance();
}
Vec3 vec1 = Vec3.createVectorHelper(xPos, yPos, zPos);
Vec3 vec2 = vec1.addVector(xComp * reachDist, yComp * reachDist, zComp * reachDist);
return world.rayTraceBlocks(vec1, vec2);
}
@SubscribeEvent
public void onLivingUpdateEvent(LivingUpdateEvent event)
{
Entity entity = event.entityLiving;
World world = entity.worldObj;
/*
* The purpose of the function is to manifest sprint particles
* and adjust slipperiness when entity is moving on block, so check
* that the conditions are met first.
*/
if (!isMovingOnGround(entity))
{
return;
}
TEBase TE = getTileEntityAtFeet(entity);
if (TE != null) {
ItemStack itemStack = BlockProperties.getFeatureSensitiveSideItemStack(TE, ForgeDirection.UP);
/* Spawn sprint particles client-side. */
if (world.isRemote && entity.isSprinting() && !entity.isInWater()) {
ParticleHelper.spawnTileParticleAt(entity, itemStack);
}
/* Adjust block slipperiness according to cover. */
Block block = BlockProperties.toBlock(itemStack);
if (block instanceof BlockCoverable) {
TE.getBlockType().slipperiness = Blocks.dirt.slipperiness;
} else {
TE.getBlockType().slipperiness = block.slipperiness;
}
}
}
/**
* {@link onPlaySoundEvent} is used differently for singleplayer
* and multiplayer sound events. This will try to locate the {@link TEBase}
* that best represents the origin of the sound.
* <p>
* In singleplayer, this is normally the origin since it's used mainly
* for placement and destruction sounds.
* <p>
* In multiplayer, foreign players produce this event for step sounds,
* requiring a y offset of -1 to approximate the origin.
*
* @param world the {@link World}
* @param x the x coordinate
* @param y the y coordinate
* @param z the z coordinate
* @return an approximate {@link TEBase} used for producing a sound
*/
private TEBase getApproximateSoundOrigin(World world, int x, int y, int z)
{
// Try origin first
TileEntity TE = world.getTileEntity(x, y, z);
if (TE != null && TE instanceof TEBase)
{
return (TEBase) TE;
}
else
{
// Try y-offset -1
TileEntity TE_YN = world.getTileEntity(x, y - 1, z);
if (TE_YN != null && TE_YN instanceof TEBase)
{
return (TEBase) TE_YN;
}
}
return null;
}
@SideOnly(Side.CLIENT)
@SubscribeEvent
public void onPlaySoundEvent(PlaySoundEvent17 event)
{
if (event != null && event.name != null && event.name.contains(CarpentersBlocks.MODID))
{
if (FMLCommonHandler.instance().getSide() == Side.CLIENT)
{
World world = FMLClientHandler.instance().getClient().theWorld;
int x = MathHelper.floor_double(event.sound.getXPosF());
int y = MathHelper.floor_double(event.sound.getYPosF());
int z = MathHelper.floor_double(event.sound.getZPosF());
// We'll set a default block type to be safe
Block block = Blocks.planks;
// Grab approximate origin, and gather accurate block type
TEBase TE = getApproximateSoundOrigin(world, x, y, z);
if (TE != null && TE.hasAttribute(TE.ATTR_COVER[6])) {
block = BlockProperties.toBlock(BlockProperties.getCoverSafe(TE, 6));
}
if (event.name.startsWith("step.")) {
event.result = new PositionedSoundRecord(new ResourceLocation(block.stepSound.getStepResourcePath()), block.stepSound.getVolume() * 0.15F, block.stepSound.getPitch(), x + 0.5F, y + 0.5F, z + 0.5F);
} else { // "dig." usually
event.result = new PositionedSoundRecord(new ResourceLocation(block.stepSound.getBreakSound()), (block.stepSound.getVolume() + 1.0F) / 2.0F, block.stepSound.getPitch() * 0.8F, x + 0.5F, y + 0.5F, z + 0.5F);
}
}
}
}
/**
* Override sounds when walking on block.
*
* @param event
*/
@SideOnly(Side.CLIENT)
@SubscribeEvent
public void onPlaySoundAtEntityEvent(PlaySoundAtEntityEvent event)
{
if (event != null && event.name != null && event.name.contains(CarpentersBlocks.MODID))
{
Entity entity = event.entity;
/*
* The function to my knowledge is only used for playing walking sounds
* at entity, so we'll check for the conditions first.
*/
if (!isMovingOnGround(entity))
{
return;
}
TEBase TE = getTileEntityAtFeet(entity);
if (TE != null) {
// Give SoundType a valid resource by default
event.name = Blocks.planks.stepSound.getStepResourcePath();
// Gather accurate SoundType based on block properties
Block block = BlockProperties.toBlock(BlockProperties.getFeatureSensitiveSideItemStack(TE, ForgeDirection.UP));
if (!(block instanceof BlockCoverable))
{
event.name = block.stepSound.getStepResourcePath();
}
}
}
}
/**
* Gets the {@link TEBase} object at player's feet, if one exists.
* <p>
* It is safer to gather the tile entity reference than a block reference.
*
* @param entity
* @return
*/
private TEBase getTileEntityAtFeet(Entity entity)
{
int x = MathHelper.floor_double(entity.posX);
int y = MathHelper.floor_double(entity.posY - 0.20000000298023224D - entity.yOffset);
int z = MathHelper.floor_double(entity.posZ);
TileEntity tileEntity = entity.worldObj.getTileEntity(x, y, z);
if (tileEntity != null && tileEntity instanceof TEBase)
{
return (TEBase) tileEntity;
}
else
{
return null;
}
}
/**
* Determines if the player is moving in the x, z directions on
* solid ground.
*
* @param entity
* @return
*/
private boolean isMovingOnGround(Entity entity)
{
return entity.onGround && (entity.motionX != 0 || entity.motionZ != 0);
}
}