diff --git a/src/main/java/noppes/npcs/CommonProxy.java b/src/main/java/noppes/npcs/CommonProxy.java index a799a8938..1d45e4cee 100644 --- a/src/main/java/noppes/npcs/CommonProxy.java +++ b/src/main/java/noppes/npcs/CommonProxy.java @@ -64,9 +64,12 @@ public class CommonProxy implements IGuiHandler { if(gui == EnumGuiType.PlayerTrader) return new ContainerNPCTrader(npc, player); - if(gui == EnumGuiType.PlayerAnvil) + if(gui == EnumGuiType.PlayerCarpentryBench) return new ContainerCarpentryBench(player.inventory, player.worldObj, x, y, z); + if(gui == EnumGuiType.PlayerAnvil) + return new ContainerAnvilRepair(player.inventory, player.worldObj, x, y, z); + if(gui == EnumGuiType.SetupItemGiver) return new ContainerNpcItemGiver(npc, player); diff --git a/src/main/java/noppes/npcs/blocks/BlockCarpentryBench.java b/src/main/java/noppes/npcs/blocks/BlockCarpentryBench.java index a435939fb..5b579c406 100644 --- a/src/main/java/noppes/npcs/blocks/BlockCarpentryBench.java +++ b/src/main/java/noppes/npcs/blocks/BlockCarpentryBench.java @@ -2,9 +2,9 @@ package noppes.npcs.blocks; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; +import net.minecraft.block.Block; import net.minecraft.block.BlockContainer; import net.minecraft.block.material.Material; -import net.minecraft.client.renderer.texture.IIconRegister; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.entity.EntityLivingBase; import net.minecraft.entity.player.EntityPlayer; @@ -32,9 +32,14 @@ public class BlockCarpentryBench extends BlockContainer @Override public boolean onBlockActivated(World par1World, int i, int j, int k, EntityPlayer player, int par6, float par7, float par8, float par9) - {//onBlockActivated + { if(!par1World.isRemote){ - player.openGui(CustomNpcs.instance, EnumGuiType.PlayerAnvil.ordinal(), par1World, i, j, k); + int meta = par1World.getBlockMetadata(i, j, k); + if(meta >= 4){ + player.openGui(CustomNpcs.instance, EnumGuiType.PlayerAnvil.ordinal(), par1World, i, j, k); + } else { + player.openGui(CustomNpcs.instance, EnumGuiType.PlayerCarpentryBench.ordinal(), par1World, i, j, k); + } } return true; } diff --git a/src/main/java/noppes/npcs/client/ClientProxy.java b/src/main/java/noppes/npcs/client/ClientProxy.java index dc9df0a67..896528a4e 100644 --- a/src/main/java/noppes/npcs/client/ClientProxy.java +++ b/src/main/java/noppes/npcs/client/ClientProxy.java @@ -316,9 +316,12 @@ public class ClientProxy extends CommonProxy { else if (gui == EnumGuiType.ScriptItem) return new GuiScriptItem(); - else if (gui == EnumGuiType.PlayerAnvil) + else if (gui == EnumGuiType.PlayerCarpentryBench) return new GuiNpcCarpentryBench((ContainerCarpentryBench) container); + else if (gui == EnumGuiType.PlayerAnvil) + return new GuiNpcAnvil((ContainerAnvilRepair) container); + else if (gui == EnumGuiType.SetupFollower) return new GuiNpcFollowerSetup(npc, (ContainerNPCFollowerSetup) container); diff --git a/src/main/java/noppes/npcs/client/gui/player/GuiAnvilRecipes.java b/src/main/java/noppes/npcs/client/gui/player/GuiAnvilRecipes.java new file mode 100644 index 000000000..d0ba2533c --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/player/GuiAnvilRecipes.java @@ -0,0 +1,232 @@ +package noppes.npcs.client.gui.player; + +import cpw.mods.fml.relauncher.Side; +import cpw.mods.fml.relauncher.SideOnly; +import java.util.ArrayList; +import java.util.List; + +import noppes.npcs.client.CustomNpcResourceListener; +import noppes.npcs.client.gui.util.*; +import noppes.npcs.controllers.RecipeController; +import noppes.npcs.controllers.data.RecipeAnvil; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.GuiButton; +import net.minecraft.client.renderer.RenderHelper; +import net.minecraft.item.ItemStack; +import net.minecraft.util.MathHelper; +import net.minecraft.util.ResourceLocation; +import noppes.npcs.util.ValueUtil; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; + +@SideOnly(Side.CLIENT) +public class GuiAnvilRecipes extends GuiNPCInterface { + + private static final ResourceLocation SLOT_TEXTURE = new ResourceLocation("customnpcs", "textures/gui/slot.png"); + + private int page = 0; + private GuiNpcLabel lblPage; + private GuiNpcButton btnLeft, btnRight; + private List recipes = new ArrayList(); + private String search = ""; + + public GuiAnvilRecipes() { + this.xSize = 256; + this.ySize = 182; + setBackground("recipesAnvil.png"); + this.closeOnEsc = true; + + recipes.addAll(RecipeController.Instance.anvilRecipes.values()); + } + + @Override + public void initGui() { + super.initGui(); + guiTop += 10; + // Add search text field (ID 3) at top. + addTextField(new GuiNpcTextField(3, this, fontRendererObj, guiLeft + 2, guiTop - 25, 250, 20, search)); + + addLabel(new GuiNpcLabel(0, "Anvil Recipe List", guiLeft + 5, guiTop + 5)); + addLabel(lblPage = new GuiNpcLabel(1, "", guiLeft + 5, guiTop + 168)); + + addButton(this.btnLeft = new GuiButtonNextPage(1, guiLeft + 150, guiTop + 164, true)); + addButton(this.btnRight = new GuiButtonNextPage(2, guiLeft + 80, guiTop + 164, false)); + + updateButton(); + } + + private void updateButton() { + // We want 8 recipes per page. + int maxPages = MathHelper.ceiling_float_int(recipes.size() / 8.0F); + btnRight.visible = btnRight.enabled = page > 0; + btnLeft.visible = btnLeft.enabled = page < maxPages - 1; + lblPage.label = (page + 1) + "/" + maxPages; + } + + @Override + public void keyTyped(char c, int i) { + super.keyTyped(c, i); + if(search.equals(getTextField(3).getText())) + return; + search = getTextField(3).getText().toLowerCase(); + recipes = getSearchList(); + page = 0; + updateButton(); + } + + private List getSearchList() { + if(search.isEmpty()) { + return new ArrayList(RecipeController.Instance.anvilRecipes.values()); + } + List list = new ArrayList(); + for(RecipeAnvil recipe : RecipeController.Instance.anvilRecipes.values()) { + ItemStack repairItem = recipe.itemToRepair; + if(repairItem == null) + continue; + if(repairItem.getDisplayName() == null) + continue; + if(repairItem.getDisplayName().trim().isEmpty()) + continue; + if(repairItem.getDisplayName().toLowerCase().contains(search)) + list.add(recipe); + } + return list; + } + + @Override + protected void actionPerformed(GuiButton button) { + if(!button.enabled) + return; + if(button == btnRight) { + if(page > 0) + page--; + } + if(button == btnLeft) { + int maxPages = MathHelper.ceiling_float_int(recipes.size() / 8.0F); + if(page < maxPages - 1) + page++; + } + updateButton(); + } + + @Override + public void drawScreen(int mouseX, int mouseY, float partialTicks) { + super.drawScreen(mouseX, mouseY, partialTicks); + + // Update page label. + lblPage.label = (page + 1) + "/" + MathHelper.ceiling_float_int(recipes.size() / 8.0F); + lblPage.x = guiLeft + (xSize - Minecraft.getMinecraft().fontRenderer.getStringWidth(lblPage.label)) / 2; + + // Determine which recipes to show. + int recipesPerPage = 8; + int startIndex = page * recipesPerPage; + int endIndex = Math.min(recipes.size(), startIndex + recipesPerPage); + + // Set base positions: first column X and first row Y. + int baseX = guiLeft + 4; + int baseY = guiTop + 28; // first row Y = 28 + int colSpacing = 124; // horizontal spacing between columns + int rowSpacing = 35; // vertical spacing so row1 becomes 28+35 = 63 + + for (int i = startIndex; i < endIndex; i++) { + RecipeAnvil recipe = recipes.get(i); + int localIndex = i - startIndex; // 0 to 7 + int col = localIndex / 4; // 0 for left column, 1 for right column + int row = localIndex % 4; // 0 to 3 + + int slotX = baseX + col * colSpacing; + int slotY = baseY + row * rowSpacing; + + // Draw slot backgrounds for two input slots. + mc.renderEngine.bindTexture(SLOT_TEXTURE); + GL11.glColor4f(1F, 1F, 1F, 1F); + // Left slot (item to repair) + drawTexturedModalRect(slotX, slotY, 0, 0, 18, 18); + // Middle slot (repair material) + drawTexturedModalRect(slotX + 22, slotY, 0, 0, 18, 18); + + // Draw the item-to-repair at 50% durability. + ItemStack toRepair = recipe.itemToRepair; + if (toRepair != null) { + ItemStack halfItem = toRepair.copy(); + if (halfItem.isItemStackDamageable()) { + int halfDamage = halfItem.getMaxDamage() / 2; + halfItem.setItemDamage(halfDamage); + } + drawItem(halfItem, slotX + 1, slotY + 1, mouseX, mouseY); + } + // Draw the repair material. + ItemStack material = recipe.repairMaterial; + if (material != null) { + drawItem(material, slotX + 23, slotY + 1, mouseX, mouseY); + } + + // Draw the output item fully repaired (damage = 0). + // For first column, output X = guiLeft+103; second column, output X = guiLeft+229. + int outputX = (col == 0) ? (guiLeft + 103) : (guiLeft + 229); + if (recipe.itemToRepair != null) { + ItemStack fullOutput = recipe.itemToRepair.copy(); + if (fullOutput.isItemStackDamageable()) { + fullOutput.setItemDamage(0); + } + drawItem(fullOutput, outputX, slotY, mouseX, mouseY); + } + + // Draw the percentage text. + // We want the "middle right" for the first column to be at 68. + // So for column 0, centerX is fixed at 68; for column 1, centerX is 68 + colSpacing. + String percentText = Math.round(recipe.getRepairPercentage()) + "%"; + int textWidth = fontRendererObj.getStringWidth(percentText); + int centerX = (col == 0) ? guiLeft + 57 : (guiLeft + 57 + colSpacing); + int textX = centerX - (textWidth / 2); + int textY = slotY + 4 + row; + fontRendererObj.drawString(percentText, textX, textY, CustomNpcResourceListener.DefaultTextColor); + + // Draw XP cost with scaling. + String xpCostStr = formatXpCost(recipe.getXpCost()); + int digitCount = xpCostStr.length(); + // Scale factor: for 1 digit = 1.0, for 2 digits = 0.9, for 3 digits = 0.8. + float xpScale = 1.0F - 0.1F * (digitCount - 1); + // Get raw text width. + int rawTextWidth = fontRendererObj.getStringWidth(xpCostStr); + int scaledTextWidth = (int)(rawTextWidth * xpScale); + int xpCenterX = (col == 0) ? (guiLeft + 76) : (guiLeft + 78 + colSpacing); + int xpTextX = xpCenterX - (scaledTextWidth / 2); + int xpTextY = slotY - 4 + row; // adjust as needed + + GL11.glPushMatrix(); + GL11.glTranslatef(xpTextX, xpTextY, 0); + GL11.glScalef(xpScale, xpScale, 1.0F); + fontRendererObj.drawString(xpCostStr, 0, 0, 0x4ac26a); + GL11.glPopMatrix(); + } + } + + private String formatXpCost(int xp) { + if (xp < 1000) { + return String.valueOf(xp); + } + double xpK = xp / 1000.0; + if (xpK == (int)xpK) { + return ((int)xpK) + "K"; + } else { + return String.format("%.1fK", xpK); + } + } + + private void drawItem(ItemStack item, int x, int y, int mouseX, int mouseY) { + GL11.glPushMatrix(); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + RenderHelper.enableGUIStandardItemLighting(); + itemRender.zLevel = 100.0F; + itemRender.renderItemAndEffectIntoGUI(fontRendererObj, mc.renderEngine, item, x, y); + itemRender.renderItemOverlayIntoGUI(fontRendererObj, mc.renderEngine, item, x, y); + itemRender.zLevel = 0.0F; + RenderHelper.disableStandardItemLighting(); + GL11.glDisable(GL12.GL_RESCALE_NORMAL); + GL11.glPopMatrix(); + } + + @Override + public void save() {} +} diff --git a/src/main/java/noppes/npcs/client/gui/player/GuiNpcAnvil.java b/src/main/java/noppes/npcs/client/gui/player/GuiNpcAnvil.java new file mode 100644 index 000000000..b33075f5c --- /dev/null +++ b/src/main/java/noppes/npcs/client/gui/player/GuiNpcAnvil.java @@ -0,0 +1,81 @@ +package noppes.npcs.client.gui.player; + +import net.minecraft.client.gui.GuiButton; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.StatCollector; +import noppes.npcs.client.CustomNpcResourceListener; +import noppes.npcs.client.gui.util.GuiContainerNPCInterface; +import noppes.npcs.client.gui.util.GuiNpcButton; +import noppes.npcs.containers.ContainerAnvilRepair; +import noppes.npcs.controllers.RecipeController; +import org.lwjgl.opengl.GL11; + +public class GuiNpcAnvil extends GuiContainerNPCInterface { + + private final ResourceLocation resource = new ResourceLocation("customnpcs", "textures/gui/anvil.png"); + private ContainerAnvilRepair container; + private GuiNpcButton button; + + public GuiNpcAnvil(ContainerAnvilRepair container) { + super(null, container); + this.container = container; + this.title = ""; + allowUserInput = false; + closeOnEsc = true; + ySize = 180; + } + + @Override + public void initGui() { + super.initGui(); + addButton(button = new GuiNpcButton(0, guiLeft + 158, guiTop + 4, 12, 20, "...")); + } + + @Override + public void buttonEvent(GuiButton guibutton) { + displayGuiScreen(new GuiAnvilRecipes()); + } + + @Override + protected void drawGuiContainerBackgroundLayer(float partialTicks, int mouseX, int mouseY) { + // Enable the button if there are any anvil recipes. + button.enabled = RecipeController.Instance != null && !RecipeController.Instance.getAnvilList().isEmpty(); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + mc.renderEngine.bindTexture(resource); + int l = (width - xSize) / 2; + int i1 = (height - ySize) / 2; + drawTexturedModalRect(l, i1, 0, 0, xSize, ySize); + super.drawGuiContainerBackgroundLayer(partialTicks, mouseX, mouseY); + + // Draw title and player's inventory label. + fontRendererObj.drawString(StatCollector.translateToLocal("tile.anvil.name"), guiLeft + 4, guiTop + 4, CustomNpcResourceListener.DefaultTextColor); + fontRendererObj.drawString(StatCollector.translateToLocal("container.inventory"), guiLeft + 4, guiTop + 87, CustomNpcResourceListener.DefaultTextColor); + + // Compute the repair status message based on container.repairCost and player's XP. + String status = ""; + if (container.repairCost > 0) { + if (container.repairCost > mc.thePlayer.experienceTotal) { + status = "Repair cost: " + container.repairCost + " XP"; + } else { + status = "Repair cost: " + container.repairCost + " XP"; + } + } else { + // If there is a damaged item that is fully repaired. + if (container.anvilMatrix.getStackInRowAndColumn(0, 0) != null && + container.anvilMatrix.getStackInRowAndColumn(0, 0).isItemStackDamageable() && + container.anvilMatrix.getStackInRowAndColumn(0, 0).getItemDamage() <= 0) { + status = "Item is already fully repaired"; + } + } + int textColor = CustomNpcResourceListener.DefaultTextColor; + if (container.repairCost > mc.thePlayer.experienceTotal) { + textColor = 0xFF0000; // red when not enough XP + } + fontRendererObj.drawString(status, guiLeft + 5, guiTop + 75, textColor); + } + + @Override + public void save() { + // Nothing to save. + } +} diff --git a/src/main/java/noppes/npcs/constants/EnumGuiType.java b/src/main/java/noppes/npcs/constants/EnumGuiType.java index 5b028c886..7ce4f7e02 100644 --- a/src/main/java/noppes/npcs/constants/EnumGuiType.java +++ b/src/main/java/noppes/npcs/constants/EnumGuiType.java @@ -1,5 +1,11 @@ package noppes.npcs.constants; +import noppes.npcs.quests.QuestItem; +import noppes.npcs.roles.companion.CompanionTrader; +import noppes.npcs.scripted.ScriptBlock; + +import javax.swing.border.Border; + public enum EnumGuiType { MainMenuDisplay, MainMenuInv, @@ -27,7 +33,8 @@ public enum EnumGuiType { SetupBank, ManageDialogs, ManageQuests, - PlayerAnvil, + PlayerCarpentryBench, + PlayerAnvil, QuestReward, ManageRecipes, QuestItem, diff --git a/src/main/java/noppes/npcs/containers/ContainerAnvilRepair.java b/src/main/java/noppes/npcs/containers/ContainerAnvilRepair.java new file mode 100644 index 000000000..df2badcc9 --- /dev/null +++ b/src/main/java/noppes/npcs/containers/ContainerAnvilRepair.java @@ -0,0 +1,370 @@ +package noppes.npcs.containers; + +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.entity.player.EntityPlayerMP; +import net.minecraft.entity.player.InventoryPlayer; +import net.minecraft.init.Items; +import net.minecraft.inventory.Container; +import net.minecraft.inventory.ICrafting; +import net.minecraft.inventory.InventoryCrafting; +import net.minecraft.inventory.InventoryCraftResult; +import net.minecraft.inventory.IInventory; +import net.minecraft.inventory.Slot; +import net.minecraft.item.ItemStack; +import net.minecraft.network.play.server.S2FPacketSetSlot; +import net.minecraft.world.World; +import noppes.npcs.api.handler.data.IAnvilRecipe; +import noppes.npcs.controllers.RecipeController; +import noppes.npcs.controllers.data.RecipeAnvil; + +public class ContainerAnvilRepair extends Container { + // A 2-slot crafting matrix: slot 0 = damaged item, slot 1 = repair material. + public InventoryCrafting anvilMatrix = new InventoryCrafting(this, 2, 1); + // Single-slot output inventory. + public InventoryCraftResult anvilResult = new InventoryCraftResult(); + + private EntityPlayer player; + private World worldObj; + private int posX, posY, posZ; + + // These fields are used for syncing the repair cost message. + public String repairStatus = ""; + public int repairCost = 0; + // How many repair material items are used during the repair calculation. + public int repairMaterialConsumed = 0; + + public ContainerAnvilRepair(InventoryPlayer playerInv, World world, int x, int y, int z) { + this.worldObj = world; + this.posX = x; + this.posY = y; + this.posZ = z; + this.player = playerInv.player; + + // Output slot (index 0) using our custom SlotAnvilOutput. + this.addSlotToContainer(new SlotAnvilOutput(this, anvilResult, 0, 133, 47)); + + // Input slot 0: damaged item. We restrict its stack size to 1. + this.addSlotToContainer(new Slot(anvilMatrix, 0, 26, 47) { + @Override + public int getSlotStackLimit() { + return 1; + } + }); + // Input slot 1: repair material. Override onSlotChanged so that changes to the stack size trigger an update. + this.addSlotToContainer(new Slot(anvilMatrix, 1, 75, 47) { + @Override + public void onSlotChanged() { + super.onSlotChanged(); + ContainerAnvilRepair.this.onCraftMatrixChanged(anvilMatrix); + } + }); + + // Add player inventory slots. + for (int row = 0; row < 3; ++row) { + for (int col = 0; col < 9; ++col) { + this.addSlotToContainer(new Slot(playerInv, col + row * 9 + 9, 8 + col * 18, 98 + row * 18)); + } + } + for (int col = 0; col < 9; ++col) { + this.addSlotToContainer(new Slot(playerInv, col, 8 + col * 18, 156)); + } + + updateRepairResult(); + + // For testing: clear recipes and add a test recipe. + RecipeController.Instance.anvilRecipes.clear(); + RecipeController.Instance.addAnvilRecipe(new RecipeAnvil( + "Test", + new ItemStack(Items.iron_pickaxe), + new ItemStack(Items.paper), + 4, + 3)); + RecipeController.Instance.addAnvilRecipe(new RecipeAnvil( + "Okay", + new ItemStack(Items.diamond_pickaxe), + new ItemStack(Items.paper), + 4, + 3)); + RecipeController.Instance.addAnvilRecipe(new RecipeAnvil( + "Bob", + new ItemStack(Items.golden_pickaxe), + new ItemStack(Items.paper), + 4, + 3)); + RecipeController.Instance.addAnvilRecipe(new RecipeAnvil( + "Dude", + new ItemStack(Items.iron_chestplate), + new ItemStack(Items.paper), + 4, + 3)); + RecipeController.Instance.addAnvilRecipe(new RecipeAnvil( + "Oas", + new ItemStack(Items.golden_pickaxe), + new ItemStack(Items.diamond), + 4, + 3)); + RecipeController.Instance.addAnvilRecipe(new RecipeAnvil( + "awd", + new ItemStack(Items.golden_sword), + new ItemStack(Items.gold_ingot), + 4, + 3)); + RecipeController.Instance.addAnvilRecipe(new RecipeAnvil( + "fawd", + new ItemStack(Items.bow), + new ItemStack(Items.string), + 4, + 3)); + RecipeController.Instance.addAnvilRecipe(new RecipeAnvil( + "cxzs", + new ItemStack(Items.bow), + new ItemStack(Items.diamond), + 4, + 3)); + RecipeController.Instance.addAnvilRecipe(new RecipeAnvil( + "awdawdwa", + new ItemStack(Items.iron_sword), + new ItemStack(Items.blaze_rod), + 4, + 3)); + } + + @Override + public void onCraftMatrixChanged(IInventory inventory) { + updateRepairResult(); + detectAndSendChanges(); + } + + /** + * Updates the output slot (and repair cost fields) based on the two input slots. + * Instead of forcing the damaged item’s stack to 1 (which deletes extra items), + * we create a copy with stackSize 1 for calculations. Then, when the repair is picked up, + * we subtract one from the original stack. + */ + public void updateRepairResult() { + if (!this.worldObj.isRemote) { + repairMaterialConsumed = 0; + ItemStack input0 = anvilMatrix.getStackInRowAndColumn(0, 0); + ItemStack input1 = anvilMatrix.getStackInRowAndColumn(1, 0); + + // If either input is missing, clear output. + if (input0 == null || input1 == null) { + repairCost = 0; + anvilResult.setInventorySlotContents(0, null); + return; + } + + // Create a copy of the damaged item for calculation (stack size 1) + ItemStack calcItem = input0.copy(); + calcItem.stackSize = 1; + + // If the item is damageable and already fully repaired, show a message. + if (calcItem.isItemStackDamageable() && calcItem.getItemDamage() <= 0) { + repairCost = 0; + anvilResult.setInventorySlotContents(0, null); + return; + } + + // Look for a matching recipe. + RecipeAnvil matchingRecipe = null; + for (IAnvilRecipe recipe : RecipeController.Instance.getAnvilList()) { + if (recipe.matches(input0, input1)) { + matchingRecipe = (RecipeAnvil) recipe; + break; + } + } + + ItemStack output = null; + if (matchingRecipe != null) { + int baseXpCost = matchingRecipe.getXpCost(); + // Assume repairPercentage is given as a percent (e.g. 20 for 20% per material) + float repairPercentage = matchingRecipe.getRepairPercentage() / 100.0f; + + int maxDamage = calcItem.getMaxDamage(); + int currentDamage = calcItem.getItemDamage(); + + // Fixed repair amount per material (at least 1 point) + int repairPerMaterial = (int) Math.floor(maxDamage * repairPercentage); + if (repairPerMaterial < 1) { + repairPerMaterial = 1; + } + + int availableMaterials = input1.stackSize; + int materialsUsed = 0; + + while (currentDamage > 0 && materialsUsed < availableMaterials) { + int repairThisIteration = repairPerMaterial; + if (repairThisIteration > currentDamage) { + repairThisIteration = currentDamage; + } + currentDamage -= repairThisIteration; + materialsUsed++; + + // Stop if item is fully repaired. + if (currentDamage <= 0) { + break; + } + } + int xpCost = baseXpCost * materialsUsed; + + calcItem.setItemDamage(currentDamage); + if (player.experienceTotal >= xpCost) { + output = calcItem; + } + repairMaterialConsumed = materialsUsed; + repairCost = xpCost; + } else { + repairCost = 0; + } + + anvilResult.setInventorySlotContents(0, output); + } + } + + + + + // --- Sync Methods --- + @Override + public void addCraftingToCrafters(ICrafting listener) { + super.addCraftingToCrafters(listener); + listener.sendProgressBarUpdate(this, 0, this.repairCost); + } + + @Override + public void detectAndSendChanges() { + super.detectAndSendChanges(); + for (Object crafterObj : this.crafters) { + ((ICrafting) crafterObj).sendProgressBarUpdate(this, 0, this.repairCost); + } + } + + @Override + public void updateProgressBar(int id, int data) { + if (id == 0) { + this.repairCost = data; + } + } + // --- End Sync Methods --- + + @Override + public void onContainerClosed(EntityPlayer player) { + super.onContainerClosed(player); + if (!this.worldObj.isRemote) { + for (int i = 0; i < this.anvilMatrix.getSizeInventory(); i++) { + ItemStack stack = this.anvilMatrix.getStackInSlotOnClosing(i); + if (stack != null) { + player.dropPlayerItemWithRandomChoice(stack, false); + } + } + } + } + + @Override + public boolean canInteractWith(EntityPlayer player) { + return player.getDistanceSq((double)this.posX + 0.5D, + (double)this.posY + 0.5D, + (double)this.posZ + 0.5D) <= 64.0D; + } + + @Override + public ItemStack transferStackInSlot(EntityPlayer player, int index) { + ItemStack copy = null; + Slot slot = (Slot)this.inventorySlots.get(index); + if (slot != null && slot.getHasStack()) { + ItemStack stack = slot.getStack(); + copy = stack.copy(); + if (index == 0) { // output slot + if (player.experienceTotal < this.repairCost) { + return null; + } + if (!this.mergeItemStack(stack, 3, this.inventorySlots.size(), true)) { + return null; + } + slot.onSlotChange(stack, copy); + } else if (index >= 1 && index <= 2) { // input slots + if (!this.mergeItemStack(stack, 3, this.inventorySlots.size(), false)) { + return null; + } + } else { // player inventory + if (!this.mergeItemStack(stack, 1, 3, false)) { + return null; + } + } + if (stack.stackSize == 0) { + slot.putStack(null); + } else { + slot.onSlotChanged(); + } + if (stack.stackSize == copy.stackSize) { + return null; + } + slot.onPickupFromSlot(player, stack); + } + if (!this.worldObj.isRemote && player instanceof EntityPlayerMP) { + ((EntityPlayerMP) player).sendContainerToPlayer(this); + } + return copy; + } + + @Override + public boolean func_94530_a(ItemStack stack, Slot slotIn) { + return slotIn.inventory != this.anvilResult && super.func_94530_a(stack, slotIn); + } + + // Custom output slot: only allows pickup if the player has enough XP and, + // when picked up, consumes the required number of repair material items and + // subtracts one from the damaged item stack. + public class SlotAnvilOutput extends Slot { + private ContainerAnvilRepair container; + + public SlotAnvilOutput(ContainerAnvilRepair container, IInventory inventory, int index, int x, int y) { + super(inventory, index, x, y); + this.container = container; + } + + @Override + public boolean isItemValid(ItemStack stack) { + return false; + } + + @Override + public boolean canTakeStack(EntityPlayer player) { + return player.experienceTotal >= container.repairCost; + } + + @Override + public void onPickupFromSlot(EntityPlayer player, ItemStack stack) { + if (player.experienceTotal < container.repairCost) { + container.updateRepairResult(); + return; + } + if (!player.capabilities.isCreativeMode) { + player.addExperience(-container.repairCost); + } + + ItemStack repairMat = container.anvilMatrix.getStackInRowAndColumn(1, 0); + if (repairMat != null) { + if (repairMat.stackSize > container.repairMaterialConsumed) { + repairMat.stackSize -= container.repairMaterialConsumed; + } else { + repairMat = null; + } + container.anvilMatrix.setInventorySlotContents(1, repairMat); + } + + ItemStack input0 = container.anvilMatrix.getStackInRowAndColumn(0, 0); + if (input0 != null) { + if (input0.stackSize > 1) { + input0.stackSize -= 1; + } else { + input0 = null; + } + container.anvilMatrix.setInventorySlotContents(0, input0); + } + container.repairCost = 0; + container.updateRepairResult(); + super.onPickupFromSlot(player, stack); + } + } +} diff --git a/src/main/java/noppes/npcs/controllers/MagicController.java b/src/main/java/noppes/npcs/controllers/MagicController.java index da72e80a2..c92465252 100644 --- a/src/main/java/noppes/npcs/controllers/MagicController.java +++ b/src/main/java/noppes/npcs/controllers/MagicController.java @@ -57,7 +57,7 @@ public class MagicController { } catch (Exception ee) { } } magics.clear(); - if (true) { + if (false) { // Create default Pokémon types Magic normal = new Magic(0, "Normal", 0xA8A878); normal.setIconItem(new ItemStack(net.minecraft.init.Items.paper)); // using paper for Normal @@ -205,7 +205,7 @@ public class MagicController { magics.put(fairy.id, fairy); } - if (false) { + if (true) { // Create default magics Magic earth = new Magic(0, "Earth", 0x00DD00, 1, 1); earth.setIconItem(new ItemStack(CustomItems.earthElement)); diff --git a/src/main/java/noppes/npcs/controllers/RecipeController.java b/src/main/java/noppes/npcs/controllers/RecipeController.java index 1bdddd556..3eaea1fac 100644 --- a/src/main/java/noppes/npcs/controllers/RecipeController.java +++ b/src/main/java/noppes/npcs/controllers/RecipeController.java @@ -9,7 +9,9 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import noppes.npcs.CustomNpcs; import noppes.npcs.api.handler.IRecipeHandler; +import noppes.npcs.api.handler.data.IAnvilRecipe; import noppes.npcs.api.handler.data.IRecipe; +import noppes.npcs.controllers.data.RecipeAnvil; import noppes.npcs.controllers.data.RecipeCarpentry; import noppes.npcs.controllers.data.RecipesDefault; @@ -21,14 +23,19 @@ import java.util.*; public class RecipeController implements IRecipeHandler { private static Collection prevRecipes; - public HashMap globalRecipes = new HashMap(); - public HashMap carpentryRecipes = new HashMap(); - public static RecipeController Instance; + + public HashMap globalRecipes = new HashMap(); + public HashMap carpentryRecipes = new HashMap(); + public HashMap anvilRecipes = new HashMap(); + + public static RecipeController Instance; public static final int version = 1; public int nextId = 1; + public int nextAnvilId = 1; - public static HashMap syncRecipes = new HashMap(); + public static HashMap syncRecipes = new HashMap(); + public static HashMap syncAnvilRecipes = new HashMap(); public RecipeController(){ Instance = this; @@ -37,6 +44,7 @@ public class RecipeController implements IRecipeHandler { loadCategories(); reloadGlobalRecipes(globalRecipes); } + public static void reloadGlobalRecipes(HashMap globalRecipes){ List list = CraftingManager.getInstance().getRecipeList(); if(prevRecipes != null){ @@ -61,6 +69,7 @@ public class RecipeController implements IRecipeHandler { else{ globalRecipes.clear(); carpentryRecipes.clear(); + anvilRecipes.clear(); loadDefaultRecipes(-1); } } catch (Exception e) { @@ -85,23 +94,34 @@ public class RecipeController implements IRecipeHandler { private void loadCategories(File file) throws Exception{ NBTTagCompound nbttagcompound1 = CompressedStreamTools.readCompressed(new FileInputStream(file)); nextId = nbttagcompound1.getInteger("LastId"); + nextAnvilId = nbttagcompound1.getInteger("LastAnvilId"); NBTTagList list = nbttagcompound1.getTagList("Data", 10); HashMap globalRecipes = new HashMap(); - HashMap anvilRecipes = new HashMap(); + HashMap carpentryRecipes = new HashMap(); + HashMap anvilRecipes = new HashMap(); if(list != null){ for(int i = 0; i < list.tagCount(); i++) { - RecipeCarpentry recipe = RecipeCarpentry.read(list.getCompoundTagAt(i)); - if(recipe.isGlobal) - globalRecipes.put(recipe.id,recipe); - else - anvilRecipes.put(recipe.id,recipe); - if(recipe.id > nextId) - nextId = recipe.id; + NBTTagCompound compound = list.getCompoundTagAt(i); + if(compound.hasKey("IsAnvil")){ + RecipeAnvil anvil = RecipeAnvil.read(compound); + anvilRecipes.put(anvil.id, anvil); + if(anvil.id > nextAnvilId) + nextAnvilId = anvil.id; + } else { + RecipeCarpentry recipe = RecipeCarpentry.read(compound); + if(recipe.isGlobal) + globalRecipes.put(recipe.id,recipe); + else + carpentryRecipes.put(recipe.id,recipe); + if(recipe.id > nextId) + nextId = recipe.id; + } } } - this.carpentryRecipes = anvilRecipes; + this.carpentryRecipes = carpentryRecipes; this.globalRecipes = globalRecipes; + this.anvilRecipes = anvilRecipes; loadDefaultRecipes(nbttagcompound1.getInteger("Version")); } private void saveCategories(){ @@ -114,9 +134,13 @@ public class RecipeController implements IRecipeHandler { for(RecipeCarpentry recipe : carpentryRecipes.values()){ list.appendTag(recipe.writeNBT()); } + for(RecipeAnvil recipe : anvilRecipes.values()){ + list.appendTag(recipe.writeNBT()); + } NBTTagCompound nbttagcompound = new NBTTagCompound(); nbttagcompound.setTag("Data", list); nbttagcompound.setInteger("LastId", nextId); + nbttagcompound.setInteger("LastAnvilId", nextAnvilId); nbttagcompound.setInteger("Version", version); File file = new File(saveDir, "recipes.dat_new"); File file1 = new File(saveDir, "recipes.dat_old"); @@ -157,6 +181,12 @@ public class RecipeController implements IRecipeHandler { return null; } + public RecipeAnvil getAnvilRecipe(int id) { + if(anvilRecipes.containsKey(id)) + return anvilRecipes.get(id); + return null; + } + public RecipeCarpentry saveRecipe(NBTTagCompound compound) throws IOException { RecipeCarpentry recipe = RecipeCarpentry.read(compound); @@ -184,9 +214,34 @@ public class RecipeController implements IRecipeHandler { return recipe; } + public RecipeAnvil saveAnvilRecipe(NBTTagCompound compound) throws IOException { + RecipeAnvil recipe = RecipeAnvil.read(compound); + + RecipeAnvil current = getAnvilRecipe(recipe.id); + if(current != null && !current.name.equals(recipe.name)){ + while(containsRecipeName(recipe.name)) + recipe.name += "_"; + } + + if(recipe.id == -1){ + recipe.id = getUniqueAnvilId(); + while(containsRecipeName(recipe.name)) + recipe.name += "_"; + } + + anvilRecipes.put(recipe.id, recipe); + saveCategories(); + return recipe; + } + private int getUniqueId() { return nextId++; } + + private int getUniqueAnvilId() { + return nextAnvilId++; + } + private boolean containsRecipeName(String name) { name = name.toLowerCase(); for(RecipeCarpentry recipe : globalRecipes.values()){ @@ -200,6 +255,15 @@ public class RecipeController implements IRecipeHandler { return false; } + private boolean containsAnvilRecipeName(String name) { + name = name.toLowerCase(); + for(RecipeAnvil recipe : anvilRecipes.values()){ + if(recipe.name.toLowerCase().equals(name)) + return true; + } + return false; + } + public RecipeCarpentry delete(int id) { RecipeCarpentry recipe = getRecipe(id); if(recipe == null) @@ -210,29 +274,58 @@ public class RecipeController implements IRecipeHandler { RecipeCarpentry carpentry = carpentryRecipes.remove(recipe.id); if(carpentry != null) - SyncController.syncAllWorkbenchRecipes(); + SyncController.syncAllCarpentryRecipes(); saveCategories(); reloadGlobalRecipes(globalRecipes); return recipe; } - public void addRecipe(RecipeCarpentry recipeAnvil) { - recipeAnvil.id = getUniqueId(); - if(!recipeAnvil.isGlobal) - RecipeController.Instance.carpentryRecipes.put(recipeAnvil.id, recipeAnvil); + + public RecipeAnvil deleteAnvil(int id) { + RecipeAnvil recipe = getAnvilRecipe(id); + if(recipe == null) + return null; + + RecipeAnvil anvilRecipe = anvilRecipes.remove(recipe.id); + if(anvilRecipe != null) + // SyncController.syncAllWorkbenchRecipes(); + + saveCategories(); + return recipe; + } + + public void addRecipe(RecipeCarpentry recipeCarpentry) { + recipeCarpentry.id = getUniqueId(); + if(!recipeCarpentry.isGlobal) + RecipeController.Instance.carpentryRecipes.put(recipeCarpentry.id, recipeCarpentry); else{ - RecipeController.Instance.globalRecipes.put(recipeAnvil.id, recipeAnvil); + RecipeController.Instance.globalRecipes.put(recipeCarpentry.id, recipeCarpentry); } } + public void addAnvilRecipe(RecipeAnvil recipeAnvil) { + recipeAnvil.id = getUniqueAnvilId(); + anvilRecipes.put(recipeAnvil.id, recipeAnvil); + } + + @Override public List getGlobalList() { return new ArrayList(this.globalRecipes.values()); } + @Override public List getCarpentryList() { return new ArrayList(this.carpentryRecipes.values()); } + @Override + public List getAnvilList() { + List list = new ArrayList<>(); + list.addAll(anvilRecipes.values()); + return list; + } + + @Override public void addRecipe(String name, boolean global, ItemStack result, Object... objects) { RecipeCarpentry recipe = new RecipeCarpentry(name); recipe.isGlobal = global; @@ -245,6 +338,7 @@ public class RecipeController implements IRecipeHandler { } } + @Override public void addRecipe(String name, boolean global, ItemStack result, int width, int height, ItemStack... objects) { ArrayList list = new ArrayList<>(); int var9 = objects.length; @@ -266,4 +360,14 @@ public class RecipeController implements IRecipeHandler { var12.printStackTrace(); } } + + @Override + public void addAnvilRecipe(String name, boolean global, ItemStack itemToRepair, ItemStack repairMaterial, int xpCost, float repairPercentage) { + RecipeAnvil recipe = new RecipeAnvil(name, itemToRepair, repairMaterial, xpCost, repairPercentage); + try { + this.saveAnvilRecipe(recipe.writeNBT()); + } catch (Exception e) { + e.printStackTrace(); + } + } } diff --git a/src/main/java/noppes/npcs/controllers/data/RecipeAnvil.java b/src/main/java/noppes/npcs/controllers/data/RecipeAnvil.java new file mode 100644 index 000000000..e153fe3a8 --- /dev/null +++ b/src/main/java/noppes/npcs/controllers/data/RecipeAnvil.java @@ -0,0 +1,111 @@ +package noppes.npcs.controllers.data; + +import net.minecraft.item.ItemStack; +import net.minecraft.nbt.NBTTagCompound; +import noppes.npcs.api.handler.data.IAnvilRecipe; +import noppes.npcs.NoppesUtilPlayer; +import noppes.npcs.NoppesUtilServer; + +public class RecipeAnvil implements IAnvilRecipe { + public int id = -1; + public String name = ""; + public Availability availability = new Availability(); + public boolean ignoreNBT = false; + + public ItemStack itemToRepair; + public ItemStack repairMaterial; + + private int xpCost; + private float repairPercentage; + + private boolean isAnvil = true; + + public RecipeAnvil() { } + + public RecipeAnvil(String name, ItemStack itemToRepair, ItemStack repairMaterial, int xpCost, float repairPercentage) { + this.name = name; + this.itemToRepair = itemToRepair; + this.repairMaterial = repairMaterial; + this.xpCost = xpCost; + this.repairPercentage = repairPercentage; + } + + public static RecipeAnvil read(NBTTagCompound compound) { + RecipeAnvil recipe = new RecipeAnvil(); + recipe.id = compound.getInteger("ID"); + recipe.name = compound.getString("Name"); + recipe.availability.readFromNBT(compound.getCompoundTag("Availability")); + recipe.xpCost = compound.getInteger("XPCost"); + recipe.repairPercentage = compound.getFloat("RepairPercentage"); + recipe.itemToRepair = NoppesUtilServer.readItem(compound.getCompoundTag("ItemToRepair")); + recipe.repairMaterial = NoppesUtilServer.readItem(compound.getCompoundTag("RepairMaterial")); + recipe.isAnvil = true; + return recipe; + } + + public NBTTagCompound writeNBT() { + NBTTagCompound compound = new NBTTagCompound(); + compound.setInteger("ID", id); + compound.setString("Name", name); + compound.setTag("Availability", availability.writeToNBT(new NBTTagCompound())); + compound.setInteger("XPCost", xpCost); + compound.setFloat("RepairPercentage", repairPercentage); + compound.setTag("ItemToRepair", NoppesUtilServer.writeItem(itemToRepair, new NBTTagCompound())); + compound.setTag("RepairMaterial", NoppesUtilServer.writeItem(repairMaterial, new NBTTagCompound())); + compound.setBoolean("IsAnvil", true); + return compound; + } + + public int getXpCost() { + return xpCost; + } + + @Override + public float getRepairPercentage() { + return repairPercentage; + } + + /** + * Checks that both input ItemStacks are non-null and match the recipe’s defined items. + */ + @Override + public boolean matches(ItemStack inputItem, ItemStack inputRepairMaterial) { + if (inputItem == null || inputRepairMaterial == null) return false; + if (!NoppesUtilPlayer.compareItems(this.itemToRepair, inputItem, true, false)) { + return false; + } + if (!NoppesUtilPlayer.compareItems(this.repairMaterial, inputRepairMaterial, false, false)) { + return false; + } + return true; + } + + /** + * Returns a repaired copy of the input item by reducing its damage. + */ + @Override + public ItemStack getResult(ItemStack inputItem) { + if (inputItem == null) + return null; + ItemStack result = inputItem.copy(); + if (!result.isItemStackDamageable()) + return result; + int maxDamage = result.getMaxDamage(); + int repairAmount = (int) (maxDamage * repairPercentage); + int currentDamage = result.getItemDamage(); + int newDamage = currentDamage - repairAmount; + if (newDamage < 0) newDamage = 0; + result.setItemDamage(newDamage); + return result; + } + + @Override + public String getName() { + return name; + } + + @Override + public int getID() { + return this.id; + } +} diff --git a/src/main/java/noppes/npcs/controllers/data/RecipesDefault.java b/src/main/java/noppes/npcs/controllers/data/RecipesDefault.java index f20b25264..8583290c9 100644 --- a/src/main/java/noppes/npcs/controllers/data/RecipesDefault.java +++ b/src/main/java/noppes/npcs/controllers/data/RecipesDefault.java @@ -19,10 +19,10 @@ public class RecipesDefault { else item = (ItemStack) ob; - RecipeCarpentry recipeAnvil = new RecipeCarpentry(name); - recipeAnvil.isGlobal = isGlobal; - recipeAnvil = RecipeCarpentry.saveRecipe(recipeAnvil, item, recipe); - RecipeController.Instance.addRecipe(recipeAnvil); + RecipeCarpentry recipeCarpentry = new RecipeCarpentry(name); + recipeCarpentry.isGlobal = isGlobal; + recipeCarpentry = RecipeCarpentry.saveRecipe(recipeCarpentry, item, recipe); + RecipeController.Instance.addRecipe(recipeCarpentry); } public static void loadDefaultRecipes(int i) { if(i < 0){ diff --git a/src/main/resources/assets/customnpcs/textures/gui/anvil.png b/src/main/resources/assets/customnpcs/textures/gui/anvil.png new file mode 100644 index 000000000..bfc3c4bbc Binary files /dev/null and b/src/main/resources/assets/customnpcs/textures/gui/anvil.png differ diff --git a/src/main/resources/assets/customnpcs/textures/gui/recipesAnvil.png b/src/main/resources/assets/customnpcs/textures/gui/recipesAnvil.png new file mode 100644 index 000000000..31402d367 Binary files /dev/null and b/src/main/resources/assets/customnpcs/textures/gui/recipesAnvil.png differ