diff --git a/Common/src/main/java/at/petrak/hexcasting/interop/patchouli/BrainsweepProcessor.java b/Common/src/main/java/at/petrak/hexcasting/interop/patchouli/BrainsweepProcessor.java index ada9cc4165..576e82bb42 100644 --- a/Common/src/main/java/at/petrak/hexcasting/interop/patchouli/BrainsweepProcessor.java +++ b/Common/src/main/java/at/petrak/hexcasting/interop/patchouli/BrainsweepProcessor.java @@ -1,18 +1,23 @@ package at.petrak.hexcasting.interop.patchouli; +import at.petrak.hexcasting.api.misc.MediaConstants; +import at.petrak.hexcasting.common.lib.HexItems; import at.petrak.hexcasting.common.recipe.BrainsweepRecipe; import at.petrak.hexcasting.common.recipe.HexRecipeStuffRegistry; import net.minecraft.client.Minecraft; -import net.minecraft.core.Registry; import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.nbt.CompoundTag; import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraft.world.level.Level; import org.jetbrains.annotations.Nullable; import vazkii.patchouli.api.IComponentProcessor; import vazkii.patchouli.api.IVariable; import vazkii.patchouli.api.IVariableProvider; +import java.util.Arrays; +import java.util.List; public class BrainsweepProcessor implements IComponentProcessor { private BrainsweepRecipe recipe; @@ -77,6 +82,31 @@ public IVariable process(Level level, String key) { .map(IVariable::from) .toList()); } + case "mediaCost" -> { + record ItemCost(Item item, int cost) { + public boolean dividesEvenly (int dividend) { + return dividend % cost == 0; + } + } + ItemCost[] costs = { + new ItemCost(HexItems.AMETHYST_DUST, (int)MediaConstants.DUST_UNIT), + new ItemCost(Items.AMETHYST_SHARD, (int)MediaConstants.SHARD_UNIT), + new ItemCost(HexItems.CHARGED_AMETHYST, (int)MediaConstants.CRYSTAL_UNIT), + }; + + // get evenly divisible ItemStacks + List validItemStacks = Arrays.stream(costs) + .filter(itemCost -> itemCost.dividesEvenly((int)this.recipe.mediaCost())) + .map(validItemCost -> new ItemStack(validItemCost.item, (int) this.recipe.mediaCost() / validItemCost.cost)) + .map(IVariable::from) + .toList(); + + if (!validItemStacks.isEmpty()) { + return IVariable.wrapList(validItemStacks); + } + // fallback: display in terms of dust + return IVariable.from(new ItemStack(HexItems.AMETHYST_DUST, (int) (this.recipe.mediaCost() / MediaConstants.DUST_UNIT))); + } default -> { return null; } diff --git a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 index cd0a30aa92..2eb861e346 100644 --- a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 +++ b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 @@ -1600,8 +1600,8 @@ quenching_allays: { "1": "THEY ARE BITS OF MEDIA. How did I not see it sooner? They are -- as I am a heap of flesh with a scrap, blessed with a scrap of thought, an Allay is a self-sustaining quarrel of media pinned to a scrap of flesh. It explains everything -- their propensity for media, their response to music, I SEE NOW, HOW did the ones before NOT?", "2": "And given this it is only RIGHT I conquer their peculiar minds -- their peculiar selves -- that is all they are, a mind, a self, a coda. Something about their phase speaks to me. I can... I can compress _media with them, overlay two wends of thought in one space, physical and cognitive, all and once.$(br2)Somehow, the process produces _media of its own. How? Perhaps -- perhaps MY work, the process of doing it --", - "3": "It matters not. I matter not. They matter not, all that matters is what it does. And this is it.$(br2)It must hurt so very much.", - "4": "The product is fragile. Breaking it shatters it into pieces, with $(thing)Fortune/$ increasing the yield... if I wish the block itself I need a silken touch.$(br2)The produced shards are worth thrice a $(l:items/amethyst)$(item)Charged Amethyst Crystal/$ apiece. The block itself is worth four of the shards.", + "3": "It matters not. I matter not. They matter not, all that matters is what it does. And this is it.$(br2)It must hurt so very much.$(br2)Ten $(l:items/amethyst)$(item)Amethyst Dust/$ is the price to enact such a perverse ritual.", + "4": "The product is fragile. Breaking it shatters it into pieces, with $(thing)Fortune/$ increasing the yield... if I wish the block itself I need a silken touch.$(br2)The produced shards are worth thrice an $(l:items/amethyst)$(item)Charged Amethyst Crystal/$ apiece. The block itself is worth four of the shards.", "5": "They are mercurial, they seem to twist and wink under my fingers, and by giving them a mentor in another form of _media they may be coerced into its shape, in an equivalent exchange of _media.", }, diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/templates/brainsweep.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/templates/brainsweep.json index 6be171dfd8..c7081d0b5b 100644 --- a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/templates/brainsweep.json +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/templates/brainsweep.json @@ -37,6 +37,12 @@ "y": 35, "item": "#input" }, + { + "type": "patchouli:item", + "x": 12, + "y": 55, + "item": "#mediaCost" + }, { "type": "patchouli:item", "x": 87, diff --git a/doc/src/hexdoc_hexcasting/_templates/index.css.jinja b/doc/src/hexdoc_hexcasting/_templates/index.css.jinja index d609633a5d..eeb0602801 100644 --- a/doc/src/hexdoc_hexcasting/_templates/index.css.jinja +++ b/doc/src/hexdoc_hexcasting/_templates/index.css.jinja @@ -17,6 +17,12 @@ left: 24px; } +.flay-recipe-cost{ + position: absolute; + top: 110px; + left: 24px; +} + /* entity chamber starts at top: 38px & left: 74px. it is 52px wide & 96px tall */ .flay-recipe-entity{ position: absolute; diff --git a/doc/src/hexdoc_hexcasting/_templates/recipes/hexcasting/brainsweep.html.jinja b/doc/src/hexdoc_hexcasting/_templates/recipes/hexcasting/brainsweep.html.jinja index 69bad23a85..8b6e9439a3 100644 --- a/doc/src/hexdoc_hexcasting/_templates/recipes/hexcasting/brainsweep.html.jinja +++ b/doc/src/hexdoc_hexcasting/_templates/recipes/hexcasting/brainsweep.html.jinja @@ -34,6 +34,16 @@ {% endblock ingredient %} + {% block cost %} +
+ {% for item in recipe.cost_items %} +
+ {{ texture_macros.render_item(item|hexdoc_item, count=item.count, is_first=true) }} +
+ {% endfor %} +
+ {% endblock cost %} + {% block result %}
{{ texture_macros.render_item(recipe.result.name) }} diff --git a/doc/src/hexdoc_hexcasting/book/recipes.py b/doc/src/hexdoc_hexcasting/book/recipes.py index d99c2cdfc0..1389f7e923 100644 --- a/doc/src/hexdoc_hexcasting/book/recipes.py +++ b/doc/src/hexdoc_hexcasting/book/recipes.py @@ -1,12 +1,17 @@ from abc import ABC, abstractmethod from typing import Any, Literal, Self -from hexdoc.core import IsVersion, ResourceLocation +from hexdoc.core import IsVersion, ItemStack, ResourceLocation from hexdoc.minecraft.assets import ItemWithTexture, PNGTexture from hexdoc.minecraft.i18n import I18n, LocalizedStr from hexdoc.minecraft.recipe import ItemIngredient, ItemIngredientList, Recipe from hexdoc.model import HexdocModel, TypeTaggedTemplate from hexdoc.utils import NoValue, classproperty +from hexdoc_hexcasting.utils.constants import ( + MEDIA_CRYSTAL_UNIT, + MEDIA_DUST_UNIT, + MEDIA_SHARD_UNIT, +) from pydantic import Field, PrivateAttr, ValidationInfo, model_validator # ingredients @@ -139,16 +144,44 @@ def brainsweepee(self) -> Any: For example, `BrainsweepRecipe_0_11` returns `entityIn`. """ + @property + @abstractmethod + def cost(self) -> int: + """Returns the cost of this recipe in raw media units.""" + + @property + def cost_items(self) -> list[ItemStack]: + """Returns the items to display for the recipe's cost.""" + + costs = [ + ("hexcasting", "amethyst_dust", MEDIA_DUST_UNIT), + ("minecraft", "amethyst_shard", MEDIA_SHARD_UNIT), + ("hexcasting", "charged_amethyst", MEDIA_CRYSTAL_UNIT), + ] + + return [ + ItemStack(namespace, path, self.cost // media) + for namespace, path, media in costs + if self.cost % media == 0 + ] or [ + # fallback if nothing divides evenly + ItemStack("hexcasting", "amethyst_dust", self.cost // MEDIA_DUST_UNIT), + ] + @IsVersion(">=1.20") class BrainsweepRecipe_0_11(BrainsweepRecipe, type="hexcasting:brainsweep"): - cost: int + cost_: int = Field(alias="cost") entityIn: BrainsweepeeIngredient @property def brainsweepee(self): return self.entityIn + @property + def cost(self): + return self.cost_ + @IsVersion("<1.20") class BrainsweepRecipe_0_10(BrainsweepRecipe, type="hexcasting:brainsweep"): @@ -157,3 +190,7 @@ class BrainsweepRecipe_0_10(BrainsweepRecipe, type="hexcasting:brainsweep"): @property def brainsweepee(self): return self.villagerIn + + @property + def cost(self): + return 10 * MEDIA_CRYSTAL_UNIT diff --git a/doc/src/hexdoc_hexcasting/utils/constants.py b/doc/src/hexdoc_hexcasting/utils/constants.py new file mode 100644 index 0000000000..89b296105b --- /dev/null +++ b/doc/src/hexdoc_hexcasting/utils/constants.py @@ -0,0 +1,6 @@ +MEDIA_DUST_UNIT = 10000 +MEDIA_SHARD_UNIT = 5 * MEDIA_DUST_UNIT +MEDIA_CRYSTAL_UNIT = 10 * MEDIA_DUST_UNIT + +MEDIA_QUENCHED_SHARD_UNIT = 3 * MEDIA_CRYSTAL_UNIT +MEDIA_QUENCHED_BLOCK_UNIT = 4 * MEDIA_QUENCHED_SHARD_UNIT