From caf416a27d1cbb63029025c44aedd8db9e012d88 Mon Sep 17 00:00:00 2001 From: abel1502 Date: Tue, 13 May 2025 00:35:35 +0300 Subject: [PATCH] Tool ability selector frontend --- .../java/com/hbm/handler/ToolAbility.java | 58 ++- .../inventory/gui/GUIScreenToolAbility.java | 339 ++++++++++++++++++ .../com/hbm/items/tool/ItemToolAbility.java | 28 +- .../textures/gui/tool/gui_tool_ability.png | Bin 0 -> 4053 bytes 4 files changed, 422 insertions(+), 3 deletions(-) create mode 100644 src/main/java/com/hbm/inventory/gui/GUIScreenToolAbility.java create mode 100644 src/main/resources/assets/hbm/textures/gui/tool/gui_tool_ability.png diff --git a/src/main/java/com/hbm/handler/ToolAbility.java b/src/main/java/com/hbm/handler/ToolAbility.java index bf9462a49..aab7757f9 100644 --- a/src/main/java/com/hbm/handler/ToolAbility.java +++ b/src/main/java/com/hbm/handler/ToolAbility.java @@ -33,7 +33,6 @@ import net.minecraft.world.World; public abstract class ToolAbility { - //how to potentially save this: cancel the event/operation so that ItemInWorldManager's harvest method falls short, then recreate it with a more sensible structure public boolean onDig(World world, int x, int y, int z, EntityPlayer player, Block block, int meta, IItemAbility tool) { return false; } public abstract String getName(); public abstract String getFullName(); @@ -205,6 +204,63 @@ public abstract class ToolAbility { } } + public static class HammerSilkAbility extends ToolAbility { + + int range; + + public HammerSilkAbility(int range) { + this.range = range; + } + + @Override + public boolean onDig(World world, int x, int y, int z, EntityPlayer player, Block block, int meta, IItemAbility tool) { + if(EnchantmentHelper.getSilkTouchModifier(player) || player.getHeldItem() == null) + return false; + + ItemStack stack = player.getHeldItem(); + EnchantmentUtil.addEnchantment(stack, Enchantment.silkTouch, 1); + + for(int a = x - range; a <= x + range; a++) { + for(int b = y - range; b <= y + range; b++) { + for(int c = z - range; c <= z + range; c++) { + + if(a == x && b == y && c == z) + continue; + + tool.breakExtraBlock(world, a, b ,c, player, x, y, z); + } + } + } + if(player instanceof EntityPlayerMP) + IItemAbility.standardDigPost(world, x, y, z, (EntityPlayerMP) player); + + EnchantmentUtil.removeEnchantment(stack, Enchantment.silkTouch); + + return false; + + } + + @Override + public String getName() { + return "tool.ability.hammersilk"; + } + + @Override + public String getFullName() { + return I18n.format(getName()) + getExtension(); + } + + @Override + public String getExtension() { + return " (" + range + ")"; + } + + @Override + public boolean isAllowed() { + return ToolConfig.abilityHammer; + } + } + public static class SilkAbility extends ToolAbility { @Override diff --git a/src/main/java/com/hbm/inventory/gui/GUIScreenToolAbility.java b/src/main/java/com/hbm/inventory/gui/GUIScreenToolAbility.java new file mode 100644 index 000000000..7980f72a5 --- /dev/null +++ b/src/main/java/com/hbm/inventory/gui/GUIScreenToolAbility.java @@ -0,0 +1,339 @@ +package com.hbm.inventory.gui; + +import java.util.ArrayList; +import java.util.List; + +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.GL11; + +import com.hbm.items.ModItems; +import com.hbm.items.special.ItemHolotapeImage.EnumHoloImage; +import com.hbm.items.tool.ItemToolAbility; +import com.hbm.lib.RefStrings; +import com.hbm.util.EnumUtil; +import com.hbm.util.I18nUtil; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.audio.PositionedSoundRecord; +import net.minecraft.client.gui.GuiScreen; +import net.minecraft.client.renderer.OpenGlHelper; +import net.minecraft.client.renderer.Tessellator; +import net.minecraft.entity.player.EntityPlayer; +import net.minecraft.item.ItemStack; +import net.minecraft.item.ItemTool; +import net.minecraft.util.ResourceLocation; + +public class GUIScreenToolAbility extends GuiScreen { + + public static ResourceLocation texture = new ResourceLocation(RefStrings.MODID + ":textures/gui/tool/gui_tool_ability.png"); + + protected int guiLeft; + protected int guiTop; + protected int xSize; + protected int ySize; + protected int insetWidth; + + protected ItemToolAbility toolDef; + protected ItemStack toolStack; + + // TODO: Move elsewhere? + public static class AbilityInfo { + String name; + int textureU, textureV; + boolean hasLevels; + + public AbilityInfo(String name, int textureU, int textureV, boolean hasLevels) { + this.name = name; + this.textureU = textureU; + this.textureV = textureV; + this.hasLevels = hasLevels; + } + } + + public static final List abilitiesArea = new ArrayList<>(); + public static final List abilitiesHarvest = new ArrayList<>(); + + static { + abilitiesArea.add(new AbilityInfo(null, 0, 76, false)); + abilitiesArea.add(new AbilityInfo("tool.ability.recursion", 32, 76, false)); + abilitiesArea.add(new AbilityInfo("tool.ability.hammer", 64, 76, true)); + abilitiesArea.add(new AbilityInfo("tool.ability.explosion", 96, 76, true)); + + abilitiesHarvest.add(new AbilityInfo(null, 0, 92, false)); + abilitiesHarvest.add(new AbilityInfo("tool.ability.silktouch", 32, 92, false)); + abilitiesHarvest.add(new AbilityInfo("tool.ability.luck", 64, 92, false)); + abilitiesHarvest.add(new AbilityInfo("tool.ability.smelter", 96, 92, false)); + abilitiesHarvest.add(new AbilityInfo("tool.ability.shredder", 128, 92, false)); + abilitiesHarvest.add(new AbilityInfo("tool.ability.centrifuge", 160, 92, false)); + abilitiesHarvest.add(new AbilityInfo("tool.ability.crystallizer", 192, 92, false)); + abilitiesHarvest.add(new AbilityInfo("tool.ability.mercury", 224, 92, false)); + } + + // TODO: availability status for abilities; list of presets; selected preset index; + // TODO: Remove this in favor of current preset + int selectionIdxArea = 0; + int selectedLevelArea = 0; + int selectionIdxHarvest = 0; + int selectedLevelHarvest = 0; + int selectedPreset = 0; + int totalPresets = 1; + + public GUIScreenToolAbility(ItemToolAbility toolDef) { + super(); + + this.toolDef = toolDef; + this.xSize = 186; // Note: increased dynamically + this.ySize = 76; + + this.insetWidth = 20 * Math.max(abilitiesArea.size() - 4, abilitiesHarvest.size() - 8); + this.xSize += insetWidth; + } + + @Override + public void initGui() { + this.toolStack = this.mc.thePlayer.getHeldItem(); + + if(this.toolStack == null) { + doClose(); + } + + guiLeft = (width - xSize) / 2; + guiTop = (height - ySize) / 2; + } + + @Override + public void drawScreen(int mouseX, int mouseY, float f) { + this.drawDefaultBackground(); + + drawGuiContainerBackgroundLayer(mouseX, mouseY); + + if(!Mouse.isButtonDown(0) && !Mouse.isButtonDown(1) && Mouse.next()) { + int scroll = Mouse.getEventDWheel() / 6; + + // TODO + selectedPreset = (selectedPreset + totalPresets + scroll) % totalPresets; + } + } + + protected void drawGuiContainerBackgroundLayer(int mouseX, int mouseY) { + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + Minecraft.getMinecraft().getTextureManager().bindTexture(texture); + + // Draw window background + drawTexturedModalRect(guiLeft, guiTop, 0, 0, 100, ySize); + for (int i = 0; i < insetWidth; i += 20) { + drawTexturedModalRect(guiLeft + 100 + i, guiTop, 80, 0, 20, ySize); + } + drawTexturedModalRect(guiLeft + 100 + insetWidth, guiTop, 100, 0, xSize - insetWidth - 100, ySize); + + // Draw the switches + drawSwitches(abilitiesArea, selectionIdxArea, selectedLevelArea, guiLeft + 15, guiTop + 25, mouseX, mouseY); + drawSwitches(abilitiesHarvest, selectionIdxHarvest, selectedLevelHarvest, guiLeft + 15, guiTop + 45, mouseX, mouseY); + + // Draw preset indicator + drawNumber(selectedPreset + 1, guiLeft + insetWidth + 115, guiTop + 25); + drawNumber(totalPresets, guiLeft + insetWidth + 149, guiTop + 25); + + // Draw extra buttons hover highlights + int extraBtnsX = guiLeft + xSize - 75; + + for (int i = 0; i < 6; ++i) { + if (isInAABB(mouseX, mouseY, extraBtnsX + i * 11, guiTop + 11, 9, 9)) { + drawTexturedModalRect(extraBtnsX + i * 11, guiTop + 11, 186 + i * 9, 18, 9, 9); + } + } + } + + protected void drawSwitches(List abilities, int selectionIdx, int selectedLevel, int x, int y, int mouseX, int mouseY) { + for (int i = 0; i < abilities.size(); ++i) { + AbilityInfo abilityInfo = abilities.get(i); + boolean available = true; // TODO + + // Draw switch + drawTexturedModalRect(x + 20 * i, y, abilityInfo.textureU + (available ? 16 : 0), abilityInfo.textureV, 16, 16); + + // Draw level LEDs + if (abilityInfo.hasLevels) { + int level = 0; // TODO + + if (i == selectionIdx) { + level = selectedLevel + 1; + } + + drawTexturedModalRect(x + 20 * i + 17, y + 1, 222 + level * 2, 0, 2, 14); + } + + if (i == selectionIdx) { + // Draw selection highlight + drawTexturedModalRect(x + 20 * i - 1, y - 1, 186, 0, 18, 18); + } else if (available && isInAABB(mouseX, mouseY, x + 20 * i, y, 16, 16)) { + // Draw hover highlight + drawTexturedModalRect(x + 20 * i - 1, y - 1, 204, 0, 18, 18); + } + } + } + + protected void drawNumber(int number, int x, int y) { + number += 100; // Against accidental negatives + drawDigit((number / 10) % 10, x, y); + drawDigit(number % 10, x + 12, y); + } + + protected void drawDigit(int digit, int x, int y) { + drawTexturedModalRect(x, y, digit * 10, 108, 10, 15); + } + + private boolean isInAABB(int mouseX, int mouseY, int x, int y, int width, int height) { + return x <= mouseX && x + width > mouseX && y <= mouseY && y + height > mouseY; + } + + @Override + public void updateScreen() { + EntityPlayer player = this.mc.thePlayer; + + if(player.getHeldItem() == null || player.getHeldItem().getItem() != toolDef) + player.closeScreen(); + } + + @Override + protected void mouseClicked(int mouseX, int mouseY, int button) { + // TODO + boolean clicked = false; + + // TODO: Encapsulate? + for (int i = 0; i < abilitiesArea.size(); ++i) { + AbilityInfo abilityInfo = abilitiesArea.get(i); + boolean available = true; // TODO + + if (available && isInAABB(mouseX, mouseY, guiLeft + 15 + 20 * i, guiTop + 25, 16, 16)) { + if (abilityInfo.hasLevels) { + int availableLevels = 5; + + if (i == selectionIdxArea) { + selectedLevelArea = (selectedLevelArea + 1) % availableLevels; + } else { + selectedLevelArea = 0; + } + } + + selectionIdxArea = i; + clicked = true; + break; + } + } + + for (int i = 0; i < abilitiesHarvest.size(); ++i) { + AbilityInfo abilityInfo = abilitiesHarvest.get(i); + boolean available = true; // TODO + + if (available && isInAABB(mouseX, mouseY, guiLeft + 15 + 20 * i, guiTop + 45, 16, 16)) { + if (abilityInfo.hasLevels) { + int availableLevels = 5; + + if (i == selectionIdxArea) { + selectedLevelHarvest = (selectedLevelHarvest + 1) % availableLevels; + } else { + selectedLevelHarvest = 0; + } + } + + selectionIdxHarvest = i; + clicked = true; + break; + } + } + + if (clicked) { + mc.getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("hbm:item.techBoop"), 1F)); + } + + // TODO + int extraBtnsX = guiLeft + xSize - 75; + + clicked = false; + + if (isInAABB(mouseX, mouseY, extraBtnsX + 0 * 11, guiTop + 11, 9, 9)) { + doDelPreset(); + clicked = true; + } + + if (isInAABB(mouseX, mouseY, extraBtnsX + 1 * 11, guiTop + 11, 9, 9)) { + doAddPreset(); + clicked = true; + } + + if (isInAABB(mouseX, mouseY, extraBtnsX + 2 * 11, guiTop + 11, 9, 9)) { + doZeroPreset(); + clicked = true; + } + + if (isInAABB(mouseX, mouseY, extraBtnsX + 3 * 11, guiTop + 11, 9, 9)) { + doNextPreset(); + clicked = true; + } + + if (isInAABB(mouseX, mouseY, extraBtnsX + 4 * 11, guiTop + 11, 9, 9)) { + doPrevPreset(); + clicked = true; + } + + if (isInAABB(mouseX, mouseY, extraBtnsX + 5 * 11, guiTop + 11, 9, 9)) { + doClose(); + clicked = true; + } + + if (clicked) { + mc.getSoundHandler().playSound(PositionedSoundRecord.func_147674_a(new ResourceLocation("gui.button.press"), 0.5F)); + } + } + + @Override + protected void keyTyped(char c, int key) { + if(key == 1 || key == this.mc.gameSettings.keyBindInventory.getKeyCode()) { + doClose(); + return; + } + + super.keyTyped(c, key); + } + + @Override + public boolean doesGuiPauseGame() { + return false; + } + + protected void doDelPreset() { + // TODO + if (totalPresets <= 1) { + return; + } + totalPresets -= 1; + selectedPreset -= 1; + } + + protected void doAddPreset() { + // TODO + totalPresets += 1; + selectedPreset += 1; + } + + protected void doZeroPreset() { + // TODO + selectedPreset = 0; + } + + protected void doNextPreset() { + // TODO + selectedPreset = (selectedPreset + 1) % totalPresets; + } + + protected void doPrevPreset() { + // TODO + selectedPreset = (selectedPreset + totalPresets - 1) % totalPresets; + } + + protected void doClose() { + this.mc.thePlayer.closeScreen(); + } +} \ No newline at end of file diff --git a/src/main/java/com/hbm/items/tool/ItemToolAbility.java b/src/main/java/com/hbm/items/tool/ItemToolAbility.java index 99ce2d356..e0c8fd8a4 100644 --- a/src/main/java/com/hbm/items/tool/ItemToolAbility.java +++ b/src/main/java/com/hbm/items/tool/ItemToolAbility.java @@ -9,11 +9,15 @@ import java.util.Set; import com.google.common.collect.HashMultimap; import com.google.common.collect.Multimap; import com.google.common.collect.Sets; +import com.hbm.inventory.gui.GUIScreenToolAbility; +import com.hbm.handler.HbmKeybinds; +import com.hbm.extprop.HbmPlayerProps; import com.hbm.handler.ToolAbility; import com.hbm.handler.ToolAbility.*; import com.hbm.main.MainRegistry; import com.hbm.packet.PacketDispatcher; import com.hbm.packet.toclient.PlayerInformPacket; +import com.hbm.tileentity.IGUIProvider; import com.hbm.util.ChatBuilder; import com.hbm.handler.WeaponAbility; @@ -28,6 +32,7 @@ import net.minecraft.entity.ai.attributes.AttributeModifier; import net.minecraft.entity.player.EntityPlayer; import net.minecraft.entity.player.EntityPlayerMP; import net.minecraft.init.Blocks; +import net.minecraft.inventory.Container; import net.minecraft.item.EnumRarity; import net.minecraft.item.ItemStack; import net.minecraft.item.ItemTool; @@ -35,7 +40,7 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.util.EnumChatFormatting; import net.minecraft.world.World; -public class ItemToolAbility extends ItemTool implements IItemAbility, IDepthRockTool { +public class ItemToolAbility extends ItemTool implements IItemAbility, IDepthRockTool, IGUIProvider { protected boolean isShears = false; protected EnumToolType toolType; @@ -228,7 +233,15 @@ public class ItemToolAbility extends ItemTool implements IItemAbility, IDepthRoc public ItemStack onItemRightClick(ItemStack stack, World world, EntityPlayer player) { - if(world.isRemote || this.breakAbility.size() < 2 || !canOperate(stack)) + if(this.breakAbility.size() < 2 || !canOperate(stack)) + return super.onItemRightClick(stack, world, player); + + if(HbmPlayerProps.getData(player).getKeyPressed(HbmKeybinds.EnumKeybind.TOOL_ALT)) { + if(world.isRemote) player.openGui(MainRegistry.instance, 0, world, 0, 0, 0); + return stack; + } + + if(world.isRemote) return super.onItemRightClick(stack, world, player); int i = getAbility(stack); @@ -300,4 +313,15 @@ public class ItemToolAbility extends ItemTool implements IItemAbility, IDepthRoc public boolean isShears(ItemStack stack) { return this.isShears; } + + @Override + public Container provideContainer(int ID, EntityPlayer player, World world, int x, int y, int z) { + return null; + } + + @Override + @SideOnly(Side.CLIENT) + public Object provideGUI(int ID, EntityPlayer player, World world, int x, int y, int z) { + return new GUIScreenToolAbility(this); + } } diff --git a/src/main/resources/assets/hbm/textures/gui/tool/gui_tool_ability.png b/src/main/resources/assets/hbm/textures/gui/tool/gui_tool_ability.png new file mode 100644 index 0000000000000000000000000000000000000000..5e53a867d0d9413790f32e433d6f0d090c3005fa GIT binary patch literal 4053 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