/**
* 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 @ [Dec 15, 2014, 3:53:30 PM (GMT)]
*/
package vazkii.botania.common.item;
import javax.annotation.Nonnull;
import org.lwjgl.opengl.GL11;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.Gui;
import net.minecraft.client.gui.ScaledResolution;
import net.minecraft.client.renderer.GlStateManager;
import net.minecraft.client.renderer.RenderHelper;
import net.minecraft.client.renderer.Tessellator;
import net.minecraft.client.renderer.block.model.ItemCameraTransforms;
import net.minecraft.client.renderer.texture.TextureMap;
import net.minecraft.client.renderer.vertex.DefaultVertexFormats;
import net.minecraft.client.resources.I18n;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityLivingBase;
import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.init.Blocks;
import net.minecraft.inventory.ContainerWorkbench;
import net.minecraft.inventory.IInventory;
import net.minecraft.inventory.InventoryCrafting;
import net.minecraft.item.ItemStack;
import net.minecraft.item.crafting.CraftingManager;
import net.minecraft.nbt.NBTTagCompound;
import net.minecraft.stats.Achievement;
import net.minecraft.util.ActionResult;
import net.minecraft.util.EnumActionResult;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.text.TextFormatting;
import net.minecraft.world.World;
import net.minecraftforge.client.event.RenderWorldLastEvent;
import net.minecraftforge.common.MinecraftForge;
import net.minecraftforge.fml.common.eventhandler.SubscribeEvent;
import net.minecraftforge.fml.common.gameevent.PlayerEvent.ItemCraftedEvent;
import net.minecraftforge.fml.relauncher.Side;
import net.minecraftforge.fml.relauncher.SideOnly;
import net.minecraftforge.items.CapabilityItemHandler;
import net.minecraftforge.items.IItemHandler;
import net.minecraftforge.items.ItemHandlerHelper;
import net.minecraftforge.items.ItemStackHandler;
import vazkii.botania.client.core.handler.ClientMethodHandles;
import vazkii.botania.client.core.handler.ClientTickHandler;
import vazkii.botania.client.gui.crafting.InventoryCraftingHalo;
import vazkii.botania.client.lib.LibResources;
import vazkii.botania.common.Botania;
import vazkii.botania.common.achievement.ICraftAchievement;
import vazkii.botania.common.achievement.ModAchievements;
import vazkii.botania.common.core.helper.ItemNBTHelper;
import vazkii.botania.common.core.helper.PlayerHelper;
import vazkii.botania.common.core.helper.Vector3;
import vazkii.botania.common.lib.LibGuiIDs;
import vazkii.botania.common.lib.LibItemNames;
import java.util.Arrays;
public class ItemCraftingHalo extends ItemMod implements ICraftAchievement {
private static final ResourceLocation glowTexture = new ResourceLocation(LibResources.MISC_GLOW_GREEN);
private static final ItemStack craftingTable = new ItemStack(Blocks.CRAFTING_TABLE);
public static final int SEGMENTS = 12;
private static final String TAG_LAST_CRAFTING = "lastCrafting";
private static final String TAG_STORED_RECIPE_PREFIX = "storedRecipe";
private static final String TAG_ITEM_PREFIX = "item";
private static final String TAG_EQUIPPED = "equipped";
private static final String TAG_ROTATION_BASE = "rotationBase";
public ItemCraftingHalo() {
this(LibItemNames.CRAFTING_HALO);
MinecraftForge.EVENT_BUS.register(this);
}
public ItemCraftingHalo(String name) {
super(name);
setMaxStackSize(1);
}
@Nonnull
@Override
public ActionResult<ItemStack> onItemRightClick(World world, EntityPlayer player, @Nonnull EnumHand hand) {
ItemStack stack = player.getHeldItem(hand);
int segment = getSegmentLookedAt(stack, player);
ItemStack itemForPos = getItemForSlot(stack, segment);
if(segment == 0)
player.openGui(Botania.instance, LibGuiIDs.CRAFTING_HALO, world, hand == EnumHand.OFF_HAND ? 1 : 0, 0, 0);
else {
if(itemForPos.isEmpty())
assignRecipe(stack, itemForPos, segment);
else tryCraft(player, stack, segment, true, getFakeInv(player), true);
}
return ActionResult.newResult(EnumActionResult.SUCCESS, stack);
}
public static IItemHandler getFakeInv(EntityPlayer player) {
ItemStackHandler ret = new ItemStackHandler(player.inventory.mainInventory.size());
for(int i = 0; i < player.inventory.mainInventory.size(); i++) {
ret.setStackInSlot(i, player.inventory.mainInventory.get(i).copy());
}
return ret;
}
@Override
public void onUpdate(ItemStack stack, World world, Entity entity, int pos, boolean equipped) {
if (!(entity instanceof EntityLivingBase))
return;
EntityLivingBase living = (EntityLivingBase) entity;
boolean eqLastTick = wasEquipped(stack);
if (!equipped && living.getHeldItemOffhand() == stack)
equipped = true;
if(eqLastTick != equipped)
setEquipped(stack, equipped);
if(!equipped) {
int angles = 360;
int segAngles = angles / SEGMENTS;
float shift = segAngles / 2;
setRotationBase(stack, getCheckingAngle((EntityLivingBase) entity) - shift);
}
}
void tryCraft(EntityPlayer player, ItemStack stack, int slot, boolean particles, IItemHandler inv, boolean validate) {
ItemStack itemForPos = getItemForSlot(stack, slot);
if(itemForPos.isEmpty())
return;
ItemStack[] recipe = getCraftingItems(stack, slot);
if(validate)
recipe = validateRecipe(player, stack, recipe, slot);
if(canCraft(recipe, inv))
doCraft(player, recipe, particles);
}
private static ItemStack[] validateRecipe(EntityPlayer player, ItemStack stack, ItemStack[] recipe, int slot) {
InventoryCrafting fakeInv = new InventoryCrafting(new ContainerWorkbench(player.inventory, player.world, BlockPos.ORIGIN), 3, 3);
for(int i = 0; i < 9; i++)
fakeInv.setInventorySlotContents(i, recipe[i]);
ItemStack result = CraftingManager.getInstance().findMatchingRecipe(fakeInv, player.world);
if(result.isEmpty()) {
assignRecipe(stack, recipe[9], slot);
return null;
}
if(!result.isItemEqual(recipe[9]) || result.getCount() != recipe[9].getCount() || !ItemStack.areItemStackTagsEqual(recipe[9], result)) {
assignRecipe(stack, recipe[9], slot);
return null;
}
return recipe;
}
private static boolean canCraft(ItemStack[] recipe, IItemHandler inv) {
if(recipe == null)
return false;
if(!ItemHandlerHelper.insertItemStacked(inv, recipe[9], true).isEmpty())
return false;
return consumeRecipeIngredients(recipe, inv, null);
}
private static void doCraft(EntityPlayer player, ItemStack[] recipe, boolean particles) {
consumeRecipeIngredients(recipe, player.getCapability(CapabilityItemHandler.ITEM_HANDLER_CAPABILITY, EnumFacing.UP), player);
ItemHandlerHelper.giveItemToPlayer(player, recipe[9]);
if(!particles)
return;
Vec3d lookVec3 = player.getLookVec();
Vector3 centerVector = Vector3.fromEntityCenter(player).add(lookVec3.xCoord * 3, 1.3, lookVec3.zCoord * 3);
float m = 0.1F;
for(int i = 0; i < 4; i++)
Botania.proxy.wispFX(centerVector.x, centerVector.y, centerVector.z, 1F, 0F, 1F, 0.2F + 0.2F * (float) Math.random(), ((float) Math.random() - 0.5F) * m, ((float) Math.random() - 0.5F) * m, ((float) Math.random() - 0.5F) * m);
}
private static boolean consumeRecipeIngredients(ItemStack[] recipe, IItemHandler inv, EntityPlayer player) {
for(int i = 0; i < 9; i++) {
ItemStack ingredient = recipe[i];
if(!ingredient.isEmpty() && !consumeFromInventory(ingredient, inv, player))
return false;
}
return true;
}
private static boolean consumeFromInventory(ItemStack stack, IItemHandler inv, EntityPlayer player) {
for(int i = 0; i < inv.getSlots(); i++) {
ItemStack stackAt = inv.getStackInSlot(i);
if(!stackAt.isEmpty() && stack.isItemEqual(stackAt) && ItemStack.areItemStackTagsEqual(stack, stackAt)) {
boolean consume = true;
ItemStack container = stackAt.getItem().getContainerItem(stackAt);
if(!container.isEmpty()) {
if(container == stackAt)
consume = false;
else {
if(player == null)
ItemHandlerHelper.insertItem(inv, container, false);
else
ItemHandlerHelper.giveItemToPlayer(player, container);
}
}
if(consume)
inv.extractItem(i, 1, false);
return true;
}
}
return false;
}
@Override
public boolean onEntitySwing(EntityLivingBase player, ItemStack stack) {
int segment = getSegmentLookedAt(stack, player);
if(segment == 0)
return false;
ItemStack itemForPos = getItemForSlot(stack, segment);
if(!itemForPos.isEmpty() && player.isSneaking()) {
assignRecipe(stack, itemForPos, segment);
return true;
}
return false;
}
private static int getSegmentLookedAt(ItemStack stack, EntityLivingBase player) {
getRotationBase(stack);
float yaw = getCheckingAngle(player, getRotationBase(stack));
int angles = 360;
int segAngles = angles / SEGMENTS;
for(int seg = 0; seg < SEGMENTS; seg++) {
float calcAngle = (float) seg * segAngles;
if(yaw >= calcAngle && yaw < calcAngle + segAngles)
return seg;
}
return -1;
}
private static float getCheckingAngle(EntityLivingBase player) {
return getCheckingAngle(player, 0F);
}
// Screw the way minecraft handles rotation
// Really...
private static float getCheckingAngle(EntityLivingBase player, float base) {
float yaw = MathHelper.wrapDegrees(player.rotationYaw) + 90F;
int angles = 360;
int segAngles = angles / SEGMENTS;
float shift = segAngles / 2;
if(yaw < 0)
yaw = 180F + (180F + yaw);
yaw -= 360F - base;
float angle = 360F - yaw + shift;
if(angle < 0)
angle = 360F + angle;
return angle;
}
public static ItemStack getItemForSlot(ItemStack stack, int slot) {
if(slot == 0)
return craftingTable;
else if(slot >= SEGMENTS)
return ItemStack.EMPTY;
else {
NBTTagCompound cmp = getStoredRecipeCompound(stack, slot);
if(cmp != null) {
ItemStack cmpStack = getLastCraftingItem(cmp, 9);
return cmpStack;
} else return ItemStack.EMPTY;
}
}
public static void assignRecipe(ItemStack stack, ItemStack itemForPos, int pos) {
if(!itemForPos.isEmpty())
ItemNBTHelper.setCompound(stack, TAG_STORED_RECIPE_PREFIX + pos, new NBTTagCompound());
else
ItemNBTHelper.setCompound(stack, TAG_STORED_RECIPE_PREFIX + pos, getLastCraftingCompound(stack, false));
}
@SubscribeEvent
public void onItemCrafted(ItemCraftedEvent event) {
if(!(event.craftMatrix instanceof InventoryCraftingHalo))
return;
for(int i = 0; i < event.player.inventory.getSizeInventory(); i++) {
ItemStack stack = event.player.inventory.getStackInSlot(i);
if(!stack.isEmpty() && stack.getItem() instanceof ItemCraftingHalo)
saveRecipeToStack(event, stack);
}
}
private void saveRecipeToStack(ItemCraftedEvent event, ItemStack stack) {
NBTTagCompound cmp = new NBTTagCompound();
NBTTagCompound cmp1 = new NBTTagCompound();
ItemStack result = CraftingManager.getInstance().findMatchingRecipe((InventoryCrafting) event.craftMatrix, event.player.world);
if(!result.isEmpty()) {
cmp1 = result.writeToNBT(cmp1);
cmp.setTag(TAG_ITEM_PREFIX + 9, cmp1);
for(int i = 0; i < 9; i++) {
cmp1 = new NBTTagCompound();
ItemStack stackSlot = event.craftMatrix.getStackInSlot(i);
if(!stackSlot.isEmpty()) {
ItemStack writeStack = stackSlot.copy();
writeStack.setCount(1);
cmp1 = writeStack.writeToNBT(cmp1);
}
cmp.setTag(TAG_ITEM_PREFIX + i, cmp1);
}
}
ItemNBTHelper.setCompound(stack, TAG_LAST_CRAFTING, cmp);
}
public static ItemStack[] getLastCraftingItems(ItemStack stack) {
return getCraftingItems(stack, SEGMENTS);
}
public static ItemStack[] getCraftingItems(ItemStack stack, int slot) {
ItemStack[] stackArray = new ItemStack[10];
Arrays.fill(stackArray, ItemStack.EMPTY);
NBTTagCompound cmp = getStoredRecipeCompound(stack, slot);
if(cmp != null)
for(int i = 0; i < stackArray.length; i++)
stackArray[i] = getLastCraftingItem(cmp, i);
return stackArray;
}
public static NBTTagCompound getLastCraftingCompound(ItemStack stack, boolean nullify) {
return ItemNBTHelper.getCompound(stack, TAG_LAST_CRAFTING, nullify);
}
public static NBTTagCompound getStoredRecipeCompound(ItemStack stack, int slot) {
return slot == SEGMENTS ? getLastCraftingCompound(stack, true) : ItemNBTHelper.getCompound(stack, TAG_STORED_RECIPE_PREFIX + slot, true);
}
public static ItemStack getLastCraftingItem(ItemStack stack, int pos) {
return getLastCraftingItem(getLastCraftingCompound(stack, true), pos);
}
public static ItemStack getLastCraftingItem(NBTTagCompound cmp, int pos) {
if(cmp == null)
return ItemStack.EMPTY;
NBTTagCompound cmp1 = cmp.getCompoundTag(TAG_ITEM_PREFIX + pos);
if(cmp1 == null)
return ItemStack.EMPTY;
return new ItemStack(cmp1);
}
public static boolean wasEquipped(ItemStack stack) {
return ItemNBTHelper.getBoolean(stack, TAG_EQUIPPED, false);
}
public static void setEquipped(ItemStack stack, boolean equipped) {
ItemNBTHelper.setBoolean(stack, TAG_EQUIPPED, equipped);
}
public static float getRotationBase(ItemStack stack) {
return ItemNBTHelper.getFloat(stack, TAG_ROTATION_BASE, 0F);
}
public static void setRotationBase(ItemStack stack, float rotation) {
ItemNBTHelper.setFloat(stack, TAG_ROTATION_BASE, rotation);
}
@SideOnly(Side.CLIENT)
@SubscribeEvent
public void onRenderWorldLast(RenderWorldLastEvent event) {
EntityPlayer player = Minecraft.getMinecraft().player;
ItemStack stack = PlayerHelper.getFirstHeldItemClass(player, ItemCraftingHalo.class);
if(!stack.isEmpty())
render(stack, player, event.getPartialTicks());
}
@SideOnly(Side.CLIENT)
public void render(ItemStack stack, EntityPlayer player, float partialTicks) {
Minecraft mc = Minecraft.getMinecraft();
Tessellator tess = Tessellator.getInstance();
double renderPosX, renderPosY, renderPosZ;
try {
renderPosX = (double) ClientMethodHandles.renderPosX_getter.invokeExact(Minecraft.getMinecraft().getRenderManager());
renderPosY = (double) ClientMethodHandles.renderPosY_getter.invokeExact(Minecraft.getMinecraft().getRenderManager());
renderPosZ = (double) ClientMethodHandles.renderPosZ_getter.invokeExact(Minecraft.getMinecraft().getRenderManager());
} catch (Throwable t) {
t.printStackTrace();
return;
}
GlStateManager.pushMatrix();
GlStateManager.enableBlend();
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
float alpha = ((float) Math.sin((ClientTickHandler.ticksInGame + partialTicks) * 0.2F) * 0.5F + 0.5F) * 0.4F + 0.3F;
double posX = player.prevPosX + (player.posX - player.prevPosX) * partialTicks;
double posY = player.prevPosY + (player.posY - player.prevPosY) * partialTicks;
double posZ = player.prevPosZ + (player.posZ - player.prevPosZ) * partialTicks;
GlStateManager.translate(posX - renderPosX, posY - renderPosY + player.getDefaultEyeHeight(), posZ - renderPosZ);
float base = getRotationBase(stack);
int angles = 360;
int segAngles = angles / SEGMENTS;
float shift = base - segAngles / 2;
float u = 1F;
float v = 0.25F;
float s = 3F;
float m = 0.8F;
float y = v * s * 2;
float y0 = 0;
int segmentLookedAt = getSegmentLookedAt(stack, player);
for(int seg = 0; seg < SEGMENTS; seg++) {
boolean inside = false;
float rotationAngle = (seg + 0.5F) * segAngles + shift;
GlStateManager.pushMatrix();
GlStateManager.rotate(rotationAngle, 0F, 1F, 0F);
GlStateManager.translate(s * m, -0.75F, 0F);
if(segmentLookedAt == seg)
inside = true;
ItemStack slotStack = getItemForSlot(stack, seg);
if(!slotStack.isEmpty()) {
mc.renderEngine.bindTexture(TextureMap.LOCATION_BLOCKS_TEXTURE);
RenderHelper.enableStandardItemLighting();
float scale = seg == 0 ? 0.9F : 0.8F;
GlStateManager.scale(scale, scale, scale);
GlStateManager.rotate(180F, 0F, 1F, 0F);
GlStateManager.translate(seg == 0 ? 0.5F : 0F, seg == 0 ? -0.1F : 0.6F, 0F);
GlStateManager.rotate(90.0F, 0.0F, 1.0F, 0.0F);
Minecraft.getMinecraft().getRenderItem().renderItem(slotStack, ItemCameraTransforms.TransformType.GUI);
RenderHelper.disableStandardItemLighting();
}
GlStateManager.popMatrix();
GlStateManager.pushMatrix();
GlStateManager.rotate(180F, 1F, 0F, 0F);
float a = alpha;
if(inside) {
a += 0.3F;
y0 = -y;
}
GlStateManager.enableBlend();
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
if(seg % 2 == 0)
GlStateManager.color(0.6F, 0.6F, 0.6F, a);
else GlStateManager.color(1F, 1F, 1F, a);
GlStateManager.disableCull();
ItemCraftingHalo item = (ItemCraftingHalo) stack.getItem();
mc.renderEngine.bindTexture(item.getGlowResource());
tess.getBuffer().begin(GL11.GL_QUADS, DefaultVertexFormats.POSITION_TEX);
for(int i = 0; i < segAngles; i++) {
float ang = i + seg * segAngles + shift;
double xp = Math.cos(ang * Math.PI / 180F) * s;
double zp = Math.sin(ang * Math.PI / 180F) * s;
tess.getBuffer().pos(xp * m, y, zp * m).tex(u, v).endVertex();
tess.getBuffer().pos(xp, y0, zp).tex(u, 0).endVertex();
xp = Math.cos((ang + 1) * Math.PI / 180F) * s;
zp = Math.sin((ang + 1) * Math.PI / 180F) * s;
tess.getBuffer().pos(xp, y0, zp).tex(0, 0).endVertex();
tess.getBuffer().pos(xp * m, y, zp * m).tex(0, v).endVertex();
}
y0 = 0;
tess.draw();
GlStateManager.enableCull();
GlStateManager.popMatrix();
}
GlStateManager.popMatrix();
}
@SideOnly(Side.CLIENT)
public ResourceLocation getGlowResource() {
return glowTexture;
}
@SideOnly(Side.CLIENT)
public static void renderHUD(ScaledResolution resolution, EntityPlayer player, ItemStack stack) {
Minecraft mc = Minecraft.getMinecraft();
int slot = getSegmentLookedAt(stack, player);
GlStateManager.enableBlend();
GlStateManager.blendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA);
if(slot == 0) {
String name = craftingTable.getDisplayName();
int l = mc.fontRendererObj.getStringWidth(name);
int x = resolution.getScaledWidth() / 2 - l / 2;
int y = resolution.getScaledHeight() / 2 - 65;
Gui.drawRect(x - 6, y - 6, x + l + 6, y + 37, 0x22000000);
Gui.drawRect(x - 4, y - 4, x + l + 4, y + 35, 0x22000000);
net.minecraft.client.renderer.RenderHelper.enableGUIStandardItemLighting();
GlStateManager.enableRescaleNormal();
mc.getRenderItem().renderItemAndEffectIntoGUI(craftingTable, resolution.getScaledWidth() / 2 - 8, resolution.getScaledHeight() / 2 - 52);
net.minecraft.client.renderer.RenderHelper.disableStandardItemLighting();
mc.fontRendererObj.drawStringWithShadow(name, x, y, 0xFFFFFF);
} else {
ItemStack[] recipe = getCraftingItems(stack, slot);
String label = I18n.format("botaniamisc.unsetRecipe");
boolean setRecipe = false;
if(recipe[9].isEmpty())
recipe = getCraftingItems(stack, SEGMENTS);
else {
label = recipe[9].getDisplayName();
setRecipe = true;
}
renderRecipe(resolution, label, recipe, player, setRecipe);
}
}
@SideOnly(Side.CLIENT)
public static void renderRecipe(ScaledResolution resolution, String label, ItemStack[] recipe, EntityPlayer player, boolean setRecipe) {
Minecraft mc = Minecraft.getMinecraft();
if(!recipe[9].isEmpty()) {
int x = resolution.getScaledWidth() / 2 - 45;
int y = resolution.getScaledHeight() / 2 - 90;
Gui.drawRect(x - 6, y - 6, x + 90 + 6, y + 60, 0x22000000);
Gui.drawRect(x - 4, y - 4, x + 90 + 4, y + 58, 0x22000000);
Gui.drawRect(x + 66, y + 14, x + 92, y + 40, 0x22000000);
Gui.drawRect(x - 2, y - 2, x + 56, y + 56, 0x22000000);
net.minecraft.client.renderer.RenderHelper.enableGUIStandardItemLighting();
GlStateManager.enableRescaleNormal();
for(int i = 0; i < 9; i++) {
ItemStack stack = recipe[i];
if(!stack.isEmpty()) {
int xpos = x + i % 3 * 18;
int ypos = y + i / 3 * 18;
Gui.drawRect(xpos, ypos, xpos + 16, ypos + 16, 0x22000000);
mc.getRenderItem().renderItemAndEffectIntoGUI(stack, xpos, ypos);
}
}
mc.getRenderItem().renderItemAndEffectIntoGUI(recipe[9], x + 72, y + 18);
mc.getRenderItem().renderItemOverlays(mc.fontRendererObj, recipe[9], x + 72, y + 18);
net.minecraft.client.renderer.RenderHelper.disableStandardItemLighting();
}
int yoff = 110;
if(setRecipe && !canCraft(recipe, getFakeInv(player))) {
String warning = TextFormatting.RED + I18n.format("botaniamisc.cantCraft");
mc.fontRendererObj.drawStringWithShadow(warning, resolution.getScaledWidth() / 2 - mc.fontRendererObj.getStringWidth(warning) / 2, resolution.getScaledHeight() / 2 - yoff, 0xFFFFFF);
yoff += 12;
}
mc.fontRendererObj.drawStringWithShadow(label, resolution.getScaledWidth() / 2 - mc.fontRendererObj.getStringWidth(label) / 2, resolution.getScaledHeight() / 2 - yoff, 0xFFFFFF);
}
@Override
public Achievement getAchievementOnCraft(ItemStack stack, EntityPlayer player, IInventory matrix) {
return ModAchievements.craftingHaloCraft;
}
}