diff --git a/.gitignore b/.gitignore index 2b0372c..2e64e8a 100644 --- a/.gitignore +++ b/.gitignore @@ -11,7 +11,7 @@ out *.ipr *.iws *.iml -.idea/workspace.xml +.idea/ # gradle build @@ -27,4 +27,4 @@ logs # Files from Forge MDK forge*changelog.txt /src/generated/resources/.cache/cache -.DS_Store \ No newline at end of file +.DS_Store diff --git a/.release-info/1.18/RELEASE_HASH b/.release-info/1.18/RELEASE_HASH index 13ff471..440757a 100644 --- a/.release-info/1.18/RELEASE_HASH +++ b/.release-info/1.18/RELEASE_HASH @@ -1 +1 @@ -3f7a5625360952bcbd99e42b635469bff7a7f01c +256510bbbd810366ee9da94fc0ddd31d450e4576 diff --git a/.release-info/1.18/VERSION b/.release-info/1.18/VERSION index 1a03094..0e24a92 100644 --- a/.release-info/1.18/VERSION +++ b/.release-info/1.18/VERSION @@ -1 +1 @@ -0.1.9 +0.1.12 diff --git a/CHANGELOG/1.18/0.1.9.md b/CHANGELOG/1.18/0.1.9.md new file mode 100644 index 0000000..b20232c --- /dev/null +++ b/CHANGELOG/1.18/0.1.9.md @@ -0,0 +1,5 @@ +# 0.1.9 + +- `[FIX]` Fix endless loop when calculating recipes +- `[FIX]` Fixes to recipe cost calculations +- `[FIX]` Miscellaneous optimizations diff --git a/CHANGELOG/1.18/current.md b/CHANGELOG/1.18/current.md index 97305b4..7e450fa 100644 --- a/CHANGELOG/1.18/current.md +++ b/CHANGELOG/1.18/current.md @@ -1,3 +1,6 @@ -- `[FIX]` Fix endless loop when calculating recipes -- `[FIX]` Fixes to recipe cost calculations -- `[FIX]` Miscellaneous optimizations +- `[FIX]` Made cost calculation of non-vanilla recipes slightly more expensive +- `[FIX]` Made cost calculation of non-crafting table recipes slightly more expensive +- `[FIX]` Increased recipe calculation depth to 3 +- `[FIX]` Added checks for recipe and ingredient namespaces in determining intermediate/raw status +- `[FIX]` Changed text for crafting queue sections +- `[NEW]` Intermediate and raw materials that are tags are marked with an asterisk diff --git a/src/generated/resources/assets/crafttracker/lang/de_de.json b/src/generated/resources/assets/crafttracker/lang/de_de.json index ea551eb..9bf89e8 100644 --- a/src/generated/resources/assets/crafttracker/lang/de_de.json +++ b/src/generated/resources/assets/crafttracker/lang/de_de.json @@ -17,10 +17,10 @@ "crafttracker.msg.shopping_list_overlay_mode.show": "Nun wird das Overlay mit der Einkaufsliste angezeigt.", "crafttracker.screen.craft_queue.empty_message": "Die Warteschlange ist leer.", "crafttracker.screen.craft_queue.help_message": "Die Warteschlange verwalten:", - "crafttracker.screen.craft_queue.section.fuel": "Kraftstoff", - "crafttracker.screen.craft_queue.section.intermediates": "Zwischenprodukte", - "crafttracker.screen.craft_queue.section.materials": "Materialien", - "crafttracker.screen.craft_queue.section.products": "Produkte", + "crafttracker.screen.craft_queue.section.fuel": "Und dieser Treibstoff:", + "crafttracker.screen.craft_queue.section.intermediates": "Sie m\u00FCssen zun\u00E4chst Folgendes tun:", + "crafttracker.screen.craft_queue.section.materials": "Mit diesen Materialien:", + "crafttracker.screen.craft_queue.section.products": "Um zu machen:", "crafttracker.screen.craft_queue.title": "Herstellungswarteschlange", "crafttracker.screen.have": "habe %d", "crafttracker.screen.queue_mgr.button.clear": "Alle l\u00F6schen", diff --git a/src/generated/resources/assets/crafttracker/lang/en_gb.json b/src/generated/resources/assets/crafttracker/lang/en_gb.json index 34181ea..6cc48ee 100644 --- a/src/generated/resources/assets/crafttracker/lang/en_gb.json +++ b/src/generated/resources/assets/crafttracker/lang/en_gb.json @@ -9,18 +9,18 @@ "crafttracker.config.client.shopping_list_width": "The width of the shopping list overlay", "crafttracker.config.client.shopping_list_x": "The X position on the screen for the shopping list overlay", "crafttracker.config.client.shopping_list_y": "The Y position on the screen for the shopping list overlay", - "crafttracker.msg.queue_overlay_mode.dynamic": "The craft list overlay mode is dynamic.", - "crafttracker.msg.queue_overlay_mode.hide": "The craft list overlay will now be hidden.", - "crafttracker.msg.queue_overlay_mode.show": "The craft list overlay will now be shown.", + "crafttracker.msg.queue_overlay_mode.dynamic": "The craft queue overlay mode is dynamic.", + "crafttracker.msg.queue_overlay_mode.hide": "The craft queue overlay will now be hidden.", + "crafttracker.msg.queue_overlay_mode.show": "The craft queue overlay will now be shown.", "crafttracker.msg.shopping_list_overlay_mode.dynamic": "The shopping list overlay mode is dynamic.", "crafttracker.msg.shopping_list_overlay_mode.hide": "The shopping list overlay will now be hidden.", "crafttracker.msg.shopping_list_overlay_mode.show": "The shopping list overlay will now be shown.", "crafttracker.screen.craft_queue.empty_message": "The queue is empty.", "crafttracker.screen.craft_queue.help_message": "To manage the queue:", - "crafttracker.screen.craft_queue.section.fuel": "Fuel", - "crafttracker.screen.craft_queue.section.intermediates": "Intermediates", - "crafttracker.screen.craft_queue.section.materials": "Materials", - "crafttracker.screen.craft_queue.section.products": "Products", + "crafttracker.screen.craft_queue.section.fuel": "And this fuel:", + "crafttracker.screen.craft_queue.section.intermediates": "You first need to make:", + "crafttracker.screen.craft_queue.section.materials": "With these materials:", + "crafttracker.screen.craft_queue.section.products": "To make:", "crafttracker.screen.craft_queue.title": "Craft Queue", "crafttracker.screen.have": "have %d", "crafttracker.screen.queue_mgr.button.clear": "Clear", diff --git a/src/generated/resources/assets/crafttracker/lang/en_us.json b/src/generated/resources/assets/crafttracker/lang/en_us.json index 34181ea..6cc48ee 100644 --- a/src/generated/resources/assets/crafttracker/lang/en_us.json +++ b/src/generated/resources/assets/crafttracker/lang/en_us.json @@ -9,18 +9,18 @@ "crafttracker.config.client.shopping_list_width": "The width of the shopping list overlay", "crafttracker.config.client.shopping_list_x": "The X position on the screen for the shopping list overlay", "crafttracker.config.client.shopping_list_y": "The Y position on the screen for the shopping list overlay", - "crafttracker.msg.queue_overlay_mode.dynamic": "The craft list overlay mode is dynamic.", - "crafttracker.msg.queue_overlay_mode.hide": "The craft list overlay will now be hidden.", - "crafttracker.msg.queue_overlay_mode.show": "The craft list overlay will now be shown.", + "crafttracker.msg.queue_overlay_mode.dynamic": "The craft queue overlay mode is dynamic.", + "crafttracker.msg.queue_overlay_mode.hide": "The craft queue overlay will now be hidden.", + "crafttracker.msg.queue_overlay_mode.show": "The craft queue overlay will now be shown.", "crafttracker.msg.shopping_list_overlay_mode.dynamic": "The shopping list overlay mode is dynamic.", "crafttracker.msg.shopping_list_overlay_mode.hide": "The shopping list overlay will now be hidden.", "crafttracker.msg.shopping_list_overlay_mode.show": "The shopping list overlay will now be shown.", "crafttracker.screen.craft_queue.empty_message": "The queue is empty.", "crafttracker.screen.craft_queue.help_message": "To manage the queue:", - "crafttracker.screen.craft_queue.section.fuel": "Fuel", - "crafttracker.screen.craft_queue.section.intermediates": "Intermediates", - "crafttracker.screen.craft_queue.section.materials": "Materials", - "crafttracker.screen.craft_queue.section.products": "Products", + "crafttracker.screen.craft_queue.section.fuel": "And this fuel:", + "crafttracker.screen.craft_queue.section.intermediates": "You first need to make:", + "crafttracker.screen.craft_queue.section.materials": "With these materials:", + "crafttracker.screen.craft_queue.section.products": "To make:", "crafttracker.screen.craft_queue.title": "Craft Queue", "crafttracker.screen.have": "have %d", "crafttracker.screen.queue_mgr.button.clear": "Clear", diff --git a/src/main/java/com/sweetrpg/crafttracker/client/event/ClientEventHandler.java b/src/main/java/com/sweetrpg/crafttracker/client/event/ClientEventHandler.java index 5c91a6c..01a002d 100644 --- a/src/main/java/com/sweetrpg/crafttracker/client/event/ClientEventHandler.java +++ b/src/main/java/com/sweetrpg/crafttracker/client/event/ClientEventHandler.java @@ -159,14 +159,14 @@ private static void handlePopulateShoppingList() { materials.forEach(m -> { var haveQty = InventoryUtil.getQuantityOf(player, m.getItemId()); - var needed = m.getQuantity() - haveQty; + var needed = m.getAmount() - haveQty; if(needed > 0) sMgr.addItem(player, m.getItemId(), needed); }); fuel.forEach(f -> { var haveQty = InventoryUtil.getQuantityOf(player, f.getItemId()); - var needed = f.getQuantity() - haveQty; + var needed = f.getAmount() - haveQty; if(needed > 0) sMgr.addItem(player, f.getItemId(), needed); diff --git a/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java b/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java index 17fc2c1..32a65e0 100644 --- a/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java +++ b/src/main/java/com/sweetrpg/crafttracker/client/overlay/CraftQueueOverlay.java @@ -37,7 +37,9 @@ public class CraftQueueOverlay { var mgr = CraftingQueueManager.INSTANCE; var products = mgr.getEndProducts().stream().sorted((i1, i2) -> { var item1 = ForgeRegistries.ITEMS.getValue(i1.getProductId()); + if(item1 == null) return 0; var item2 = ForgeRegistries.ITEMS.getValue(i2.getProductId()); + if(item2 == null) return 0; return item1.getDescription().getString().compareTo(item2.getDescription().getString()); }).toList(); @@ -103,6 +105,9 @@ public class CraftQueueOverlay { var p = products.get(i); var item = ForgeRegistries.ITEMS.getValue(p.getProductId()); + if(item == null) { + continue; + } var stack = item.getDefaultInstance(); var selectedRecipe = p.getRecipes().get(p.getIndex()); var amountProduced = selectedRecipe.getResultItem().getCount() * p.getIterations(); @@ -118,7 +123,7 @@ public class CraftQueueOverlay { } Player player = Minecraft.getInstance().player; - var inventory = player.getInventory(); +// var inventory = player.getInventory(); // SECTION: intermediates @@ -136,7 +141,9 @@ public class CraftQueueOverlay { // items var sortedIntermediates = mgr.getIntermediates().stream().sorted((i1, i2) -> { var item1 = ForgeRegistries.ITEMS.getValue(i1.getItemId()); + if(item1 == null) return 0; var item2 = ForgeRegistries.ITEMS.getValue(i2.getItemId()); + if(item2 == null) return 0; return item1.getDescription().getString().compareTo(item2.getDescription().getString()); }).toList(); for(int i = 0; i < sortedIntermediates.size(); i++) { @@ -144,10 +151,10 @@ public class CraftQueueOverlay { var item = ForgeRegistries.ITEMS.getValue(inter.getItemId()); var stack = item.getDefaultInstance(); - stack.setCount(inter.getQuantity()); + stack.setCount(inter.getAmount()); int playerHasQuantity = InventoryUtil.getQuantityOf(player, inter.getItemId()); - if(playerHasQuantity >= inter.getQuantity()) { + if(playerHasQuantity >= inter.getAmount()) { // don't need to display this intermediate, since the user doesn't need to make it continue; } @@ -159,14 +166,16 @@ public class CraftQueueOverlay { final int lambdaYpos = yPos; if(playerHasQuantity > 0) { var countText = I18n.get(Constants.TRANSLATION_KEY_GUI_HAVE, playerHasQuantity); - var text = String.format("%s [%s]", + var text = String.format("%s%s [%s]", item.getDescription().getString(MAX_STRING_LENGTH - countText.length() - 3), + inter.isTag() ? "*" : "", countText); CraftTracker.LOGGER.trace("text: {}", text); GuiComponent.drawString(poseStack, gui.getFont(), text, x + ITEM_NAME_X_OFFSET, lambdaYpos + 4, TEXT_COLOR); } else { - var text = item.getDescription().getString(MAX_STRING_LENGTH); + var text = item.getDescription().getString(MAX_STRING_LENGTH) + + (inter.isTag() ? "*" : ""); GuiComponent.drawString(poseStack, gui.getFont(), text, x + ITEM_NAME_X_OFFSET, yPos + 4, TEXT_COLOR); } @@ -199,10 +208,10 @@ public class CraftQueueOverlay { var item = ForgeRegistries.ITEMS.getValue(m.getItemId()); var stack = item.getDefaultInstance(); - stack.setCount(m.getQuantity()); + stack.setCount(m.getAmount()); int playerHasQuantity = InventoryUtil.getQuantityOf(player, m.getItemId()); - if(playerHasQuantity >= m.getQuantity()) { + if(playerHasQuantity >= m.getAmount()) { // don't need to display this intermediate, since the user doesn't need to make it continue; } @@ -214,14 +223,16 @@ public class CraftQueueOverlay { final int lambdaYpos = yPos; if(playerHasQuantity > 0) { var countText = I18n.get(Constants.TRANSLATION_KEY_GUI_HAVE, playerHasQuantity); - var text = String.format("%s [%s]", + var text = String.format("%s%s [%s]", item.getDescription().getString(MAX_STRING_LENGTH - countText.length() - 3), + m.isTag() ? "*" : "", countText); CraftTracker.LOGGER.trace("text: {}", text); GuiComponent.drawString(poseStack, gui.getFont(), text, x + ITEM_NAME_X_OFFSET, lambdaYpos + 4, TEXT_COLOR); } else { - var text = item.getDescription().getString(MAX_STRING_LENGTH); + var text = item.getDescription().getString(MAX_STRING_LENGTH) + + (m.isTag() ? "*" : ""); GuiComponent.drawString(poseStack, gui.getFont(), text, x + ITEM_NAME_X_OFFSET, yPos + 4, TEXT_COLOR); } @@ -254,10 +265,10 @@ public class CraftQueueOverlay { var item = ForgeRegistries.ITEMS.getValue(f.getItemId()); var stack = item.getDefaultInstance(); - stack.setCount(f.getQuantity()); + stack.setCount(f.getAmount()); int playerHasQuantity = InventoryUtil.getQuantityOf(player, f.getItemId()); - if(playerHasQuantity >= f.getQuantity()) { + if(playerHasQuantity >= f.getAmount()) { // don't need to display this intermediate, since the user doesn't need to make it continue; } @@ -269,14 +280,16 @@ public class CraftQueueOverlay { final int lambdaYpos = yPos; if(playerHasQuantity > 0) { var countText = I18n.get(Constants.TRANSLATION_KEY_GUI_HAVE, playerHasQuantity); - var text = String.format("%s [%s]", + var text = String.format("%s%s [%s]", item.getDescription().getString(MAX_STRING_LENGTH - countText.length() - 3), + f.isTag() ? "*" : "", countText); CraftTracker.LOGGER.trace("text: {}", text); GuiComponent.drawString(poseStack, gui.getFont(), text, x + ITEM_NAME_X_OFFSET, lambdaYpos + 4, TEXT_COLOR); } else { - var text = item.getDescription().getString(MAX_STRING_LENGTH); + var text = item.getDescription().getString(MAX_STRING_LENGTH) + + (f.isTag() ? "*" : ""); GuiComponent.drawString(poseStack, gui.getFont(), text, x + ITEM_NAME_X_OFFSET, yPos + 4, TEXT_COLOR); } diff --git a/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java index 7293161..1315c33 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/manager/CraftingQueueManager.java @@ -1,6 +1,7 @@ package com.sweetrpg.crafttracker.common.manager; import com.sweetrpg.crafttracker.CraftTracker; +import com.sweetrpg.crafttracker.common.model.CraftingQueueItem; import com.sweetrpg.crafttracker.common.model.CraftingQueueProduct; import com.sweetrpg.crafttracker.common.storage.CraftingQueueStorage; import com.sweetrpg.crafttracker.common.util.DebugUtil; @@ -11,6 +12,7 @@ import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtIo; import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Tuple; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; @@ -28,18 +30,17 @@ import java.nio.file.StandardOpenOption; import java.text.MessageFormat; import java.util.*; -import java.util.stream.Collectors; public class CraftingQueueManager { public static CraftingQueueManager INSTANCE = new CraftingQueueManager(); - private static final int MAX_PROCESSING_LEVEL = 2; + private static final int MAX_PROCESSING_LEVEL = 3; private Map endProducts = new HashMap<>(); - private Map intermediateProducts = new HashMap<>(); - private Map rawMaterials = new HashMap<>(); - private Map fuel = new HashMap<>(); + private Map intermediateProducts = new HashMap<>(); + private Map rawMaterials = new HashMap<>(); + private Map fuel = new HashMap<>(); public CraftingQueueManager() { } @@ -96,31 +97,27 @@ public void save(Player player) { } public List getEndProducts() { - return endProducts.entrySet() + return endProducts.values() .stream() - .map(e -> e.getValue()) .toList(); } - public List getIntermediates() { - return intermediateProducts.entrySet() + public List getIntermediates() { + return intermediateProducts.values() .stream() - .map(e -> new QueueItem(e.getKey(), e.getValue())) - .collect(Collectors.toUnmodifiableList()); + .toList(); } - public List getRawMaterials() { - return rawMaterials.entrySet() + public List getRawMaterials() { + return rawMaterials.values() .stream() - .map(e -> new QueueItem(e.getKey(), e.getValue())) - .collect(Collectors.toUnmodifiableList()); + .toList(); } - public List getFuel() { - return fuel.entrySet() + public List getFuel() { + return fuel.values() .stream() - .map(e -> new QueueItem(e.getKey(), e.getValue())) - .collect(Collectors.toUnmodifiableList()); + .toList(); } public void addProduct(Player player, ResourceLocation itemId, int quantity) { @@ -130,7 +127,7 @@ public void addProduct(Player player, ResourceLocation itemId, int quantity) { var recipes = RecipeUtil.getRecipesFor(itemId); - if(recipes.size() > 0) { + if(!recipes.isEmpty()) { CraftTracker.LOGGER.debug("recipes: {}", recipes.stream().map(DebugUtil::printRecipe)); var product = new CraftingQueueProduct(itemId, recipes, quantity); @@ -258,17 +255,23 @@ void coalesceProducts(ProcessingContext ctx) { r.intermediateProducts.forEach((ik, iv) -> { this.intermediateProducts.compute(ik, (ik1, iv1) -> { - return ObjectUtils.defaultIfNull(iv1, 0) + iv; + return ObjectUtils.defaultIfNull(iv1, new CraftingQueueItem(ik1, 0, false)) + .increment(iv.amount) + .setTag(iv.tag); }); }); r.rawMaterials.forEach((rk, rv) -> { this.rawMaterials.compute(rk, (rk1, rv1) -> { - return ObjectUtils.defaultIfNull(rv1, 0) + rv; + return ObjectUtils.defaultIfNull(rv1, new CraftingQueueItem(rk1, 0, false)) + .increment(rv.amount) + .setTag(rv.tag); }); }); r.fuel.forEach((fk, fv) -> { this.fuel.compute(fk, (fk1, fv1) -> { - return ObjectUtils.defaultIfNull(fv1, 0) + fv; + return ObjectUtils.defaultIfNull(fv1, new CraftingQueueItem(fk1, 0, false)) + .increment(fv.amount) + .setTag(fv.tag); }); }); }); @@ -284,8 +287,19 @@ ComputedRecipe computeRecipe(Recipe recipe, int iterations, int depth) { var ingredients = recipe.getIngredients(); CraftTracker.LOGGER.debug("ingredients: {}", ingredients.stream().map(DebugUtil::printIngredient).toList()); + // if the ingredients are in a different namespace than the recipe, and we're not at the root, treat the recipe + var recipeNamespace = ObjectUtils.defaultIfNull(recipe.getId().getNamespace(), ""); + var itemNamespace = ObjectUtils.defaultIfNull(recipe.getResultItem().getItem().getRegistryName().getNamespace(), ""); + if(depth > 0 && + (!RecipeUtil.areIngredientsSameNamespace(recipeNamespace, ingredients) || + !RecipeUtil.areIngredientsSameNamespace(itemNamespace, ingredients))) { + CraftTracker.LOGGER.debug("ingredients for sub-recipe are not in the same namespace as the recipe: {}", + DebugUtil.printRecipe(recipe)); + return null; + } + // tally ingredients - Map ingredientTally = new HashMap<>(); + Map> ingredientTally = new HashMap<>(); for(Ingredient ingredient : ingredients) { CraftTracker.LOGGER.debug("ingredient: {}", DebugUtil.printIngredient(ingredient)); @@ -293,23 +307,40 @@ ComputedRecipe computeRecipe(Recipe recipe, int iterations, int depth) { continue; } + CraftTracker.LOGGER.debug("ingredient class: {}", ingredient.getClass()); + CraftTracker.LOGGER.debug("ingredient.values: {}", (Object) ingredient.values); + + Boolean tag; + if(ingredient.values.length > 0 && ingredient.values[0] instanceof Ingredient.TagValue) { + // ingredient is a tag + tag = true; + } + else { + tag = false; + } + ItemStack chosenStack = RecipeUtil.chooseLeastExpensiveOf(ingredient.getItems()); - ingredientTally.compute(chosenStack.getItem().getRegistryName(), (ingredientId, amount) -> { - return ObjectUtils.defaultIfNull(amount, 0) + 1; + ingredientTally.compute(chosenStack.getItem().getRegistryName(), (ingredientId, tuple) -> { +// return ObjectUtils.defaultIfNull(amount, 0) + 1; + tuple = ObjectUtils.defaultIfNull(tuple, new Tuple<>(false, 0)); + tuple.setA(tag); + tuple.setB(tuple.getB() + 1); +// newTuple.setA(tag); +// newTuple.setB(); + return tuple; }); } // process ingredients -// for(Ingredient ingredient : ingredients) { ingredientTally.forEach((ingredientId, ingredientAmount) -> { CraftTracker.LOGGER.debug("ingredient: id {}, amount {}", ingredientId, ingredientAmount); -// ItemStack chosenStack = RecipeUtil.chooseLeastExpensiveOf(ingredient.getItems()); Item item = ForgeRegistries.ITEMS.getValue(ingredientId); -// Item item = chosenStack.getItem(); CraftTracker.LOGGER.debug("item: {}", DebugUtil.printItem(item)); - int amountRequired = ingredientAmount; // chosenStack.getCount(); + int amountRequired = ingredientAmount.getB(); // chosenStack.getCount(); CraftTracker.LOGGER.debug("amountRequired: {}", amountRequired); + boolean isTag = ingredientAmount.getA(); + CraftTracker.LOGGER.debug("isTag: {}", isTag); // check if player already has the item CraftTracker.LOGGER.debug("check if player already has {}", DebugUtil.printItem(item)); @@ -334,37 +365,56 @@ ComputedRecipe computeRecipe(Recipe recipe, int iterations, int depth) { // no recipes for this ingredient, so it's a raw material computedRecipe.rawMaterials.compute(id, (itemId, quantity) -> - ObjectUtils.defaultIfNull(quantity, 0) + (amountRequired * iterations)); + ObjectUtils.defaultIfNull(quantity, new ComputedRecipeItem(itemId)) + .increase(amountRequired * iterations) + .tag(isTag)); } else { CraftTracker.LOGGER.debug("subRecipes has {} items; ingredient {} is an intermediate product", subRecipes.size(), ingredientId); var chosenSubRecipe = RecipeUtil.chooseLeastExpensiveOf(subRecipes); -// CraftTracker.LOGGER.debug("least expensive item index: {}", subIndex); -// var chosenSubRecipe = subRecipes.get(subIndex); CraftTracker.LOGGER.debug("chosenSubRecipe: {}", DebugUtil.printRecipe(chosenSubRecipe)); + var computedSubRecipe = this.computeRecipe(chosenSubRecipe, amountRequired * iterations, depth + 1); + CraftTracker.LOGGER.debug("computedSubRecipe: {}", computedSubRecipe); + if(computedSubRecipe == null) { + CraftTracker.LOGGER.debug("computed sub-recipe for {} returned is null; treat as raw material", DebugUtil.printRecipe(chosenSubRecipe)); + // if the sub-recipe comes back null, then treat the result item as a raw material + computedRecipe.rawMaterials.compute(id, + (itemId, quantity) -> + ObjectUtils.defaultIfNull(quantity, new ComputedRecipeItem(itemId)) + .increase(amountRequired * iterations) + .tag(isTag)); + return; + } + computedRecipe.intermediateProducts.compute(id, (itemId, quantity) -> - ObjectUtils.defaultIfNull(quantity, 0) + needsQty); - - var computedSubRecipe = this.computeRecipe(chosenSubRecipe, amountRequired * iterations, depth + 1); + ObjectUtils.defaultIfNull(quantity, new ComputedRecipeItem(itemId)) + .increase(needsQty) + .tag(isTag)); // merge subrecipe items into this CraftTracker.LOGGER.debug("merging subrecipe contents: {} into this: {}", computedSubRecipe, computedRecipe); - computedSubRecipe.intermediateProducts.forEach((itemId, amount) -> { + computedSubRecipe.intermediateProducts.forEach((itemId, cri) -> { computedRecipe.intermediateProducts.compute(itemId, (k1, v1) -> { - return ObjectUtils.defaultIfNull(v1, 0) + amount; + return ObjectUtils.defaultIfNull(v1, new ComputedRecipeItem(k1)) + .increase(cri.amount) + .tag(cri.tag); }); }); - computedSubRecipe.rawMaterials.forEach((itemId, amount) -> { + computedSubRecipe.rawMaterials.forEach((itemId, cri) -> { computedRecipe.rawMaterials.compute(itemId, (k1, v1) -> { - return ObjectUtils.defaultIfNull(v1, 0) + amount; + return ObjectUtils.defaultIfNull(v1, new ComputedRecipeItem(k1)) + .increase(cri.amount) + .tag(cri.tag); }); }); - computedSubRecipe.fuel.forEach((itemId, amount) -> { + computedSubRecipe.fuel.forEach((itemId, cri) -> { computedRecipe.fuel.compute(itemId, (k1, v1) -> { - return ObjectUtils.defaultIfNull(v1, 0) + amount; + return ObjectUtils.defaultIfNull(v1, new ComputedRecipeItem(k1)) + .increase(cri.amount) + .tag(cri.tag); }); }); @@ -375,44 +425,12 @@ ComputedRecipe computeRecipe(Recipe recipe, int iterations, int depth) { return computedRecipe; } -// public static class ProductItem { -// private ResourceLocation itemId; -// private int iterations; -// private List categories; -// -// public ProductItem(ResourceLocation itemId, int iterations, List categories) { -// this.itemId = itemId; -// this.iterations = iterations; -// this.categories = categories; -// } -// -// public ResourceLocation getItemId() { -// return itemId; -// } -// -// public int getIterations() { -// return iterations; -// } -// -// public List getCategories() { -// return categories; -// } -// -// @Override -// public String toString() { -// return "ProductItem{" + -// "itemId=" + itemId + -// ", iterations=" + iterations + -// ", categories=" + categories + -// '}'; -// } -// } - public class QueueItem { private ResourceLocation itemId; + private boolean tag; private int quantity; - public QueueItem(ResourceLocation itemId, int quantity) { + public QueueItem(ResourceLocation itemId, boolean tag, int quantity) { this.itemId = itemId; this.quantity = quantity; } @@ -425,6 +443,14 @@ public void setItemId(ResourceLocation itemId) { this.itemId = itemId; } + public boolean isTag() { + return tag; + } + + public void setTag(boolean tag) { + this.tag = tag; + } + public int getQuantity() { return quantity; } @@ -464,11 +490,43 @@ public String toString() { } } + class ComputedRecipeItem { + ResourceLocation itemId; + int amount; + boolean tag; + + public ComputedRecipeItem(ResourceLocation itemId) { + this.itemId = itemId; + } + + public ComputedRecipeItem increase(int amount) { + this.amount += amount; + return this; + } + + public ComputedRecipeItem tag(boolean tag) { + this.tag = tag; + return this; + } + + @Override + public String toString() { + return MessageFormat.format(""" + ComputedRecipeItem[ + itemId={0} + amount={1} + tag={2} + ] + """, + itemId, amount, tag); + } + } + class ComputedRecipe { ResourceLocation recipeId; - Map intermediateProducts = new HashMap<>(); - Map rawMaterials = new HashMap<>(); - Map fuel = new HashMap<>(); + Map intermediateProducts = new HashMap<>(); + Map rawMaterials = new HashMap<>(); + Map fuel = new HashMap<>(); ComputedRecipe(ResourceLocation recipeId) { this.recipeId = recipeId; diff --git a/src/main/java/com/sweetrpg/crafttracker/common/model/CraftingQueueItem.java b/src/main/java/com/sweetrpg/crafttracker/common/model/CraftingQueueItem.java new file mode 100644 index 0000000..37a8584 --- /dev/null +++ b/src/main/java/com/sweetrpg/crafttracker/common/model/CraftingQueueItem.java @@ -0,0 +1,48 @@ +package com.sweetrpg.crafttracker.common.model; + +import net.minecraft.resources.ResourceLocation; + +public class CraftingQueueItem { + + ResourceLocation itemId; + int amount; + boolean tag; + + public CraftingQueueItem(ResourceLocation itemId, int amount, boolean tag) { + this.itemId = itemId; + this.amount = amount; + this.tag = tag; + } + + public CraftingQueueItem increment(int increase) { + this.amount += increase; + return this; + } + + public ResourceLocation getItemId() { + return itemId; + } + + public CraftingQueueItem setItemId(ResourceLocation itemId) { + this.itemId = itemId; + return this; + } + + public int getAmount() { + return amount; + } + + public CraftingQueueItem setAmount(int amount) { + this.amount = amount; + return this; + } + + public boolean isTag() { + return tag; + } + + public CraftingQueueItem setTag(boolean tag) { + this.tag = tag; + return this; + } +} diff --git a/src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java b/src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java index a72811f..94f779a 100644 --- a/src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java +++ b/src/main/java/com/sweetrpg/crafttracker/common/util/RecipeUtil.java @@ -9,8 +9,10 @@ import net.minecraft.util.Tuple; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.CraftingRecipe; import net.minecraft.world.item.crafting.Ingredient; import net.minecraft.world.item.crafting.Recipe; +import org.apache.commons.lang3.ObjectUtils; import java.io.IOException; import java.util.*; @@ -18,6 +20,9 @@ public class RecipeUtil { + public static final float NON_VANILLA_COST_MULTIPLIER = 1.2f; + public static final float NON_CRAFTING_COST_MULTIPLIER = 1.25f; + private static Map ingredientCostsByTag = new HashMap<>(); private static Map ingredientCostOverrides = new HashMap<>(); @@ -80,12 +85,41 @@ public static boolean areIngredientsSame(NonNullList ingredients) { return ing.size() == 1; } + public static boolean areIngredientsSameNamespace(String namespace, NonNullList ingredients) { + CraftTracker.LOGGER.debug("RecipeUtil#areIngredientsSame: {}", ingredients.stream().map(DebugUtil::printIngredient).toList()); + + Set ing = ingredients.stream() + .map(i -> Arrays.asList(i.getItems())) + .filter(l -> !l.isEmpty()) + .map(l -> l.get(0)) + .map(i -> ObjectUtils.defaultIfNull(i.getItem().getRegistryName().getNamespace(), "")) + .filter(n -> n.equals(namespace)) + .collect(Collectors.toSet()); + + return ing.size() == 1; + } + public static int calculateRecipeCost(Recipe recipe) { CraftTracker.LOGGER.debug("RecipeUtil#calculateRecipeCost: {}", DebugUtil.printRecipe(recipe)); - return recipe.getIngredients().stream() + int cost = recipe.getIngredients().stream() .map(RecipeUtil::calculateIngredientCost) .reduce(0, Integer::sum); + + // if the item's namespace is not 'minecraft:', increase the cost + if(!recipe.getId().getNamespace().equals("minecraft")) { + CraftTracker.LOGGER.debug("RecipeUtil#calculateRecipeCost: increasing cost ({}) of non-vanilla recipe {} by {}", + cost, DebugUtil.printRecipe(recipe), NON_VANILLA_COST_MULTIPLIER); + cost = (int) (cost * NON_VANILLA_COST_MULTIPLIER); + } + + if(!(recipe instanceof CraftingRecipe)) { + CraftTracker.LOGGER.debug("RecipeUtil#calculateRecipeCost: increasing cost ({}) of non-crafting table recipe {} by {}", + cost, DebugUtil.printRecipe(recipe), NON_CRAFTING_COST_MULTIPLIER); + cost = (int) (cost * NON_VANILLA_COST_MULTIPLIER); + } + + return cost; } public static int calculateIngredientCost(Ingredient ingredient) { @@ -107,7 +141,16 @@ public static int calculateIngredientCost(Ingredient ingredient) { var tagId = tag.location(); if(ingredientCostsByTag.containsKey(tagId)) { CraftTracker.LOGGER.debug("found item {} in tag list", tagId); - var cost = ingredientCostsByTag.get(tagId) * count; + int cost = ingredientCostsByTag.get(tagId) * count; + + // if the item's namespace is not 'minecraft:', increase the cost + if(!ObjectUtils.defaultIfNull(stack.getItem().getRegistryName().getNamespace(), "").equals("minecraft") && + !ObjectUtils.defaultIfNull(tagId.getNamespace(), "").equals("minecraft")) { + CraftTracker.LOGGER.debug("RecipeUtil#calculateRecipeCost: increasing cost ({}) of non-vanilla item {} by {}", + cost, tagId, NON_VANILLA_COST_MULTIPLIER); + cost = (int) (cost * NON_VANILLA_COST_MULTIPLIER); + } + if(cost > highestCost) { highestCost = cost; } @@ -139,9 +182,18 @@ public static int calculateItemCost(ItemStack stack) { var tagId = tag.location(); if(ingredientCostsByTag.containsKey(tagId)) { CraftTracker.LOGGER.debug("found item {} in tag list", tagId); - var cost = ingredientCostsByTag.get(tagId) * count; + int cost = ingredientCostsByTag.get(tagId) * count; + + // if the item's namespace is not 'minecraft:', increase the cost + if(!ObjectUtils.defaultIfNull(stack.getItem().getRegistryName().getNamespace(), "").equals("minecraft") && + !ObjectUtils.defaultIfNull(tagId.getNamespace(), "").equals("minecraft")) { + CraftTracker.LOGGER.debug("RecipeUtil#calculateItemCost: increasing cost ({}) of non-vanilla item {} by {}", + cost, tagId, NON_VANILLA_COST_MULTIPLIER); + cost = (int) (cost * NON_VANILLA_COST_MULTIPLIER); + } + if(cost > highestCost) { - highestCost = cost; + highestCost = (int) cost; } } } diff --git a/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java b/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java index 2493c69..c7c4069 100644 --- a/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java +++ b/src/main/java/com/sweetrpg/crafttracker/data/CTLangProvider.java @@ -33,10 +33,10 @@ private void processENUS() { add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_TITLE, "Craft Queue"); add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_EMPTY, "The queue is empty."); add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_HELP, "To manage the queue:"); - add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_PRODUCTS, "Products"); - add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_INTERMEDIATES, "Intermediates"); - add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_MATERIALS, "Materials"); - add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_FUEL, "Fuel"); + add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_PRODUCTS, "To make:"); + add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_INTERMEDIATES, "You first need to make:"); + add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_MATERIALS, "With these materials:"); + add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_FUEL, "And this fuel:"); add(Constants.TRANSLATION_KEY_GUI_HAVE, "have %d"); add(Constants.TRANSLATION_KEY_GUI_SHOPPING_LIST_TITLE, "Shopping List"); add(Constants.TRANSLATION_KEY_GUI_SHOPPING_LIST_EMPTY, "The shopping list is empty."); @@ -78,10 +78,10 @@ private void processENGB() { add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_TITLE, "Craft Queue"); add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_EMPTY, "The queue is empty."); add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_HELP, "To manage the queue:"); - add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_PRODUCTS, "Products"); - add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_INTERMEDIATES, "Intermediates"); - add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_MATERIALS, "Materials"); - add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_FUEL, "Fuel"); + add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_PRODUCTS, "To make:"); + add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_INTERMEDIATES, "You first need to make:"); + add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_MATERIALS, "With these materials:"); + add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_FUEL, "And this fuel:"); add(Constants.TRANSLATION_KEY_GUI_HAVE, "have %d"); add(Constants.TRANSLATION_KEY_GUI_SHOPPING_LIST_TITLE, "Shopping List"); add(Constants.TRANSLATION_KEY_GUI_SHOPPING_LIST_EMPTY, "The shopping list is empty."); @@ -123,10 +123,10 @@ private void processDEDE() { add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_TITLE, "Herstellungswarteschlange"); add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_EMPTY, "Die Warteschlange ist leer."); add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_HELP, "Die Warteschlange verwalten:"); - add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_PRODUCTS, "Produkte"); - add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_INTERMEDIATES, "Zwischenprodukte"); - add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_MATERIALS, "Materialien"); - add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_FUEL, "Kraftstoff"); + add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_PRODUCTS, "Um zu machen:"); + add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_INTERMEDIATES, "Sie müssen zunächst Folgendes tun:"); + add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_MATERIALS, "Mit diesen Materialien:"); + add(Constants.TRANSLATION_KEY_GUI_CRAFT_QUEUE_SECTION_FUEL, "Und dieser Treibstoff:"); add(Constants.TRANSLATION_KEY_GUI_HAVE, "habe %d"); add(Constants.TRANSLATION_KEY_GUI_SHOPPING_LIST_TITLE, "Einkaufsliste"); add(Constants.TRANSLATION_KEY_GUI_SHOPPING_LIST_EMPTY, "The shopping list is empty."); diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index 3760722..b772753 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -3,3 +3,6 @@ public net.minecraft.client.gui.screens.Screen f_96545_ # buttons # RecipeManager public net.minecraft.world.item.crafting.RecipeManager m_44054_(Lnet/minecraft/world/item/crafting/RecipeType;)Ljava/util/Map; # byType + +# Ingredient +public net.minecraft.world.item.crafting.Ingredient f_43902_ # values