From 1c05e9d15492df0dd98876baa46e4e4d2cf4aae7 Mon Sep 17 00:00:00 2001 From: Discreater Date: Sat, 1 May 2021 22:34:55 +0800 Subject: [PATCH] fix --- .../jecalculation/data/Controller.java | 55 ++++--- .../jecalculation/data/label/ILabel.java | 95 ++++++++---- .../data/label/labels/LFluidStack.java | 11 +- .../data/label/labels/LItemStack.java | 62 +++++++- .../data/label/labels/LOreDict.java | 89 ++++------- .../data/label/labels/LPlaceholder.java | 10 +- .../data/structure/CostList.java | 4 +- .../jecalculation/data/structure/Recents.java | 6 +- .../jecalculation/data/structure/Recipe.java | 39 +++-- .../jecalculation/data/structure/Recipes.java | 51 ++++-- .../me/towdium/jecalculation/gui/JecaGui.java | 31 ++-- .../towdium/jecalculation/gui/Resource.java | 145 +++++++++++------- .../jecalculation/gui/guis/GuiCalculator.java | 95 ++++++++++-- .../gui/guis/GuiDisambiguation.java | 12 +- .../jecalculation/gui/guis/GuiImport.java | 10 +- .../jecalculation/gui/guis/GuiLabel.java | 2 +- .../jecalculation/gui/guis/GuiRecipe.java | 92 ++++++----- .../jecalculation/gui/guis/GuiSearch.java | 50 ++++-- .../gui/guis/pickers/PickerItemStack.java | 53 +++---- .../gui/guis/pickers/PickerPlaceholder.java | 2 +- .../gui/guis/pickers/PickerSimple.java | 8 +- .../jecalculation/gui/widgets/IContainer.java | 8 +- .../jecalculation/gui/widgets/WButton.java | 18 +-- .../gui/widgets/WButtonIcon.java | 2 +- .../jecalculation/gui/widgets/WContainer.java | 17 +- .../jecalculation/gui/widgets/WHelp.java | 68 ++++---- .../jecalculation/gui/widgets/WLabel.java | 51 +++--- .../jecalculation/gui/widgets/WLine.java | 20 ++- .../jecalculation/gui/widgets/WPage.java | 6 +- .../jecalculation/gui/widgets/WScroll.java | 2 +- .../jecalculation/gui/widgets/WSwitcher.java | 20 +-- .../jecalculation/gui/widgets/WTextField.java | 4 + .../jecalculation/gui/widgets/WTick.java | 33 ++-- .../jecalculation/polyfill/NBTHelper.java | 4 +- .../jecalculation/utils/Utilities.java | 13 +- .../assets/jecalculation/lang/en_US.lang | 49 ++++-- .../jecalculation/textures/gui/resources.png | Bin 21034 -> 23287 bytes 37 files changed, 763 insertions(+), 474 deletions(-) diff --git a/src/main/java/me/towdium/jecalculation/data/Controller.java b/src/main/java/me/towdium/jecalculation/data/Controller.java index 13bb5cb..063a339 100644 --- a/src/main/java/me/towdium/jecalculation/data/Controller.java +++ b/src/main/java/me/towdium/jecalculation/data/Controller.java @@ -10,16 +10,11 @@ import me.towdium.jecalculation.data.structure.Recipe; import me.towdium.jecalculation.data.structure.Recipes; import me.towdium.jecalculation.utils.Utilities; import me.towdium.jecalculation.utils.wrappers.Pair; -import me.towdium.jecalculation.utils.wrappers.Trio; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.CompressedStreamTools; import net.minecraft.nbt.NBTTagCompound; import javax.annotation.ParametersAreNonnullByDefault; import java.io.File; -import java.io.FileInputStream; -import java.io.FileOutputStream; -import java.io.IOException; import java.util.*; import java.util.function.Function; import java.util.stream.Collectors; @@ -35,8 +30,10 @@ public class Controller { public static final String KEY_RECIPES = "recipes"; public static final String KEY_RECENTS = "recents"; public static final String KEY_AMOUNT = "amount"; + public static final String KEY_INVENTORY = "inventory"; static Recipes recipesClient; static Recents recentsClient; + static boolean inventoryClient; static String amountClient; static Recipes getRecord() { @@ -51,26 +48,26 @@ public class Controller { public static List> discover() { File dir = JecaConfig.dataDir; File[] fs = dir.listFiles(); - if (fs == null) return new ArrayList<>(); + if (fs == null) + return new ArrayList<>(); Function read = f -> { NBTTagCompound nbt = Utilities.Json.read(f); JustEnoughCalculation.logger.warn("File " + f.getAbsolutePath() + " contains invalid records."); return nbt == null ? null : new Recipes(nbt); }; - return Arrays.stream(fs) - .map(i -> new Pair<>(i.getName(), read.apply(i))) - .filter(i -> i.two != null) + return Arrays.stream(fs).map(i -> new Pair<>(i.getName(), read.apply(i))).filter(i -> i.two != null) .collect(Collectors.toList()); } public static void inport(Recipes recipes, String group) { ArrayList buffer = new ArrayList<>(); - recipes.getGroup(group).forEach(i -> - getRecord().getGroup(group).stream().filter(j -> j.equals(i)).findAny().orElseGet(() -> { - buffer.add(i); - return null; - })); - for (Recipe r : buffer) addRecipe(group, r); + recipes.getGroup(group) + .forEach(i -> getRecord().getGroup(group).stream().filter(j -> j.equals(i)).findAny().orElseGet(() -> { + buffer.add(i); + return null; + })); + for (Recipe r : buffer) + addRecipe(group, r); } public static File export(String group) { @@ -132,16 +129,27 @@ public class Controller { amountClient = amount; } + public static boolean getDetectInv() { + return inventoryClient; + } + + public static void setDetectInv(boolean b) { + inventoryClient = b; + } public static List getRecent() { return recentsClient.getRecords(); } - public static void setRecent(ILabel label) { - recentsClient.push(label); + public static void setRecent(ILabel label, boolean replace) { + recentsClient.push(label, replace); writeToLocal(); } + public static boolean hasDuplicate(Recipe r) { + return getRecord().hasDuplicate(r); + } + public static void loadFromLocal() { //noinspection ResultOfMethodCallIgnored JecaConfig.dataDir.mkdirs(); @@ -151,13 +159,13 @@ public class Controller { recipesClient = nbt.hasKey(KEY_RECIPES) ? new Recipes(nbt.getCompoundTag(KEY_RECIPES)) : new Recipes(); recentsClient = nbt.hasKey(KEY_RECENTS) ? new Recents(nbt.getTagList(KEY_RECENTS, 10)) : new Recents(); amountClient = nbt.hasKey(KEY_AMOUNT) ? nbt.getString(KEY_AMOUNT) : ""; - return; + inventoryClient = !nbt.hasKey(KEY_INVENTORY) || nbt.getBoolean(KEY_INVENTORY); + } else { + recipesClient = new Recipes(); + recentsClient = new Recents(); + amountClient = ""; + inventoryClient = true; } - file = JecaConfig.defaultFile; - nbt = Utilities.Json.read(file); - recipesClient = nbt == null ? new Recipes() : new Recipes(nbt); - recentsClient = new Recents(); - amountClient = ""; } public static void writeToLocal() { @@ -166,6 +174,7 @@ public class Controller { nbt.setTag(KEY_RECENTS, recentsClient.serialize()); nbt.setTag(KEY_RECIPES, recipesClient.serialize()); nbt.setString(KEY_AMOUNT, amountClient); + nbt.setBoolean(KEY_INVENTORY, inventoryClient); Utilities.Json.write(nbt, file); } } diff --git a/src/main/java/me/towdium/jecalculation/data/label/ILabel.java b/src/main/java/me/towdium/jecalculation/data/label/ILabel.java index df8854a..ce47c57 100644 --- a/src/main/java/me/towdium/jecalculation/data/label/ILabel.java +++ b/src/main/java/me/towdium/jecalculation/data/label/ILabel.java @@ -1,5 +1,6 @@ package me.towdium.jecalculation.data.label; +import codechicken.nei.recipe.IRecipeHandler; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import me.towdium.jecalculation.JustEnoughCalculation; @@ -15,6 +16,7 @@ import me.towdium.jecalculation.gui.guis.pickers.PickerSimple; import me.towdium.jecalculation.polyfill.mc.client.renderer.GlStateManager; import me.towdium.jecalculation.utils.Utilities; import me.towdium.jecalculation.utils.Utilities.ReversedIterator; +import me.towdium.jecalculation.utils.wrappers.Pair; import net.minecraft.init.Items; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; @@ -23,10 +25,7 @@ import net.minecraftforge.fluids.FluidStack; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Optional; +import java.util.*; import java.util.function.BiPredicate; import java.util.function.Function; import java.util.function.Supplier; @@ -56,7 +55,11 @@ public interface ILabel { ILabel decreaseAmount(); static void initClient() { - CONVERTER.register(LOreDict::guess); + CONVERTER.register(LItemStack::suggest, Converter.Priority.SUGGEST); + CONVERTER.register(LOreDict::suggest, Converter.Priority.SUGGEST); + CONVERTER.register(LFluidStack::suggest, Converter.Priority.SUGGEST); + CONVERTER.register(LItemStack::fallback, Converter.Priority.FALLBACK); + CONVERTER.register(LOreDict::fallback, Converter.Priority.FALLBACK); EDITOR.register(PickerSimple.FluidStack::new, "fluid", new LFluidStack(1000, FluidRegistry.WATER)); EDITOR.register(PickerSimple.OreDict::new, "ore", new LOreDict("ingotIron")); EDITOR.register(PickerPlaceholder::new, "placeholder", new LPlaceholder("example", 1, true)); @@ -64,7 +67,10 @@ public interface ILabel { MERGER.register("itemStack", "itemStack", Impl.form(LItemStack.class, LItemStack.class, LItemStack::merge)); MERGER.register("oreDict", "oreDict", Impl.form(LOreDict.class, LOreDict.class, LOreDict::mergeSame)); MERGER.register("oreDict", "itemStack", Impl.form(LOreDict.class, LItemStack.class, LOreDict::mergeFuzzy)); - MERGER.register("fluidStack", "fluidStack", Impl.form(LFluidStack.class, LFluidStack.class, LFluidStack::merge)); + MERGER.register("fluidStack", "fluidStack", + Impl.form(LFluidStack.class, LFluidStack.class, LFluidStack::merge)); + MERGER.register("placeholder", "placeholder", + Impl.form(LPlaceholder.class, LPlaceholder.class, LPlaceholder::merge)); } long getAmount(); @@ -137,7 +143,8 @@ public interface ILabel { */ public Optional merge(ILabel a, ILabel b) { MergerFunction mf = functions.get(a.getIdentifier(), b.getIdentifier()); - if (mf == null) return Optional.empty(); + if (mf == null) + return Optional.empty(); return Optional.ofNullable(mf.merge(a, b)); } @@ -190,11 +197,13 @@ public interface ILabel { public ILabel deserialize(NBTTagCompound nbt) { String s = nbt.getString(KEY_IDENTIFIER); Function func = idToData.get(s); - if (func == null) JustEnoughCalculation.logger.warn("Unrecognized identifier \"" + s + "\", abort"); - else try { - return func.apply(nbt.getCompoundTag(KEY_CONTENT)); - } catch (SerializationException ignored) { - } + if (func == null) + JustEnoughCalculation.logger.warn("Unrecognized identifier \"" + s + "\", abort"); + else + try { + return func.apply(nbt.getCompoundTag(KEY_CONTENT)); + } catch (SerializationException ignored) { + } return EMPTY; } @@ -219,7 +228,15 @@ public interface ILabel { * It can also convert ItemStack or FluidStack to ILabel */ class Converter { - ArrayList, List>> handlers = new ArrayList<>(); + static EnumMap> handlers; + + public enum Priority {SUGGEST, FALLBACK} + + static { + handlers = new EnumMap<>(Priority.class); + handlers.put(Priority.SUGGEST, new ArrayList<>()); + handlers.put(Priority.FALLBACK, new ArrayList<>()); + } public static ILabel from(@Nullable Object o) { if (o == null) @@ -232,20 +249,37 @@ public interface ILabel { throw new RuntimeException("Unrecognized ingredient type: " + o.getClass()); } - public void register(Function, List> handler) { - handlers.add(handler); + public void register(ConverterFunction handler, Priority priority) { + handlers.get(priority).add(handler); } // get most possible guess from labels - public ILabel first(List labels) { - List guess = guess(labels); + public ILabel first(List labels, @Nullable IRecipeHandler context) { + List guess = guess(labels, context).one; return guess.isEmpty() ? labels.get(0) : guess.get(0); } + public ILabel first(List labels) { + return first(labels, null); + } + // to test if the labels can be converted to other labels (like oreDict) - public List guess(List labels) { - return new ReversedIterator<>(handlers).stream().flatMap(h -> h.apply(labels).stream()) - .collect(Collectors.toList()); + public Pair, List> guess(List labels) { + return guess(labels, null); + } + + public Pair, List> guess(List labels, @Nullable IRecipeHandler context) { + List ret = new ArrayList<>(); + List suggest = new ReversedIterator<>(handlers.get(Priority.SUGGEST)).stream().flatMap( + h -> h.convert(labels, context).stream()).collect(Collectors.toList()); + List fallback = new ReversedIterator<>(handlers.get(Priority.FALLBACK)).stream().flatMap( + h -> h.convert(labels, context).stream()).collect(Collectors.toList()); + return new Pair<>(suggest, fallback); + } + + @FunctionalInterface + public interface ConverterFunction { + List convert(List a, @Nullable IRecipeHandler context); } } @@ -440,17 +474,22 @@ public interface ILabel { long amountD = d.isPercent() ? d.getAmount() : Math.multiplyExact(d.getAmount(), 100); long amount = Math.addExact(amountC, amountD); long amountI = (amount > 0 ? Math.addExact(amount, 99) : Math.subtractExact(amount, 99)) / 100; - if (amount == 0) return EMPTY; - else if (amount > 0) return d.copy().setAmount(d.isPercent() ? amount : amountI); - else return c.copy().setAmount(c.isPercent() ? amount : amountI); - } else return null; + if (amount == 0) + return EMPTY; + else if (amount > 0) + return d.copy().setAmount(d.isPercent() ? amount : amountI); + else + return c.copy().setAmount(c.isPercent() ? amount : amountI); + } else + return null; }; } @Override public ILabel multiply(float i) { float amount = i * getAmount(); - if (amount > Long.MAX_VALUE) throw new ArithmeticException("Multiply overflow"); + if (amount > Long.MAX_VALUE) + throw new ArithmeticException("Multiply overflow"); setAmount((long) amount); return this; } @@ -460,8 +499,7 @@ public interface ILabel { @SideOnly(Side.CLIENT) public void getToolTip(List existing, boolean detailed) { if (detailed) - existing.add( - FORMAT_GREY + Utilities.I18n.get("label.common.tooltip.amount", getAmountString(false))); + existing.add(FORMAT_GREY + Utilities.I18n.get("label.common.amount", getAmountString(false))); } @Override @@ -481,7 +519,8 @@ public interface ILabel { public NBTTagCompound toNbt() { NBTTagCompound nbt = new NBTTagCompound(); nbt.setLong(KEY_AMOUNT, amount); - if (percent) nbt.setBoolean(KEY_PERCENT, true); + if (percent) + nbt.setBoolean(KEY_PERCENT, true); return nbt; } diff --git a/src/main/java/me/towdium/jecalculation/data/label/labels/LFluidStack.java b/src/main/java/me/towdium/jecalculation/data/label/labels/LFluidStack.java index 88140cc..9c6c8e0 100644 --- a/src/main/java/me/towdium/jecalculation/data/label/labels/LFluidStack.java +++ b/src/main/java/me/towdium/jecalculation/data/label/labels/LFluidStack.java @@ -1,5 +1,6 @@ package me.towdium.jecalculation.data.label.labels; +import codechicken.nei.recipe.IRecipeHandler; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import me.towdium.jecalculation.gui.JecaGui; @@ -13,6 +14,8 @@ import net.minecraftforge.fluids.FluidStack; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -122,7 +125,7 @@ public class LFluidStack extends ILabel.Impl { @SideOnly(Side.CLIENT) public void getToolTip(List existing, boolean detailed) { if (detailed) existing.add(FORMAT_GREY + - Utilities.I18n.get("label.common.tooltip.amount", Long.toString(getAmount())) + "mB"); + Utilities.I18n.get("label.common.amount", Long.toString(getAmount())) + "mB"); existing.add(FORMAT_BLUE + FORMAT_ITALIC + Utilities.getModName(fluid)); } @@ -136,7 +139,11 @@ public class LFluidStack extends ILabel.Impl { @Override public int hashCode() { - return fluid.getUnlocalizedName().hashCode() ^ (int) amount ^ (nbt == null ? 0 : nbt.hashCode()); + return super.hashCode() ^ fluid.getUnlocalizedName().hashCode() ^ (nbt == null ? 0 : nbt.hashCode()); + } + + public static List suggest(List iss, @Nullable IRecipeHandler rl) { + return new ArrayList<>(); } public static boolean merge(ILabel a, ILabel b) { diff --git a/src/main/java/me/towdium/jecalculation/data/label/labels/LItemStack.java b/src/main/java/me/towdium/jecalculation/data/label/labels/LItemStack.java index 8a8da7c..b617201 100644 --- a/src/main/java/me/towdium/jecalculation/data/label/labels/LItemStack.java +++ b/src/main/java/me/towdium/jecalculation/data/label/labels/LItemStack.java @@ -1,5 +1,6 @@ package me.towdium.jecalculation.data.label.labels; +import codechicken.nei.recipe.IRecipeHandler; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import me.towdium.jecalculation.data.label.ILabel; @@ -12,6 +13,8 @@ import net.minecraft.nbt.NBTTagCompound; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; +import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.Objects; @@ -77,13 +80,13 @@ public class LItemStack extends ILabel.Impl { boolean fNbt) { Objects.requireNonNull(item); this.item = item; - this.meta = meta; - this.nbt = nbt; + this.meta = fMeta ? 0 : meta; + this.nbt = fNbt ? null : nbt; this.fMeta = fMeta; this.fCap = fCap; this.fNbt = fNbt; - temp = new ItemStack(item, 1, meta); - temp.setTagCompound(nbt); + temp = new ItemStack(item, 1, this.meta); + temp.setTagCompound(this.nbt); } @@ -103,18 +106,60 @@ public class LItemStack extends ILabel.Impl { return false; } - public ILabel setFMeta(boolean f) { + public static List suggest(List iss, @Nullable IRecipeHandler rl) { + if (iss.size() == 0) return new ArrayList<>(); + for (ILabel i : iss) if (!(i instanceof LItemStack)) return new ArrayList<>(); + LItemStack lis = (LItemStack) iss.get(0); + boolean fMeta = false; + boolean fNbt = false; + boolean fCap = false; + for (ILabel i : iss) { + LItemStack ii = (LItemStack) i; + if (ii.item != lis.item) return new ArrayList<>(); + if (ii.meta != lis.meta || ii.fMeta) fMeta = true; + if (!Objects.equals(ii.nbt, lis.nbt)) fNbt = true; + } + if (fMeta || fNbt || fCap) return Collections.singletonList( + lis.copy().setFCap(fCap).setFMeta(fMeta).setFNbt(fNbt)); + else return new ArrayList<>(); + } + + + public static List fallback(List iss, @Nullable IRecipeHandler rl) { + List ret = new ArrayList<>(); + if (iss.size() == 1) { + ILabel label = iss.get(0); + if (!(label instanceof LItemStack)) return ret; + LItemStack lis = (LItemStack) label; + if (lis.fCap || lis.fNbt || lis.fMeta) return new ArrayList<>(); + ret.add(lis.copy().setFMeta(true)); + ret.add(lis.copy().setFNbt(true)); + ret.add(lis.copy().setFCap(true)); + ret.add(lis.copy().setFMeta(true).setFNbt(true).setFCap(true)); + } + return ret; + } + + public LItemStack setFMeta(boolean f) { fMeta = f; + if (f) meta = 0; + temp = new ItemStack(item, 1, meta); + temp.setTagCompound(nbt); return this; } - public ILabel setFNbt(boolean f) { + public LItemStack setFNbt(boolean f) { fNbt = f; + if (f) nbt = null; + temp = new ItemStack(item, 1, meta); + temp.setTagCompound(nbt); return this; } - public ILabel setFCap(boolean f) { + public LItemStack setFCap(boolean f) { fCap = f; + temp = new ItemStack(item, 1, meta); + temp.setTagCompound(nbt); return this; } @@ -126,6 +171,9 @@ public class LItemStack extends ILabel.Impl { @SideOnly(Side.CLIENT) public void getToolTip(List existing, boolean detailed) { super.getToolTip(existing, detailed); + if (fMeta) existing.add(FORMAT_GREY + Utilities.I18n.get("label.item_stack.fuzzy_meta")); + if (fNbt) existing.add(FORMAT_GREY + Utilities.I18n.get("label.item_stack.fuzzy_nbt")); + if (fCap) existing.add(FORMAT_GREY + Utilities.I18n.get("label.item_stack.fuzzy_cap")); existing.add(FORMAT_BLUE + FORMAT_ITALIC + Utilities.getModName(item)); } diff --git a/src/main/java/me/towdium/jecalculation/data/label/labels/LOreDict.java b/src/main/java/me/towdium/jecalculation/data/label/labels/LOreDict.java index ba343b3..d3a6b2a 100644 --- a/src/main/java/me/towdium/jecalculation/data/label/labels/LOreDict.java +++ b/src/main/java/me/towdium/jecalculation/data/label/labels/LOreDict.java @@ -1,5 +1,6 @@ package me.towdium.jecalculation.data.label.labels; +import codechicken.nei.recipe.IRecipeHandler; import cpw.mods.fml.relauncher.Side; import cpw.mods.fml.relauncher.SideOnly; import me.towdium.jecalculation.JustEnoughCalculation; @@ -9,16 +10,19 @@ import me.towdium.jecalculation.gui.Resource; import me.towdium.jecalculation.polyfill.mc.util.NonNullList; import me.towdium.jecalculation.utils.ItemStackHelper; import me.towdium.jecalculation.utils.Utilities; +import me.towdium.jecalculation.utils.wrappers.Wrapper; import net.minecraft.creativetab.CreativeTabs; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NBTTagCompound; import net.minecraftforge.oredict.OreDictionary; import javax.annotation.Nonnull; +import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import java.util.ArrayList; import java.util.HashSet; import java.util.List; +import java.util.function.BiPredicate; import java.util.stream.Collectors; /** @@ -53,10 +57,6 @@ public class LOreDict extends ILabel.Impl { name = nbt.getString(KEY_NAME); } - public boolean isEmpty() { - return OreDictionary.getOres(name).isEmpty(); - } - @Override @Nonnull public ItemStack getRepresentation() { @@ -97,53 +97,44 @@ public class LOreDict extends ILabel.Impl { return false; } - public static List guess(List iss) { + public static List suggest(List iss, @Nullable IRecipeHandler rl) { ILabel l = iss.get(0); + if (!(l instanceof LItemStack)) return new ArrayList<>(); + LItemStack lis = (LItemStack) l; HashSet ids = new HashSet<>(); - long amount = l.getAmount(); - for (int i : OreDictionary.getOreIDs(((LItemStack) l).getRep())) - if (check(i, iss)) + long amount = lis.getAmount(); + for (int i : OreDictionary.getOreIDs(lis.getRep())) + if (check(i, iss, true)) ids.add(i); + return ids.stream().map(i -> new LOreDict(OreDictionary.getOreName(i), amount)) + .collect(Collectors.toList()); + } + + public static List fallback(List iss, @Nullable IRecipeHandler rl) { + ILabel l = iss.get(0); + if (!(l instanceof LItemStack)) return new ArrayList<>(); + LItemStack lis = (LItemStack) l; + HashSet ids = new HashSet<>(); + long amount = lis.getAmount(); + for (int i : OreDictionary.getOreIDs(lis.getRep())) + if (check(i, iss, false)) ids.add(i); return ids.stream().map(i -> new LOreDict(OreDictionary.getOreName(i), amount)).collect(Collectors.toList()); } // check labels in the list suitable for the ore id - private static boolean check(int id, List labels) { + private static boolean check(int id, List labels, boolean biDir) { ArrayList ores = OreDictionary.getOres(OreDictionary.getOreName(id)); - if (labels.size() == 1 && ores.size() == 1 && - (ores.get(0).getItemDamage() == OreDictionary.WILDCARD_VALUE || !MODE_FORCE)) + if (labels.size() == 1 && ores.size() == 1 && biDir && !MODE_FORCE) return false; - boolean acceptable = true; - for (ItemStack ore : ores) { - boolean found = false; - for (ILabel label : labels) { - if (label instanceof LItemStack && - OreDictionary.itemMatches(ore, ((LItemStack) label).getRep(), false)) { - found = true; - break; - } - } - if (!found) { - acceptable = false; - break; - } - } - for (ILabel label : labels) { - boolean found = false; - for (ItemStack ore : ores) { - if (label instanceof LItemStack && - OreDictionary.itemMatches(ore, ((LItemStack) label).getRep(), false)) { - found = true; - break; - } - } - if (!found) { - acceptable = false; - break; - } - } - return acceptable; + Wrapper acceptable = new Wrapper<>(true); + BiPredicate match = (l, o) -> l instanceof LItemStack + && OreDictionary.itemMatches(o, ((LItemStack) l).getRep(), false); + if (biDir) ores.stream().filter(ore -> labels.stream().noneMatch(label -> match.test(label, ore))) + .findAny().ifPresent(i -> acceptable.value = false); + labels.stream().filter(label -> ores.stream().noneMatch(ore -> match.test(label, ore))) + .findAny().ifPresent(i -> acceptable.value = false); + return acceptable.value; } @Override @@ -164,21 +155,7 @@ public class LOreDict extends ILabel.Impl { @Override @SideOnly(Side.CLIENT) public void drawLabel(JecaGui gui) { - NonNullList list = NonNullList.create(); - OreDictionary.getOres(name).forEach(is -> { - if (is.getItemDamage() == OreDictionary.WILDCARD_VALUE) { - is.getItem().getSubItems(is.getItem(), CreativeTabs.tabAllSearch, list); - } else - list.add(is); - }); - if (list.isEmpty()) - return; - long index = System.currentTimeMillis() / 1500; - //GlStateManager.pushMatrix(); - //GlStateManager.translate(1, 1, 0); - //GlStateManager.scale(14f / 16, 14f / 16, 1); - gui.drawItemStack(0, 0, list.get((int) (index % list.size())), false); - //GlStateManager.popMatrix(); + gui.drawItemStack(0, 0, getRepresentation(), false); gui.drawResource(Resource.LBL_FRAME, 0, 0); } diff --git a/src/main/java/me/towdium/jecalculation/data/label/labels/LPlaceholder.java b/src/main/java/me/towdium/jecalculation/data/label/labels/LPlaceholder.java index 0a0ef74..7e08dc4 100644 --- a/src/main/java/me/towdium/jecalculation/data/label/labels/LPlaceholder.java +++ b/src/main/java/me/towdium/jecalculation/data/label/labels/LPlaceholder.java @@ -39,7 +39,7 @@ public class LPlaceholder extends ILabel.Impl { public LPlaceholder(String name, long amount, boolean silent) { super(amount, false); this.name = name; - if (!silent) getRecord().push(new LPlaceholder(name, 1, true)); + if (!silent) getRecord().push(new LPlaceholder(name, 1, true), false); } public LPlaceholder(LPlaceholder label) { @@ -111,4 +111,12 @@ public class LPlaceholder extends ILabel.Impl { super.getToolTip(existing, detailed); existing.add(FORMAT_BLUE + FORMAT_ITALIC + JustEnoughCalculation.Reference.MODNAME); } + + public static boolean merge(ILabel a, ILabel b) { + if (a instanceof LPlaceholder && b instanceof LPlaceholder) { + LPlaceholder lpA = (LPlaceholder) a; + LPlaceholder lpB = (LPlaceholder) b; + return lpA.name.equals(lpB.name); + } else return false; + } } diff --git a/src/main/java/me/towdium/jecalculation/data/structure/CostList.java b/src/main/java/me/towdium/jecalculation/data/structure/CostList.java index 6a183d5..618ab90 100644 --- a/src/main/java/me/towdium/jecalculation/data/structure/CostList.java +++ b/src/main/java/me/towdium/jecalculation/data/structure/CostList.java @@ -158,7 +158,7 @@ public class CostList { if (label.getAmount() >= 0) continue; while (iterator.hasNext()) { Recipe r = iterator.next(); - if (r.matches(label)) return new Pair<>(r, r.multiplier(label)); + if (r.matches(label).isPresent()) return new Pair<>(r, r.multiplier(label)); } iterator = Controller.recipeIterator(); } @@ -187,7 +187,7 @@ public class CostList { .collect(Collectors.toList()); } - public List getOutputs(List ignore) { + public List getOutputs(List ignore) { // TODO check logic return getCurrent().labels.stream() .map(i -> i.copy().multiply(-1)) .map(i -> ignore.stream().flatMap(j -> stream(ILabel.MERGER.merge(i, j))).findFirst().orElse(i)) diff --git a/src/main/java/me/towdium/jecalculation/data/structure/Recents.java b/src/main/java/me/towdium/jecalculation/data/structure/Recents.java index 8af19c5..385db0d 100644 --- a/src/main/java/me/towdium/jecalculation/data/structure/Recents.java +++ b/src/main/java/me/towdium/jecalculation/data/structure/Recents.java @@ -20,14 +20,14 @@ public class Recents { .filter(n -> n instanceof NBTTagCompound) .map(n -> ILabel.SERIALIZER.deserialize((NBTTagCompound) n)) .collect(Collectors.toList()); - new Utilities.ReversedIterator<>(ls).forEachRemaining(l -> record.push(l)); + new Utilities.ReversedIterator<>(ls).forEachRemaining(l -> record.push(l, false)); } public Recents() { } - public void push(ILabel label) { - record.push(label); + public void push(ILabel label, boolean replace) { + record.push(label, replace); } public ILabel getLatest() { diff --git a/src/main/java/me/towdium/jecalculation/data/structure/Recipe.java b/src/main/java/me/towdium/jecalculation/data/structure/Recipe.java index 2c66ea0..319203c 100644 --- a/src/main/java/me/towdium/jecalculation/data/structure/Recipe.java +++ b/src/main/java/me/towdium/jecalculation/data/structure/Recipe.java @@ -9,15 +9,13 @@ import net.minecraft.nbt.NBTTagCompound; import net.minecraft.nbt.NBTTagList; import javax.annotation.ParametersAreNonnullByDefault; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.List; -import java.util.Objects; +import java.util.*; import java.util.function.BiFunction; import java.util.function.BiPredicate; import java.util.function.Consumer; import java.util.function.Function; import java.util.stream.Collectors; +import java.util.stream.Stream; import java.util.stream.StreamSupport; /** @@ -33,7 +31,6 @@ public class Recipe { ILabel[] input; ILabel[] catalyst; ILabel[] output; - int hashcode; public Recipe(NBTTagCompound nbt) { this(readNbtList(nbt.getTagList(KEY_INPUT, 10)), @@ -53,19 +50,15 @@ public class Recipe { for (int j = ls.length; j < i; j++) ret[j] = ILabel.EMPTY; return ret; }; - this.input = convert.apply(input, 16); - this.catalyst = convert.apply(catalyst, 8); - this.output = convert.apply(output, 8); - - Wrapper hash = new Wrapper<>(0); - Consumer hasher = (ls) -> Arrays.stream(ls) - .filter(Objects::nonNull).forEach(i -> hash.value ^= i.hashCode()); - hasher.accept(input); - hasher.accept(catalyst); - hasher.accept(output); - hashcode = hash.value; + this.input = convert.apply(input, 14); + this.catalyst = convert.apply(catalyst, 7); + this.output = convert.apply(output, 7); + Stream.of(input, output).forEach(i -> Arrays.stream(i).filter(j -> j != ILabel.EMPTY).findAny() + .orElseThrow(() -> new IllegalArgumentException("Invalid recipe"))); } + + static private List readNbtList(NBTTagList list) { return StreamSupport.stream(NBTHelper.spliterator(list), false) .filter(n -> n instanceof NBTTagCompound) @@ -75,12 +68,18 @@ public class Recipe { @Override public int hashCode() { - return hashcode; + Wrapper hash = new Wrapper<>(0); + Consumer hasher = (ls) -> Arrays.stream(ls) + .filter(Objects::nonNull).forEach(i -> hash.value ^= i.hashCode()); + hasher.accept(input); + hasher.accept(catalyst); + hasher.accept(output); + return hash.value; } @Override public boolean equals(Object obj) { - if (obj == null || hashcode != obj.hashCode() || !(obj instanceof Recipe)) return false; + if (!(obj instanceof Recipe)) return false; Recipe r = (Recipe) obj; BiPredicate p = (i, j) -> { if (i.length != j.length) return false; @@ -133,8 +132,8 @@ public class Recipe { return ret; } - public boolean matches(ILabel label) { - return Arrays.stream(output).anyMatch(i -> ILabel.MERGER.merge(label, i).isPresent()); + public Optional matches(ILabel label) { + return Arrays.stream(output).filter(i -> ILabel.MERGER.merge(label, i).isPresent()).findAny(); } public long multiplier(ILabel label) { diff --git a/src/main/java/me/towdium/jecalculation/data/structure/Recipes.java b/src/main/java/me/towdium/jecalculation/data/structure/Recipes.java index dab0122..b357dd8 100644 --- a/src/main/java/me/towdium/jecalculation/data/structure/Recipes.java +++ b/src/main/java/me/towdium/jecalculation/data/structure/Recipes.java @@ -1,7 +1,10 @@ package me.towdium.jecalculation.data.structure; +import me.towdium.jecalculation.JecaConfig; +import me.towdium.jecalculation.JustEnoughCalculation; import me.towdium.jecalculation.data.label.ILabel; import me.towdium.jecalculation.polyfill.NBTHelper; +import me.towdium.jecalculation.utils.Utilities; import me.towdium.jecalculation.utils.wrappers.Pair; import me.towdium.jecalculation.utils.wrappers.Trio; import net.minecraft.nbt.NBTTagCompound; @@ -9,6 +12,7 @@ import net.minecraft.nbt.NBTTagList; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; +import java.io.File; import java.util.*; import java.util.function.BiConsumer; import java.util.stream.Collectors; @@ -19,32 +23,41 @@ import java.util.stream.StreamSupport; @ParametersAreNonnullByDefault public class Recipes { LinkedHashMap> records = new LinkedHashMap<>(); + HashSet cache = new HashSet<>(); public Recipes() { + File file = JecaConfig.defaultFile; + NBTTagCompound nbt = Utilities.Json.read(file); + if (nbt == null) JustEnoughCalculation.logger.info("Failed to load default records at " + file + "."); + else { + JustEnoughCalculation.logger.info("Loading default records at " + file + "."); + deserialize(nbt); + } } public Recipes(NBTTagCompound nbt) { deserialize(nbt); } - public void deserialize(NBTTagCompound nbt) { + protected void deserialize(NBTTagCompound nbt) { Set keySet = (Set) nbt.func_150296_c(); keySet.stream().sorted().forEach(i -> { NBTTagList group = nbt.getTagList(i, 10); StreamSupport.stream(NBTHelper.spliterator(group), false) .filter(r -> r instanceof NBTTagCompound) - .forEach(r -> add(i, new Recipe((NBTTagCompound) r))); + .forEach(r -> { + try { + add(i, new Recipe((NBTTagCompound) r)); + } catch (IllegalArgumentException e) { + JustEnoughCalculation.logger.warn("Invalid recipe record :" + r); + } + }); }); } public void add(String group, Recipe recipe) { - if (records.containsKey(group)) { - records.get(group).add(recipe); - } else { - ArrayList l = new ArrayList<>(); - l.add(recipe); - records.put(group, l); - } + records.computeIfAbsent(group, k -> new ArrayList<>()).add(recipe); + cache.add(recipe); } public void modify(String group, int index, @Nullable Recipe recipe) { @@ -54,7 +67,9 @@ public class Recipes { } public void set(String group, int index, Recipe recipe) { - records.get(group).set(index, recipe); + Recipe r = records.get(group).set(index, recipe); + cache.remove(r); + cache.add(recipe); } public int size() { @@ -67,7 +82,7 @@ public class Recipes { public void remove(String group, int index) { List l = records.get(group); - l.remove(index); + cache.remove(l.remove(index)); if (l.isEmpty()) records.remove(group); } @@ -87,6 +102,12 @@ public class Recipes { records.forEach(consumer); } + /** + * Do not modify return value! + * + * @param group Name of group to get + * @return List of Recipes in group + */ public List getGroup(String group) { return records.get(group); } @@ -132,5 +153,13 @@ public class Recipes { while (j == null || !j.hasNext()) if (i.hasNext()) j = i.next().getValue().iterator(); return j.next(); } + + public Stream stream() { + return StreamSupport.stream(Spliterators.spliteratorUnknownSize(this, Spliterator.ORDERED), false); + } + } + + public boolean hasDuplicate(Recipe r) { + return cache.contains(r); } } diff --git a/src/main/java/me/towdium/jecalculation/gui/JecaGui.java b/src/main/java/me/towdium/jecalculation/gui/JecaGui.java index 1492d0e..ae07640 100644 --- a/src/main/java/me/towdium/jecalculation/gui/JecaGui.java +++ b/src/main/java/me/towdium/jecalculation/gui/JecaGui.java @@ -57,6 +57,12 @@ public class JecaGui extends GuiContainer { ((JecContainer) inventorySlots).setGui(this); } + @Override + public void initGui() { + this.guiLeft = (this.width - this.xSize) / 2; + this.guiTop = (this.height - this.ySize) / 2; + } + public static boolean mouseIn(int xPos, int yPos, int xSize, int ySize, int xMouse, int yMouse) { return xMouse > xPos && yMouse > yPos && xMouse <= xPos + xSize && yMouse <= yPos + ySize; } @@ -173,22 +179,13 @@ public class JecaGui extends GuiContainer { int yMouse = this.height - Mouse.getEventY() * this.height / this.mc.displayHeight - 1; int button = Mouse.getEventButton(); if (Mouse.getEventButtonState()) { - if (button == 0) { - if (hand == ILabel.EMPTY) { - ILabel e = NEIPlugin.getLabelUnderMouse(); - if (e != ILabel.EMPTY) { - hand = e; - return true; - } - } else { - if (!mouseIn(guiLeft, guiTop, width, height, xMouse, yMouse)) { - hand = ILabel.EMPTY; - return true; - } - } - } else if (button == 1) { - if (hand != ILabel.EMPTY) { - hand = ILabel.EMPTY; + if (hand != ILabel.EMPTY) { + hand = ILabel.EMPTY; + return true; + } else { + ILabel e = NEIPlugin.getLabelUnderMouse(); + if (e != ILabel.EMPTY) { + hand = e; return true; } } @@ -295,7 +292,7 @@ public class JecaGui extends GuiContainer { boolean unicode = fontRendererObj.getUnicodeFlag(); if (f.half) fontRendererObj.setUnicodeFlag(false); GlStateManager.pushMatrix(); - GlStateManager.translate(xPos, yPos, 0); + GlStateManager.translate(unicode && f.half ? xPos - 5 : xPos, yPos, 0); if (f.half) GlStateManager.scale(0.5, 0.5, 1); r.run(); GlStateManager.popMatrix(); diff --git a/src/main/java/me/towdium/jecalculation/gui/Resource.java b/src/main/java/me/towdium/jecalculation/gui/Resource.java index a28a89f..2981a56 100644 --- a/src/main/java/me/towdium/jecalculation/gui/Resource.java +++ b/src/main/java/me/towdium/jecalculation/gui/Resource.java @@ -26,6 +26,7 @@ public class Resource { public static final Resource BTN_SEARCH_F = getIcon(2, 0); public static final Resource BTN_SAVE_N = getIcon(3, 1); public static final Resource BTN_SAVE_F = getIcon(3, 0); + public static final Resource BTN_SAVE_D = getIcon(3, 2); public static final Resource BTN_COPY_N = getIcon(4, 1); public static final Resource BTN_COPY_F = getIcon(4, 0); public static final Resource BTN_COPY_D = getIcon(4, 2); @@ -52,63 +53,78 @@ public class Resource { public static final Resource BTN_LIST_N = getIcon(12, 1); public static final Resource BTN_LIST_F = getIcon(12, 0); public static final Resource BTN_LIST_D = getIcon(12, 2); - public static final Resource BTN_META_N_N = getIcon(14, 0); - public static final Resource BTN_META_N_F = getIcon(13, 0); - public static final Resource BTN_META_F_N = getIcon(16, 0); - public static final Resource BTN_META_F_F = getIcon(15, 0); - public static final Resource BTN_META_D = getIcon(17, 0); - public static final Resource BTN_NBT_N_N = getIcon(14, 1); - public static final Resource BTN_NBT_N_F = getIcon(13, 1); - public static final Resource BTN_NBT_F_N = getIcon(16, 1); - public static final Resource BTN_NBT_F_F = getIcon(15, 1); - public static final Resource BTN_NBT_D = getIcon(17, 1); - public static final Resource BTN_CAP_N_N = getIcon(14, 2); - public static final Resource BTN_CAP_N_F = getIcon(13, 2); - public static final Resource BTN_CAP_F_N = getIcon(16, 2); - public static final Resource BTN_CAP_F_F = getIcon(15, 2); - public static final Resource BTN_CAP_D = getIcon(17, 2); - public static final Resource WGT_BUTTON_N = new Resource(40, 232, 20, 20); - public static final Resource WGT_BUTTON_F = new Resource(60, 232, 20, 20); - public static final Resource WGT_BUTTON_D = new Resource(80, 232, 20, 20); - public static final Resource WGT_BUTTON_S_N = new Resource(0, 212, 20, 20); - public static final Resource WGT_BUTTON_S_F = new Resource(20, 212, 20, 20); - public static final Resource WGT_BUTTON_S_D = new Resource(40, 212, 20, 20); - public static final Resource WGT_SLOT = new Resource(20, 232, 20, 20); - public static final Resource WGT_LINE = new Resource(0, 252, 163, 4); - public static final Resource WGT_PANEL_F = new Resource(0, 232, 20, 20); - public static final Resource WGT_PAGER_FN = new Resource(100, 232, 20, 20); - public static final Resource WGT_PAGER_F0 = new Resource(120, 232, 20, 20); - public static final Resource WGT_PANEL_N = new Resource(140, 232, 20, 20); - public static final Resource WGT_ARR_L_F = new Resource(160, 232, 7, 9); - public static final Resource WGT_ARR_L_N = new Resource(167, 232, 7, 9); - public static final Resource WGT_ARR_L_D = new Resource(174, 232, 7, 9); - public static final Resource WGT_ARR_R_F = new Resource(160, 242, 7, 9); - public static final Resource WGT_ARR_R_N = new Resource(167, 242, 7, 9); - public static final Resource WGT_ARR_R_D = new Resource(174, 242, 7, 9); - public static final Resource WGT_SCROLL = new Resource(181, 232, 12, 15); - public static final Resource WGT_HELP_F = new Resource(193, 232, 20, 20); - public static final Resource WGT_HELP_N = new Resource(213, 232, 20, 20); - public static final Resource WGT_HELP_B = new Resource(233, 232, 20, 20); - public static final Resource ICN_RECENT_N = getIcon(0, 4); - public static final Resource ICN_RECENT_F = getIcon(0, 3); - public static final Resource ICN_INPUT_N = getIcon(1, 4); - public static final Resource ICN_INPUT_F = getIcon(1, 3); - public static final Resource ICN_OUTPUT_N = getIcon(2, 4); - public static final Resource ICN_OUTPUT_F = getIcon(2, 3); - public static final Resource ICN_CATALYST_N = getIcon(3, 4); - public static final Resource ICN_CATALYST_F = getIcon(3, 3); - public static final Resource ICN_LIST_N = getIcon(4, 4); - public static final Resource ICN_LIST_F = getIcon(4, 3); - public static final Resource ICN_LABEL_N = getIcon(5, 4); - public static final Resource ICN_LABEL_F = getIcon(5, 3); - public static final Resource ICN_HELP_N = getIcon(6, 4); - public static final Resource ICN_HELP_F = getIcon(6, 3); - public static final Resource ICN_STACK_N = getIcon(7, 4); - public static final Resource ICN_STACK_F = getIcon(7, 3); - public static final Resource ICN_TEXT_N = getIcon(8, 4); - public static final Resource ICN_TEXT_F = getIcon(8, 3); - public static final Resource ICN_MULTI_N = getIcon(9, 4); - public static final Resource ICN_MULTI_F = getIcon(9, 3); + public static final Resource BTN_IMPORT_N = getIcon(13, 1); + public static final Resource BTN_IMPORT_F = getIcon(13, 0); + public static final Resource BTN_IMPORT_D = getIcon(13, 2); + public static final Resource BTN_EXPORT_N_N = getIcon(14, 1); + public static final Resource BTN_EXPORT_N_F = getIcon(14, 0); + public static final Resource BTN_EXPORT_N_D = getIcon(14, 2); + public static final Resource BTN_EXPORT_1_N = getIcon(15, 1); + public static final Resource BTN_EXPORT_1_F = getIcon(15, 0); + public static final Resource BTN_EXPORT_1_D = getIcon(15, 2); + public static final Resource BTN_INV_E_N = getIcon(16, 1); + public static final Resource BTN_INV_E_F = getIcon(16, 0); + public static final Resource BTN_INV_D_N = getIcon(17, 1); + public static final Resource BTN_INV_D_F = getIcon(17, 0); + public static final Resource BTN_CAP_N_N = getIcon(0, 4); + public static final Resource BTN_CAP_N_F = getIcon(0, 3); + public static final Resource BTN_CAP_F_N = getIcon(1, 4); + public static final Resource BTN_CAP_F_F = getIcon(1, 3); + public static final Resource BTN_CAP_D = getIcon(0, 5); + public static final Resource BTN_META_N_N = getIcon(2, 4); + public static final Resource BTN_META_N_F = getIcon(2, 3); + public static final Resource BTN_META_F_N = getIcon(3, 4); + public static final Resource BTN_META_F_F = getIcon(3, 3); + public static final Resource BTN_META_D = getIcon(2, 5); + public static final Resource BTN_NBT_N_N = getIcon(4, 4); + public static final Resource BTN_NBT_N_F = getIcon(4, 3); + public static final Resource BTN_NBT_F_N = getIcon(5, 4); + public static final Resource BTN_NBT_F_F = getIcon(5, 3); + public static final Resource BTN_NBT_D = getIcon(4, 5); + + public static final Resource WGT_BUTTON_N = getWidget(0); + public static final Resource WGT_BUTTON_F = getWidget(1); + public static final Resource WGT_BUTTON_D = getWidget(2); + public static final Resource WGT_BUTTON_S_N = getWidget(3); + public static final Resource WGT_BUTTON_S_F = getWidget(4); + public static final Resource WGT_BUTTON_S_D = getWidget(5); + public static final Resource WGT_SLOT = getWidget(6); + public static final Resource WGT_PANEL_N = getWidget(7); + public static final Resource WGT_PANEL_F = getWidget(8); + public static final Resource WGT_PAGER_F = getWidget(9); + public static final Resource WGT_HELP_B = getWidget(10); + public static final Resource WGT_LCD_BG = getWidget(11); + + public static final Resource WGT_ARR_L_F = new Resource(112, 132, 7, 10); + public static final Resource WGT_ARR_L_N = new Resource(119, 132, 7, 10); + public static final Resource WGT_ARR_L_D = new Resource(126, 132, 7, 10); + public static final Resource WGT_ARR_R_F = new Resource(112, 142, 7, 10); + public static final Resource WGT_ARR_R_N = new Resource(119, 142, 7, 10); + public static final Resource WGT_ARR_R_D = new Resource(126, 142, 7, 10); + public static final Resource WGT_SCROLL = new Resource(133, 132, 14, 17); + public static final Resource WGT_HELP_F = new Resource(147, 132, 20, 20); + public static final Resource WGT_HELP_N = new Resource(167, 132, 20, 20); + + public static final Resource ICN_RECENT_N = getIcon(0, 7); + public static final Resource ICN_RECENT_F = getIcon(0, 6); + public static final Resource ICN_INPUT_N = getIcon(1, 7); + public static final Resource ICN_INPUT_F = getIcon(1, 6); + public static final Resource ICN_OUTPUT_N = getIcon(2, 7); + public static final Resource ICN_OUTPUT_F = getIcon(2, 6); + public static final Resource ICN_CATALYST_N = getIcon(3, 7); + public static final Resource ICN_CATALYST_F = getIcon(3, 6); + public static final Resource ICN_LIST_N = getIcon(4, 7); + public static final Resource ICN_LIST_F = getIcon(4, 6); + public static final Resource ICN_LABEL_N = getIcon(5, 7); + public static final Resource ICN_LABEL_F = getIcon(5, 6); + public static final Resource ICN_HELP_N = getIcon(6, 7); + public static final Resource ICN_HELP_F = getIcon(6, 6); + public static final Resource ICN_STACK_N = getIcon(7, 7); + public static final Resource ICN_STACK_F = getIcon(7, 6); + public static final Resource ICN_TEXT_N = getIcon(8, 7); + public static final Resource ICN_TEXT_F = getIcon(8, 6); + public static final Resource ICN_MULTI_N = getIcon(9, 7); + public static final Resource ICN_MULTI_F = getIcon(9, 6); public static final Resource LBL_FRAME = getLabel(0); public static final Resource LBL_FLUID = getLabel(1); public static final Resource LBL_UNIV_B = getLabel(2); @@ -120,7 +136,7 @@ public class Resource { public static final ResourceGroup BTN_LABEL = new ResourceGroup(BTN_LABEL_N, BTN_LABEL_F); public static final ResourceGroup BTN_NEW = new ResourceGroup(BTN_NEW_N, BTN_NEW_F); public static final ResourceGroup BTN_SEARCH = new ResourceGroup(BTN_SEARCH_N, BTN_SEARCH_F); - public static final ResourceGroup BTN_SAVE = new ResourceGroup(BTN_SAVE_N, BTN_SAVE_F); + public static final ResourceGroup BTN_SAVE = new ResourceGroup(BTN_SAVE_N, BTN_SAVE_F, BTN_SAVE_D); public static final ResourceGroup BTN_COPY = new ResourceGroup(BTN_COPY_N, BTN_COPY_F, BTN_COPY_D); public static final ResourceGroup BTN_DEL = new ResourceGroup(BTN_DEL_N, BTN_DEL_F); public static final ResourceGroup BTN_YES = new ResourceGroup(BTN_YES_N, BTN_YES_F, BTN_YES_D); @@ -130,6 +146,12 @@ public class Resource { public static final ResourceGroup BTN_OUT = new ResourceGroup(BTN_OUT_N, BTN_OUT_F, BTN_OUT_D); public static final ResourceGroup BTN_CAT = new ResourceGroup(BTN_CAT_N, BTN_CAT_F, BTN_CAT_D); public static final ResourceGroup BTN_LIST = new ResourceGroup(BTN_LIST_N, BTN_LIST_F, BTN_LIST_D); + public static final ResourceGroup BTN_IMPORT = new ResourceGroup(BTN_IMPORT_N, BTN_IMPORT_F, BTN_IMPORT_D); + public static final ResourceGroup BTN_EXPORT_N = new ResourceGroup(BTN_EXPORT_N_N, BTN_EXPORT_N_F, BTN_EXPORT_N_D); + public static final ResourceGroup BTN_EXPORT_1 = new ResourceGroup(BTN_EXPORT_1_N, BTN_EXPORT_1_F, BTN_EXPORT_1_D); + public static final ResourceGroup BTN_INV_E = new ResourceGroup(BTN_INV_E_N, BTN_INV_E_F); + public static final ResourceGroup BTN_INV_D = new ResourceGroup(BTN_INV_D_N, BTN_INV_D_F); + public static final ResourceGroup BTN_META_N = new ResourceGroup(BTN_META_N_N, BTN_META_N_F, BTN_META_D); public static final ResourceGroup BTN_CAP_N = new ResourceGroup(BTN_CAP_N_N, BTN_CAP_N_F, BTN_CAP_D); public static final ResourceGroup BTN_NBT_N = new ResourceGroup(BTN_NBT_N_N, BTN_NBT_N_F, BTN_NBT_D); @@ -145,6 +167,7 @@ public class Resource { public static final ResourceGroup ICN_LIST = new ResourceGroup(ICN_LIST_N, ICN_LIST_F); public static final ResourceGroup ICN_LABEL = new ResourceGroup(ICN_LABEL_N, ICN_LABEL_F); public static final ResourceGroup ICN_TEXT = new ResourceGroup(ICN_TEXT_N, ICN_TEXT_F); + public static final ResourceGroup ICN_HELP = new ResourceGroup(ICN_HELP_N, ICN_HELP_F); protected int xPos, yPos, xSize, ySize; protected ResourceLocation rl; @@ -186,7 +209,11 @@ public class Resource { } private static Resource getLabel(int x) { - return new Resource(x * 16, 70, 16, 16); + return new Resource(x * 16, 132, 16, 16); + } + + private static Resource getWidget(int x) { + return new Resource(x * 20, 112, 20, 20); } public static class ResourceGroup { diff --git a/src/main/java/me/towdium/jecalculation/gui/guis/GuiCalculator.java b/src/main/java/me/towdium/jecalculation/gui/guis/GuiCalculator.java index b359aea..7962c89 100644 --- a/src/main/java/me/towdium/jecalculation/gui/guis/GuiCalculator.java +++ b/src/main/java/me/towdium/jecalculation/gui/guis/GuiCalculator.java @@ -9,16 +9,16 @@ import me.towdium.jecalculation.gui.JecaGui; import me.towdium.jecalculation.gui.Resource; import me.towdium.jecalculation.gui.widgets.*; import me.towdium.jecalculation.utils.ItemStackHelper; +import me.towdium.jecalculation.utils.wrappers.Pair; import net.minecraft.client.Minecraft; import net.minecraft.entity.player.InventoryPlayer; import net.minecraft.item.ItemStack; +import org.lwjgl.input.Keyboard; import javax.annotation.ParametersAreNonnullByDefault; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Collections; -import java.util.List; +import java.util.*; import java.util.function.Consumer; +import java.util.stream.Collectors; import java.util.stream.Stream; /** @@ -45,11 +45,9 @@ public class GuiCalculator extends WContainer implements IGui { Controller.setAmount(i.getText()); refreshCalculator(); }); - WLabel label = new WLabel(31, 7, 20, 20, WLabel.Mode.SELECTOR).setListener((i, v) -> { - Controller.setRecent(v); - refreshRecent(); - refreshCalculator(); - }); + WLabel label = new WLabel(31, 7, 20, 20, WLabel.Mode.SELECTOR).setListener((i, v) -> refreshLabel(v, false)); + WButton invE = new WButtonIcon(149, 62, 20, 20, Resource.BTN_INV_E, "calculator.inventory_enabled"); + WButton invD = new WButtonIcon(149, 62, 20, 20, Resource.BTN_INV_D, "calculator.inventory_disabled"); public GuiCalculator() { add(new WHelp("calculator")); @@ -66,7 +64,19 @@ public class GuiCalculator extends WContainer implements IGui { add(new WText(53, 13, JecaGui.Font.PLAIN, "x")); add(new WLine(55)); add(new WIcon(151, 31, 18, 18, Resource.ICN_RECENT, "calculator.history")); - addAll(recent, label, input, output, catalyst, steps, result, amount); + add(recent, label, input, output, catalyst, steps, result, amount, Controller.getDetectInv() ? invE : invD); + invE.setListener(i -> { + Controller.setDetectInv(false); + remove(invE); + add(invD); + refreshCalculator(); + }); + invD.setListener(i -> { + Controller.setDetectInv(true); + remove(invD); + add(invE); + refreshCalculator(); + }); refreshRecent(); setMode(Mode.INPUT); } @@ -97,7 +107,8 @@ public class GuiCalculator extends WContainer implements IGui { long i = s.isEmpty() ? 1 : Long.parseLong(amount.getText()); amount.setColor(JecaGui.COLOR_TEXT_WHITE); List dest = Collections.singletonList(label.getLabel().copy().setAmount(i)); - calculator = new CostList(getInventory(), dest).calculate(); + CostList list = Controller.getDetectInv() ? new CostList(getInventory(), dest) : new CostList(dest); + calculator = list.calculate(); } catch (NumberFormatException | ArithmeticException e) { amount.setColor(JecaGui.COLOR_TEXT_RED); calculator = null; @@ -138,7 +149,69 @@ public class GuiCalculator extends WContainer implements IGui { } } + private void refreshLabel(ILabel l, boolean replace) { + Controller.setRecent(l, replace); + refreshRecent(); + refreshCalculator(); + if (findRecipe(l).isEmpty()) { + Pair, List> guess = ILabel.CONVERTER.guess(Collections.singletonList(l)); + LinkedHashSet match = new LinkedHashSet<>(); + List fuzzy = new ArrayList<>(); + Stream.of(guess.one, guess.two).flatMap(Collection::stream).forEach(i -> { + List list = findRecipe(i); + list.forEach(j -> match.add(j.setPercent(false).setAmount(1))); + if (!list.isEmpty()) fuzzy.add(i); + }); + match.addAll(fuzzy); + List list = new ArrayList<>(match); + if (!match.isEmpty()) add(new Suggest(list.size() > 3 ? list.subList(0, 3) : list)); + } + } + + private static List findRecipe(ILabel l) { + return Controller.recipeIterator().stream() + .map(i -> i.matches(l)) + .filter(Optional::isPresent) + .map(Optional::get) + .collect(Collectors.toList()); + } + enum Mode { INPUT, OUTPUT, CATALYST, STEPS } + class Suggest extends WContainer { + public Suggest(List labels) { + int width = labels.size() * 20; + add(new WPanel(0 - width, 2, 56 + width, 30)); + add(new WLabel(31, 7, 20, 20, WLabel.Mode.SELECTOR).setLabel(label.getLabel()) + .setListener((i, v) -> refresh(v))); + add(new WIcon(5 - width, 7, 18, 20, Resource.ICN_HELP, "calculator.suggest")); + add(new WLine(26, 7, 20, false)); + for (int i = 0; i < labels.size(); i++) { + add(new WLabel(3 - i * 20, 7, 20, 20, WLabel.Mode.PICKER).setLabel(labels.get(i)) + .setListener((j, v) -> refresh(v))); + } + } + + public void refresh(ILabel l) { + GuiCalculator.this.remove(this); + refreshLabel(l, true); + } + + @Override + public boolean onClicked(JecaGui gui, int xMouse, int yMouse, int button) { + if (!super.onClicked(gui, xMouse, yMouse, button)) GuiCalculator.this.remove(this); + return true; + } + + @Override + public boolean onKey(JecaGui gui, char ch, int code) { + if (!super.onKey(gui, ch, code)) { + if (code == Keyboard.KEY_ESCAPE) { + GuiCalculator.this.remove(this); + return true; + } else return false; + } else return true; + } + } } diff --git a/src/main/java/me/towdium/jecalculation/gui/guis/GuiDisambiguation.java b/src/main/java/me/towdium/jecalculation/gui/guis/GuiDisambiguation.java index ad199ee..75d3b8b 100644 --- a/src/main/java/me/towdium/jecalculation/gui/guis/GuiDisambiguation.java +++ b/src/main/java/me/towdium/jecalculation/gui/guis/GuiDisambiguation.java @@ -11,8 +11,7 @@ import javax.annotation.ParametersAreNonnullByDefault; import java.util.List; import java.util.function.Consumer; -import static me.towdium.jecalculation.gui.Resource.ICN_LABEL; -import static me.towdium.jecalculation.gui.Resource.ICN_LIST; +import static me.towdium.jecalculation.gui.Resource.*; /** * Author: towdium @@ -30,16 +29,17 @@ public class GuiDisambiguation extends IPicker.Impl implements IGui { WSwitcher switcher = new WSwitcher(7, 7, 162, this.record.size()).setListener(i -> setPage(i.getIndex())); ListenerValue consumer = (i, v) -> callback.accept(i.get(v).copy().multiply(-1)); add(new WPanel()); - add(new WIcon(7, 48, 18, 54, ICN_LIST, "disambiguation.list")); - add(new WIcon(7, 105, 18, 54, ICN_LABEL, "disambiguation.label")); + add(new WIcon(7, 24, 20, 20, ICN_TEXT, "common.search")); + add(new WIcon(7, 48, 18, 54, ICN_LIST, "disamb.list")); + add(new WIcon(7, 105, 18, 54, ICN_LABEL, "disamb.label")); add(new WSearch(25, 24, 90, lsUp.setListener(consumer), lsDown.setListener(consumer))); - addAll(switcher, lsUp, lsDown); + add(switcher, lsUp, lsDown); setPage(0); } protected void setPage(int n) { lsUp.setLabels(record.get(n)); - lsDown.setLabels(ILabel.CONVERTER.guess(record.get(n))); + lsDown.setLabels(ILabel.CONVERTER.guess(record.get(n)).one); } } diff --git a/src/main/java/me/towdium/jecalculation/gui/guis/GuiImport.java b/src/main/java/me/towdium/jecalculation/gui/guis/GuiImport.java index 3ec8696..1edc0f5 100644 --- a/src/main/java/me/towdium/jecalculation/gui/guis/GuiImport.java +++ b/src/main/java/me/towdium/jecalculation/gui/guis/GuiImport.java @@ -25,9 +25,9 @@ import static me.towdium.jecalculation.gui.Resource.ICN_TEXT; public class GuiImport extends WContainer implements IGui, ISearchable { List> data; // selected, file, group, recipes List> filtered; - WSearch search = new WSearch(25, 25, 90, this); + WSearch search = new WSearch(26, 25, 90, this); WSwitcher page; - WButton confirm = new WButtonIcon(149, 25, 20, 20, BTN_YES, "common.confirm").setDisabled(true) + WButton confirm = new WButtonIcon(149, 25, 20, 20, BTN_YES, "import.confirm").setDisabled(true) .setListener(i -> { data.stream().filter(j -> j.one).forEach(j -> Controller.inport(j.four, j.three)); JecaGui.displayParent(); @@ -35,9 +35,9 @@ public class GuiImport extends WContainer implements IGui, ISearchable { List> content = new ArrayList<>(); public GuiImport() { - add(new WPanel()); + add(new WHelp("import"), new WPanel()); add(new WIcon(7, 25, 20, 20, ICN_TEXT, "common.search")); - addAll(search, confirm); + add(search, confirm); IntStream.range(0, 7).forEach(i -> { WTick tick = new WTick(7, 49 + 16 * i, 13, 13, "import.tick").setDisabled(true) .setListener(j -> { @@ -45,7 +45,7 @@ public class GuiImport extends WContainer implements IGui, ISearchable { confirm.setDisabled(data.stream().noneMatch(k -> k.one)); }); WText text = new WTextExpand(49 + 16 * i, ""); - addAll(tick, text); + add(tick, text); content.add(new Pair<>(tick, text)); }); } diff --git a/src/main/java/me/towdium/jecalculation/gui/guis/GuiLabel.java b/src/main/java/me/towdium/jecalculation/gui/guis/GuiLabel.java index b87e398..97f4a6b 100644 --- a/src/main/java/me/towdium/jecalculation/gui/guis/GuiLabel.java +++ b/src/main/java/me/towdium/jecalculation/gui/guis/GuiLabel.java @@ -40,7 +40,7 @@ public class GuiLabel extends WContainer implements IGui { add(new WPage(i, r, false).setListener(j -> refresh(i))); index.value += 1; }); - addAll(new WHelp("label"), new WPanel()); + add(new WHelp("label"), new WPanel()); add(container); refresh(0); } diff --git a/src/main/java/me/towdium/jecalculation/gui/guis/GuiRecipe.java b/src/main/java/me/towdium/jecalculation/gui/guis/GuiRecipe.java index 97aba3f..9e14f77 100644 --- a/src/main/java/me/towdium/jecalculation/gui/guis/GuiRecipe.java +++ b/src/main/java/me/towdium/jecalculation/gui/guis/GuiRecipe.java @@ -1,5 +1,6 @@ package me.towdium.jecalculation.gui.guis; +import codechicken.nei.PositionedStack; import codechicken.nei.recipe.IRecipeHandler; import me.towdium.jecalculation.data.Controller; import me.towdium.jecalculation.data.label.ILabel; @@ -26,34 +27,37 @@ public class GuiRecipe extends WContainer implements IGui { Pair dest; HashMap> disambiguation = new HashMap<>(); WSwitcher switcherGroup = new WSwitcher(7, 7, 162, Controller.getGroups()); - WTextField textField = new WTextField(49, 33, 119); - WLabelGroup groupCatalyst = new WLabelGroup(28, 87, 7, 1, 20, 20, WLabel.Mode.EDITOR); - WButton buttonDisamb = new WButtonIcon(121, 33, 20, 20, BTN_DISAMB, "recipe.disamb").setListener(i -> { + WTextField textField = new WTextField(49, 31, 119); + WLabelGroup groupCatalyst = new WLabelGroup(29, 87, 7, 1, 20, 20, WLabel.Mode.EDITOR).setListener((i, v) -> { + disambiguation.remove(v + 14); + refresh(); + }); + WLabelGroup groupInput = new WLabelGroup(29, 111, 7, 2, 20, 20, WLabel.Mode.EDITOR).setListener((i, v) -> { + disambiguation.remove(v); + refresh(); + }); + WLabelGroup groupOutput = new WLabelGroup(29, 63, 7, 1, 20, 20, WLabel.Mode.EDITOR).setListener((i, v) -> { + disambiguation.remove(v + 21); + refresh(); + }); + WButton buttonDisamb = new WButtonIcon(121, 31, 20, 20, BTN_DISAMB, "recipe.disamb").setListener(i -> { if (disambiguation != null) JecaGui.displayGui(new GuiDisambiguation(new ArrayList<>(disambiguation.values())) .setCallback(l -> { JecaGui.displayParent(); JecaGui.getCurrent().hand = l; })); }); - WLabelGroup groupInput = new WLabelGroup(28, 111, 7, 2, 20, 20, WLabel.Mode.EDITOR).setListener((i, v) -> { - disambiguation.remove(v); - refresh(); - }); - WLabelGroup groupOutput = new WLabelGroup(28, 63, 7, 1, 20, 20, WLabel.Mode.EDITOR).setListener((i, v) -> { - disambiguation.remove(v + 21); - refresh(); - }); - WButton buttonClear = new WButtonIcon(64, 33, 20, 20, BTN_DEL, "recipe.clear").setListener(i -> clear()); - WButton buttonCopy = new WButtonIcon(83, 33, 20, 20, BTN_COPY, "recipe.copy").setListener(i -> { + WButton buttonClear = new WButtonIcon(64, 31, 20, 20, BTN_DEL, "recipe.clear").setListener(i -> clear()); + WButton buttonCopy = new WButtonIcon(83, 31, 20, 20, BTN_COPY, "recipe.copy").setListener(i -> { Controller.addRecipe(switcherGroup.getText(), toRecipe()); JecaGui.displayParent(); }); - WButton buttonLabel = new WButtonIcon(45, 33, 20, 20, BTN_LABEL, "recipe.label").setListener(i -> + WButton buttonLabel = new WButtonIcon(45, 31, 20, 20, BTN_LABEL, "recipe.label").setListener(i -> JecaGui.displayGui(new GuiLabel((l) -> { JecaGui.displayParent(); JecaGui.getCurrent().hand = l; }))); - WButton buttonSave = new WButtonIcon(26, 33, 20, 20, BTN_SAVE, "recipe.save").setListener(i -> { + WButton buttonSave = new WButtonIcon(26, 31, 20, 20, BTN_SAVE, "recipe.save").setDisabled(true).setListener(i -> { if (dest == null) Controller.addRecipe(switcherGroup.getText(), toRecipe()); else { @@ -66,17 +70,17 @@ public class GuiRecipe extends WContainer implements IGui { } JecaGui.displayParent(); }); - WButton buttonDel = new WButtonIcon(102, 33, 20, 20, BTN_NO, "recipe.delete").setListener(i -> { + WButton buttonDel = new WButtonIcon(102, 31, 20, 20, BTN_NO, "recipe.delete").setListener(i -> { Controller.removeRecipe(dest.one, dest.two); JecaGui.displayParent(); }); - WButton buttonYes = new WButtonIcon(7, 33, 20, 20, BTN_YES, "recipe.confirm").setDisabled(true).setListener(i -> { + WButton buttonYes = new WButtonIcon(7, 31, 20, 20, BTN_YES, "recipe.confirm").setDisabled(true).setListener(i -> { switcherGroup.setTemp(textField.getText()); textField.setText(""); setNewGroup(false); }); - WButton buttonNo = new WButtonIcon(26, 33, 20, 20, BTN_NO, "common.cancel").setListener(i -> setNewGroup(false)); - WButton buttonNew = new WButtonIcon(7, 33, 20, 20, BTN_NEW, "recipe.new").setListener(i -> setNewGroup(true)); + WButton buttonNo = new WButtonIcon(26, 31, 20, 20, BTN_NO, "common.cancel").setListener(i -> setNewGroup(false)); + WButton buttonNew = new WButtonIcon(7, 31, 20, 20, BTN_NEW, "recipe.new").setListener(i -> setNewGroup(true)); public GuiRecipe(String group, int index) { this(); @@ -86,15 +90,16 @@ public class GuiRecipe extends WContainer implements IGui { switcherGroup.setIndex(Controller.getGroups().indexOf(group)); buttonCopy.setDisabled(false); buttonDel.setDisabled(false); + buttonSave.setDisabled(false); } public GuiRecipe() { - addAll(new WHelp("recipe"), new WPanel()); - add(new WIcon(7, 63, 21, 20, ICN_OUTPUT, "common.output")); - add(new WIcon(7, 87, 21, 20, ICN_CATALYST, "common.catalyst")); - add(new WIcon(7, 111, 21, 40, ICN_INPUT, "common.input")); + add(new WHelp("recipe"), new WPanel()); + add(new WIcon(7, 63, 22, 20, ICN_OUTPUT, "common.output")); + add(new WIcon(7, 87, 22, 20, ICN_CATALYST, "common.catalyst")); + add(new WIcon(7, 111, 22, 40, ICN_INPUT, "common.input")); add(new WLine(57)); - addAll(groupInput, groupCatalyst, groupOutput, switcherGroup); + add(groupInput, groupCatalyst, groupOutput, switcherGroup); if (switcherGroup.getTexts().isEmpty()) switcherGroup.setTemp(Utilities.I18n.get("gui.common.default")); setNewGroup(false); buttonCopy.setDisabled(true); @@ -105,11 +110,11 @@ public class GuiRecipe extends WContainer implements IGui { public void setNewGroup(boolean b) { if (b) { - removeAll(buttonNew, buttonLabel, buttonClear, buttonCopy, buttonSave, buttonDisamb, buttonDel); - addAll(buttonYes, buttonNo, textField); + remove(buttonNew, buttonLabel, buttonClear, buttonCopy, buttonSave, buttonDisamb, buttonDel); + add(buttonYes, buttonNo, textField); } else { - addAll(buttonNew, buttonLabel, buttonClear, buttonCopy, buttonSave, buttonDisamb, buttonDel); - removeAll(buttonYes, buttonNo, textField); + add(buttonNew, buttonLabel, buttonClear, buttonCopy, buttonSave, buttonDisamb, buttonDel); + remove(buttonYes, buttonNo, textField); } } @@ -126,50 +131,49 @@ public class GuiRecipe extends WContainer implements IGui { disambiguation = new HashMap<>(); // input - List itemStacks = recipe.getIngredientStacks(recipeIndex).stream() + List inputItemStacks = recipe.getIngredientStacks(recipeIndex).stream() .map((positionedStack) -> positionedStack.item).collect(Collectors.toList()); - merge(input, itemStacks.stream().map(ILabel.Converter::from).collect(Collectors.toList())); + List outputItemStacks = Collections.singletonList(recipe.getResultStack(recipeIndex).item); - // output - ItemStack outputStack = recipe.getResultStack(recipeIndex).item; - merge(output, Stream.of(outputStack).map(ILabel.Converter::from).collect(Collectors.toList())); + merge(input, inputItemStacks, recipe); + merge(output, outputItemStacks, recipe); // catalyst ignore multiple catalyst NEIPlugin.getCatalyst(recipe).map(ILabel.Converter::from).ifPresent(catalyst -> { groupCatalyst.setLabel(catalyst, 0); }); - groupInput.setLabel(sort(input, 0, true), 0); - groupOutput.setLabel(sort(output, 21, false), 0); + groupInput.setLabel(sort(input, 0), 0); + groupOutput.setLabel(sort(output, 21), 0); refresh(); } - private void merge(ArrayList> dst, List list) { + private void merge(ArrayList> dst, List gi, IRecipeHandler context) { + List list = gi.stream().map(ILabel.Converter::from).collect(Collectors.toList()); if (list.isEmpty()) return; dst.stream().filter(p -> { CostList cl = new CostList(list); if (p.three.equals(cl)) { - ILabel.MERGER.merge(p.one, ILabel.CONVERTER.first(list)).ifPresent(i -> p.one = i); + ILabel.MERGER.merge(p.one, ILabel.CONVERTER.first(list, context)).ifPresent(i -> p.one = i); p.two = p.two.merge(cl, true, false); return true; } else return false; }).findAny().orElseGet(() -> { - Trio ret = new Trio<>(ILabel.CONVERTER.first(list), new CostList(list), + Trio ret = new Trio<>(ILabel.CONVERTER.first(list, context), new CostList(list), new CostList(list)); dst.add(ret); return ret; }); } - private ArrayList sort(ArrayList> src, int offset, boolean guess) { + private ArrayList sort(ArrayList> src, int offset) { ArrayList ret = new ArrayList<>(); for (int i = 0; i < src.size(); i++) { Trio p = src.get(i); ret.add(p.one); - if (guess && !ILabel.CONVERTER.guess(p.three.getLabels()).isEmpty()) - disambiguation.put(i + offset, p.two.getLabels()); + if (p.two.getLabels().size() > 1) disambiguation.put(i + offset, p.two.getLabels()); } return ret; } @@ -190,5 +194,11 @@ public class GuiRecipe extends WContainer implements IGui { void refresh() { buttonDisamb.setDisabled(disambiguation.isEmpty()); + try { + Recipe r = toRecipe(); + buttonSave.setDisabled(Controller.hasDuplicate(r)); + } catch (IllegalArgumentException e) { + buttonSave.setDisabled(true); + } } } diff --git a/src/main/java/me/towdium/jecalculation/gui/guis/GuiSearch.java b/src/main/java/me/towdium/jecalculation/gui/guis/GuiSearch.java index 59d8332..0348f85 100644 --- a/src/main/java/me/towdium/jecalculation/gui/guis/GuiSearch.java +++ b/src/main/java/me/towdium/jecalculation/gui/guis/GuiSearch.java @@ -1,8 +1,10 @@ package me.towdium.jecalculation.gui.guis; import me.towdium.jecalculation.data.Controller; +import me.towdium.jecalculation.data.label.ILabel; import me.towdium.jecalculation.data.structure.Recipe; import me.towdium.jecalculation.gui.JecaGui; +import me.towdium.jecalculation.gui.Resource; import me.towdium.jecalculation.gui.widgets.*; import me.towdium.jecalculation.polyfill.google.common.collect.Streams; import me.towdium.jecalculation.utils.Utilities; @@ -12,37 +14,52 @@ import net.minecraft.util.ChatComponentTranslation; import java.io.File; import java.util.ArrayList; +import java.util.IdentityHashMap; import java.util.List; +import java.util.function.Consumer; import java.util.function.Supplier; import java.util.stream.Collectors; + import static me.towdium.jecalculation.gui.Resource.*; import static me.towdium.jecalculation.gui.widgets.WLabel.Mode.PICKER; public class GuiSearch extends WContainer implements IGui { WSwitcher group; WButton export; - List> recipes; - WLabelScroll labels = new WLabelScroll(7, 51, 8, 6, PICKER, true).setListener((i, v) -> - JecaGui.displayGui(true, true, new GuiRecipe(recipes.get(v).two, recipes.get(v).three))); + List identifiers; + IdentityHashMap> recipes; + WLabelScroll labels = new WLabelScroll(7, 51, 8, 6, PICKER, true).setListener((i, v) -> { + Trio recipe = recipes.get(i.get(v)); + JecaGui.displayGui(true, true, new GuiRecipe(recipe.two, recipe.three)); + }); + public GuiSearch() { - add(new WPanel()); - add(new WSearch(25, 25, 90, labels)); + add(new WHelp("search"), new WPanel()); + add(new WSearch(26, 25, 90, labels)); add(new WIcon(7, 25, 20, 20, ICN_TEXT, "common.search")); - add(new WButtonIcon(131, 25, 20, 20, BTN_IN, "search.import") + add(new WButtonIcon(131, 25, 20, 20, BTN_IMPORT, "search.import") .setListener(i -> JecaGui.displayGui(new GuiImport()))); add(labels); } private void generate() { + identifiers = new ArrayList<>(); + recipes = new IdentityHashMap<>(); + Consumer> add = i -> { + ILabel id = i.one.getRep(); + recipes.put(id, i); + identifiers.add(id); + }; + if (group.getIndex() == 0) { - recipes = Controller.stream() - .flatMap(i -> Streams.mapWithIndex(i.two.stream(), (j, k) -> new Trio<>(j, i.one, (int) k))) - .collect(Collectors.toList()); + Controller.stream() + .flatMap(i -> Streams.mapWithIndex(i.two.stream(), (j, k) -> new Trio<>(j, i.one, (int) k))) + .forEach(add); } else { String s = group.getText(); - recipes = Streams.mapWithIndex(Controller.getRecipes(s).stream(), (i, j) -> new Trio<>(i, s, (int) j)) - .collect(Collectors.toList()); + Streams.mapWithIndex(Controller.getRecipes(s).stream(), (j, k) -> new Trio<>(j, s, (int) k)) + .forEach(add); } } @@ -51,16 +68,19 @@ public class GuiSearch extends WContainer implements IGui { remove(export); String tooltip; Supplier func; + ResourceGroup texture; generate(); if (group.getIndex() == 0) { tooltip = "search.export_all"; func = Controller::export; + texture = Resource.BTN_EXPORT_N; } else { tooltip = "search.export_group"; func = () -> Controller.export(group.getText()); + texture = Resource.BTN_EXPORT_1; } - labels.setLabels(recipes.stream().map(i -> i.one.getRep()).collect(Collectors.toList())); - export = new WButtonIcon(149, 25, 20, 20, BTN_OUT, tooltip).setListener(i -> { + labels.setLabels(identifiers); + export = new WButtonIcon(149, 25, 20, 20, texture, tooltip).setListener(i -> { File f = func.get(); Minecraft.getMinecraft().thePlayer.addChatMessage(new ChatComponentTranslation( "jecalculation.chat.export", f.getAbsolutePath())); @@ -70,7 +90,7 @@ public class GuiSearch extends WContainer implements IGui { @Override public void onVisible(JecaGui gui) { - removeAll(group); + this.remove(group); ArrayList groups = new ArrayList<>(); groups.add(Utilities.I18n.get("gui.search.all")); groups.addAll(Controller.getGroups()); @@ -78,7 +98,7 @@ public class GuiSearch extends WContainer implements IGui { if (index >= groups.size()) index = groups.size() - 1; group = new WSwitcher(7, 7, 162, groups).setListener(i -> refresh()); group.setIndex(index); - addAll(group); + this.add(group); refresh(); } } diff --git a/src/main/java/me/towdium/jecalculation/gui/guis/pickers/PickerItemStack.java b/src/main/java/me/towdium/jecalculation/gui/guis/pickers/PickerItemStack.java index 6cfa0bf..5a59869 100644 --- a/src/main/java/me/towdium/jecalculation/gui/guis/pickers/PickerItemStack.java +++ b/src/main/java/me/towdium/jecalculation/gui/guis/pickers/PickerItemStack.java @@ -22,53 +22,54 @@ import static me.towdium.jecalculation.gui.Resource.*; @ParametersAreNonnullByDefault @SideOnly(Side.CLIENT) public class PickerItemStack extends IPicker.Impl implements IGui { - WLabel label = new WLabel(7, 7, 20, 20, WLabel.Mode.SELECTOR).setListener((i, v) -> update()); - WButton bConfirm = new WButtonIcon(149, 7, 20, 20, BTN_YES).setListener(i -> callback.accept(label.getLabel())); - WButton bBbtN = new WButtonIcon(49, 7, 20, 20, BTN_NBT_N).setListener(i -> setFNbt(true)); - WButton bNbtF = new WButtonIcon(49, 7, 20, 20, BTN_NBT_F).setListener(i -> setFNbt(false)); - WButton bCapN = new WButtonIcon(68, 7, 20, 20, BTN_CAP_N).setListener(i -> setFCap(true)); - WButton bCapF = new WButtonIcon(68, 7, 20, 20, BTN_CAP_F).setListener(i -> setFCap(false)); - WButton bMetaN = new WButtonIcon(30, 7, 20, 20, BTN_META_N).setListener(i -> setFMeta(true)); - WButton bMetaF = new WButtonIcon(30, 7, 20, 20, BTN_META_F).setListener(i -> setFMeta(false)); + WLabel label = new WLabel(7, 7, 20, 20, WLabel.Mode.SELECTOR).setListener((i, v) -> update(v)); + WButton bConfirm = new WButtonIcon(149, 7, 20, 20, BTN_YES, "item_stack.confirm").setListener(i -> callback.accept(label.getLabel())); + WButton bNbtN = new WButtonIcon(49, 7, 20, 20, BTN_NBT_N, "item_stack.nbt_normal").setListener(i -> setFNbt(true)); + WButton bNbtF = new WButtonIcon(49, 7, 20, 20, BTN_NBT_F, "item_stack.nbt_fuzzy").setListener(i -> setFNbt(false)); + WButton bCapN = new WButtonIcon(68, 7, 20, 20, BTN_CAP_N, "item_stack.capability_normal").setListener(i -> setFCap(true)); + WButton bCapF = new WButtonIcon(68, 7, 20, 20, BTN_CAP_F, "item_stack.capability_fuzzy").setListener(i -> setFCap(false)); + WButton bMetaN = new WButtonIcon(30, 7, 20, 20, BTN_META_N, "item_stack.meta_normal").setListener(i -> setFMeta(true)); + WButton bMetaF = new WButtonIcon(30, 7, 20, 20, BTN_META_F, "item_stack.meta_fuzzy").setListener(i -> setFMeta(false)); + ILabel raw = ILabel.EMPTY; + boolean fMeta, fNbt, fCap = false; public PickerItemStack() { - addAll(label, bConfirm); - add(bConfirm); - update(); + add(label, bConfirm); + update(ILabel.EMPTY); } - public void update() { + public void update(ILabel l) { + raw = l; setFCap(false); setFMeta(false); setFNbt(false); - boolean b = label.getLabel() == ILabel.EMPTY; - bBbtN.setDisabled(b); + boolean b = l == ILabel.EMPTY; + bNbtN.setDisabled(b); bCapN.setDisabled(b); bMetaN.setDisabled(b); bConfirm.setDisabled(b); } private void setFNbt(boolean b) { - setF(b, bBbtN, bNbtF, i -> i.setFNbt(b)); + setF(b, bNbtN, bNbtF, () -> fNbt = b); } private void setFMeta(boolean b) { - setF(b, bMetaN, bMetaF, i -> i.setFMeta(b)); + setF(b, bMetaN, bMetaF, () -> fMeta = b); } private void setFCap(boolean b) { - setF(b, bCapN, bCapF, i -> i.setFCap(b)); + setF(b, bCapN, bCapF, () -> fCap = b); } - private void setF(boolean b, WButton be, WButton bd, Consumer c) { - if (b) { - remove(be); - add(bd); - } else { - remove(bd); - add(be); + private void setF(boolean b, WButton be, WButton bd, Runnable r) { + remove(be, bd); + if (b) add(bd); + else add(be); + r.run(); + if (raw instanceof LItemStack) { + LItemStack lis = (LItemStack) raw; + label.setLabel(lis.copy().setFCap(fCap).setFMeta(fMeta).setFNbt(fNbt)); } - ILabel l = label.getLabel(); - if (l instanceof LItemStack) c.accept((LItemStack) l); } } diff --git a/src/main/java/me/towdium/jecalculation/gui/guis/pickers/PickerPlaceholder.java b/src/main/java/me/towdium/jecalculation/gui/guis/pickers/PickerPlaceholder.java index 26ab731..730153e 100644 --- a/src/main/java/me/towdium/jecalculation/gui/guis/pickers/PickerPlaceholder.java +++ b/src/main/java/me/towdium/jecalculation/gui/guis/pickers/PickerPlaceholder.java @@ -33,6 +33,6 @@ public class PickerPlaceholder extends IPicker.Impl implements IGui { add(new WButtonIcon(95, 7, 20, 20, BTN_YES, "common.confirm").setListener(i -> { if (!create.getText().equals("")) callback.accept(new LPlaceholder(create.getText(), 1)); })); - addAll(scroll, create); + add(scroll, create); } } diff --git a/src/main/java/me/towdium/jecalculation/gui/guis/pickers/PickerSimple.java b/src/main/java/me/towdium/jecalculation/gui/guis/pickers/PickerSimple.java index 3318cbf..4031f1e 100644 --- a/src/main/java/me/towdium/jecalculation/gui/guis/pickers/PickerSimple.java +++ b/src/main/java/me/towdium/jecalculation/gui/guis/pickers/PickerSimple.java @@ -50,11 +50,9 @@ public class PickerSimple extends IPicker.Impl implements IGui { } static List generate() { - List present = new ArrayList<>(); - List empty = new ArrayList<>(); - Arrays.stream(OreDictionary.getOreNames()).map(LOreDict::new).forEach(i -> (i.isEmpty() ? empty : present).add(i)); - present.addAll(empty); - return present; + return Arrays.stream(OreDictionary.getOreNames()) + .filter(i -> !OreDictionary.getOres(i).isEmpty()) + .map(LOreDict::new).collect(Collectors.toList()); } } } diff --git a/src/main/java/me/towdium/jecalculation/gui/widgets/IContainer.java b/src/main/java/me/towdium/jecalculation/gui/widgets/IContainer.java index 481b03f..3fff469 100644 --- a/src/main/java/me/towdium/jecalculation/gui/widgets/IContainer.java +++ b/src/main/java/me/towdium/jecalculation/gui/widgets/IContainer.java @@ -5,13 +5,9 @@ package me.towdium.jecalculation.gui.widgets; * Date: 18-9-25 */ public interface IContainer extends IWidget { - void add(IWidget w); + void add(IWidget... w); - void addAll(IWidget... w); - - void remove(IWidget w); - - void removeAll(IWidget... w); + void remove(IWidget... w); void clear(); } diff --git a/src/main/java/me/towdium/jecalculation/gui/widgets/WButton.java b/src/main/java/me/towdium/jecalculation/gui/widgets/WButton.java index 44d726c..deb75cb 100644 --- a/src/main/java/me/towdium/jecalculation/gui/widgets/WButton.java +++ b/src/main/java/me/towdium/jecalculation/gui/widgets/WButton.java @@ -37,24 +37,12 @@ public abstract class WButton extends WTooltip { return this; } - protected Resource getDisabled() { - return WGT_BUTTON_D; - } - - protected Resource getNormal() { - return WGT_BUTTON_N; - } - - protected Resource getFocused() { - return WGT_BUTTON_F; - } - @Override public void onDraw(JecaGui gui, int xMouse, int yMouse) { super.onDraw(gui, xMouse, yMouse); boolean hovered = JecaGui.mouseIn(xPos + 1, yPos + 1, xSize - 2, ySize - 2, xMouse, yMouse); - gui.drawResourceContinuous(disabled ? getDisabled() : (hovered ? getFocused() : getNormal()), xPos, yPos, xSize, - ySize, 5, 5, 5, 5); + gui.drawResourceContinuous(disabled ? WGT_BUTTON_D : (hovered ? WGT_BUTTON_F : WGT_BUTTON_N) + , xPos, yPos, xSize, ySize, 5, 5, 5, 5); } @Override @@ -82,6 +70,6 @@ public abstract class WButton extends WTooltip { @Override protected List getSuffix() { - return disabled ? Arrays.asList("disabled", "active", "") : Arrays.asList("active", ""); + return disabled ? Arrays.asList("disabled", "enabled", "") : Arrays.asList("enabled", ""); } } diff --git a/src/main/java/me/towdium/jecalculation/gui/widgets/WButtonIcon.java b/src/main/java/me/towdium/jecalculation/gui/widgets/WButtonIcon.java index 5e19118..8ba1797 100644 --- a/src/main/java/me/towdium/jecalculation/gui/widgets/WButtonIcon.java +++ b/src/main/java/me/towdium/jecalculation/gui/widgets/WButtonIcon.java @@ -44,6 +44,6 @@ public class WButtonIcon extends WButton { super.onDraw(gui, xMouse, yMouse); Resource r = disabled ? rDisabled : (mouseIn(xMouse, yMouse) ? rFocused : rNormal); if (r != null) - gui.drawResource(r, xPos + (xSize - r.getXSize()) / 2, yPos + (ySize - r.getYSize()) / 2); + gui.drawResource(r, xPos + (xSize - r.getXSize() + 1) / 2, yPos + (ySize - r.getYSize() + 1) / 2); } } diff --git a/src/main/java/me/towdium/jecalculation/gui/widgets/WContainer.java b/src/main/java/me/towdium/jecalculation/gui/widgets/WContainer.java index 8983080..0e44ec4 100644 --- a/src/main/java/me/towdium/jecalculation/gui/widgets/WContainer.java +++ b/src/main/java/me/towdium/jecalculation/gui/widgets/WContainer.java @@ -9,6 +9,7 @@ import me.towdium.jecalculation.utils.Utilities; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Objects; @@ -25,18 +26,14 @@ public class WContainer implements IContainer { widgets.add(w); } - public void addAll(IWidget... w) { - for (IWidget aw : w) - add(aw); + public void add(IWidget... w) { + if (w.length == 1) widgets.add(w[0]); + else widgets.addAll(Arrays.asList(w)); } - public void remove(IWidget w) { - widgets.remove(w); - } - - public void removeAll(IWidget... w) { - for (IWidget aw : w) - remove(aw); + public void remove(IWidget... w) { + if (w.length == 1) widgets.remove(w[0]); + else widgets.removeAll(Arrays.asList(w)); } public void clear() { diff --git a/src/main/java/me/towdium/jecalculation/gui/widgets/WHelp.java b/src/main/java/me/towdium/jecalculation/gui/widgets/WHelp.java index dec2ae0..384cffb 100644 --- a/src/main/java/me/towdium/jecalculation/gui/widgets/WHelp.java +++ b/src/main/java/me/towdium/jecalculation/gui/widgets/WHelp.java @@ -17,43 +17,53 @@ import static me.towdium.jecalculation.gui.Resource.*; * Date: 18-9-23 */ @ParametersAreNonnullByDefault -public class WHelp extends WTooltip { +public class WHelp extends WContainer { protected String key; public WHelp(String content) { - super("common.help"); key = content; + if (I18n.search("gui." + WHelp.this.key + ".title").two + && I18n.search("gui." + WHelp.this.key + ".help").two) { + add(new Impl()); + } } - @Override - public void onDraw(JecaGui gui, int xMouse, int yMouse) { - super.onDraw(gui, xMouse, yMouse); - gui.drawResourceContinuous(WGT_PANEL_N, -21, 0, 25, 24, 4); - gui.drawResource(WGT_HELP_N, -19, 2); - } + private class Impl extends WTooltip { + public Impl() { + super("common.help"); + } - @Override - public boolean mouseIn(int xMouse, int yMouse) { - return JecaGui.mouseIn(-21, 0, 24, 24, xMouse, yMouse); - } + @Override + public void onDraw(JecaGui gui, int xMouse, int yMouse) { + super.onDraw(gui, xMouse, yMouse); + gui.drawResourceContinuous(WGT_PANEL_N, -21, 0, 25, 24, 4); + gui.drawResource(WGT_HELP_N, -19, 2); + } - @Override - public boolean onClicked(JecaGui gui, int xMouse, int yMouse, int button) { - boolean ret = mouseIn(xMouse, yMouse); - if (ret) - gui.root.add(new Doc()); - return ret; + @Override + public boolean mouseIn(int xMouse, int yMouse) { + return JecaGui.mouseIn(-21, 0, 24, 24, xMouse, yMouse); + } + + @Override + public boolean onClicked(JecaGui gui, int xMouse, int yMouse, int button) { + boolean ret = mouseIn(xMouse, yMouse); + if (ret) + gui.root.add(new Doc()); + return ret; + } } private class Doc extends WContainer { + Text text = new Text(); + WSwitcher switcher = new WSwitcher(7, 146, 162, text.amount()).setListener(i -> text.setPage(i.getIndex())); + public Doc() { - Text tContent = new Text(); - WSwitcher sPage = new WSwitcher(7, 146, 162, tContent.amount()); - sPage.setListener(i -> tContent.setPage(i.getIndex())); - WText tTitle = new WText(7, 7, SHADOW, I18n.get("gui." + WHelp.this.key + ".title")); - addAll(new WPanel(), new Icon(), tTitle, tContent, sPage); + WText title = new WText(7, 7, SHADOW, I18n.get("gui." + key + ".title")); + add(new WPanel(), new Icon(), title, text, switcher); } + @Override public boolean onClicked(JecaGui gui, int xMouse, int yMouse, int button) { if (!super.onClicked(gui, xMouse, yMouse, button)) @@ -61,12 +71,19 @@ public class WHelp extends WTooltip { return true; } + @Override + public boolean onScroll(JecaGui gui, int xMouse, int yMouse, int diff) { + switcher.move(-diff); + text.setPage(switcher.getIndex()); + return super.onScroll(gui, xMouse, yMouse, diff); + } + public class Text implements IWidget { List> pages = new ArrayList<>(); int page; public Text() { - List ss = I18n.wrap(I18n.get("gui." + WHelp.this.key + ".help"), 162); + List ss = I18n.wrap(I18n.get("gui." + key + ".help"), 162); List tmp = new ArrayList<>(); int count = 0; for (String s : ss) { @@ -85,8 +102,7 @@ public class WHelp extends WTooltip { count++; } } - if (!tmp.isEmpty()) - pages.add(tmp); + if (!tmp.isEmpty()) pages.add(tmp); } @Override diff --git a/src/main/java/me/towdium/jecalculation/gui/widgets/WLabel.java b/src/main/java/me/towdium/jecalculation/gui/widgets/WLabel.java index a07da9b..53fb5a8 100644 --- a/src/main/java/me/towdium/jecalculation/gui/widgets/WLabel.java +++ b/src/main/java/me/towdium/jecalculation/gui/widgets/WLabel.java @@ -31,7 +31,7 @@ import static me.towdium.jecalculation.gui.Resource.BTN_YES; @SideOnly(Side.CLIENT) public class WLabel implements IWidget { public int xPos, yPos, xSize, ySize; - public ILabel label; + ILabel label; public Mode mode; public ListenerValue listener; protected Utilities.Timer timer = new Utilities.Timer(); @@ -50,8 +50,9 @@ public class WLabel implements IWidget { return label; } - public void setLabel(ILabel label) { + public WLabel setLabel(ILabel label) { this.label = label; + return this; } @Override @@ -85,13 +86,14 @@ public class WLabel implements IWidget { @Override public boolean onScroll(JecaGui gui, int xMouse, int yMouse, int diff) { - if (mouseIn(xMouse, yMouse) && mode == Mode.EDITOR && label != ILabel.EMPTY) { - for (int i = 0; i < Math.abs(diff); i++) - label = diff > 0 ? label.increaseAmount() : label.decreaseAmount(); - notifyLsnr(); + if (mouseIn(xMouse, yMouse)) { + if (mode == Mode.EDITOR && label != ILabel.EMPTY) { + for (int i = 0; i < Math.abs(diff); i++) + label = diff > 0 ? label.increaseAmount() : label.decreaseAmount(); + notifyLsnr(); + } return true; - } else - return false; + } else return false; } @@ -165,59 +167,62 @@ public class WLabel implements IWidget { class WAmount extends WContainer { + long old = label.getAmount(); WButton bAmount = new WButtonText(xPos + xSize + 60, yPos, 20, 20, "general.to_percent", "#") .setListener(i -> setPercent(true)); WTextField wtf = new WTextField(xPos + xSize + 10, yPos + ySize / 2 - WTextField.HEIGHT / 2, 50); WButton bPercent = new WButtonText(xPos + xSize + 60, yPos, 20, 20, "general.to_percent", "%") .setListener(i -> setPercent(false)); WLabel wl = new WLabel(xPos, yPos, xSize, ySize, Mode.SELECTOR).setListener((i, v) -> update()); - WButton bYes = new WButtonIcon(xPos + xSize + 83, yPos, 20, 20, BTN_YES).setListener(i -> { - setLabel(wl.getLabel().setAmount( - wtf.getText().isEmpty() ? 0 : Integer.parseInt(wtf.getText()))); - JecaGui.getCurrent().root.remove(this); - }); + WButton bYes = new WButtonIcon(xPos + xSize + 83, yPos, 20, 20, BTN_YES) + .setListener(i -> JecaGui.getCurrent().root.remove(this)); WButton bNo = new WButtonIcon(xPos + xSize + 102, yPos, 20, 20, BTN_NO).setListener(i -> { setLabel(ILabel.EMPTY); JecaGui.getCurrent().root.remove(this); }); + public WAmount() { wl.setLabel(label); add(new WPanel(xPos - 5, yPos - 5, xSize + 133, ySize + 10)); add(new WText(xPos + xSize + 3, yPos + 5, PLAIN, "x")); - addAll(wl, wtf, bYes, bNo); + add(wl, wtf, bYes, bNo); wtf.setListener(i -> { try { - Integer.parseInt(wtf.getText()); + long n = Long.parseLong(wtf.getText()); + label.setAmount(n); wtf.setColor(COLOR_TEXT_WHITE); bYes.setDisabled(false); } catch (NumberFormatException e) { boolean acceptable = wtf.getText().isEmpty(); wtf.setColor(acceptable ? COLOR_TEXT_WHITE : COLOR_TEXT_RED); bYes.setDisabled(!acceptable); + label.setAmount(acceptable ? 1 : old); } + notifyLsnr(); }); update(); } private void update() { - label = wl.label; - bAmount.setDisabled(!wl.label.acceptPercent()); - setPercent(label.isPercent()); - } - - private void setPercent(boolean b) { - if (b) { + bAmount.setDisabled(!label.acceptPercent()); + if (label.isPercent()) { remove(bAmount); add(bPercent); } else { remove(bPercent); add(bAmount); } - if (label.acceptPercent()) label.setPercent(b); wtf.setText(Long.toString(label.getAmount())); } + private void setPercent(boolean b) { + boolean original = label.isPercent(); + if (label.acceptPercent()) label.setPercent(b); + if (b != original) notifyLsnr(); + update(); + } + @Override public boolean onKey(JecaGui gui, char ch, int code) { if (super.onKey(gui, ch, code)) diff --git a/src/main/java/me/towdium/jecalculation/gui/widgets/WLine.java b/src/main/java/me/towdium/jecalculation/gui/widgets/WLine.java index df3394c..ac12f0c 100644 --- a/src/main/java/me/towdium/jecalculation/gui/widgets/WLine.java +++ b/src/main/java/me/towdium/jecalculation/gui/widgets/WLine.java @@ -6,7 +6,7 @@ import me.towdium.jecalculation.gui.JecaGui; import me.towdium.jecalculation.gui.Resource; import javax.annotation.ParametersAreNonnullByDefault; -import static me.towdium.jecalculation.gui.Resource.WGT_LINE; + /** * Author: towdium * Date: 17-8-18. @@ -14,14 +14,26 @@ import static me.towdium.jecalculation.gui.Resource.WGT_LINE; @ParametersAreNonnullByDefault @SideOnly(Side.CLIENT) public class WLine implements IWidget { - public int y; + int xPos, yPos, xSize, ySize; public WLine(int y) { - this.y = y; + this(7, y, 162, true); + } + + public WLine(int xPos, int yPos, int size, boolean horizontal) { + this.yPos = yPos; + this.xPos = xPos; + if (horizontal) { + xSize = size; + ySize = 2; + } else { + xSize = 2; + ySize = size; + } } @Override public void onDraw(JecaGui gui, int xMouse, int yMouse) { - gui.drawResource(WGT_LINE, 6, y - 1); + gui.drawResourceContinuous(Resource.WGT_SLOT, xPos, yPos, xSize, ySize, 1); } } diff --git a/src/main/java/me/towdium/jecalculation/gui/widgets/WPage.java b/src/main/java/me/towdium/jecalculation/gui/widgets/WPage.java index 09b4aa6..c2e8479 100644 --- a/src/main/java/me/towdium/jecalculation/gui/widgets/WPage.java +++ b/src/main/java/me/towdium/jecalculation/gui/widgets/WPage.java @@ -33,9 +33,9 @@ public class WPage extends WTooltip { @Override public void onDraw(JecaGui gui, int xMouse, int yMouse) { - Resource resource = focused ? (index == 0 ? WGT_PAGER_F0 : WGT_PAGER_FN) : WGT_PANEL_N; - gui.drawResourceContinuous(resource, index * 24, -21, 24, 25, 4, 4, 4, 4); - record.representation.drawLabel(gui, index * 24 + 4, -17, false); + Resource resource = focused ? WGT_PAGER_F : WGT_PANEL_N; + gui.drawResourceContinuous(resource, index * 24 + 3, -21, 24, 25, 4, 4, 4, 4); + record.representation.drawLabel(gui, index * 24 + 7, -17, false); super.onDraw(gui, xMouse, yMouse); } diff --git a/src/main/java/me/towdium/jecalculation/gui/widgets/WScroll.java b/src/main/java/me/towdium/jecalculation/gui/widgets/WScroll.java index 321dea0..45c39a2 100644 --- a/src/main/java/me/towdium/jecalculation/gui/widgets/WScroll.java +++ b/src/main/java/me/towdium/jecalculation/gui/widgets/WScroll.java @@ -34,7 +34,7 @@ public class WScroll implements IWidget { else drag = false; gui.drawResourceContinuous(Resource.WGT_SLOT, xPos, yPos, 14, ySize, 3, 3, 3, 3); - gui.drawResource(Resource.WGT_SCROLL, xPos + 1, yPos + 1 + current); + gui.drawResource(Resource.WGT_SCROLL, xPos, yPos + current); } @Override diff --git a/src/main/java/me/towdium/jecalculation/gui/widgets/WSwitcher.java b/src/main/java/me/towdium/jecalculation/gui/widgets/WSwitcher.java index 4743b59..b00f961 100644 --- a/src/main/java/me/towdium/jecalculation/gui/widgets/WSwitcher.java +++ b/src/main/java/me/towdium/jecalculation/gui/widgets/WSwitcher.java @@ -47,29 +47,29 @@ public class WSwitcher extends WContainer { this.yPos = yPos; this.keys = keys; left = new WButtonIcon(xPos, yPos, SIZE, SIZE, WGT_ARR_L).setListener(i -> { - if (temp == null) move(false); + if (temp == null) move(-1); else setTemp(null); }); right = new WButtonIcon(xPos + xSize - SIZE, yPos, SIZE, SIZE, WGT_ARR_R).setListener(i -> { - if (temp == null) move(true); + if (temp == null) move(1); else setTemp(null); }); wRect = new WRectangle(xPos + SIZE, yPos, xSize - 2 * SIZE, SIZE, COLOR_GUI_GREY); wText = new WText(xPos + SIZE, yPos + 2, xSize - 2 * SIZE, SHADOW, "", true); index = new Circulator(keys.size()); refresh(); - addAll(left, right, wRect, wText); + add(left, right, wRect, wText); } - protected void move(boolean forward) { - if (temp == null) { - if (forward) - index.move(1); - else - index.move(-1); - } else + public void move(int diff) { + if (diff == 0) return; + if (temp != null) { temp = null; + if (diff > 0) diff -= 1; + else diff += 1; + } + index.move(diff); notifyLsnr(); refresh(); } diff --git a/src/main/java/me/towdium/jecalculation/gui/widgets/WTextField.java b/src/main/java/me/towdium/jecalculation/gui/widgets/WTextField.java index 1e77937..f382571 100644 --- a/src/main/java/me/towdium/jecalculation/gui/widgets/WTextField.java +++ b/src/main/java/me/towdium/jecalculation/gui/widgets/WTextField.java @@ -31,6 +31,10 @@ public class WTextField implements IWidget { @Override public boolean onClicked(JecaGui gui, int xMouse, int yMouse, int button) { textField.mouseClicked(xMouse, yMouse, button); + if (textField.isFocused() && button == 1) { + textField.setText(""); + notifyLsnr(); + } return false; } diff --git a/src/main/java/me/towdium/jecalculation/gui/widgets/WTick.java b/src/main/java/me/towdium/jecalculation/gui/widgets/WTick.java index ad00f5c..009274f 100644 --- a/src/main/java/me/towdium/jecalculation/gui/widgets/WTick.java +++ b/src/main/java/me/towdium/jecalculation/gui/widgets/WTick.java @@ -1,10 +1,12 @@ package me.towdium.jecalculation.gui.widgets; +import me.towdium.jecalculation.gui.JecaGui; import me.towdium.jecalculation.gui.Resource; import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; import java.util.ArrayList; +import java.util.Collections; import java.util.List; import java.util.function.Consumer; @@ -57,7 +59,7 @@ public class WTick extends WContainer { private class Normal extends WButton { public Normal(int xPos, int yPos, int xSize, int ySize, @Nullable String name) { - super(xPos, yPos, xSize, ySize, name + ".normal"); + super(xPos, yPos, xSize, ySize, name); listener = i -> { setSelected(true); WTick.this.listener.invoke(WTick.this); @@ -67,13 +69,13 @@ public class WTick extends WContainer { @Override protected List getSuffix() { - return new ArrayList<>(); + return Collections.singletonList("unselected"); } } private class Selected extends WButton { public Selected(int xPos, int yPos, int xSize, int ySize, @Nullable String name) { - super(xPos, yPos, xSize, ySize, name + ".selected"); + super(xPos, yPos, xSize, ySize, name); listener = i -> { setSelected(false); WTick.this.listener.invoke(WTick.this); @@ -81,24 +83,17 @@ public class WTick extends WContainer { disabled = WTick.this.disabled; } - @Override - protected Resource getDisabled() { - return WGT_BUTTON_S_D; - } - - @Override - protected Resource getNormal() { - return WGT_BUTTON_S_N; - } - - @Override - protected Resource getFocused() { - return WGT_BUTTON_S_F; - } - @Override protected List getSuffix() { - return new ArrayList<>(); + return Collections.singletonList("selected"); + } + + @Override + public void onDraw(JecaGui gui, int xMouse, int yMouse) { + super.onDraw(gui, xMouse, yMouse); + boolean hovered = JecaGui.mouseIn(xPos + 1, yPos + 1, xSize - 2, ySize - 2, xMouse, yMouse); + gui.drawResourceContinuous(disabled ? WGT_BUTTON_S_D : (hovered ? WGT_BUTTON_S_F : WGT_BUTTON_S_N) + , xPos, yPos, xSize, ySize, 5, 5, 5, 5); } } } diff --git a/src/main/java/me/towdium/jecalculation/polyfill/NBTHelper.java b/src/main/java/me/towdium/jecalculation/polyfill/NBTHelper.java index 26101ca..e63a765 100644 --- a/src/main/java/me/towdium/jecalculation/polyfill/NBTHelper.java +++ b/src/main/java/me/towdium/jecalculation/polyfill/NBTHelper.java @@ -34,8 +34,8 @@ public class NBTHelper { } public static Spliterator spliterator(NBTTagList list) { - List tagList = (List) ReflectionHelper.get(list, "tagList"); - return Spliterators.spliteratorUnknownSize(((List)tagList).iterator(), 0); + List tagList = (List) list.tagList; + return Spliterators.spliteratorUnknownSize(tagList.iterator(), 0); } public static NBTTagCompound getOrCreateSubCompound(ItemStack itemStack, String key) { diff --git a/src/main/java/me/towdium/jecalculation/utils/Utilities.java b/src/main/java/me/towdium/jecalculation/utils/Utilities.java index 38e9bac..36eb866 100644 --- a/src/main/java/me/towdium/jecalculation/utils/Utilities.java +++ b/src/main/java/me/towdium/jecalculation/utils/Utilities.java @@ -75,6 +75,11 @@ public class Utilities { }; } + public static String repeat(String s, int n) { + StringBuilder sb = new StringBuilder(); + for (int i = 0; i < n; i++) sb.append(s); + return sb.toString(); + } // MOD NAME @Nullable @@ -312,11 +317,11 @@ public class Utilities { this.limit = limit; } - public void push(T obj) { + public void push(T obj, boolean replace) { + if (replace) data.pop(); data.removeIf(t -> tester != null ? tester.test(t, obj) : t.equals(obj)); - data.push(obj); - if (data.size() > limit) - data.pop(); + data.addFirst(obj); + if (data.size() > limit) data.removeLast(); } public List toList() { diff --git a/src/main/resources/assets/jecalculation/lang/en_US.lang b/src/main/resources/assets/jecalculation/lang/en_US.lang index f1a30a9..c94ddb7 100644 --- a/src/main/resources/assets/jecalculation/lang/en_US.lang +++ b/src/main/resources/assets/jecalculation/lang/en_US.lang @@ -1,28 +1,35 @@ +# suppress inspection "UnusedProperty" for whole file jecalculation.key.category=Just Enough Calculation jecalculation.key.open_gui=Open GUI jecalculation.command.help.list=Here is the list of all commands available: jecalculation.command.state.desc=Server %s. jecalculation.command.reload.done=Reloading finished -jecalculation.chat.max_loop=Process terminated when exceeding max loop count to avoid freezing. +jecalculation.chat.max_loop=Sorry, it seems the calculation does not converge in 1000 iterations. Please contact the developer if you think this process should have a result. jecalculation.chat.server_mode=The mod is not working in client mode. Key binding will not work. jecalculation.chat.export=Selected recipes has been exported to "%s". -jecalculation.gui.calculator.recipe.active=New recipe +jecalculation.gui.calculator.inventory_enabled=Inventory detection enabled +jecalculation.gui.calculator.inventory_disabled=Inventory detection disabled +jecalculation.gui.calculator.recipeNew recipe jecalculation.gui.calculator.help=Welcome to the main interface of the mod. The first thing suggested to do is to use the two buttons in the upper right to input the recipes, which allows you to create and edit recipes. All of the calculations require recipes to be recorded.\fTo calculate the cost of an item, simply pick the item from JEIs item list then put it in the slot in the upper left of the interface with all of the recipes to use recorded. The result will then be displayed in the lower slots of the interface. The four buttons provide options to display input, output, catalyst and crafting steps.\fThe button in the upper left allows you to edit and get a label. Labels are a very important part of the mod. They are basically things that can be put into a slot, including ItemStack, FluidStack, OreDict entry, or simply a placeholder. Lables are used in the calculation process. Most of time they are quite clever as long as you configure them properly. jecalculation.gui.calculator.title=Crafting Calculator jecalculation.gui.calculator.label=Add label jecalculation.gui.calculator.step=Steps jecalculation.gui.calculator.recipe=Add recipe jecalculation.gui.calculator.search=Search recipe -jecalculation.gui.recipe.save=Save -jecalculation.gui.recipe.copy.active=Save as new recipe +jecalculation.gui.calculator.suggest=Suggestions +jecalculation.gui.disamb.list=Raw types +jecalculation.gui.disamb.label=Inferred types +jecalculation.gui.recipe.save.enabled=Save +jecalculation.gui.recipe.save.disabled=Cannot save recipe because\nrecipe is invalid or you\nalready have same recipe +jecalculation.gui.recipe.copy.enabled=Save as new recipe jecalculation.gui.recipe.copy.disabled=Cannot save as new recipe\nbecause you are not editing a recipe -jecalculation.gui.recipe.delete.active=Delete recipe +jecalculation.gui.recipe.delete.enabled=Delete recipe jecalculation.gui.recipe.delete.disabled=Cannot delete recipe because\nyou are not editing a recipe jecalculation.gui.recipe.clear=Clear grid jecalculation.gui.recipe.label=Add label -jecalculation.gui.recipe.confirm.active=Confirm +jecalculation.gui.recipe.confirm.enabled=Confirm jecalculation.gui.recipe.confirm.disabled=Cannot use empty string as group name -jecalculation.gui.recipe.disamb.active=Open disambiguate settings +jecalculation.gui.recipe.disamb.enabled=Open disambiguate settings jecalculation.gui.recipe.disamb.disabled=Disambiguate settings not available\nbecause of no ambiguous element jecalculation.gui.recipe.new=New group jecalculation.gui.recipe.title=Recipe Editing @@ -31,11 +38,29 @@ jecalculation.gui.label.title=Label Editing jecalculation.gui.label.help=This interface allows you to create labels. This mod provides 4 types of labels: fluid, oreDict, placeholder and item. To create fluid and oreDict label, you can just search and pick. Item labels represent an item stack, but with fuzzy options for meta, nbt and capalibity.\fAs is indicated by the name, Placeholder labels do not hold any content. It is just a string to connect recipes. You can use it for anything, like structure or event energy. To create one, simply type in the name, then hit the tick. You can also search and pick from existing ones. jecalculation.gui.search.all=All jecalculation.gui.search.import=Import recipes -jecalculation.gui.search.export_group.active=Export recipes in current group -jecalculation.gui.search.export_all.active=Export all recorded recipes -jecalculation.gui.search.export_group.disabled=Cannot export because no recipe recorded +jecalculation.gui.search.export_group=Export recipes in current group +jecalculation.gui.search.export_all.enabled=Export all recipes jecalculation.gui.search.export_all.disabled=Cannot export because no recipe recorded +jecalculation.gui.search.title=Recipe Management +jecalculation.gui.search.help=This interface allows you to search, import and export recipes. Simply click the item to edit or delete the recipe. The two buttons in upper left will be helpful if you want to import or export recipes. +jecalculation.gui.import.title=Recipe Import +jecalculation.gui.import.help=This interface allows you to import recipes. First place exported data files into "config/JustEnoughCalculation/data/", then you will see them in the list organized as "filename/group" if there is no error (otherwise check the log). Use the buttons to select recipe groups to import and hit the tick, then you are done.\fFor pack makers or server owners, you can rename the data file and place it at "config/JustEnoughCalculation/default.json" to set it as default. When player creates world or joins server, the default recipes will be automatically setup for the user. +jecalculation.gui.import.confirm.enabled=Confirm +jecalculation.gui.import.confirm.disabled=Cannot import because no record is selected. +jecalculation.gui.import.tick.unselected=Click to select group +jecalculation.gui.import.tick.selected=Click to deselect group jecalculation.gui.placeholder.create=Create new placeholder +jecalculation.gui.item_stack.confirm.enabled=Confirm +jecalculation.gui.item_stack.confirm.disabled=Cannot create because no item is chosen +jecalculation.gui.item_stack.nbt_normal.enabled=NBT sensitive +jecalculation.gui.item_stack.nbt_normal.disabled=Please choose item first +jecalculation.gui.item_stack.nbt_fuzzy.enabled=NBT ignored +jecalculation.gui.item_stack.capability_normal.enabled=Capability sensitive +jecalculation.gui.item_stack.capability_normal.disabled=Please choose item first +jecalculation.gui.item_stack.capability_fuzzy.enabled=Capability ignored +jecalculation.gui.item_stack.meta_normal.enabled=Metadata sensitive +jecalculation.gui.item_stack.meta_normal.disabled=Please choose item first +jecalculation.gui.item_stack.meta_fuzzy.enabled=Metadata ignored jecalculation.gui.common.label.ore=OreDict jecalculation.gui.common.label.fluid=Fluid jecalculation.gui.common.label.placeholder=Placeholder @@ -52,4 +77,8 @@ jecalculation.gui.common.cancel=Cancel jecalculation.config.force_client=Force Client jecalculation.label.ore_dict.name=Ore: %s jecalculation.label.placeholder.name=Placeholder: %s +jecalculation.label.item_stack.fuzzy_meta=Fuzzy metadata +jecalculation.label.item_stack.fuzzy_nbt=Fuzzy NBT +jecalculation.label.item_stack.fuzzy_cap=Fuzzy capabilities jecalculation.label.common.amount=Amount: %s + diff --git a/src/main/resources/assets/jecalculation/textures/gui/resources.png b/src/main/resources/assets/jecalculation/textures/gui/resources.png index 0230b6e48572174aa3968dadff0294898e6e0147..6020a05c9cc8807f32b523cc6ee30fbb07983dec 100644 GIT binary patch literal 23287 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 SVFO200~)HjkIR&;-v1A`tD5%! literal 21034 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