diff --git a/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronBlock.java b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronBlock.java index 32e48ee11..e905b0a2a 100644 --- a/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronBlock.java +++ b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronBlock.java @@ -18,7 +18,10 @@ import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.LevelAccessor; -import net.minecraft.world.level.block.*; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.RenderShape; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.entity.BlockEntityTicker; import net.minecraft.world.level.block.entity.BlockEntityType; @@ -26,7 +29,6 @@ import net.minecraft.world.level.block.state.StateDefinition; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.minecraft.world.level.block.state.properties.BooleanProperty; -import net.minecraft.world.level.block.state.properties.IntegerProperty; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.shapes.BooleanOp; import net.minecraft.world.phys.shapes.CollisionContext; @@ -38,7 +40,7 @@ public class AlchemistCauldronBlock extends BaseEntityBlock { public AlchemistCauldronBlock() { super(Properties.copy(Blocks.CAULDRON).lightLevel((blockState) -> 3)); - this.registerDefaultState(this.stateDefinition.any().setValue(LIT, false).setValue(LEVEL, 0)); + this.registerDefaultState(this.stateDefinition.any().setValue(LIT, true)/*.setValue(LEVEL, 0)*/); } @@ -53,8 +55,8 @@ public AlchemistCauldronBlock() { private static final VoxelShape SHAPE = Shapes.or(Shapes.or(box(0, 0, 4, 16, 2, 6), box(0, 0, 10, 16, 2, 12), box(4, 0, 0, 6, 2, 16), box(10, 0, 0, 12, 2, 16)), Shapes.join(Shapes.or(Shapes.join(box(0, 2, 0, 16, 16, 16), box(0, 12, 0, 16, 14, 16), BooleanOp.ONLY_FIRST), box(1, 12, 1, 15, 14, 15)), box(2, 4, 2, 14, 16, 14), BooleanOp.ONLY_FIRST)); public static final BooleanProperty LIT = BlockStateProperties.LIT; - public static final int MAX_LEVELS = 4; - public static final IntegerProperty LEVEL = IntegerProperty.create("level", 0, MAX_LEVELS); + //public static final int MAX_LEVELS = 4; + //public static final IntegerProperty LEVEL = IntegerProperty.create("level", 0, MAX_LEVELS); @Override public BlockEntityTicker getTicker(Level pLevel, BlockState pState, BlockEntityType pBlockEntityType) { @@ -68,7 +70,7 @@ protected static BlockEntityTicker createTicker(Level @Override protected void createBlockStateDefinition(StateDefinition.Builder builder) { - builder.add(LIT, LEVEL); + builder.add(LIT/*, LEVEL*/); } public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) { @@ -76,27 +78,27 @@ public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, } @Override - public InteractionResult use(BlockState blockState, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult blockHit) { + public InteractionResult use(BlockState pState, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult pHit) { if (level.getBlockEntity(pos) instanceof AlchemistCauldronTile tile) { - return tile.handleUse(blockState, level, pos, player, hand); + return tile.handleUse(level.getBlockState(pos), level, pos, player, hand); } - return super.use(blockState, level, pos, player, hand, blockHit); + return super.use(pState, level, pos, player, hand, pHit); } @Override public void entityInside(BlockState blockState, Level level, BlockPos pos, Entity entity) { if (entity.tickCount % 20 == 0) { - if (isBoiling(blockState)) { - if (entity instanceof LivingEntity livingEntity && livingEntity.hurt(DamageSources.get(level, ISSDamageTypes.CAULDRON), 2)) { - MagicManager.spawnParticles(level, ParticleHelper.BLOOD, entity.getX(), entity.getY() + entity.getBbHeight() / 2, entity.getZ(), 20, .05, .05, .05, .1, false); - if (level.getBlockEntity(pos) instanceof AlchemistCauldronTile cauldronTile) { - AlchemistCauldronTile.appendItem(cauldronTile.resultItems, new ItemStack(ItemRegistry.BLOOD_VIAL.get())); - cauldronTile.setChanged(); + if (level.getBlockEntity(pos) instanceof AlchemistCauldronTile cauldronTile) { + if (AlchemistCauldronBlock.isLit(blockState)) { + if (entity instanceof LivingEntity livingEntity && livingEntity.hurt(DamageSources.get(level, ISSDamageTypes.CAULDRON), 2)) { + MagicManager.spawnParticles(level, ParticleHelper.BLOOD, entity.getX(), entity.getY() + entity.getBbHeight() / 2, entity.getZ(), 20, .05, .05, .05, .1, false); + if (cauldronTile.addToOutput(new ItemStack(ItemRegistry.BLOOD_VIAL.get()))) { + cauldronTile.setChanged(); + } } } } } - super.entityInside(blockState, level, pos, entity); } @@ -150,12 +152,4 @@ public static boolean isLit(BlockState blockState) { return blockState.hasProperty(LIT) && blockState.getValue(LIT); } - public static int getLevel(BlockState blockState) { - return blockState.hasProperty(LEVEL) ? blockState.getValue(LEVEL) : 0; - } - - public static boolean isBoiling(BlockState blockState) { - return isLit(blockState) && getLevel(blockState) > 0; - } - } diff --git a/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronBuildInteractionsEvent.java b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronBuildInteractionsEvent.java new file mode 100644 index 000000000..f051e55bc --- /dev/null +++ b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronBuildInteractionsEvent.java @@ -0,0 +1,25 @@ +package io.redspace.ironsspellbooks.block.alchemist_cauldron; + +import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; +import net.minecraft.world.item.Item; +import net.minecraftforge.eventbus.api.Event; + +public class AlchemistCauldronBuildInteractionsEvent extends Event { + private final Object2ObjectOpenHashMap interactionMap; + + public AlchemistCauldronBuildInteractionsEvent(Object2ObjectOpenHashMap interactionMap) { + this.interactionMap = interactionMap; + } + + public void addInteraction(Item item, AlchemistCauldronInteraction interaction) { + if (!interactionMap.containsKey(item)) { + interactionMap.put(item, interaction); + } + } + + public void addSimpleBottleEmptyInteraction(Item item) { + if (!interactionMap.containsKey(item)) { + AlchemistCauldronTile.createBottleEmptyInteraction(interactionMap, () -> item); + } + } +} diff --git a/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronInteraction.java b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronInteraction.java index af9ae7c26..8b76b01bc 100644 --- a/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronInteraction.java +++ b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronInteraction.java @@ -1,16 +1,13 @@ package io.redspace.ironsspellbooks.block.alchemist_cauldron; import net.minecraft.core.BlockPos; -import net.minecraft.world.InteractionHand; -import net.minecraft.world.InteractionResult; -import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.state.BlockState; import javax.annotation.Nullable; -public interface AlchemistCauldronInteraction{ +public interface AlchemistCauldronInteraction { @Nullable - ItemStack interact(BlockState blockState, Level level, BlockPos pos, int currentLevel, ItemStack itemStack); + ItemStack interact(AlchemistCauldronTile blockEntity, BlockState blockState, Level level, BlockPos pos, ItemStack itemStack); } diff --git a/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronRecipe.java b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronRecipe.java index ef5213556..9220f2840 100644 --- a/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronRecipe.java +++ b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronRecipe.java @@ -1,19 +1,13 @@ package io.redspace.ironsspellbooks.block.alchemist_cauldron; -import com.google.common.collect.ImmutableList; -import io.redspace.ironsspellbooks.IronsSpellbooks; -import io.redspace.ironsspellbooks.registries.ItemRegistry; -import io.redspace.ironsspellbooks.registries.PotionRegistry; -import net.minecraft.core.NonNullList; +import io.redspace.ironsspellbooks.api.util.Utils; +import net.minecraft.core.Holder; import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.alchemy.Potion; import net.minecraft.world.item.alchemy.PotionUtils; -import java.util.ArrayList; -import java.util.List; - public class AlchemistCauldronRecipe { //Base is the item inside the cauldron being acted on @@ -48,9 +42,9 @@ public AlchemistCauldronRecipe setResultLimit(int i) { return this; } - public ItemStack createOutput(ItemStack input, ItemStack ingredient, boolean consumeOnSuccess) { - if (ItemStack.isSameItemSameTags(input, this.inputStack) && ItemStack.isSameItemSameTags(ingredient, this.ingredientStack)) { - if (input.getCount() >= this.requiredBaseCount) { + public ItemStack createOutput(ItemStack input, ItemStack ingredient, boolean ignoreCount, boolean consumeOnSuccess) { + if (CauldronPlatformHelper.itemMatches(input, this.inputStack) && CauldronPlatformHelper.itemMatches(ingredient, this.ingredientStack)) { + if (ignoreCount || input.getCount() >= this.requiredBaseCount) { ItemStack result = this.resultStack.copy(); result.setCount(this.resultLimitCount); if (consumeOnSuccess) { diff --git a/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronRecipeRegistry.java b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronRecipeRegistry.java index a47d92c85..fe5eb210b 100644 --- a/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronRecipeRegistry.java +++ b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronRecipeRegistry.java @@ -4,16 +4,18 @@ import io.redspace.ironsspellbooks.IronsSpellbooks; import io.redspace.ironsspellbooks.registries.ItemRegistry; import io.redspace.ironsspellbooks.registries.PotionRegistry; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; -import net.minecraft.world.item.alchemy.PotionUtils; import net.minecraft.world.item.alchemy.Potions; +import net.minecraftforge.registries.ForgeRegistries; +import org.jetbrains.annotations.Nullable; -import java.util.ArrayList; -import java.util.List; +import java.util.HashMap; +import java.util.Map; public class AlchemistCauldronRecipeRegistry { - private static final List recipes = new ArrayList<>(); + private static final Map recipes = new HashMap<>(); static { //No cool recipes for right now :( @@ -22,38 +24,57 @@ public class AlchemistCauldronRecipeRegistry { //recipes.add(new AlchemistCauldronRecipe(ItemRegistry.INK_EPIC.get(), Items.OBSIDIAN, Items.CRYING_OBSIDIAN)); //recipes.add(new AlchemistCauldronRecipe(ItemRegistry.INK_LEGENDARY.get(), Items.BLUE_ORCHID, Items.DANDELION).setBaseRequirement(2).setResultLimit(2)); // recipes.add(new AlchemistCauldronRecipe(PotionRegistry.INSTANT_MANA_ONE.get(), Items.FLOWERING_AZALEA_LEAVES, ItemRegistry.CASTERS_TEA.get())); - recipes.add(new AlchemistCauldronRecipe(Potions.STRONG_HEALING, Items.OAK_LOG, ItemRegistry.OAKSKIN_ELIXIR.get()).setBaseRequirement(2).setResultLimit(1)); - recipes.add(new AlchemistCauldronRecipe(ItemRegistry.OAKSKIN_ELIXIR.get(), Items.AMETHYST_SHARD, ItemRegistry.GREATER_OAKSKIN_ELIXIR.get()).setBaseRequirement(2).setResultLimit(1)); - recipes.add(new AlchemistCauldronRecipe(Potions.STRONG_HEALING, Items.AMETHYST_SHARD, ItemRegistry.GREATER_HEALING_POTION.get()).setBaseRequirement(4).setResultLimit(1)); - recipes.add(new AlchemistCauldronRecipe(Potions.INVISIBILITY, ItemRegistry.SHRIVING_STONE.get(), ItemRegistry.INVISIBILITY_ELIXIR.get()).setBaseRequirement(4).setResultLimit(1)); - recipes.add(new AlchemistCauldronRecipe(Potions.LONG_INVISIBILITY, ItemRegistry.SHRIVING_STONE.get(), ItemRegistry.INVISIBILITY_ELIXIR.get()).setBaseRequirement(4).setResultLimit(1)); - recipes.add(new AlchemistCauldronRecipe(ItemRegistry.INVISIBILITY_ELIXIR.get(), Items.AMETHYST_CLUSTER, ItemRegistry.GREATER_INVISIBILITY_ELIXIR.get())); - recipes.add(new AlchemistCauldronRecipe(PotionRegistry.INSTANT_MANA_THREE.get(), Items.ENDER_PEARL, ItemRegistry.EVASION_ELIXIR.get()).setBaseRequirement(4).setResultLimit(1)); - recipes.add(new AlchemistCauldronRecipe(ItemRegistry.EVASION_ELIXIR.get(), Items.DRAGON_BREATH, ItemRegistry.GREATER_EVASION_ELIXIR.get())); - recipes.add(new AlchemistCauldronRecipe(ItemRegistry.INK_COMMON.get(), Items.COPPER_INGOT, ItemRegistry.INK_UNCOMMON.get()).setBaseRequirement(4).setResultLimit(1)); - recipes.add(new AlchemistCauldronRecipe(ItemRegistry.INK_UNCOMMON.get(), Items.IRON_INGOT, ItemRegistry.INK_RARE.get()).setBaseRequirement(4).setResultLimit(1)); - recipes.add(new AlchemistCauldronRecipe(ItemRegistry.INK_RARE.get(), Items.GOLD_INGOT, ItemRegistry.INK_EPIC.get()).setBaseRequirement(4).setResultLimit(1)); - recipes.add(new AlchemistCauldronRecipe(ItemRegistry.INK_EPIC.get(), Items.AMETHYST_SHARD, ItemRegistry.INK_LEGENDARY.get()).setBaseRequirement(4).setResultLimit(1)); + addRecipe(new AlchemistCauldronRecipe(Potions.STRONG_HEALING, Items.OAK_LOG, ItemRegistry.OAKSKIN_ELIXIR.get()).setBaseRequirement(2).setResultLimit(1)); + addRecipe(new AlchemistCauldronRecipe(ItemRegistry.OAKSKIN_ELIXIR.get(), Items.AMETHYST_SHARD, ItemRegistry.GREATER_OAKSKIN_ELIXIR.get()).setBaseRequirement(2).setResultLimit(1)); + addRecipe(new AlchemistCauldronRecipe(Potions.STRONG_HEALING, Items.AMETHYST_SHARD, ItemRegistry.GREATER_HEALING_POTION.get()).setBaseRequirement(4).setResultLimit(1)); + addRecipe(new AlchemistCauldronRecipe(Potions.INVISIBILITY, ItemRegistry.SHRIVING_STONE.get(), ItemRegistry.INVISIBILITY_ELIXIR.get()).setBaseRequirement(4).setResultLimit(1)); + addRecipe(new AlchemistCauldronRecipe(Potions.LONG_INVISIBILITY, ItemRegistry.SHRIVING_STONE.get(), ItemRegistry.INVISIBILITY_ELIXIR.get()).setBaseRequirement(4).setResultLimit(1)); + addRecipe(new AlchemistCauldronRecipe(ItemRegistry.INVISIBILITY_ELIXIR.get(), Items.AMETHYST_CLUSTER, ItemRegistry.GREATER_INVISIBILITY_ELIXIR.get())); + addRecipe(new AlchemistCauldronRecipe(PotionRegistry.INSTANT_MANA_THREE.get(), Items.ENDER_PEARL, ItemRegistry.EVASION_ELIXIR.get()).setBaseRequirement(4).setResultLimit(1)); + addRecipe(new AlchemistCauldronRecipe(ItemRegistry.EVASION_ELIXIR.get(), Items.DRAGON_BREATH, ItemRegistry.GREATER_EVASION_ELIXIR.get())); + addRecipe(new AlchemistCauldronRecipe(ItemRegistry.INK_COMMON.get(), Items.COPPER_INGOT, ItemRegistry.INK_UNCOMMON.get()).setBaseRequirement(4).setResultLimit(1)); + addRecipe(new AlchemistCauldronRecipe(ItemRegistry.INK_UNCOMMON.get(), Items.IRON_INGOT, ItemRegistry.INK_RARE.get()).setBaseRequirement(4).setResultLimit(1)); + addRecipe(new AlchemistCauldronRecipe(ItemRegistry.INK_RARE.get(), Items.GOLD_INGOT, ItemRegistry.INK_EPIC.get()).setBaseRequirement(4).setResultLimit(1)); + addRecipe(new AlchemistCauldronRecipe(ItemRegistry.INK_EPIC.get(), Items.AMETHYST_SHARD, ItemRegistry.INK_LEGENDARY.get()).setBaseRequirement(4).setResultLimit(1)); } /** - * If any modder is crazy enough to want to use this, do it during FMLCommonSetup Event + * Use this during FMLCommonSetup Event for custom recipes while better API handling is WIP */ - public static AlchemistCauldronRecipe addRecipe(AlchemistCauldronRecipe recipe) { - recipes.add(recipe); + public static AlchemistCauldronRecipe registerRecipe(ResourceLocation resourceLocation, AlchemistCauldronRecipe recipe) { + recipes.put(resourceLocation, recipe); + return recipe; + } + + private static AlchemistCauldronRecipe addRecipe(AlchemistCauldronRecipe recipe) { + recipes.put(IronsSpellbooks.id(ForgeRegistries.ITEMS.getKey(recipe.getResult().getItem()).getPath()), recipe); return recipe; } /** * Searches through registered recipes, and returns the resulting item or ItemStack.EMPTY if there are no matches. - * It is expected for input to have a consolidated count, and the result can have a count > 1 + * Input without the required count will return negative, and the result can have a count > 1 */ public static ItemStack getOutput(ItemStack input, ItemStack ingredient, boolean consumeOnSucces) { if (input.isEmpty() || ingredient.isEmpty()) return ItemStack.EMPTY; - for (AlchemistCauldronRecipe recipe : recipes) { - ItemStack result = recipe.createOutput(input, ingredient, consumeOnSucces); + for (AlchemistCauldronRecipe recipe : recipes.values()) { + ItemStack result = recipe.createOutput(input, ingredient, false, consumeOnSucces); + if (!result.isEmpty()) { + return result; + } + } + return ItemStack.EMPTY; + } + + /** + * Searches through registered recipes, and returns the resulting item or ItemStack.EMPTY if there are no matches + */ + public static ItemStack getOutput(ItemStack input, ItemStack ingredient, boolean ignoreCount, boolean consumeOnSucces) { + if (input.isEmpty() || ingredient.isEmpty()) return ItemStack.EMPTY; + for (AlchemistCauldronRecipe recipe : recipes.values()) { + ItemStack result = recipe.createOutput(input, ingredient, ignoreCount, consumeOnSucces); if (!result.isEmpty()) { return result; } @@ -65,8 +86,8 @@ public static ItemStack getOutput(ItemStack input, ItemStack ingredient, boolean * Returns if any cauldron recipe has this as the ingredient */ public static boolean isValidIngredient(ItemStack itemStack) { - for (AlchemistCauldronRecipe recipe : recipes) { - if (ItemStack.isSameItemSameTags(recipe.getIngredient(), itemStack)) + for (AlchemistCauldronRecipe recipe : recipes.values()) { + if (CauldronPlatformHelper.itemMatches(recipe.getIngredient(), itemStack)) return true; } return false; @@ -76,13 +97,33 @@ public static boolean isValidIngredient(ItemStack itemStack) { * Returns if this combination of items (the count of input matters) yields a result */ public static boolean hasOutput(ItemStack input, ItemStack ingredient) { - return !getOutput(input, ingredient, false).isEmpty(); + return !getOutput(input, ingredient, true, false).isEmpty(); + } + + @Nullable + public static AlchemistCauldronRecipe getRecipeForResult(ItemStack result) { + for (AlchemistCauldronRecipe recipe : recipes.values()) { + if (CauldronPlatformHelper.itemMatches(result, recipe.getResult())) { + return recipe; + } + } + return null; + } + + @Nullable + public static AlchemistCauldronRecipe getRecipeForInputs(ItemStack base, ItemStack reagent) { + for (AlchemistCauldronRecipe recipe : recipes.values()) { + if (CauldronPlatformHelper.itemMatches(base, recipe.getInput()) && CauldronPlatformHelper.itemMatches(reagent, recipe.getIngredient())) { + return recipe; + } + } + return null; } /** - * Returns an immutable list of all "registered" recipes + * Returns an immutable list of all registered recipes */ public static ImmutableList getRecipes() { - return ImmutableList.copyOf(recipes); + return ImmutableList.copyOf(recipes.values()); } } diff --git a/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronRenderer.java b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronRenderer.java index e7fdd801d..2f940e9a5 100644 --- a/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronRenderer.java +++ b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronRenderer.java @@ -7,7 +7,6 @@ import net.minecraft.client.renderer.LevelRenderer; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.RenderType; -import net.minecraft.client.renderer.block.model.ItemTransforms; import net.minecraft.client.renderer.blockentity.BlockEntityRenderer; import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider; import net.minecraft.client.renderer.entity.ItemRenderer; @@ -16,14 +15,11 @@ import net.minecraft.util.Mth; import net.minecraft.world.item.ItemDisplayContext; import net.minecraft.world.item.ItemStack; -import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.phys.Vec2; import net.minecraft.world.phys.Vec3; import org.joml.Matrix4f; import org.joml.Vector3f; -import java.util.function.Function; - public class AlchemistCauldronRenderer implements BlockEntityRenderer { ItemRenderer itemRenderer; @@ -33,11 +29,13 @@ public AlchemistCauldronRenderer(BlockEntityRendererProvider.Context context) { } private static final Vec3 ITEM_POS = new Vec3(.5, 1.5, .5); + @Override public void render(AlchemistCauldronTile cauldron, float partialTick, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, int packedOverlay) { - float waterOffset = getWaterOffest(cauldron.getBlockState()); + int waterLevel = cauldron.getLiquidLevel(); + + float waterOffset = Mth.lerp(waterLevel / (float) AlchemistCauldronTile.MAX_LEVELS, .25f, .9f); - int waterLevel = cauldron.getBlockState().getValue(AlchemistCauldronBlock.LEVEL); if (waterLevel > 0) { renderWater(cauldron, poseStack, bufferSource, packedLight, waterOffset); } @@ -55,9 +53,111 @@ public void render(AlchemistCauldronTile cauldron, float partialTick, PoseStack waterOffset + i * .01f, floatOffset.y), yRot, cauldron, partialTick, poseStack, bufferSource, packedLight, packedOverlay); + } } +// MinecraftInstanceHelper.ifPlayerPresent(player -> { +// if (Math.abs(player.getX() - cauldron.getBlockPos().getX()) < 5 && Math.abs(player.getY() - cauldron.getBlockPos().getY()) < 5 && Math.abs(player.getZ() - cauldron.getBlockPos().getZ()) < 5) +// if (player.isCrouching()) { +// for (int i = 0; i < cauldron.outputItems.size(); i++) { +// var itemStack = cauldron.outputItems.get(i); +// if (!itemStack.isEmpty()) { +// var component = Component.translatable(itemStack.getDescriptionId()); +// var effects = PotionUtils.getAllEffects(itemStack.getTag()); +// if (!effects.isEmpty()) { +// var primaryEffect = effects.get(0); +// if (primaryEffect.getAmplifier() > 0) { +// component.append(Component.literal(String.format(" (%s)", simpleRomanNumeral(primaryEffect.getAmplifier() + 1)))); +// } +// } +// renderWorldText(itemStack, component, new Vec3(0.5, 1.1 + i * .25, 0.5), poseStack, bufferSource, packedLight, partialTick); +// } +// } +// } +// }); } +// +// private String simpleRomanNumeral(int num) { +// return switch (num) { +// case 1 -> "I"; +// case 2 -> "II"; +// case 3 -> "III"; +// case 4 -> "IV"; +// case 5 -> "V"; +// case 6 -> "VI"; +// case 7 -> "VII"; +// case 8 -> "VIII"; +// case 9 -> "IX"; +// case 10 -> "X"; +// default -> String.valueOf(num); +// }; +// } +// +// public void renderWorldText( +// ItemStack stack, +// Component text, +// Vec3 offset, +// PoseStack poseStack, +// MultiBufferSource pBuffer, +// int pLightmapUV, +// float pPartialTick +// ) { +// boolean seeTextThroughBlocks = false;//(b0 & 2) != 0; +// boolean dropShadow = false;//(b0 & 1) != 0; +// byte opacity = -1; +// float f = Minecraft.getInstance().options.getBackgroundOpacity(0.25F); +// int i = (int) (f * 255.0F) << 24; +// text = Component.literal(" ").append(text); +// float f2 = 0.0F; +// poseStack.pushPose(); +// poseStack.translate((float) offset.x, (float) offset.y, (float) offset.z); +// poseStack.mulPose(Minecraft.getInstance().getEntityRenderDispatcher().cameraOrientation()); +// +// Matrix4f matrix4f = poseStack.last().pose(); +// matrix4f.rotate((float) Math.PI, 0.0F, 1.0F, 0.0F); +// matrix4f.scale(-0.025F, -0.025F, -0.025F); +// var font = Minecraft.getInstance().font; +// int lineHeight = 9 + 1; +// float customScale = .7f; +// +// int textWidth = (int) (font.width(text) * .7f) + lineHeight; +// int textHeight = (int) (lineHeight * .85f); +// matrix4f.translate(1.0F - (float) textWidth / 2.0F, (float) (-textHeight), 0.0F); +// if (i != 0) { +// RenderHelper.quadBuilder() +// .matrix(matrix4f) +// .color(i) +// .light(pLightmapUV) +// .vertex(-1, -1, 0) +// .vertex(-1, textHeight, 0) +// .vertex(textWidth, textHeight, 0) +// .vertex(textWidth, -1, 0) +// .build(pBuffer.getBuffer(RenderType.textBackground())); +// } +// +// float f1 = 0; +// matrix4f.scale(customScale); +// matrix4f.translate(0, lineHeight * (1 - customScale) * .5f, 0); +// +// font.drawInBatch( +// text, +// f1 + lineHeight / 2f, +// f2, +// opacity << 24 | 16777215, +// dropShadow, +// matrix4f, +// pBuffer, +// seeTextThroughBlocks ? Font.DisplayMode.SEE_THROUGH : Font.DisplayMode.POLYGON_OFFSET, +// 0, +// pLightmapUV +// ); +// poseStack.pushPose(); +// poseStack.scale(-0.4f / 0.025F, -0.4f / 0.025F, -0.4f / 0.025F); +// poseStack.translate(-0.5, -0.25, -.1); +// itemRenderer.renderStatic(stack, ItemDisplayContext.FIXED, pLightmapUV, OverlayTexture.NO_OVERLAY, poseStack, pBuffer, null, 0); +// poseStack.popPose(); +// poseStack.popPose(); +// } public Vec2 getFloatingItemOffset(float time, int offset) { //for our case, offset never changes @@ -75,10 +175,6 @@ public Vec2 getFloatingItemOffset(float time, int offset) { } - public static float getWaterOffest(BlockState blockState) { - return Mth.lerp(AlchemistCauldronBlock.getLevel(blockState) / (float) AlchemistCauldronBlock.MAX_LEVELS, .25f, .9f); - } - private void renderWater(AlchemistCauldronTile cauldron, PoseStack poseStack, MultiBufferSource bufferSource, int packedLight, float waterOffset) { VertexConsumer consumer = bufferSource.getBuffer(RenderType.beaconBeam(new ResourceLocation(IronsSpellbooks.MODID, "textures/block/water_still.png"), true)); long color = cauldron.getAverageWaterColor(); diff --git a/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronTile.java b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronTile.java index 78bb16118..342d5c7ec 100644 --- a/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronTile.java +++ b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/AlchemistCauldronTile.java @@ -5,6 +5,7 @@ import io.redspace.ironsspellbooks.api.util.Utils; import io.redspace.ironsspellbooks.capabilities.magic.MagicManager; import io.redspace.ironsspellbooks.config.ServerConfigs; +import io.redspace.ironsspellbooks.item.InkItem; import io.redspace.ironsspellbooks.item.Scroll; import io.redspace.ironsspellbooks.item.consumables.SimpleElixir; import io.redspace.ironsspellbooks.registries.BlockRegistry; @@ -14,12 +15,12 @@ import net.minecraft.client.renderer.BiomeColors; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.core.HolderLookup; import net.minecraft.core.NonNullList; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.nbt.CompoundTag; import net.minecraft.network.Connection; import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket; -import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; import net.minecraft.util.Mth; @@ -35,23 +36,26 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.entity.BlockEntity; import net.minecraft.world.level.block.state.BlockState; -import net.minecraftforge.common.brewing.BrewingRecipeRegistry; -import net.minecraftforge.registries.RegistryObject; +import net.minecraftforge.common.MinecraftForge; import org.jetbrains.annotations.Nullable; import javax.annotation.Nonnull; -import java.util.ArrayList; -import java.util.List; - -import static io.redspace.ironsspellbooks.block.alchemist_cauldron.AlchemistCauldronBlock.LEVEL; -import static io.redspace.ironsspellbooks.block.alchemist_cauldron.AlchemistCauldronBlock.MAX_LEVELS; +import java.util.function.Predicate; +import java.util.function.Supplier; public class AlchemistCauldronTile extends BlockEntity implements WorldlyContainer { - public Object2ObjectOpenHashMap interactions = AlchemistCauldronTile.newInteractionMap(); - //basically the input container + /* + Cauldron Interaction Doctrine: + - Placing in an input item should always replace a liquid item when brewed (or not brew at all) + - Scroll: replace water for ink + - Potion Reagent: replace previous potion for next potion + - Elixir Crafting: consume previous liquid items, produce new liquid item + - Emptying a liquid into the cauldron should always increase the level, and extracting a liquid item should decrease it (the level should be strictly tied to the amount of liquid item present) + */ + public static int MAX_LEVELS = 4; + public static final Object2ObjectOpenHashMap INTERACTIONS = AlchemistCauldronTile.newInteractionMap(); public final NonNullList inputItems = NonNullList.withSize(MAX_LEVELS, ItemStack.EMPTY); - //basically the output container - public final NonNullList resultItems = NonNullList.withSize(MAX_LEVELS, ItemStack.EMPTY); + public final NonNullList outputItems = NonNullList.withSize(MAX_LEVELS, ItemStack.EMPTY); private final int[] cooktimes = new int[MAX_LEVELS]; public AlchemistCauldronTile(BlockPos pWorldPosition, BlockState pBlockState) { @@ -64,120 +68,155 @@ public AlchemistCauldronTile(BlockPos pWorldPosition, BlockState pBlockState) { public static void serverTick(Level level, BlockPos pos, BlockState blockState, AlchemistCauldronTile cauldronTile) { for (int i = 0; i < cauldronTile.inputItems.size(); i++) { ItemStack itemStack = cauldronTile.inputItems.get(i); - if (itemStack.isEmpty() || !AlchemistCauldronBlock.isBoiling(blockState)) + if (itemStack.isEmpty() || !cauldronTile.isBoiling(blockState)) cauldronTile.cooktimes[i] = 0; else { cauldronTile.cooktimes[i]++; } if (cauldronTile.cooktimes[i] > 100) { - cauldronTile.meltComponent(itemStack); + cauldronTile.tryMeltInput(itemStack); cauldronTile.cooktimes[i] = 0; } } var random = Utils.random; - if (AlchemistCauldronBlock.isBoiling(blockState)) { - float waterLevel = Mth.lerp(AlchemistCauldronBlock.getLevel(blockState) / (float) AlchemistCauldronBlock.MAX_LEVELS, .25f, .9f); + if (cauldronTile.isBoiling(blockState)) { + float waterLevel = Mth.lerp(cauldronTile.getLiquidLevel() / (float) MAX_LEVELS, .25f, .9f); MagicManager.spawnParticles(level, ParticleTypes.BUBBLE_POP, pos.getX() + Mth.randomBetween(random, .2f, .8f), pos.getY() + waterLevel, pos.getZ() + Mth.randomBetween(random, .2f, .8f), 1, 0, 0, 0, 0, false); } } public InteractionResult handleUse(BlockState blockState, Level level, BlockPos pos, Player player, InteractionHand hand) { ItemStack itemStack = player.getItemInHand(hand); - int currentLevel = blockState.getValue(LEVEL); - var cauldronInteractionResult = interactions.get(itemStack.getItem()).interact(blockState, level, pos, currentLevel, itemStack); - if (cauldronInteractionResult != null) { - player.setItemInHand(hand, ItemUtils.createFilledResult(itemStack, player, cauldronInteractionResult)); - this.setChanged(); - return InteractionResult.sidedSuccess(level.isClientSide); - } else if (isValidInput(itemStack)) { - if (!level.isClientSide && appendItem(inputItems, itemStack)) { - if (!player.getAbilities().instabuild) - itemStack.shrink(1); + if (level.getBlockEntity(pos) instanceof AlchemistCauldronTile tile) { + var cauldronInteractionResult = INTERACTIONS.get(itemStack.getItem()).interact(tile, blockState, level, pos, itemStack); + if (cauldronInteractionResult != null) { + player.setItemInHand(hand, ItemUtils.createFilledResult(itemStack, player, cauldronInteractionResult)); this.setChanged(); - } - return InteractionResult.sidedSuccess(level.isClientSide); - } else if (itemStack.isEmpty() && hand.equals(InteractionHand.MAIN_HAND)) { - var item = grabItem(inputItems); - if (!item.isEmpty()) { + return InteractionResult.sidedSuccess(level.isClientSide); + } else if (isValidInput(itemStack)) { if (!level.isClientSide) { - player.setItemInHand(hand, item); - this.setChanged(); + for (int i = 0; i < inputItems.size(); i++) { + var stack = inputItems.get(i); + if (stack.isEmpty()) { + var input = player.getAbilities().instabuild ? itemStack.copy() : itemStack.split(1); + input.setCount(1); + inputItems.set(i, input); + player.setItemInHand(hand, itemStack); + this.setChanged(); + break; + } + } } return InteractionResult.sidedSuccess(level.isClientSide); + } else if ((itemStack.isEmpty() || player.isCrouching()) && hand.equals(InteractionHand.MAIN_HAND)) { + for (ItemStack item : inputItems) { + if (!item.isEmpty()) { + if (!level.isClientSide) { + var take = item.split(1); + if (player.getItemInHand(hand).isEmpty()) { + player.setItemInHand(hand, take); + } else { + if (!player.getInventory().add(take)) { + player.drop(take, false); + } + } + this.setChanged(); + } + return InteractionResult.sidedSuccess(level.isClientSide); + } + } } } return InteractionResult.PASS; } - public void meltComponent(ItemStack itemStack) { - //This is only called Server Side - if (level == null) + protected boolean isBaseIngredientPresent(ItemStack stack) { + return isBaseIngredientPresent(stack2 -> CauldronPlatformHelper.itemMatches(stack, stack2), 1); + } + + protected boolean isBaseIngredientPresent(Predicate baseIngredientPredicate, int minCount) { + int count = 0; + for (ItemStack stack : this.outputItems) { + if (baseIngredientPredicate.test(stack)) { + count += stack.getCount(); + if (count >= minCount) { + return true; + } + } + } + return false; + } + + protected void convertOutput(Predicate itemToReplace, ItemStack outputItem, int maxCount) { + int count = 0; + for (int i = outputItems.size() - 1; i >= 0; i--) { + var stack = outputItems.get(i); + if (itemToReplace.test(stack)) { + outputItems.set(i, outputItem.copy()); + count++; + if (count >= maxCount) { + return; + } + } + } + } + + public boolean addToOutput(ItemStack itemStack) { + for (int i = 0; i < outputItems.size(); i++) { + var stack = outputItems.get(i); + if (stack.isEmpty()) { + outputItems.set(i, itemStack); + return true; + } + } + return false; + } + + public void tryMeltInput(ItemStack itemStack) { + if (level == null || level.isClientSide) return; - //IronsSpellbooks.LOGGER.debug("AlchemistCauldronTile.meltComponent: {}", itemStack.getDisplayName().getString()); + /** shouldMelt is whether the input should be consumed*/ boolean shouldMelt = false; + /** success is whether the process yields a result*/ boolean success = true; - if (itemStack.is(ItemRegistry.SCROLL.get()) && !isFull(resultItems)) { + if (itemStack.is(ItemRegistry.SCROLL.get()) && isBaseIngredientPresent(CauldronPlatformHelper.IS_WATER, 1)) { if (Utils.random.nextFloat() < ServerConfigs.SCROLL_RECYCLE_CHANCE.get()) { ItemStack result = new ItemStack(getInkFromScroll(itemStack)); - appendItem(resultItems, result); + convertOutput(CauldronPlatformHelper.IS_WATER, result, 1); } else { success = false; } shouldMelt = true; } if (!shouldMelt && isBrewable(itemStack)) { - for (int i = 0; i < resultItems.size(); i++) { - ItemStack potentialPotion = resultItems.get(i); - ItemStack output = BrewingRecipeRegistry.getOutput(potentialPotion.isEmpty() ? PotionUtils.setPotion(new ItemStack(Items.POTION), Potions.WATER) : potentialPotion, itemStack); - if (!output.isEmpty()) { - resultItems.set(i, output); - shouldMelt = true; + for (int i = 0; i < outputItems.size(); i++) { + ItemStack potentialPotionBase = outputItems.get(i); + if (!potentialPotionBase.isEmpty()) { + ItemStack output = CauldronPlatformHelper.getNonDestructiveBrewingResult(potentialPotionBase, itemStack, level); + if (!output.isEmpty()) { + outputItems.set(i, output.copy()); + shouldMelt = true; + } } - //IronsSpellbooks.LOGGER.debug("{} + {} = {} ({})", potentialPotion.getDisplayName().getString(), itemStack.getDisplayName().getString(), output.getDisplayName().getString(), shouldMelt); } } if (!shouldMelt && AlchemistCauldronRecipeRegistry.isValidIngredient(itemStack)) { - //IronsSpellbooks.LOGGER.debug("AlchemistCauldronTile: custom recipe for: {}", itemStack.toString()); - for (int i = 0; i < resultItems.size(); i++) { - ItemStack potentialInput = resultItems.get(i).copy(); - List matchingItems = new ArrayList<>(List.of(i)); - if (!potentialInput.isEmpty()) { - for (int j = 0; j < resultItems.size(); j++) { - if (j != i && ItemStack.isSameItemSameTags(resultItems.get(j), potentialInput)) { - //Collect matching items into a single cumulative item stack (some recipes require counts > 1), and mark them down for later - int c = resultItems.get(j).getCount(); - potentialInput.grow(c); - matchingItems.add(j); - } - } - } - //IronsSpellbooks.LOGGER.debug("AlchemistCauldronTile: collecting results {}: {}", i, potentialInput); - - int inputsCollected = potentialInput.getCount(); - //IronsSpellbooks.LOGGER.debug("Checking cauldron recipes. CauldronInternalIndex: {}. Original Item: {} Copycat Item: {}", i, resultItems.get(i), potentialInput); - ItemStack output = AlchemistCauldronRecipeRegistry.getOutput(potentialInput, itemStack.copy(), true); - //IronsSpellbooks.LOGGER.debug("AlchemistCauldronTile: {} + {} = {}", itemStack, potentialInput, output); - - if (!output.isEmpty()) { - //If we have an output, consume inputs, and replace with as many outputs as we can fit - int inputsToConsume = inputsCollected - potentialInput.getCount(); - for (Integer integer : matchingItems) { - //Consume inputs we collected - if (inputsToConsume > 0) { - //This code is technically not precise, but only things of stack size 1 should be allowed to make it here in the first place - int c = resultItems.get(integer).getCount(); - resultItems.get(integer).shrink(c); - inputsToConsume -= c; - } - } - for (int j = 0; j < resultItems.size(); j++) { - //Place result if this used to be a base item, and we still have outputs to distribute - if (matchingItems.contains(j) && output.getCount() >= 1) { - resultItems.set(j, output.split(1)); + for (int i = 0; i < outputItems.size(); i++) { + ItemStack potentialPotionBase = outputItems.get(i).copy(); + if (!potentialPotionBase.isEmpty()) { + var recipe = AlchemistCauldronRecipeRegistry.getRecipeForInputs(potentialPotionBase, itemStack); + if (recipe != null && isBaseIngredientPresent(stack -> CauldronPlatformHelper.itemMatches(stack, potentialPotionBase), recipe.getInput().getCount())) { + //This given potion base has a recipe with our reagent, and we have enough of it to successfully craft the result + ItemStack result = recipe.getResult(); + int toConsume = recipe.getInput().getCount(); + convertOutput((stack) -> CauldronPlatformHelper.itemMatches(stack, potentialPotionBase.copy()), ItemStack.EMPTY, toConsume); + int c = result.getCount(); + for (int j = 0; j < c; j++) { + addToOutput(result.split(1)); } + shouldMelt = true; + break; } - shouldMelt = true; - break; } } } @@ -190,6 +229,22 @@ public void meltComponent(ItemStack itemStack) { } else { level.playSound(null, this.getBlockPos(), SoundEvents.GENERIC_EXTINGUISH_FIRE, SoundSource.MASTER, 1, 1); } + collapseContainer(outputItems); + } + } + + public void collapseContainer(NonNullList container) { + for (int i = 0; i < container.size(); i++) { + if (container.get(i).isEmpty()) { + for (int j = i + 1; j < container.size(); j++) { + var stack = container.get(j); + if (!stack.isEmpty()) { + container.set(i, stack); + container.set(j, ItemStack.EMPTY); + break; + } + } + } } } @@ -200,8 +255,8 @@ public boolean isValidInput(ItemStack itemStack) { return itemStack.is(ItemRegistry.SCROLL.get()) || isBrewable(itemStack) || AlchemistCauldronRecipeRegistry.isValidIngredient(itemStack); } - public static boolean isBrewable(ItemStack itemStack) { - return ServerConfigs.ALLOW_CAULDRON_BREWING.get() && BrewingRecipeRegistry.isValidIngredient(itemStack); + public boolean isBrewable(ItemStack itemStack) { + return ServerConfigs.ALLOW_CAULDRON_BREWING.get() && this.level != null && CauldronPlatformHelper.isBrewingIngredient(itemStack, this.level); } public int getItemWaterColor(ItemStack itemStack) { @@ -231,16 +286,20 @@ public int getAverageWaterColor() { float f1 = 0.0F; float f2 = 0.0F; - for (ItemStack itemStack : resultItems) { - int k = getItemWaterColor(itemStack); - f += (float) ((k >> 16 & 255)) / 255.0F; - f1 += (float) ((k >> 8 & 255)) / 255.0F; - f2 += (float) ((k >> 0 & 255)) / 255.0F; + int i = 0; + for (ItemStack itemStack : outputItems) { + if (!itemStack.isEmpty()) { + int k = getItemWaterColor(itemStack); + f += (float) ((k >> 16 & 255)) / 255.0F; + f1 += (float) ((k >> 8 & 255)) / 255.0F; + f2 += (float) ((k >> 0 & 255)) / 255.0F; + i++; + } } - f = f / (float) 4 * 255.0F; - f1 = f1 / (float) 4 * 255.0F; - f2 = f2 / (float) 4 * 255.0F; + f = f / (float) i * 255.0F; + f1 = f1 / (float) i * 255.0F; + f2 = f2 / (float) i * 255.0F; return (int) f << 16 | (int) f1 << 8 | (int) f2; } @@ -250,56 +309,11 @@ public static Item getInkFromScroll(ItemStack scrollStack) { var spellData = spellContainer.getSpellAtIndex(0); SpellRarity rarity = spellData.getSpell().getRarity(spellData.getLevel()); - return switch (rarity) { - case COMMON -> ItemRegistry.INK_COMMON.get(); - case UNCOMMON -> ItemRegistry.INK_UNCOMMON.get(); - case RARE -> ItemRegistry.INK_RARE.get(); - case EPIC -> ItemRegistry.INK_EPIC.get(); - case LEGENDARY -> ItemRegistry.INK_LEGENDARY.get(); - default -> ItemRegistry.INK_COMMON.get(); - }; + return InkItem.getInkForRarity(rarity); } else - return null; + return Items.AIR; } - public static boolean appendItem(NonNullList container, ItemStack newItem) { - for (int i = 0; i < container.size(); i++) { - if (container.get(i).isEmpty()) { - var newItemCopy = newItem.copy(); - newItemCopy.setCount(1); - container.set(i, newItemCopy); - //IronsSpellbooks.LOGGER.debug("{}", container.toString()); - return true; - } - } - return false; - } - - public static ItemStack grabItem(NonNullList container) { - for (int i = container.size() - 1; i >= 0; i--) { - ItemStack item = container.get(i); - if (!item.isEmpty()) { - return item.split(1); - } - } - return ItemStack.EMPTY; - } - - public static boolean isEmpty(NonNullList container) { - for (ItemStack itemStack : container) - if (!itemStack.isEmpty()) - return false; - return true; - } - - public static boolean isFull(NonNullList container) { - for (ItemStack itemStack : container) - if (itemStack.isEmpty()) - return false; - return true; - } - - /************************************************************ Tile Entity Handling ***********************************************************/ @@ -315,28 +329,20 @@ public boolean stillValid(Player pPlayer) { return false; } - @Override - public void load(CompoundTag tag) { - Utils.loadAllItems(tag, this.inputItems, "Items"); - Utils.loadAllItems(tag, this.resultItems, "Results"); - super.load(tag); + public void load(CompoundTag pTag) { + Utils.loadAllItems(pTag, this.inputItems, "Items"); + Utils.loadAllItems(pTag, this.outputItems, "Results"); + super.load(pTag); } @Override protected void saveAdditional(@Nonnull CompoundTag tag) { Utils.saveAllItems(tag, this.inputItems, "Items"); - Utils.saveAllItems(tag, this.resultItems, "Results"); + Utils.saveAllItems(tag, this.outputItems, "Results"); super.saveAdditional(tag); } - @Override - public CompoundTag getUpdateTag() { - CompoundTag tag = new CompoundTag(); - saveAdditional(tag); - return tag; - } - @Override public ClientboundBlockEntityDataPacket getUpdatePacket() { var packet = ClientboundBlockEntityDataPacket.create(this); @@ -346,24 +352,26 @@ public ClientboundBlockEntityDataPacket getUpdatePacket() { @Override public void onDataPacket(Connection net, ClientboundBlockEntityDataPacket pkt) { - //irons_spellbooks.LOGGER.debug("onDataPacket: pkt.getTag:{}", pkt.getTag()); handleUpdateTag(pkt.getTag()); - if (level != null) + if (level != null) { level.sendBlockUpdated(worldPosition, getBlockState(), getBlockState(), Block.UPDATE_ALL); + } + } + + @Override + public CompoundTag getUpdateTag() { + CompoundTag tag = new CompoundTag(); + saveAdditional(tag); + return tag; } @Override public void handleUpdateTag(CompoundTag tag) { - //this only gets run client side this.inputItems.clear(); - this.resultItems.clear(); + this.outputItems.clear(); if (tag != null) { load(tag); } - //IronsSpellbooks.LOGGER.debug("AlchemistCauldronTile.handleUpdateTag: tag:{}", tag); - //IronsSpellbooks.LOGGER.debug("AlchemistCauldronTile.handleUpdateTag: items:{}", inputItems); - //IronsSpellbooks.LOGGER.debug("AlchemistCauldronTile.handleUpdateTag: results:{}", resultItems); - } public void drops() { @@ -375,84 +383,94 @@ public void drops() { Containers.dropContents(this.level, this.worldPosition, simpleContainer); } + protected static ItemStack waterBottle() { + var stack = new ItemStack(Items.POTION); + PotionUtils.setPotion(stack, Potions.WATER); + return stack; + } + /************************************************************ Interaction Map ***********************************************************/ static Object2ObjectOpenHashMap newInteractionMap() { var map = Util.make(new Object2ObjectOpenHashMap(), (o2o) -> { - o2o.defaultReturnValue((blockState, level, blockPos, i, itemStack) -> null); + o2o.defaultReturnValue((tile, blockState, level, pos, itemstack) -> null); }); - map.put(Items.WATER_BUCKET, (blockState, level, pos, currentLevel, itemstack) -> { - if (currentLevel < MAX_LEVELS) { - return createFilledResult(level, blockState, pos, MAX_LEVELS, new ItemStack(Items.BUCKET), SoundEvents.BUCKET_EMPTY); + map.put(Items.WATER_BUCKET, (tile, blockState, level, pos, itemstack) -> { + if (tile.outputItems.stream().anyMatch(ItemStack::isEmpty)) { + level.playSound(null, pos, SoundEvents.BUCKET_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F); + for (int i = 0; i < tile.outputItems.size(); i++) { + if (tile.outputItems.get(i).isEmpty()) { + tile.outputItems.set(i, waterBottle()); + } + } + return new ItemStack(Items.BUCKET); } else { return null; } }); - map.put(Items.BUCKET, (blockState, level, pos, currentLevel, itemstack) -> { - if (level.getBlockEntity(pos) instanceof AlchemistCauldronTile tile) { - if (isEmpty(tile.resultItems) && currentLevel == MAX_LEVELS) { - return createFilledResult(level, blockState, pos, 0, new ItemStack(Items.WATER_BUCKET), SoundEvents.BUCKET_FILL); - } + map.put(Items.BUCKET, (tile, blockState, level, pos, itemstack) -> { + if (tile.outputItems.stream().allMatch(CauldronPlatformHelper.IS_WATER)) { + tile.outputItems.clear(); + level.playSound(null, pos, SoundEvents.BUCKET_FILL, SoundSource.BLOCKS, 1.0F, 1.0F); + return new ItemStack(Items.WATER_BUCKET); } return null; }); - map.put(Items.GLASS_BOTTLE, (blockState, level, pos, currentLevel, itemstack) -> { - if (currentLevel > 0 && level.getBlockEntity(pos) instanceof AlchemistCauldronTile tile) { - var storedItems = tile.resultItems; - if (isEmpty(storedItems)) { - //No items means we only hold water, so we should create a water bottle and decrement level - return createFilledResult(level, blockState, pos, currentLevel - 1, PotionUtils.setPotion(new ItemStack(Items.POTION), Potions.WATER), SoundEvents.BOTTLE_FILL); - } else { - //If we have an item ready, pop it but don't change the level - return createFilledResult(level, blockState, pos, currentLevel, grabItem(storedItems), SoundEvents.BOTTLE_FILL_DRAGONBREATH); + map.put(Items.GLASS_BOTTLE, (tile, blockState, level, pos, itemstack) -> { + for (int i = tile.outputItems.size() - 1; i >= 0; i--) { + var stack = tile.outputItems.get(i); + if (!stack.isEmpty()) { + level.playSound(null, pos, (CauldronPlatformHelper.IS_WATER.test(stack) ? SoundEvents.BOTTLE_FILL : SoundEvents.BOTTLE_FILL_DRAGONBREATH), SoundSource.BLOCKS, 1.0F, 1.0F); + return stack.split(1); } - } return null; }); - map.put(Items.POTION, (blockState, level, pos, currentLevel, itemstack) -> { - //If we are a water bottle, put additional water level - if (PotionUtils.getPotion(itemstack) == Potions.WATER) { - if (currentLevel < MAX_LEVELS) { - return createFilledResult(level, blockState, pos, currentLevel + 1, new ItemStack(Items.GLASS_BOTTLE), SoundEvents.BOTTLE_EMPTY); - } - } - //Otherwise, put potion in - else if (level.getBlockEntity(pos) instanceof AlchemistCauldronTile tile && !isFull(tile.resultItems)) { - appendItem(tile.resultItems, itemstack); - return createFilledResult(level, blockState, pos, Math.min(currentLevel + 1, MAX_LEVELS), new ItemStack(Items.GLASS_BOTTLE), SoundEvents.BOTTLE_EMPTY); - } - return null; - }); - createInkInteraction(map, ItemRegistry.INK_COMMON); - createInkInteraction(map, ItemRegistry.INK_UNCOMMON); - createInkInteraction(map, ItemRegistry.INK_RARE); - createInkInteraction(map, ItemRegistry.INK_EPIC); - createInkInteraction(map, ItemRegistry.INK_LEGENDARY); + + createBottleEmptyInteraction(map, () -> Items.POTION); + + createBottleEmptyInteraction(map, ItemRegistry.INK_COMMON); + createBottleEmptyInteraction(map, ItemRegistry.INK_UNCOMMON); + createBottleEmptyInteraction(map, ItemRegistry.INK_RARE); + createBottleEmptyInteraction(map, ItemRegistry.INK_EPIC); + createBottleEmptyInteraction(map, ItemRegistry.INK_LEGENDARY); + + createBottleEmptyInteraction(map, ItemRegistry.BLOOD_VIAL); + + createBottleEmptyInteraction(map, ItemRegistry.OAKSKIN_ELIXIR); + createBottleEmptyInteraction(map, ItemRegistry.GREATER_OAKSKIN_ELIXIR); + + createBottleEmptyInteraction(map, ItemRegistry.EVASION_ELIXIR); + createBottleEmptyInteraction(map, ItemRegistry.GREATER_EVASION_ELIXIR); + + createBottleEmptyInteraction(map, ItemRegistry.INVISIBILITY_ELIXIR); + createBottleEmptyInteraction(map, ItemRegistry.GREATER_INVISIBILITY_ELIXIR); + + createBottleEmptyInteraction(map, ItemRegistry.GREATER_HEALING_POTION); + + MinecraftForge.EVENT_BUS.post(new AlchemistCauldronBuildInteractionsEvent(map)); return map; } - private static void createInkInteraction(Object2ObjectOpenHashMap map, RegistryObject ink) { - map.put(ink.get(), (blockState, level, pos, currentLevel, itemstack) -> { - if (currentLevel > 0 && level.getBlockEntity(pos) instanceof AlchemistCauldronTile tile) { - if (!isFull(tile.resultItems)) { - appendItem(tile.resultItems, itemstack); - return createFilledResult(level, blockState, pos, currentLevel, new ItemStack(Items.GLASS_BOTTLE), SoundEvents.BOTTLE_EMPTY); + protected static void createBottleEmptyInteraction(Object2ObjectOpenHashMap map, Supplier item) { + map.put(item.get(), (tile, blockState, level, pos, itemstack) -> { + for (int i = 0; i < tile.outputItems.size(); i++) { + var stack = tile.outputItems.get(i); + if (stack.isEmpty()) { + var input = itemstack.copy(); + input.setCount(1); + tile.outputItems.set(i, input); + level.playSound(null, pos, SoundEvents.BOTTLE_EMPTY, SoundSource.BLOCKS, 1.0F, 1.0F); + return new ItemStack(Items.GLASS_BOTTLE); } } return null; }); } - private static ItemStack createFilledResult(Level level, BlockState blockState, BlockPos blockPos, int newLevel, ItemStack resultItem, SoundEvent soundEvent) { - level.setBlock(blockPos, blockState.setValue(LEVEL, newLevel), 3); - level.playSound(null, blockPos, soundEvent, SoundSource.BLOCKS, 1.0F, 1.0F); - return resultItem; - } - /************************************************************ Wordly Container Implementation ***********************************************************/ @@ -463,7 +481,7 @@ public int[] getSlotsForFace(Direction pSide) { @Override public boolean canPlaceItemThroughFace(int pIndex, ItemStack pItemStack, @Nullable Direction pDirection) { - return !isFull(inputItems) && isValidInput(pItemStack); + return inputItems.stream().anyMatch(ItemStack::isEmpty) && isValidInput(pItemStack); } @Override @@ -473,17 +491,18 @@ public boolean canTakeItemThroughFace(int pIndex, ItemStack pStack, Direction pD @Override public void clearContent() { - //IronsSpellbooks.LOGGER.debug("AlchemistCauldronTile.clearContents"); + inputItems.clear(); + outputItems.clear(); } @Override public int getContainerSize() { - return 4; + return MAX_LEVELS; } @Override public boolean isEmpty() { - return isEmpty(inputItems); + return inputItems.stream().allMatch(ItemStack::isEmpty); } @Override @@ -510,10 +529,15 @@ public ItemStack removeItemNoUpdate(int pSlot) { @Override public void setItem(int pSlot, ItemStack pStack) { if (pSlot >= 0 && pSlot <= inputItems.size()) { - if (inputItems.get(pSlot).isEmpty()) - inputItems.set(pSlot, pStack); - else - appendItem(inputItems, pStack); + inputItems.set(pSlot, pStack); } } + + public boolean isBoiling(BlockState blockState) { + return AlchemistCauldronBlock.isLit(blockState) && getLiquidLevel() >= 1; + } + + public int getLiquidLevel() { + return this.outputItems.stream().filter(itemstack -> !itemstack.isEmpty()).toList().size(); + } } diff --git a/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/CauldronPlatformHelper.java b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/CauldronPlatformHelper.java new file mode 100644 index 000000000..29a770271 --- /dev/null +++ b/src/main/java/io/redspace/ironsspellbooks/block/alchemist_cauldron/CauldronPlatformHelper.java @@ -0,0 +1,30 @@ +package io.redspace.ironsspellbooks.block.alchemist_cauldron; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.alchemy.PotionUtils; +import net.minecraft.world.item.alchemy.Potions; +import net.minecraft.world.level.Level; +import net.minecraftforge.common.brewing.BrewingRecipeRegistry; + +import java.util.function.Predicate; + +public class CauldronPlatformHelper { + public static final Predicate IS_WATER = (itemStack) -> PotionUtils.getPotion(itemStack) == Potions.WATER; + + public static boolean itemMatches(ItemStack a, ItemStack b) { + return ItemStack.isSameItemSameTags(a, b); + } + + public static boolean isBrewingIngredient(ItemStack stack, Level level) { + return BrewingRecipeRegistry.isValidIngredient(stack); + } + + /** + * @param base Base is the existing item attempting to be transformed (ie water bottle) + * @param reagent Reagent is the acting brewing ingredient (ie nether wart) + * @return Returns brewing result (without affecting input itemstacks) or ItemStack.EMPTY + */ + public static ItemStack getNonDestructiveBrewingResult(ItemStack base, ItemStack reagent, Level level) { + return BrewingRecipeRegistry.getOutput(base, reagent); + } +} diff --git a/src/main/java/io/redspace/ironsspellbooks/block/inscription_table/InscriptionTableBlock.java b/src/main/java/io/redspace/ironsspellbooks/block/inscription_table/InscriptionTableBlock.java index f7bfe9ab6..3459948ea 100644 --- a/src/main/java/io/redspace/ironsspellbooks/block/inscription_table/InscriptionTableBlock.java +++ b/src/main/java/io/redspace/ironsspellbooks/block/inscription_table/InscriptionTableBlock.java @@ -61,19 +61,18 @@ public InscriptionTableBlock() { super(BlockBehaviour.Properties.of().strength(2.5F).sound(SoundType.WOOD).noOcclusion()); } - public void playerWillDestroy(Level pLevel, BlockPos pos1, BlockState state1, Player pPlayer) { - if (!pLevel.isClientSide/* && pPlayer.isCreative()*/) { - ChestType half = state1.getValue(PART); - BlockPos pos2 = pos1.relative(getNeighbourDirection(half, state1.getValue(FACING))); - BlockState state2 = pLevel.getBlockState(pos2); - //IronsSpellbooks.LOGGER.debug("InscriptionTableBlock.playerWillDestory: mypos:{}, targted pos:{}", pos1, pos2); - if (state2.is(this) && state2.getValue(PART) != state1.getValue(PART)) { - pLevel.setBlock(pos2, Blocks.AIR.defaultBlockState(), 35); - pLevel.levelEvent(pPlayer, 2001, pos2, Block.getId(state2)); - } + public BlockState updateShape(BlockState myState, Direction pFacing, BlockState pFacingState, LevelAccessor pLevel, BlockPos myPos, BlockPos pFacingPos) { + ChestType half = myState.getValue(PART); + BlockPos requiredNeighborPos = myPos.relative(getNeighbourDirection(half, myState.getValue(FACING))); + BlockState neighborState = pLevel.getBlockState(requiredNeighborPos); + if (!neighborState.is(this)) { + var air = Blocks.AIR.defaultBlockState(); + //manually set to prevent block from dropping + pLevel.setBlock(myPos, air, 35); + pLevel.levelEvent(null, 2001, myPos, Block.getId(air)); + return air; } - - super.playerWillDestroy(pLevel, pos1, state1, pPlayer); + return super.updateShape(myState, pFacing, pFacingState, pLevel, myPos, pFacingPos); } private static Direction getNeighbourDirection(ChestType pPart, Direction pDirection) { @@ -92,10 +91,6 @@ public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, }; } - public BlockState updateShape(BlockState pState, Direction pFacing, BlockState pFacingState, LevelAccessor pLevel, BlockPos pCurrentPos, BlockPos pFacingPos) { - return super.updateShape(pState, pFacing, pFacingState, pLevel, pCurrentPos, pFacingPos); - } - @javax.annotation.Nullable public BlockState getStateForPlacement(BlockPlaceContext pContext) { Direction direction = pContext.getHorizontalDirection(); diff --git a/src/main/java/io/redspace/ironsspellbooks/effect/AbyssalShroudEffect.java b/src/main/java/io/redspace/ironsspellbooks/effect/AbyssalShroudEffect.java index b9ba3b95d..2f218ae8a 100644 --- a/src/main/java/io/redspace/ironsspellbooks/effect/AbyssalShroudEffect.java +++ b/src/main/java/io/redspace/ironsspellbooks/effect/AbyssalShroudEffect.java @@ -18,7 +18,12 @@ import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.ai.attributes.AttributeMap; import net.minecraft.world.level.ClipContext; +import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; + +import java.util.Optional; public class AbyssalShroudEffect extends MagicMobEffect { @@ -55,6 +60,18 @@ public static boolean doEffect(LivingEntity livingEntity, DamageSource damageSou Vec3 ground = livingEntity.position().add(sideStep); ground = Utils.moveToRelativeGroundLevel(level, ground, 2, 1); + var dimensions = livingEntity.getDimensions(livingEntity.getPose()); + Vec3 vec3 = ground.add(0.0, dimensions.height / 2.0, 0.0); + VoxelShape voxelshape = Shapes.create(AABB.ofSize(vec3, dimensions.width + .2f, dimensions.height + .2f, dimensions.width + .2f)); + Optional optional = level + .findFreePosition(null, voxelshape, vec3, (double) dimensions.width, (double) dimensions.height, (double) dimensions.width); + if (optional.isPresent()) { + ground = optional.get().add(0, -dimensions.height / 2 + 1.0E-6, 0); + } + if (level.collidesWithSuffocatingBlock(null, AABB.ofSize(ground.add(0, dimensions.height / 2, 0), dimensions.width, dimensions.height, dimensions.width))) { + ground = livingEntity.position(); + } + if (livingEntity.isPassenger()) { livingEntity.stopRiding(); } diff --git a/src/main/java/io/redspace/ironsspellbooks/effect/TrueInvisibilityEffect.java b/src/main/java/io/redspace/ironsspellbooks/effect/TrueInvisibilityEffect.java index c4e82173f..4fd7f06e9 100644 --- a/src/main/java/io/redspace/ironsspellbooks/effect/TrueInvisibilityEffect.java +++ b/src/main/java/io/redspace/ironsspellbooks/effect/TrueInvisibilityEffect.java @@ -10,6 +10,7 @@ import net.minecraft.world.entity.Mob; import net.minecraft.world.entity.ai.attributes.AttributeMap; import net.minecraft.world.entity.ai.goal.WrappedGoal; +import net.minecraft.world.entity.ai.memory.MemoryModuleType; import net.minecraft.world.entity.ai.targeting.TargetingConditions; import net.minecraft.world.entity.player.Player; @@ -40,6 +41,7 @@ public void addAttributeModifiers(LivingEntity livingEntity, AttributeMap pAttri entityTargetingCaster.setLastHurtMob(null); entityTargetingCaster.setLastHurtByMob(null); entityTargetingCaster.targetSelector.getAvailableGoals().forEach(WrappedGoal::stop); + entityTargetingCaster.getBrain().eraseMemory(MemoryModuleType.ATTACK_TARGET); }); this.lastHurtTimestamp = livingEntity.getLastHurtMobTimestamp(); diff --git a/src/main/java/io/redspace/ironsspellbooks/entity/mobs/dead_king_boss/DeadKingAmbienceSoundInstance.java b/src/main/java/io/redspace/ironsspellbooks/entity/mobs/dead_king_boss/DeadKingAmbienceSoundInstance.java index 67693789a..ee8975c57 100644 --- a/src/main/java/io/redspace/ironsspellbooks/entity/mobs/dead_king_boss/DeadKingAmbienceSoundInstance.java +++ b/src/main/java/io/redspace/ironsspellbooks/entity/mobs/dead_king_boss/DeadKingAmbienceSoundInstance.java @@ -9,8 +9,8 @@ import net.minecraft.world.phys.Vec3; public class DeadKingAmbienceSoundInstance extends AbstractTickableSoundInstance { - public static final int rangeSqr = 48 * 48; - public static final int maxVolumeRangeSqr = 18 * 18; + public static final int SOUND_RANGE_SQR = 20 * 20; + public static final int MAX_VOLUME_RANGE_SQR = 12 * 12; private static final float END_TRANSITION_TIME = 1f / 100; final Vec3 vec3; boolean ending = false; @@ -35,7 +35,7 @@ public void tick() { } else { MinecraftInstanceHelper.ifPlayerPresent(player -> { var d = player.distanceToSqr(vec3); - this.volume = 1f - (float) Mth.clamp((d - maxVolumeRangeSqr) / (rangeSqr), 0, 1f); + this.volume = 1f - (float) Mth.clamp((d - MAX_VOLUME_RANGE_SQR) / (SOUND_RANGE_SQR), 0, 1f); }); } diff --git a/src/main/java/io/redspace/ironsspellbooks/entity/mobs/dead_king_boss/DeadKingAmbienceSoundManager.java b/src/main/java/io/redspace/ironsspellbooks/entity/mobs/dead_king_boss/DeadKingAmbienceSoundManager.java index 90ed2805f..4031b9545 100644 --- a/src/main/java/io/redspace/ironsspellbooks/entity/mobs/dead_king_boss/DeadKingAmbienceSoundManager.java +++ b/src/main/java/io/redspace/ironsspellbooks/entity/mobs/dead_king_boss/DeadKingAmbienceSoundManager.java @@ -13,8 +13,8 @@ public class DeadKingAmbienceSoundManager { @OnlyIn(Dist.CLIENT) private DeadKingAmbienceSoundInstance soundInstance; - protected DeadKingAmbienceSoundManager(DeadKingCorpseEntity entity) { - this.vec3 = entity.position(); + protected DeadKingAmbienceSoundManager(Vec3 origin) { + this.vec3 = origin; } public void trigger() { diff --git a/src/main/java/io/redspace/ironsspellbooks/entity/mobs/dead_king_boss/DeadKingCorpseEntity.java b/src/main/java/io/redspace/ironsspellbooks/entity/mobs/dead_king_boss/DeadKingCorpseEntity.java index 471fb8236..757572596 100644 --- a/src/main/java/io/redspace/ironsspellbooks/entity/mobs/dead_king_boss/DeadKingCorpseEntity.java +++ b/src/main/java/io/redspace/ironsspellbooks/entity/mobs/dead_king_boss/DeadKingCorpseEntity.java @@ -32,7 +32,6 @@ import software.bernie.geckolib.core.object.PlayState; public class DeadKingCorpseEntity extends AbstractSpellCastingMob { - public static final int ambienceRange = 32; DeadKingAmbienceSoundManager ambienceSoundManager; private final static EntityDataAccessor TRIGGERED = SynchedEntityData.defineId(DeadKingCorpseEntity.class, EntityDataSerializers.BOOLEAN); @@ -98,9 +97,11 @@ public void tick() { if (tickCount % 40 == 0) { MinecraftInstanceHelper.ifPlayerPresent(player -> { //Local player who we want to play music to - if (distanceToSqr(player) < ambienceRange * ambienceRange) { - if(ambienceSoundManager == null){ - ambienceSoundManager = new DeadKingAmbienceSoundManager(this); + float yRot = this.getYRot(); + Vec3 musicCenter = this.position().add(-15 * Mth.sin(yRot * Mth.DEG_TO_RAD), 0, 15 * Mth.cos(yRot * Mth.DEG_TO_RAD)); + if (musicCenter.distanceToSqr(player.position()) < DeadKingAmbienceSoundInstance.SOUND_RANGE_SQR) { + if (ambienceSoundManager == null) { + ambienceSoundManager = new DeadKingAmbienceSoundManager(musicCenter); } ambienceSoundManager.trigger(); } diff --git a/src/main/java/io/redspace/ironsspellbooks/entity/mobs/wizards/IMerchantWizard.java b/src/main/java/io/redspace/ironsspellbooks/entity/mobs/wizards/IMerchantWizard.java index d45979f76..0f8988bfd 100644 --- a/src/main/java/io/redspace/ironsspellbooks/entity/mobs/wizards/IMerchantWizard.java +++ b/src/main/java/io/redspace/ironsspellbooks/entity/mobs/wizards/IMerchantWizard.java @@ -63,7 +63,7 @@ default boolean shouldRestock() { if (getLastRestockCheckDayTime() > 0L) { long lastRestockDay = getLastRestockCheckDayTime() / 24000L; long currentDay = currentDayTime / 24000L; - //Or, if day time is accurate, and a whole day has passed, we can also restock. + //Or, if day time is accurate (we arent in the future), and a whole day has passed, we can also restock. hasDayElapsed |= currentDay > lastRestockDay; } else { //Make our day time accurate again @@ -72,11 +72,14 @@ default boolean shouldRestock() { if (hasDayElapsed) { //update times - setLastRestockGameTime(currentGameTime); setLastRestockCheckDayTime(currentDayTime); setRestocksToday(0); } - return this.needsToRestock() && allowedToRestock(); + boolean shouldRestock = this.needsToRestock() && allowedToRestock(); + if (shouldRestock) { + setLastRestockGameTime(currentGameTime); + } + return shouldRestock; } default void restock() { diff --git a/src/main/java/io/redspace/ironsspellbooks/gui/arcane_anvil/ArcaneAnvilMenu.java b/src/main/java/io/redspace/ironsspellbooks/gui/arcane_anvil/ArcaneAnvilMenu.java index 42304834f..5c96dd3c2 100644 --- a/src/main/java/io/redspace/ironsspellbooks/gui/arcane_anvil/ArcaneAnvilMenu.java +++ b/src/main/java/io/redspace/ironsspellbooks/gui/arcane_anvil/ArcaneAnvilMenu.java @@ -1,10 +1,13 @@ package io.redspace.ironsspellbooks.gui.arcane_anvil; +import io.redspace.ironsspellbooks.api.item.curios.AffinityData; import io.redspace.ironsspellbooks.api.spells.ISpellContainer; import io.redspace.ironsspellbooks.api.util.Utils; import io.redspace.ironsspellbooks.capabilities.magic.UpgradeData; import io.redspace.ironsspellbooks.config.ServerConfigs; import io.redspace.ironsspellbooks.item.*; +import io.redspace.ironsspellbooks.item.armor.UpgradeType; +import io.redspace.ironsspellbooks.item.curios.AffinityRing; import io.redspace.ironsspellbooks.registries.BlockRegistry; import io.redspace.ironsspellbooks.registries.ItemRegistry; import io.redspace.ironsspellbooks.registries.MenuRegistry; @@ -12,6 +15,7 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.sounds.SoundEvents; import net.minecraft.sounds.SoundSource; +import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.ContainerLevelAccess; @@ -21,11 +25,15 @@ import net.minecraft.world.item.ItemStack; import net.minecraft.world.level.block.state.BlockState; +import java.util.ArrayList; +import java.util.List; + public class ArcaneAnvilMenu extends ItemCombinerMenu { public ArcaneAnvilMenu(int pContainerId, Inventory inventory, ContainerLevelAccess containerLevelAccess) { super(MenuRegistry.ARCANE_ANVIL_MENU.get(), pContainerId, inventory, containerLevelAccess); } + private final List additionalDrops = new ArrayList<>(); public ArcaneAnvilMenu(int pContainerId, Inventory inventory, FriendlyByteBuf extraData) { this(pContainerId, inventory, ContainerLevelAccess.NULL); @@ -44,6 +52,13 @@ protected void onTake(Player p_150601_, ItemStack p_150602_) { this.access.execute((level, pos) -> { level.playSound(null, pos, SoundEvents.ANVIL_USE, SoundSource.BLOCKS, .8f, 1.1f); level.playSound(null, pos, SoundEvents.AMETHYST_BLOCK_BREAK, SoundSource.BLOCKS, 1f, 1f); + additionalDrops.forEach(stack -> { + if (!stack.isEmpty()) { + level.addFreshEntity(new ItemEntity(level, pos.getX() + .5, pos.getY() + 1, pos.getZ() + .5, stack)); + } + }); + additionalDrops.clear(); + }); createResult(); } @@ -56,6 +71,7 @@ protected boolean isValidBlock(BlockState pState) { @Override public void createResult() { ItemStack result = ItemStack.EMPTY; + this.additionalDrops.clear(); /* Actions that can be taken in arcane anvil: - Upgrade scroll (scroll + scroll) @@ -77,14 +93,6 @@ public void createResult() { ISpellContainer.createScrollContainer(spell1.getSpell(), spell1.getLevel() + 1, result); } } - //var spell2 = ISpellContainer.get(modifierItemStack).getSpellAtIndex(0); - - //if (spell1.equals(spell2)) { - // if (spell1.getLevel() < ServerConfigs.getSpellConfig(spell1.getSpell()).maxLevel()) { - // result = new ItemStack(ItemRegistry.SCROLL.get()); - // ISpellContainer.createScrollContainer(spell1.getSpell(), spell1.getLevel() + 1, result); - // } - //} } //Unique Weapon Improving else if (baseItemStack.getItem() instanceof UniqueItem && modifierItemStack.getItem() instanceof Scroll scroll) { @@ -130,6 +138,8 @@ else if (Utils.canBeUpgraded(baseItemStack) && UpgradeData.getUpgradeData(baseIt //Shriving Stone else if (modifierItemStack.is(ItemRegistry.SHRIVING_STONE.get())) { result = Utils.handleShriving(baseItemStack); + UpgradeData upgradeData = UpgradeData.getUpgradeData(baseItemStack); + upgradeData.getUpgrades().forEach((upgrade, count) -> additionalDrops.add(upgradeOrbFromType(upgrade, count))); } //Spell Slot upgrades else if (modifierItemStack.getItem() instanceof SpellSlotUpgradeItem spellSlotUpgradeItem) { @@ -144,11 +154,25 @@ else if (modifierItemStack.getItem() instanceof SpellSlotUpgradeItem spellSlotUp } } } + //Attune Affinity Ring + else if (baseItemStack.getItem() instanceof AffinityRing affinityRing && modifierItemStack.getItem() instanceof Scroll scroll) { + result = baseItemStack.copy(); + var scrollSlot = ISpellContainer.get(modifierItemStack).getSpellAtIndex(0); + AffinityData.setAffinityData(result, scrollSlot.getSpell()); + } } resultSlots.setItem(0, result); } + private ItemStack upgradeOrbFromType(UpgradeType type, int count) { + return type.getContainerItem().map(item -> { + var stack = new ItemStack(item.get()); + stack.setCount(count); + return stack; + }).orElse(ItemStack.EMPTY); + } + @Override protected ItemCombinerMenuSlotDefinition createInputSlotDefinitions() { //copied from anvil for 1.19.4 diff --git a/src/main/java/io/redspace/ironsspellbooks/gui/inscription_table/InscriptionTableMenu.java b/src/main/java/io/redspace/ironsspellbooks/gui/inscription_table/InscriptionTableMenu.java index 88899b76c..af7aebfd0 100644 --- a/src/main/java/io/redspace/ironsspellbooks/gui/inscription_table/InscriptionTableMenu.java +++ b/src/main/java/io/redspace/ironsspellbooks/gui/inscription_table/InscriptionTableMenu.java @@ -12,6 +12,7 @@ import net.minecraft.network.FriendlyByteBuf; import net.minecraft.world.Container; import net.minecraft.world.SimpleContainer; +import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Inventory; import net.minecraft.world.entity.player.Player; import net.minecraft.world.inventory.AbstractContainerMenu; @@ -271,7 +272,11 @@ private void addPlayerHotbar(Inventory playerInventory) { @Override public void removed(Player pPlayer) { if (fromCurioSlot) { - Utils.setPlayerSpellbookStack(pPlayer, spellBookSlot.remove(1)); + if (pPlayer.isDeadOrDying() || pPlayer.isRemoved()) { + pPlayer.level.addFreshEntity(new ItemEntity(pPlayer.level, pPlayer.getX(), pPlayer.getY(), pPlayer.getZ(), spellBookSlot.remove(1))); + } else { + Utils.setPlayerSpellbookStack(pPlayer, spellBookSlot.remove(1)); + } } super.removed(pPlayer); diff --git a/src/main/java/io/redspace/ironsspellbooks/item/armor/UpgradeType.java b/src/main/java/io/redspace/ironsspellbooks/item/armor/UpgradeType.java index 112646317..141737339 100644 --- a/src/main/java/io/redspace/ironsspellbooks/item/armor/UpgradeType.java +++ b/src/main/java/io/redspace/ironsspellbooks/item/armor/UpgradeType.java @@ -3,10 +3,12 @@ import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.ai.attributes.Attribute; import net.minecraft.world.entity.ai.attributes.AttributeModifier; +import net.minecraft.world.item.Item; import java.util.HashMap; import java.util.Map; import java.util.Optional; +import java.util.function.Supplier; public interface UpgradeType { @@ -28,4 +30,6 @@ static Optional getUpgrade(ResourceLocation key) { float getAmountPerUpgrade(); ResourceLocation getId(); + + Optional> getContainerItem(); } diff --git a/src/main/java/io/redspace/ironsspellbooks/item/armor/UpgradeTypes.java b/src/main/java/io/redspace/ironsspellbooks/item/armor/UpgradeTypes.java index c23acce3f..d79950222 100644 --- a/src/main/java/io/redspace/ironsspellbooks/item/armor/UpgradeTypes.java +++ b/src/main/java/io/redspace/ironsspellbooks/item/armor/UpgradeTypes.java @@ -2,38 +2,49 @@ import io.redspace.ironsspellbooks.IronsSpellbooks; import io.redspace.ironsspellbooks.api.registry.AttributeRegistry; +import io.redspace.ironsspellbooks.registries.ItemRegistry; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.entity.ai.attributes.Attribute; import net.minecraft.world.entity.ai.attributes.AttributeModifier; import net.minecraft.world.entity.ai.attributes.Attributes; +import net.minecraft.world.item.Item; + +import java.util.Optional; +import java.util.function.Supplier; public enum UpgradeTypes implements UpgradeType { - FIRE_SPELL_POWER("fire_power", AttributeRegistry.FIRE_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), - ICE_SPELL_POWER("ice_power", AttributeRegistry.ICE_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), - LIGHTNING_SPELL_POWER("lightning_power", AttributeRegistry.LIGHTNING_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), - HOLY_SPELL_POWER("holy_power", AttributeRegistry.HOLY_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), - ENDER_SPELL_POWER("ender_power", AttributeRegistry.ENDER_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), - BLOOD_SPELL_POWER("blood_power", AttributeRegistry.BLOOD_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), - EVOCATION_SPELL_POWER("evocation_power", AttributeRegistry.EVOCATION_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), - NATURE_SPELL_POWER("nature_power", AttributeRegistry.NATURE_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), - COOLDOWN("cooldown", AttributeRegistry.COOLDOWN_REDUCTION.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), - SPELL_RESISTANCE("spell_resistance", AttributeRegistry.SPELL_RESIST.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), - MANA("mana", AttributeRegistry.MAX_MANA.get(), AttributeModifier.Operation.ADDITION, 50), - ATTACK_DAMAGE("melee_damage", Attributes.ATTACK_DAMAGE, AttributeModifier.Operation.MULTIPLY_BASE, .05f), - ATTACK_SPEED("melee_speed", Attributes.ATTACK_SPEED, AttributeModifier.Operation.MULTIPLY_BASE, .05f), - HEALTH("health", Attributes.MAX_HEALTH, AttributeModifier.Operation.ADDITION, 2), + FIRE_SPELL_POWER("fire_power", ItemRegistry.FIRE_UPGRADE_ORB, AttributeRegistry.FIRE_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), + ICE_SPELL_POWER("ice_power", ItemRegistry.ICE_UPGRADE_ORB, AttributeRegistry.ICE_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), + LIGHTNING_SPELL_POWER("lightning_power", ItemRegistry.LIGHTNING_UPGRADE_ORB, AttributeRegistry.LIGHTNING_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), + HOLY_SPELL_POWER("holy_power", ItemRegistry.HOLY_UPGRADE_ORB, AttributeRegistry.HOLY_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), + ENDER_SPELL_POWER("ender_power", ItemRegistry.ENDER_UPGRADE_ORB, AttributeRegistry.ENDER_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), + BLOOD_SPELL_POWER("blood_power", ItemRegistry.BLOOD_UPGRADE_ORB, AttributeRegistry.BLOOD_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), + EVOCATION_SPELL_POWER("evocation_power", ItemRegistry.EVOCATION_UPGRADE_ORB, AttributeRegistry.EVOCATION_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), + NATURE_SPELL_POWER("nature_power", ItemRegistry.NATURE_UPGRADE_ORB, AttributeRegistry.NATURE_SPELL_POWER.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), + COOLDOWN("cooldown", ItemRegistry.COOLDOWN_UPGRADE_ORB, AttributeRegistry.COOLDOWN_REDUCTION.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), + SPELL_RESISTANCE("spell_resistance", ItemRegistry.PROTECTION_UPGRADE_ORB, AttributeRegistry.SPELL_RESIST.get(), AttributeModifier.Operation.MULTIPLY_BASE, .05f), + MANA("mana", ItemRegistry.MANA_UPGRADE_ORB, AttributeRegistry.MAX_MANA.get(), AttributeModifier.Operation.ADDITION, 50), + ATTACK_DAMAGE("melee_damage", Optional.empty(), Attributes.ATTACK_DAMAGE, AttributeModifier.Operation.MULTIPLY_BASE, .05f), + ATTACK_SPEED("melee_speed", Optional.empty(), Attributes.ATTACK_SPEED, AttributeModifier.Operation.MULTIPLY_BASE, .05f), + HEALTH("health", Optional.empty(), Attributes.MAX_HEALTH, AttributeModifier.Operation.ADDITION, 2), ; final Attribute attribute; final AttributeModifier.Operation operation; final float amountPerUpgrade; final ResourceLocation id; + final Optional> containerItem; + + UpgradeTypes(String key, Supplier containerItem, Attribute attribute, AttributeModifier.Operation operation, float amountPerUpgrade) { + this(key, Optional.of(containerItem), attribute, operation, amountPerUpgrade); + } - UpgradeTypes(String key, Attribute attribute, AttributeModifier.Operation operation, float amountPerUpgrade) { + UpgradeTypes(String key, Optional> containerItem, Attribute attribute, AttributeModifier.Operation operation, float amountPerUpgrade) { this.id = IronsSpellbooks.id(key); this.attribute = attribute; this.operation = operation; this.amountPerUpgrade = amountPerUpgrade; + this.containerItem = containerItem; UpgradeType.registerUpgrade(this); } @@ -56,4 +67,9 @@ public float getAmountPerUpgrade() { public ResourceLocation getId() { return id; } + + @Override + public Optional> getContainerItem() { + return containerItem; + } } diff --git a/src/main/java/io/redspace/ironsspellbooks/item/weapons/AmethystRapierItem.java b/src/main/java/io/redspace/ironsspellbooks/item/weapons/AmethystRapierItem.java index 4904cf3b5..dd3e2e277 100644 --- a/src/main/java/io/redspace/ironsspellbooks/item/weapons/AmethystRapierItem.java +++ b/src/main/java/io/redspace/ironsspellbooks/item/weapons/AmethystRapierItem.java @@ -17,6 +17,6 @@ public AmethystRapierItem(SpellDataRegistryHolder[] imbuedSpells) { Map.of( // AttributeRegistry.COOLDOWN_REDUCTION.get(), new AttributeModifier(UUID.fromString("412b5a66-2b43-4c18-ab05-6de0bb4d64d3"), "Weapon Modifier", .15, AttributeModifier.Operation.MULTIPLY_BASE) ), - ItemPropertiesHelper.hidden(1).rarity(Rarity.EPIC)); + ItemPropertiesHelper.equipment(1).rarity(Rarity.EPIC)); } } diff --git a/src/main/java/io/redspace/ironsspellbooks/item/weapons/SpellbreakerItem.java b/src/main/java/io/redspace/ironsspellbooks/item/weapons/SpellbreakerItem.java index b108dc403..87188194b 100644 --- a/src/main/java/io/redspace/ironsspellbooks/item/weapons/SpellbreakerItem.java +++ b/src/main/java/io/redspace/ironsspellbooks/item/weapons/SpellbreakerItem.java @@ -18,6 +18,6 @@ public SpellbreakerItem(SpellDataRegistryHolder[] imbuedSpells) { Map.of( AttributeRegistry.COOLDOWN_REDUCTION.get(), new AttributeModifier(UUID.fromString("412b5a66-2b43-4c18-ab05-6de0bb4d64d3"), "Weapon Modifier", .15, AttributeModifier.Operation.MULTIPLY_BASE) ), - ItemPropertiesHelper.hidden(1).rarity(Rarity.EPIC)); + ItemPropertiesHelper.equipment(1).rarity(Rarity.EPIC)); } } diff --git a/src/main/java/io/redspace/ironsspellbooks/jei/AlchemistCauldronRecipeCategory.java b/src/main/java/io/redspace/ironsspellbooks/jei/AlchemistCauldronRecipeCategory.java index 7681917c0..61c01d124 100644 --- a/src/main/java/io/redspace/ironsspellbooks/jei/AlchemistCauldronRecipeCategory.java +++ b/src/main/java/io/redspace/ironsspellbooks/jei/AlchemistCauldronRecipeCategory.java @@ -17,10 +17,8 @@ import mezz.jei.api.recipe.category.IRecipeCategory; import net.minecraft.ChatFormatting; import net.minecraft.client.Minecraft; -import net.minecraft.client.gui.GuiGraphics; import net.minecraft.network.chat.Component; import net.minecraft.world.item.ItemStack; -import org.jetbrains.annotations.NotNull; import java.util.List; import java.util.Optional; @@ -91,9 +89,8 @@ public void setRecipe(IRecipeLayoutBuilder builder, AlchemistCauldronJeiRecipe r } } - @Override - public void draw(@NotNull AlchemistCauldronJeiRecipe recipe, IRecipeSlotsView recipeSlotsView, @NotNull GuiGraphics guiHelper, double mouseX, double mouseY) { + public void draw(AlchemistCauldronJeiRecipe recipe, IRecipeSlotsView recipeSlotsView, PoseStack stack, double mouseX, double mouseY) { Optional leftStack = recipeSlotsView.findSlotByName(inputSlotName) .flatMap(IRecipeSlotView::getDisplayedItemStack); @@ -103,21 +100,21 @@ public void draw(@NotNull AlchemistCauldronJeiRecipe recipe, IRecipeSlotsView re Optional outputStack = recipeSlotsView.findSlotByName(outputSlotName) .flatMap(IRecipeSlotView::getDisplayedItemStack); - guiHelper.pose().pushPose(); + stack.pushPose(); { - guiHelper.pose().translate((getWidth() / 2) - 8 * 1.4f, (getHeight() / 2) - 2, 0); - guiHelper.pose().scale(1.4f, 1.4f, 1.4f); - cauldron_block_icon.draw(guiHelper); + stack.translate((getWidth() / 2) - 8 * 1.4f, (getHeight() / 2) - 2, 0); + stack.scale(1.4f, 1.4f, 1.4f); + cauldron_block_icon.draw(stack); } - guiHelper.pose().popPose(); + stack.popPose(); if (leftStack.isPresent() && leftStack.get().is(ItemRegistry.SCROLL.get())) { var inputText = String.format("%s%%", (int) (ServerConfigs.SCROLL_RECYCLE_CHANCE.get() * 100)); var font = Minecraft.getInstance().font; - int y = (getHeight() / 2) - 14; - int x = (getWidth() - font.width(inputText)) / 2; - guiHelper.drawString(font, inputText, x, y, Math.min(ServerConfigs.SCROLL_RECYCLE_CHANCE.get(), 1d) == 1d ? ChatFormatting.GREEN.getColor() : ChatFormatting.RED.getColor()); + int y = (getHeight() / 2); + int x = (getWidth() - font.width(inputText)) * 3 / 4; + font.drawShadow(stack, inputText, x, y, Math.min(ServerConfigs.SCROLL_RECYCLE_CHANCE.get(), 1d) == 1d ? ChatFormatting.GREEN.getColor() : ChatFormatting.RED.getColor()); } } } diff --git a/src/main/java/io/redspace/ironsspellbooks/jei/AlchemistCauldronRecipeMaker.java b/src/main/java/io/redspace/ironsspellbooks/jei/AlchemistCauldronRecipeMaker.java index 03c177d77..9c84cf781 100644 --- a/src/main/java/io/redspace/ironsspellbooks/jei/AlchemistCauldronRecipeMaker.java +++ b/src/main/java/io/redspace/ironsspellbooks/jei/AlchemistCauldronRecipeMaker.java @@ -6,14 +6,18 @@ import io.redspace.ironsspellbooks.api.spells.ISpellContainer; import io.redspace.ironsspellbooks.api.spells.SpellRarity; import io.redspace.ironsspellbooks.block.alchemist_cauldron.AlchemistCauldronRecipeRegistry; -import io.redspace.ironsspellbooks.block.alchemist_cauldron.AlchemistCauldronTile; +import io.redspace.ironsspellbooks.block.alchemist_cauldron.CauldronPlatformHelper; import io.redspace.ironsspellbooks.config.ServerConfigs; +import io.redspace.ironsspellbooks.item.InkItem; import io.redspace.ironsspellbooks.registries.ItemRegistry; import mezz.jei.api.recipe.vanilla.IVanillaRecipeFactory; import mezz.jei.api.runtime.IIngredientManager; +import net.minecraft.client.Minecraft; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraft.world.item.PotionItem; -import net.minecraft.world.item.alchemy.PotionBrewing; +import net.minecraft.world.item.alchemy.PotionUtils; +import net.minecraft.world.item.alchemy.Potions; import net.minecraftforge.common.brewing.BrewingRecipeRegistry; import java.util.ArrayList; @@ -42,34 +46,21 @@ private static Stream getScrollRecipes(IVanillaRecip } private static Stream getCustomRecipes(IVanillaRecipeFactory vanillaRecipeFactory, IIngredientManager ingredientManager) { - var recipes = AlchemistCauldronRecipeRegistry.getRecipes(); List reagents = ingredientManager.getAllItemStacks().stream() .filter(AlchemistCauldronRecipeRegistry::isValidIngredient) .toList(); - //List reagents = new ArrayList<>(); - //List catalysts = new ArrayList<>(); - //List outputs = new ArrayList<>(); - //for(AlchemistCauldronRecipe recipe : recipes){ - // reagents.add(recipe.getIngredient()); - // catalysts.add(recipe.getInput()); - // outputs.add(recipe.getResult()); - //} - //return new AlchemistCauldronJeiRecipe(reagents, outputs, catalysts); - return reagents.stream().map((reagentStack) -> { - List catalysts = new ArrayList<>(); - List outputs = new ArrayList<>(); - AlchemistCauldronRecipeRegistry.getRecipes().forEach((recipe) -> { - if (ItemStack.isSameItemSameTags(reagentStack, recipe.getIngredient())) { - catalysts.add(recipe.getInput()); + return reagents.stream().flatMap(reagentStack -> + AlchemistCauldronRecipeRegistry.getRecipes().stream().filter((recipe) -> CauldronPlatformHelper.itemMatches(reagentStack, recipe.getIngredient())).map(recipe -> + { ItemStack result = recipe.getResult(); - if (result.getCount() == 4) + if (result.getCount() == 4) { result.setCount(1); - - outputs.add(result); - } - }); - return new AlchemistCauldronJeiRecipe(List.of(reagentStack), outputs, catalysts); - }); + } + return new AlchemistCauldronJeiRecipe( + List.of(reagentStack), + List.of(result), + List.of(recipe.getInput())); + })); } private static Stream getPotionRecipes(IVanillaRecipeFactory vanillaRecipeFactory, IIngredientManager ingredientManager) { @@ -80,64 +71,18 @@ private static Stream getPotionRecipes(IVanillaRecip List potionReagents = ingredientManager.getAllItemStacks().stream() .filter(AlchemistCauldronRecipeMaker::isIngredient) .toList(); - - //All in one -// List inputs = new ArrayList<>(); -// List catalysts = new ArrayList<>(); -// List outputs = new ArrayList<>(); -// potionReagents.forEach( -// (reagentStack) -> ingredientManager.getAllItemStacks().stream().filter((itemStack) -> BrewingRecipeRegistry.hasOutput(itemStack, reagentStack)).forEach((baseStack) -> { -// inputs.add(reagentStack); -// catalysts.add(baseStack); -// outputs.add(BrewingRecipeRegistry.getOutput(baseStack, reagentStack)); -// }) -// ); -// return Stream.of(new AlchemistCauldronRecipe(inputs, outputs, catalysts)); - - //Grouped by reagent - return potionReagents.stream().map((reagentStack) -> { - List catalysts = new ArrayList<>(); - List outputs = new ArrayList<>(); - ingredientManager.getAllItemStacks().stream().filter((itemStack) -> itemStack.getItem() instanceof PotionItem && BrewingRecipeRegistry.hasOutput(itemStack, reagentStack)).forEach((baseStack) -> { - catalysts.add(baseStack); - outputs.add(BrewingRecipeRegistry.getOutput(baseStack, reagentStack)); - }); - return new AlchemistCauldronJeiRecipe(List.of(reagentStack), outputs, catalysts); - }); - //Grouped by catalyst -// List potionCatalysts = ingredientManager.getAllItemStacks().stream() -// .filter((itemStack) -> { -// for (ItemStack reagentStack : potionReagents) -// if (BrewingRecipeRegistry.hasOutput(itemStack, reagentStack)) -// return true; -// return false; -// }) -// .toList(); -// return potionCatalysts.stream().map((catalystStack) -> { -// List reagents = new ArrayList<>(); -// List outputs = new ArrayList<>(); -// ingredientManager.getAllItemStacks().stream().filter((reagentStack) -> BrewingRecipeRegistry.hasOutput(catalystStack, reagentStack)).forEach((baseStack) -> { -// //inputs.add(reagentStack); -// reagents.add(baseStack); -// outputs.add(BrewingRecipeRegistry.getOutput(catalystStack, baseStack)); -// }); -// return new AlchemistCauldronRecipe(reagents, outputs, List.of(catalystStack)); -// }); - - - } - - private static List enumerateScrollLevels(AbstractSpell spell) { - var scrollStack = new ItemStack(ItemRegistry.SCROLL.get()); - - var scrolls = new ArrayList(); - - IntStream.rangeClosed(spell.getMinLevel(), spell.getMaxLevel()) - .forEach((spellLevel) -> { - scrolls.add(getScrollStack(scrollStack, spell, spellLevel)); - }); - - return scrolls; + var level = Minecraft.getInstance().level; + if (level == null) { + return Stream.of(); + } + return potionReagents.stream().flatMap(reagentStack -> + ingredientManager.getAllItemStacks().stream().filter((itemStack) -> itemStack.getItem() instanceof PotionItem && BrewingRecipeRegistry.hasOutput(itemStack, reagentStack)).map(baseItem -> + new AlchemistCauldronJeiRecipe( + List.of(reagentStack), + List.of(BrewingRecipeRegistry.getOutput(baseItem, reagentStack)), + List.of(baseItem) + )) + ); } private static AlchemistCauldronJeiRecipe enumerateSpellsForRarity(SpellRarity spellRarity) { @@ -154,15 +99,18 @@ private static AlchemistCauldronJeiRecipe enumerateSpellsForRarity(SpellRarity s inputs.add(getScrollStack(scrollStack, spell, filteredLevel)); }); }); - + ItemStack ink = new ItemStack(InkItem.getInkForRarity(spellRarity)); + ItemStack waterBottle = new ItemStack(Items.POTION); + PotionUtils.setPotion(waterBottle, Potions.WATER); inputs.forEach((itemStack -> { - catalysts.add(ItemStack.EMPTY); - outputs.add(new ItemStack(AlchemistCauldronTile.getInkFromScroll(itemStack))); + catalysts.add(waterBottle); + outputs.add(ink); })); return new AlchemistCauldronJeiRecipe(inputs, outputs, catalysts); } + private static ItemStack getScrollStack(ItemStack stack, AbstractSpell spell, int spellLevel) { var scrollStack = stack.copy(); ISpellContainer.createScrollContainer(spell, spellLevel, scrollStack); @@ -171,7 +119,7 @@ private static ItemStack getScrollStack(ItemStack stack, AbstractSpell spell, in private static boolean isIngredient(ItemStack itemStack) { try { - return PotionBrewing.isIngredient(itemStack); + return CauldronPlatformHelper.isBrewingIngredient(itemStack, Minecraft.getInstance().level); } catch (RuntimeException | LinkageError e) { IronsSpellbooks.LOGGER.error("Failed to check if item is a potion reagent {}.", itemStack.toString(), e); return false; diff --git a/src/main/java/io/redspace/ironsspellbooks/jei/ArcaneAnvilRecipe.java b/src/main/java/io/redspace/ironsspellbooks/jei/ArcaneAnvilRecipe.java index 53dc11b34..53e439585 100644 --- a/src/main/java/io/redspace/ironsspellbooks/jei/ArcaneAnvilRecipe.java +++ b/src/main/java/io/redspace/ironsspellbooks/jei/ArcaneAnvilRecipe.java @@ -1,15 +1,14 @@ package io.redspace.ironsspellbooks.jei; +import io.redspace.ironsspellbooks.api.item.curios.AffinityData; import io.redspace.ironsspellbooks.api.registry.SpellRegistry; import io.redspace.ironsspellbooks.api.spells.AbstractSpell; import io.redspace.ironsspellbooks.api.spells.ISpellContainer; import io.redspace.ironsspellbooks.capabilities.magic.UpgradeData; import io.redspace.ironsspellbooks.item.InkItem; -import io.redspace.ironsspellbooks.item.Scroll; import io.redspace.ironsspellbooks.item.UpgradeOrbItem; import io.redspace.ironsspellbooks.registries.ItemRegistry; import io.redspace.ironsspellbooks.util.UpgradeUtils; -import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import org.jetbrains.annotations.NotNull; @@ -23,7 +22,8 @@ public class ArcaneAnvilRecipe { enum Type { Scroll_Upgrade, Item_Upgrade, - Imbue + Imbue, + Affinity_Ring_Attune } @NotNull Type type; @@ -54,6 +54,11 @@ public ArcaneAnvilRecipe(AbstractSpell spell, int baseLevel) { this.type = Type.Scroll_Upgrade; } + public ArcaneAnvilRecipe(AbstractSpell spell) { + this.spell = spell; + this.type = Type.Affinity_Ring_Attune; + } + public Tuple, List, List> getRecipeItems() { return switch (this.type) { case Scroll_Upgrade -> { @@ -66,14 +71,13 @@ public Tuple, List, List> getRecipeItems() } case Imbue -> { var tuple = new Tuple, List, List>(new ArrayList(), new ArrayList(), new ArrayList()); + tuple.a.add(leftItem); SpellRegistry.getEnabledSpells().forEach(spell -> { IntStream.rangeClosed(spell.getMinLevel(), spell.getMaxLevel()).forEach(i -> { var scroll = new ItemStack(ItemRegistry.SCROLL.get()); ISpellContainer.createScrollContainer(spell, i, scroll); var result = leftItem.copy(); ISpellContainer.createScrollContainer(spell, i, result); - - tuple.a.add(leftItem); tuple.b.add(scroll); tuple.c.add(result); }); @@ -83,15 +87,32 @@ public Tuple, List, List> getRecipeItems() } case Item_Upgrade -> { var tuple = new Tuple, List, List>(new ArrayList(), new ArrayList(), new ArrayList()); + tuple.a.add(leftItem); rightItem.forEach(upgradeStack -> { var result = leftItem.copy(); UpgradeData.setUpgradeData(result, UpgradeData.NONE.addUpgrade(result, ((UpgradeOrbItem) upgradeStack.getItem()).getUpgradeType(), UpgradeUtils.getRelevantEquipmentSlot(leftItem))); - tuple.a.add(leftItem); tuple.b.add(upgradeStack); tuple.c.add(result); }); yield tuple; } + case Affinity_Ring_Attune -> { + var tuple = new Tuple, List, List>(new ArrayList(), new ArrayList(), new ArrayList()); + var result = new ItemStack(ItemRegistry.AFFINITY_RING.get()); + AffinityData.setAffinityData(result, this.spell); + SpellRegistry.getEnabledSpells().forEach(randomSpell -> { + var baseRing = new ItemStack(ItemRegistry.AFFINITY_RING.get()); + AffinityData.setAffinityData(baseRing, randomSpell); + tuple.a.add(baseRing); + }); + IntStream.rangeClosed(this.spell.getMinLevel(), this.spell.getMaxLevel()).forEach(i -> { + var scroll = new ItemStack(ItemRegistry.SCROLL.get()); + ISpellContainer.createScrollContainer(this.spell, i, scroll); + tuple.b.add(scroll); + }); + tuple.c.add(result); + yield tuple; + } }; } diff --git a/src/main/java/io/redspace/ironsspellbooks/jei/ArcaneAnvilRecipeCategory.java b/src/main/java/io/redspace/ironsspellbooks/jei/ArcaneAnvilRecipeCategory.java index 4a9db2ef0..b0f78c5df 100644 --- a/src/main/java/io/redspace/ironsspellbooks/jei/ArcaneAnvilRecipeCategory.java +++ b/src/main/java/io/redspace/ironsspellbooks/jei/ArcaneAnvilRecipeCategory.java @@ -61,10 +61,6 @@ public IDrawable getIcon() { @Override public void setRecipe(IRecipeLayoutBuilder builder, ArcaneAnvilRecipe recipe, IFocusGroup focuses) { - //TODO: optimization potential here: don't store 500 item stacks of scrolls, just enumerate them here and throw them away when done -// List leftInputs = recipe.leftInputs(); -// List rightInputs = recipe.rightInputs(); -// List outputs = recipe.outputs(); var recipeitems = recipe.getRecipeItems(); List leftInputs = recipeitems.a(); List rightInputs = recipeitems.b(); diff --git a/src/main/java/io/redspace/ironsspellbooks/jei/ArcaneAnvilRecipeMaker.java b/src/main/java/io/redspace/ironsspellbooks/jei/ArcaneAnvilRecipeMaker.java index 296f4de0f..f287345e9 100644 --- a/src/main/java/io/redspace/ironsspellbooks/jei/ArcaneAnvilRecipeMaker.java +++ b/src/main/java/io/redspace/ironsspellbooks/jei/ArcaneAnvilRecipeMaker.java @@ -32,7 +32,8 @@ public static List getRecipes(IVanillaRecipeFactory vanillaRe return Stream.of( getScrollRecipes(visibleItems), getImbueRecipes(visibleItems), - getUpgradeRecipes(visibleItems)) + getUpgradeRecipes(visibleItems), + getAffinityAttuneRecipes(visibleItems)) .flatMap(x -> x) .toList(); } @@ -41,7 +42,6 @@ private static Stream getScrollRecipes(List visibleItem return SpellRegistry.getEnabledSpells().stream() .sorted(Comparator.comparing(AbstractSpell::getSpellId)) .flatMap(spell -> IntStream.rangeClosed(spell.getMinLevel(), spell.getMaxLevel() - 1).mapToObj(i -> new ArcaneAnvilRecipe(spell, i))); - /*.filter(ArcaneAnvilRecipe::isValid)*///Filter out any blank recipes created where min and max spell level are equal } private static Stream getImbueRecipes(List visibleItems) { @@ -58,6 +58,12 @@ private static Stream getUpgradeRecipes(List visibleIte .map(item -> new ArcaneAnvilRecipe(new ItemStack(item), List.of(new ItemStack(upgradeOrb))))); } + private static Stream getAffinityAttuneRecipes(List visibleItems) { + return SpellRegistry.getEnabledSpells().stream() + .sorted(Comparator.comparing(AbstractSpell::getSpellId)) + .map(ArcaneAnvilRecipe::new); + } + public static List getVisibleItems() { return ForgeRegistries.ITEMS.getValues().stream().filter(item -> CreativeModeTabs.allTabs().stream().anyMatch(tab -> tab.contains(new ItemStack(item)))).toList(); } diff --git a/src/main/java/io/redspace/ironsspellbooks/registries/ItemRegistry.java b/src/main/java/io/redspace/ironsspellbooks/registries/ItemRegistry.java index c4920d0ef..dd0a9e373 100644 --- a/src/main/java/io/redspace/ironsspellbooks/registries/ItemRegistry.java +++ b/src/main/java/io/redspace/ironsspellbooks/registries/ItemRegistry.java @@ -176,6 +176,7 @@ public static void register(IEventBus eventBus) { public static final RegistryObject FROSTED_HELVE = ITEMS.register("frosted_helve", () -> new Item(ItemPropertiesHelper.material().rarity(Rarity.COMMON))); public static final RegistryObject ICE_CRYSTAL = ITEMS.register("permafrost_shard", () -> new Item(ItemPropertiesHelper.material().rarity(Rarity.RARE))); public static final RegistryObject ENERGIZED_CORE = ITEMS.register("energized_core", () -> new EnergizedCoreItem(ItemPropertiesHelper.material(1).rarity(Rarity.UNCOMMON))); + public static final RegistryObject WEAPON_PARTS = ITEMS.register("weapon_parts", () -> new Item(ItemPropertiesHelper.material().rarity(Rarity.RARE))); /** * Block Items diff --git a/src/main/java/io/redspace/ironsspellbooks/setup/ModSetup.java b/src/main/java/io/redspace/ironsspellbooks/setup/ModSetup.java index 79aa7a371..355515978 100644 --- a/src/main/java/io/redspace/ironsspellbooks/setup/ModSetup.java +++ b/src/main/java/io/redspace/ironsspellbooks/setup/ModSetup.java @@ -1,6 +1,7 @@ package io.redspace.ironsspellbooks.setup; import io.redspace.ironsspellbooks.block.alchemist_cauldron.AlchemistCauldronBlock; +import io.redspace.ironsspellbooks.block.alchemist_cauldron.AlchemistCauldronInteraction; import io.redspace.ironsspellbooks.block.alchemist_cauldron.AlchemistCauldronTile; import io.redspace.ironsspellbooks.capabilities.magic.MagicEvents; import io.redspace.ironsspellbooks.compat.CompatHandler; @@ -11,6 +12,7 @@ import net.minecraft.core.dispenser.OptionalDispenseItemBehavior; import net.minecraft.server.level.ServerLevel; import net.minecraft.world.entity.Entity; +import net.minecraft.world.item.Item; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.level.block.DispenserBlock; @@ -21,6 +23,9 @@ import net.minecraftforge.eventbus.api.IEventBus; import net.minecraftforge.fml.event.lifecycle.FMLCommonSetupEvent; +import java.util.Map; +import java.util.Optional; + public class ModSetup { public static void setup() { @@ -56,44 +61,48 @@ public static void init(FMLCommonSetupEvent event) { CompatHandler.init(); - event.enqueueWork(() -> - DispenserBlock.registerBehavior(Items.GLASS_BOTTLE.asItem(), new OptionalDispenseItemBehavior() { - private final DefaultDispenseItemBehavior defaultDispenseItemBehavior = new DefaultDispenseItemBehavior(); - final DispenseItemBehavior oldBehavior = DispenserBlock.DISPENSER_REGISTRY.get(Items.GLASS_BOTTLE); - - //takeLiquid copied from the other dispenser interactions - private ItemStack takeLiquid(BlockSource p_123447_, ItemStack p_123448_, ItemStack p_123449_) { - p_123448_.shrink(1); - if (p_123448_.isEmpty()) { - p_123447_.getLevel().gameEvent(null, GameEvent.FLUID_PICKUP, p_123447_.getPos()); - return p_123449_.copy(); - } else { - if (p_123447_.getEntity().addItem(p_123449_.copy()) < 0) { - this.defaultDispenseItemBehavior.dispense(p_123447_, p_123449_.copy()); - } + event.enqueueWork(() -> { + for (Map.Entry< Item, AlchemistCauldronInteraction > entry : AlchemistCauldronTile.INTERACTIONS.entrySet()) { + var item = entry.getKey(); + DispenserBlock.registerBehavior(item, new OptionalDispenseItemBehavior() { + private final DefaultDispenseItemBehavior defaultDispenseItemBehavior = new DefaultDispenseItemBehavior(); + final Optional oldBehavior = DispenserBlock.DISPENSER_REGISTRY.containsKey(item) ? Optional.of(DispenserBlock.DISPENSER_REGISTRY.get(item)) : Optional.empty(); - return p_123448_; + //takeLiquid copied from the other dispenser interactions + private ItemStack takeLiquid(BlockSource p_123447_, ItemStack p_123448_, ItemStack p_123449_) { + p_123448_.shrink(1); + if (p_123448_.isEmpty()) { + p_123447_.getLevel().gameEvent((Entity)null, GameEvent.FLUID_PICKUP, p_123447_.getPos()); + return p_123449_.copy(); + } else { + if (((DispenserBlockEntity)p_123447_.getEntity()).addItem(p_123449_.copy()) < 0) { + this.defaultDispenseItemBehavior.dispense(p_123447_, p_123449_.copy()); } + + return p_123448_; } + } - /** - * Dispense the specified stack, play the dispense sound, and spawn particles. - */ - public ItemStack execute(BlockSource blockSource, ItemStack itemStack) { - this.setSuccess(false); - ServerLevel serverlevel = blockSource.getLevel(); - BlockPos blockpos = blockSource.getPos().relative(blockSource.getBlockState().getValue(DispenserBlock.FACING)); - BlockState blockstate = serverlevel.getBlockState(blockpos); - if (AlchemistCauldronBlock.getLevel(blockstate) > 0 && serverlevel.getBlockEntity(blockpos) instanceof AlchemistCauldronTile cauldron) { - var resultStack = cauldron.interactions.get(itemStack.getItem()).interact(blockstate, serverlevel, blockpos, AlchemistCauldronBlock.getLevel(blockstate), itemStack); - if (resultStack != null) { - this.setSuccess(true); - cauldron.setChanged(); - return this.takeLiquid(blockSource, itemStack, resultStack); - } + /** + * Dispense the specified stack, play the dispense sound, and spawn particles. + */ + public ItemStack execute(BlockSource blockSource, ItemStack itemStack) { + this.setSuccess(false); + ServerLevel serverlevel = blockSource.getLevel(); + BlockPos blockpos = blockSource.getPos().relative(blockSource.getBlockState().getValue(DispenserBlock.FACING)); + BlockState blockstate = serverlevel.getBlockState(blockpos); + if (serverlevel.getBlockEntity(blockpos) instanceof AlchemistCauldronTile cauldron) { + var resultStack = cauldron.INTERACTIONS.get(itemStack.getItem()).interact(cauldron, blockstate, serverlevel, blockpos, itemStack); + if (resultStack != null) { + this.setSuccess(true); + cauldron.setChanged(); + return this.takeLiquid(blockSource, itemStack, resultStack); } - return oldBehavior.dispense(blockSource, itemStack); } - })); + return oldBehavior.map(behavior -> behavior.dispense(blockSource, itemStack)).orElse(itemStack); + } + }); + } + }); } } \ No newline at end of file diff --git a/src/main/resources/assets/irons_spellbooks/lang/en_us.json b/src/main/resources/assets/irons_spellbooks/lang/en_us.json index 8b2a4f60b..bcc49c30c 100644 --- a/src/main/resources/assets/irons_spellbooks/lang/en_us.json +++ b/src/main/resources/assets/irons_spellbooks/lang/en_us.json @@ -37,6 +37,7 @@ "item.irons_spellbooks.arcane_essence": "Arcane Essence", "item.irons_spellbooks.cinder_essence": "Cinder Essence", "item.irons_spellbooks.cinder_essence.guide": "Dropped from Ancient Knights.", + "item.irons_spellbooks.weapon_parts": "Weapon Parts", "item.irons_spellbooks.dev_staff": "Staff of Testing", "item.irons_spellbooks.staff_of_the_nines": "Staff of the Nines", "item.irons_spellbooks.arcane_salvage": "Arcane Salvage", diff --git a/src/main/resources/assets/irons_spellbooks/models/item/affinity_ring_eldritch.json b/src/main/resources/assets/irons_spellbooks/models/item/affinity_ring_eldritch.json new file mode 100644 index 000000000..27ed3ca8f --- /dev/null +++ b/src/main/resources/assets/irons_spellbooks/models/item/affinity_ring_eldritch.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "irons_spellbooks:item/affinity_ring_eldritch" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/irons_spellbooks/models/item/weapon_parts.json b/src/main/resources/assets/irons_spellbooks/models/item/weapon_parts.json new file mode 100644 index 000000000..db22438e1 --- /dev/null +++ b/src/main/resources/assets/irons_spellbooks/models/item/weapon_parts.json @@ -0,0 +1,6 @@ +{ + "parent": "item/generated", + "textures": { + "layer0": "irons_spellbooks:item/weapon_parts" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/irons_spellbooks/textures/item/affinity_ring_eldritch.png b/src/main/resources/assets/irons_spellbooks/textures/item/affinity_ring_eldritch.png new file mode 100644 index 000000000..f202573db Binary files /dev/null and b/src/main/resources/assets/irons_spellbooks/textures/item/affinity_ring_eldritch.png differ diff --git a/src/main/resources/assets/irons_spellbooks/textures/item/weapon_parts.png b/src/main/resources/assets/irons_spellbooks/textures/item/weapon_parts.png new file mode 100644 index 000000000..b3c6ea617 Binary files /dev/null and b/src/main/resources/assets/irons_spellbooks/textures/item/weapon_parts.png differ diff --git a/src/main/resources/data/irons_spellbooks/loot_tables/blocks/firefly_jar.json b/src/main/resources/data/irons_spellbooks/loot_tables/blocks/firefly_jar.json new file mode 100644 index 000000000..4e14dbc0d --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/loot_tables/blocks/firefly_jar.json @@ -0,0 +1,14 @@ +{ + "type": "minecraft:block", + "pools": [ + { + "rolls": 1, + "entries": [ + { + "type": "minecraft:item", + "name": "irons_spellbooks:firefly_jar" + } + ] + } + ] +} diff --git a/src/main/resources/data/irons_spellbooks/recipes/amethyst_rapier.json b/src/main/resources/data/irons_spellbooks/recipes/amethyst_rapier.json new file mode 100644 index 000000000..fe6b874ab --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/recipes/amethyst_rapier.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + " A", + "CA ", + "WC " + ], + "key": { + "A": { + "item": "minecraft:amethyst_shard" + }, + "C": { + "item": "minecraft:chain" + }, + "W": { + "item": "irons_spellbooks:weapon_parts" + } + }, + "result": { + "item": "irons_spellbooks:amethyst_rapier", + "count": 1 + } +} \ No newline at end of file diff --git a/src/main/resources/data/irons_spellbooks/recipes/arcane_salvage_from_curio.json b/src/main/resources/data/irons_spellbooks/recipes/arcane_salvage_from_curio.json new file mode 100644 index 000000000..1fe0c2e1a --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/recipes/arcane_salvage_from_curio.json @@ -0,0 +1,11 @@ +{ + "type": "minecraft:blasting", + "cookingtime": 100, + "experience": 0.0, + "ingredient": { + "tag": "irons_spellbooks:salvageable_curio" + }, + "result": { + "item": "irons_spellbooks:arcane_salvage" + } +} \ No newline at end of file diff --git a/src/main/resources/data/irons_spellbooks/recipes/curios/affinity_ring.json b/src/main/resources/data/irons_spellbooks/recipes/curios/affinity_ring.json new file mode 100644 index 000000000..b2bdc5634 --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/recipes/curios/affinity_ring.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "M": { + "item": "minecraft:bucket" + }, + "X": { + "item": "irons_spellbooks:arcane_salvage" + } + }, + "pattern": [ + "M ", + " X" + ], + "result": { + "count": 1, + "item": "irons_spellbooks:affinity_ring" + } +} \ No newline at end of file diff --git a/src/main/resources/data/irons_spellbooks/recipes/curios/cast_time_ring.json b/src/main/resources/data/irons_spellbooks/recipes/curios/cast_time_ring.json new file mode 100644 index 000000000..328218d4e --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/recipes/curios/cast_time_ring.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "M": { + "item": "minecraft:amethyst_shard" + }, + "X": { + "item": "irons_spellbooks:arcane_salvage" + } + }, + "pattern": [ + "M ", + " X" + ], + "result": { + "count": 1, + "item": "irons_spellbooks:cast_time_ring" + } +} \ No newline at end of file diff --git a/src/main/resources/data/irons_spellbooks/recipes/curios/concentration_amulet.json b/src/main/resources/data/irons_spellbooks/recipes/curios/concentration_amulet.json new file mode 100644 index 000000000..c3bc825d5 --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/recipes/curios/concentration_amulet.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "M": { + "item": "irons_spellbooks:mithril_ingot" + }, + "S": { + "item": "minecraft:chain" + }, + "X": { + "item": "irons_spellbooks:arcane_salvage" + } + }, + "pattern": [ + " S ", + "SXS", + " M " + ], + "result": { + "count": 1, + "item": "irons_spellbooks:concentration_amulet" + } +} \ No newline at end of file diff --git a/src/main/resources/data/irons_spellbooks/recipes/curios/conjurers_talisman.json b/src/main/resources/data/irons_spellbooks/recipes/curios/conjurers_talisman.json new file mode 100644 index 000000000..e3373c8f7 --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/recipes/curios/conjurers_talisman.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "M": { + "item": "minecraft:skeleton_skull" + }, + "S": { + "item": "minecraft:string" + }, + "X": { + "item": "irons_spellbooks:arcane_salvage" + } + }, + "pattern": [ + " S ", + "SXS", + " M " + ], + "result": { + "count": 1, + "item": "irons_spellbooks:conjurers_talisman" + } +} \ No newline at end of file diff --git a/src/main/resources/data/irons_spellbooks/recipes/curios/cooldown_ring.json b/src/main/resources/data/irons_spellbooks/recipes/curios/cooldown_ring.json new file mode 100644 index 000000000..26ddd588e --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/recipes/curios/cooldown_ring.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "M": { + "tag": "forge:ingots/copper" + }, + "X": { + "item": "irons_spellbooks:arcane_salvage" + } + }, + "pattern": [ + " M ", + "MXM", + " M " + ], + "result": { + "count": 1, + "item": "irons_spellbooks:cooldown_ring" + } +} \ No newline at end of file diff --git a/src/main/resources/data/irons_spellbooks/recipes/curios/emerald_stoneplate_ring.json b/src/main/resources/data/irons_spellbooks/recipes/curios/emerald_stoneplate_ring.json new file mode 100644 index 000000000..baf5c349e --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/recipes/curios/emerald_stoneplate_ring.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "M": { + "item": "minecraft:experience_bottle" + }, + "X": { + "item": "irons_spellbooks:arcane_salvage" + } + }, + "pattern": [ + "M ", + " X" + ], + "result": { + "count": 1, + "item": "irons_spellbooks:emerald_stoneplate_ring" + } +} \ No newline at end of file diff --git a/src/main/resources/data/irons_spellbooks/recipes/curios/fireward_ring.json b/src/main/resources/data/irons_spellbooks/recipes/curios/fireward_ring.json new file mode 100644 index 000000000..568e4be10 --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/recipes/curios/fireward_ring.json @@ -0,0 +1,21 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "M": { + "item": "irons_spellbooks:cinder_essence" + }, + "X": { + "item": "irons_spellbooks:arcane_salvage" + } + }, + "pattern": [ + " M ", + "MXM", + " M " + ], + "result": { + "count": 1, + "item": "irons_spellbooks:fireward_ring" + } +} \ No newline at end of file diff --git a/src/main/resources/data/irons_spellbooks/recipes/curios/frostward_ring.json b/src/main/resources/data/irons_spellbooks/recipes/curios/frostward_ring.json new file mode 100644 index 000000000..4845ad339 --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/recipes/curios/frostward_ring.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "M": { + "item": "irons_spellbooks:permafrost_shard" + }, + "X": { + "item": "irons_spellbooks:arcane_salvage" + } + }, + "pattern": [ + "M ", + " X" + ], + "result": { + "count": 1, + "item": "irons_spellbooks:frostward_ring" + } +} \ No newline at end of file diff --git a/src/main/resources/data/irons_spellbooks/recipes/curios/heavy_chain_necklace.json b/src/main/resources/data/irons_spellbooks/recipes/curios/heavy_chain_necklace.json new file mode 100644 index 000000000..9e2ab9e3f --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/recipes/curios/heavy_chain_necklace.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "M": { + "item": "minecraft:chain" + }, + "S": { + "item": "minecraft:chain" + }, + "X": { + "item": "irons_spellbooks:arcane_salvage" + } + }, + "pattern": [ + " S ", + "SXS", + " M " + ], + "result": { + "count": 1, + "item": "irons_spellbooks:heavy_chain_necklace" + } +} \ No newline at end of file diff --git a/src/main/resources/data/irons_spellbooks/recipes/curios/poisonward_ring.json b/src/main/resources/data/irons_spellbooks/recipes/curios/poisonward_ring.json new file mode 100644 index 000000000..dbd6a7ee5 --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/recipes/curios/poisonward_ring.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "misc", + "key": { + "M": { + "item": "irons_spellbooks:nature_rune" + }, + "X": { + "item": "irons_spellbooks:arcane_salvage" + } + }, + "pattern": [ + "M ", + " X" + ], + "result": { + "count": 1, + "item": "irons_spellbooks:poisonward_ring" + } +} \ No newline at end of file diff --git a/src/main/resources/data/irons_spellbooks/recipes/spellbreaker.json b/src/main/resources/data/irons_spellbooks/recipes/spellbreaker.json new file mode 100644 index 000000000..87a6b5496 --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/recipes/spellbreaker.json @@ -0,0 +1,23 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + " M", + "DM ", + "WD " + ], + "key": { + "M": { + "item": "irons_spellbooks:arcane_salvage" + }, + "D": { + "tag": "forge:gems/diamond" + }, + "W": { + "item": "irons_spellbooks:weapon_parts" + } + }, + "result": { + "item": "irons_spellbooks:spellbreaker", + "count": 1 + } +} \ No newline at end of file diff --git a/src/main/resources/data/irons_spellbooks/recipes/weapon_parts.json b/src/main/resources/data/irons_spellbooks/recipes/weapon_parts.json new file mode 100644 index 000000000..a9bfb9eda --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/recipes/weapon_parts.json @@ -0,0 +1,20 @@ +{ + "type": "minecraft:crafting_shaped", + "pattern": [ + " IM", + " MI", + "M " + ], + "key": { + "M": { + "item": "irons_spellbooks:arcane_salvage" + }, + "I": { + "item": "irons_spellbooks:arcane_ingot" + } + }, + "result": { + "item": "irons_spellbooks:weapon_parts", + "count": 1 + } +} \ No newline at end of file diff --git a/src/main/resources/data/irons_spellbooks/tags/items/salvageable_curio.json b/src/main/resources/data/irons_spellbooks/tags/items/salvageable_curio.json new file mode 100644 index 000000000..ea2318c5b --- /dev/null +++ b/src/main/resources/data/irons_spellbooks/tags/items/salvageable_curio.json @@ -0,0 +1,17 @@ +{ + "replace": false, + "values": [ + "irons_spellbooks:cooldown_ring", + "irons_spellbooks:cast_time_ring", + "irons_spellbooks:silver_ring", + "irons_spellbooks:emerald_stoneplate_ring", + "irons_spellbooks:fireward_ring", + "irons_spellbooks:frostward_ring", + "irons_spellbooks:poisonward_ring", + "irons_spellbooks:affinity_ring", + "irons_spellbooks:invisibility_ring", + "irons_spellbooks:heavy_chain_necklace", + "irons_spellbooks:conjurers_talisman", + "irons_spellbooks:concentration_amulet" + ] +} \ No newline at end of file