diff --git a/CHANGELOG.md b/CHANGELOG.md index 1c6847ca84..b3233336b5 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). ### Added - Updated to Minecraft 1.21.1 ([#985](https://github.com/FallingColors/HexMod/pull/985)) @SuperKnux @slava110 +- Added the Storage Locus, a spell circle component that lets a circle stop depending on its caster's inventory ([#1364](https://github.com/FallingColors/HexMod/pull/1364)) @Robotgiggle - Added Simulate, which causes the next pattern drawn to be simulated (to check for mishaps) rather than executed ([#1194](https://github.com/FallingColors/HexMod/pull/1194)) @Robotgiggle - Added Coalescing Distillation, which performs the null-coalescing operation ([#1196](https://github.com/FallingColors/HexMod/pull/1196)) @Robotgiggle - Added the `hex_unbreakable` tag for blocks that should be immune to Break Block regardless of the configured mining tier ([#1186](https://github.com/FallingColors/HexMod/pull/1186)) @Robotgiggle @slava110 @@ -25,9 +26,6 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/). - Iotas included via the above process are now popped from the original stack - Entity iotas no longer become Null if the referenced entity ceases to exist or becomes unloaded ([#1299](https://github.com/FallingColors/HexMod/pull/1299)) @Robotgiggle - Augur's Purification now returns false for nonexistent or unloaded entity iotas ([#1299](https://github.com/FallingColors/HexMod/pull/1299)) @Robotgiggle - -### Changed - - The media cost of Impulse now scales linearly rather than quadratically ([#1145](https://github.com/FallingColors/HexMod/pull/1145)) @Robotgiggle - Erase Item is now free for most items ([#1359](https://github.com/FallingColors/HexMod/pull/1359)) @beholderface diff --git a/Common/src/generated/resources/assets/hexcasting/blockstates/locus/chest.json b/Common/src/generated/resources/assets/hexcasting/blockstates/locus/chest.json new file mode 100644 index 0000000000..6f4acb0b0e --- /dev/null +++ b/Common/src/generated/resources/assets/hexcasting/blockstates/locus/chest.json @@ -0,0 +1,56 @@ +{ + "variants": { + "facing=down,open=false": { + "model": "hexcasting:block/chest_locus", + "x": 180 + }, + "facing=down,open=true": { + "model": "hexcasting:block/chest_locus_open", + "x": 180 + }, + "facing=east,open=false": { + "model": "hexcasting:block/chest_locus", + "x": 270, + "y": 270 + }, + "facing=east,open=true": { + "model": "hexcasting:block/chest_locus_open", + "x": 270, + "y": 270 + }, + "facing=north,open=false": { + "model": "hexcasting:block/chest_locus", + "x": 270, + "y": 180 + }, + "facing=north,open=true": { + "model": "hexcasting:block/chest_locus_open", + "x": 270, + "y": 180 + }, + "facing=south,open=false": { + "model": "hexcasting:block/chest_locus", + "x": 270 + }, + "facing=south,open=true": { + "model": "hexcasting:block/chest_locus_open", + "x": 270 + }, + "facing=up,open=false": { + "model": "hexcasting:block/chest_locus" + }, + "facing=up,open=true": { + "model": "hexcasting:block/chest_locus_open" + }, + "facing=west,open=false": { + "model": "hexcasting:block/chest_locus", + "x": 270, + "y": 90 + }, + "facing=west,open=true": { + "model": "hexcasting:block/chest_locus_open", + "x": 270, + "y": 90 + } + } +} \ No newline at end of file diff --git a/Common/src/generated/resources/assets/hexcasting/models/item/locus/chest.json b/Common/src/generated/resources/assets/hexcasting/models/item/locus/chest.json new file mode 100644 index 0000000000..db79c5fdd5 --- /dev/null +++ b/Common/src/generated/resources/assets/hexcasting/models/item/locus/chest.json @@ -0,0 +1,3 @@ +{ + "parent": "hexcasting:block/chest_locus" +} \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/advancement/recipes/redstone/locus/chest.json b/Common/src/generated/resources/data/hexcasting/advancement/recipes/redstone/locus/chest.json new file mode 100644 index 0000000000..f22eb01746 --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/advancement/recipes/redstone/locus/chest.json @@ -0,0 +1,35 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "enlightenment": { + "conditions": { + "health_used": { + "min": 0.8 + }, + "media_generated": {}, + "mojang_i_am_begging_and_crying_please_add_an_entity_health_criterion": { + "max": 1.0, + "min": 2.2250738585072014E-308 + } + }, + "trigger": "hexcasting:overcast" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:locus/chest" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "enlightenment" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:locus/chest" + ] + } +} \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/loot_table/blocks/locus/chest.json b/Common/src/generated/resources/data/hexcasting/loot_table/blocks/locus/chest.json new file mode 100644 index 0000000000..b3fdb297e3 --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/loot_table/blocks/locus/chest.json @@ -0,0 +1,15 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "hexcasting:locus/chest" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "hexcasting:blocks/locus/chest" +} \ No newline at end of file diff --git a/Common/src/generated/resources/data/hexcasting/recipe/locus/chest.json b/Common/src/generated/resources/data/hexcasting/recipe/locus/chest.json new file mode 100644 index 0000000000..ca31765021 --- /dev/null +++ b/Common/src/generated/resources/data/hexcasting/recipe/locus/chest.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "redstone", + "key": { + "C": { + "item": "minecraft:chest" + }, + "E": { + "item": "hexcasting:edified_planks" + }, + "S": { + "item": "hexcasting:slate" + } + }, + "pattern": [ + " E ", + "ECE", + " S " + ], + "result": { + "count": 1, + "id": "hexcasting:locus/chest" + } +} \ No newline at end of file diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/circles/CircleExecutionState.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/circles/CircleExecutionState.java index bc34df2ea1..2bfbde63d3 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/circles/CircleExecutionState.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/circles/CircleExecutionState.java @@ -34,6 +34,7 @@ public class CircleExecutionState { TAG_REACHED_POSITIONS = "reached_positions", TAG_CURRENT_POS = "current_pos", TAG_ENTERED_FROM = "entered_from", + TAG_INVENTORY_POS = "inventory_pos", TAG_IMAGE = "image", TAG_CASTER = "caster", TAG_PIGMENT = "pigment", @@ -47,6 +48,7 @@ public class CircleExecutionState { public final HashSet reachedPositions; public BlockPos currentPos; public Direction enteredFrom; + public @Nullable BlockPos inventoryPos; public CastingImage currentImage; public @Nullable UUID caster; public @Nullable FrozenPigment casterPigment; @@ -60,13 +62,14 @@ public class CircleExecutionState { protected CircleExecutionState(BlockPos impetusPos, Direction impetusDir, HashSet reachedPositions, - BlockPos currentPos, Direction enteredFrom, CastingImage currentImage, @Nullable UUID caster, + BlockPos currentPos, Direction enteredFrom, BlockPos inventoryPos, CastingImage currentImage, @Nullable UUID caster, @Nullable FrozenPigment casterPigment, Long reachedSlate, BlockPos greaterCorner, BlockPos lesserCorner) { this.impetusPos = impetusPos; this.impetusDir = impetusDir; this.reachedPositions = reachedPositions; this.currentPos = currentPos; this.enteredFrom = enteredFrom; + this.inventoryPos = inventoryPos; this.currentImage = currentImage; this.caster = caster; this.casterPigment = casterPigment; @@ -170,7 +173,7 @@ protected CircleExecutionState(BlockPos impetusPos, Direction impetusDir, HashSe return new Result.Ok<>( new CircleExecutionState(impetus.getBlockPos(), impetus.getStartDirection(), reachedPositions, impetus.getBlockPos().offset(impetus.getStartDirection().getNormal()), - impetus.getStartDirection(), new CastingImage(), casterUUID, colorizer, 0L, + impetus.getStartDirection(), null, new CastingImage(), casterUUID, colorizer, 0L, positiveBlock, negativeBlock)); } @@ -188,6 +191,8 @@ public CompoundTag save() { out.put(TAG_CURRENT_POS, NbtUtils.writeBlockPos(this.currentPos)); out.putByte(TAG_ENTERED_FROM, (byte) this.enteredFrom.ordinal()); + if (this.inventoryPos != null) + out.put(TAG_INVENTORY_POS, NbtUtils.writeBlockPos(this.inventoryPos)); out.put(TAG_IMAGE, CastingImage.getCODEC().encodeStart(NbtOps.INSTANCE, currentImage).getOrThrow()); if (this.caster != null) @@ -217,6 +222,7 @@ public static CircleExecutionState load(CompoundTag nbt, ServerLevel world) { var currentPos = NbtUtils.readBlockPos(nbt, TAG_CURRENT_POS).orElseThrow(); var enteredFrom = Direction.values()[nbt.getByte(TAG_ENTERED_FROM)]; + var inventoryPos = NbtUtils.readBlockPos(nbt, TAG_INVENTORY_POS).orElse(null); var image = CastingImage.getCODEC().parse(NbtOps.INSTANCE, nbt.getCompound(TAG_IMAGE)).getOrThrow(); var reachedSlate = nbt.getLong(TAG_REACHED_NUMBER); @@ -232,7 +238,7 @@ public static CircleExecutionState load(CompoundTag nbt, ServerLevel world) { pigment = FrozenPigment.CODEC.parse(NbtOps.INSTANCE, nbt.getCompound(TAG_PIGMENT)).getOrThrow(); return new CircleExecutionState(startPos, startDir, reachedPositions, currentPos, - enteredFrom, image, caster, pigment, reachedSlate, positivePos, negativePos); + enteredFrom, inventoryPos, image, caster, pigment, reachedSlate, positivePos, negativePos); } private static Optional readBlockPos(int[] aint) { diff --git a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/CircleCastEnv.java b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/CircleCastEnv.java index a61a522476..9c35a0e96c 100644 --- a/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/CircleCastEnv.java +++ b/Common/src/main/java/at/petrak/hexcasting/api/casting/eval/env/CircleCastEnv.java @@ -13,6 +13,7 @@ import at.petrak.hexcasting.api.casting.mishaps.MishapDisallowedSpell; import at.petrak.hexcasting.api.mod.HexConfig; import at.petrak.hexcasting.api.pigment.FrozenPigment; +import at.petrak.hexcasting.common.blocks.circles.BlockEntityChestLocus; import at.petrak.hexcasting.common.lib.HexAttributes; import net.minecraft.core.BlockPos; import net.minecraft.network.chat.Component; @@ -163,26 +164,52 @@ public InteractionHand getCastingHand() { } @Override - // TODO: Could do something like get items in inventories adjacent to the circle? public List getUsableStacks(StackDiscoveryMode mode) { - if (this.getCastingEntity() instanceof ServerPlayer caster) + var invPos = this.execState.inventoryPos; + if (invPos != null && world.getBlockEntity(invPos) instanceof BlockEntityChestLocus chestLocus) { + return switch (mode) { + // top row of items is the "hotbar" for use in filtering + case QUERY -> chestLocus.getItems().subList(0,9); + // reverse order so non-hotbar items are prioritized for extraction + case EXTRACTION -> chestLocus.getItems().reversed(); + }; + } else if (this.getCastingEntity() instanceof ServerPlayer caster) { return getUsableStacksForPlayer(mode, null, caster); + } return new ArrayList<>(); } @Override - // TODO: Adjacent inv! public List getPrimaryStacks() { - if (this.getCastingEntity() instanceof ServerPlayer caster) + var invPos = this.execState.inventoryPos; + if (invPos != null && world.getBlockEntity(invPos) instanceof BlockEntityChestLocus chestLocus) { + // slot 0 is treated as the "offhand" + return List.of(new HeldItemInfo(chestLocus.getItem(0), InteractionHand.OFF_HAND)); + } else if (this.getCastingEntity() instanceof ServerPlayer caster) { return getPrimaryStacksForPlayer(InteractionHand.OFF_HAND, caster); + } return List.of(); } @Override - // TODO: Adjacent inv! public boolean replaceItem(Predicate stackOk, ItemStack replaceWith, @Nullable InteractionHand hand) { - if (this.getCastingEntity() instanceof ServerPlayer caster) + var invPos = this.execState.inventoryPos; + if (invPos != null && world.getBlockEntity(invPos) instanceof BlockEntityChestLocus chestLocus) { + if (hand != null && stackOk.test(chestLocus.getItem(0))) { + // slot 0 is treated as the "offhand" + chestLocus.setItem(0, replaceWith); + return true; + } else { + for (int i = 26; i >= 0; i--) { + if (stackOk.test(chestLocus.getItem(i))) { + chestLocus.setItem(0, replaceWith); + return true; + } + } + } + } else if (this.getCastingEntity() instanceof ServerPlayer caster) { return replaceItemForPlayer(stackOk, replaceWith, hand, caster); + } return false; } diff --git a/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/BlockChestLocus.java b/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/BlockChestLocus.java new file mode 100644 index 0000000000..b5a6635641 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/BlockChestLocus.java @@ -0,0 +1,193 @@ +package at.petrak.hexcasting.common.blocks.circles; + +import at.petrak.hexcasting.api.block.circle.BlockCircleComponent; +import at.petrak.hexcasting.api.casting.eval.env.CircleCastEnv; +import at.petrak.hexcasting.api.casting.eval.vm.CastingImage; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.FluidTags; +import net.minecraft.util.RandomSource; +import net.minecraft.world.Containers; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.EntityBlock; +import net.minecraft.world.level.block.SimpleWaterloggedBlock; +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.AttachFace; +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.DirectionProperty; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.phys.BlockHitResult; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.Shapes; +import net.minecraft.world.phys.shapes.VoxelShape; +import org.jetbrains.annotations.Nullable; + +import java.util.EnumSet; + +import static at.petrak.hexcasting.common.blocks.circles.BlockSlate.*; + +public class BlockChestLocus extends BlockCircleComponent implements EntityBlock, SimpleWaterloggedBlock { + public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; + public static final DirectionProperty FACING = BlockStateProperties.FACING; + public static final BooleanProperty OPEN = BlockStateProperties.OPEN; + + private static final VoxelShape BOX_FLOOR = Shapes.or(AABB_FLOOR, Block.box(1, 1, 1, 15, 9, 15)); + private static final VoxelShape BOX_CEILING = Shapes.or(AABB_CEILING, Block.box(1, 7, 1, 15, 15, 15)); + private static final VoxelShape BOX_EAST = Shapes.or(AABB_EAST_WALL, Block.box(1, 1, 1, 9, 15, 15)); + private static final VoxelShape BOX_WEST = Shapes.or(AABB_WEST_WALL, Block.box(7, 1, 1, 15, 15, 15)); + private static final VoxelShape BOX_SOUTH = Shapes.or(AABB_SOUTH_WALL, Block.box(1, 1, 1, 15, 15, 9)); + private static final VoxelShape BOX_NORTH = Shapes.or(AABB_NORTH_WALL, Block.box(1, 1, 7, 15, 15, 15)); + + public BlockChestLocus(Properties properties) { + super(properties); + this.registerDefaultState( + this.stateDefinition.any() + .setValue(OPEN, false) + .setValue(ENERGIZED, false) + .setValue(FACING, Direction.NORTH) + .setValue(WATERLOGGED, false)); + } + + @Override + public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) { + return new BlockEntityChestLocus(blockPos, blockState); + } + + @Override + public ControlFlow acceptControlFlow(CastingImage imageIn, CircleCastEnv env, Direction enterDir, BlockPos pos, + BlockState bs, ServerLevel world) { + + env.circleState().inventoryPos = pos; + + var exitDirsSet = this.possibleExitDirections(pos, bs, world); + exitDirsSet.remove(enterDir.getOpposite()); + var exitDirs = exitDirsSet.stream().map((dir) -> this.exitPositionFromDirection(pos, dir)); + + return new ControlFlow.Continue(imageIn, exitDirs.toList()); + } + + @Override + public Direction normalDir(BlockPos pos, BlockState bs, Level world, int recursionLeft) { + return bs.getValue(FACING); + } + + @Override + public float particleHeight(BlockPos pos, BlockState bs, Level world) { + return 0.5f - 7f / 16f; + } + + @Override + public boolean canEnterFromDirection(Direction enterDir, BlockPos pos, BlockState bs, ServerLevel world) { + var thisNormal = this.normalDir(pos, bs, world); + return enterDir != thisNormal.getOpposite(); + } + + @Override + public EnumSet possibleExitDirections(BlockPos pos, BlockState bs, Level world) { + var allDirs = EnumSet.allOf(Direction.class); + var normal = this.normalDir(pos, bs, world); + allDirs.remove(normal); + return allDirs; + } + + protected InteractionResult useWithoutItem(BlockState blockState, Level level, BlockPos blockPos, Player player, BlockHitResult blockHitResult) { + if (level.isClientSide) { + return InteractionResult.SUCCESS; + } + + BlockEntity blockEntity = level.getBlockEntity(blockPos); + if (blockEntity instanceof BlockEntityChestLocus) { + player.openMenu((BlockEntityChestLocus)blockEntity); + } + + return InteractionResult.CONSUME; + } + + @Override + protected void onRemove(BlockState blockState, Level level, BlockPos blockPos, BlockState blockState2, boolean bl) { + Containers.dropContentsOnDestroy(blockState, blockState2, level, blockPos); + super.onRemove(blockState, level, blockPos, blockState2, bl); + } + + protected void tick(BlockState blockState, ServerLevel serverLevel, BlockPos blockPos, RandomSource randomSource) { + BlockEntity blockEntity = serverLevel.getBlockEntity(blockPos); + if (blockEntity instanceof BlockEntityChestLocus) { + ((BlockEntityChestLocus)blockEntity).recheckOpen(); + } + } + + @Override + public int getAnalogOutputSignal(BlockState pState, Level pLevel, BlockPos pPos) { + return AbstractContainerMenu.getRedstoneSignalFromBlockEntity(pLevel.getBlockEntity(pPos)); + } + + @Override + public VoxelShape getShape(BlockState pState, BlockGetter pLevel, BlockPos pPos, CollisionContext pContext) { + return switch (pState.getValue(FACING)) { + case UP -> BOX_FLOOR; + case DOWN -> BOX_CEILING; + case NORTH -> BOX_NORTH; + case EAST -> BOX_EAST; + case SOUTH -> BOX_SOUTH; + case WEST -> BOX_WEST; + }; + } + + @Override + public BlockState updateShape(BlockState pState, Direction pFacing, BlockState pFacingState, LevelAccessor pLevel, + BlockPos pCurrentPos, BlockPos pFacingPos) { + if (pState.getValue(WATERLOGGED)) { + pLevel.scheduleTick(pCurrentPos, Fluids.WATER, Fluids.WATER.getTickDelay(pLevel)); + } + + return (pFacing.getOpposite() == pState.getValue(FACING) && !pState.canSurvive(pLevel, pCurrentPos)) ? + pState.getFluidState().createLegacyBlock() + : super.updateShape(pState, pFacing, pFacingState, pLevel, pCurrentPos, pFacingPos); + } + + @Override + @Nullable + public BlockState getStateForPlacement(BlockPlaceContext pContext) { + FluidState fluidState = pContext.getLevel().getFluidState(pContext.getClickedPos()); + for (Direction direction : pContext.getNearestLookingDirections()) { + var state = this.defaultBlockState() + .setValue(FACING, direction.getOpposite()) + .setValue(WATERLOGGED, fluidState.is(FluidTags.WATER) && fluidState.getAmount() == 8); + if (state.canSurvive(pContext.getLevel(), pContext.getClickedPos())) { + return state; + } + } + return null; + } + + @Override + protected boolean canSurvive(BlockState blockState, LevelReader levelReader, BlockPos blockPos) { + Direction direction = blockState.getValue(FACING); + return this.canAttachTo(levelReader, blockPos.relative(direction.getOpposite()), direction); + } + + private boolean canAttachTo(BlockGetter blockGetter, BlockPos blockPos, Direction direction) { + BlockState blockState = blockGetter.getBlockState(blockPos); + return blockState.isFaceSturdy(blockGetter, blockPos, direction); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(WATERLOGGED, FACING, OPEN); + } +} diff --git a/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/BlockEntityChestLocus.java b/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/BlockEntityChestLocus.java new file mode 100644 index 0000000000..f45d402529 --- /dev/null +++ b/Common/src/main/java/at/petrak/hexcasting/common/blocks/circles/BlockEntityChestLocus.java @@ -0,0 +1,113 @@ +package at.petrak.hexcasting.common.blocks.circles; + +import at.petrak.hexcasting.common.lib.HexBlockEntities; +import net.minecraft.core.BlockPos; +import net.minecraft.core.HolderLookup.Provider; +import net.minecraft.core.NonNullList; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.chat.Component; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.ContainerHelper; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.inventory.AbstractContainerMenu; +import net.minecraft.world.inventory.ChestMenu; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.entity.ContainerOpenersCounter; +import net.minecraft.world.level.block.entity.RandomizableContainerBlockEntity; +import net.minecraft.world.level.block.state.BlockState; + +public class BlockEntityChestLocus extends RandomizableContainerBlockEntity { + private NonNullList items = NonNullList.withSize(27, ItemStack.EMPTY); + private final ContainerOpenersCounter openersCounter = new ContainerOpenersCounter() { + @Override + protected void onOpen(Level level, BlockPos blockPos, BlockState blockState) { + level.playSound(null, blockPos, SoundEvents.BAMBOO_WOOD_TRAPDOOR_OPEN, SoundSource.BLOCKS, + 0.8F, level.random.nextFloat() * 0.1F + 0.9F); + level.setBlock(blockPos, blockState.setValue(BlockChestLocus.OPEN, true), 3); + } + @Override + protected void onClose(Level level, BlockPos blockPos, BlockState blockState) { + level.playSound(null, blockPos, SoundEvents.BAMBOO_WOOD_TRAPDOOR_CLOSE, SoundSource.BLOCKS, + 0.8F, level.random.nextFloat() * 0.1F + 0.9F); + level.setBlock(blockPos, blockState.setValue(BlockChestLocus.OPEN, false), 3); + } + @Override + protected void openerCountChanged(Level level, BlockPos blockPos, BlockState blockState, int i, int j) {} + @Override + protected boolean isOwnContainer(Player player) { + if (player.containerMenu instanceof ChestMenu chestMenu) { + return chestMenu.getContainer() == BlockEntityChestLocus.this; + } + return false; + } + }; + + public BlockEntityChestLocus(BlockPos blockPos, BlockState blockState) { + super(HexBlockEntities.CHEST_LOCUS_TILE.get(), blockPos, blockState); + } + + @Override + protected void loadAdditional(CompoundTag compoundTag, Provider provider) { + super.loadAdditional(compoundTag, provider); + this.items = NonNullList.withSize(this.getContainerSize(), ItemStack.EMPTY); + if (!this.tryLoadLootTable(compoundTag)) { + ContainerHelper.loadAllItems(compoundTag, this.items, provider); + } + } + + @Override + protected void saveAdditional(CompoundTag compoundTag, Provider provider) { + super.saveAdditional(compoundTag, provider); + if (!this.trySaveLootTable(compoundTag)) { + ContainerHelper.saveAllItems(compoundTag, this.items, provider); + } + } + + @Override + public int getContainerSize() { + return 27; + } + + @Override + protected Component getDefaultName() { + return Component.translatable("hexcasting.container.chest_locus"); + } + + @Override + public NonNullList getItems() { + return this.items; + } + + @Override + protected void setItems(NonNullList itemList) { + this.items = itemList; + } + + @Override + protected AbstractContainerMenu createMenu(int i, Inventory inventory) { + return ChestMenu.threeRows(i, inventory, this); + } + + @Override + public void startOpen(Player player) { + if (!this.remove && !player.isSpectator()) { + this.openersCounter.incrementOpeners(player, this.getLevel(), this.getBlockPos(), this.getBlockState()); + } + } + + @Override + public void stopOpen(Player player) { + if (!this.remove && !player.isSpectator()) { + this.openersCounter.decrementOpeners(player, this.getLevel(), this.getBlockPos(), this.getBlockState()); + } + } + + public void recheckOpen() { + if (!this.remove) { + this.openersCounter.recheckOpeners(this.getLevel(), this.getBlockPos(), this.getBlockState()); + } + } +} diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexBlockEntities.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexBlockEntities.java index 988af09b34..131f373cde 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexBlockEntities.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexBlockEntities.java @@ -2,6 +2,7 @@ import at.petrak.hexcasting.common.blocks.BlockQuenchedAllay; import at.petrak.hexcasting.common.blocks.akashic.BlockEntityAkashicBookshelf; +import at.petrak.hexcasting.common.blocks.circles.BlockEntityChestLocus; import at.petrak.hexcasting.common.blocks.circles.BlockEntitySlate; import at.petrak.hexcasting.common.blocks.circles.impetuses.BlockEntityLookingImpetus; import at.petrak.hexcasting.common.blocks.circles.impetuses.BlockEntityRedstoneImpetus; @@ -31,6 +32,11 @@ public static void register() { () -> IXplatAbstractions.INSTANCE.createBlockEntityType(BlockEntityAkashicBookshelf::new, HexBlocks.AKASHIC_BOOKSHELF.get())); + public static final Supplier> CHEST_LOCUS_TILE = REGISTER.register( + "chest_locus", + () -> IXplatAbstractions.INSTANCE.createBlockEntityType(BlockEntityChestLocus::new, + HexBlocks.CHEST_LOCUS.get())); + public static final Supplier> IMPETUS_REDSTONE_TILE = REGISTER.register( "impetus/redstone", () -> IXplatAbstractions.INSTANCE.createBlockEntityType(BlockEntityRedstoneImpetus::new, diff --git a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexBlocks.java b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexBlocks.java index 4f9fd2f54e..2320538a1c 100644 --- a/Common/src/main/java/at/petrak/hexcasting/common/lib/HexBlocks.java +++ b/Common/src/main/java/at/petrak/hexcasting/common/lib/HexBlocks.java @@ -8,6 +8,7 @@ import at.petrak.hexcasting.common.blocks.akashic.BlockAkashicBookshelf; import at.petrak.hexcasting.common.blocks.akashic.BlockAkashicLigature; import at.petrak.hexcasting.common.blocks.akashic.BlockAkashicRecord; +import at.petrak.hexcasting.common.blocks.circles.BlockChestLocus; import at.petrak.hexcasting.common.blocks.circles.BlockEmptyImpetus; import at.petrak.hexcasting.common.blocks.circles.BlockSlate; import at.petrak.hexcasting.common.blocks.circles.directrix.BlockBooleanDirectrix; @@ -150,6 +151,9 @@ private static BlockBehaviour.Properties quenched() { new BlockSlate(slateish() .pushReaction(PushReaction.DESTROY))); + public static final Supplier CHEST_LOCUS = blockItem("locus/chest", () -> + new BlockChestLocus(slateish())); + public static final Supplier IMPETUS_EMPTY = blockItem("impetus/empty", () -> new BlockEmptyImpetus(slateish() .pushReaction(PushReaction.BLOCK))); diff --git a/Common/src/main/java/at/petrak/hexcasting/datagen/HexLootTables.java b/Common/src/main/java/at/petrak/hexcasting/datagen/HexLootTables.java index 713b16d052..f732ec6cab 100644 --- a/Common/src/main/java/at/petrak/hexcasting/datagen/HexLootTables.java +++ b/Common/src/main/java/at/petrak/hexcasting/datagen/HexLootTables.java @@ -45,7 +45,7 @@ public HexLootTables(HolderLookup.Provider registries) { @Override protected void makeLootTables(Map blockTables, Map, LootTable.Builder> lootTables) { - dropSelf(blockTables, HexBlocks.IMPETUS_EMPTY.get(), + dropSelf(blockTables, HexBlocks.CHEST_LOCUS.get(), HexBlocks.IMPETUS_EMPTY.get(), HexBlocks.IMPETUS_RIGHTCLICK.get(), HexBlocks.IMPETUS_LOOK.get(), HexBlocks.IMPETUS_REDSTONE.get(), HexBlocks.EMPTY_DIRECTRIX.get(), HexBlocks.DIRECTRIX_REDSTONE.get(), HexBlocks.DIRECTRIX_BOOLEAN.get(), HexBlocks.AKASHIC_RECORD.get(), HexBlocks.AKASHIC_BOOKSHELF.get(), HexBlocks.AKASHIC_LIGATURE.get(), diff --git a/Common/src/main/java/at/petrak/hexcasting/datagen/recipe/HexplatRecipes.java b/Common/src/main/java/at/petrak/hexcasting/datagen/recipe/HexplatRecipes.java index 4f817a0293..02964d379e 100644 --- a/Common/src/main/java/at/petrak/hexcasting/datagen/recipe/HexplatRecipes.java +++ b/Common/src/main/java/at/petrak/hexcasting/datagen/recipe/HexplatRecipes.java @@ -416,6 +416,15 @@ public void buildRecipes(RecipeOutput recipes) { .pattern("SSC") .unlockedBy("enlightenment", new Criterion<>(HexAdvancementTriggers.OVERCAST_TRIGGER.get(), enlightenment)).save(recipes); + ShapedRecipeBuilder.shaped(RecipeCategory.REDSTONE, HexBlocks.CHEST_LOCUS.get()) + .define('E', HexBlocks.EDIFIED_PLANKS.get()) + .define('C', Blocks.CHEST) + .define('S', HexItems.SLATE.get()) + .pattern(" E ") + .pattern("ECE") + .pattern(" S ") + .unlockedBy("enlightenment", new Criterion<>(HexAdvancementTriggers.OVERCAST_TRIGGER.get(), enlightenment)).save(recipes); + ShapedRecipeBuilder.shaped(RecipeCategory.REDSTONE, HexBlocks.AKASHIC_BOOKSHELF.get()) .define('L', HexTags.Items.EDIFIED_LOGS) .define('P', HexTags.Items.EDIFIED_PLANKS) diff --git a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 index b9cab3d8fd..5995b083ae 100644 --- a/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 +++ b/Common/src/main/resources/assets/hexcasting/lang/en_us.flatten.json5 @@ -138,6 +138,10 @@ look: "Fletcher Impetus", redstone: "Cleric Impetus", }, + + locus: { + chest: "Storage Locus", + }, akashic_: { record: "Akashic Record", @@ -551,6 +555,8 @@ hexcasting: { "pattern.unknown": "Unknown pattern resource location %s", + + "container.chest_locus": "Storage Locus", debug: { media_withdrawn: "%s - Media withdrawn: %s", @@ -1316,6 +1322,7 @@ spellcircles: "Spell Circles", impetus: "Impeti", directrix: "Directrices", + chest_locus: "Storage Locus", akashiclib: "Akashic Libraries", quenching_allays: "Quenching Allays", fanciful_staves: "Fanciful Staves", @@ -1761,6 +1768,12 @@ directrix_redstone: "A $(l:greatwork/directrix)$(item)Mason Directrix/$ switches output side based on a redstone signal. Without a signal, the exit is the _media-color side; with a signal, the exit is the redstone-color side.", directrix_boolean: "A $(l:greatwork/directrix)$(item)Shepherd Directrix/$ switches output side based on a boolean popped from the stack. A $(thing)True/$ makes the wave exit from the back, a $(thing)False/$ from the front. Failing to provide a boolean at all will cause a mishap." }, + + chest_locus: { + "1": "Even when cast from a spell circle, certain spells rely on the presence of items in my inventory or offhand. Such a limitation is clearly not befitting of my grand design!$(br2)Consequently, I have developed a solution. By affixing an $(l:items/edified)$(item)edified/$ box to a $(l:items/slate)$(item)slate/$, I can create a surrogate inventory for the spell circle in which I install it. I call this device a $(l:greatwork/chest_locus)$(item)Storage Locus/$.", + "2": "The locus works like a chest, but it can be built directly into a spell circle. Once the _media wave passes over it, any subsequent spells cast by the circle will reach for its inventory rather than mine.$(br2)Activating a second $(l:greatwork/chest_locus)$(item)Storage Locus/$ within the same circle will cause spells to look there instead - only one can be active at a time. A cleverly built circle might be able to take advantage of this to swap between multiple \"hands\".", + "crafting.desc": "The upper row of slots in the inventory of the locus acts as the hotbar, for the purposes of $(l:patterns/spells/itempicking)item selection/$. The leftmost slot in that row acts as the offhand.", + }, akashiclib: { "1": "I KNOW SO MUCH it is ONLY RIGHT to have a place to store it all. Information can be stored in books but it is oh so so so so $(italic)slow/$ to write by hand and read by eye. I demand BETTER. And so I shall MAKE better.$(br2)... I am getting worse ... do not know if I have time to write everything bursting through my head before expiring.", diff --git a/Common/src/main/resources/assets/hexcasting/models/block/chest_locus.json b/Common/src/main/resources/assets/hexcasting/models/block/chest_locus.json new file mode 100644 index 0000000000..b06839f0d9 --- /dev/null +++ b/Common/src/main/resources/assets/hexcasting/models/block/chest_locus.json @@ -0,0 +1,59 @@ +{ + "credit": "Made with Blockbench", + "parent": "block/block", + "textures": { + "0": "hexcasting:block/slate", + "1": "hexcasting:block/circle/locus/chest_side", + "2": "hexcasting:block/circle/locus/chest_closed", + "particle": "hexcasting:block/slate" + }, + "elements": [ + { + "name": "slate", + "from": [0, 0, 0], + "to": [16, 1, 16], + "faces": { + "north": {"uv": [0, 0, 16, 1], "texture": "#0"}, + "east": {"uv": [15, 0, 16, 16], "rotation": 90, "texture": "#0"}, + "south": {"uv": [0, 15, 16, 16], "texture": "#0"}, + "west": {"uv": [0, 0, 1, 16], "rotation": 270, "texture": "#0"}, + "up": {"uv": [0, 0, 16, 16], "texture": "#0"}, + "down": {"uv": [0, 0, 16, 16], "texture": "#0"} + } + }, + { + "name": "chest", + "from": [1, 1, 1], + "to": [15, 9, 15], + "faces": { + "north": {"uv": [1, 8, 15, 16], "texture": "#1"}, + "east": {"uv": [1, 8, 15, 16], "texture": "#1"}, + "south": {"uv": [1, 8, 15, 16], "texture": "#1"}, + "west": {"uv": [1, 8, 15, 16], "texture": "#1"}, + "up": {"uv": [1, 1, 15, 15], "texture": "#2"} + } + } + ], + "display": { + "gui": { + "rotation": [ 30, 45, 0 ], + "translation": [ 0, 0, 0], + "scale":[ 0.625, 0.625, 0.625 ] + }, + "thirdperson_righthand": { + "rotation": [ 75, 225, 0 ], + "translation": [ 0, 2.5, 0], + "scale": [ 0.375, 0.375, 0.375 ] + }, + "firstperson_righthand": { + "rotation": [ 0, 315, 0 ], + "translation": [ 0, 0, 0 ], + "scale": [ 0.40, 0.40, 0.40 ] + }, + "firstperson_lefthand": { + "rotation": [ 0, 315, 0 ], + "translation": [ 0, 0, 0 ], + "scale": [ 0.40, 0.40, 0.40 ] + } + } +} \ No newline at end of file diff --git a/Common/src/main/resources/assets/hexcasting/models/block/chest_locus_open.json b/Common/src/main/resources/assets/hexcasting/models/block/chest_locus_open.json new file mode 100644 index 0000000000..1590b51c73 --- /dev/null +++ b/Common/src/main/resources/assets/hexcasting/models/block/chest_locus_open.json @@ -0,0 +1,9 @@ +{ + "parent": "hexcasting:block/chest_locus", + "textures": { + "0": "hexcasting:block/slate", + "1": "hexcasting:block/circle/locus/chest_side", + "2": "hexcasting:block/circle/locus/chest_open", + "particle": "hexcasting:block/slate" + } +} \ No newline at end of file diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/akashiclib.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/akashiclib.json index ad63b36001..453beda304 100644 --- a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/akashiclib.json +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/akashiclib.json @@ -4,7 +4,7 @@ "icon": "hexcasting:akashic_record", "advancement": "hexcasting:enlightenment", "entry_color": "54398a", - "sortnum": 4, + "sortnum": 5, "pages": [ { "type": "patchouli:text", diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/chest_locus.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/chest_locus.json new file mode 100644 index 0000000000..466683e660 --- /dev/null +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/chest_locus.json @@ -0,0 +1,23 @@ +{ + "name": "hexcasting.entry.chest_locus", + "category": "hexcasting:greatwork", + "icon": "hexcasting:locus/chest", + "advancement": "hexcasting:enlightenment", + "entry_color": "54398a", + "sortnum": 4, + "pages": [ + { + "type": "patchouli:text", + "text": "hexcasting.page.chest_locus.1" + }, + { + "type": "patchouli:text", + "text": "hexcasting.page.chest_locus.2" + }, + { + "type": "patchouli:crafting", + "recipe": "hexcasting:locus/chest", + "text": "hexcasting.page.chest_locus.crafting.desc" + } + ] +} \ No newline at end of file diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/fanciful_staves.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/fanciful_staves.json index f62fbc0013..b1c04421b4 100644 --- a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/fanciful_staves.json +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/fanciful_staves.json @@ -2,7 +2,7 @@ "name": "hexcasting.entry.fanciful_staves", "category": "hexcasting:greatwork", "icon": "hexcasting:staff/mindsplice", - "sortnum": 6, + "sortnum": 7, "advancement": "hexcasting:enlightenment", "entry_color": "54398a", "pages": [ diff --git a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/quenching_allays.json b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/quenching_allays.json index f11537b77d..7f58d95817 100644 --- a/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/quenching_allays.json +++ b/Common/src/main/resources/assets/hexcasting/patchouli_books/thehexbook/en_us/entries/greatwork/quenching_allays.json @@ -4,7 +4,7 @@ "icon": "hexcasting:quenched_allay", "advancement": "hexcasting:enlightenment", "entry_color": "54398a", - "sortnum": 5, + "sortnum": 6, "pages": [ { "type": "patchouli:text", diff --git a/Common/src/main/resources/assets/hexcasting/textures/block/circle/locus/chest_closed.png b/Common/src/main/resources/assets/hexcasting/textures/block/circle/locus/chest_closed.png new file mode 100644 index 0000000000..ad99d3bc7a Binary files /dev/null and b/Common/src/main/resources/assets/hexcasting/textures/block/circle/locus/chest_closed.png differ diff --git a/Common/src/main/resources/assets/hexcasting/textures/block/circle/locus/chest_open.png b/Common/src/main/resources/assets/hexcasting/textures/block/circle/locus/chest_open.png new file mode 100644 index 0000000000..bd55db307b Binary files /dev/null and b/Common/src/main/resources/assets/hexcasting/textures/block/circle/locus/chest_open.png differ diff --git a/Common/src/main/resources/assets/hexcasting/textures/block/circle/locus/chest_side.png b/Common/src/main/resources/assets/hexcasting/textures/block/circle/locus/chest_side.png new file mode 100644 index 0000000000..e0431e144a Binary files /dev/null and b/Common/src/main/resources/assets/hexcasting/textures/block/circle/locus/chest_side.png differ diff --git a/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/redstone/locus/chest.json b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/redstone/locus/chest.json new file mode 100644 index 0000000000..f22eb01746 --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/advancement/recipes/redstone/locus/chest.json @@ -0,0 +1,35 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "enlightenment": { + "conditions": { + "health_used": { + "min": 0.8 + }, + "media_generated": {}, + "mojang_i_am_begging_and_crying_please_add_an_entity_health_criterion": { + "max": 1.0, + "min": 2.2250738585072014E-308 + } + }, + "trigger": "hexcasting:overcast" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:locus/chest" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "enlightenment" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:locus/chest" + ] + } +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/loot_table/blocks/locus/chest.json b/Fabric/src/generated/resources/data/hexcasting/loot_table/blocks/locus/chest.json new file mode 100644 index 0000000000..b3fdb297e3 --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/loot_table/blocks/locus/chest.json @@ -0,0 +1,15 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "hexcasting:locus/chest" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "hexcasting:blocks/locus/chest" +} \ No newline at end of file diff --git a/Fabric/src/generated/resources/data/hexcasting/recipe/locus/chest.json b/Fabric/src/generated/resources/data/hexcasting/recipe/locus/chest.json new file mode 100644 index 0000000000..ca31765021 --- /dev/null +++ b/Fabric/src/generated/resources/data/hexcasting/recipe/locus/chest.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "redstone", + "key": { + "C": { + "item": "minecraft:chest" + }, + "E": { + "item": "hexcasting:edified_planks" + }, + "S": { + "item": "hexcasting:slate" + } + }, + "pattern": [ + " E ", + "ECE", + " S " + ], + "result": { + "count": 1, + "id": "hexcasting:locus/chest" + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/assets/hexcasting/blockstates/locus/chest.json b/Neoforge/src/generated/resources/assets/hexcasting/blockstates/locus/chest.json new file mode 100644 index 0000000000..6f4acb0b0e --- /dev/null +++ b/Neoforge/src/generated/resources/assets/hexcasting/blockstates/locus/chest.json @@ -0,0 +1,56 @@ +{ + "variants": { + "facing=down,open=false": { + "model": "hexcasting:block/chest_locus", + "x": 180 + }, + "facing=down,open=true": { + "model": "hexcasting:block/chest_locus_open", + "x": 180 + }, + "facing=east,open=false": { + "model": "hexcasting:block/chest_locus", + "x": 270, + "y": 270 + }, + "facing=east,open=true": { + "model": "hexcasting:block/chest_locus_open", + "x": 270, + "y": 270 + }, + "facing=north,open=false": { + "model": "hexcasting:block/chest_locus", + "x": 270, + "y": 180 + }, + "facing=north,open=true": { + "model": "hexcasting:block/chest_locus_open", + "x": 270, + "y": 180 + }, + "facing=south,open=false": { + "model": "hexcasting:block/chest_locus", + "x": 270 + }, + "facing=south,open=true": { + "model": "hexcasting:block/chest_locus_open", + "x": 270 + }, + "facing=up,open=false": { + "model": "hexcasting:block/chest_locus" + }, + "facing=up,open=true": { + "model": "hexcasting:block/chest_locus_open" + }, + "facing=west,open=false": { + "model": "hexcasting:block/chest_locus", + "x": 270, + "y": 90 + }, + "facing=west,open=true": { + "model": "hexcasting:block/chest_locus_open", + "x": 270, + "y": 90 + } + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/assets/hexcasting/models/item/locus/chest.json b/Neoforge/src/generated/resources/assets/hexcasting/models/item/locus/chest.json new file mode 100644 index 0000000000..db79c5fdd5 --- /dev/null +++ b/Neoforge/src/generated/resources/assets/hexcasting/models/item/locus/chest.json @@ -0,0 +1,3 @@ +{ + "parent": "hexcasting:block/chest_locus" +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/redstone/locus/chest.json b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/redstone/locus/chest.json new file mode 100644 index 0000000000..f22eb01746 --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/advancement/recipes/redstone/locus/chest.json @@ -0,0 +1,35 @@ +{ + "parent": "minecraft:recipes/root", + "criteria": { + "enlightenment": { + "conditions": { + "health_used": { + "min": 0.8 + }, + "media_generated": {}, + "mojang_i_am_begging_and_crying_please_add_an_entity_health_criterion": { + "max": 1.0, + "min": 2.2250738585072014E-308 + } + }, + "trigger": "hexcasting:overcast" + }, + "has_the_recipe": { + "conditions": { + "recipe": "hexcasting:locus/chest" + }, + "trigger": "minecraft:recipe_unlocked" + } + }, + "requirements": [ + [ + "has_the_recipe", + "enlightenment" + ] + ], + "rewards": { + "recipes": [ + "hexcasting:locus/chest" + ] + } +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/loot_table/blocks/locus/chest.json b/Neoforge/src/generated/resources/data/hexcasting/loot_table/blocks/locus/chest.json new file mode 100644 index 0000000000..b3fdb297e3 --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/loot_table/blocks/locus/chest.json @@ -0,0 +1,15 @@ +{ + "pools": [ + { + "bonus_rolls": 0.0, + "entries": [ + { + "type": "minecraft:item", + "name": "hexcasting:locus/chest" + } + ], + "rolls": 1.0 + } + ], + "random_sequence": "hexcasting:blocks/locus/chest" +} \ No newline at end of file diff --git a/Neoforge/src/generated/resources/data/hexcasting/recipe/locus/chest.json b/Neoforge/src/generated/resources/data/hexcasting/recipe/locus/chest.json new file mode 100644 index 0000000000..ca31765021 --- /dev/null +++ b/Neoforge/src/generated/resources/data/hexcasting/recipe/locus/chest.json @@ -0,0 +1,24 @@ +{ + "type": "minecraft:crafting_shaped", + "category": "redstone", + "key": { + "C": { + "item": "minecraft:chest" + }, + "E": { + "item": "hexcasting:edified_planks" + }, + "S": { + "item": "hexcasting:slate" + } + }, + "pattern": [ + " E ", + "ECE", + " S " + ], + "result": { + "count": 1, + "id": "hexcasting:locus/chest" + } +} \ No newline at end of file diff --git a/Neoforge/src/main/java/at/petrak/hexcasting/forge/datagen/xplat/HexBlockStatesAndModels.java b/Neoforge/src/main/java/at/petrak/hexcasting/forge/datagen/xplat/HexBlockStatesAndModels.java index d1f11fd7aa..0f857c2fcc 100644 --- a/Neoforge/src/main/java/at/petrak/hexcasting/forge/datagen/xplat/HexBlockStatesAndModels.java +++ b/Neoforge/src/main/java/at/petrak/hexcasting/forge/datagen/xplat/HexBlockStatesAndModels.java @@ -3,6 +3,7 @@ import at.petrak.hexcasting.api.HexAPI; import at.petrak.hexcasting.api.block.circle.BlockCircleComponent; import at.petrak.hexcasting.common.blocks.akashic.BlockAkashicBookshelf; +import at.petrak.hexcasting.common.blocks.circles.BlockChestLocus; import at.petrak.hexcasting.common.blocks.circles.BlockSlate; import at.petrak.hexcasting.common.blocks.circles.directrix.BlockBooleanDirectrix; import at.petrak.hexcasting.common.blocks.circles.directrix.BlockRedstoneDirectrix; @@ -12,12 +13,17 @@ import net.minecraft.data.PackOutput; import net.minecraft.resources.ResourceLocation; import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.AttachFace; import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.neoforged.neoforge.client.model.generators.BlockModelBuilder; import net.neoforged.neoforge.client.model.generators.ConfiguredModel; import net.neoforged.neoforge.client.model.generators.ModelBuilder; +import net.neoforged.neoforge.client.model.generators.ModelFile; import net.neoforged.neoforge.common.data.ExistingFileHelper; +import java.util.function.Function; + import static net.neoforged.neoforge.client.model.generators.ModelProvider.BLOCK_FOLDER; public class HexBlockStatesAndModels extends PaucalBlockStateAndModelProvider { @@ -52,6 +58,19 @@ protected void registerStatesAndModels() { .build(); }, BlockSlate.WATERLOGGED); + var chestLocusClosed = models().getExistingFile(modLoc("chest_locus")); + var chestLocusOpen = models().getExistingFile(modLoc("chest_locus_open")); + getVariantBuilder(HexBlocks.CHEST_LOCUS.get()).forAllStatesExcept(state -> { + Direction dir = state.getValue(BlockStateProperties.FACING); + return ConfiguredModel.builder() + .modelFile(state.getValue(BlockStateProperties.OPEN) ? chestLocusOpen : chestLocusClosed) + .rotationX(dir == Direction.DOWN ? 180 : dir.getAxis().isHorizontal() ? 270 : 0) + .rotationY(dir.getAxis().isVertical() ? 0 : ((int) dir.toYRot()) % 360) + .build(); + }, BlockChestLocus.WATERLOGGED, BlockChestLocus.ENERGIZED); + // simpleItemModel doesn't like slashes in the path, so we do it manually + itemModels().getBuilder("item/locus/chest").parent(chestLocusClosed); + impetus(HexBlocks.IMPETUS_EMPTY.get(), "impetus/empty", "empty", false); impetus(HexBlocks.IMPETUS_RIGHTCLICK.get(), "impetus/rightclick", "rightclick", true); impetus(HexBlocks.IMPETUS_LOOK.get(), "impetus/look", "look", true);