From 5a780d0ce81db6c64a348d0f7e832b020a96821e Mon Sep 17 00:00:00 2001 From: stellanera Date: Mon, 13 Apr 2026 17:54:41 +0200 Subject: [PATCH 1/5] skeleton exists, but I got distracted by talking about the beacon effect thingies and now I wanna make flasks and alchemy stuff --- .../common/block/ArcaneAshesBlock.java | 77 +++++++++++++++++++ .../bloodmagic/common/block/BMBlocks.java | 2 + .../common/blockentity/ArcaneAshesTile.java | 15 ++++ .../common/blockentity/BMTiles.java | 3 + .../common/item/ArcaneAshesItem.java | 21 +++++ 5 files changed, 118 insertions(+) create mode 100644 src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java create mode 100644 src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java create mode 100644 src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java diff --git a/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java b/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java new file mode 100644 index 0000000000..a362759368 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java @@ -0,0 +1,77 @@ +package wayoftime.bloodmagic.common.block; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.ItemInteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +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.DirectionProperty; +import net.minecraft.world.phys.BlockHitResult; +import org.jetbrains.annotations.Nullable; +import wayoftime.bloodmagic.common.blockentity.ArcaneAshesTile; + +public class ArcaneAshesBlock extends Block implements EntityBlock { + + public static final DirectionProperty FACING = BlockStateProperties.FACING; + + public ArcaneAshesBlock() { + super(Properties.of() + .instabreak() // feels right not to have to mine it + .noCollission() + .ignitedByLava() + ); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(FACING); + } + + @Override + protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult) { + // insert items, on second check if first is flask -> beacon; else recipe lookup. if either true + //level.scheduleTick(pos, state.getBlock(), 1); + //return ItemInteractionResult.sidedSuccess(level.isClientSide); + + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + + @Override + protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + BlockEntity be = level.getBlockEntity(pos); + if (!(be instanceof ArcaneAshesTile ashes)) { + return; + } + + // advance crafting + // reapply beacon + //level.scheduleTick(pos, state.getBlock(), 1); + } + + @Override + protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { + // TODO check if valid trap items -> do trap; else do nothing + } + + @Nullable + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @Override + public @Nullable BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new ArcaneAshesTile(pos, state); + } +} diff --git a/src/main/java/wayoftime/bloodmagic/common/block/BMBlocks.java b/src/main/java/wayoftime/bloodmagic/common/block/BMBlocks.java index 1544cba42a..08b1c722fc 100644 --- a/src/main/java/wayoftime/bloodmagic/common/block/BMBlocks.java +++ b/src/main/java/wayoftime/bloodmagic/common/block/BMBlocks.java @@ -17,6 +17,7 @@ import wayoftime.bloodmagic.common.datamap.BMDataMaps; import wayoftime.bloodmagic.common.datamap.BloodRune; import wayoftime.bloodmagic.api.altar.EnumRuneType; +import wayoftime.bloodmagic.common.item.ArcaneAshesItem; import wayoftime.bloodmagic.util.BlockEntityHelper; import wayoftime.bloodmagic.util.blockitem.BlockWithItemHolder; import wayoftime.bloodmagic.util.blockitem.BlockWithItemRegister; @@ -38,6 +39,7 @@ public class BMBlocks { public static final BlockWithItemHolder BLOOD_TANK = BLOCK_REG.register("blood_tank", BloodTankBlock::new, block -> new BlockItem(block, new Item.Properties().component(BMDataComponents.CONTAINER_TIER, 1))); public static final BlockWithItemHolder HELLFIRE_FORGE = BLOCK_REG.register("hellfire_forge", HellfireForgeBlock::new); public static final BlockWithItemHolder ARC_BLOCK = BLOCK_REG.register("arc", ARCBlock::new); + public static final BlockWithItemHolder ARCANE_ASHES = BLOCK_REG.register("arcane_ashes", ArcaneAshesBlock::new, ArcaneAshesItem::new); // TODO add model/textures for this and change registry to BASIC_REG public static final BlockWithItemHolder LIVING_STATION = BLOCK_REG.register("living_station", LivingStationBlock::new); diff --git a/src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java b/src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java new file mode 100644 index 0000000000..fd228d1be6 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java @@ -0,0 +1,15 @@ +package wayoftime.bloodmagic.common.blockentity; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.items.ItemStackHandler; + +public class ArcaneAshesTile extends BlockEntity { + + public ItemStackHandler inv = new ItemStackHandler(2); + + public ArcaneAshesTile(BlockPos pos, BlockState blockState) { + super(BMTiles.ARCANE_ASHES.get(), pos, blockState); + } +} diff --git a/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java b/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java index ff8e0e2bda..1faa60cf90 100644 --- a/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java +++ b/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java @@ -36,6 +36,9 @@ public class BMTiles { public static final DeferredHolder, BlockEntityType> LIVING_STATION_TYPE = TILES.register("living_station", () -> new BlockEntityType<>(LivingStationTile::new, Set.of(BMBlocks.LIVING_STATION.block().get()), null)); + public static final DeferredHolder, BlockEntityType> ARCANE_ASHES = TILES.register("arcane_ashes", + () -> new BlockEntityType<>(ArcaneAshesTile::new, Set.of(BMBlocks.ARCANE_ASHES.block().get()), null)); + private static void registerTileCapabilities(RegisterCapabilitiesEvent event) { event.registerBlockEntity( Capabilities.ItemHandler.BLOCK, diff --git a/src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java b/src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java new file mode 100644 index 0000000000..b30b87587f --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java @@ -0,0 +1,21 @@ +package wayoftime.bloodmagic.common.item; + +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.block.Block; + +public class ArcaneAshesItem extends BlockItem { + public ArcaneAshesItem(Block block) { + super(block, new Properties() + .stacksTo(1) + .durability(42) // 1.20 has 20 but that seems low given that we want to give it more stuff to do + ); + } + + @Override + public InteractionResult useOn(UseOnContext context) { + context.getItemInHand().hurtAndBreak(1, context.getPlayer(), context.getItemInHand().getEquipmentSlot()); + return super.useOn(context); + } +} From c7e507a33554a4e5ad7a742fbf6697583929dc82 Mon Sep 17 00:00:00 2001 From: stellanera Date: Tue, 14 Apr 2026 19:09:02 +0200 Subject: [PATCH 2/5] ashes! --- .../datagen/provider/BMLanguageProvider.java | 2 + .../assets/bloodmagic/lang/en_us.json | 1 + .../common/block/ArcaneAshesBlock.java | 132 ++++++++++++++++-- .../common/blockentity/ArcaneAshesTile.java | 35 ++++- .../common/blockentity/BMTiles.java | 11 ++ .../common/item/ArcaneAshesItem.java | 56 +++++++- .../bloodmagic/common/recipe/BMRecipes.java | 6 + .../common/recipe/ash/AshCraftingRecipe.java | 30 ++++ .../recipe/ash/AshCraftingSerializer.java | 37 +++++ .../common/recipe/ash/AshInput.java | 15 ++ .../common/recipe/ash/AshRecipe.java | 19 +++ .../bloodmagic/util/BlockEntityHelper.java | 6 + .../bloodmagic/blockstates/arcane_ashes.json | 19 +++ .../bloodmagic/models/block/arcane_ashes.json | 17 +++ .../bloodmagic/models/item/arcane_ashes.json | 6 + .../bloodmagic/textures/block/wip_array.png | Bin 0 -> 16402 bytes .../bloodmagic/textures/item/arcane_ashes.png | Bin 0 -> 550 bytes 17 files changed, 380 insertions(+), 12 deletions(-) create mode 100644 src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingRecipe.java create mode 100644 src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingSerializer.java create mode 100644 src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshInput.java create mode 100644 src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshRecipe.java create mode 100755 src/main/resources/assets/bloodmagic/blockstates/arcane_ashes.json create mode 100644 src/main/resources/assets/bloodmagic/models/block/arcane_ashes.json create mode 100644 src/main/resources/assets/bloodmagic/models/item/arcane_ashes.json create mode 100755 src/main/resources/assets/bloodmagic/textures/block/wip_array.png create mode 100755 src/main/resources/assets/bloodmagic/textures/item/arcane_ashes.png diff --git a/src/datagen/java/wayoftime/bloodmagic/datagen/provider/BMLanguageProvider.java b/src/datagen/java/wayoftime/bloodmagic/datagen/provider/BMLanguageProvider.java index 42961a717b..9119af9033 100644 --- a/src/datagen/java/wayoftime/bloodmagic/datagen/provider/BMLanguageProvider.java +++ b/src/datagen/java/wayoftime/bloodmagic/datagen/provider/BMLanguageProvider.java @@ -85,6 +85,8 @@ protected void addTranslations() { addTooltip("fluid_content_empty", "Empty"); addTooltip("fluid_content", "Contains: %smB of %s"); + add(BMBlocks.ARCANE_ASHES, "Arcane Ashes"); + add(BMBlocks.IMPERFECT_RITUAL_BLOCK, "Imperfect Ritual Stone"); add(BMBlocks.HELLFIRE_FORGE, "Hellfire Forge"); diff --git a/src/generated/resources/assets/bloodmagic/lang/en_us.json b/src/generated/resources/assets/bloodmagic/lang/en_us.json index bedf5dc981..8f2563af0d 100644 --- a/src/generated/resources/assets/bloodmagic/lang/en_us.json +++ b/src/generated/resources/assets/bloodmagic/lang/en_us.json @@ -1,5 +1,6 @@ { "block.bloodmagic.arc": "Alchemical Reaction Chamber", + "block.bloodmagic.arcane_ashes": "Arcane Ashes", "block.bloodmagic.blood_altar": "Blood Altar", "block.bloodmagic.blood_tank": "Blood Tank", "block.bloodmagic.bloodstone": "Polished Bloodstone", diff --git a/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java b/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java index a362759368..e094eeecbf 100644 --- a/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java +++ b/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java @@ -1,14 +1,22 @@ package wayoftime.bloodmagic.common.block; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.particles.DustParticleOptions; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.RandomSource; +import net.minecraft.util.StringRepresentable; import net.minecraft.world.InteractionHand; import net.minecraft.world.ItemInteractionResult; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.item.crafting.RecipeHolder; +import net.minecraft.world.item.crafting.RecipeManager; +import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.EntityBlock; @@ -17,13 +25,25 @@ 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.DirectionProperty; +import net.minecraft.world.level.block.state.properties.EnumProperty; import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.neoforged.neoforge.items.wrapper.RecipeWrapper; import org.jetbrains.annotations.Nullable; import wayoftime.bloodmagic.common.blockentity.ArcaneAshesTile; +import wayoftime.bloodmagic.common.item.BMItems; +import wayoftime.bloodmagic.common.recipe.BMRecipes; +import wayoftime.bloodmagic.common.recipe.ash.AshCraftingRecipe; +import wayoftime.bloodmagic.common.recipe.ash.AshInput; +import wayoftime.bloodmagic.common.recipe.ash.AshRecipe; +import wayoftime.bloodmagic.util.BlockEntityHelper; public class ArcaneAshesBlock extends Block implements EntityBlock { public static final DirectionProperty FACING = BlockStateProperties.FACING; + public static final EnumProperty MODE = EnumProperty.create("state", AshMode.class); public ArcaneAshesBlock() { super(Properties.of() @@ -33,16 +53,46 @@ public ArcaneAshesBlock() { ); } + protected static final VoxelShape BOX = Block.box(0, 0, 0, 16, 1, 16); + @Override + protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { + return BOX; + } + @Override protected void createBlockStateDefinition(StateDefinition.Builder builder) { - builder.add(FACING); + builder.add(FACING, MODE); } + private static RecipeManager.CachedCheck lookup = null; @Override protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult) { - // insert items, on second check if first is flask -> beacon; else recipe lookup. if either true - //level.scheduleTick(pos, state.getBlock(), 1); - //return ItemInteractionResult.sidedSuccess(level.isClientSide); + BlockEntity be = level.getBlockEntity(pos); + if (!(be instanceof ArcaneAshesTile ashes)) { + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + ItemStack heldItem = player.getItemInHand(hand); + if (ashes.inv.getStackInSlot(0).isEmpty()) { + ashes.inv.setStackInSlot(0, heldItem.split(1)); + } else if (ashes.inv.getStackInSlot(1).isEmpty()) { + ashes.inv.setStackInSlot(1, heldItem.split(1)); + + if (ashes.inv.getStackInSlot(0).is(Items.POTION /*BMItems.FLASK.get()*/)) { + // TODO implement beacon effect + } else { + AshInput input = new AshInput(ashes.inv); + if (lookup == null) { + lookup = RecipeManager.createCheck(BMRecipes.ASH_TYPE.get()); + } + RecipeHolder holder = lookup.getRecipeFor(input, level).orElse(null); + if (holder != null) { + ashes.inv.setStackInSlot(2, holder.value().assemble(input, level.registryAccess())); + level.sendBlockUpdated(pos, state, state.setValue(MODE, AshMode.CRAFTING), UPDATE_ALL); + level.scheduleTick(pos, state.getBlock(), 1); + return ItemInteractionResult.sidedSuccess(level.isClientSide); + } + } + } return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; } @@ -54,24 +104,88 @@ protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSou return; } - // advance crafting - // reapply beacon - //level.scheduleTick(pos, state.getBlock(), 1); + AshMode mode = state.getValue(MODE); + switch (mode) { + case CRAFTING -> { + if (++ashes.progress >= 200) { + ItemStack output = ashes.inv.getStackInSlot(2); + if (level.isClientSide) { + Vec3 vec3 = Vec3.atLowerCornerWithOffset(pos, 0.5, 1 / 16f, 0.5).offsetRandom(level.random, 0.7f); + ItemEntity itementity = new ItemEntity(level, vec3.x(), vec3.y(), vec3.z(), output); + itementity.setDefaultPickUpDelay(); + level.addFreshEntity(itementity); + } + ashes.progress = 0; + ashes.inv.setStackInSlot(0, ItemStack.EMPTY); + ashes.inv.setStackInSlot(1, ItemStack.EMPTY); + ashes.inv.setStackInSlot(2, ItemStack.EMPTY); + level.sendBlockUpdated(pos, state, state.setValue(MODE, AshMode.IDLE), UPDATE_ALL); + return; + } + level.scheduleTick(pos, state.getBlock(), 1); + } + + case BEACON -> { + // TODO implement + } + + case IDLE -> { + // why has tick when idle? + } + } + } + + @Override + public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) { + super.animateTick(state, level, pos, random); + if (random.nextFloat() < 0.3f) { + switch (state.getValue(MODE)) { + case CRAFTING -> level.addParticle(new DustParticleOptions(DustParticleOptions.REDSTONE_PARTICLE_COLOR, 1.1f), pos.getX() + 0.5, pos.getY() + 0.1f, pos.getZ() + 0.5, 0.2, 0.2, 0.2); + case BEACON -> { + } + case IDLE -> { + } + } + } } @Override protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { - // TODO check if valid trap items -> do trap; else do nothing + // TODO implement + // check if valid trap items -> do trap; else do nothing } @Nullable @Override public BlockState getStateForPlacement(BlockPlaceContext context) { - return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + return this.defaultBlockState(). + setValue(MODE, AshMode.IDLE) + .setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @Override + protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { + if (!state.is(newState.getBlock())) { + if (level.getBlockEntity(pos) instanceof ArcaneAshesTile ash) { + BlockEntityHelper.dropContents(level, pos, ash.inv, 1); + } + } + super.onRemove(state, level, pos, newState, movedByPiston); } @Override public @Nullable BlockEntity newBlockEntity(BlockPos pos, BlockState state) { return new ArcaneAshesTile(pos, state); } + + public enum AshMode implements StringRepresentable { + IDLE, + CRAFTING, + BEACON; + + @Override + public String getSerializedName() { + return name().toLowerCase(); + } + } } diff --git a/src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java b/src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java index fd228d1be6..e6775aff87 100644 --- a/src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java +++ b/src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java @@ -1,15 +1,48 @@ package wayoftime.bloodmagic.common.blockentity; import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.RecipeHolder; +import net.minecraft.world.item.crafting.RecipeInput; +import net.minecraft.world.item.crafting.RecipeManager; +import net.minecraft.world.level.Level; +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.neoforged.neoforge.items.ItemStackHandler; +import wayoftime.bloodmagic.common.recipe.ash.AshRecipe; public class ArcaneAshesTile extends BlockEntity { - public ItemStackHandler inv = new ItemStackHandler(2); + public ItemStackHandler inv = new ItemStackHandler(3) { + @Override + protected void onContentsChanged(int slot) { + super.onContentsChanged(slot); + setChanged(); + level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_ALL); + } + }; + + public int progress = 0; public ArcaneAshesTile(BlockPos pos, BlockState blockState) { super(BMTiles.ARCANE_ASHES.get(), pos, blockState); } + + @Override + protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.loadAdditional(tag, registries); + inv.deserializeNBT(registries, tag.getCompound("inv")); + progress = tag.getInt("progress"); + } + + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditional(tag, registries); + tag.put("inv", inv.serializeNBT(registries)); + tag.putInt("progress", progress); + } } diff --git a/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java b/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java index 1faa60cf90..f851afbb21 100644 --- a/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java +++ b/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java @@ -7,6 +7,7 @@ import net.neoforged.neoforge.capabilities.Capabilities; import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; import net.neoforged.neoforge.client.event.EntityRenderersEvent; +import net.neoforged.neoforge.items.ItemStackHandler; import net.neoforged.neoforge.items.wrapper.RangedWrapper; import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; @@ -83,6 +84,16 @@ private static void registerTileCapabilities(RegisterCapabilitiesEvent event) { return new RangedWrapper(tile.itemCap, start, end); } ); + event.registerBlockEntity( + Capabilities.ItemHandler.BLOCK, + ARCANE_ASHES.get(), + (tile, side) -> { + if (side == null) { + return tile.inv; // jade & co only. no automation + } + return null; + } + ); } private static void registerBlockEntityRenderer(EntityRenderersEvent.RegisterRenderers event) { diff --git a/src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java b/src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java index b30b87587f..4092bbe769 100644 --- a/src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java +++ b/src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java @@ -1,9 +1,22 @@ package wayoftime.bloodmagic.common.item; +import net.minecraft.advancements.CriteriaTriggers; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.gameevent.GameEvent; public class ArcaneAshesItem extends BlockItem { public ArcaneAshesItem(Block block) { @@ -15,7 +28,46 @@ public ArcaneAshesItem(Block block) { @Override public InteractionResult useOn(UseOnContext context) { - context.getItemInHand().hurtAndBreak(1, context.getPlayer(), context.getItemInHand().getEquipmentSlot()); - return super.useOn(context); + return place(new BlockPlaceContext(context)); + } + + @Override + public InteractionResult place(BlockPlaceContext context) { + if (!this.getBlock().isEnabled(context.getLevel().enabledFeatures())) { + return InteractionResult.FAIL; + } else if (!context.canPlace()) { + return InteractionResult.FAIL; + } else { + BlockPlaceContext blockplacecontext = this.updatePlacementContext(context); + if (blockplacecontext == null) { + return InteractionResult.FAIL; + } else { + BlockState blockstate = this.getPlacementState(blockplacecontext); + if (blockstate == null) { + return InteractionResult.FAIL; + } else if (!this.placeBlock(blockplacecontext, blockstate)) { + return InteractionResult.FAIL; + } else { + BlockPos blockpos = blockplacecontext.getClickedPos(); + Level level = blockplacecontext.getLevel(); + Player player = blockplacecontext.getPlayer(); + ItemStack itemstack = blockplacecontext.getItemInHand(); + BlockState blockstate1 = level.getBlockState(blockpos); + + SoundType soundtype = blockstate1.getSoundType(level, blockpos, context.getPlayer()); + level.playSound( + player, + blockpos, + this.getPlaceSound(blockstate1, level, blockpos, context.getPlayer()), + SoundSource.BLOCKS, + (soundtype.getVolume() + 1.0F) / 2.0F, + soundtype.getPitch() * 0.8F + ); + level.gameEvent(GameEvent.BLOCK_PLACE, blockpos, GameEvent.Context.of(player, blockstate1)); + itemstack.hurtAndBreak(1, player, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND); + return InteractionResult.sidedSuccess(level.isClientSide); + } + } + } } } diff --git a/src/main/java/wayoftime/bloodmagic/common/recipe/BMRecipes.java b/src/main/java/wayoftime/bloodmagic/common/recipe/BMRecipes.java index 7b05a3410d..dab9c01540 100644 --- a/src/main/java/wayoftime/bloodmagic/common/recipe/BMRecipes.java +++ b/src/main/java/wayoftime/bloodmagic/common/recipe/BMRecipes.java @@ -10,6 +10,9 @@ import wayoftime.bloodmagic.BloodMagic; import wayoftime.bloodmagic.common.recipe.arc.ARCRecipe; import wayoftime.bloodmagic.common.recipe.arc.ARCSerializer; +import wayoftime.bloodmagic.common.recipe.ash.AshCraftingRecipe; +import wayoftime.bloodmagic.common.recipe.ash.AshCraftingSerializer; +import wayoftime.bloodmagic.common.recipe.ash.AshRecipe; import wayoftime.bloodmagic.common.recipe.bloodaltar.BloodAltarRecipe; import wayoftime.bloodmagic.common.recipe.bloodaltar.BloodAltarRecipeSerializer; import wayoftime.bloodmagic.common.recipe.forge.ForgeRecipe; @@ -38,6 +41,9 @@ public class BMRecipes { public static final DeferredHolder, RecipeType> ENERGY_TIERED_TYPE = TYPES.register(EnergyTieredRecipe.NAME, () -> RecipeType.simple(bm(EnergyTieredRecipe.NAME))); public static final DeferredHolder, RecipeSerializer> ENERGY_TIERED_SERIALIZER = SERIALIZERS.register(EnergyTieredRecipe.NAME, EnergyTieredSerializer::new); + public static final DeferredHolder, RecipeType> ASH_TYPE = TYPES.register(AshRecipe.NAME, () -> RecipeType.simple(bm(AshRecipe.NAME))); + public static final DeferredHolder, RecipeSerializer> ASH_CRAFTING_SERIALIZER = SERIALIZERS.register(AshCraftingSerializer.NAME, AshCraftingSerializer::new); + public static void register(IEventBus modBus) { SERIALIZERS.register(modBus); TYPES.register(modBus); diff --git a/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingRecipe.java b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingRecipe.java new file mode 100644 index 0000000000..35a31f24e7 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingRecipe.java @@ -0,0 +1,30 @@ +package wayoftime.bloodmagic.common.recipe.ash; + +import net.minecraft.core.HolderLookup; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.minecraft.world.level.Level; +import wayoftime.bloodmagic.common.recipe.BMRecipes; + +public record AshCraftingRecipe(Ingredient first, Ingredient second, ItemStack output) implements AshRecipe { + @Override + public boolean matches(AshInput input, Level level) { + return first.test(input.getItem(0)) && second.test(input.getItem(1)); + } + + @Override + public ItemStack assemble(AshInput input, HolderLookup.Provider registries) { + return output.copy(); + } + + @Override + public ItemStack getResultItem(HolderLookup.Provider registries) { + return output.copy(); + } + + @Override + public RecipeSerializer getSerializer() { + return BMRecipes.ASH_CRAFTING_SERIALIZER.get(); + } +} diff --git a/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingSerializer.java b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingSerializer.java new file mode 100644 index 0000000000..d4c87a6612 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingSerializer.java @@ -0,0 +1,37 @@ +package wayoftime.bloodmagic.common.recipe.ash; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.RecipeSerializer; + +public class AshCraftingSerializer implements RecipeSerializer { + + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(builder -> builder.group( + Ingredient.CODEC.fieldOf("first").forGetter(AshCraftingRecipe::first), + Ingredient.CODEC.fieldOf("second").forGetter(AshCraftingRecipe::second), + ItemStack.CODEC.fieldOf("result").forGetter(AshCraftingRecipe::output) + ).apply(builder, AshCraftingRecipe::new)); + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + Ingredient.CONTENTS_STREAM_CODEC, AshCraftingRecipe::first, + Ingredient.CONTENTS_STREAM_CODEC, AshCraftingRecipe::second, + ItemStack.STREAM_CODEC, AshCraftingRecipe::output, + AshCraftingRecipe::new + ); + + public static final String NAME = "ash_crafting"; + + @Override + public MapCodec codec() { + return CODEC; + } + + @Override + public StreamCodec streamCodec() { + return STREAM_CODEC; + } +} diff --git a/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshInput.java b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshInput.java new file mode 100644 index 0000000000..7f91670986 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshInput.java @@ -0,0 +1,15 @@ +package wayoftime.bloodmagic.common.recipe.ash; + +import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.items.wrapper.RecipeWrapper; + +public class AshInput extends RecipeWrapper { + public AshInput(IItemHandler inv) { + super(inv); + } + + @Override + public int size() { + return 2; + } +} diff --git a/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshRecipe.java b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshRecipe.java new file mode 100644 index 0000000000..eb9967e6b1 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshRecipe.java @@ -0,0 +1,19 @@ +package wayoftime.bloodmagic.common.recipe.ash; + +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeType; +import wayoftime.bloodmagic.common.recipe.BMRecipes; + +public interface AshRecipe extends Recipe { + String NAME = "arcane_ash"; + + @Override + default RecipeType getType() { + return BMRecipes.ASH_TYPE.get(); + } + + @Override + default boolean canCraftInDimensions(int width, int height) { + return false; + } +} diff --git a/src/main/java/wayoftime/bloodmagic/util/BlockEntityHelper.java b/src/main/java/wayoftime/bloodmagic/util/BlockEntityHelper.java index beb1a85b2f..8713467117 100644 --- a/src/main/java/wayoftime/bloodmagic/util/BlockEntityHelper.java +++ b/src/main/java/wayoftime/bloodmagic/util/BlockEntityHelper.java @@ -29,4 +29,10 @@ public static void dropContents(Level level, BlockPos pos, IItemHandler handler) Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), handler.getStackInSlot(i)); } } + + public static void dropContents(Level level, BlockPos pos, IItemHandler handler, int maxIndex) { + for (int i = 0; i < maxIndex; i++) { + Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), handler.getStackInSlot(i)); + } + } } diff --git a/src/main/resources/assets/bloodmagic/blockstates/arcane_ashes.json b/src/main/resources/assets/bloodmagic/blockstates/arcane_ashes.json new file mode 100755 index 0000000000..532b96d027 --- /dev/null +++ b/src/main/resources/assets/bloodmagic/blockstates/arcane_ashes.json @@ -0,0 +1,19 @@ +{ + "variants": { + "facing=south": { + "model": "bloodmagic:block/arcane_ashes" + }, + "facing=west": { + "model": "bloodmagic:block/arcane_ashes", + "y": 90 + }, + "facing=north": { + "model": "bloodmagic:block/arcane_ashes", + "y": 180 + }, + "facing=east": { + "model": "bloodmagic:block/arcane_ashes", + "y": 270 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/bloodmagic/models/block/arcane_ashes.json b/src/main/resources/assets/bloodmagic/models/block/arcane_ashes.json new file mode 100644 index 0000000000..7987280d79 --- /dev/null +++ b/src/main/resources/assets/bloodmagic/models/block/arcane_ashes.json @@ -0,0 +1,17 @@ +{ + "parent": "minecraft:block/block", + "render_type": "minecraft:translucent", + "textures": { + "particle": "bloodmagic:block/wip_array", + "top": "bloodmagic:block/wip_array" + }, + "elements": [ + { + "from": [0, 0, 0], + "to": [16, 0, 16], + "faces": { + "up": {"uv": [0, 0, 16, 16], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/bloodmagic/models/item/arcane_ashes.json b/src/main/resources/assets/bloodmagic/models/item/arcane_ashes.json new file mode 100644 index 0000000000..c664c909f4 --- /dev/null +++ b/src/main/resources/assets/bloodmagic/models/item/arcane_ashes.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "bloodmagic:item/arcane_ashes" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/bloodmagic/textures/block/wip_array.png b/src/main/resources/assets/bloodmagic/textures/block/wip_array.png new file mode 100755 index 0000000000000000000000000000000000000000..11297382699333125223b24da139234f956ee01a GIT binary patch literal 16402 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/bloodmagic/textures/item/arcane_ashes.png b/src/main/resources/assets/bloodmagic/textures/item/arcane_ashes.png new file mode 100755 index 0000000000000000000000000000000000000000..43025e282ba9b41a6479b1ccd3b6025865d1ac91 GIT binary patch literal 550 zcmV+>0@?kEP)`HXO-~DAoV~0kcU& zK~y-)y_3IdQ&AMhKleS-AsHS7O11M6%qP{%Nfq+emI=VRU*Rw#P}C@ zQK^9nfQayDsGfI*nUM_<;r?1d=AS;6VdhVEWMh7PMP9yrEju4}C0BSTqb5>bOI%o8 zkvlVU)IT-=2m}Iby?D;n#-{q~67$arvbwm8oVvK}VLd_J1Z zCOg~P_zV-%Ft99(R4OHB1buOFzPC9%Y-5Vz*3D@`p%9Tsgnqw|X__RHNqW5=uIrwi z;6(r6ePfR?40^|1KG`;YzaQJS2?m2C5($dMBAHAEt@Urgz$?ry?!z_bUH0s oYqZvg2<38Fd2#AOf1_7^0zHq)$07*qoM6N<$f_7% Date: Mon, 13 Apr 2026 17:54:41 +0200 Subject: [PATCH 3/5] Rebased onto incoming changes from Alchemy Table PR --- .../common/block/ArcaneAshesBlock.java | 77 +++++++++++++++++++ .../bloodmagic/common/block/BMBlocks.java | 2 + .../common/blockentity/ArcaneAshesTile.java | 15 ++++ .../common/blockentity/BMTiles.java | 3 + .../common/item/ArcaneAshesItem.java | 21 +++++ 5 files changed, 118 insertions(+) create mode 100644 src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java create mode 100644 src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java create mode 100644 src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java diff --git a/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java b/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java new file mode 100644 index 0000000000..a362759368 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java @@ -0,0 +1,77 @@ +package wayoftime.bloodmagic.common.block; + +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.RandomSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.ItemInteractionResult; +import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +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.DirectionProperty; +import net.minecraft.world.phys.BlockHitResult; +import org.jetbrains.annotations.Nullable; +import wayoftime.bloodmagic.common.blockentity.ArcaneAshesTile; + +public class ArcaneAshesBlock extends Block implements EntityBlock { + + public static final DirectionProperty FACING = BlockStateProperties.FACING; + + public ArcaneAshesBlock() { + super(Properties.of() + .instabreak() // feels right not to have to mine it + .noCollission() + .ignitedByLava() + ); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(FACING); + } + + @Override + protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult) { + // insert items, on second check if first is flask -> beacon; else recipe lookup. if either true + //level.scheduleTick(pos, state.getBlock(), 1); + //return ItemInteractionResult.sidedSuccess(level.isClientSide); + + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + + @Override + protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) { + BlockEntity be = level.getBlockEntity(pos); + if (!(be instanceof ArcaneAshesTile ashes)) { + return; + } + + // advance crafting + // reapply beacon + //level.scheduleTick(pos, state.getBlock(), 1); + } + + @Override + protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { + // TODO check if valid trap items -> do trap; else do nothing + } + + @Nullable + @Override + public BlockState getStateForPlacement(BlockPlaceContext context) { + return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @Override + public @Nullable BlockEntity newBlockEntity(BlockPos pos, BlockState state) { + return new ArcaneAshesTile(pos, state); + } +} diff --git a/src/main/java/wayoftime/bloodmagic/common/block/BMBlocks.java b/src/main/java/wayoftime/bloodmagic/common/block/BMBlocks.java index 05efdde40b..8c4d02b909 100644 --- a/src/main/java/wayoftime/bloodmagic/common/block/BMBlocks.java +++ b/src/main/java/wayoftime/bloodmagic/common/block/BMBlocks.java @@ -17,6 +17,7 @@ import wayoftime.bloodmagic.common.datamap.BMDataMaps; import wayoftime.bloodmagic.common.datamap.BloodRune; import wayoftime.bloodmagic.api.altar.EnumRuneType; +import wayoftime.bloodmagic.common.item.ArcaneAshesItem; import wayoftime.bloodmagic.util.BlockEntityHelper; import wayoftime.bloodmagic.util.blockitem.BlockWithItemHolder; import wayoftime.bloodmagic.util.blockitem.BlockWithItemRegister; @@ -38,6 +39,7 @@ public class BMBlocks { public static final BlockWithItemHolder BLOOD_TANK = BLOCK_REG.register("blood_tank", BloodTankBlock::new, block -> new BlockItem(block, new Item.Properties().component(BMDataComponents.CONTAINER_TIER, 1))); public static final BlockWithItemHolder HELLFIRE_FORGE = BLOCK_REG.register("hellfire_forge", HellfireForgeBlock::new); public static final BlockWithItemHolder ARC_BLOCK = BLOCK_REG.register("arc", ARCBlock::new); + public static final BlockWithItemHolder ARCANE_ASHES = BLOCK_REG.register("arcane_ashes", ArcaneAshesBlock::new, ArcaneAshesItem::new); public static final BlockWithItemHolder ALCHEMY_TABLE = BLOCK_REG.register("alchemy_table", AlchemyTableBlock::new); // TODO add model/textures for this and change registry to BASIC_REG diff --git a/src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java b/src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java new file mode 100644 index 0000000000..fd228d1be6 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java @@ -0,0 +1,15 @@ +package wayoftime.bloodmagic.common.blockentity; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.neoforged.neoforge.items.ItemStackHandler; + +public class ArcaneAshesTile extends BlockEntity { + + public ItemStackHandler inv = new ItemStackHandler(2); + + public ArcaneAshesTile(BlockPos pos, BlockState blockState) { + super(BMTiles.ARCANE_ASHES.get(), pos, blockState); + } +} diff --git a/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java b/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java index c5b4f3c795..9f99186f6e 100644 --- a/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java +++ b/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java @@ -36,6 +36,9 @@ public class BMTiles { public static final DeferredHolder, BlockEntityType> LIVING_STATION_TYPE = TILES.register("living_station", () -> new BlockEntityType<>(LivingStationTile::new, Set.of(BMBlocks.LIVING_STATION.block().get()), null)); + public static final DeferredHolder, BlockEntityType> ARCANE_ASHES = TILES.register("arcane_ashes", + () -> new BlockEntityType<>(ArcaneAshesTile::new, Set.of(BMBlocks.ARCANE_ASHES.block().get()), null)); + public static final DeferredHolder, BlockEntityType> ALCHEMY_TABLE_TYPE = TILES.register("alchemy_table", () -> new BlockEntityType<>(AlchemyTableTile::new, Set.of(BMBlocks.ALCHEMY_TABLE.block().get()), null)); diff --git a/src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java b/src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java new file mode 100644 index 0000000000..b30b87587f --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java @@ -0,0 +1,21 @@ +package wayoftime.bloodmagic.common.item; + +import net.minecraft.world.InteractionResult; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.block.Block; + +public class ArcaneAshesItem extends BlockItem { + public ArcaneAshesItem(Block block) { + super(block, new Properties() + .stacksTo(1) + .durability(42) // 1.20 has 20 but that seems low given that we want to give it more stuff to do + ); + } + + @Override + public InteractionResult useOn(UseOnContext context) { + context.getItemInHand().hurtAndBreak(1, context.getPlayer(), context.getItemInHand().getEquipmentSlot()); + return super.useOn(context); + } +} From 6890fe4ab28c14c8fe4cfb5a7e6bc8908c584e19 Mon Sep 17 00:00:00 2001 From: stellanera Date: Tue, 14 Apr 2026 19:09:02 +0200 Subject: [PATCH 4/5] Rebased to include Alchemy Table updates --- .../datagen/provider/BMLanguageProvider.java | 2 + .../assets/bloodmagic/lang/en_us.json | 1 + .../common/block/ArcaneAshesBlock.java | 132 ++++++++++++++++-- .../common/blockentity/ArcaneAshesTile.java | 35 ++++- .../common/blockentity/BMTiles.java | 11 ++ .../common/item/ArcaneAshesItem.java | 56 +++++++- .../bloodmagic/common/recipe/BMRecipes.java | 6 + .../common/recipe/ash/AshCraftingRecipe.java | 30 ++++ .../recipe/ash/AshCraftingSerializer.java | 37 +++++ .../common/recipe/ash/AshInput.java | 15 ++ .../common/recipe/ash/AshRecipe.java | 19 +++ .../bloodmagic/util/BlockEntityHelper.java | 6 + .../bloodmagic/blockstates/arcane_ashes.json | 19 +++ .../bloodmagic/models/block/arcane_ashes.json | 17 +++ .../bloodmagic/models/item/arcane_ashes.json | 6 + .../bloodmagic/textures/block/wip_array.png | Bin 0 -> 16402 bytes .../bloodmagic/textures/item/arcane_ashes.png | Bin 0 -> 550 bytes 17 files changed, 380 insertions(+), 12 deletions(-) create mode 100644 src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingRecipe.java create mode 100644 src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingSerializer.java create mode 100644 src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshInput.java create mode 100644 src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshRecipe.java create mode 100755 src/main/resources/assets/bloodmagic/blockstates/arcane_ashes.json create mode 100644 src/main/resources/assets/bloodmagic/models/block/arcane_ashes.json create mode 100644 src/main/resources/assets/bloodmagic/models/item/arcane_ashes.json create mode 100755 src/main/resources/assets/bloodmagic/textures/block/wip_array.png create mode 100755 src/main/resources/assets/bloodmagic/textures/item/arcane_ashes.png diff --git a/src/datagen/java/wayoftime/bloodmagic/datagen/provider/BMLanguageProvider.java b/src/datagen/java/wayoftime/bloodmagic/datagen/provider/BMLanguageProvider.java index bb0fa47b03..5dfe1570cf 100644 --- a/src/datagen/java/wayoftime/bloodmagic/datagen/provider/BMLanguageProvider.java +++ b/src/datagen/java/wayoftime/bloodmagic/datagen/provider/BMLanguageProvider.java @@ -85,6 +85,8 @@ protected void addTranslations() { addTooltip("fluid_content_empty", "Empty"); addTooltip("fluid_content", "Contains: %smB of %s"); + add(BMBlocks.ARCANE_ASHES, "Arcane Ashes"); + add(BMBlocks.IMPERFECT_RITUAL_BLOCK, "Imperfect Ritual Stone"); add(BMBlocks.HELLFIRE_FORGE, "Hellfire Forge"); diff --git a/src/generated/resources/assets/bloodmagic/lang/en_us.json b/src/generated/resources/assets/bloodmagic/lang/en_us.json index 9e795b0ca3..86ce9445bc 100644 --- a/src/generated/resources/assets/bloodmagic/lang/en_us.json +++ b/src/generated/resources/assets/bloodmagic/lang/en_us.json @@ -1,6 +1,7 @@ { "block.bloodmagic.alchemy_table": "Alchemy Table", "block.bloodmagic.arc": "Alchemical Reaction Chamber", + "block.bloodmagic.arcane_ashes": "Arcane Ashes", "block.bloodmagic.blood_altar": "Blood Altar", "block.bloodmagic.blood_tank": "Blood Tank", "block.bloodmagic.bloodstone": "Polished Bloodstone", diff --git a/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java b/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java index a362759368..e094eeecbf 100644 --- a/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java +++ b/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java @@ -1,14 +1,22 @@ package wayoftime.bloodmagic.common.block; import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.particles.DustParticleOptions; import net.minecraft.server.level.ServerLevel; import net.minecraft.util.RandomSource; +import net.minecraft.util.StringRepresentable; import net.minecraft.world.InteractionHand; import net.minecraft.world.ItemInteractionResult; import net.minecraft.world.entity.Entity; +import net.minecraft.world.entity.item.ItemEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.item.crafting.RecipeHolder; +import net.minecraft.world.item.crafting.RecipeManager; +import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.EntityBlock; @@ -17,13 +25,25 @@ 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.DirectionProperty; +import net.minecraft.world.level.block.state.properties.EnumProperty; import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.neoforged.neoforge.items.wrapper.RecipeWrapper; import org.jetbrains.annotations.Nullable; import wayoftime.bloodmagic.common.blockentity.ArcaneAshesTile; +import wayoftime.bloodmagic.common.item.BMItems; +import wayoftime.bloodmagic.common.recipe.BMRecipes; +import wayoftime.bloodmagic.common.recipe.ash.AshCraftingRecipe; +import wayoftime.bloodmagic.common.recipe.ash.AshInput; +import wayoftime.bloodmagic.common.recipe.ash.AshRecipe; +import wayoftime.bloodmagic.util.BlockEntityHelper; public class ArcaneAshesBlock extends Block implements EntityBlock { public static final DirectionProperty FACING = BlockStateProperties.FACING; + public static final EnumProperty MODE = EnumProperty.create("state", AshMode.class); public ArcaneAshesBlock() { super(Properties.of() @@ -33,16 +53,46 @@ public ArcaneAshesBlock() { ); } + protected static final VoxelShape BOX = Block.box(0, 0, 0, 16, 1, 16); + @Override + protected VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) { + return BOX; + } + @Override protected void createBlockStateDefinition(StateDefinition.Builder builder) { - builder.add(FACING); + builder.add(FACING, MODE); } + private static RecipeManager.CachedCheck lookup = null; @Override protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hitResult) { - // insert items, on second check if first is flask -> beacon; else recipe lookup. if either true - //level.scheduleTick(pos, state.getBlock(), 1); - //return ItemInteractionResult.sidedSuccess(level.isClientSide); + BlockEntity be = level.getBlockEntity(pos); + if (!(be instanceof ArcaneAshesTile ashes)) { + return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; + } + ItemStack heldItem = player.getItemInHand(hand); + if (ashes.inv.getStackInSlot(0).isEmpty()) { + ashes.inv.setStackInSlot(0, heldItem.split(1)); + } else if (ashes.inv.getStackInSlot(1).isEmpty()) { + ashes.inv.setStackInSlot(1, heldItem.split(1)); + + if (ashes.inv.getStackInSlot(0).is(Items.POTION /*BMItems.FLASK.get()*/)) { + // TODO implement beacon effect + } else { + AshInput input = new AshInput(ashes.inv); + if (lookup == null) { + lookup = RecipeManager.createCheck(BMRecipes.ASH_TYPE.get()); + } + RecipeHolder holder = lookup.getRecipeFor(input, level).orElse(null); + if (holder != null) { + ashes.inv.setStackInSlot(2, holder.value().assemble(input, level.registryAccess())); + level.sendBlockUpdated(pos, state, state.setValue(MODE, AshMode.CRAFTING), UPDATE_ALL); + level.scheduleTick(pos, state.getBlock(), 1); + return ItemInteractionResult.sidedSuccess(level.isClientSide); + } + } + } return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; } @@ -54,24 +104,88 @@ protected void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSou return; } - // advance crafting - // reapply beacon - //level.scheduleTick(pos, state.getBlock(), 1); + AshMode mode = state.getValue(MODE); + switch (mode) { + case CRAFTING -> { + if (++ashes.progress >= 200) { + ItemStack output = ashes.inv.getStackInSlot(2); + if (level.isClientSide) { + Vec3 vec3 = Vec3.atLowerCornerWithOffset(pos, 0.5, 1 / 16f, 0.5).offsetRandom(level.random, 0.7f); + ItemEntity itementity = new ItemEntity(level, vec3.x(), vec3.y(), vec3.z(), output); + itementity.setDefaultPickUpDelay(); + level.addFreshEntity(itementity); + } + ashes.progress = 0; + ashes.inv.setStackInSlot(0, ItemStack.EMPTY); + ashes.inv.setStackInSlot(1, ItemStack.EMPTY); + ashes.inv.setStackInSlot(2, ItemStack.EMPTY); + level.sendBlockUpdated(pos, state, state.setValue(MODE, AshMode.IDLE), UPDATE_ALL); + return; + } + level.scheduleTick(pos, state.getBlock(), 1); + } + + case BEACON -> { + // TODO implement + } + + case IDLE -> { + // why has tick when idle? + } + } + } + + @Override + public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) { + super.animateTick(state, level, pos, random); + if (random.nextFloat() < 0.3f) { + switch (state.getValue(MODE)) { + case CRAFTING -> level.addParticle(new DustParticleOptions(DustParticleOptions.REDSTONE_PARTICLE_COLOR, 1.1f), pos.getX() + 0.5, pos.getY() + 0.1f, pos.getZ() + 0.5, 0.2, 0.2, 0.2); + case BEACON -> { + } + case IDLE -> { + } + } + } } @Override protected void entityInside(BlockState state, Level level, BlockPos pos, Entity entity) { - // TODO check if valid trap items -> do trap; else do nothing + // TODO implement + // check if valid trap items -> do trap; else do nothing } @Nullable @Override public BlockState getStateForPlacement(BlockPlaceContext context) { - return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()); + return this.defaultBlockState(). + setValue(MODE, AshMode.IDLE) + .setValue(FACING, context.getHorizontalDirection().getOpposite()); + } + + @Override + protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { + if (!state.is(newState.getBlock())) { + if (level.getBlockEntity(pos) instanceof ArcaneAshesTile ash) { + BlockEntityHelper.dropContents(level, pos, ash.inv, 1); + } + } + super.onRemove(state, level, pos, newState, movedByPiston); } @Override public @Nullable BlockEntity newBlockEntity(BlockPos pos, BlockState state) { return new ArcaneAshesTile(pos, state); } + + public enum AshMode implements StringRepresentable { + IDLE, + CRAFTING, + BEACON; + + @Override + public String getSerializedName() { + return name().toLowerCase(); + } + } } diff --git a/src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java b/src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java index fd228d1be6..e6775aff87 100644 --- a/src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java +++ b/src/main/java/wayoftime/bloodmagic/common/blockentity/ArcaneAshesTile.java @@ -1,15 +1,48 @@ package wayoftime.bloodmagic.common.blockentity; import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.RecipeHolder; +import net.minecraft.world.item.crafting.RecipeInput; +import net.minecraft.world.item.crafting.RecipeManager; +import net.minecraft.world.level.Level; +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.neoforged.neoforge.items.ItemStackHandler; +import wayoftime.bloodmagic.common.recipe.ash.AshRecipe; public class ArcaneAshesTile extends BlockEntity { - public ItemStackHandler inv = new ItemStackHandler(2); + public ItemStackHandler inv = new ItemStackHandler(3) { + @Override + protected void onContentsChanged(int slot) { + super.onContentsChanged(slot); + setChanged(); + level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), Block.UPDATE_ALL); + } + }; + + public int progress = 0; public ArcaneAshesTile(BlockPos pos, BlockState blockState) { super(BMTiles.ARCANE_ASHES.get(), pos, blockState); } + + @Override + protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.loadAdditional(tag, registries); + inv.deserializeNBT(registries, tag.getCompound("inv")); + progress = tag.getInt("progress"); + } + + @Override + protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) { + super.saveAdditional(tag, registries); + tag.put("inv", inv.serializeNBT(registries)); + tag.putInt("progress", progress); + } } diff --git a/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java b/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java index 9f99186f6e..7e3b88a5ab 100644 --- a/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java +++ b/src/main/java/wayoftime/bloodmagic/common/blockentity/BMTiles.java @@ -7,6 +7,7 @@ import net.neoforged.neoforge.capabilities.Capabilities; import net.neoforged.neoforge.capabilities.RegisterCapabilitiesEvent; import net.neoforged.neoforge.client.event.EntityRenderersEvent; +import net.neoforged.neoforge.items.ItemStackHandler; import net.neoforged.neoforge.items.wrapper.RangedWrapper; import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; @@ -86,6 +87,16 @@ private static void registerTileCapabilities(RegisterCapabilitiesEvent event) { return new RangedWrapper(tile.itemCap, start, end); } ); + event.registerBlockEntity( + Capabilities.ItemHandler.BLOCK, + ARCANE_ASHES.get(), + (tile, side) -> { + if (side == null) { + return tile.inv; // jade & co only. no automation + } + return null; + } + ); event.registerBlockEntity( Capabilities.ItemHandler.BLOCK, ALCHEMY_TABLE_TYPE.get(), diff --git a/src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java b/src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java index b30b87587f..4092bbe769 100644 --- a/src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java +++ b/src/main/java/wayoftime/bloodmagic/common/item/ArcaneAshesItem.java @@ -1,9 +1,22 @@ package wayoftime.bloodmagic.common.item; +import net.minecraft.advancements.CriteriaTriggers; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.EquipmentSlot; +import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.context.BlockPlaceContext; import net.minecraft.world.item.context.UseOnContext; +import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.gameevent.GameEvent; public class ArcaneAshesItem extends BlockItem { public ArcaneAshesItem(Block block) { @@ -15,7 +28,46 @@ public ArcaneAshesItem(Block block) { @Override public InteractionResult useOn(UseOnContext context) { - context.getItemInHand().hurtAndBreak(1, context.getPlayer(), context.getItemInHand().getEquipmentSlot()); - return super.useOn(context); + return place(new BlockPlaceContext(context)); + } + + @Override + public InteractionResult place(BlockPlaceContext context) { + if (!this.getBlock().isEnabled(context.getLevel().enabledFeatures())) { + return InteractionResult.FAIL; + } else if (!context.canPlace()) { + return InteractionResult.FAIL; + } else { + BlockPlaceContext blockplacecontext = this.updatePlacementContext(context); + if (blockplacecontext == null) { + return InteractionResult.FAIL; + } else { + BlockState blockstate = this.getPlacementState(blockplacecontext); + if (blockstate == null) { + return InteractionResult.FAIL; + } else if (!this.placeBlock(blockplacecontext, blockstate)) { + return InteractionResult.FAIL; + } else { + BlockPos blockpos = blockplacecontext.getClickedPos(); + Level level = blockplacecontext.getLevel(); + Player player = blockplacecontext.getPlayer(); + ItemStack itemstack = blockplacecontext.getItemInHand(); + BlockState blockstate1 = level.getBlockState(blockpos); + + SoundType soundtype = blockstate1.getSoundType(level, blockpos, context.getPlayer()); + level.playSound( + player, + blockpos, + this.getPlaceSound(blockstate1, level, blockpos, context.getPlayer()), + SoundSource.BLOCKS, + (soundtype.getVolume() + 1.0F) / 2.0F, + soundtype.getPitch() * 0.8F + ); + level.gameEvent(GameEvent.BLOCK_PLACE, blockpos, GameEvent.Context.of(player, blockstate1)); + itemstack.hurtAndBreak(1, player, context.getHand() == InteractionHand.MAIN_HAND ? EquipmentSlot.MAINHAND : EquipmentSlot.OFFHAND); + return InteractionResult.sidedSuccess(level.isClientSide); + } + } + } } } diff --git a/src/main/java/wayoftime/bloodmagic/common/recipe/BMRecipes.java b/src/main/java/wayoftime/bloodmagic/common/recipe/BMRecipes.java index ee1d6d5ea9..b4f80f160d 100644 --- a/src/main/java/wayoftime/bloodmagic/common/recipe/BMRecipes.java +++ b/src/main/java/wayoftime/bloodmagic/common/recipe/BMRecipes.java @@ -12,6 +12,9 @@ import wayoftime.bloodmagic.common.recipe.alchemy_table.AlchemyTableSerializer; import wayoftime.bloodmagic.common.recipe.arc.ARCRecipe; import wayoftime.bloodmagic.common.recipe.arc.ARCSerializer; +import wayoftime.bloodmagic.common.recipe.ash.AshCraftingRecipe; +import wayoftime.bloodmagic.common.recipe.ash.AshCraftingSerializer; +import wayoftime.bloodmagic.common.recipe.ash.AshRecipe; import wayoftime.bloodmagic.common.recipe.bloodaltar.BloodAltarRecipe; import wayoftime.bloodmagic.common.recipe.bloodaltar.BloodAltarRecipeSerializer; import wayoftime.bloodmagic.common.recipe.forge.ForgeRecipe; @@ -42,6 +45,9 @@ public class BMRecipes { public static final DeferredHolder, RecipeType> ENERGY_TIERED_TYPE = TYPES.register(EnergyTieredRecipe.NAME, () -> RecipeType.simple(bm(EnergyTieredRecipe.NAME))); public static final DeferredHolder, RecipeSerializer> ENERGY_TIERED_SERIALIZER = SERIALIZERS.register(EnergyTieredRecipe.NAME, EnergyTieredSerializer::new); + public static final DeferredHolder, RecipeType> ASH_TYPE = TYPES.register(AshRecipe.NAME, () -> RecipeType.simple(bm(AshRecipe.NAME))); + public static final DeferredHolder, RecipeSerializer> ASH_CRAFTING_SERIALIZER = SERIALIZERS.register(AshCraftingSerializer.NAME, AshCraftingSerializer::new); + public static final DeferredHolder, RecipeType> ALCHEMY_TABLE_TYPE = TYPES.register(AlchemyTableRecipe.RECIPE_TYPE_NAME, () -> RecipeType.simple(bm(AlchemyTableRecipe.RECIPE_TYPE_NAME))); public static final DeferredHolder, RecipeSerializer> ALCHEMY_TABLE_SERIALIZER = SERIALIZERS.register(AlchemyTableRecipe.RECIPE_TYPE_NAME, AlchemyTableSerializer::new); diff --git a/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingRecipe.java b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingRecipe.java new file mode 100644 index 0000000000..35a31f24e7 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingRecipe.java @@ -0,0 +1,30 @@ +package wayoftime.bloodmagic.common.recipe.ash; + +import net.minecraft.core.HolderLookup; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.RecipeSerializer; +import net.minecraft.world.level.Level; +import wayoftime.bloodmagic.common.recipe.BMRecipes; + +public record AshCraftingRecipe(Ingredient first, Ingredient second, ItemStack output) implements AshRecipe { + @Override + public boolean matches(AshInput input, Level level) { + return first.test(input.getItem(0)) && second.test(input.getItem(1)); + } + + @Override + public ItemStack assemble(AshInput input, HolderLookup.Provider registries) { + return output.copy(); + } + + @Override + public ItemStack getResultItem(HolderLookup.Provider registries) { + return output.copy(); + } + + @Override + public RecipeSerializer getSerializer() { + return BMRecipes.ASH_CRAFTING_SERIALIZER.get(); + } +} diff --git a/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingSerializer.java b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingSerializer.java new file mode 100644 index 0000000000..d4c87a6612 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshCraftingSerializer.java @@ -0,0 +1,37 @@ +package wayoftime.bloodmagic.common.recipe.ash; + +import com.mojang.serialization.MapCodec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import net.minecraft.network.RegistryFriendlyByteBuf; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.crafting.Ingredient; +import net.minecraft.world.item.crafting.RecipeSerializer; + +public class AshCraftingSerializer implements RecipeSerializer { + + public static final MapCodec CODEC = RecordCodecBuilder.mapCodec(builder -> builder.group( + Ingredient.CODEC.fieldOf("first").forGetter(AshCraftingRecipe::first), + Ingredient.CODEC.fieldOf("second").forGetter(AshCraftingRecipe::second), + ItemStack.CODEC.fieldOf("result").forGetter(AshCraftingRecipe::output) + ).apply(builder, AshCraftingRecipe::new)); + + public static final StreamCodec STREAM_CODEC = StreamCodec.composite( + Ingredient.CONTENTS_STREAM_CODEC, AshCraftingRecipe::first, + Ingredient.CONTENTS_STREAM_CODEC, AshCraftingRecipe::second, + ItemStack.STREAM_CODEC, AshCraftingRecipe::output, + AshCraftingRecipe::new + ); + + public static final String NAME = "ash_crafting"; + + @Override + public MapCodec codec() { + return CODEC; + } + + @Override + public StreamCodec streamCodec() { + return STREAM_CODEC; + } +} diff --git a/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshInput.java b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshInput.java new file mode 100644 index 0000000000..7f91670986 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshInput.java @@ -0,0 +1,15 @@ +package wayoftime.bloodmagic.common.recipe.ash; + +import net.neoforged.neoforge.items.IItemHandler; +import net.neoforged.neoforge.items.wrapper.RecipeWrapper; + +public class AshInput extends RecipeWrapper { + public AshInput(IItemHandler inv) { + super(inv); + } + + @Override + public int size() { + return 2; + } +} diff --git a/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshRecipe.java b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshRecipe.java new file mode 100644 index 0000000000..eb9967e6b1 --- /dev/null +++ b/src/main/java/wayoftime/bloodmagic/common/recipe/ash/AshRecipe.java @@ -0,0 +1,19 @@ +package wayoftime.bloodmagic.common.recipe.ash; + +import net.minecraft.world.item.crafting.Recipe; +import net.minecraft.world.item.crafting.RecipeType; +import wayoftime.bloodmagic.common.recipe.BMRecipes; + +public interface AshRecipe extends Recipe { + String NAME = "arcane_ash"; + + @Override + default RecipeType getType() { + return BMRecipes.ASH_TYPE.get(); + } + + @Override + default boolean canCraftInDimensions(int width, int height) { + return false; + } +} diff --git a/src/main/java/wayoftime/bloodmagic/util/BlockEntityHelper.java b/src/main/java/wayoftime/bloodmagic/util/BlockEntityHelper.java index beb1a85b2f..8713467117 100644 --- a/src/main/java/wayoftime/bloodmagic/util/BlockEntityHelper.java +++ b/src/main/java/wayoftime/bloodmagic/util/BlockEntityHelper.java @@ -29,4 +29,10 @@ public static void dropContents(Level level, BlockPos pos, IItemHandler handler) Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), handler.getStackInSlot(i)); } } + + public static void dropContents(Level level, BlockPos pos, IItemHandler handler, int maxIndex) { + for (int i = 0; i < maxIndex; i++) { + Containers.dropItemStack(level, pos.getX(), pos.getY(), pos.getZ(), handler.getStackInSlot(i)); + } + } } diff --git a/src/main/resources/assets/bloodmagic/blockstates/arcane_ashes.json b/src/main/resources/assets/bloodmagic/blockstates/arcane_ashes.json new file mode 100755 index 0000000000..532b96d027 --- /dev/null +++ b/src/main/resources/assets/bloodmagic/blockstates/arcane_ashes.json @@ -0,0 +1,19 @@ +{ + "variants": { + "facing=south": { + "model": "bloodmagic:block/arcane_ashes" + }, + "facing=west": { + "model": "bloodmagic:block/arcane_ashes", + "y": 90 + }, + "facing=north": { + "model": "bloodmagic:block/arcane_ashes", + "y": 180 + }, + "facing=east": { + "model": "bloodmagic:block/arcane_ashes", + "y": 270 + } + } +} \ No newline at end of file diff --git a/src/main/resources/assets/bloodmagic/models/block/arcane_ashes.json b/src/main/resources/assets/bloodmagic/models/block/arcane_ashes.json new file mode 100644 index 0000000000..7987280d79 --- /dev/null +++ b/src/main/resources/assets/bloodmagic/models/block/arcane_ashes.json @@ -0,0 +1,17 @@ +{ + "parent": "minecraft:block/block", + "render_type": "minecraft:translucent", + "textures": { + "particle": "bloodmagic:block/wip_array", + "top": "bloodmagic:block/wip_array" + }, + "elements": [ + { + "from": [0, 0, 0], + "to": [16, 0, 16], + "faces": { + "up": {"uv": [0, 0, 16, 16], "texture": "#top"} + } + } + ] +} \ No newline at end of file diff --git a/src/main/resources/assets/bloodmagic/models/item/arcane_ashes.json b/src/main/resources/assets/bloodmagic/models/item/arcane_ashes.json new file mode 100644 index 0000000000..c664c909f4 --- /dev/null +++ b/src/main/resources/assets/bloodmagic/models/item/arcane_ashes.json @@ -0,0 +1,6 @@ +{ + "parent": "minecraft:item/generated", + "textures": { + "layer0": "bloodmagic:item/arcane_ashes" + } +} \ No newline at end of file diff --git a/src/main/resources/assets/bloodmagic/textures/block/wip_array.png b/src/main/resources/assets/bloodmagic/textures/block/wip_array.png new file mode 100755 index 0000000000000000000000000000000000000000..11297382699333125223b24da139234f956ee01a GIT binary patch literal 16402 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 literal 0 HcmV?d00001 diff --git a/src/main/resources/assets/bloodmagic/textures/item/arcane_ashes.png b/src/main/resources/assets/bloodmagic/textures/item/arcane_ashes.png new file mode 100755 index 0000000000000000000000000000000000000000..43025e282ba9b41a6479b1ccd3b6025865d1ac91 GIT binary patch literal 550 zcmV+>0@?kEP)`HXO-~DAoV~0kcU& zK~y-)y_3IdQ&AMhKleS-AsHS7O11M6%qP{%Nfq+emI=VRU*Rw#P}C@ zQK^9nfQayDsGfI*nUM_<;r?1d=AS;6VdhVEWMh7PMP9yrEju4}C0BSTqb5>bOI%o8 zkvlVU)IT-=2m}Iby?D;n#-{q~67$arvbwm8oVvK}VLd_J1Z zCOg~P_zV-%Ft99(R4OHB1buOFzPC9%Y-5Vz*3D@`p%9Tsgnqw|X__RHNqW5=uIrwi z;6(r6ePfR?40^|1KG`;YzaQJS2?m2C5($dMBAHAEt@Urgz$?ry?!z_bUH0s oYqZvg2<38Fd2#AOf1_7^0zHq)$07*qoM6N<$f_7% Date: Sun, 19 Apr 2026 19:11:47 +0200 Subject: [PATCH 5/5] fix dropping contents, prevent arcane ashes from being put into arcane ashes --- .../bloodmagic/common/block/ArcaneAshesBlock.java | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java b/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java index e094eeecbf..bd9555e504 100644 --- a/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java +++ b/src/main/java/wayoftime/bloodmagic/common/block/ArcaneAshesBlock.java @@ -72,8 +72,12 @@ protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Lev return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; } ItemStack heldItem = player.getItemInHand(hand); + if (heldItem.is(BMBlocks.ARCANE_ASHES.asItem())) { + return ItemInteractionResult.CONSUME; // do not want to place ashes + } if (ashes.inv.getStackInSlot(0).isEmpty()) { ashes.inv.setStackInSlot(0, heldItem.split(1)); + return ItemInteractionResult.sidedSuccess(level.isClientSide); } else if (ashes.inv.getStackInSlot(1).isEmpty()) { ashes.inv.setStackInSlot(1, heldItem.split(1)); @@ -89,9 +93,9 @@ protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Lev ashes.inv.setStackInSlot(2, holder.value().assemble(input, level.registryAccess())); level.sendBlockUpdated(pos, state, state.setValue(MODE, AshMode.CRAFTING), UPDATE_ALL); level.scheduleTick(pos, state.getBlock(), 1); - return ItemInteractionResult.sidedSuccess(level.isClientSide); } } + return ItemInteractionResult.sidedSuccess(level.isClientSide); } return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION; @@ -167,7 +171,7 @@ public BlockState getStateForPlacement(BlockPlaceContext context) { protected void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean movedByPiston) { if (!state.is(newState.getBlock())) { if (level.getBlockEntity(pos) instanceof ArcaneAshesTile ash) { - BlockEntityHelper.dropContents(level, pos, ash.inv, 1); + BlockEntityHelper.dropContents(level, pos, ash.inv, 2); } } super.onRemove(state, level, pos, newState, movedByPiston);