/* * Copyright (C) 2022 NotEnoughUpdates contributors * * This file is part of NotEnoughUpdates. * * NotEnoughUpdates is free software: you can redistribute it * and/or modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation, either * version 3 of the License, or (at your option) any later version. * * NotEnoughUpdates is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with NotEnoughUpdates. If not, see . */ package io.github.moulberry.notenoughupdates.recipes; import com.google.common.collect.Sets; import com.google.gson.JsonArray; import com.google.gson.JsonElement; import com.google.gson.JsonObject; import com.google.gson.JsonPrimitive; import io.github.moulberry.notenoughupdates.NEUManager; import io.github.moulberry.notenoughupdates.NotEnoughUpdates; import io.github.moulberry.notenoughupdates.miscgui.GuiItemRecipe; import io.github.moulberry.notenoughupdates.options.NEUConfig; import io.github.moulberry.notenoughupdates.util.HotmInformation; import io.github.moulberry.notenoughupdates.util.Utils; import net.minecraft.client.Minecraft; import net.minecraft.item.ItemStack; import net.minecraft.util.EnumChatFormatting; import net.minecraft.util.ResourceLocation; import java.util.ArrayList; import java.util.Collections; import java.util.List; import java.util.Set; public class ForgeRecipe implements NeuRecipe { private static final ResourceLocation BACKGROUND = new ResourceLocation( "notenoughupdates", "textures/gui/forge_recipe_tall.png" ); private static final int SLOT_IMAGE_U = 176; private static final int SLOT_IMAGE_V = 0; private static final int SLOT_IMAGE_SIZE = 18; private static final int SLOT_PADDING = 1; private static final int EXTRA_INFO_MAX_WIDTH = 75; public static final int EXTRA_INFO_X = 132; public static final int EXTRA_INFO_Y = 55; public enum ForgeType { REFINING, ITEM_FORGING } private final NEUManager manager; private final List inputs; private final Ingredient output; private final int hotmLevel; private final int timeInSeconds; // TODO: quick forge private List slots; public ForgeRecipe( NEUManager manager, List inputs, Ingredient output, int durationInSeconds, int hotmLevel ) { this.manager = manager; this.inputs = inputs; this.output = output; this.hotmLevel = hotmLevel; this.timeInSeconds = durationInSeconds; } public List getInputs() { return inputs; } public Ingredient getOutput() { return output; } public int getTimeInSeconds() { return timeInSeconds; } @Override public ResourceLocation getBackground() { return BACKGROUND; } @Override public RecipeType getType() { return RecipeType.FORGE; } @Override public Set getIngredients() { return Sets.newHashSet(inputs); } @Override public boolean hasVariableCost() { return false; } @Override public Set getOutputs() { return Collections.singleton(output); } @Override public List getSlots() { if (slots != null) return slots; slots = new ArrayList<>(); for (int i = 0; i < inputs.size(); i++) { Ingredient input = inputs.get(i); ItemStack itemStack = input.getItemStack(); if (itemStack == null) continue; int[] slotCoordinates = getSlotCoordinates(i, inputs.size()); slots.add(new RecipeSlot(slotCoordinates[0], slotCoordinates[1], itemStack)); } slots.add(new RecipeSlot(124, 66, output.getItemStack())); return slots; } @Override public void drawExtraBackground(GuiItemRecipe gui, int mouseX, int mouseY) { Minecraft.getMinecraft().getTextureManager().bindTexture(BACKGROUND); for (int i = 0; i < inputs.size(); i++) { int[] slotCoordinates = getSlotCoordinates(i, inputs.size()); gui.drawTexturedModalRect( gui.guiLeft + slotCoordinates[0] - SLOT_PADDING, gui.guiTop + slotCoordinates[1] - SLOT_PADDING, SLOT_IMAGE_U, SLOT_IMAGE_V, SLOT_IMAGE_SIZE, SLOT_IMAGE_SIZE ); } } @Override public void drawExtraInfo(GuiItemRecipe gui, int mouseX, int mouseY) { if (timeInSeconds > 0) Utils.drawStringCenteredScaledMaxWidth( formatDuration(timeInSeconds), gui.guiLeft + EXTRA_INFO_X, gui.guiTop + EXTRA_INFO_Y, false, EXTRA_INFO_MAX_WIDTH, 0xff00ff ); } @Override public void drawHoverInformation(GuiItemRecipe gui, int mouseX, int mouseY) { NEUConfig.HiddenProfileSpecific profileSpecific = NotEnoughUpdates.INSTANCE.config.getProfileSpecific(); if (profileSpecific == null) return; if (timeInSeconds <= 0 || !gui.isWithinRect( mouseX, mouseY, gui.guiLeft + EXTRA_INFO_X - EXTRA_INFO_MAX_WIDTH / 2, gui.guiTop + EXTRA_INFO_Y - 8, EXTRA_INFO_MAX_WIDTH, 16 )) return; int level = profileSpecific.hotmTree.getOrDefault("Quick Forge", 0); if (level == 0) return; int reducedTime = getReducedTime(level); Utils.drawHoveringText( Collections.singletonList( EnumChatFormatting.YELLOW + formatDuration(reducedTime) + " with Quick Forge (Level " + level + ")"), mouseX, mouseY, gui.width, gui.height, 500); } public int getReducedTime(int quickForgeUpgradeLevel) { return HotmInformation.getQuickForgeMultiplier(quickForgeUpgradeLevel) * timeInSeconds / 1000; } @Override public JsonObject serialize() { JsonObject object = new JsonObject(); JsonArray ingredients = new JsonArray(); for (Ingredient input : inputs) { ingredients.add(new JsonPrimitive(input.serialize())); } object.addProperty("type", "forge"); object.add("inputs", ingredients); object.addProperty("count", output.getCount()); object.addProperty("overrideOutputId", output.getInternalItemId()); if (hotmLevel >= 0) object.addProperty("hotmLevel", hotmLevel); if (timeInSeconds >= 0) object.addProperty("duration", timeInSeconds); return object; } static ForgeRecipe parseForgeRecipe(NEUManager manager, JsonObject recipe, JsonObject output) { List ingredients = new ArrayList<>(); for (JsonElement element : recipe.getAsJsonArray("inputs")) { String ingredientString = element.getAsString(); ingredients.add(new Ingredient(manager, ingredientString)); } String internalItemId = output.get("internalname").getAsString(); if (recipe.has("overrideOutputId")) internalItemId = recipe.get("overrideOutputId").getAsString(); int resultCount = 1; if (recipe.has("count")) { resultCount = recipe.get("count").getAsInt(); } int duration = -1; if (recipe.has("duration")) { duration = recipe.get("duration").getAsInt(); } int hotmLevel = -1; if (recipe.has("hotmLevel")) { hotmLevel = recipe.get("hotmLevel").getAsInt(); } return new ForgeRecipe( manager, ingredients, new Ingredient(manager, internalItemId, resultCount), duration, hotmLevel ); } private static final int RECIPE_CENTER_X = 49; private static final int RECIPE_CENTER_Y = 74; private static final int SLOT_DISTANCE_FROM_CENTER = 30; private static final int RECIPE_FALLBACK_X = 20; private static final int RECIPE_FALLBACK_Y = 15; static int[] getSlotCoordinates(int slotNumber, int totalSlotCount) { if (totalSlotCount > 8) { return new int[]{ RECIPE_FALLBACK_X + (slotNumber % 4) * GuiItemRecipe.SLOT_SPACING, RECIPE_FALLBACK_Y + (slotNumber / 4) * GuiItemRecipe.SLOT_SPACING, }; } if (totalSlotCount == 1) { return new int[]{ RECIPE_CENTER_X - GuiItemRecipe.SLOT_SIZE / 2, RECIPE_CENTER_Y - GuiItemRecipe.SLOT_SIZE / 2 }; } double rad = Math.PI * 2 * slotNumber / totalSlotCount; int x = (int) (Math.cos(rad) * SLOT_DISTANCE_FROM_CENTER); int y = (int) (Math.sin(rad) * SLOT_DISTANCE_FROM_CENTER); return new int[]{ RECIPE_CENTER_X + x - GuiItemRecipe.SLOT_SIZE / 2, RECIPE_CENTER_Y + y - GuiItemRecipe.SLOT_SIZE / 2 }; } static String formatDuration(int seconds) { int minutes = seconds / 60; seconds %= 60; int hours = minutes / 60; minutes %= 60; int days = hours / 24; hours %= 24; StringBuilder sB = new StringBuilder(); if (days != 0) sB.append(days).append("d "); if (hours != 0) sB.append(hours).append("h "); if (minutes != 0) sB.append(minutes).append("m "); if (seconds != 0) sB.append(seconds).append("s "); return sB.substring(0, sB.length() - 1); } }