package joshie.progression.gui.editors; import com.google.common.cache.Cache; import com.google.common.cache.CacheBuilder; import joshie.progression.api.criteria.ICriteria; import joshie.progression.api.criteria.IRewardProvider; import joshie.progression.api.special.ICustomTreeIcon; import joshie.progression.gui.core.GuiList; import joshie.progression.helpers.PlayerHelper; import joshie.progression.json.Options; import joshie.progression.lib.PInfo; import joshie.progression.player.PlayerTracker; import net.minecraft.client.gui.GuiScreen; import net.minecraft.client.renderer.GlStateManager; import net.minecraft.client.renderer.RenderHelper; import net.minecraft.item.ItemStack; import net.minecraft.util.text.TextFormatting; import org.lwjgl.input.Keyboard; import org.lwjgl.opengl.GL11; import java.util.*; import java.util.concurrent.Callable; import static joshie.progression.api.special.DisplayMode.DISPLAY; import static joshie.progression.api.special.DisplayMode.EDIT; import static joshie.progression.gui.core.GuiList.*; import static joshie.progression.gui.editors.TreeEditorElement.ColorMode.*; public class TreeEditorElement { private static final Cache<ICriteria, Counter> ticker = CacheBuilder.newBuilder().maximumSize(1024).build(); private final ICriteria criteria; private boolean isSelected; private boolean isHeld; private int prevX; private int prevY; protected int left; protected int right; protected int top; protected int bottom; protected int width = 200; protected int height = 25; public TreeEditorElement(ICriteria criteria) { this.criteria = criteria; } public int getX() { return criteria.getX(); } public int getY() { return criteria.getY(); } private int getWidthFromRewards() { int size = criteria.getRewards().size(); if (size == 1) { return 21; //12 Bigger 1 * 12 + 9 } else if (size == 2) { return 33; // 12 Bigger 2 * 12 } else if (size == 3) { return 45; //12 Bigger } else if (size == 4) { return 57; //12 Bigger } return 0; } private void recalculate(int x) { int textWidth = CORE.mc.fontRendererObj.getStringWidth(criteria.getLocalisedName()) + 9; int iconWidth = 9 + (criteria.getRewards().size() * 12); if (textWidth >= iconWidth) { width = textWidth; } else width = iconWidth; width = Math.max(width, 20); left = criteria.getX() + x; right = (int) (criteria.getX() + width) + x; top = criteria.getY(); bottom = (int) (criteria.getY() + height); } public void draw(int x, int y, int offsetX) { draw(x, y, offsetX, 0); } public boolean isCriteriaVisible() { if (criteria.isVisible()) return true; else return PlayerTracker.getClientPlayer().getMappings().getCompletedCriteria().keySet().containsAll(criteria.getPreReqs()); } private static boolean isCriteriaCompleteable(ICriteria criteria) { HashMap<ICriteria, Integer> completedMap = PlayerTracker.getClientPlayer().getMappings().getCompletedCriteria(); boolean completeable = true; //Check the conflicts of this criteria for (ICriteria conflicts : criteria.getConflicts()) { if (completedMap.containsKey(conflicts)) return false; } //Check the requirements, if they aren't completable return false for (ICriteria requirements : criteria.getPreReqs()) { if (requirements.equals(criteria)) return false; if (!isCriteriaCompleteable(requirements)) return false; } return true; } public enum ColorMode { DEFAULT(false, 0), COMPLETED(true, 25), AVAILABLE(true, 50), ERROR(false, 75), SELECTED(true, 100), UNUSED(true, 125), READY(true, 150), INVISIBLE(false, 0); public final int y; public final boolean openable; private ColorMode(boolean openable, int y) { this.openable = openable; this.y = y; } } private static class Counter { int count; } private static Counter getCounter(ICriteria criteria) { try { return ticker.get(criteria, new Callable<Counter>() { @Override public Counter call() throws Exception { return new Counter(); } }); } catch (Exception e) { return new Counter();} } private static boolean rewardsAvailable(ICriteria criteria) { for (IRewardProvider provider: criteria.getRewards()) { UUID uuid = provider.isOnePerTeam() ? PlayerTracker.getClientPlayer().getTeam().getOwner() : PlayerHelper.getClientUUID(); if (PlayerTracker.getClientPlayer().getMappings().getUnclaimedRewards(uuid).contains(provider)) { return true; } } return false; } public static ColorMode getModeForCriteria(ICriteria criteria, boolean isSelected) { ColorMode mode = DEFAULT; HashMap<ICriteria, Integer> completedMap = PlayerTracker.getClientPlayer().getMappings().getCompletedCriteria(); boolean isCompleted = completedMap.containsKey(criteria); boolean anyConflicts = false; boolean allRequires = false; int requires = 0; for (ICriteria c : criteria.getConflicts()) { if (completedMap.containsKey(c)) { anyConflicts = true; break; } } if (!anyConflicts) { for (ICriteria c : criteria.getPreReqs()) { if (completedMap.containsKey(c)) { requires++; } } allRequires = criteria.getPreReqs().size() == requires; } boolean available = allRequires && !anyConflicts; if (isCompleted) { mode = COMPLETED; } else if (available) mode = AVAILABLE; if (rewardsAvailable(criteria)) { Counter counter = getCounter(criteria); if (counter.count < 250) { mode = READY; } else mode = UNUSED; if (counter.count < 500) counter.count++; else counter.count = 0; } if (!criteria.isVisible()) { if (MODE == DISPLAY) { if (available || isCompleted) { } else return INVISIBLE; } } if (isSelected) mode = ColorMode.SELECTED; ICriteria selected = TREE_EDITOR.lastClicked; if (!isCompleted) { if (!isCriteriaCompleteable(criteria)) { mode = ERROR; } } if (selected != null) { if (selected.getConflicts().contains(criteria)) { mode = ERROR; } } if (PlayerTracker.getClientPlayer().getMappings().isImpossible(criteria)) { mode = ERROR; } return mode; } public void draw(int x, int y, int offsetX, int highlight) { recalculate(offsetX); if (highlight != 0) { CORE.drawRectWithBorder(x + left, top, x + right, bottom, THEME.invisible, highlight); } else { ColorMode mode = getModeForCriteria(criteria, isSelected); if (mode == INVISIBLE) return; //If we returned invisible, don't show it int textureX = 0; int textureY = mode.y; if (!criteria.isVisible()) { //Make the texture transparent if this is edit MODE if (MODE == EDIT) { textureX = 100; } } GlStateManager.enableBlend(); GlStateManager.color(1F, 1F, 1F, 1F); CORE.mc.getTextureManager().bindTexture(PInfo.textures); CORE.drawTexture(PInfo.textures, x + left, top, textureX, textureY, 10, 25); for (int i = 0; i < width - 20; i++) { CORE.drawTexture(PInfo.textures, x + left + 10 + i, top, textureX + 10, textureY, 1, 25); } CORE.drawTexture(PInfo.textures, x + right - 10, top, textureX + 90, textureY, 10, 25); //gui.drawTexturedModalRect(x + left, y + top, textureX, textureY, 100, 25); CORE.drawText(criteria.getLocalisedName(), x + left + 4, top + 3, THEME.criteriaDisplayNameColor); GlStateManager.clear(GL11.GL_DEPTH_BUFFER_BIT); //Draw in the rewards int xOffset = 0; for (final IRewardProvider reward : criteria.getRewards()) { final float scale = 0.75F; final int xPos = x + 4 + left + (xOffset * 12); final int yPos = top + 12; if (reward.getProvided() instanceof ICustomTreeIcon) { LAST.add(new Callable() { @Override public Object call() throws Exception { ((ICustomTreeIcon)reward.getProvided()).draw(xPos, yPos, scale); return null; } }); } else { ItemStack icon = reward.getIcon(); if (icon == null || icon.getItem() == null) continue; //Protection against null icons icon = icon.copy(); icon.stackSize = 1; //Force it to 1 CORE.drawStack(icon, xPos, yPos, scale); } xOffset++; } int mouseX = CORE.mouseX; int mouseY = CORE.mouseY; GlStateManager.clear(GL11.GL_DEPTH_BUFFER_BIT); xOffset = 0; boolean hoveredReward = false; for (IRewardProvider reward : criteria.getRewards()) { int x1 = 3 + left + (xOffset * 12); int x2 = x1 + 11; int y1 = bottom - 13; int y2 = y1 + 12; if (isOver(mouseX, mouseY, x1, x2, y1, y2)) { List list = new ArrayList(); reward.addTooltip(list); GuiList.TOOLTIP.add(list); hoveredReward = true; } xOffset++; } RenderHelper.disableStandardItemLighting(); if (!hoveredReward) { //If we weren't hovering over the reward, display the requirements if (isOver(mouseX, mouseY)) { List list = new ArrayList(); if (MODE == EDIT && !Options.hideTooltips) { list.add("Double Click to edit "/* + (Hold shift for display MODE) */); list.add("Shift + Click to make something a requirement"); list.add("Ctrl + Click to make something conflict"); list.add("I + Click to Hide/Unhide"); } for (ICriteria c : criteria.getPreReqs()) { if (c.getTab() != criteria.getTab()) { list.add(TextFormatting.RED + "Requires: " + c.getLocalisedName() + " from the \"" + c.getTab().getLocalisedName() + "\" tab"); } } GuiList.TOOLTIP.add(list); RenderHelper.disableStandardItemLighting(); } } } } private boolean isOver(int mouseX, int mouseY, int x1, int x2, int y1, int y2) { return mouseX >= x1 && mouseX <= x2 && mouseY >= y1 && mouseY <= y2; } private boolean noOtherSelected() { return TREE_EDITOR.selected == null; } private void clearSelected() { TREE_EDITOR.selected = null; } private void setSelected() { TREE_EDITOR.selected = criteria; TREE_EDITOR.previous = criteria; } private ICriteria getPrevious() { return TREE_EDITOR.previous; } private boolean isOver(int x, int y) { return x >= left && x <= right && y >= top && y <= bottom; } private void remove(List<ICriteria> list, ICriteria criteria) { Iterator<ICriteria> it = list.iterator(); while (it.hasNext()) { ICriteria c = it.next(); if (c.equals(criteria)) { it.remove(); break; } } } public boolean keyTyped(char character, int key) { if (isSelected && MODE == EDIT) { return key == 211 || key == 14; } return false; } public boolean isAssignableTo(ICriteria to, ICriteria from) { if (to.equals(from)) return false; //If they are the same criteria we can't assign them if (from.getPreReqs().contains(to)) return false; //If the criteria we are trying to assign has this one in it, we can't //We need to check down the chain to stop the assignments return true; } public boolean click(int x, int y, boolean isDouble) { if (isOver(x, y)) { if (noOtherSelected()) { ICriteria previous = getPrevious(); if (previous != null && MODE == EDIT) { List<ICriteria> list = null; boolean isConflict = false; if (GuiScreen.isShiftKeyDown()) { list = previous.getPreReqs(); if (previous.getConflicts().contains(criteria)) { list = null; } } else if (GuiScreen.isCtrlKeyDown()) { list = previous.getConflicts(); isConflict = true; } if (!isAssignableTo(previous, criteria)) list = null; if (list != null) { if (list.contains(criteria)) { remove(list, criteria); if (isConflict) { remove(criteria.getConflicts(), previous); } } else { //Now that it's been added, move everything list.add(criteria); if (isConflict) { criteria.getConflicts().add(previous); } } return true; } } if (MODE == EDIT) { if (Keyboard.isKeyDown(Keyboard.KEY_I)) { criteria.setVisiblity(!criteria.isVisible()); return true; } } if (isDouble && TREE_EDITOR.previous == criteria) { isHeld = false; isSelected = false; CRITERIA_EDITOR.set(criteria); CORE.setEditor(CRITERIA_EDITOR); return true; } isHeld = true; isSelected = true; prevX = x; prevY = y; setSelected(); return true; } return false; } else { if (isSelected && x >= 0) { clearSelected(); isSelected = false; } return false; } } public void release(int x, int y) { if (isHeld) { isHeld = false; clearSelected(); } } public void follow(int x, int y) { if (isHeld && MODE == EDIT) { criteria.setCoordinates(criteria.getX() + x - prevX, criteria.getY() + y - prevY); prevX = x; prevY = y; } } public void scroll(boolean scrolledDown) { return; } }