From caa5464641a9b19d4a38202bc811e8f2a77c60ca Mon Sep 17 00:00:00 2001 From: DBotThePony Date: Wed, 9 Nov 2022 19:46:44 +0700 Subject: [PATCH] Recipe backtracking --- .../mc/otm/matter/RecipeResolverManager.kt | 62 +++++++++++++++++-- 1 file changed, 57 insertions(+), 5 deletions(-) diff --git a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/RecipeResolverManager.kt b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/RecipeResolverManager.kt index 9bb33b81b..35e2b1a16 100644 --- a/src/main/kotlin/ru/dbotthepony/mc/otm/matter/RecipeResolverManager.kt +++ b/src/main/kotlin/ru/dbotthepony/mc/otm/matter/RecipeResolverManager.kt @@ -65,7 +65,19 @@ object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().se * if we can't resolve one of ingredient's matter value - consider output also having no matter value */ val isCritical: Boolean = true, - val name: ResourceLocation? = null + val name: ResourceLocation? = null, + + /** + * Whenever to allow "backtracking" recipe ingredients to recipe outputs. + * + * When an item has no recipes, but takes part in recipes with output of already determined matter value + * (e.g. 4 rabbit hides -> 1 leather), we can assume that we can determine matter value of ingredients based on matter value of output + * + * "Backtrack" can happen if everything of the next holds true: + * * All recipes with item in question contains only that item; and + * * All recipes allow backtracking + */ + val allowBacktrack: Boolean = true, ) { val inputs: List> = inputs .map { it.filter { it.multiplier > 0.0 }.collect(ImmutableList.toImmutableList()) } @@ -92,6 +104,8 @@ object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().se * * Internally, OTM separate parallel streams from sequential so sequential streams happen strictly on server thread * (if you can't avoid side effects, or don't bother with synchronization). + * + * You can safely call [MatterManager.getDirect] inside returned stream, both parallel and sequential. */ fun find(server: MinecraftServer, json: JsonObject): Stream } @@ -118,6 +132,7 @@ object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().se val isCritical = data["is_critical"]?.asBoolean ?: true val ignoreDamageables = data["ignore_damageables"]?.asBoolean ?: false + val allowBacktrack = data["allow_backtrack"]?.asBoolean ?: true server.recipeManager.byType(findRecipeType).values.parallelStream() .filter { !it.isIncomplete && !it.ingredients.stream().anyMatch { it.isActuallyEmpty } } // get rid of invalid recipes, second "isActuallyEmpty" is required because we do care about ingredients being "missing" @@ -129,7 +144,8 @@ object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().se it.ingredients.stream().map { Arrays.stream(it.items).map { ImmutableStack(it) } }, ImmutableStack(it.resultItem), isCritical = isCritical, - name = it.id + name = it.id, + allowBacktrack = allowBacktrack ) } .filter { it.inputs.isNotEmpty() } @@ -291,9 +307,45 @@ object RecipeResolverManager : SimpleJsonResourceReloadListener(GsonBuilder().se val recipes = output2Recipes[item] - if (recipes == null) { - commentary[item] = TextComponent("Item '${item.registryName}' has no recipes") - return Result.MISSING + if (recipes == null || recipes.isEmpty()) { + val recipes2 = input2Recipes[item] + + if (recipes2 == null || recipes2.isEmpty() || !recipes2.all { it.allowBacktrack } || !recipes2.all { it.inputs.all { it.all { it.item == item } } }) { + commentary[item] = TextComponent("Item '${item.registryName}' has no recipes") + return Result.MISSING + } + + var minimal: IMatterValue? = null + var minimalMultiplier = 0.0 + var foundRecipe: ResolvedRecipe? = null + + for (recipe in recipes2) { + val value = MatterManager.getDirect(recipe.output.item) + + if (value.hasMatterValue) { + if (minimal == null || minimal < value) { + minimal = value + minimalMultiplier = recipe.output.multiplier / recipe.inputs.size + foundRecipe = recipe + } + } else { + minimal = null + break + } + } + + if (minimal == null) { + commentary[item] = TextComponent("Item '${item.registryName}' has no valid backtracking recipes, and no output recipes") + return Result.MISSING + } + + val result = MatterValue(minimal.matter * minimalMultiplier, minimal.complexity * minimalMultiplier) + + changes = true + determinedValues[item] = result + commentary[item] = TextComponent("Matter value backtracked from ${foundRecipe!!.formattedName}") + + return Result(result) } var hadSkips = false