From 766570aeb2e1944a97ace9358bfb52d98531ee64 Mon Sep 17 00:00:00 2001 From: Electroblob <35599699+Electroblob77@users.noreply.github.com> Date: Mon, 9 Apr 2018 17:28:59 +0100 Subject: [PATCH] Fix spell book for crystals trade not working --- .../client/WizardryClientEventHandler.java | 78 +++++++++++++++++++ .../wizardry/entity/living/EntityWizard.java | 8 +- .../wizardry/util/WildcardTradeList.java | 57 ++++++++++++++ 3 files changed, 140 insertions(+), 3 deletions(-) create mode 100644 src/main/java/electroblob/wizardry/util/WildcardTradeList.java diff --git a/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java b/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java index 0576e750..90b5a81c 100644 --- a/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java +++ b/src/main/java/electroblob/wizardry/client/WizardryClientEventHandler.java @@ -1,5 +1,7 @@ package electroblob.wizardry.client; +import java.lang.reflect.Field; + import org.lwjgl.opengl.GL11; import electroblob.wizardry.WizardData; @@ -9,6 +11,7 @@ import electroblob.wizardry.item.ItemSpectralBow; import electroblob.wizardry.item.ItemWand; import electroblob.wizardry.packet.PacketControlInput; import electroblob.wizardry.packet.WizardryPacketHandler; +import electroblob.wizardry.registry.WizardryItems; import electroblob.wizardry.registry.WizardryPotions; import electroblob.wizardry.spell.Flight; import electroblob.wizardry.spell.ShadowWard; @@ -17,19 +20,27 @@ import electroblob.wizardry.tileentity.ContainerArcaneWorkbench; import electroblob.wizardry.util.WandHelper; import electroblob.wizardry.util.WizardryUtilities; import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiMerchant; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.gui.inventory.GuiContainer; import net.minecraft.client.renderer.BufferBuilder; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.client.renderer.RenderItem; import net.minecraft.client.renderer.Tessellator; import net.minecraft.client.renderer.entity.RenderManager; import net.minecraft.client.renderer.vertex.DefaultVertexFormats; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.item.EntityArmorStand; import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.inventory.Slot; import net.minecraft.item.ItemStack; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.RayTraceResult; +import net.minecraft.village.MerchantRecipe; import net.minecraftforge.client.event.FOVUpdateEvent; +import net.minecraftforge.client.event.GuiContainerEvent; import net.minecraftforge.client.event.MouseEvent; import net.minecraftforge.client.event.RenderGameOverlayEvent; import net.minecraftforge.client.event.RenderLivingEvent; @@ -39,6 +50,7 @@ import net.minecraftforge.client.event.TextureStitchEvent; import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.eventhandler.SubscribeEvent; import net.minecraftforge.fml.common.network.simpleimpl.IMessage; +import net.minecraftforge.fml.relauncher.ReflectionHelper; import net.minecraftforge.fml.relauncher.Side; /** @@ -59,6 +71,12 @@ public final class WizardryClientEventHandler { private static final ResourceLocation pointerTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/pointer.png"); private static final ResourceLocation targetPointerTexture = new ResourceLocation(Wizardry.MODID, "textures/entity/target_pointer.png"); + private static final Field ITEM_RENDERER; + + static { + ITEM_RENDERER = ReflectionHelper.findField(GuiScreen.class, "itemRender", ""); + } + @SubscribeEvent public static void onTextureStitchEvent(TextureStitchEvent.Pre event){ event.getMap().registerSprite(ContainerArcaneWorkbench.EMPTY_SLOT_CRYSTAL); @@ -115,6 +133,66 @@ public final class WizardryClientEventHandler { event.setNewfov(event.getFov() * 1.0F - maxUseSeconds * 0.15F); } } + + // Brute-force fix for crystals not showing up when a wizard is given a spell book in the trade GUI. + @SubscribeEvent + public static void onGuiDrawForegroundEvent(GuiContainerEvent.DrawForeground event){ + + if(event.getGuiContainer() instanceof GuiMerchant){ + + GuiMerchant gui = (GuiMerchant)event.getGuiContainer(); + // Note that gui.getMerchant() returns an NpcMerchant, not an EntityWizard. + + // Using == the specific item rather than instanceof because that's how trades do it. + if(gui.inventorySlots.getSlot(0).getStack().getItem() == WizardryItems.spell_book + || gui.inventorySlots.getSlot(1).getStack().getItem() == WizardryItems.spell_book){ + + for(MerchantRecipe trade : gui.getMerchant().getRecipes(Minecraft.getMinecraft().player)){ + if(trade.getItemToBuy().getItem() == WizardryItems.spell_book && trade.getSecondItemToBuy().isEmpty()){ + Slot slot = gui.inventorySlots.getSlot(2); + // Uses reflection to draw the itemstack + // It still doesn't look quite right because the slot highlight is behind the item, but it'll do + // until/unless I find a better solution. + renderItemAndTooltip(gui, trade.getItemToSell(), slot.xPos, slot.yPos, event.getMouseX(), event.getMouseY(), + gui.getSlotUnderMouse() == slot); + } + } + } + } + } + + private static void renderItemAndTooltip(GuiContainer gui, ItemStack stack, int x, int y, int mouseX, int mouseY, boolean tooltip){ + + try { + + RenderItem renderItem = (RenderItem)ITEM_RENDERER.get(gui); + GlStateManager.pushMatrix(); + RenderHelper.enableGUIStandardItemLighting(); + GlStateManager.disableLighting(); + GlStateManager.enableRescaleNormal(); + GL11.glEnable(GL11.GL_COLOR_MATERIAL); + GlStateManager.enableLighting(); + renderItem.zLevel = 100.0F; + + if(!stack.isEmpty()){ + renderItem.renderItemAndEffectIntoGUI(stack, x, y); + renderItem.renderItemOverlays(Minecraft.getMinecraft().fontRenderer, stack, x, y); + + if(tooltip){ + gui.drawHoveringText(gui.getItemToolTip(stack), mouseX + gui.getXSize()/2 - gui.width/2, + mouseY + gui.getYSize()/2 - gui.height/2); + } + } + + GlStateManager.popMatrix(); + GlStateManager.enableLighting(); + GL11.glEnable(GL11.GL_DEPTH_TEST); + RenderHelper.enableStandardItemLighting(); + + } catch (Exception e){ + e.printStackTrace(); + } + } // Third person @SubscribeEvent diff --git a/src/main/java/electroblob/wizardry/entity/living/EntityWizard.java b/src/main/java/electroblob/wizardry/entity/living/EntityWizard.java index 7f8b5cc1..b34c5a5f 100644 --- a/src/main/java/electroblob/wizardry/entity/living/EntityWizard.java +++ b/src/main/java/electroblob/wizardry/entity/living/EntityWizard.java @@ -23,6 +23,7 @@ import electroblob.wizardry.registry.WizardrySounds; import electroblob.wizardry.spell.Spell; import electroblob.wizardry.util.SpellModifiers; import electroblob.wizardry.util.WandHelper; +import electroblob.wizardry.util.WildcardTradeList; import electroblob.wizardry.util.WizardryParticleType; import electroblob.wizardry.util.WizardryUtilities; import io.netty.buffer.ByteBuf; @@ -91,6 +92,7 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit * are overriding something, in which case it should be fine). */ // Extending EntityVillager turned out to be a pretty neat thing to do, since now zombies will attack wizards + // TODO: Perhaps we should be implementing IMerchant now instead? private EntityAIAttackSpell spellCastingAI = new EntityAIAttackSpell(this, 0.5D, 14.0F, 30, 50); @@ -369,7 +371,7 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit if(nbt.hasKey("trades")){ NBTTagCompound nbttagcompound1 = nbt.getCompoundTag("trades"); - this.trades = new MerchantRecipeList(nbttagcompound1); + this.trades = new WildcardTradeList(nbttagcompound1); } this.setElement(Element.values()[nbt.getInteger("element")]); @@ -428,7 +430,7 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit if(this.trades == null){ - this.trades = new MerchantRecipeList(); + this.trades = new WildcardTradeList(); // All wizards will buy spell books ItemStack anySpellBook = new ItemStack(WizardryItems.spell_book, 1, OreDictionary.WILDCARD_VALUE); @@ -509,7 +511,7 @@ public class EntityWizard extends EntityVillager implements ISpellCaster, IEntit Collections.shuffle(merchantrecipelist); if(this.trades == null){ - this.trades = new MerchantRecipeList(); + this.trades = new WildcardTradeList(); } for(int j1 = 0; j1 < merchantrecipelist.size(); ++j1){ diff --git a/src/main/java/electroblob/wizardry/util/WildcardTradeList.java b/src/main/java/electroblob/wizardry/util/WildcardTradeList.java new file mode 100644 index 00000000..d7a60a2a --- /dev/null +++ b/src/main/java/electroblob/wizardry/util/WildcardTradeList.java @@ -0,0 +1,57 @@ +package electroblob.wizardry.util; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import net.minecraft.nbt.NBTUtil; +import net.minecraft.village.MerchantRecipe; +import net.minecraft.village.MerchantRecipeList; +import net.minecraftforge.oredict.OreDictionary; + +/** Custom version of {@link MerchantRecipeList} which allows wildcard recipes (i.e. trades which accept items with any + * damage value). Function is otherwise identical. For some reason this feature was removed in 1.11. + * @author Electroblob + * @since Wizardry 4.1 */ +@SuppressWarnings("serial") +public class WildcardTradeList extends MerchantRecipeList { + + public WildcardTradeList(){ + super(); + } + + public WildcardTradeList(NBTTagCompound tag){ + super(tag); + } + + @Override + public MerchantRecipe canRecipeBeUsed(ItemStack offer1, ItemStack offer2, int index){ + + if(index > 0 && index < this.size()){ + + MerchantRecipe merchantrecipe1 = (MerchantRecipe)this.get(index); + return !this.areItemStacksExactlyEqual(offer1, merchantrecipe1.getItemToBuy()) || (!offer2.isEmpty() || merchantrecipe1.hasSecondItemToBuy()) && (!merchantrecipe1.hasSecondItemToBuy() || !this.areItemStacksExactlyEqual(offer2, merchantrecipe1.getSecondItemToBuy())) || offer1.getCount() < merchantrecipe1.getItemToBuy().getCount() || merchantrecipe1.hasSecondItemToBuy() && offer2.getCount() < merchantrecipe1.getSecondItemToBuy().getCount() ? null : merchantrecipe1; + + }else{ + + for(int i = 0; i < this.size(); ++i){ + + MerchantRecipe merchantrecipe = (MerchantRecipe)this.get(i); + + if (this.areItemStacksExactlyEqual(offer1, merchantrecipe.getItemToBuy()) && offer1.getCount() >= merchantrecipe.getItemToBuy().getCount() && (!merchantrecipe.hasSecondItemToBuy() && offer2.isEmpty() || merchantrecipe.hasSecondItemToBuy() && this.areItemStacksExactlyEqual(offer2, merchantrecipe.getSecondItemToBuy()) && offer2.getCount() >= merchantrecipe.getSecondItemToBuy().getCount())){ + return merchantrecipe; + } + } + + return null; + } + } + + private boolean areItemStacksExactlyEqual(ItemStack stack1, ItemStack stack2){ + // Added to allow wildcards; this line is the only actual change. + if((stack1.getItemDamage() == OreDictionary.WILDCARD_VALUE || stack2.getItemDamage() == OreDictionary.WILDCARD_VALUE) + // Can't use ItemStack.areItemsEqualIgnoreDurability because that only works for items with durability, not subtypes. + && stack1.getItem() == stack2.getItem()) return true; + + return ItemStack.areItemsEqual(stack1, stack2) && (!stack2.hasTagCompound() || stack1.hasTagCompound() && NBTUtil.areNBTEquals(stack2.getTagCompound(), stack1.getTagCompound(), false)); + } + +}