diff --git a/build.gradle b/build.gradle index 3e9b035..1fb20e7 100644 --- a/build.gradle +++ b/build.gradle @@ -177,7 +177,7 @@ publishing { maven(MavenPublication) { groupId = project.mod_group artifactId = project.mod_name - version = project.mod_version + "-" + project.bta_version + version = project.mod_version from components.java } } diff --git a/gradle.properties b/gradle.properties index 5358f64..b62d8e8 100644 --- a/gradle.properties +++ b/gradle.properties @@ -15,6 +15,6 @@ mod_menu_version=3.0.0 halplibe_version=5.2.4 # Mod -mod_version=1.1.2 +mod_version=2.0.0 mod_group=turing mod_name=tmb diff --git a/src/main/java/turing/tmb/InfoRecipeCategory.java b/src/main/java/turing/tmb/InfoRecipeCategory.java index 28f2a69..c66c76c 100644 --- a/src/main/java/turing/tmb/InfoRecipeCategory.java +++ b/src/main/java/turing/tmb/InfoRecipeCategory.java @@ -45,13 +45,18 @@ public IDrawable getBackground() { @Override public void drawRecipe(ITMBRuntime runtime, InfoRecipeTranslator recipe, IRecipeLayout layout, List ingredients, ILookupContext context) { - ingredients.add(0, new IngredientList(recipe.getOriginal().getIngredient())); + getIngredients(recipe, layout, context, ingredients); String text = recipe.getOriginal().getInfoTranslated(); runtime.getGuiHelper().getMinecraft().font.drawStringIntoConstrainedBlock(text, 3, 21, background.getWidth() - 5, 0xFFFFFF, true); runtime.getGuiHelper().getMinecraft().font.drawStringIntoConstrainedBlock(text, 2, 20, background.getWidth() - 4, 0xFFFFFF); } + @Override + public void getIngredients(InfoRecipeTranslator recipe, IRecipeLayout layout, ILookupContext context, List ingredients) { + ingredients.add(0, new IngredientList(recipe.getOriginal().getIngredient())); + } + @Override public IRecipeLayout getRecipeLayout() { return new RecipeLayoutBuilder() diff --git a/src/main/java/turing/tmb/RecipeIndex.java b/src/main/java/turing/tmb/RecipeIndex.java index f4cc2c1..cde9af7 100644 --- a/src/main/java/turing/tmb/RecipeIndex.java +++ b/src/main/java/turing/tmb/RecipeIndex.java @@ -116,6 +116,11 @@ public List> getAllCategories() { return Collections.unmodifiableList(categories); } + @Override + public Map, List>> getRecipeLists() { + return recipeLists; + } + @Override public void registerCatalyst(IRecipeCategory category, ITypedIngredient catalyst) { catalysts.get(category).add(catalyst); diff --git a/src/main/java/turing/tmb/RecipeIngredient.java b/src/main/java/turing/tmb/RecipeIngredient.java new file mode 100644 index 0000000..9b4f8ca --- /dev/null +++ b/src/main/java/turing/tmb/RecipeIngredient.java @@ -0,0 +1,20 @@ +package turing.tmb; + +import turing.tmb.api.ingredient.ITypedIngredient; +import turing.tmb.api.recipe.IRecipeCategory; +import turing.tmb.api.recipe.IRecipeTranslator; +import turing.tmb.api.recipe.RecipeIngredientRole; + +public class RecipeIngredient { + public final ITypedIngredient ingredient; + public final IRecipeTranslator recipe; + public final IRecipeCategory> category; + public final RecipeIngredientRole role; + + public RecipeIngredient(ITypedIngredient ingredient, IRecipeTranslator recipe, IRecipeCategory category, RecipeIngredientRole role) { + this.ingredient = ingredient; + this.recipe = recipe; + this.category = (IRecipeCategory>) category; + this.role = role; + } +} diff --git a/src/main/java/turing/tmb/RecipeTreeIngredient.java b/src/main/java/turing/tmb/RecipeTreeIngredient.java new file mode 100644 index 0000000..d81eca5 --- /dev/null +++ b/src/main/java/turing/tmb/RecipeTreeIngredient.java @@ -0,0 +1,21 @@ +package turing.tmb; + +public class RecipeTreeIngredient { + public final RecipeIngredient ingredient; + public int x; + public int y; + + public RecipeTreeIngredient(RecipeIngredient ingredient, int x, int y) { + this.ingredient = ingredient; + this.x = x; + this.y = y; + } + + public int getX() { + return x; + } + + public int getY() { + return y; + } +} diff --git a/src/main/java/turing/tmb/TMB.java b/src/main/java/turing/tmb/TMB.java index 6e6ddef..48c9b6a 100644 --- a/src/main/java/turing/tmb/TMB.java +++ b/src/main/java/turing/tmb/TMB.java @@ -1,5 +1,8 @@ package turing.tmb; +import com.mojang.nbt.NbtIo; +import com.mojang.nbt.tags.CompoundTag; +import com.mojang.nbt.tags.Tag; import net.fabricmc.api.ModInitializer; import net.fabricmc.loader.api.FabricLoader; import net.minecraft.client.Minecraft; @@ -8,19 +11,29 @@ import net.minecraft.client.gui.options.components.OptionsCategory; import net.minecraft.client.gui.options.data.OptionsPages; import net.minecraft.client.option.GameSettings; +import net.minecraft.core.item.ItemStack; import net.minecraft.core.net.command.CommandManager; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import turing.tmb.api.ITMBPlugin; import turing.tmb.api.TMBEntrypoint; +import turing.tmb.api.ingredient.ITypedIngredient; +import turing.tmb.api.recipe.IRecipeCategory; +import turing.tmb.api.recipe.IRecipeTranslator; +import turing.tmb.api.recipe.RecipeIngredientRole; import turing.tmb.api.runtime.ITMBRuntime; import turing.tmb.plugin.BTATweaker; import turing.tmb.util.IKeybinds; import turing.tmb.vanilla.VanillaPlugin; import turniplabs.halplibe.util.ClientStartEntrypoint; +import java.io.File; +import java.io.IOException; +import java.nio.file.Files; import java.util.ArrayList; import java.util.List; +import java.util.Map; +import java.util.Optional; public class TMB implements ModInitializer, ClientStartEntrypoint, TMBEntrypoint { public static final String MOD_ID = "tmb"; @@ -49,6 +62,9 @@ public static void onClientStart() { GameSettings settings = Minecraft.getMinecraft().gameSettings; OptionsCategory category = new OptionsCategory("gui.options.page.controls.category.tmb"); category.withComponent(new KeyBindingComponent(((IKeybinds) settings).toomanyblocks$getKeyHideTMB())); + category.withComponent(new KeyBindingComponent(((IKeybinds) settings).toomanyblocks$getKeyAddFavourite())); + category.withComponent(new KeyBindingComponent(((IKeybinds) settings).toomanyblocks$getKeySetDefaultRecipe())); + category.withComponent(new KeyBindingComponent(((IKeybinds) settings).toomanyblocks$getKeyShowRecipeTree())); OptionsPages.CONTROLS.withComponent(category); OptionsCategory category1 = new OptionsCategory("gui.options.page.general.category.tmb"); category1.withComponent(new BooleanOptionComponent(((IKeybinds) settings).toomanyblocks$getIsRecipeViewEnabled())); @@ -67,7 +83,7 @@ public void beforeClientStart() { @Override public void afterClientStart() { - + loadData(); } private static void loadTMB() { @@ -84,9 +100,111 @@ private static void loadTMB() { plugin.registerRecipeCatalysts(runtime); plugin.registerRecipes(runtime); } + runtime.isReady = true; long timeTook = System.currentTimeMillis() - time; LOGGER.info("TMB loaded in {}ms!", timeTook); - runtime.isReady = true; + } + + public static void saveData() { + File dataFolder = new File(Minecraft.getMinecraft().getMinecraftDir()+"/config/tmb/"); + dataFolder.mkdirs(); + + File dataFile = new File(Minecraft.getMinecraft().getMinecraftDir()+"/config/tmb/", "data.dat"); + + CompoundTag dataTag = new CompoundTag(); + + CompoundTag favourites = new CompoundTag(); + List> iTypedIngredients = TMB.getRuntime().getFavourites(); + for (int j = 0; j < iTypedIngredients.size(); j++) { + ITypedIngredient favourite = iTypedIngredients.get(j); + CompoundTag favouriteTag = new CompoundTag(); + favouriteTag.putString("namespace", favourite.getNamespace()); + favouriteTag.putString("uid", favourite.getUid()); + favourites.put(String.valueOf(j), favouriteTag); + } + dataTag.putCompound("Favourites", favourites); + + CompoundTag defaultRecipes = new CompoundTag(); + int i = 0; + for (Map.Entry> entry : TMB.getRuntime().getDefaultRecipes().entrySet()) { + RecipeIngredient key = entry.getKey(); + IRecipeTranslator value = entry.getValue(); + CompoundTag defaultRecipeTag = new CompoundTag(); + CompoundTag ingredientTag = new CompoundTag(); + CompoundTag categoryTag = new CompoundTag(); + + defaultRecipeTag.putString("recipe", value.getOriginal().toString()); + ingredientTag.putString("namespace", key.ingredient.getNamespace()); + ingredientTag.putString("uid", key.ingredient.getUid()); + defaultRecipeTag.put("ingredient", ingredientTag); + categoryTag.putString("namespace", key.category.getNamespace()); + categoryTag.putString("name", key.category.getName()); + defaultRecipeTag.put("category", categoryTag); + defaultRecipes.put(String.valueOf(i), defaultRecipeTag); + i++; + } + dataTag.putCompound("DefaultRecipes", defaultRecipes); + + try { + if(!dataFile.exists()) { + if (!dataFile.createNewFile()) { + return; + } + } + NbtIo.writeCompressed(dataTag, Files.newOutputStream(dataFile.toPath())); + } catch (IOException e) { + throw new RuntimeException(e); + } + } + + public static void loadData() { + + File dataFolder = new File(Minecraft.getMinecraft().getMinecraftDir()+"/config/tmb/"); + dataFolder.mkdirs(); + + File dataFile = new File(Minecraft.getMinecraft().getMinecraftDir()+"/config/tmb/", "data.dat"); + try { + if(!dataFile.exists()) { + return; + } + CompoundTag tag = NbtIo.readCompressed(Files.newInputStream(dataFile.toPath())); + + TMB.getRuntime().getFavourites().clear(); + for (Tag compoundTag : tag.getCompound("Favourites").getValues()) { + CompoundTag favouriteTag = (CompoundTag) compoundTag; + TMB.getRuntime().getIngredientIndex() + .getIngredient(favouriteTag.getString("namespace"), favouriteTag.getString("uid")) + .ifPresent(TMB.getRuntime().getFavourites()::add); + } + + for (Tag compoundTag : tag.getCompound("DefaultRecipes").getValues()) { + CompoundTag defaultRecipeTag = (CompoundTag) compoundTag; + CompoundTag ingredientTag = defaultRecipeTag.getCompound("ingredient"); + CompoundTag categoryTag = defaultRecipeTag.getCompound("category"); + String recipeId = defaultRecipeTag.getString("recipe"); + + Optional> category = TMB.getRuntime().getRecipeIndex().getAllCategories().stream() + .filter(it -> + it.getName().equals(categoryTag.getString("name")) + && it.getNamespace().equals(categoryTag.getString("namespace"))).findFirst(); + + Optional> recipe = category + .flatMap(it -> TMB.getRuntime().getRecipeIndex().getRecipeLists().get(it).stream() + .filter(it2 -> it2.getOriginal().toString().equals(recipeId)).findFirst()); + + Optional> ingredient = recipe + .flatMap(it -> TMB.getRuntime().getIngredientIndex() + .getIngredient(ingredientTag.getString("namespace"), ingredientTag.getString("uid"))); + + ingredient + .ifPresent(it -> { + RecipeIngredient recipeIngredient = new RecipeIngredient(it, recipe.get(), category.get(), RecipeIngredientRole.OUTPUT); + TMB.getRuntime().getDefaultRecipes().put(recipeIngredient, recipe.get()); + }); + } + } catch (IOException e) { + throw new RuntimeException(e); + } } private static void clear() { diff --git a/src/main/java/turing/tmb/TMBRuntime.java b/src/main/java/turing/tmb/TMBRuntime.java index 7d36190..f6563a5 100644 --- a/src/main/java/turing/tmb/TMBRuntime.java +++ b/src/main/java/turing/tmb/TMBRuntime.java @@ -4,7 +4,6 @@ import net.minecraft.client.gui.TooltipElement; import net.minecraft.core.item.ItemStack; import net.minecraft.core.util.collection.Pair; -import org.jetbrains.annotations.Nullable; import turing.tmb.api.ingredient.IIngredientRegistry; import turing.tmb.api.ingredient.IIngredientType; import turing.tmb.api.ingredient.IIngredientTypeRegistry; @@ -31,6 +30,8 @@ public class TMBRuntime implements ITMBRuntime { protected final RecipeIndex recipeIndex = new RecipeIndex(this); protected static final TooltipElement tooltipElement = new TooltipElement(Minecraft.getMinecraft()); protected final GuiHelper guiHelper = new GuiHelper(this); + public final List> favourites = new ArrayList<>(); + protected final Map> defaultRecipes = new HashMap<>(); protected boolean isReady; protected TMBRuntime() { @@ -56,6 +57,10 @@ public void showRecipe(ILookupContext lookup) { } } } + if(results.size() == 1 && lookup.getRole() == RecipeIngredientRole.OUTPUT){ + Pair, IRecipeTranslator> pair = results.get(0); + defaultRecipes.put(new RecipeIngredient(lookup.getIngredient(),pair.getRight(),pair.getLeft(),RecipeIngredientRole.OUTPUT),pair.getRight()); + } if (!results.isEmpty()) { ScreenTMBRecipe.show(results, lookup, null); } @@ -107,6 +112,16 @@ public RecipeIndex getRecipeIndex() { return recipeIndex; } + @Override + public Map> getDefaultRecipes() { + return defaultRecipes; + } + + @Override + public List> getFavourites() { + return favourites; + } + public static String getTooltipText(ItemStack stack, boolean showDescription) { return tooltipElement.getTooltipText(stack, showDescription); } diff --git a/src/main/java/turing/tmb/TypedIngredient.java b/src/main/java/turing/tmb/TypedIngredient.java index e805391..c7c71e6 100644 --- a/src/main/java/turing/tmb/TypedIngredient.java +++ b/src/main/java/turing/tmb/TypedIngredient.java @@ -3,6 +3,7 @@ import net.minecraft.core.item.ItemStack; import turing.tmb.api.VanillaTypes; import turing.tmb.api.ingredient.IIngredientType; +import turing.tmb.api.ingredient.IIngredientTypeWithSubtypes; import turing.tmb.api.ingredient.ITypedIngredient; import turing.tmb.util.ModIDHelper; @@ -20,7 +21,7 @@ public TypedIngredient(String namespace, String name, IIngredientType type, T } public static TypedIngredient itemStackIngredient(ItemStack stack) { - return new TypedIngredient<>(ModIDHelper.getModIDForItem(stack), stack.getDisplayName(), VanillaTypes.ITEM_STACK, stack); + return new TypedIngredient<>(ModIDHelper.getModIDForItem(stack), stack.getDisplayName(), VanillaTypes.ITEM_STACK, stack.copy()); } @Override @@ -43,6 +44,36 @@ public String getUid() { return name; } + @Override + public String getName() { + if(getType() instanceof IIngredientTypeWithSubtypes){ + return ((IIngredientTypeWithSubtypes) getType()).getName(ingredient); + } + return ""; + } + + @Override + public void addAmount(int amount) { + if(getType() instanceof IIngredientTypeWithSubtypes){ + ((IIngredientTypeWithSubtypes) getType()).add(ingredient, amount); + } + } + + @Override + public int getAmount() { + if(getType() instanceof IIngredientTypeWithSubtypes){ + return ((IIngredientTypeWithSubtypes) getType()).getAmount(ingredient); + } + return 1; + } + + public boolean matches(Object ingredient){ + if(getType() instanceof IIngredientTypeWithSubtypes){ + return ((IIngredientTypeWithSubtypes) getType()).matches(this.ingredient, ingredient); + } + return false; + } + @Override public int hashCode() { return getUid().hashCode() + getNamespace().hashCode(); diff --git a/src/main/java/turing/tmb/api/VanillaTypes.java b/src/main/java/turing/tmb/api/VanillaTypes.java index 24688eb..62783bd 100644 --- a/src/main/java/turing/tmb/api/VanillaTypes.java +++ b/src/main/java/turing/tmb/api/VanillaTypes.java @@ -3,6 +3,7 @@ import net.minecraft.core.WeightedRandomLootObject; import net.minecraft.core.item.Item; import net.minecraft.core.item.ItemStack; +import turing.tmb.TypedIngredient; import turing.tmb.api.ingredient.IIngredientType; import turing.tmb.api.ingredient.IIngredientTypeWithSubtypes; @@ -32,6 +33,28 @@ public Item getBase(ItemStack ingredient) { public ItemStack getDefaultIngredient(Item base) { return base.getDefaultStack(); } + + @Override + public String getName(ItemStack ingredient) { + return ingredient.getDisplayName(); + } + + @Override + public void add(ItemStack ingredient, int amount) { + ingredient.stackSize += amount; + } + + @Override + public int getAmount(ItemStack ingredient) { + return ingredient.stackSize; + } + + @Override + public boolean matches(ItemStack ingredient, Object otherIngredient) { + if(otherIngredient instanceof TypedIngredient) throw new IllegalArgumentException("Received TypedIngredient instead of actual ingredient class, use .getIngredient() when calling this method."); + if(!(otherIngredient instanceof ItemStack)) return false; + return ingredient.isItemEqual((ItemStack) otherIngredient); + } }; public static final IIngredientType LOOT_OBJECT = new IIngredientType() { diff --git a/src/main/java/turing/tmb/api/ingredient/IIngredientTypeWithSubtypes.java b/src/main/java/turing/tmb/api/ingredient/IIngredientTypeWithSubtypes.java index 7bca121..92f2383 100644 --- a/src/main/java/turing/tmb/api/ingredient/IIngredientTypeWithSubtypes.java +++ b/src/main/java/turing/tmb/api/ingredient/IIngredientTypeWithSubtypes.java @@ -11,4 +11,12 @@ public interface IIngredientTypeWithSubtypes extends IIngredientType { default T getDefaultIngredient(B base) { throw new UnsupportedOperationException(); } + + String getName(T ingredient); + + void add(T ingredient, int amount); + + int getAmount(T ingredient); + + boolean matches(T ingredient, Object otherIngredient); } diff --git a/src/main/java/turing/tmb/api/ingredient/ITypedIngredient.java b/src/main/java/turing/tmb/api/ingredient/ITypedIngredient.java index b6c1c6f..1cf16e1 100644 --- a/src/main/java/turing/tmb/api/ingredient/ITypedIngredient.java +++ b/src/main/java/turing/tmb/api/ingredient/ITypedIngredient.java @@ -17,6 +17,14 @@ public interface ITypedIngredient { String getUid(); + String getName(); + + void addAmount(int amount); + + int getAmount(); + + boolean matches(Object ingredient); + default Optional getIngredient(IIngredientType ingredientType) { return ingredientType.castIngredient(getIngredient()); } diff --git a/src/main/java/turing/tmb/api/recipe/IRecipeCategory.java b/src/main/java/turing/tmb/api/recipe/IRecipeCategory.java index 985507f..07fabee 100644 --- a/src/main/java/turing/tmb/api/recipe/IRecipeCategory.java +++ b/src/main/java/turing/tmb/api/recipe/IRecipeCategory.java @@ -23,6 +23,8 @@ default IDrawable getIcon() { void drawRecipe(ITMBRuntime runtime, R recipe, IRecipeLayout layout, List ingredients, ILookupContext context); + void getIngredients(R recipe, IRecipeLayout layout, ILookupContext context, List ingredients); + IRecipeLayout getRecipeLayout(); default > List getTooltips(R recipe, IRecipeSlot slot, int mouseX, int mouseY) { diff --git a/src/main/java/turing/tmb/api/recipe/IRecipeIndex.java b/src/main/java/turing/tmb/api/recipe/IRecipeIndex.java index a9d49f6..4bfde65 100644 --- a/src/main/java/turing/tmb/api/recipe/IRecipeIndex.java +++ b/src/main/java/turing/tmb/api/recipe/IRecipeIndex.java @@ -5,11 +5,14 @@ import java.util.Collection; import java.util.List; +import java.util.Map; import java.util.function.Function; public interface IRecipeIndex { List> getCategoriesForCatalyst(ITypedIngredient ingredient); + Map, List>> getRecipeLists(); + void registerCatalyst(IRecipeCategory category, ITypedIngredient catalyst); void hideCategory(String name); diff --git a/src/main/java/turing/tmb/api/recipe/IRecipeSlot.java b/src/main/java/turing/tmb/api/recipe/IRecipeSlot.java index ed2aa1b..1bae74d 100644 --- a/src/main/java/turing/tmb/api/recipe/IRecipeSlot.java +++ b/src/main/java/turing/tmb/api/recipe/IRecipeSlot.java @@ -28,6 +28,14 @@ default void draw(IGuiHelper helper) { GL11.glPopMatrix(); } + default void draw(IGuiHelper helper, float r, float g, float b, float a) { + GL11.glPushMatrix(); + GL11.glColor4f(r,g,b,a); + helper.getMinecraft().textureManager.loadTexture("/assets/minecraft/textures/gui/container/crafting.png").bind(); + helper.drawTexturedModalRect(0, 0, 7, 83, 18, 18); + GL11.glPopMatrix(); + } + @Override default int getHeight() { return 16; diff --git a/src/main/java/turing/tmb/api/runtime/ITMBRuntime.java b/src/main/java/turing/tmb/api/runtime/ITMBRuntime.java index c70eae9..f6d340f 100644 --- a/src/main/java/turing/tmb/api/runtime/ITMBRuntime.java +++ b/src/main/java/turing/tmb/api/runtime/ITMBRuntime.java @@ -1,5 +1,6 @@ package turing.tmb.api.runtime; +import turing.tmb.RecipeIngredient; import turing.tmb.api.drawable.gui.IGuiHelper; import turing.tmb.api.ingredient.IIngredientRegistry; import turing.tmb.api.ingredient.IIngredientType; @@ -7,8 +8,12 @@ import turing.tmb.api.ingredient.ITypedIngredient; import turing.tmb.api.recipe.ILookupContext; import turing.tmb.api.recipe.IRecipeIndex; +import turing.tmb.api.recipe.IRecipeTranslator; import turing.tmb.api.recipe.RecipeIngredientRole; +import java.util.List; +import java.util.Map; + public interface ITMBRuntime { IIngredientTypeRegistry getIngredientTypeRegistry(); @@ -20,9 +25,15 @@ public interface ITMBRuntime { IRecipeIndex getRecipeIndex(); + + void showRecipe(ILookupContext lookup); void showRecipe(ITypedIngredient ingredient, RecipeIngredientRole role); void showAllRecipes(); + + Map> getDefaultRecipes(); + + List> getFavourites(); } diff --git a/src/main/java/turing/tmb/client/RecipeTreePage.java b/src/main/java/turing/tmb/client/RecipeTreePage.java new file mode 100644 index 0000000..3c1a397 --- /dev/null +++ b/src/main/java/turing/tmb/client/RecipeTreePage.java @@ -0,0 +1,266 @@ +package turing.tmb.client; + +import net.minecraft.client.render.block.model.BlockModelDispatcher; +import net.minecraft.client.render.texture.stitcher.IconCoordinate; +import net.minecraft.client.render.texture.stitcher.TextureRegistry; +import net.minecraft.core.achievement.Achievement; +import net.minecraft.core.block.Block; +import net.minecraft.core.block.Blocks; +import net.minecraft.core.item.ItemStack; +import net.minecraft.core.util.helper.Side; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import turing.tmb.RecipeIngredient; +import turing.tmb.RecipeTreeIngredient; +import turing.tmb.TMB; +import turing.tmb.api.drawable.IIngredientList; +import turing.tmb.api.ingredient.ITypedIngredient; +import turing.tmb.api.recipe.IRecipeCategory; +import turing.tmb.api.recipe.IRecipeTranslator; +import turing.tmb.api.recipe.RecipeIngredientRole; + +import java.util.*; +import java.util.concurrent.atomic.AtomicReference; +import java.util.stream.Collectors; + +public class RecipeTreePage { + protected final Map entryMap = new HashMap<>(); + protected final List ingredientList = new ArrayList<>(); + + protected final String id; + protected final RecipeIngredient ingredient; + protected final RecipeTreeIngredient root; + + protected int currentX = 1; + protected int currentY = 1; + + protected final List allRequired = new ArrayList<>(); + protected final List usedRecipes = new ArrayList<>(); + + public @NotNull String getName() { + if (ingredient.ingredient.getItemStack().isPresent()) { + return ingredient.ingredient.getItemStack().get().getDisplayName(); + } + return ""; + } + + public @NotNull String getDescription() { + return ""; + } + + public RecipeTreePage(@NotNull RecipeIngredient rootIngredient){ + this.ingredient = rootIngredient; + this.root = new RecipeTreeIngredient(rootIngredient, currentX, currentY); + addIngredient(rootIngredient, currentX, currentY); + + this.id = rootIngredient.recipe.toString(); + + currentX += 1; + addRecipe(rootIngredient, true); + usedRecipes.clear(); + addRequirements(rootIngredient, true); + + int j = currentY + 2; + int k = 0; + for (RecipeIngredient recipeIngredient : allRequired) { + addIngredient(recipeIngredient, k+1, j); + k += 2; + if (k % 10 == 0) { + j++; + k = 0; + } + } + + } + + public void addRequirements(RecipeIngredient result, boolean root){ + List ingredients = new ArrayList<>(); + AtomicReference>> currentCategory = new AtomicReference<>(); + AtomicReference> currentRecipe = new AtomicReference<>(); + if(root){ + result.category.getIngredients(result.recipe, result.category.getRecipeLayout(), null, ingredients); + currentCategory.set(result.category); + currentRecipe.set(result.recipe); + } else { + Optional>> optional = TMB.getRuntime().getDefaultRecipes().entrySet().stream().filter(E -> E.getKey().ingredient.matches(result.ingredient.getIngredient())).findFirst(); + optional.ifPresent(entry -> { + entry.getKey().category.getIngredients(entry.getValue(), entry.getKey().category.getRecipeLayout(), null, ingredients); + currentCategory.set(entry.getKey().category); + currentRecipe.set(entry.getValue()); + }); + if (!optional.isPresent()) { + return; + } + } + if(usedRecipes.contains(currentRecipe.get().getOriginal().toString())){ + return; + } + usedRecipes.add(currentRecipe.get().getOriginal().toString()); + + for (int i = 0; i < ingredients.size(); i++) { + IIngredientList list = ingredients.get(i); + if (list.getSize() <= 0) continue; + ITypedIngredient typedIngredient = list.getIngredients().get(0); + RecipeIngredient ingredient = new RecipeIngredient(typedIngredient, null, null, RecipeIngredientRole.INPUT); + if (currentCategory.get().getRecipeLayout().getSlots().get(i).getRole() != RecipeIngredientRole.INPUT) continue; + if(allRequired + .stream() + .anyMatch(it -> + it.ingredient.matches(typedIngredient.getIngredient()) + ) + ){ + RecipeIngredient alreadyAdded = allRequired.stream().filter(it -> it.ingredient.matches(typedIngredient.getIngredient())).collect(Collectors.toList()).get(0); + alreadyAdded.ingredient.addAmount(typedIngredient.getAmount()); + addRequirements(ingredient,false); + continue; + } + addRequirements(ingredient,false); + allRequired.add(ingredient); + } + usedRecipes.remove(currentRecipe.get().getOriginal().toString()); + + } + + public void addRecipe(RecipeIngredient result, boolean root){ + int x = currentX; + int y = currentY; + List ingredients = new ArrayList<>(); + + AtomicReference>> currentCategory = new AtomicReference<>(); + AtomicReference> currentRecipe = new AtomicReference<>(); + + List alreadyUsed = new ArrayList<>(); + + if(root){ + result.category.getIngredients(result.recipe, result.category.getRecipeLayout(), null, ingredients); + currentCategory.set(result.category); + currentRecipe.set(result.recipe); + currentX += 1; + } else { + Optional>> optional = TMB.getRuntime().getDefaultRecipes().entrySet().stream().filter(E -> E.getKey().ingredient.matches(result.ingredient.getIngredient())).findFirst(); + optional.ifPresent(entry -> { + entry.getKey().category.getIngredients(entry.getValue(), entry.getKey().category.getRecipeLayout(), null, ingredients); + currentCategory.set(entry.getKey().category); + currentRecipe.set(entry.getValue()); + currentX += 1; + }); + if (!optional.isPresent()) { + return; + } + } + + TMB.LOGGER.info(result.recipe.getOriginal().toString()); + TMB.LOGGER.info(currentRecipe.get().getOriginal().toString()); + + if(usedRecipes.contains(currentRecipe.get().getOriginal().toString())){ + return; + } + usedRecipes.add(currentRecipe.get().getOriginal().toString()); + + int uniqueIngredients = 0; + for (int i = 0; i < ingredients.size(); i++) { + IIngredientList list = ingredients.get(i); + if (list.getSize() <= 0) continue; + ITypedIngredient typedIngredient = list.getIngredients().get(0); + if (currentCategory.get().getRecipeLayout().getSlots().get(i).getRole() != RecipeIngredientRole.INPUT) continue; + if(alreadyUsed + .stream() + .anyMatch(it -> + it.ingredient + .matches(typedIngredient.getIngredient()) + ) + ){ + RecipeIngredient alreadyAdded = alreadyUsed.stream().filter(it -> it.ingredient.matches(typedIngredient.getIngredient())).collect(Collectors.toList()).get(0); + alreadyAdded.ingredient.addAmount(typedIngredient.getAmount()); + continue; + } + RecipeIngredient ingredient = new RecipeIngredient(typedIngredient, currentRecipe.get(), currentCategory.get(), RecipeIngredientRole.INPUT); + if(currentCategory.get().getRecipeLayout().getSlots().stream().filter(it->it.getRole() == RecipeIngredientRole.INPUT).count() > 0){ + currentY++; + } + addIngredient(ingredient, x, currentY); + addRecipe(ingredient, false); + alreadyUsed.add(ingredient); + currentX = x+1; + uniqueIngredients++; + } + usedRecipes.remove(currentRecipe.get().getOriginal().toString()); + } + + public void addIngredient(@NotNull RecipeIngredient ingredient, int x, int y) { + RecipeTreeIngredient entry = new RecipeTreeIngredient(ingredient, x,y); + ingredientList.add(entry); + entryMap.put(ingredient, entry); + } + + public @Nullable IconCoordinate getBackgroundTile(ScreenRecipeTree screen, int layer, Random random, int tileX, int tileY) { + return getTextureFromBlock(Blocks.DIRT); + } + + public void postProcessBackground(ScreenRecipeTree screen, Random random, ScreenRecipeTree.BGLayer layerCache, int orgX, int orgY) { + + } + + public ItemStack getIcon() { + return ingredient.ingredient.getItemStack().orElse(null); + } + + public @NotNull Set getIngredients() { + return entryMap.keySet(); + } + + public @NotNull List getTreeIngredients() { + return ingredientList; + } + + public @Nullable RecipeTreeIngredient getTreeIngredient(RecipeIngredient ingredient) { + return entryMap.get(ingredient); + } + + public double getCompletionFraction() { + return 1; + } + + public int backgroundLayers() { + return 1; + } + + public int backgroundColor() { + return 0; + } + + public boolean hasIngredient(RecipeIngredient ingredient) { + return entryMap.containsKey(ingredient); + } + + public RecipeTreeIngredient getTreeRoot() { + return root; + } + + public IconCoordinate drawIngredientBackground(RecipeIngredient ingredient) { + if(ingredient == root.ingredient){ + return TextureRegistry.getTexture(Achievement.TYPE_SPECIAL.texture); + } + return TextureRegistry.getTexture(Achievement.TYPE_NORMAL.texture); + } + + public int lineColorLocked(boolean isHovered) { + return 0x808080; + } + + public int lineColorUnlocked(boolean isHovered) { + return 0x00ff00; + } + + public int lineColorCanUnlock(boolean isHovered) { + return 0x707070; + } + + public static IconCoordinate getTextureFromBlock(Block block) { + return BlockModelDispatcher.getInstance().getDispatch(block).getBlockTextureFromSideAndMetadata(Side.TOP, 0); + } + + public String getId() { + return id; + } +} diff --git a/src/main/java/turing/tmb/client/ScreenRecipeTree.java b/src/main/java/turing/tmb/client/ScreenRecipeTree.java new file mode 100644 index 0000000..8fd7ed7 --- /dev/null +++ b/src/main/java/turing/tmb/client/ScreenRecipeTree.java @@ -0,0 +1,735 @@ +package turing.tmb.client; + +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.ButtonElement; +import net.minecraft.client.gui.ItemElement; +import net.minecraft.client.gui.Screen; +import net.minecraft.client.gui.TooltipElement; +import net.minecraft.client.gui.options.OptionsButtonElement; +import net.minecraft.client.render.Lighting; +import net.minecraft.client.render.Scissor; +import net.minecraft.client.render.item.model.ItemModelDispatcher; +import net.minecraft.client.render.tessellator.Tessellator; +import net.minecraft.client.render.texture.stitcher.IconCoordinate; +import net.minecraft.client.render.texture.stitcher.TextureRegistry; +import net.minecraft.core.item.ItemStack; +import net.minecraft.core.lang.I18n; +import net.minecraft.core.net.command.TextFormatting; +import net.minecraft.core.util.collection.Pair; +import net.minecraft.core.util.helper.Color; +import net.minecraft.core.util.helper.MathHelper; +import org.jetbrains.annotations.Nullable; +import org.lwjgl.input.Keyboard; +import org.lwjgl.input.Mouse; +import org.lwjgl.opengl.GL11; +import org.lwjgl.opengl.GL12; +import turing.tmb.RecipeIngredient; +import turing.tmb.RecipeTreeIngredient; +import turing.tmb.TMB; +import turing.tmb.api.drawable.IIngredientList; +import turing.tmb.api.ingredient.IIngredientRenderer; +import turing.tmb.api.ingredient.IIngredientType; +import turing.tmb.api.ingredient.ITypedIngredient; +import turing.tmb.api.recipe.RecipeIngredientRole; + +import java.util.ArrayList; +import java.util.List; +import java.util.Objects; +import java.util.Random; +import java.util.stream.Collectors; + +import static org.lwjgl.opengl.GL11.*; + +public class ScreenRecipeTree extends Screen +{ + private static final int TOP_SPACING = 24; + private static final int BUTTON_SPACING = 4; + private static final int SEPARATOR_WIDTH = 8; + private static final int PADDING = 8; + private static final int PAGE_BUTTON_HEIGHT = 20; + + private static final int ACHIEVEMENT_CELL_WIDTH = 24; + private static final int ACHIEVEMENT_CELL_HEIGHT = 24; + + private static final int ACHIEVEMENT_ICON_WIDTH = 26; + private static final int ACHIEVEMENT_ICON_HEIGHT = 26; + + private static final int TOOLTIP_BOX_WIDTH_MIN = 120; + private static final int TOOLTIP_OFF_X = 8; + private static final int TOOLTIP_OFF_Y = -4; + + protected int mouseXOld; + protected int mouseYOld; + protected double oldShiftX; + protected double oldShiftY; + protected double targetShiftX; + protected double targetShiftY; + protected double currentShiftX; + protected double currentShiftY; + private boolean draggingViewport; + private final TooltipElement tooltip; + private ItemElement renderItem = null; + Screen parent; + + private int top; + private int bottom; + + private int viewportLeft; + private int viewportTop; + private int viewportRight; + private int viewportBottom; + private int viewportWidth; + private int viewportHeight; + + private double viewportZoom = 1; + + private double shiftMinX; + private double shiftMinY; + private double shiftMaxX; + private double shiftMaxY; + + private int pageListLeft; + private int pageListRight; + + private float pageListScrollAmount = 0.0f; + private Float oldPagesListScrollAmount; + private int pagesListScrollRegionHeight; + + private Integer clickX, clickY; + + private final RecipeIngredient mainRecipeResult; + private RecipeTreePage hoveredPage = null; + private RecipeTreeIngredient hoveredTreeIngredient = null; + + private RecipeTreePage currentPage; + + private BGLayer[] layers; + + public ScreenRecipeTree(Screen parent, RecipeTreePage page, RecipeIngredient ingredient) + { + mouseXOld = 0; + mouseYOld = 0; + draggingViewport = false; + currentPage = page; + mainRecipeResult = ingredient; + + this.parent = parent; + this.mc = Minecraft.getMinecraft(); + this.tooltip = new TooltipElement(mc); + this.renderItem = new ItemElement(mc); + + layers = new BGLayer[currentPage.backgroundLayers()]; + for (int i = 0; i < layers.length; i++) { + layers[i] = new BGLayer(i); + } + } + + @Override + public void init() + { + buttons.clear(); + buttons.add(new OptionsButtonElement(1, width / 2 - 100, height - 20 - BUTTON_SPACING, 200, 20, I18n.getInstance().translateKey("gui.achievements.button.done"))); + + lastTileX = Integer.MIN_VALUE; + lastTileY = Integer.MIN_VALUE; + + top = TOP_SPACING; + bottom = height - (BUTTON_SPACING + 20 + BUTTON_SPACING); + + pagesListScrollRegionHeight = bottom - top; + pageListLeft = 0; + pageListRight = width/4; + + viewportZoom = 1; + + viewportLeft = drawSidebar() ? pageListRight + SEPARATOR_WIDTH : 0; + viewportTop = top; + viewportBottom = bottom; + viewportRight = width; + + viewportWidth = viewportRight - viewportLeft; + viewportHeight = viewportBottom - viewportTop; + + int achMinX = Integer.MAX_VALUE; + int achMinY = Integer.MAX_VALUE; + int achMaxX = Integer.MIN_VALUE; + int achMaxY = Integer.MIN_VALUE; + + for (RecipeTreeIngredient q : currentPage.getTreeIngredients()){ + if (q.getX() < achMinX){ + achMinX = q.getX(); + } + if (q.getY() < achMinY){ + achMinY = q.getY(); + } + if (q.getX() > achMaxX){ + achMaxX = q.getX(); + } + if (q.getY() > achMaxY){ + achMaxY = q.getY(); + } + } + + shiftMinX = achMinX * ACHIEVEMENT_CELL_WIDTH ; + shiftMinY = achMinY * ACHIEVEMENT_CELL_HEIGHT; + shiftMaxX = achMaxX * ACHIEVEMENT_CELL_WIDTH + ACHIEVEMENT_CELL_WIDTH; + shiftMaxY = achMaxY * ACHIEVEMENT_CELL_HEIGHT + ACHIEVEMENT_CELL_HEIGHT; + + shiftMinX -= (int) (viewportWidth /4d); + shiftMinY -= (int) (viewportHeight/4d); + shiftMaxX += (int) (viewportWidth /4d); + shiftMaxY += (int) (viewportHeight/4d); + + // Centers the screen on the Open ContainerInventory achievement + RecipeTreeIngredient root = currentPage.getTreeRoot(); + oldShiftX = targetShiftX = currentShiftX = root.getX() * ACHIEVEMENT_CELL_WIDTH + ACHIEVEMENT_CELL_WIDTH/2d; + oldShiftY = targetShiftY = currentShiftY = root.getY() * ACHIEVEMENT_CELL_HEIGHT + ACHIEVEMENT_CELL_HEIGHT/2d; + } + + @Override + protected void buttonClicked(ButtonElement button) { + if(button.id == 1) { + mc.displayScreen(parent); + } + super.buttonClicked(button); + } + + @Override + public void keyPressed(char eventCharacter, int eventKey, int mx, int my) + { + if (eventKey == Keyboard.KEY_ESCAPE) { + mc.displayScreen(parent); + } else { + super.keyPressed(eventCharacter, eventKey, mx, my); + } + + if (eventKey == mc.gameSettings.keyShowRecipe.getKeyCode() || eventKey == mc.gameSettings.keyShowUsage.getKeyCode()) { + if(hoveredTreeIngredient != null){ + if (eventKey == mc.gameSettings.keyShowUsage.getKeyCode()) { + TMB.getRuntime().showRecipe(hoveredTreeIngredient.ingredient.ingredient, RecipeIngredientRole.INPUT); + } else { + TMB.getRuntime().showRecipe(hoveredTreeIngredient.ingredient.ingredient, RecipeIngredientRole.OUTPUT); + } + } + } + } + @Override + public void mouseClicked(int mx, int my, int buttonNum) { + if (drawSidebar() && mx >= pageListLeft && mx <= (pageListRight - 6) && my >= top && my <= bottom) { + int pagesListHeight = getTotalPagesListHeight(); + int pagesListY = top - (int) pageListScrollAmount; + if (pagesListHeight < bottom - top) { + pagesListY = top + (bottom - top - pagesListHeight) / 2; + } + } + + + super.mouseClicked(mx, my, buttonNum); + + clickX = mx; + clickY = my; + } + + @Override + public void render(int mx, int my, float partialTick) + { + if(Mouse.isButtonDown(0)) { + if(mx >= viewportLeft && mx < viewportRight && my >= viewportTop && my < viewportBottom) { + if(!draggingViewport) { + draggingViewport = true; + } else { + targetShiftX -= (mx - mouseXOld) / viewportZoom; + targetShiftY -= (my - mouseYOld) / viewportZoom; + currentShiftX = oldShiftX = targetShiftX; + currentShiftY = oldShiftY = targetShiftY; + } + mouseXOld = mx; + mouseYOld = my; + } + currentShiftX = MathHelper.clamp(currentShiftX, shiftMinX, shiftMaxX); + currentShiftY = MathHelper.clamp(currentShiftY, shiftMinY, shiftMaxY); + } else if (mc.controllerInput != null) { + targetShiftX += mc.controllerInput.joyRight.getX() / viewportZoom * 4; + targetShiftY += mc.controllerInput.joyRight.getY() / viewportZoom * 4; + currentShiftX = oldShiftX = targetShiftX; + currentShiftY = oldShiftY = targetShiftY; + currentShiftX = MathHelper.clamp(currentShiftX, shiftMinX, shiftMaxX); + currentShiftY = MathHelper.clamp(currentShiftY, shiftMinY, shiftMaxY); + + if (mc.controllerInput.buttonLeftTrigger.isPressed()) { + viewportZoom -= 0.01d; + } else if (mc.controllerInput.buttonRightTrigger.isPressed()) { + viewportZoom += 0.01f; + } + viewportZoom = MathHelper.clamp(viewportZoom, 0.5d, 2d); + + } else { + clickX = clickY = null; + oldPagesListScrollAmount = null; + draggingViewport = false; + } + + if (mx >= viewportLeft && mx <= viewportRight && my >= viewportTop && my <= viewportBottom){ + final double change = (Mouse.getDWheel()/10d); + viewportZoom = MathHelper.clamp(viewportZoom + change, 0.5d, 2); + + // Make zoom notch onto integer multiples + if (change != 0) { + final double[] notches = new double[]{0.25, 0.5, 1, 2, 4}; + for (double notch : notches){ + if (Math.abs(viewportZoom - notch) < 0.05){ + viewportZoom = notch; + break; + } + } + } + } + Mouse.getDWheel(); + + renderBackground(); + + renderAchievementsPanel(mx, my, partialTick); + + overlayBackground(0, width, 0, top, 0x404040); + overlayBackground(0, width, bottom, height, 0x404040); + + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glDisable(GL11.GL_ALPHA_TEST); + GL11.glShadeModel(GL11.GL_SMOOTH); + GL11.glEnable(GL11.GL_TEXTURE_2D); + + super.render(mx, my, partialTick); // Draw Buttons + GL11.glEnable(GL11.GL_DEPTH_TEST); + GL11.glEnable(GL11.GL_LIGHTING); + Lighting.disable(); + + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glDisable(GL11.GL_DEPTH_TEST); + GL11.glEnable(GL11.GL_TEXTURE_2D); + GL11.glShadeModel(GL11.GL_FLAT); + GL11.glEnable(GL11.GL_ALPHA_TEST); + + { + GL11.glDisable(GL11.GL_TEXTURE_2D); + GL11.glEnable(GL11.GL_BLEND); + GL11.glBlendFunc(GL11.GL_SRC_ALPHA, GL11.GL_ONE_MINUS_SRC_ALPHA); + GL11.glDisable(GL11.GL_ALPHA_TEST); + GL11.glShadeModel(GL11.GL_SMOOTH); + + byte fadeDist = 4; + Tessellator tessellator = Tessellator.instance; + tessellator.startDrawingQuads(); + tessellator.setColorRGBA_I(0, 0); + tessellator.addVertexWithUV(0, top + fadeDist, 0.0D, 0.0D, 1.0D); + tessellator.addVertexWithUV(width, top + fadeDist, 0.0D, 1.0D, 1.0D); + tessellator.setColorRGBA_I(0, 255); + tessellator.addVertexWithUV(width, top, 0.0D, 1.0D, 0.0D); + tessellator.addVertexWithUV(0, top, 0.0D, 0.0D, 0.0D); + tessellator.draw(); + + tessellator.startDrawingQuads(); + tessellator.setColorRGBA_I(0, 255); + tessellator.addVertexWithUV(0, bottom, 0.0D, 0.0D, 1.0D); + tessellator.addVertexWithUV(width, bottom, 0.0D, 1.0D, 1.0D); + tessellator.setColorRGBA_I(0, 0); + tessellator.addVertexWithUV(width, bottom - fadeDist, 0.0D, 1.0D, 0.0D); + tessellator.addVertexWithUV(0, bottom - fadeDist, 0.0D, 0.0D, 0.0D); + tessellator.draw(); + GL11.glEnable(GL_TEXTURE_2D); + GL11.glEnable(GL11.GL_ALPHA_TEST); + } + + if (hoveredTreeIngredient != null){ + drawAchievementToolTip(hoveredTreeIngredient, mx, my); + } + + renderLabels(); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_DEPTH_TEST); + + hoveredPage = null; + } + + @Override + public void tick() { + oldShiftX = targetShiftX; + oldShiftY = targetShiftY; + double xDiff = currentShiftX - targetShiftX; + double yDiff = currentShiftY - targetShiftY; + if(xDiff * xDiff + yDiff * yDiff < 4D) { + targetShiftX += xDiff; + targetShiftY += yDiff; + } else { + targetShiftX += xDiff * 0.85D; + targetShiftY += yDiff * 0.85D; + } + } + + protected void renderLabels() { + font.drawCenteredString(I18n.getInstance().translateKey("gui.tmb.recipeTree.label.title")/* + " " + viewportZoom + " X:" + currentShiftX + ", Y:" + currentShiftY*/, width/2, 5 , 0xFFFFFF); + } + + + protected void renderAchievementsPanel(int mouseX, int mouseY, float partialTick){ + double shiftX = MathHelper.lerp(oldShiftX, targetShiftX, partialTick); + double shiftY = MathHelper.lerp(oldShiftY, targetShiftY, partialTick); + shiftX = MathHelper.clamp(shiftX, shiftMinX, shiftMaxX); + shiftY = MathHelper.clamp(shiftY, shiftMinY, shiftMaxY); + + zLevel = 0.0F; + + GL11.glDepthFunc(GL11.GL_GEQUAL); + GL11.glPushMatrix(); + GL11.glTranslatef(0, 0, -200F); + Scissor.enable(viewportLeft, viewportTop, viewportWidth, viewportHeight); + GL11.glEnable(GL11.GL_TEXTURE_2D); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + GL11.glEnable(GL11.GL_COLOR_MATERIAL); + drawRectDouble(viewportLeft, viewportTop, viewportRight, viewportBottom, 0xFF000000 | currentPage.backgroundColor()); // Ensures that the viewport always has a background of some kind + + GL11.glPushMatrix(); + drawBackgroundTiles(shiftX, shiftY); + + GL11.glEnable(GL11.GL_DEPTH_TEST); + GL11.glDepthFunc(GL11.GL_LEQUAL); // Responsible for culling the overdraw later on + drawConnectingLines(mouseX, mouseY,shiftX, shiftY); + + Lighting.enableInventoryLight(); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glEnable(GL12.GL_RESCALE_NORMAL); + GL11.glEnable(GL11.GL_COLOR_MATERIAL); + hoveredTreeIngredient = drawAchievementIcons(mouseX, mouseY, shiftX, shiftY); + + GL11.glPopMatrix(); + + GL11.glDisable(GL11.GL_DEPTH_TEST); + GL11.glEnable(GL11.GL_BLEND); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + Scissor.disable(); + + // drawRectDouble(viewportLeft + viewportWidth/2d - 2.5, viewportTop + viewportHeight/2d - 2.5, viewportLeft + viewportWidth/2d + 2.5, viewportTop + viewportHeight/2d + 2.5, 0xFFA0A0A0); // Debug cross-hair + + GL11.glPopMatrix(); + + zLevel = 0.0F; + GL11.glDepthFunc(GL11.GL_LEQUAL); + GL11.glDisable(GL11.GL_DEPTH_TEST); + GL11.glEnable(GL11.GL_TEXTURE_2D); + } + + private static final int TILE_WIDTH = 16; + private static final int TILE_HEIGHT = 16; + + public int lastTileX = Integer.MIN_VALUE; + public int lastTileY = Integer.MIN_VALUE; + public int lastTilesWide = Integer.MIN_VALUE; + public int lastTilesTall = Integer.MIN_VALUE; + private void drawBackgroundTiles(double shiftX, double shiftY){ + double zoom = viewportZoom/* * 0.85*/; + + TextureRegistry.blockAtlas.bind(); + final int offset = 18 * TILE_WIDTH; + int viewTileX = (MathHelper.floor(shiftX) + offset) / TILE_WIDTH; + int viewTileY = (MathHelper.floor(shiftY) + offset) / TILE_HEIGHT; + double remainderX = (shiftX + offset) % TILE_WIDTH; + double remainderY = (shiftY + offset) % TILE_HEIGHT; + Random random = new Random(); + + int tilesWide = (int) (viewportWidth/(TILE_WIDTH * zoom) + 2); + int tilesTall = (int) (viewportHeight/(TILE_HEIGHT * zoom) + 2); + + int orgX = -tilesWide/2 - 1; + int orgY = -tilesTall/2 - 1; + int endX = tilesWide/2 + 1; + int endY = tilesTall/2 + 1; + + tilesWide = endX - orgX; + tilesTall = endY - orgY; + + // Cache background, saves some render time which is nice + if (viewTileX != lastTileX || viewTileY != lastTileY || tilesWide != lastTilesWide || tilesTall != lastTilesTall) { + lastTileX = viewTileX; + lastTileY = viewTileY; + lastTilesWide = tilesWide; + lastTilesTall = tilesTall; + + for (BGLayer layer : layers){ + layer.resize(tilesWide, tilesTall); + } + + long worldSeed = mc.currentWorld == null ? 0 : mc.currentWorld.getRandomSeed(); + for (int _y = 0; _y < tilesTall; _y++){ + for (int _x = 0; _x < tilesWide; _x++) { + int tileX = orgX + _x + viewTileX; + int tileY = orgY + _y + viewTileY; + // Hopefully this is actually random enough :) + random.setSeed(worldSeed); + long l1 = random.nextLong(); + random.setSeed(tileX); + long l2 = random.nextLong(); + random.setSeed(tileY); + long l3 = random.nextLong(); + + long seed = Objects.hash(l1, l2, ~l3); + + for (BGLayer layer : layers) { + random.setSeed(seed); + IconCoordinate fore = currentPage.getBackgroundTile(this, layer.id, random, tileX, tileY); + layer.put(fore, _x, _y); + } + } + } + + for (BGLayer layer : layers) { + random.setSeed(worldSeed); + currentPage.postProcessBackground(this, random, layer, orgX + viewTileX, orgY + viewTileY); + } + } + + + + for(int _y = 0; _y < tilesTall; _y++) { + int tileY = orgY + _y + viewTileY; + float brightness = 0.3F; //- ((float)(tileY) / 25F) * 0.3F; + for(int _x = 0; _x < tilesWide; _x++) { + int tileX = orgX + _x + viewTileX; + + for (int i = layers.length - 1; i >= 0; i--) { + BGLayer topLayer = getLayer(i); + IconCoordinate fore = topLayer.get(_x, _y); + + IconCoordinate next = null; + if (i - 1 >= 0) { + BGLayer nextLayer = getLayer(i - 1); + next = nextLayer.get(_x, _y); + } + + + boolean bottom = false; + boolean top = false; + boolean left = false; + boolean right = false; + boolean topLeft = false; + boolean topRight = false; + boolean bottomLeft = false; + boolean bottomRight = false; + + + if (fore != null && next == null && i - 1 >= 0) { + BGLayer nextLayer = getLayer(i - 1); + top = nextLayer.get(_x, _y - 1) != null; + left = nextLayer.get(_x - 1, _y) != null; + right = nextLayer.get(_x + 1, _y) != null; + bottom = nextLayer.get(_x, _y + 1) != null; + topLeft = nextLayer.get(_x - 1, _y - 1) != null; + topRight = nextLayer.get(_x + 1, _y - 1) != null; + bottomLeft = nextLayer.get(_x - 1, _y + 1) != null; + bottomRight = nextLayer.get(_x + 1, _y + 1) != null; + } + + double iconLeft = (viewportLeft + viewportWidth / 2d) + zoom * (((orgX + _x) * TILE_WIDTH) - remainderX); + double iconTop = (viewportTop + viewportHeight / 2d) + zoom * (((orgY + _y) * TILE_HEIGHT) - remainderY); + double iconWidth = TILE_WIDTH * zoom; + double iconHeight = TILE_HEIGHT * zoom; + + if (fore != null) { + float shadowScale = (float) Math.pow(0.65f, i); + if (next != null) { + shadowScale *= 0.5f; + } + GL11.glColor4f(brightness, brightness, brightness, 1.0F); + final double epsilon = 0.05; + drawGuiIconDouble(iconLeft - epsilon, iconTop - epsilon, iconWidth + epsilon * 2, iconHeight + epsilon * 2, fore); + } + } + } + } + } + + public BGLayer getLayer(int layer){ + if (layer < 0 || layer >= layers.length) return null; + return layers[layer]; + } + + private double timeSin(double amplitude, long period){ + return Math.sin(((double)(System.currentTimeMillis() % period) / period) * Math.PI * 2D) * amplitude; + } + private void drawConnectingLines(int mouseX, int mouseY, double shiftX, double shiftY){ + double zoom = viewportZoom; + + for(RecipeTreeIngredient entry : currentPage.getTreeIngredients()) { + RecipeIngredient ingredient = entry.ingredient; + List list = new ArrayList<>(); + if(ingredient.recipe == null || ingredient.category == null) continue; + ingredient.category.getIngredients(ingredient.recipe,ingredient.category.getRecipeLayout(), null, list); + List inputs = currentPage.getTreeIngredients(); + for (RecipeTreeIngredient child : inputs) { + if(child == entry) continue; + if(child.ingredient.recipe == null || child.ingredient.category == null) continue; + double childX = (viewportLeft + viewportWidth/2d) + ((entry.getX() * ACHIEVEMENT_CELL_WIDTH - shiftX) + 11) * zoom; + double childY = (viewportTop + viewportHeight/2d) + ((entry.getY() * ACHIEVEMENT_CELL_HEIGHT - shiftY) + 11) * zoom; + double parentX = (viewportLeft + viewportWidth/2d) + ((child.getX() * ACHIEVEMENT_CELL_WIDTH - shiftX) + 11) * zoom; + double parentY = (viewportTop + viewportHeight/2d) + ((child.getY() * ACHIEVEMENT_CELL_HEIGHT - shiftY) + 11) * zoom; + boolean unlocked = false; + boolean canUnlock = false; + + final double zoomOff = 11 * zoom; + + boolean isHovered = false; + { + double x = parentX - zoomOff; + double y = parentY - zoomOff; + if ((mouseX >= 0 && mouseY >= viewportTop && mouseX < width && mouseY < viewportBottom) && // In viewport and + (mouseX >= x && mouseX <= x + 22 * zoom && mouseY >= y && mouseY <= y + 22 * zoom)) { // Hovering over achievement + isHovered = true; + } + + x = childX - zoomOff; + y = childY - zoomOff; + if ((mouseX >= 0 && mouseY >= viewportTop && mouseX < width && mouseY < viewportBottom) && // In viewport and + (mouseX >= x && mouseX <= x + 22 * zoom && mouseY >= y && mouseY <= y + 22 * zoom)) { // Hovering over achievement + isHovered = true; + } + } + + int color; + if(unlocked) { + color = 0xff << Color.SHIFT_ALPHA | (currentPage.lineColorUnlocked(isHovered) & 0xffffff); + } else if (canUnlock) { + int alpha = timeSin(1, 600) >= 0.6 ? 0x82 : 0xff; + color = (alpha << Color.SHIFT_ALPHA) | (currentPage.lineColorCanUnlock(isHovered) & 0xffffff); + } else { + color = 0xff << Color.SHIFT_ALPHA | (currentPage.lineColorLocked(isHovered) & 0xffffff); + } + + drawLineHorizontalDouble(childX, parentX, childY, color); + drawLineVerticalDouble(parentX, childY, parentY, color); + } + } + } + + private RecipeTreeIngredient drawAchievementIcons(int mouseX, int mouseY, double shiftX, double shiftY){ + double zoom = viewportZoom; + + RecipeTreeIngredient hoveredAchievment = null; + for(RecipeTreeIngredient recipeTreeIngredient : currentPage.getTreeIngredients()) { + RecipeIngredient ingredient = recipeTreeIngredient.ingredient; + double achViewX = (viewportLeft + viewportWidth/2d) + (recipeTreeIngredient.getX() * ACHIEVEMENT_CELL_WIDTH - shiftX ) * zoom; + double achViewY = (viewportTop + viewportHeight/2d) + (recipeTreeIngredient.getY() * ACHIEVEMENT_CELL_HEIGHT - shiftY) * zoom; + if(achViewX < viewportLeft - ACHIEVEMENT_CELL_WIDTH * zoom || achViewY < viewportTop - ACHIEVEMENT_CELL_HEIGHT * zoom || achViewX > viewportRight || achViewY > viewportBottom) { // Continue if outside viewport + continue; + } + float brightness = 0.50F; + GL11.glColor4f(brightness, brightness, brightness, 1.0F); + + drawGuiIconDouble(achViewX - (ACHIEVEMENT_ICON_WIDTH - ACHIEVEMENT_CELL_WIDTH) * zoom, achViewY - (ACHIEVEMENT_ICON_HEIGHT - ACHIEVEMENT_CELL_HEIGHT) * zoom, ACHIEVEMENT_ICON_WIDTH * zoom, ACHIEVEMENT_ICON_HEIGHT * zoom, currentPage.drawIngredientBackground(ingredient)); + + GL11.glPushMatrix(); + GL11.glEnable(GL11.GL_LIGHTING); + GL11.glEnable(GL11.GL_CULL_FACE); + ItemStack achievementItem = ingredient.ingredient.getItemStack().orElse(null); + + GL11.glTranslated(achViewX + 3 * zoom, achViewY + 3 * zoom, 0); + GL11.glScaled(zoom, zoom, 1); + IIngredientType type = ingredient.ingredient.getType(); + IIngredientRenderer renderer = (IIngredientRenderer) type.getRenderer(TMB.getRuntime()); + new DrawableIngredient<>(ingredient.ingredient.getIngredient(), renderer).draw(TMB.getRuntime().getGuiHelper()); + GL11.glDisable(GL11.GL_LIGHTING); + GL11.glPopMatrix(); + + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + + if((mouseX >= 0 && mouseY >= viewportTop && mouseX < width && mouseY < viewportBottom) && // In viewport and + (mouseX >= achViewX && mouseX <= achViewX + 22 * zoom && mouseY >= achViewY && mouseY <= achViewY + 22 * zoom)) { // Hovering over achievement + hoveredAchievment = recipeTreeIngredient; + } + } + return hoveredAchievment; + } + private void drawAchievementToolTip(RecipeTreeIngredient treeIngredient, int mouseX, int mouseY){ + StringBuilder s = new StringBuilder(treeIngredient.ingredient.ingredient.getName()); + tooltip.render(s.toString(),mouseX,mouseY,8,-8); + } + public boolean drawSidebar(){ + return false; + } + private void scrollPagesList(float amount) { + if(amount == 0.0f) return; + + pageListScrollAmount += amount; + onScrollPagesList(); + } + + private void onScrollPagesList() { + int totalPagesListHeight = getTotalPagesListHeight(); + if (pageListScrollAmount < 0 || pagesListScrollRegionHeight > totalPagesListHeight) pageListScrollAmount = 0; + else if (pageListScrollAmount > totalPagesListHeight - pagesListScrollRegionHeight) pageListScrollAmount = totalPagesListHeight - pagesListScrollRegionHeight; + } + private int getTotalPagesListHeight() { + return PAGE_BUTTON_HEIGHT * 0;//VintageQuesting.CHAPTERS.size(); + } + + private void overlayBackground(int minX, int maxX, int minY, int maxY, int color) + { + Tessellator tessellator = Tessellator.instance; + mc.textureManager.loadTexture("/assets/minecraft/textures/gui/background.png").bind(); + GL11.glColor4f(1.0F, 1.0F, 1.0F, 1.0F); + float scale = 32F; + tessellator.startDrawingQuads(); + tessellator.setColorOpaque_I(color); + tessellator.addVertexWithUV(minX, maxY, 0.0D, (float) minX / scale, (float) maxY / scale); + tessellator.addVertexWithUV(maxX, maxY, 0.0D, (float) maxX / scale, (float) maxY / scale); + tessellator.setColorOpaque_I(color); + tessellator.addVertexWithUV(maxX, minY, 0.0D, (float) maxX / scale, (float) minY / scale); + tessellator.addVertexWithUV(minX, minY, 0.0D, (float) minX / scale, (float) minY / scale); + tessellator.draw(); + } + + public static class BGLayer { + private IconCoordinate[] data; + private int width; + private int height; + public final int id; + public BGLayer(int id) { + this.id = id; + data = new IconCoordinate[0]; + width = 0; + height = 0; + } + public int getWidth() { + return width; + } + public int getHeight() { + return height; + } + public IconCoordinate[] getData() { + return data; + } + protected void resize(int width, int height) { + this.width = width; + this.height = height; + data = new IconCoordinate[width * height]; + } + public void put(IconCoordinate coordinate, int x, int y){ + if (x < 0) return; + if (y < 0) return; + if (x >= width) return; + if (y >= height) return; + data[makeIndex(x, y)] = coordinate; + } + + public IconCoordinate get(int x, int y){ + if (x < 0) return null; + if (y < 0) return null; + if (x >= width) return null; + if (y >= height) return null; + return data[makeIndex(x, y)]; + } + + private int makeIndex(int x, int y){ + return x % width + y * width; + } + } +} diff --git a/src/main/java/turing/tmb/client/ScreenTMBRecipe.java b/src/main/java/turing/tmb/client/ScreenTMBRecipe.java index ca98342..da2d955 100644 --- a/src/main/java/turing/tmb/client/ScreenTMBRecipe.java +++ b/src/main/java/turing/tmb/client/ScreenTMBRecipe.java @@ -6,7 +6,6 @@ import net.minecraft.client.gui.ButtonElement; import net.minecraft.client.gui.Screen; import net.minecraft.client.gui.TooltipElement; -import net.minecraft.core.item.ItemStack; import net.minecraft.core.lang.I18n; import net.minecraft.core.net.command.TextFormatting; import net.minecraft.core.sound.SoundCategory; @@ -15,21 +14,18 @@ import org.jetbrains.annotations.Nullable; import org.lwjgl.input.Keyboard; import org.lwjgl.input.Mouse; -import org.lwjgl.opengl.GL; import org.lwjgl.opengl.GL11; +import turing.tmb.RecipeIngredient; import turing.tmb.TMB; import turing.tmb.TooltipBuilder; -import turing.tmb.api.ItemStackIngredientRenderer; -import turing.tmb.api.VanillaTypes; -import turing.tmb.api.drawable.IDrawable; import turing.tmb.api.drawable.IIngredientList; import turing.tmb.api.drawable.builder.ITooltipBuilder; import turing.tmb.api.ingredient.IIngredientRenderer; import turing.tmb.api.ingredient.IIngredientType; import turing.tmb.api.ingredient.ITypedIngredient; import turing.tmb.api.recipe.*; +import turing.tmb.util.IKeybinds; import turing.tmb.util.IngredientList; -import turing.tmb.util.LookupContext; import turing.tmb.util.RenderUtil; import java.util.*; @@ -51,6 +47,7 @@ public class ScreenTMBRecipe extends Screen { private int tabsPerPage = 7; private final List> tabList = new ArrayList<>(); private final List, Pair>> drawnIngredients = new ArrayList<>(); + private final List recipeIngredients = new ArrayList<>(); private final ButtonElement rightButton = new ButtonElement(0, 0, 0, ">"); private final ButtonElement leftButton = new ButtonElement(1, 0, 0, "<"); private final ButtonElement tabLeftButton = new ButtonElement(2, 0, 0, "<"); @@ -229,6 +226,7 @@ public void render(int mx, int my, float partialTick) { TMB.getRuntime().getGuiHelper().getCycleTimer().onDraw(); drawnIngredients.clear(); + recipeIngredients.clear(); tooltip = ""; TooltipBuilder tooltipBuilder = new TooltipBuilder(); @@ -263,6 +261,7 @@ public void render(int mx, int my, float partialTick) { renderCatalysts(category, mx, my, tooltipBuilder, isCtrl, isShift); int X = x + 4; int Y = y + 14; + GL11.glPushMatrix(); GL11.glTranslatef(x + 4, y + 14, 0); IRecipeLayout layout = category.getRecipeLayout(); @@ -279,13 +278,33 @@ public void render(int mx, int my, float partialTick) { GL11.glTranslatef(slot.getX(), slot.getY(), 0); X += slot.getX(); Y += slot.getY(); - slot.draw(TMB.getRuntime().getGuiHelper()); + boolean defaultRecipe = false; + if(slot.getRole() == RecipeIngredientRole.OUTPUT){ + if (ingredients.size() > I) { + IIngredientList list = ingredients.get(I); + ITypedIngredient ingredient = TMB.getRuntime().getGuiHelper().getCycleTimer().getCycledItem(list.getIngredients()); + if(ingredient != null){ + if(TMB.getRuntime().getDefaultRecipes().entrySet().stream().anyMatch((E)->E.getKey().ingredient.matches(ingredient.getIngredient()))){ + Map.Entry> entry = TMB.getRuntime().getDefaultRecipes().entrySet().stream().filter((E) -> E.getKey().ingredient.matches(ingredient.getIngredient())).collect(Collectors.toList()).get(0); + if(entry.getValue() == recipe){ + defaultRecipe = true; + } + } + } + } + } + if(defaultRecipe){ + slot.draw(TMB.getRuntime().getGuiHelper(),1,1,0,1); + } else { + slot.draw(TMB.getRuntime().getGuiHelper()); + } if (ingredients.size() > I) { GL11.glTranslatef(1, 1, 0); X++; Y++; IIngredientList list = ingredients.get(I); ITypedIngredient ingredient = TMB.getRuntime().getGuiHelper().getCycleTimer().getCycledItem(list.getIngredients()); + if (lookupContext != null) { Optional> found = list.getIngredients().stream().filter((t) -> t.hashCode() == lookupContext.getIngredient().hashCode()).findFirst(); if (found.isPresent()) { @@ -297,6 +316,7 @@ public void render(int mx, int my, float partialTick) { IIngredientRenderer renderer = (IIngredientRenderer) type.getRenderer(TMB.getRuntime()); new DrawableIngredient<>(ingredient.getIngredient(), renderer).draw(TMB.getRuntime().getGuiHelper()); drawnIngredients.add(Pair.of(ingredient, Pair.of(X, Y))); + recipeIngredients.add(new RecipeIngredient(ingredient, recipe, category, slot.getRole())); if (mx >= X && mx < X + 18 && my >= Y && my < Y + 18) { int mouseX = mx - ((this.width - this.xSize) / 2) - 4; int mouseY = my - ((this.height - this.ySize) / 2) - 14 - ((category.getBackground().getHeight() + 4) * i); @@ -373,6 +393,7 @@ public void render(int mx, int my, float partialTick) { tooltipElement.render(tooltip, mx, my, 8, -8); GL11.glPopMatrix(); } + super.render(mx,my,partialTick); } @SuppressWarnings("unchecked") @@ -393,6 +414,7 @@ private void renderCatalysts(IRecipeCategory category, int mx, int my, IToolt new DrawableIngredient<>(ingredient.getIngredient(), ingredient.getType().getRenderer(TMB.getRuntime())).draw(TMB.getRuntime().getGuiHelper(), x + 3, y + 3); drawnIngredients.add(Pair.of(ingredient, Pair.of(x + 3, y + 3))); + recipeIngredients.add(new RecipeIngredient(ingredient,null, null, RecipeIngredientRole.CATALYST)); if (mx >= x && mx < x + 22 && my >= y && my < y + 22) { ingredient.getType().getRenderer(TMB.getRuntime()).getTooltip(tooltipBuilder, ingredient.getIngredient(), isCtrl, isShift); @@ -427,8 +449,66 @@ public void keyPressed(char eventCharacter, int eventKey, int mx, int my) { } mc.displayScreen(s); } + + if (eventKey == ((IKeybinds)mc.gameSettings).toomanyblocks$getKeyShowRecipeTree().getKeyCode()){ + for (int i = 0; i < drawnIngredients.size(); i++) { + Pair, Pair> drawn = drawnIngredients.get(i); + RecipeIngredient recipeIngredient = recipeIngredients.get(i); + if (mx >= drawn.getRight().getLeft() && my >= drawn.getRight().getRight() && mx < drawn.getRight().getLeft() + 16 && my < drawn.getRight().getRight() + 16) { + if (recipeIngredient.recipe != null) { + mc.displayScreen(new ScreenRecipeTree(this,new RecipeTreePage(recipeIngredient),recipeIngredient)); + } + break; + } + } + } + + if (eventKey == ((IKeybinds)mc.gameSettings).toomanyblocks$getKeyAddFavourite().getKeyCode()){ + for (int i = 0; i < drawnIngredients.size(); i++) { + Pair, Pair> drawn = drawnIngredients.get(i); + RecipeIngredient recipeIngredient = recipeIngredients.get(i); + if (mx >= drawn.getRight().getLeft() && my >= drawn.getRight().getRight() && mx < drawn.getRight().getLeft() + 16 && my < drawn.getRight().getRight() + 16) { + if (recipeIngredient.recipe != null) { + if (TMB.getRuntime().getFavourites().stream().anyMatch(it -> it.matches(recipeIngredient.ingredient.getIngredient()))) { + TMB.getRuntime().getFavourites().removeIf(ingredient -> recipeIngredient.ingredient.matches(ingredient.getIngredient())); + } else { + TMB.getRuntime().getFavourites().add(recipeIngredient.ingredient); + } + } + break; + } + } + } + + if (eventKey == ((IKeybinds)mc.gameSettings).toomanyblocks$getKeySetDefaultRecipe().getKeyCode()){ + loop: for (int i = 0; i < drawnIngredients.size(); i++) { + Pair, Pair> drawn = drawnIngredients.get(i); + RecipeIngredient recipeIngredient = recipeIngredients.get(i); + if (mx >= drawn.getRight().getLeft() && my >= drawn.getRight().getRight() && mx < drawn.getRight().getLeft() + 16 && my < drawn.getRight().getRight() + 16) { + if (recipeIngredient.recipe != null && recipeIngredient.role == RecipeIngredientRole.OUTPUT) { + Iterator>> iterator = TMB.getRuntime().getDefaultRecipes().entrySet().iterator(); + while (iterator.hasNext()) { + RecipeIngredient ingredient = iterator.next().getKey(); + if(ingredient.ingredient.matches(recipeIngredient.ingredient.getIngredient())){ + iterator.remove(); + if(Objects.equals(recipeIngredient.recipe.getOriginal().toString(), ingredient.recipe.getOriginal().toString())){ + mc.hudIngame.addChatMessage(String.format(I18n.getInstance().translateKey("message.tmb.removeDefaultRecipe"),recipeIngredient.ingredient.getName())); + break loop; + } + } + } + TMB.getRuntime().getDefaultRecipes().put(recipeIngredient, recipeIngredient.recipe); + mc.hudIngame.addChatMessage(String.format(I18n.getInstance().translateKey("message.tmb.setDefaultRecipe"), recipeIngredient.ingredient.getName(), recipeIngredient.recipe.getOriginal().toString())); + //mc.displayScreen(new ScreenRecipeTree(this,new RecipeTreePage(recipeIngredient),recipeIngredient)); + } + break; + } + } + } + if (eventKey == mc.gameSettings.keyShowRecipe.getKeyCode() || eventKey == mc.gameSettings.keyShowUsage.getKeyCode()) { - for (Pair, Pair> drawn : drawnIngredients) { + for (int i = 0; i < drawnIngredients.size(); i++) { + Pair, Pair> drawn = drawnIngredients.get(i); if (mx >= drawn.getRight().getLeft() && my >= drawn.getRight().getRight() && mx < drawn.getRight().getLeft() + 16 && my < drawn.getRight().getRight() + 16) { if (eventKey == mc.gameSettings.keyShowUsage.getKeyCode()) { TMB.getRuntime().showRecipe(drawn.getLeft(), RecipeIngredientRole.INPUT); diff --git a/src/main/java/turing/tmb/client/TMBRenderer.java b/src/main/java/turing/tmb/client/TMBRenderer.java index 33066a8..9b6f5f4 100644 --- a/src/main/java/turing/tmb/client/TMBRenderer.java +++ b/src/main/java/turing/tmb/client/TMBRenderer.java @@ -8,6 +8,7 @@ import net.minecraft.client.gui.TextFieldElement; import net.minecraft.client.gui.TooltipElement; import net.minecraft.client.gui.container.ScreenContainerAbstract; +import net.minecraft.core.item.ItemStack; import net.minecraft.core.sound.SoundCategory; import net.minecraft.core.util.helper.MathHelper; import org.jetbrains.annotations.Nullable; @@ -34,13 +35,19 @@ public class TMBRenderer { public static int currentPage = 0; public static int pages = 0; + public static int currentFavouritePage = 0; + public static int favouritePages = 0; public static boolean show = true; public static boolean enabledRecipes = true; protected static boolean initialized; protected static final ButtonElement leftButton = new ButtonElement(0, 0, 0, 16, 16, "<"); protected static final ButtonElement rightButton = new ButtonElement(1, 0, 0, 16, 16, ">"); + protected static final ButtonElement leftButton2 = new ButtonElement(0, 0, 0, 16, 16, "<"); + protected static final ButtonElement rightButton2 = new ButtonElement(1, 0, 0, 16, 16, ">"); public static TextFieldElement search; protected static TooltipElement tooltip; + private static ITypedIngredient hoveredItem = null; + private static int debounce = 0; public static void init(Minecraft mc) { initialized = true; @@ -77,6 +84,28 @@ public static void renderHeader(int mouseX, int mouseY, int width, int height, M search.drawTextBox(); } + public static void renderHeader2(int mouseX, int mouseY, int width, int height, Minecraft mc, float pt, @Nullable IGuiProperties properties) { + if (!show || !initialized) return; + int w = (int) (width / 3.5F); + Screen currentScreen = mc.currentScreen; + if (currentScreen instanceof ScreenContainerAbstract) { + w = Math.min(((width / 2) - ((ScreenContainerAbstract) (currentScreen)).xSize / 2) - 16, w); + } else if (properties != null) { + w = Math.min(((width / 2) - properties.guiXSize() / 2) - 16, w); + } + int startX = 0;//width - w; + int startY = 4; + String str = "Page " + (currentFavouritePage + 1) + " / " + (favouritePages + 1); + + leftButton2.xPosition = startX; + leftButton2.yPosition = startY; + rightButton2.xPosition = Math.min(startX + (18 * (w / 18)), width - 18); + rightButton2.yPosition = startY; + mc.font.renderString(str, ((((leftButton2.xPosition + leftButton2.width) + (rightButton2.xPosition + rightButton2.width)) / 2) - mc.font.getStringWidth(str) / 2) - 4, startY + 4, 0xFFFFFF, false); + leftButton2.drawButton(mc, mouseX, mouseY); + rightButton2.drawButton(mc, mouseX, mouseY); + } + public static void onTick() { if (!show) return; if(search == null) return; @@ -100,6 +129,20 @@ public static void mouseClicked(int mouseX, int mouseY, int width, int height, M if (currentPage < 0) currentPage = pages; if (currentPage > pages) currentPage = 0; } + + boolean left2 = leftButton2.mouseClicked(mc, mouseX, mouseY); + boolean right2 = rightButton2.mouseClicked(mc, mouseX, mouseY); + if (left2 || right2) { + mc.sndManager.playSound("random.click", SoundCategory.GUI_SOUNDS, 1.0F, 1.0F); + int change = 1; + if (left2) { + change = -1; + } + currentFavouritePage += change; + if (currentFavouritePage < 0) currentFavouritePage = favouritePages; + if (currentFavouritePage > favouritePages) currentFavouritePage = 0; + } + search.mouseClicked(mouseX, mouseY, 1); } @@ -151,11 +194,11 @@ public static void renderItems(int mouseX, int mouseY, int width, int height, Mi List> pageList = toDisplay.stream().skip((long) itemsPerPage * currentPage).limit(itemsPerPage).collect(Collectors.toList()); - ITypedIngredient hoveredItem = null; TooltipBuilder tooltipBuilder = new TooltipBuilder(); boolean isCtrl = Keyboard.isKeyDown(29) || Keyboard.isKeyDown(157); boolean isShift = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT); + hoveredItem = null; int i = 0; loop: for (int y = 0; y < itemsY; y++) { for (int x = 0; x < itemsX; x++) { @@ -197,6 +240,107 @@ public static void renderItems(int mouseX, int mouseY, int width, int height, Mi runtime.showRecipe(hoveredItem, RecipeIngredientRole.INPUT); } } + + if (((IKeybinds) mc.gameSettings).toomanyblocks$getKeyAddFavourite().isPressed()) { + if (runtime.getFavourites().stream().noneMatch(it -> it.matches(hoveredItem.getIngredient()))) { + runtime.getFavourites().add(hoveredItem); + } + } + } + } + + public static void renderItems2(int mouseX, int mouseY, int width, int height, Minecraft mc, float pt, @Nullable IGuiProperties properties) { + if (!initialized) { + init(mc); + return; + } + if (!show) { + return; + } + if(debounce > 0) debounce--; + + Screen currentScreen = mc.currentScreen; + ITMBRuntime runtime = TMB.getRuntime(); + Collection> toDisplay = runtime.getFavourites(); + + int startX = (int) (width / 3.5F); + + if (currentScreen instanceof ScreenContainerAbstract) { + startX = Math.min(((width / 2) - ((ScreenContainerAbstract) (currentScreen)).xSize / 2) - 18, startX); + } else if (properties != null) { + startX = Math.min(((width / 2) - properties.guiXSize() / 2) - 18, startX); + } + + int itemsX = (startX / 18); + int itemsY = Math.min((height / 18) - 1, ((height - 28) / 18)); + int xOffset = 0; + int yOffset = 1; + + startX = 0; + + int itemsPerPage = itemsX * itemsY; + if (itemsPerPage <= 0) return; + favouritePages = toDisplay.size() / itemsPerPage; + + if (currentFavouritePage > favouritePages) currentFavouritePage = favouritePages; + + List> pageList = toDisplay.stream().skip((long) itemsPerPage * currentFavouritePage).limit(itemsPerPage).collect(Collectors.toList()); + + TooltipBuilder tooltipBuilder = new TooltipBuilder(); + boolean isCtrl = Keyboard.isKeyDown(29) || Keyboard.isKeyDown(157); + boolean isShift = Keyboard.isKeyDown(Keyboard.KEY_LSHIFT) || Keyboard.isKeyDown(Keyboard.KEY_RSHIFT); + + hoveredItem = null; + int i = 0; + loop: for (int y = 0; y < itemsY; y++) { + for (int x = 0; x < itemsX; x++) { + if (i >= pageList.size()) break loop; + ITypedIngredient ingredient = (ITypedIngredient) pageList.get(i); + int xOff = /*width -*/ startX + (xOffset * 16) + (2 * xOffset); + int yOff = 8 + (yOffset * 16) + (2 * yOffset); + + ingredient.getType().getRenderer(runtime).render(runtime.getGuiHelper(), ingredient.getIngredient(), xOff, yOff); + + if (mouseX >= xOff && mouseX < xOff + 16 && mouseY >= yOff && mouseY < yOff + 16) { + hoveredItem = ingredient; + ingredient.getType().getRenderer(runtime).getTooltip(tooltipBuilder, hoveredItem.getIngredient(), isCtrl, isShift); + RenderUtil.renderItemSelected(runtime.getGuiHelper(), xOff, yOff); + } + + i++; + xOffset++; + if (xOffset >= itemsX) { + xOffset = 0; + } + } + yOffset++; + } + + if (hoveredItem != null) { + if (!tooltipBuilder.getLines().isEmpty()) { + StringBuilder builder = new StringBuilder(); + tooltipBuilder.getLines().forEach(str -> builder.append(str).append("\n")); + GL11.glPushMatrix(); + tooltip.render(builder.toString(), mouseX, mouseY, 8, -8); + GL11.glPopMatrix(); + } + + if (enabledRecipes) { + if (mc.gameSettings.keyShowRecipe.isPressed()) { + runtime.showRecipe(hoveredItem, RecipeIngredientRole.OUTPUT); + } else if (mc.gameSettings.keyShowUsage.isPressed()) { + runtime.showRecipe(hoveredItem, RecipeIngredientRole.INPUT); + } + } + + if (((IKeybinds) mc.gameSettings).toomanyblocks$getKeyAddFavourite().isPressed() && debounce <= 0) { + debounce = 10; + if (runtime.getFavourites().stream().anyMatch(it -> it.matches(hoveredItem.getIngredient()))) { + runtime.getFavourites().removeIf(it -> it.matches(hoveredItem.getIngredient())); + } else { + runtime.getFavourites().add(hoveredItem); + } + } } } diff --git a/src/main/java/turing/tmb/mixin/client/GameSettingsMixin.java b/src/main/java/turing/tmb/mixin/client/GameSettingsMixin.java index 87e6688..2318ced 100644 --- a/src/main/java/turing/tmb/mixin/client/GameSettingsMixin.java +++ b/src/main/java/turing/tmb/mixin/client/GameSettingsMixin.java @@ -19,6 +19,15 @@ public class GameSettingsMixin implements IKeybinds { @Unique public KeyBinding keyHideTMB = new KeyBinding("key.tmb.hide").bind(InputDevice.keyboard, Keyboard.KEY_O); + @Unique + public KeyBinding keyShowRecipeTree = new KeyBinding("key.tmb.showRecipeTree").bind(InputDevice.keyboard, Keyboard.KEY_T); + + @Unique + public KeyBinding keySetDefaultRecipe = new KeyBinding("key.tmb.setDefaultRecipe").bind(InputDevice.keyboard, Keyboard.KEY_D); + + @Unique + public KeyBinding keyAddFavourite = new KeyBinding("key.tmb.keyAddFavourite").bind(InputDevice.keyboard, Keyboard.KEY_A); + @Unique public OptionBoolean isTMBHidden = new OptionBoolean((GameSettings) ((Object) this), "isTMBHidden", false); @@ -48,6 +57,21 @@ public class GameSettingsMixin implements IKeybinds { return lastTMBSearch; } + @Override + public KeyBinding toomanyblocks$getKeyShowRecipeTree() { + return keyShowRecipeTree; + } + + @Override + public KeyBinding toomanyblocks$getKeySetDefaultRecipe() { + return keySetDefaultRecipe; + } + + @Override + public KeyBinding toomanyblocks$getKeyAddFavourite() { + return keyAddFavourite; + } + @Inject(method = "saveOptions", at = @At("HEAD")) public void saveTMBState(CallbackInfo ci) { if (TMBRenderer.search != null) { diff --git a/src/main/java/turing/tmb/mixin/client/ScreenContainerAbstractMixin.java b/src/main/java/turing/tmb/mixin/client/ScreenContainerAbstractMixin.java index f51d109..593aaa4 100644 --- a/src/main/java/turing/tmb/mixin/client/ScreenContainerAbstractMixin.java +++ b/src/main/java/turing/tmb/mixin/client/ScreenContainerAbstractMixin.java @@ -30,6 +30,8 @@ public ScreenContainerAbstractMixin() { public void render(int mouseX, int mouseY, float pt, CallbackInfo ci) { TMBRenderer.renderHeader(mouseX, mouseY, width, height, mc, pt, null); TMBRenderer.renderItems(mouseX, mouseY, width, height, mc, pt, null); + TMBRenderer.renderHeader2(mouseX, mouseY, width, height, mc, pt, null); + TMBRenderer.renderItems2(mouseX, mouseY, width, height, mc, pt, null); } @Inject(method = "tick", at = @At("HEAD")) diff --git a/src/main/java/turing/tmb/mixin/client/ScreenMixin.java b/src/main/java/turing/tmb/mixin/client/ScreenMixin.java index 1371b7e..a8c224b 100644 --- a/src/main/java/turing/tmb/mixin/client/ScreenMixin.java +++ b/src/main/java/turing/tmb/mixin/client/ScreenMixin.java @@ -30,6 +30,8 @@ public void render(int mouseX, int mouseY, float partialTick, CallbackInfo ci) { IGuiProperties properties = GuiHelper.extraScreens.get(t.getClass().getCanonicalName()).apply(t); TMBRenderer.renderHeader(mouseX, mouseY, width, height, mc, partialTick, properties); TMBRenderer.renderItems(mouseX, mouseY, width, height, mc, partialTick, properties); + TMBRenderer.renderHeader2(mouseX, mouseY, width, height, mc, partialTick, properties); + TMBRenderer.renderItems2(mouseX, mouseY, width, height, mc, partialTick, properties); } } diff --git a/src/main/java/turing/tmb/mixin/client/ScreenPauseMixin.java b/src/main/java/turing/tmb/mixin/client/ScreenPauseMixin.java new file mode 100644 index 0000000..70b2f50 --- /dev/null +++ b/src/main/java/turing/tmb/mixin/client/ScreenPauseMixin.java @@ -0,0 +1,19 @@ +package turing.tmb.mixin.client; + +import net.minecraft.client.gui.ButtonElement; +import net.minecraft.client.gui.ScreenPause; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.Inject; +import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; +import turing.tmb.TMB; + +@Mixin(value = ScreenPause.class,remap = false) +public class ScreenPauseMixin { + + @Inject(method = "buttonClicked", at = @At(value = "INVOKE", target = "Lnet/minecraft/client/Minecraft;changeWorld(Lnet/minecraft/client/world/WorldClient;)V")) + protected void buttonClicked(ButtonElement button, CallbackInfo ci) { + TMB.saveData(); + } + +} diff --git a/src/main/java/turing/tmb/util/IKeybinds.java b/src/main/java/turing/tmb/util/IKeybinds.java index 16546c7..6e326c1 100644 --- a/src/main/java/turing/tmb/util/IKeybinds.java +++ b/src/main/java/turing/tmb/util/IKeybinds.java @@ -12,4 +12,11 @@ public interface IKeybinds { OptionBoolean toomanyblocks$getIsRecipeViewEnabled(); OptionString toomanyblocks$getLastTMBSearch(); + + + KeyBinding toomanyblocks$getKeyShowRecipeTree(); + + KeyBinding toomanyblocks$getKeySetDefaultRecipe(); + + KeyBinding toomanyblocks$getKeyAddFavourite(); } diff --git a/src/main/java/turing/tmb/vanilla/AbstractCraftingRecipeCategory.java b/src/main/java/turing/tmb/vanilla/AbstractCraftingRecipeCategory.java index 4f6c99c..1fdb192 100644 --- a/src/main/java/turing/tmb/vanilla/AbstractCraftingRecipeCategory.java +++ b/src/main/java/turing/tmb/vanilla/AbstractCraftingRecipeCategory.java @@ -7,6 +7,7 @@ import org.jetbrains.annotations.Nullable; import turing.tmb.RecipeLayoutBuilder; import turing.tmb.RecipeTranslator; +import turing.tmb.TMB; import turing.tmb.TypedIngredient; import turing.tmb.api.ItemStackIngredientRenderer; import turing.tmb.api.VanillaTypes; @@ -52,15 +53,19 @@ public IDrawable getBackground() { @Override public void drawRecipe(ITMBRuntime runtime, T recipe, IRecipeLayout layout, List ingredients, ILookupContext context) { - ingredients.add(0, new IngredientList(TypedIngredient.itemStackIngredient(recipe.getOriginal().getOutput()))); - - addInputs(runtime, recipe, layout, ingredients, context); + getIngredients(recipe, layout, context, ingredients); arrow.draw(runtime.getGuiHelper(), x + 62, (background.getHeight() / 2) + 6); } abstract void addInputs(ITMBRuntime runtime, T recipe, IRecipeLayout layout, List ingredients, ILookupContext context); + @Override + public void getIngredients(T recipe, IRecipeLayout layout, ILookupContext context, List ingredients) { + ingredients.add(0, new IngredientList(TypedIngredient.itemStackIngredient(recipe.getOriginal().getOutput()))); + addInputs(TMB.getRuntime(), recipe, layout, ingredients, context); + } + @Override public IRecipeLayout getRecipeLayout() { return new RecipeLayoutBuilder() diff --git a/src/main/java/turing/tmb/vanilla/FurnaceRecipeCategory.java b/src/main/java/turing/tmb/vanilla/FurnaceRecipeCategory.java index be927bb..9909153 100644 --- a/src/main/java/turing/tmb/vanilla/FurnaceRecipeCategory.java +++ b/src/main/java/turing/tmb/vanilla/FurnaceRecipeCategory.java @@ -65,8 +65,7 @@ public IDrawable getBackground() { @Override public void drawRecipe(ITMBRuntime runtime, T recipe, IRecipeLayout layout, List ingredients, ILookupContext context) { - ingredients.add(0, IngredientList.fromRecipeSymbol(recipe.getOriginal().getInput())); - ingredients.add(1, new IngredientList(TypedIngredient.itemStackIngredient(recipe.getOriginal().getOutput()))); + getIngredients(recipe, layout, context, ingredients); arrowBack.draw(runtime.getGuiHelper(), x + 26, (background.getHeight() / 2) - 5); arrow.draw(runtime.getGuiHelper(), x + 26, (background.getHeight() / 2) - 5); @@ -74,6 +73,12 @@ public void drawRecipe(ITMBRuntime runtime, T recipe, IRecipeLayout layout, List flame.draw(runtime.getGuiHelper(), x + 2, (background.getHeight() / 2) + 13); } + @Override + public void getIngredients(T recipe, IRecipeLayout layout, ILookupContext context, List ingredients) { + ingredients.add(0, IngredientList.fromRecipeSymbol(recipe.getOriginal().getInput())); + ingredients.add(1, new IngredientList(TypedIngredient.itemStackIngredient(recipe.getOriginal().getOutput()))); + } + @Override public IRecipeLayout getRecipeLayout() { return new RecipeLayoutBuilder() diff --git a/src/main/java/turing/tmb/vanilla/MobInfoCategory.java b/src/main/java/turing/tmb/vanilla/MobInfoCategory.java index a51b034..ac8ae99 100644 --- a/src/main/java/turing/tmb/vanilla/MobInfoCategory.java +++ b/src/main/java/turing/tmb/vanilla/MobInfoCategory.java @@ -152,11 +152,7 @@ public void drawRecipe(ITMBRuntime runtime, MobInfoRecipeTranslator recipe, IRec yOffset += 10; } - if (recipe.getOriginal().getDrops() != null && recipe.getOriginal().getDrops().length > 0) { - for (int i = 0; i < recipe.getOriginal().getDrops().length; i++) { - ingredients.add(new IngredientList(TypedIngredient.itemStackIngredient(recipe.getOriginal().getDrops()[i].getStack()))); - } - } + getIngredients(recipe, layout, context, ingredients); if (mob == null && !giveUp) { Class mobClass = recipe.getOriginal().getEntityClass(); @@ -174,6 +170,15 @@ public void drawRecipe(ITMBRuntime runtime, MobInfoRecipeTranslator recipe, IRec } } + @Override + public void getIngredients(MobInfoRecipeTranslator recipe, IRecipeLayout layout, ILookupContext context, List ingredients) { + if (recipe.getOriginal().getDrops() != null && recipe.getOriginal().getDrops().length > 0) { + for (int i = 0; i < recipe.getOriginal().getDrops().length; i++) { + ingredients.add(new IngredientList(TypedIngredient.itemStackIngredient(recipe.getOriginal().getDrops()[i].getStack()))); + } + } + } + @Override public > List getTooltips(MobInfoRecipeTranslator recipe, IRecipeSlot slot, int mouseX, int mouseY) { if (recipe.getOriginal().getDrops() != null && recipe.getOriginal().getDrops().length > 0) { diff --git a/src/main/java/turing/tmb/vanilla/TrommelRecipeCategory.java b/src/main/java/turing/tmb/vanilla/TrommelRecipeCategory.java index 8b351f2..485609f 100644 --- a/src/main/java/turing/tmb/vanilla/TrommelRecipeCategory.java +++ b/src/main/java/turing/tmb/vanilla/TrommelRecipeCategory.java @@ -63,6 +63,15 @@ public IDrawable getBackground() { @Override public void drawRecipe(ITMBRuntime runtime, TrommelRecipeTranslator recipe, IRecipeLayout layout, List ingredients, ILookupContext context) { + + getIngredients(recipe, layout, context, ingredients); + + arrowBack.draw(runtime.getGuiHelper(), x + 26, (background.getHeight() / 2) + 7); + arrow.draw(runtime.getGuiHelper(), x + 26, (background.getHeight() / 2) + 7); + } + + @Override + public void getIngredients(TrommelRecipeTranslator recipe, IRecipeLayout layout, ILookupContext context, List ingredients) { ingredients.add(0, IngredientList.fromRecipeSymbol(recipe.getOriginal().getInput())); for (int i = 0; i < 9; i++) { @@ -74,8 +83,6 @@ public void drawRecipe(ITMBRuntime runtime, TrommelRecipeTranslator recipe, IRec } } - arrowBack.draw(runtime.getGuiHelper(), x + 26, (background.getHeight() / 2) + 7); - arrow.draw(runtime.getGuiHelper(), x + 26, (background.getHeight() / 2) + 7); } @Override diff --git a/src/main/resources/lang/tmb/en_US.lang b/src/main/resources/lang/tmb/en_US.lang index e90d12c..38d382f 100644 --- a/src/main/resources/lang/tmb/en_US.lang +++ b/src/main/resources/lang/tmb/en_US.lang @@ -7,6 +7,13 @@ tmb.tooltip.allCategories=Click to see all categories gui.options.page.controls.category.tmb=Too Many Blocks gui.options.page.general.category.tmb=Too Many Blocks +gui.tmb.recipeTree.label.title=Recipe Tree key.tmb.hide=Hide TMB +key.tmb.keyAddFavourite=Add/Remove Favourite +key.tmb.setDefaultRecipe=Set/Remove Default Recipe +key.tmb.showRecipeTree=Show Recipe Tree options.isRecipeViewEnabled=Enable Recipe Viewing options.isTMBHidden=Hide TMB + +message.tmb.setDefaultRecipe=Set default recipe for '%s' to '%s'! +message.tmb.removeDefaultRecipe=Removed default recipe for '%s'. diff --git a/src/main/resources/tmb.mixins.json b/src/main/resources/tmb.mixins.json index 0483afa..2a324a8 100644 --- a/src/main/resources/tmb.mixins.json +++ b/src/main/resources/tmb.mixins.json @@ -13,7 +13,8 @@ "client.ScreenContainerAbstractMixin", "client.ScreenInventoryCreativeAccessor", "client.ScreenInventoryCreativeMixin", - "client.ScreenMixin" + "client.ScreenMixin", + "client.ScreenPauseMixin" ], "injectors": { "defaultRequire": 1