diff --git a/gradle.properties b/gradle.properties index 40fc81b4..6eeed752 100644 --- a/gradle.properties +++ b/gradle.properties @@ -8,7 +8,7 @@ yarn_mappings=6 loader_version=0.11.3 # Mod Properties -mod_version = 0.1.2 +mod_version = 0.1.3 maven_group = ru.bclib archives_base_name = bclib diff --git a/src/main/java/ru/bclib/blocks/BaseAttachedBlock.java b/src/main/java/ru/bclib/blocks/BaseAttachedBlock.java new file mode 100644 index 00000000..9d021fd0 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/BaseAttachedBlock.java @@ -0,0 +1,75 @@ +package ru.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.item.context.BlockPlaceContext; +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.Mirror; +import net.minecraft.world.level.block.Rotation; +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 ru.bclib.util.BlocksHelper; + +public abstract class BaseAttachedBlock extends BaseBlockNotFull { + public static final DirectionProperty FACING = BlockStateProperties.FACING; + + public BaseAttachedBlock(Properties settings) { + super(settings); + this.registerDefaultState(this.defaultBlockState().setValue(FACING, Direction.UP)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(FACING); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext ctx) { + BlockState blockState = this.defaultBlockState(); + LevelReader worldView = ctx.getLevel(); + BlockPos blockPos = ctx.getClickedPos(); + Direction[] directions = ctx.getNearestLookingDirections(); + for (Direction direction : directions) { + Direction direction2 = direction.getOpposite(); + blockState = blockState.setValue(FACING, direction2); + if (blockState.canSurvive(worldView, blockPos)) { + return blockState; + } + } + return null; + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + Direction direction = (Direction) state.getValue(FACING); + BlockPos blockPos = pos.relative(direction.getOpposite()); + return canSupportCenter(world, blockPos, direction) || world.getBlockState(blockPos).is(BlockTags.LEAVES); + } + + @Override + public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) { + if (!canSurvive(state, world, pos)) { + return Blocks.AIR.defaultBlockState(); + } + else { + return state; + } + } + + + @Override + public BlockState rotate(BlockState state, Rotation rotation) { + return BlocksHelper.rotateHorizontal(state, rotation, FACING); + } + + @Override + public BlockState mirror(BlockState state, Mirror mirror) { + return BlocksHelper.mirrorHorizontal(state, mirror, FACING); + } +} diff --git a/src/main/java/ru/bclib/blocks/BaseBarkBlock.java b/src/main/java/ru/bclib/blocks/BaseBarkBlock.java new file mode 100644 index 00000000..bd59a1dc --- /dev/null +++ b/src/main/java/ru/bclib/blocks/BaseBarkBlock.java @@ -0,0 +1,24 @@ +package ru.bclib.blocks; + +import java.util.Optional; + +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import ru.betterend.client.models.Patterns; + +public class BarkBlock extends EndPillarBlock { + public BarkBlock(Properties settings) { + super(settings); + } + + @Override + protected Optional createBlockPattern(ResourceLocation blockId) { + blockId = Registry.BLOCK.getKey(this); + return Patterns.createJson(Patterns.BLOCK_BASE, getName(blockId), blockId.getPath()); + } + + private String getName(ResourceLocation blockId) { + String name = blockId.getPath(); + return name.replace("_bark", "_log_side"); + } +} diff --git a/src/main/java/ru/bclib/blocks/BaseBlock.java b/src/main/java/ru/bclib/blocks/BaseBlock.java new file mode 100644 index 00000000..274261a5 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/BaseBlock.java @@ -0,0 +1,28 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.bclib.client.models.BlockModelProvider; + +public class BaseBlock extends Block implements BlockModelProvider { + public BaseBlock(Properties settings) { + super(settings); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public BlockModel getItemModel(ResourceLocation blockId) { + return getBlockModel(blockId, defaultBlockState()); + } +} \ No newline at end of file diff --git a/src/main/java/ru/bclib/blocks/BaseBlockNotFull.java b/src/main/java/ru/bclib/blocks/BaseBlockNotFull.java new file mode 100644 index 00000000..6d1a0a38 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/BaseBlockNotFull.java @@ -0,0 +1,25 @@ +package ru.bclib.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.EntityType; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.state.BlockState; + +public class BaseBlockNotFull extends BaseBlock { + + public BaseBlockNotFull(Properties settings) { + super(settings); + } + + public boolean canSuffocate(BlockState state, BlockGetter view, BlockPos pos) { + return false; + } + + public boolean isSimpleFullBlock(BlockState state, BlockGetter view, BlockPos pos) { + return false; + } + + public boolean allowsSpawning(BlockState state, BlockGetter view, BlockPos pos, EntityType type) { + return false; + } +} diff --git a/src/main/java/ru/bclib/blocks/BaseBlockWithEntity.java b/src/main/java/ru/bclib/blocks/BaseBlockWithEntity.java new file mode 100644 index 00000000..fa731e03 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/BaseBlockWithEntity.java @@ -0,0 +1,27 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; + +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.BaseEntityBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; + +public class BaseBlockWithEntity extends BaseEntityBlock { + public BaseBlockWithEntity(Properties settings) { + super(settings); + } + + @Override + public BlockEntity newBlockEntity(BlockGetter world) { + return null; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } +} diff --git a/src/main/java/ru/bclib/blocks/BaseCropBlock.java b/src/main/java/ru/bclib/blocks/BaseCropBlock.java new file mode 100644 index 00000000..86bf1f20 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/BaseCropBlock.java @@ -0,0 +1,122 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import com.google.common.collect.Lists; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.util.Mth; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.item.enchantment.Enchantments; +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.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import ru.bclib.util.MHelper; +import ru.betterend.util.BlocksHelper; + +public class BaseCropBlock extends BasePlantBlock { + private static final VoxelShape SHAPE = Block.box(2, 0, 2, 14, 14, 14); + public static final IntegerProperty AGE = IntegerProperty.create("age", 0, 3); + + private final Block[] terrain; + private final Item drop; + + public BaseCropBlock(Item drop, Block... terrain) { + super(FabricBlockSettings.of(Material.PLANT) + .breakByTool(FabricToolTags.HOES) + .breakByHand(true) + .sound(SoundType.GRASS) + .randomTicks() + .noCollission()); + this.drop = drop; + this.terrain = terrain; + this.registerDefaultState(defaultBlockState().setValue(AGE, 0)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(AGE); + } + + @Override + protected boolean isTerrain(BlockState state) { + for (Block block: terrain) { + if (state.is(block)) { + return true; + } + } + return false; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + if (state.getValue(AGE) < 3) { + return Collections.singletonList(new ItemStack(this)); + } + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && tool.isCorrectToolForDrops(state)) { + int enchantment = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_FORTUNE, tool); + if (enchantment > 0) { + int countSeeds = MHelper.randRange(Mth.clamp(1 + enchantment, 1, 3), 3, MHelper.RANDOM); + int countDrops = MHelper.randRange(Mth.clamp(1 + enchantment, 1, 2), 2, MHelper.RANDOM); + return Lists.newArrayList(new ItemStack(this, countSeeds), new ItemStack(drop, countDrops)); + } + } + int countSeeds = MHelper.randRange(1, 3, MHelper.RANDOM); + int countDrops = MHelper.randRange(1, 2, MHelper.RANDOM); + return Lists.newArrayList(new ItemStack(this, countSeeds), new ItemStack(drop, countDrops)); + } + + @Override + public BlockBehaviour.OffsetType getOffsetType() { + return BlockBehaviour.OffsetType.NONE; + } + + @Override + public void performBonemeal(ServerLevel world, Random random, BlockPos pos, BlockState state) { + int age = state.getValue(AGE); + if (age < 3) { + BlocksHelper.setWithUpdate(world, pos, state.setValue(AGE, age + 1)); + } + } + + @Override + public boolean isValidBonemealTarget(BlockGetter world, BlockPos pos, BlockState state, boolean isClient) { + return state.getValue(AGE) < 3; + } + + @Override + public boolean isBonemealSuccess(Level world, Random random, BlockPos pos, BlockState state) { + return state.getValue(AGE) < 3; + } + + @Override + public void tick(BlockState state, ServerLevel world, BlockPos pos, Random random) { + super.tick(state, world, pos, random); + if (isBonemealSuccess(world, random, pos, state) && random.nextInt(8) == 0) { + performBonemeal(world, random, pos, state); + } + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return SHAPE; + } +} diff --git a/src/main/java/ru/bclib/blocks/BasePlantBlock.java b/src/main/java/ru/bclib/blocks/BasePlantBlock.java new file mode 100644 index 00000000..3f8d3deb --- /dev/null +++ b/src/main/java/ru/bclib/blocks/BasePlantBlock.java @@ -0,0 +1,130 @@ +package ru.bclib.blocks; + +import java.util.List; +import java.util.Random; + +import com.google.common.collect.Lists; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.item.enchantment.Enchantments; +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.BonemealableBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import ru.bclib.client.render.ERenderLayer; +import ru.bclib.interfaces.IRenderTypeable; + +public class BasePlantBlock extends BaseBlockNotFull implements IRenderTypeable, BonemealableBlock { + private static final VoxelShape SHAPE = Block.box(4, 0, 4, 12, 14, 12); + + public BasePlantBlock() { + this(false); + } + + public BasePlantBlock(int light) { + this(false, light); + } + + public BasePlantBlock(boolean replaceable) { + super(FabricBlockSettings.of(replaceable ? Material.REPLACEABLE_PLANT : Material.PLANT) + .breakByTool(FabricToolTags.SHEARS) + .breakByHand(true) + .sound(SoundType.GRASS) + .noCollission()); + } + + public BasePlantBlock(boolean replaceable, int light) { + super(FabricBlockSettings.of(replaceable ? Material.REPLACEABLE_PLANT : Material.PLANT) + .breakByTool(FabricToolTags.SHEARS) + .breakByHand(true) + .luminance(light) + .sound(SoundType.GRASS) + .noCollission()); + } + + public BasePlantBlock(Properties settings) { + super(settings); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + Vec3 vec3d = state.getOffset(view, pos); + return SHAPE.move(vec3d.x, vec3d.y, vec3d.z); + } + + @Override + public BlockBehaviour.OffsetType getOffsetType() { + return BlockBehaviour.OffsetType.XZ; + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + BlockState down = world.getBlockState(pos.below()); + return isTerrain(down); + } + + protected boolean isTerrain(BlockState state) { + return state.is(EndTags.END_GROUND); + } + + @Override + public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) { + if (!canSurvive(state, world, pos)) { + return Blocks.AIR.defaultBlockState(); + } + else { + return state; + } + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && tool.getItem().is(FabricToolTags.SHEARS) || EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) { + return Lists.newArrayList(new ItemStack(this)); + } + else { + return Lists.newArrayList(); + } + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.CUTOUT; + } + + @Override + public boolean isValidBonemealTarget(BlockGetter world, BlockPos pos, BlockState state, boolean isClient) { + return true; + } + + @Override + public boolean isBonemealSuccess(Level world, Random random, BlockPos pos, BlockState state) { + return true; + } + + @Override + public void performBonemeal(ServerLevel world, Random random, BlockPos pos, BlockState state) { + ItemEntity item = new ItemEntity(world, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, new ItemStack(this)); + world.addFreshEntity(item); + } +} diff --git a/src/main/java/ru/bclib/blocks/BasePlantWithAgeBlock.java b/src/main/java/ru/bclib/blocks/BasePlantWithAgeBlock.java new file mode 100644 index 00000000..ad6f6779 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/BasePlantWithAgeBlock.java @@ -0,0 +1,65 @@ +package ru.bclib.blocks; + +import java.util.Random; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.WorldGenLevel; +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.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.material.Material; +import ru.betterend.blocks.BlockProperties; + +public abstract class BasePlantWithAgeBlock extends BasePlantBlock { + public static final IntegerProperty AGE = BlockProperties.AGE; + + public BasePlantWithAgeBlock() { + this(FabricBlockSettings.of(Material.PLANT) + .breakByTool(FabricToolTags.SHEARS) + .breakByHand(true) + .sound(SoundType.GRASS) + .randomTicks() + .noCollission()); + } + + public BasePlantWithAgeBlock(Properties settings) { + super(settings); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(AGE); + } + + public abstract void growAdult(WorldGenLevel world, Random random, BlockPos pos); + + @Override + public void performBonemeal(ServerLevel world, Random random, BlockPos pos, BlockState state) { + int age = state.getValue(AGE); + if (age < 3) { + world.setBlockAndUpdate(pos, state.setValue(AGE, age + 1)); + } + else { + growAdult(world, random, pos); + } + } + + @Override + public boolean isBonemealSuccess(Level world, Random random, BlockPos pos, BlockState state) { + return true; + } + + @Override + public void tick(BlockState state, ServerLevel world, BlockPos pos, Random random) { + super.tick(state, world, pos, random); + if (random.nextInt(8) == 0) { + performBonemeal(world, random, pos, state); + } + } +} diff --git a/src/main/java/ru/bclib/blocks/BaseTerrainBlock.java b/src/main/java/ru/bclib/blocks/BaseTerrainBlock.java new file mode 100644 index 00000000..ae9be8dc --- /dev/null +++ b/src/main/java/ru/bclib/blocks/BaseTerrainBlock.java @@ -0,0 +1,125 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Random; + +import org.jetbrains.annotations.Nullable; + +import com.google.common.collect.Maps; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.level.Level; +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.SnowLayerBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.lighting.LayerLightEngine; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.BlockHitResult; +import ru.bclib.client.models.BasePatterns; +import ru.bclib.client.models.ModelsHelper; +import ru.bclib.client.models.PatternsHelper; +import ru.bclib.client.sound.BlockSounds; + +public class BaseTerrainBlock extends BaseBlock { + private Block pathBlock; + + public BaseTerrainBlock(MaterialColor color) { + super(FabricBlockSettings.copyOf(Blocks.END_STONE).materialColor(color).sound(BlockSounds.TERRAIN_SOUND).randomTicks()); + } + + public void setPathBlock(Block roadBlock) { + this.pathBlock = roadBlock; + } + + @Override + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (pathBlock != null && player.getMainHandItem().getItem().is(FabricToolTags.SHOVELS)) { + world.playSound(player, pos, SoundEvents.SHOVEL_FLATTEN, SoundSource.BLOCKS, 1.0F, 1.0F); + if (!world.isClientSide) { + world.setBlockAndUpdate(pos, pathBlock.defaultBlockState()); + if (!player.isCreative()) { + player.getMainHandItem().hurt(1, world.random, (ServerPlayer) player); + } + } + return InteractionResult.SUCCESS; + } + return InteractionResult.FAIL; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) { + return Collections.singletonList(new ItemStack(this)); + } + return Collections.singletonList(new ItemStack(Blocks.END_STONE)); + } + + @Override + public void randomTick(BlockState state, ServerLevel world, BlockPos pos, Random random) { + if (random.nextInt(16) == 0 && !canStay(state, world, pos)) { + world.setBlockAndUpdate(pos, Blocks.END_STONE.defaultBlockState()); + } + } + + public boolean canStay(BlockState state, LevelReader worldView, BlockPos pos) { + BlockPos blockPos = pos.above(); + BlockState blockState = worldView.getBlockState(blockPos); + if (blockState.is(Blocks.SNOW) && (Integer) blockState.getValue(SnowLayerBlock.LAYERS) == 1) { + return true; + } + else if (blockState.getFluidState().getAmount() == 8) { + return false; + } + else { + int i = LayerLightEngine.getLightBlockInto(worldView, state, pos, blockState, blockPos, Direction.UP, blockState.getLightBlock(worldView, blockPos)); + return i < 5; + } + } + + @Override + public BlockModel getItemModel(ResourceLocation blockId) { + return getBlockModel(blockId, defaultBlockState()); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + String name = resourceLocation.getPath(); + Map textures = Maps.newHashMap(); + textures.put("%top%", "betterend:block/" + name + "_top"); + textures.put("%side%", "betterend:block/" + name + "_side"); + textures.put("%bottom%", "minecraft:block/end_stone"); + Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_TOP_SIDE_BOTTOM, textures); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createRandomTopModel(modelId); + } +} diff --git a/src/main/java/ru/bclib/blocks/BaseUnderwaterWallPlantBlock.java b/src/main/java/ru/bclib/blocks/BaseUnderwaterWallPlantBlock.java new file mode 100644 index 00000000..eb61ee40 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/BaseUnderwaterWallPlantBlock.java @@ -0,0 +1,59 @@ +package ru.bclib.blocks; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.LevelAccessor; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.LiquidBlockContainer; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.level.material.Material; + +public class BaseUnderwaterWallPlantBlock extends BaseWallPlantBlock implements LiquidBlockContainer { + + public BaseUnderwaterWallPlantBlock() { + super(FabricBlockSettings.of(Material.WATER_PLANT) + .breakByTool(FabricToolTags.SHEARS) + .breakByHand(true) + .sound(SoundType.WET_GRASS) + .noCollission()); + } + + public BaseUnderwaterWallPlantBlock(int light) { + super(FabricBlockSettings.of(Material.WATER_PLANT) + .breakByTool(FabricToolTags.SHEARS) + .breakByHand(true) + .luminance(light) + .sound(SoundType.WET_GRASS) + .noCollission()); + } + + public BaseUnderwaterWallPlantBlock(Properties settings) { + super(settings); + } + + @Override + public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid) { + return false; + } + + @Override + public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState) { + return false; + } + + @Override + public FluidState getFluidState(BlockState state) { + return Fluids.WATER.getSource(false); + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + return world.getFluidState(pos).getType() == Fluids.WATER && super.canSurvive(state, world, pos); + } +} diff --git a/src/main/java/ru/bclib/blocks/BaseWallPlantBlock.java b/src/main/java/ru/bclib/blocks/BaseWallPlantBlock.java new file mode 100644 index 00000000..32378fee --- /dev/null +++ b/src/main/java/ru/bclib/blocks/BaseWallPlantBlock.java @@ -0,0 +1,125 @@ +package ru.bclib.blocks; + +import java.util.EnumMap; + +import com.google.common.collect.ImmutableMap; +import com.google.common.collect.Maps; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +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.HorizontalDirectionalBlock; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import ru.bclib.util.BlocksHelper; + +public class BaseWallPlantBlock extends BasePlantBlock { + private static final EnumMap SHAPES = Maps.newEnumMap(ImmutableMap.of( + Direction.NORTH, Block.box(1, 1, 8, 15, 15, 16), + Direction.SOUTH, Block.box(1, 1, 0, 15, 15, 8), + Direction.WEST, Block.box(8, 1, 1, 16, 15, 15), + Direction.EAST, Block.box(0, 1, 1, 8, 15, 15))); + public static final DirectionProperty FACING = HorizontalDirectionalBlock.FACING; + + public BaseWallPlantBlock() { + this(FabricBlockSettings.of(Material.PLANT) + .breakByTool(FabricToolTags.SHEARS) + .breakByHand(true) + .sound(SoundType.GRASS) + .noCollission()); + } + + public BaseWallPlantBlock(int light) { + this(FabricBlockSettings.of(Material.PLANT) + .breakByTool(FabricToolTags.SHEARS) + .breakByHand(true) + .luminance(light) + .sound(SoundType.GRASS) + .noCollission()); + } + + public BaseWallPlantBlock(Properties settings) { + super(settings); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(FACING); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return SHAPES.get(state.getValue(FACING)); + } + + @Override + public BlockBehaviour.OffsetType getOffsetType() { + return BlockBehaviour.OffsetType.NONE; + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + Direction direction = (Direction) state.getValue(FACING); + BlockPos blockPos = pos.relative(direction.getOpposite()); + BlockState blockState = world.getBlockState(blockPos); + return isSupport(world, blockPos, blockState, direction); + } + + public boolean isSupport(LevelReader world, BlockPos pos, BlockState blockState, Direction direction) { + return blockState.getMaterial().isSolid() && blockState.isFaceSturdy(world, pos, direction); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext ctx) { + BlockState blockState = this.defaultBlockState(); + LevelReader worldView = ctx.getLevel(); + BlockPos blockPos = ctx.getClickedPos(); + Direction[] directions = ctx.getNearestLookingDirections(); + for (int i = 0; i < directions.length; ++i) { + Direction direction = directions[i]; + if (direction.getAxis().isHorizontal()) { + Direction direction2 = direction.getOpposite(); + blockState = blockState.setValue(FACING, direction2); + if (blockState.canSurvive(worldView, blockPos)) { + return blockState; + } + } + } + return null; + } + + @Override + public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) { + if (!canSurvive(state, world, pos)) { + return Blocks.AIR.defaultBlockState(); + } + else { + return state; + } + } + + @Override + public BlockState rotate(BlockState state, Rotation rotation) { + return BlocksHelper.rotateHorizontal(state, rotation, FACING); + } + + @Override + public BlockState mirror(BlockState state, Mirror mirror) { + return BlocksHelper.mirrorHorizontal(state, mirror, FACING); + } +} diff --git a/src/main/java/ru/bclib/blocks/DoublePlantBlock.java b/src/main/java/ru/bclib/blocks/DoublePlantBlock.java new file mode 100644 index 00000000..c258c2af --- /dev/null +++ b/src/main/java/ru/bclib/blocks/DoublePlantBlock.java @@ -0,0 +1,153 @@ +package ru.bclib.blocks; + +import java.util.List; +import java.util.Random; + +import com.google.common.collect.Lists; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.item.enchantment.Enchantments; +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.BonemealableBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import ru.betterend.blocks.BlockProperties; +import ru.betterend.client.render.ERenderLayer; +import ru.betterend.interfaces.IRenderTypeable; +import ru.betterend.registry.EndTags; +import ru.betterend.util.BlocksHelper; + +public class DoublePlantBlock extends BlockBaseNotFull implements IRenderTypeable, BonemealableBlock { + private static final VoxelShape SHAPE = Block.box(4, 2, 4, 12, 16, 12); + public static final IntegerProperty ROTATION = BlockProperties.ROTATION; + public static final BooleanProperty TOP = BooleanProperty.create("top"); + + public DoublePlantBlock() { + super(FabricBlockSettings.of(Material.PLANT) + .breakByTool(FabricToolTags.SHEARS) + .breakByHand(true) + .sound(SoundType.WET_GRASS) + .noCollission()); + this.registerDefaultState(this.stateDefinition.any().setValue(TOP, false)); + } + + public DoublePlantBlock(int light) { + super(FabricBlockSettings.of(Material.PLANT) + .breakByTool(FabricToolTags.SHEARS) + .breakByHand(true) + .sound(SoundType.WET_GRASS) + .lightLevel((state) -> state.getValue(TOP) ? light : 0) + .noCollission()); + this.registerDefaultState(this.stateDefinition.any().setValue(TOP, false)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(TOP, ROTATION); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + Vec3 vec3d = state.getOffset(view, pos); + return SHAPE.move(vec3d.x, vec3d.y, vec3d.z); + } + + @Override + public BlockBehaviour.OffsetType getOffsetType() { + return BlockBehaviour.OffsetType.XZ; + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + BlockState down = world.getBlockState(pos.below()); + BlockState up = world.getBlockState(pos.above()); + return state.getValue(TOP) ? down.getBlock() == this : isTerrain(down) && (up.getMaterial().isReplaceable()); + } + + public boolean canStayAt(BlockState state, LevelReader world, BlockPos pos) { + BlockState down = world.getBlockState(pos.below()); + BlockState up = world.getBlockState(pos.above()); + return state.getValue(TOP) ? down.getBlock() == this : isTerrain(down) && (up.getBlock() == this); + } + + protected boolean isTerrain(BlockState state) { + return state.is(EndTags.END_GROUND); + } + + @Override + public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) { + if (!canStayAt(state, world, pos)) { + return Blocks.AIR.defaultBlockState(); + } + else { + return state; + } + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + if (state.getValue(TOP)) { + return Lists.newArrayList(); + } + + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && tool.getItem().is(FabricToolTags.SHEARS) || EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) { + return Lists.newArrayList(new ItemStack(this)); + } + else { + return Lists.newArrayList(); + } + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.CUTOUT; + } + + @Override + public boolean isValidBonemealTarget(BlockGetter world, BlockPos pos, BlockState state, boolean isClient) { + return true; + } + + @Override + public boolean isBonemealSuccess(Level world, Random random, BlockPos pos, BlockState state) { + return true; + } + + @Override + public void performBonemeal(ServerLevel world, Random random, BlockPos pos, BlockState state) { + ItemEntity item = new ItemEntity(world, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, new ItemStack(this)); + world.addFreshEntity(item); + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) { + int rot = world.random.nextInt(4); + BlockState bs = this.defaultBlockState().setValue(ROTATION, rot); + BlocksHelper.setWithoutUpdate(world, pos, bs); + BlocksHelper.setWithoutUpdate(world, pos.above(), bs.setValue(TOP, true)); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndAnvilBlock.java b/src/main/java/ru/bclib/blocks/EndAnvilBlock.java new file mode 100644 index 00000000..b0f68873 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndAnvilBlock.java @@ -0,0 +1,96 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import com.google.common.collect.Maps; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.AnvilBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.blocks.BlockProperties; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; + +public class EndAnvilBlock extends AnvilBlock implements BlockModelProvider { + private static final IntegerProperty DESTRUCTION = BlockProperties.DESTRUCTION; + protected final int level; + + public EndAnvilBlock(MaterialColor color, int level) { + super(FabricBlockSettings.copyOf(Blocks.ANVIL).materialColor(color)); + this.level = level; + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + super.createBlockStateDefinition(builder); + builder.add(getDestructionProperty()); + } + + public IntegerProperty getDestructionProperty() { + return DESTRUCTION; + } + + public int getCraftingLevel() { + return level; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack stack = new ItemStack(this); + int level = state.getValue(getDestructionProperty()); + stack.getOrCreateTag().putInt("level", level); + return Collections.singletonList(stack); + } + + protected String getTop(ResourceLocation blockId, String block) { + if (block.contains("item")) { + return blockId.getPath() + "_top_0"; + } + char last = block.charAt(block.length() - 1); + return blockId.getPath() + "_top_" + last; + } + + @Override + public BlockModel getItemModel(ResourceLocation blockId) { + return getBlockModel(blockId, defaultBlockState()); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + IntegerProperty destructionProperty = getDestructionProperty(); + int destruction = blockState.getValue(destructionProperty); + String name = blockId.getPath(); + Map textures = Maps.newHashMap(); + textures.put("%anvil%", name); + textures.put("%top%", name + "_top_" + destruction); + Optional pattern = Patterns.createJson(Patterns.BLOCK_ANVIL, textures); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + IntegerProperty destructionProperty = getDestructionProperty(); + int destruction = blockState.getValue(destructionProperty); + String modId = stateId.getNamespace(); + String modelId = "block/" + stateId.getPath() + "_top_" + destruction; + ResourceLocation modelLocation = new ResourceLocation(modId, modelId); + registerBlockModel(stateId, modelLocation, blockState, modelCache); + return ModelsHelper.createFacingModel(modelLocation, blockState.getValue(FACING), false, false); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndBarrelBlock.java b/src/main/java/ru/bclib/blocks/EndBarrelBlock.java new file mode 100644 index 00000000..345d13e6 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndBarrelBlock.java @@ -0,0 +1,133 @@ +package ru.bclib.blocks; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Random; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.stats.Stats; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.entity.monster.piglin.PiglinAi; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.BarrelBlock; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.RenderShape; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.phys.BlockHitResult; +import ru.betterend.blocks.entities.EBarrelBlockEntity; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; +import ru.betterend.registry.EndBlockEntities; + +public class EndBarrelBlock extends BarrelBlock implements BlockModelProvider { + public EndBarrelBlock(Block source) { + super(FabricBlockSettings.copyOf(source).noOcclusion()); + } + + @Override + public BlockEntity newBlockEntity(BlockGetter world) { + return EndBlockEntities.BARREL.create(); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + List drop = super.getDrops(state, builder); + drop.add(new ItemStack(this.asItem())); + return drop; + } + + @Override + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, + BlockHitResult hit) { + if (world.isClientSide) { + return InteractionResult.SUCCESS; + } else { + BlockEntity blockEntity = world.getBlockEntity(pos); + if (blockEntity instanceof EBarrelBlockEntity) { + player.openMenu((EBarrelBlockEntity) blockEntity); + player.awardStat(Stats.OPEN_BARREL); + PiglinAi.angerNearbyPiglins(player, true); + } + + return InteractionResult.CONSUME; + } + } + + @Override + public void tick(BlockState state, ServerLevel world, BlockPos pos, Random random) { + BlockEntity blockEntity = world.getBlockEntity(pos); + if (blockEntity instanceof EBarrelBlockEntity) { + ((EBarrelBlockEntity) blockEntity).tick(); + } + } + + @Override + public RenderShape getRenderShape(BlockState state) { + return RenderShape.MODEL; + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, + ItemStack itemStack) { + if (itemStack.hasCustomHoverName()) { + BlockEntity blockEntity = world.getBlockEntity(pos); + if (blockEntity instanceof EBarrelBlockEntity) { + ((EBarrelBlockEntity) blockEntity).setCustomName(itemStack.getHoverName()); + } + } + } + + @Override + public BlockModel getItemModel(ResourceLocation blockId) { + return getBlockModel(blockId, defaultBlockState()); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + String texture = blockId.getPath(); + Optional pattern; + if (blockState.getValue(OPEN)) { + pattern = Patterns.createJson(Patterns.BLOCK_BARREL_OPEN, texture, texture); + } else { + pattern = Patterns.createJson(Patterns.BLOCK_BOTTOM_TOP, texture, texture); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + String open = blockState.getValue(OPEN) ? "_open" : ""; + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + open); + registerBlockModel(stateId, modelId, blockState, modelCache); + Direction facing = blockState.getValue(FACING); + BlockModelRotation rotation = BlockModelRotation.X0_Y0; + switch (facing) { + case NORTH: rotation = BlockModelRotation.X90_Y0; break; + case EAST: rotation = BlockModelRotation.X90_Y90; break; + case SOUTH: rotation = BlockModelRotation.X90_Y180; break; + case WEST: rotation = BlockModelRotation.X90_Y270; break; + case DOWN: + default: rotation = BlockModelRotation.X180_Y0; break; + } + return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), false); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndBlockStripableLogLog.java b/src/main/java/ru/bclib/blocks/EndBlockStripableLogLog.java new file mode 100644 index 00000000..64640813 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndBlockStripableLogLog.java @@ -0,0 +1,41 @@ +package ru.bclib.blocks; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.RotatedPillarBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.phys.BlockHitResult; + +public class EndBlockStripableLogLog extends EndPillarBlock { + private final Block striped; + + public EndBlockStripableLogLog(MaterialColor color, Block striped) { + super(FabricBlockSettings.copyOf(striped).materialColor(color)); + this.striped = striped; + } + + @Override + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (player.getMainHandItem().getItem().is(FabricToolTags.AXES)) { + world.playSound(player, pos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F); + if (!world.isClientSide) { + world.setBlock(pos, striped.defaultBlockState().setValue(RotatedPillarBlock.AXIS, state.getValue(RotatedPillarBlock.AXIS)), 11); + if (player != null && !player.isCreative()) { + player.getMainHandItem().hurt(1, world.random, (ServerPlayer) player); + } + } + return InteractionResult.SUCCESS; + } + return InteractionResult.FAIL; + } +} diff --git a/src/main/java/ru/bclib/blocks/EndBookshelfBlock.java b/src/main/java/ru/bclib/blocks/EndBookshelfBlock.java new file mode 100644 index 00000000..4ae6181d --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndBookshelfBlock.java @@ -0,0 +1,51 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.Items; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; + +public class EndBookshelfBlock extends BlockBase { + public EndBookshelfBlock(Block source) { + super(FabricBlockSettings.copyOf(source)); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && tool.isCorrectToolForDrops(state)) { + int silk = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool); + if (silk > 0) { + return Collections.singletonList(new ItemStack(this)); + } + } + return Collections.singletonList(new ItemStack(Items.BOOK, 3)); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + Optional pattern = Patterns.createJson(Patterns.BLOCK_BOOKSHELF, + getName(blockId), blockId.getPath()); + return ModelsHelper.fromPattern(pattern); + } + + private String getName(ResourceLocation blockId) { + String name = blockId.getPath(); + return name.replace("_bookshelf", ""); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndButtonBlock.java b/src/main/java/ru/bclib/blocks/EndButtonBlock.java new file mode 100644 index 00000000..766df64f --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndButtonBlock.java @@ -0,0 +1,80 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.ButtonBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.AttachFace; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; + +public abstract class EndButtonBlock extends ButtonBlock implements BlockModelProvider { + + private final Block parent; + + protected EndButtonBlock(Block parent, Properties properties, boolean sensitive) { + super(sensitive, properties); + this.parent = parent; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public BlockModel getItemModel(ResourceLocation blockId) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern = Patterns.createJson(Patterns.ITEM_BUTTON, parentId.getPath(), blockId.getPath()); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + ResourceLocation blockId = Registry.BLOCK.getKey(this); + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern = blockState.getValue(POWERED) ? + Patterns.createJson(Patterns.BLOCK_BUTTON_PRESSED, parentId.getPath(), blockId.getPath()) : + Patterns.createJson(Patterns.BLOCK_BUTTON, parentId.getPath(), blockId.getPath()); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + String powered = blockState.getValue(POWERED) ? "_powered" : ""; + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + powered); + registerBlockModel(stateId, modelId, blockState, modelCache); + AttachFace face = blockState.getValue(FACE); + boolean isCeiling = face == AttachFace.CEILING; + int x = 0, y = 0; + switch (face) { + case CEILING: x = 180; break; + case WALL: + default: x = 90; break; + } + switch (blockState.getValue(FACING)) { + case NORTH: if (isCeiling) { y = 180; } break; + case EAST: y = isCeiling ? 270 : 90; break; + case SOUTH: if(!isCeiling) { y = 180; } break; + case WEST: + default: y = isCeiling ? 90 : 270; break; + } + BlockModelRotation rotation = BlockModelRotation.by(x, y); + return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), face == AttachFace.WALL); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndChainBlock.java b/src/main/java/ru/bclib/blocks/EndChainBlock.java new file mode 100644 index 00000000..4767f9d4 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndChainBlock.java @@ -0,0 +1,62 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.ChainBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; +import ru.betterend.client.render.ERenderLayer; +import ru.betterend.interfaces.IRenderTypeable; + +public class EndChainBlock extends ChainBlock implements BlockModelProvider, IRenderTypeable { + public EndChainBlock(MaterialColor color) { + super(FabricBlockSettings.copyOf(Blocks.CHAIN).materialColor(color)); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public BlockModel getItemModel(ResourceLocation blockId) { + return ModelsHelper.createItemModel(blockId.getPath()); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + String name = blockId.getPath(); + Optional pattern = Patterns.createJson(Patterns.BLOCK_CHAIN, name, name); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + Direction.Axis axis = blockState.getValue(AXIS); + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath()); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createRotatedModel(modelId, axis); + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.CUTOUT; + } +} diff --git a/src/main/java/ru/bclib/blocks/EndChestBlock.java b/src/main/java/ru/bclib/blocks/EndChestBlock.java new file mode 100644 index 00000000..a71742d1 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndChestBlock.java @@ -0,0 +1,57 @@ +package ru.bclib.blocks; + +import java.util.List; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.ChestBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; +import ru.betterend.registry.EndBlockEntities; + +public class EndChestBlock extends ChestBlock implements BlockModelProvider { + private final Block parent; + + public EndChestBlock(Block source) { + super(FabricBlockSettings.copyOf(source).noOcclusion(), () -> EndBlockEntities.CHEST); + this.parent = source; + } + + @Override + public BlockEntity newBlockEntity(BlockGetter world) + { + return EndBlockEntities.CHEST.create(); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) + { + List drop = super.getDrops(state, builder); + drop.add(new ItemStack(this.asItem())); + return drop; + } + + @Override + public BlockModel getItemModel(ResourceLocation blockId) { + Optional pattern = Patterns.createJson(Patterns.ITEM_CHEST, blockId.getPath()); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + return ModelsHelper.createBlockEmpty(parentId); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndComposterBlock.java b/src/main/java/ru/bclib/blocks/EndComposterBlock.java new file mode 100644 index 00000000..901a26ee --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndComposterBlock.java @@ -0,0 +1,66 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.ComposterBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.ModelsHelper.MultiPartBuilder; +import ru.betterend.client.models.Patterns; + +public class EndComposterBlock extends ComposterBlock implements BlockModelProvider { + public EndComposterBlock(Block source) { + super(FabricBlockSettings.copyOf(source)); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this.asItem())); + } + + @Override + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + Optional pattern = Patterns.createJson(Patterns.BLOCK_COMPOSTER, blockId.getPath()); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); + registerBlockModel(stateId, modelId, blockState, modelCache); + + MultiPartBuilder builder = MultiPartBuilder.create(stateDefinition); + LEVEL.getPossibleValues().forEach(level -> { + if (level > 0) { + ResourceLocation contentId; + if (level > 7) { + contentId = new ResourceLocation("block/composter_contents_ready"); + } else { + contentId = new ResourceLocation("block/composter_contents" + level); + } + builder.part(contentId).setCondition(state -> state.getValue(LEVEL).equals(level)).add(); + } + }); + builder.part(modelId).add(); + + return builder.build(); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndCraftingTableBlock.java b/src/main/java/ru/bclib/blocks/EndCraftingTableBlock.java new file mode 100644 index 00000000..8c3a40b4 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndCraftingTableBlock.java @@ -0,0 +1,54 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.CraftingTableBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; + +public class EndCraftingTableBlock extends CraftingTableBlock implements BlockModelProvider { + public EndCraftingTableBlock(Block source) { + super(FabricBlockSettings.copyOf(source)); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this.asItem())); + } + + @Override + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + String blockName = blockId.getPath(); + Optional pattern = Patterns.createJson(Patterns.BLOCK_SIDED, new HashMap() { + private static final long serialVersionUID = 1L; + { + put("%particle%", blockName + "_front"); + put("%down%", blockName + "_bottom"); + put("%up%", blockName + "_top"); + put("%north%", blockName + "_front"); + put("%south%", blockName + "_side"); + put("%west%", blockName + "_front"); + put("%east%", blockName + "_side"); + } + }); + return ModelsHelper.fromPattern(pattern); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndDoorBlock.java b/src/main/java/ru/bclib/blocks/EndDoorBlock.java new file mode 100644 index 00000000..f11e2e27 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndDoorBlock.java @@ -0,0 +1,159 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.StringRepresentable; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.DoorBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.DoorHingeSide; +import net.minecraft.world.level.block.state.properties.DoubleBlockHalf; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; +import ru.betterend.client.render.ERenderLayer; +import ru.betterend.interfaces.IRenderTypeable; + +public class EndDoorBlock extends DoorBlock implements IRenderTypeable, BlockModelProvider { + public EndDoorBlock(Block source) { + super(FabricBlockSettings.copyOf(source).strength(3F, 3F).noOcclusion()); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + if (state.getValue(HALF) == DoubleBlockHalf.LOWER) + return Collections.singletonList(new ItemStack(this.asItem())); + else + return Collections.emptyList(); + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.CUTOUT; + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + String blockName = resourceLocation.getPath(); + DoorType doorType = getDoorType(blockState); + Optional pattern = Patterns.createJson(Patterns.BLOCK_DOOR_BOTTOM, blockName, blockName); + switch (doorType) { + case TOP_HINGE: + pattern = Patterns.createJson(Patterns.BLOCK_DOOR_TOP_HINGE, blockName, blockName); + break; + case BOTTOM_HINGE: + pattern = Patterns.createJson(Patterns.BLOCK_DOOR_BOTTOM_HINGE, blockName, blockName); + break; + case TOP: + default: + pattern = Patterns.createJson(Patterns.BLOCK_DOOR_TOP, blockName, blockName); + break; + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + Direction facing = blockState.getValue(FACING); + DoorType doorType = getDoorType(blockState); + boolean open = blockState.getValue(OPEN); + boolean hinge = doorType.isHinge(); + BlockModelRotation rotation = BlockModelRotation.X0_Y0; + switch (facing) { + case EAST: + if (hinge && open) { + rotation = BlockModelRotation.X0_Y90; + } else if (open) { + rotation = BlockModelRotation.X0_Y270; + } + break; + case SOUTH: + if (!hinge && !open || hinge && !open) { + rotation = BlockModelRotation.X0_Y90; + } else if (hinge) { + rotation = BlockModelRotation.X0_Y180; + } + break; + case WEST: + if (!hinge && !open || hinge && !open) { + rotation = BlockModelRotation.X0_Y180; + } else if (hinge) { + rotation = BlockModelRotation.X0_Y270; + } else { + rotation = BlockModelRotation.X0_Y90; + } + break; + case NORTH: + default: + if (!hinge && !open || hinge && !open) { + rotation = BlockModelRotation.X0_Y270; + } else if (!hinge) { + rotation = BlockModelRotation.X0_Y180; + } + break; + } + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + "_" + doorType); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), false); + } + + protected DoorType getDoorType(BlockState blockState) { + boolean isHinge = isHinge(blockState.getValue(HINGE), blockState.getValue(OPEN)); + switch (blockState.getValue(HALF)) { + case UPPER: { + return isHinge ? DoorType.TOP_HINGE : DoorType.TOP; + } + case LOWER: { + return isHinge ? DoorType.BOTTOM_HINGE : DoorType.BOTTOM; + } + } + return DoorType.BOTTOM; + } + + private boolean isHinge(DoorHingeSide hingeSide, boolean open) { + boolean isHinge = hingeSide == DoorHingeSide.RIGHT; + return isHinge && !open || !isHinge && open; + } + + protected enum DoorType implements StringRepresentable { + BOTTOM_HINGE("bottom_hinge"), + TOP_HINGE("top_hinge"), + BOTTOM("bottom"), + TOP("top"); + + private final String name; + + DoorType(String name) { + this.name = name; + } + + public boolean isHinge() { + return this == BOTTOM_HINGE || + this == TOP_HINGE; + } + + @Override + public String toString() { + return getSerializedName(); + } + + @Override + public String getSerializedName() { + return name; + } + } +} diff --git a/src/main/java/ru/bclib/blocks/EndFenceBlock.java b/src/main/java/ru/bclib/blocks/EndFenceBlock.java new file mode 100644 index 00000000..3fbc5874 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndFenceBlock.java @@ -0,0 +1,81 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.FenceBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.ModelsHelper.MultiPartBuilder; +import ru.betterend.client.models.Patterns; + +public class EndFenceBlock extends FenceBlock implements BlockModelProvider { + private final Block parent; + + public EndFenceBlock(Block source) { + super(FabricBlockSettings.copyOf(source).noOcclusion()); + this.parent = source; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public BlockModel getItemModel(ResourceLocation blockId) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern = Patterns.createJson(Patterns.ITEM_FENCE, parentId.getPath(), blockId.getPath()); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + String path = blockId.getPath(); + Optional pattern = Optional.empty(); + if (path.endsWith("_post")) { + pattern = Patterns.createJson(Patterns.BLOCK_FENCE_POST, parentId.getPath(), blockId.getPath()); + } + if (path.endsWith("_side")) { + pattern = Patterns.createJson(Patterns.BLOCK_FENCE_SIDE, parentId.getPath(), blockId.getPath()); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + ResourceLocation postId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + "_post"); + ResourceLocation sideId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + "_side"); + registerBlockModel(postId, postId, blockState, modelCache); + registerBlockModel(sideId, sideId, blockState, modelCache); + + MultiPartBuilder builder = MultiPartBuilder.create(stateDefinition); + builder.part(sideId).setCondition(state -> state.getValue(NORTH)).setUVLock(true).add(); + builder.part(sideId).setCondition(state -> state.getValue(EAST)) + .setTransformation(BlockModelRotation.X0_Y90.getRotation()).setUVLock(true).add(); + builder.part(sideId).setCondition(state -> state.getValue(SOUTH)) + .setTransformation(BlockModelRotation.X0_Y180.getRotation()).setUVLock(true).add(); + builder.part(sideId).setCondition(state -> state.getValue(WEST)) + .setTransformation(BlockModelRotation.X0_Y270.getRotation()).setUVLock(true).add(); + builder.part(postId).add(); + + return builder.build(); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndFurnaceBlock.java b/src/main/java/ru/bclib/blocks/EndFurnaceBlock.java new file mode 100644 index 00000000..3775a44d --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndFurnaceBlock.java @@ -0,0 +1,104 @@ +package ru.bclib.blocks; + +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.stats.Stats; +import net.minecraft.world.MenuProvider; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +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.FurnaceBlock; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import ru.betterend.blocks.entities.EFurnaceBlockEntity; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; +import ru.betterend.client.render.ERenderLayer; +import ru.betterend.interfaces.IRenderTypeable; + +public class EndFurnaceBlock extends FurnaceBlock implements BlockModelProvider, IRenderTypeable { + public EndFurnaceBlock(Block source) { + super(FabricBlockSettings.copyOf(source).luminance(state -> state.getValue(LIT) ? 13 : 0)); + } + + @Override + public BlockEntity newBlockEntity(BlockGetter world) { + return new EFurnaceBlockEntity(); + } + + @Override + protected void openContainer(Level world, BlockPos pos, Player player) { + BlockEntity blockEntity = world.getBlockEntity(pos); + if (blockEntity instanceof EFurnaceBlockEntity) { + player.openMenu((MenuProvider) blockEntity); + player.awardStat(Stats.INTERACT_WITH_FURNACE); + } + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + String blockName = blockId.getPath(); + Map textures = Maps.newHashMap(); + textures.put("%top%", blockName + "_top"); + textures.put("%side%", blockName + "_side"); + Optional pattern; + if (blockState.getValue(LIT)) { + textures.put("%front%", blockName + "_front_on"); + textures.put("%glow%", blockName + "_glow"); + pattern = Patterns.createJson(Patterns.BLOCK_FURNACE_LIT, textures); + } else { + textures.put("%front%", blockName + "_front"); + pattern = Patterns.createJson(Patterns.BLOCK_FURNACE, textures); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + String lit = blockState.getValue(LIT) ? "_lit" : ""; + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + lit); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createFacingModel(modelId, blockState.getValue(FACING), false, true); + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.CUTOUT; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + List drop = Lists.newArrayList(new ItemStack(this)); + BlockEntity blockEntity = builder.getOptionalParameter(LootContextParams.BLOCK_ENTITY); + if (blockEntity instanceof EFurnaceBlockEntity) { + EFurnaceBlockEntity entity = (EFurnaceBlockEntity) blockEntity; + for (int i = 0; i < entity.getContainerSize(); i++) { + drop.add(entity.getItem(i)); + } + } + return drop; + } +} diff --git a/src/main/java/ru/bclib/blocks/EndGateBlock.java b/src/main/java/ru/bclib/blocks/EndGateBlock.java new file mode 100644 index 00000000..e262a14d --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndGateBlock.java @@ -0,0 +1,68 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.FenceGateBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; + +public class EndGateBlock extends FenceGateBlock implements BlockModelProvider { + private final Block parent; + + public EndGateBlock(Block source) { + super(FabricBlockSettings.copyOf(source).noOcclusion()); + this.parent = source; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + boolean inWall = blockState.getValue(IN_WALL); + boolean isOpen = blockState.getValue(OPEN); + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern; + if (inWall) { + pattern = isOpen ? Patterns.createJson(Patterns.BLOCK_GATE_OPEN_WALL, parentId.getPath(), blockId.getPath()) : + Patterns.createJson(Patterns.BLOCK_GATE_CLOSED_WALL, parentId.getPath(), blockId.getPath()); + } else { + pattern = isOpen ? Patterns.createJson(Patterns.BLOCK_GATE_OPEN, parentId.getPath(), blockId.getPath()) : + Patterns.createJson(Patterns.BLOCK_GATE_CLOSED, parentId.getPath(), blockId.getPath()); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + boolean inWall = blockState.getValue(IN_WALL); + boolean isOpen = blockState.getValue(OPEN); + String state = "" + (inWall ? "_wall" : "") + (isOpen ? "_open" : "_closed"); + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + state); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createFacingModel(modelId, blockState.getValue(FACING), true, false); + } +} \ No newline at end of file diff --git a/src/main/java/ru/bclib/blocks/EndLadderBlock.java b/src/main/java/ru/bclib/blocks/EndLadderBlock.java new file mode 100644 index 00000000..a8c1c567 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndLadderBlock.java @@ -0,0 +1,161 @@ +package ru.bclib.blocks; + +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.BlockGetter; +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.HorizontalDirectionalBlock; +import net.minecraft.world.level.block.Mirror; +import net.minecraft.world.level.block.Rotation; +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.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.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import ru.bclib.client.render.ERenderLayer; +import ru.bclib.client.models.BasePatterns; +import ru.bclib.client.models.BlockModelProvider; +import ru.bclib.client.models.ModelsHelper; +import ru.bclib.client.models.PatternsHelper; +import ru.bclib.interfaces.IRenderTypeable; +import ru.bclib.util.BlocksHelper; + +public class EndLadderBlock extends BaseBlockNotFull implements IRenderTypeable, BlockModelProvider { + public static final DirectionProperty FACING = HorizontalDirectionalBlock.FACING; + public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; + protected static final VoxelShape EAST_SHAPE = Block.box(0.0D, 0.0D, 0.0D, 3.0D, 16.0D, 16.0D); + protected static final VoxelShape WEST_SHAPE = Block.box(13.0D, 0.0D, 0.0D, 16.0D, 16.0D, 16.0D); + protected static final VoxelShape SOUTH_SHAPE = Block.box(0.0D, 0.0D, 0.0D, 16.0D, 16.0D, 3.0D); + protected static final VoxelShape NORTH_SHAPE = Block.box(0.0D, 0.0D, 13.0D, 16.0D, 16.0D, 16.0D); + + public EndLadderBlock(Block block) { + super(FabricBlockSettings.copyOf(block).noOcclusion()); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(FACING); + stateManager.add(WATERLOGGED); + } + + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + switch (state.getValue(FACING)) { + case SOUTH: + return SOUTH_SHAPE; + case WEST: + return WEST_SHAPE; + case EAST: + return EAST_SHAPE; + default: + return NORTH_SHAPE; + } + } + + private boolean canPlaceOn(BlockGetter world, BlockPos pos, Direction side) { + BlockState blockState = world.getBlockState(pos); + return !blockState.isSignalSource() && blockState.isFaceSturdy(world, pos, side); + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + Direction direction = (Direction) state.getValue(FACING); + return this.canPlaceOn(world, pos.relative(direction.getOpposite()), direction); + } + + @Override + public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, + LevelAccessor world, BlockPos pos, BlockPos neighborPos) { + if (facing.getOpposite() == state.getValue(FACING) && !state.canSurvive(world, pos)) { + return Blocks.AIR.defaultBlockState(); + } else { + if ((Boolean) state.getValue(WATERLOGGED)) { + world.getLiquidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world)); + } + + return super.updateShape(state, facing, neighborState, world, pos, neighborPos); + } + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext ctx) { + BlockState blockState; + if (!ctx.replacingClickedOnBlock()) { + blockState = ctx.getLevel().getBlockState(ctx.getClickedPos().relative(ctx.getClickedFace().getOpposite())); + if (blockState.getBlock() == this && blockState.getValue(FACING) == ctx.getClickedFace()) { + return null; + } + } + + blockState = defaultBlockState(); + LevelReader worldView = ctx.getLevel(); + BlockPos blockPos = ctx.getClickedPos(); + FluidState fluidState = ctx.getLevel().getFluidState(ctx.getClickedPos()); + Direction[] directions = ctx.getNearestLookingDirections(); + + for (Direction direction : directions) { + if (direction.getAxis().isHorizontal()) { + blockState = blockState.setValue(FACING, direction.getOpposite()); + if (blockState.canSurvive(worldView, blockPos)) { + return blockState.setValue(WATERLOGGED, fluidState.getType() == Fluids.WATER); + } + } + } + + return null; + } + + @Override + public BlockState rotate(BlockState state, Rotation rotation) { + return BlocksHelper.rotateHorizontal(state, rotation, FACING); + } + + @Override + public BlockState mirror(BlockState state, Mirror mirror) { + return BlocksHelper.mirrorHorizontal(state, mirror, FACING); + } + + @Override + public FluidState getFluidState(BlockState state) { + return (Boolean) state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state); + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.CUTOUT; + } + + @Override + public BlockModel getItemModel(ResourceLocation blockId) { + return ModelsHelper.createBlockItem(blockId); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + Optional pattern = PatternsHelper.createJson(BasePatterns.BLOCK_LADDER, blockId); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createFacingModel(modelId, blockState.getValue(FACING), false, true); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndLeavesBlock.java b/src/main/java/ru/bclib/blocks/EndLeavesBlock.java new file mode 100644 index 00000000..364f4311 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndLeavesBlock.java @@ -0,0 +1,79 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; + +import com.google.common.collect.Lists; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.LeavesBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import ru.bclib.util.MHelper; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.render.ERenderLayer; +import ru.betterend.interfaces.IRenderTypeable; + +public class EndLeavesBlock extends LeavesBlock implements BlockModelProvider, IRenderTypeable { + private final Block sapling; + + public EndLeavesBlock(Block sapling, MaterialColor color) { + super(FabricBlockSettings.copyOf(Blocks.OAK_LEAVES) + .materialColor(color) + .breakByTool(FabricToolTags.HOES) + .breakByTool(FabricToolTags.SHEARS) + .breakByHand(true) + .isValidSpawn((state, world, pos, type) -> false) + .isSuffocating((state, world, pos) -> false) + .isViewBlocking((state, world, pos) -> false)); + this.sapling = sapling; + } + + public EndLeavesBlock(Block sapling, MaterialColor color, int light) { + super(FabricBlockSettings.copyOf(Blocks.OAK_LEAVES) + .materialColor(color) + .luminance(light) + .breakByTool(FabricToolTags.HOES) + .breakByTool(FabricToolTags.SHEARS) + .isValidSpawn((state, world, pos, type) -> false) + .isSuffocating((state, world, pos) -> false) + .isViewBlocking((state, world, pos) -> false)); + this.sapling = sapling; + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.CUTOUT; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null) { + if (tool.getItem().is(FabricToolTags.SHEARS) || EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) { + return Collections.singletonList(new ItemStack(this)); + } + int fortune = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_FORTUNE, tool); + if (MHelper.RANDOM.nextInt(16) <= fortune) { + return Lists.newArrayList(new ItemStack(sapling)); + } + return Lists.newArrayList(); + } + return MHelper.RANDOM.nextInt(16) == 0 ? Lists.newArrayList(new ItemStack(sapling)) : Lists.newArrayList(); + } + + @Override + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndMetalPaneBlock.java b/src/main/java/ru/bclib/blocks/EndMetalPaneBlock.java new file mode 100644 index 00000000..c995180a --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndMetalPaneBlock.java @@ -0,0 +1,108 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Direction; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.IronBarsBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; +import ru.betterend.client.render.ERenderLayer; +import ru.betterend.interfaces.IRenderTypeable; + +public class EndMetalPaneBlock extends IronBarsBlock implements BlockModelProvider, IRenderTypeable { + public EndMetalPaneBlock(Block source) { + super(FabricBlockSettings.copyOf(source).strength(5.0F, 6.0F).noOcclusion()); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + public Optional getModelString(String block) { + ResourceLocation blockId = Registry.BLOCK.getKey(this); + if (block.contains("item")) { + return Patterns.createJson(Patterns.ITEM_BLOCK, blockId.getPath()); + } + if (block.contains("post")) { + return Patterns.createJson(Patterns.BLOCK_BARS_POST, blockId.getPath(), blockId.getPath()); + } + else { + return Patterns.createJson(Patterns.BLOCK_BARS_SIDE, blockId.getPath(), blockId.getPath()); + } + } + + @Override + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return ModelsHelper.createBlockItem(resourceLocation); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + ResourceLocation thisId = Registry.BLOCK.getKey(this); + String path = blockId.getPath(); + Optional pattern = Optional.empty(); + if (path.endsWith("_post")) { + pattern = Patterns.createJson(Patterns.BLOCK_BARS_POST, thisId.getPath(), thisId.getPath()); + } + if (path.endsWith("_side")) { + pattern = Patterns.createJson(Patterns.BLOCK_BARS_SIDE, thisId.getPath(), thisId.getPath()); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + ResourceLocation postId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + "_post"); + ResourceLocation sideId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + "_side"); + registerBlockModel(postId, postId, blockState, modelCache); + registerBlockModel(sideId, sideId, blockState, modelCache); + + ModelsHelper.MultiPartBuilder builder = ModelsHelper.MultiPartBuilder.create(stateDefinition); + builder.part(postId).setCondition(state -> + !state.getValue(NORTH) && !state.getValue(EAST) && + !state.getValue(SOUTH) && !state.getValue(WEST)).add(); + builder.part(sideId).setCondition(state -> state.getValue(NORTH)).setUVLock(true).add(); + builder.part(sideId).setCondition(state -> state.getValue(EAST)) + .setTransformation(BlockModelRotation.X0_Y90.getRotation()).setUVLock(true).add(); + builder.part(sideId).setCondition(state -> state.getValue(SOUTH)) + .setTransformation(BlockModelRotation.X0_Y180.getRotation()).setUVLock(true).add(); + builder.part(sideId).setCondition(state -> state.getValue(WEST)) + .setTransformation(BlockModelRotation.X0_Y270.getRotation()).setUVLock(true).add(); + + return builder.build(); + } + + @Environment(EnvType.CLIENT) + public boolean skipRendering(BlockState state, BlockState stateFrom, Direction direction) { + if (direction.getAxis().isVertical() && stateFrom.getBlock().is(this) && !stateFrom.equals(state)) { + return false; + } + return super.skipRendering(state, stateFrom, direction); + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.CUTOUT; + } +} diff --git a/src/main/java/ru/bclib/blocks/EndOreBlock.java b/src/main/java/ru/bclib/blocks/EndOreBlock.java new file mode 100644 index 00000000..ef8b7a8e --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndOreBlock.java @@ -0,0 +1,76 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Random; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.level.block.OreBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import ru.bclib.util.MHelper; +import ru.betterend.client.models.BlockModelProvider; + +public class EndOreBlock extends OreBlock implements BlockModelProvider { + private final Item dropItem; + private final int minCount; + private final int maxCount; + private final int experience; + + public EndOreBlock(Item drop, int minCount, int maxCount, int experience) { + super(FabricBlockSettings.of(Material.STONE, MaterialColor.SAND) + .hardness(3F) + .resistance(9F) + .requiresCorrectToolForDrops() + .sound(SoundType.STONE)); + this.dropItem = drop; + this.minCount = minCount; + this.maxCount = maxCount; + this.experience = experience; + } + + @Override + protected int xpOnDrop(Random random) { + return this.experience > 0 ? random.nextInt(experience) + 1 : 0; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && tool.isCorrectToolForDrops(state)) { + if (EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) { + return Collections.singletonList(new ItemStack(this)); + } + int count = 0; + int enchantment = EnchantmentHelper.getItemEnchantmentLevel(Enchantments.BLOCK_FORTUNE, tool); + if (enchantment > 0) { + int min = Mth.clamp(minCount + enchantment, minCount, maxCount); + int max = maxCount + (enchantment / Enchantments.BLOCK_FORTUNE.getMaxLevel()); + if (min == max) { + return Collections.singletonList(new ItemStack(dropItem, max)); + } + count = MHelper.randRange(min, max, MHelper.RANDOM); + } else { + count = MHelper.randRange(minCount, maxCount, MHelper.RANDOM); + } + return Collections.singletonList(new ItemStack(dropItem, count)); + } + return Collections.emptyList(); + } + + @Override + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndPathBlock.java b/src/main/java/ru/bclib/blocks/EndPathBlock.java new file mode 100644 index 00000000..4878d66a --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndPathBlock.java @@ -0,0 +1,82 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import com.google.common.collect.Maps; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.item.enchantment.Enchantments; +import net.minecraft.world.level.BlockGetter; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; + +public class EndPathBlock extends BlockBaseNotFull { + private static final VoxelShape SHAPE = Block.box(0, 0, 0, 16, 15, 16); + + public EndPathBlock(Block source) { + super(FabricBlockSettings.copyOf(source).isValidSpawn((state, world, pos, type) -> { return false; })); + if (source instanceof BaseTerrainBlock) { + BaseTerrainBlock terrain = (BaseTerrainBlock) source; + terrain.setPathBlock(this); + } + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) { + return Collections.singletonList(new ItemStack(this)); + } + return Collections.singletonList(new ItemStack(Blocks.END_STONE)); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return SHAPE; + } + + @Override + public VoxelShape getCollisionShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return SHAPE; + } + + @Override + public BlockModel getItemModel(ResourceLocation blockId) { + return getBlockModel(blockId, defaultBlockState()); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + String name = resourceLocation.getPath(); + Map textures = Maps.newHashMap(); + textures.put("%top%", name + "_top"); + textures.put("%side%", name.replace("_path", "") + "_side"); + Optional pattern = Patterns.createJson(Patterns.BLOCK_PATH, textures); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createRandomTopModel(modelId); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndPillarBlock.java b/src/main/java/ru/bclib/blocks/EndPillarBlock.java new file mode 100644 index 00000000..deae0c31 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndPillarBlock.java @@ -0,0 +1,59 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.RotatedPillarBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; + +public class EndPillarBlock extends RotatedPillarBlock implements BlockModelProvider { + public EndPillarBlock(Properties settings) { + super(settings); + } + + public EndPillarBlock(Block block) { + super(FabricBlockSettings.copyOf(block)); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public BlockModel getItemModel(ResourceLocation blockId) { + return getBlockModel(blockId, defaultBlockState()); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + Optional pattern = createBlockPattern(blockId); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath()); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createRotatedModel(modelId, blockState.getValue(AXIS)); + } + + protected Optional createBlockPattern(ResourceLocation blockId) { + return Patterns.createBlockPillar(blockId.getPath()); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndPlateBlock.java b/src/main/java/ru/bclib/blocks/EndPlateBlock.java new file mode 100644 index 00000000..8c01e9f1 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndPlateBlock.java @@ -0,0 +1,62 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.PressurePlateBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; + +public class EndPlateBlock extends PressurePlateBlock implements BlockModelProvider { + private final Block parent; + + public EndPlateBlock(Sensitivity rule, Block source) { + super(rule, FabricBlockSettings.copyOf(source).noCollission().noOcclusion().strength(0.5F)); + this.parent = source; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern; + if (blockState.getValue(POWERED)) { + pattern = Patterns.createJson(Patterns.BLOCK_PLATE_DOWN, parentId.getPath(), resourceLocation.getPath()); + } else { + pattern = Patterns.createJson(Patterns.BLOCK_PLATE_UP, parentId.getPath(), resourceLocation.getPath()); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + String state = blockState.getValue(POWERED) ? "_down" : "_up"; + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + state); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createBlockSimple(modelId); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndSignBlock.java b/src/main/java/ru/bclib/blocks/EndSignBlock.java new file mode 100644 index 00000000..9125c676 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndSignBlock.java @@ -0,0 +1,197 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.Registry; +import net.minecraft.network.protocol.game.ClientboundOpenSignEditorPacket; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.LivingEntity; +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.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.Mirror; +import net.minecraft.world.level.block.Rotation; +import net.minecraft.world.level.block.SignBlock; +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.BooleanProperty; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.block.state.properties.WoodType; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import ru.betterend.blocks.entities.ESignBlockEntity; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.interfaces.ISpetialItem; +import ru.betterend.util.BlocksHelper; + +public class EndSignBlock extends SignBlock implements BlockModelProvider, ISpetialItem { + public static final IntegerProperty ROTATION = BlockStateProperties.ROTATION_16; + public static final BooleanProperty FLOOR = BooleanProperty.create("floor"); + private static final VoxelShape[] WALL_SHAPES = new VoxelShape[] { + Block.box(0.0D, 4.5D, 14.0D, 16.0D, 12.5D, 16.0D), + Block.box(0.0D, 4.5D, 0.0D, 2.0D, 12.5D, 16.0D), + Block.box(0.0D, 4.5D, 0.0D, 16.0D, 12.5D, 2.0D), + Block.box(14.0D, 4.5D, 0.0D, 16.0D, 12.5D, 16.0D) + }; + + private final Block parent; + + public EndSignBlock(Block source) { + super(FabricBlockSettings.copyOf(source).strength(1.0F, 1.0F).noCollission().noOcclusion(), WoodType.OAK); + this.registerDefaultState(this.stateDefinition.any().setValue(ROTATION, 0).setValue(FLOOR, false).setValue(WATERLOGGED, false)); + this.parent = source; + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder builder) { + builder.add(ROTATION, FLOOR, WATERLOGGED); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return state.getValue(FLOOR) ? SHAPE : WALL_SHAPES[state.getValue(ROTATION) >> 2]; + } + + @Override + public BlockEntity newBlockEntity(BlockGetter world) { + return new ESignBlockEntity(); + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) { + if (placer instanceof Player) { + ESignBlockEntity sign = (ESignBlockEntity) world.getBlockEntity(pos); + if (sign != null) { + if (!world.isClientSide) { + sign.setAllowedPlayerEditor((Player) placer); + ((ServerPlayer) placer).connection.send(new ClientboundOpenSignEditorPacket(pos)); + } else { + sign.setEditable(true); + } + } + } + } + + @Override + public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) { + if ((Boolean) state.getValue(WATERLOGGED)) { + world.getLiquidTicks().scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(world)); + } + if (!canSurvive(state, world, pos)) { + return state.getValue(WATERLOGGED) ? state.getFluidState().createLegacyBlock() : Blocks.AIR.defaultBlockState(); + } + return super.updateShape(state, facing, neighborState, world, pos, neighborPos); + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + if (!state.getValue(FLOOR)) { + int index = (((state.getValue(ROTATION) >> 2) + 2)) & 3; + return world.getBlockState(pos.relative(BlocksHelper.HORIZONTAL[index])).getMaterial().isSolid(); + } + else { + return world.getBlockState(pos.below()).getMaterial().isSolid(); + } + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext ctx) { + if (ctx.getClickedFace() == Direction.UP) { + FluidState fluidState = ctx.getLevel().getFluidState(ctx.getClickedPos()); + return this.defaultBlockState().setValue(FLOOR, true) + .setValue(ROTATION, Mth.floor((180.0 + ctx.getRotation() * 16.0 / 360.0) + 0.5 - 12) & 15) + .setValue(WATERLOGGED, fluidState.getType() == Fluids.WATER); + } + else if (ctx.getClickedFace() != Direction.DOWN) { + BlockState blockState = this.defaultBlockState(); + FluidState fluidState = ctx.getLevel().getFluidState(ctx.getClickedPos()); + LevelReader worldView = ctx.getLevel(); + BlockPos blockPos = ctx.getClickedPos(); + Direction[] directions = ctx.getNearestLookingDirections(); + + for (Direction direction : directions) { + if (direction.getAxis().isHorizontal()) { + Direction dir = direction.getOpposite(); + int rot = Mth.floor((180.0 + dir.toYRot() * 16.0 / 360.0) + 0.5 + 4) & 15; + blockState = blockState.setValue(ROTATION, rot); + if (blockState.canSurvive(worldView, blockPos)) { + return blockState.setValue(FLOOR, false).setValue(WATERLOGGED, fluidState.getType() == Fluids.WATER); + } + } + } + } + + return null; + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + return ModelsHelper.createBlockEmpty(parentId); + } + + @Override + public BlockState rotate(BlockState state, Rotation rotation) { + return (BlockState) state.setValue(ROTATION, rotation.rotate((Integer) state.getValue(ROTATION), 16)); + } + + @Override + public BlockState mirror(BlockState state, Mirror mirror) { + return (BlockState) state.setValue(ROTATION, mirror.mirror((Integer) state.getValue(ROTATION), 16)); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public Fluid takeLiquid(LevelAccessor world, BlockPos pos, BlockState state) { + // TODO Auto-generated method stub + return null; + } + + @Override + public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid) { + // TODO Auto-generated method stub + return false; + } + + @Override + public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState) { + // TODO Auto-generated method stub + return false; + } + + @Override + public int getStackSize() { + return 16; + } + + @Override + public boolean canPlaceOnWater() { + return false; + } +} \ No newline at end of file diff --git a/src/main/java/ru/bclib/blocks/EndSlabBlock.java b/src/main/java/ru/bclib/blocks/EndSlabBlock.java new file mode 100644 index 00000000..84de2baf --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndSlabBlock.java @@ -0,0 +1,62 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.SlabBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.SlabType; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; + +public class EndSlabBlock extends SlabBlock implements BlockModelProvider { + private final Block parent; + + public EndSlabBlock(Block source) { + super(FabricBlockSettings.copyOf(source)); + this.parent = source; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern = Patterns.createJson(Patterns.BLOCK_SLAB, parentId.getPath(), blockId.getPath()); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + SlabType type = blockState.getValue(TYPE); + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + "_" + type); + registerBlockModel(stateId, modelId, blockState, modelCache); + if (type == SlabType.TOP) { + return ModelsHelper.createMultiVariant(modelId, BlockModelRotation.X180_Y0.getRotation(), true); + } + return ModelsHelper.createBlockSimple(modelId); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndStairsBlock.java b/src/main/java/ru/bclib/blocks/EndStairsBlock.java new file mode 100644 index 00000000..6e77e478 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndStairsBlock.java @@ -0,0 +1,110 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.StairBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.Half; +import net.minecraft.world.level.block.state.properties.StairsShape; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; + +public class EndStairsBlock extends StairBlock implements BlockModelProvider { + + private final Block parent; + + public EndStairsBlock(Block source) { + super(source.defaultBlockState(), FabricBlockSettings.copyOf(source)); + this.parent = source; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern = Optional.empty(); + switch (blockState.getValue(SHAPE)) { + case STRAIGHT: + pattern = Patterns.createJson(Patterns.BLOCK_STAIR, parentId.getPath(), blockId.getPath()); + break; + case INNER_LEFT: + case INNER_RIGHT: + pattern = Patterns.createJson(Patterns.BLOCK_STAIR_INNER, parentId.getPath(), blockId.getPath()); + break; + case OUTER_LEFT: + case OUTER_RIGHT: + pattern = Patterns.createJson(Patterns.BLOCK_STAIR_OUTER, parentId.getPath(), blockId.getPath()); + break; + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + String state = ""; + StairsShape shape = blockState.getValue(SHAPE); + switch (shape) { + case INNER_LEFT: + case INNER_RIGHT: + state = "_inner"; break; + case OUTER_LEFT: + case OUTER_RIGHT: + default: + state = "_outer"; break; + } + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath() + state); + registerBlockModel(stateId, modelId, blockState, modelCache); + + boolean isTop = blockState.getValue(HALF) == Half.TOP; + boolean isLeft = shape == StairsShape.INNER_LEFT || + shape == StairsShape.OUTER_LEFT; + boolean isRight = shape == StairsShape.INNER_RIGHT || + shape == StairsShape.OUTER_RIGHT; + int y = 0; + int x = isTop ? 180 : 0; + switch (blockState.getValue(FACING)) { + case NORTH: + if (isTop && !isRight) y = 270; + else if (!isTop) y = isLeft ? 180 : 270; + break; + case EAST: + if (isTop && isRight) y = 90; + else if (!isTop && isLeft) y = 270; + break; + case SOUTH: + if (isTop) y = isRight ? 180 : 90; + else if (!isLeft) y = 90; + break; + case WEST: + default: + y = (isTop && isRight) ? 270 : (!isTop && isLeft) ? 90 : 180; + break; + } + BlockModelRotation rotation = BlockModelRotation.by(x, y); + return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), true); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndStoneButtonBlock.java b/src/main/java/ru/bclib/blocks/EndStoneButtonBlock.java new file mode 100644 index 00000000..2a7e5936 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndStoneButtonBlock.java @@ -0,0 +1,18 @@ +package ru.bclib.blocks; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.level.block.Block; + +public class EndStoneButtonBlock extends EndButtonBlock { + + public EndStoneButtonBlock(Block source) { + super(source, FabricBlockSettings.copyOf(source).noOcclusion(), false); + } + + @Override + protected SoundEvent getSound(boolean clicked) { + return clicked ? SoundEvents.STONE_BUTTON_CLICK_ON : SoundEvents.STONE_BUTTON_CLICK_OFF; + } +} diff --git a/src/main/java/ru/bclib/blocks/EndStonePlateBlock.java b/src/main/java/ru/bclib/blocks/EndStonePlateBlock.java new file mode 100644 index 00000000..38483da1 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndStonePlateBlock.java @@ -0,0 +1,9 @@ +package ru.bclib.blocks; + +import net.minecraft.world.level.block.Block; + +public class EndStonePlateBlock extends EndPlateBlock { + public EndStonePlateBlock(Block source) { + super(Sensitivity.MOBS, source); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndTrapdoorBlock.java b/src/main/java/ru/bclib/blocks/EndTrapdoorBlock.java new file mode 100644 index 00000000..e9c9b883 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndTrapdoorBlock.java @@ -0,0 +1,87 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.TrapDoorBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.Half; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; +import ru.betterend.client.render.ERenderLayer; +import ru.betterend.interfaces.IRenderTypeable; + +public class EndTrapdoorBlock extends TrapDoorBlock implements IRenderTypeable, BlockModelProvider { + public EndTrapdoorBlock(Block source) { + super(FabricBlockSettings.copyOf(source).strength(3.0F, 3.0F).noOcclusion()); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.CUTOUT; + } + + @Override + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + String name = resourceLocation.getPath(); + Optional pattern = Patterns.createJson(Patterns.BLOCK_TRAPDOOR, new HashMap() { + private static final long serialVersionUID = 1L; + { + put("%block%", name); + put("%texture%", name.replace("trapdoor", "door_side")); + } + }); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), "block/" + stateId.getPath()); + registerBlockModel(stateId, modelId, blockState, modelCache); + boolean isTop = blockState.getValue(HALF) == Half.TOP; + boolean isOpen = blockState.getValue(OPEN); + int y = 0; + int x = (isTop && isOpen) ? 270 : isTop ? 180 : isOpen ? 90 : 0; + switch (blockState.getValue(FACING)) { + case EAST: + y = (isTop && isOpen) ? 270 : 90; + break; + case NORTH: + if (isTop && isOpen) y = 180; + break; + case SOUTH: + y = (isTop && isOpen) ? 0 : 180; + break; + case WEST: + default: + y = (isTop && isOpen) ? 90 : 270; + break; + } + BlockModelRotation rotation = BlockModelRotation.by(x, y); + return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), false); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndWallBlock.java b/src/main/java/ru/bclib/blocks/EndWallBlock.java new file mode 100644 index 00000000..db1ffe82 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndWallBlock.java @@ -0,0 +1,95 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.WallBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.WallSide; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; + +public class EndWallBlock extends WallBlock implements BlockModelProvider { + + private final Block parent; + + public EndWallBlock(Block source) { + super(FabricBlockSettings.copyOf(source).noOcclusion()); + this.parent = source; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public BlockModel getItemModel(ResourceLocation blockId) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern = Patterns.createJson(Patterns.ITEM_WALL, parentId.getPath(), blockId.getPath()); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation blockId, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + String path = blockId.getPath(); + Optional pattern = Optional.empty(); + if (path.endsWith("_post")) { + pattern = Patterns.createJson(Patterns.BLOCK_WALL_POST, parentId.getPath(), blockId.getPath()); + } + if (path.endsWith("_side")) { + pattern = Patterns.createJson(Patterns.BLOCK_WALL_SIDE, parentId.getPath(), blockId.getPath()); + } + if (path.endsWith("_side_tall")) { + pattern = Patterns.createJson(Patterns.BLOCK_WALL_SIDE_TALL, parentId.getPath(), blockId.getPath()); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + ResourceLocation postId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + "_post"); + ResourceLocation sideId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + "_side"); + ResourceLocation sideTallId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + "_side_tall"); + registerBlockModel(postId, postId, blockState, modelCache); + registerBlockModel(sideId, sideId, blockState, modelCache); + registerBlockModel(sideTallId, sideTallId, blockState, modelCache); + + ModelsHelper.MultiPartBuilder builder = ModelsHelper.MultiPartBuilder.create(stateDefinition); + builder.part(sideId).setCondition(state -> state.getValue(NORTH_WALL) == WallSide.LOW).setUVLock(true).add(); + builder.part(sideId).setCondition(state -> state.getValue(EAST_WALL) == WallSide.LOW) + .setTransformation(BlockModelRotation.X0_Y90.getRotation()).setUVLock(true).add(); + builder.part(sideId).setCondition(state -> state.getValue(SOUTH_WALL) == WallSide.LOW) + .setTransformation(BlockModelRotation.X0_Y180.getRotation()).setUVLock(true).add(); + builder.part(sideId).setCondition(state -> state.getValue(WEST_WALL) == WallSide.LOW) + .setTransformation(BlockModelRotation.X0_Y270.getRotation()).setUVLock(true).add(); + builder.part(sideTallId).setCondition(state -> state.getValue(NORTH_WALL) == WallSide.TALL).setUVLock(true).add(); + builder.part(sideTallId).setCondition(state -> state.getValue(EAST_WALL) == WallSide.TALL) + .setTransformation(BlockModelRotation.X0_Y90.getRotation()).setUVLock(true).add(); + builder.part(sideTallId).setCondition(state -> state.getValue(SOUTH_WALL) == WallSide.TALL) + .setTransformation(BlockModelRotation.X0_Y180.getRotation()).setUVLock(true).add(); + builder.part(sideTallId).setCondition(state -> state.getValue(WEST_WALL) == WallSide.TALL) + .setTransformation(BlockModelRotation.X0_Y270.getRotation()).setUVLock(true).add(); + builder.part(postId).setCondition(state -> state.getValue(UP)).add(); + + return builder.build(); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndWeightedPlateBlock.java b/src/main/java/ru/bclib/blocks/EndWeightedPlateBlock.java new file mode 100644 index 00000000..ce87ac60 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndWeightedPlateBlock.java @@ -0,0 +1,62 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.Registry; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.WeightedPressurePlateBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.storage.loot.LootContext; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; + +public class EndWeightedPlateBlock extends WeightedPressurePlateBlock implements BlockModelProvider { + private final Block parent; + + public EndWeightedPlateBlock(Block source) { + super(15, FabricBlockSettings.copyOf(source).noCollission().noOcclusion().requiresCorrectToolForDrops().strength(0.5F)); + this.parent = source; + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return getBlockModel(resourceLocation, defaultBlockState()); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + ResourceLocation parentId = Registry.BLOCK.getKey(parent); + Optional pattern; + if (blockState.getValue(POWER) > 0) { + pattern = Patterns.createJson(Patterns.BLOCK_PLATE_DOWN, parentId.getPath(), resourceLocation.getPath()); + } else { + pattern = Patterns.createJson(Patterns.BLOCK_PLATE_UP, parentId.getPath(), resourceLocation.getPath()); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + String state = blockState.getValue(POWER) > 0 ? "_down" : "_up"; + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + state); + registerBlockModel(stateId, modelId, blockState, modelCache); + return ModelsHelper.createBlockSimple(modelId); + } +} diff --git a/src/main/java/ru/bclib/blocks/EndWoodenButtonBlock.java b/src/main/java/ru/bclib/blocks/EndWoodenButtonBlock.java new file mode 100644 index 00000000..531548bd --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndWoodenButtonBlock.java @@ -0,0 +1,18 @@ +package ru.bclib.blocks; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.sounds.SoundEvent; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.level.block.Block; + +public class EndWoodenButtonBlock extends EndButtonBlock { + + public EndWoodenButtonBlock(Block source) { + super(source, FabricBlockSettings.copyOf(source).strength(0.5F, 0.5F).noOcclusion(), true); + } + + @Override + protected SoundEvent getSound(boolean clicked) { + return clicked ? SoundEvents.WOODEN_BUTTON_CLICK_ON : SoundEvents.WOODEN_BUTTON_CLICK_OFF; + } +} diff --git a/src/main/java/ru/bclib/blocks/EndWoodenPlateBlock.java b/src/main/java/ru/bclib/blocks/EndWoodenPlateBlock.java new file mode 100644 index 00000000..80138c46 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/EndWoodenPlateBlock.java @@ -0,0 +1,9 @@ +package ru.bclib.blocks; + +import net.minecraft.world.level.block.Block; + +public class EndWoodenPlateBlock extends EndPlateBlock { + public EndWoodenPlateBlock(Block source) { + super(Sensitivity.EVERYTHING, source); + } +} diff --git a/src/main/java/ru/bclib/blocks/FeatureSaplingBlock.java b/src/main/java/ru/bclib/blocks/FeatureSaplingBlock.java new file mode 100644 index 00000000..13b88cf3 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/FeatureSaplingBlock.java @@ -0,0 +1,123 @@ +package ru.bclib.blocks; + +import java.util.Collections; +import java.util.List; +import java.util.Optional; +import java.util.Random; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.item.ItemStack; +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.SaplingBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.levelgen.feature.Feature; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import ru.betterend.client.models.BlockModelProvider; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; +import ru.betterend.client.render.ERenderLayer; +import ru.betterend.interfaces.IRenderTypeable; +import ru.betterend.registry.EndTags; + +public abstract class FeatureSaplingBlock extends SaplingBlock implements IRenderTypeable, BlockModelProvider { + private static final VoxelShape SHAPE = Block.box(4, 0, 4, 12, 14, 12); + + public FeatureSaplingBlock() { + super(null, FabricBlockSettings.of(Material.PLANT) + .breakByHand(true) + .collidable(false) + .instabreak() + .sound(SoundType.GRASS) + .randomTicks()); + } + + public FeatureSaplingBlock(int light) { + super(null, FabricBlockSettings.of(Material.PLANT) + .breakByHand(true) + .collidable(false) + .luminance(light) + .instabreak() + .sound(SoundType.GRASS) + .randomTicks()); + } + + protected abstract Feature getFeature(); + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Collections.singletonList(new ItemStack(this)); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return SHAPE; + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + return world.getBlockState(pos.below()).is(EndTags.END_GROUND); + } + + @Override + public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) { + if (!canSurvive(state, world, pos)) + return Blocks.AIR.defaultBlockState(); + else + return state; + } + + @Override + public boolean isBonemealSuccess(Level world, Random random, BlockPos pos, BlockState state) { + return random.nextInt(16) == 0; + } + + @Override + public void advanceTree(ServerLevel world, BlockPos pos, BlockState blockState, Random random) { + getFeature().place(world, world.getChunkSource().getGenerator(), random, pos, null); + } + + @Override + public void randomTick(BlockState state, ServerLevel world, BlockPos pos, Random random) { + this.tick(state, world, pos, random); + } + + @Override + public void tick(BlockState state, ServerLevel world, BlockPos pos, Random random) { + super.tick(state, world, pos, random); + if (isBonemealSuccess(world, random, pos, state)) { + performBonemeal(world, random, pos, state); + } + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.CUTOUT; + } + + @Override + public BlockModel getItemModel(ResourceLocation resourceLocation) { + return ModelsHelper.createBlockItem(resourceLocation); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + Optional pattern = Patterns.createJson(Patterns.BLOCK_CROSS, resourceLocation.getPath()); + return ModelsHelper.fromPattern(pattern); + } +} diff --git a/src/main/java/ru/bclib/blocks/SimpleLeavesBlock.java b/src/main/java/ru/bclib/blocks/SimpleLeavesBlock.java new file mode 100644 index 00000000..fd2b4012 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/SimpleLeavesBlock.java @@ -0,0 +1,38 @@ +package ru.bclib.blocks; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.material.MaterialColor; +import ru.betterend.client.render.ERenderLayer; +import ru.betterend.interfaces.IRenderTypeable; + +public class SimpleLeavesBlock extends BlockBaseNotFull implements IRenderTypeable { + public SimpleLeavesBlock(MaterialColor color) { + super(FabricBlockSettings.of(Material.LEAVES) + .strength(0.2F) + .materialColor(color) + .sound(SoundType.GRASS) + .noOcclusion() + .isValidSpawn((state, world, pos, type) -> false) + .isSuffocating((state, world, pos) -> false) + .isViewBlocking((state, world, pos) -> false)); + } + + public SimpleLeavesBlock(MaterialColor color, int light) { + super(FabricBlockSettings.of(Material.LEAVES) + .luminance(light) + .materialColor(color) + .strength(0.2F) + .sound(SoundType.GRASS) + .noOcclusion() + .isValidSpawn((state, world, pos, type) -> false) + .isSuffocating((state, world, pos) -> false) + .isViewBlocking((state, world, pos) -> false)); + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.CUTOUT; + } +} \ No newline at end of file diff --git a/src/main/java/ru/bclib/blocks/StalactiteBlock.java b/src/main/java/ru/bclib/blocks/StalactiteBlock.java new file mode 100644 index 00000000..dc6e3669 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/StalactiteBlock.java @@ -0,0 +1,247 @@ +package ru.bclib.blocks; + +import java.util.Map; +import java.util.Optional; + +import org.jetbrains.annotations.Nullable; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.BlockPos.MutableBlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.util.Mth; +import net.minecraft.world.entity.LivingEntity; +import net.minecraft.world.item.ItemStack; +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.LiquidBlockContainer; +import net.minecraft.world.level.block.SimpleWaterloggedBlock; +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.BooleanProperty; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import ru.bclib.interfaces.IRenderTypeable; +import ru.betterend.blocks.BlockProperties; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; +import ru.betterend.client.render.ERenderLayer; +import ru.betterend.interfaces.IRenderTypeable; + +public class StalactiteBlock extends BaseBlockNotFull implements SimpleWaterloggedBlock, LiquidBlockContainer, IRenderTypeable { + public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED; + public static final BooleanProperty IS_FLOOR = BlockProperties.IS_FLOOR; + public static final IntegerProperty SIZE = BlockProperties.SIZE; + private static final VoxelShape[] SHAPES; + + public StalactiteBlock(Block source) { + super(FabricBlockSettings.copy(source).noOcclusion()); + this.registerDefaultState(getStateDefinition().any().setValue(SIZE, 0).setValue(IS_FLOOR, true).setValue(WATERLOGGED, false)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(WATERLOGGED, IS_FLOOR, SIZE); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return SHAPES[state.getValue(SIZE)]; + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext ctx) { + LevelReader world = ctx.getLevel(); + BlockPos pos = ctx.getClickedPos(); + Direction dir = ctx.getClickedFace(); + boolean water = world.getFluidState(pos).getType() == Fluids.WATER; + + if (dir == Direction.DOWN) { + if (isThis(world, pos.above()) || canSupportCenter(world, pos.above(), Direction.DOWN)) { + return defaultBlockState().setValue(IS_FLOOR, false).setValue(WATERLOGGED, water); + } + else if (isThis(world, pos.below()) || canSupportCenter(world, pos.below(), Direction.UP)) { + return defaultBlockState().setValue(IS_FLOOR, true).setValue(WATERLOGGED, water); + } + else { + return null; + } + } + else { + if (isThis(world, pos.below()) || canSupportCenter(world, pos.below(), Direction.UP)) { + return defaultBlockState().setValue(IS_FLOOR, true).setValue(WATERLOGGED, water); + } + else if (isThis(world, pos.above()) || canSupportCenter(world, pos.above(), Direction.DOWN)) { + return defaultBlockState().setValue(IS_FLOOR, false).setValue(WATERLOGGED, water); + } + else { + return null; + } + } + } + + @Override + public void setPlacedBy(Level world, BlockPos pos, BlockState state, LivingEntity placer, ItemStack itemStack) { + boolean hasUp = isThis(world, pos.above()); + boolean hasDown = isThis(world, pos.below()); + MutableBlockPos mut = new MutableBlockPos(); + if (hasUp && hasDown) { + boolean floor = state.getValue(IS_FLOOR); + BlockPos second = floor ? pos.above() : pos.below(); + BlockState bState = world.getBlockState(second); + world.setBlockAndUpdate(pos, state.setValue(SIZE, 1).setValue(IS_FLOOR, floor)); + world.setBlockAndUpdate(second, bState.setValue(SIZE, 1).setValue(IS_FLOOR, !floor)); + + bState = state; + int startSize = floor ? 1 : 2; + mut.set(pos.getX(), pos.getY() + 1, pos.getZ()); + for (int i = 0; i < 8 && isThis(bState); i++) { + world.setBlockAndUpdate(mut, bState.setValue(SIZE, startSize++).setValue(IS_FLOOR, false)); + mut.setY(mut.getY() + 1); + bState = world.getBlockState(mut); + } + + bState = state; + startSize = floor ? 2 : 1; + mut.set(pos.getX(), pos.getY() - 1, pos.getZ()); + for (int i = 0; i < 8 && isThis(bState); i++) { + world.setBlockAndUpdate(mut, bState.setValue(SIZE, startSize++).setValue(IS_FLOOR, true)); + mut.setY(mut.getY() - 1); + bState = world.getBlockState(mut); + } + } + else if (hasDown) { + mut.setX(pos.getX()); + mut.setZ(pos.getZ()); + for (int i = 1; i < 8; i++) { + mut.setY(pos.getY() - i); + if (isThis(world, mut)) { + BlockState state2 = world.getBlockState(mut); + int size = state2.getValue(SIZE); + if (size < i) { + world.setBlockAndUpdate(mut, state2.setValue(SIZE, i).setValue(IS_FLOOR, true)); + } + else { + break; + } + } + else { + break; + } + } + } + else if (hasUp) { + mut.setX(pos.getX()); + mut.setZ(pos.getZ()); + for (int i = 1; i < 8; i++) { + mut.setY(pos.getY() + i); + if (isThis(world, mut)) { + BlockState state2 = world.getBlockState(mut); + int size = state2.getValue(SIZE); + if (size < i) { + world.setBlockAndUpdate(mut, state2.setValue(SIZE, i).setValue(IS_FLOOR, false)); + } + else { + break; + } + } + else { + break; + } + } + } + } + + private boolean isThis(LevelReader world, BlockPos pos) { + return isThis(world.getBlockState(pos)); + } + + private boolean isThis(BlockState state) { + return state.getBlock() instanceof StalactiteBlock; + } + + @Override + public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) { + if (!canSurvive(state, world, pos)) { + return Blocks.AIR.defaultBlockState(); + } + return state; + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + int size = state.getValue(SIZE); + return checkUp(world, pos, size) || checkDown(world, pos, size); + } + + private boolean checkUp(BlockGetter world, BlockPos pos, int size) { + BlockPos p = pos.above(); + BlockState state = world.getBlockState(p); + return (isThis(state) && state.getValue(SIZE) >= size) || state.isCollisionShapeFullBlock(world, p); + } + + private boolean checkDown(BlockGetter world, BlockPos pos, int size) { + BlockPos p = pos.below(); + BlockState state = world.getBlockState(p); + return (isThis(state) && state.getValue(SIZE) >= size) || state.isCollisionShapeFullBlock(world, p); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + Optional pattern = Patterns.createJson(Patterns.BLOCK_CROSS_SHADED, resourceLocation.getPath()); + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + BlockModelRotation rotation = blockState.getValue(IS_FLOOR) ? BlockModelRotation.X0_Y0 : BlockModelRotation.X180_Y0; + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), + stateId.getPath() + "_" + blockState.getValue(SIZE)); + registerBlockModel(modelId, modelId, blockState, modelCache); + return ModelsHelper.createMultiVariant(modelId, rotation.getRotation(), false); + } + + @Override + public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid) { + return false; + } + + @Override + public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState) { + return false; + } + + @Override + public FluidState getFluidState(BlockState state) { + return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : Fluids.EMPTY.defaultFluidState(); + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.CUTOUT; + } + + static { + float end = 2F / 8F; + float start = 5F / 8F; + SHAPES = new VoxelShape[8]; + for (int i = 0; i < 8; i++) { + int side = Mth.floor(Mth.lerp(i / 7F, start, end) * 8F + 0.5F); + SHAPES[i] = Block.box(side, 0, side, 16 - side, 16, 16 - side); + } + } +} \ No newline at end of file diff --git a/src/main/java/ru/bclib/blocks/StrippableBarkBlock.java b/src/main/java/ru/bclib/blocks/StrippableBarkBlock.java new file mode 100644 index 00000000..60fb6f43 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/StrippableBarkBlock.java @@ -0,0 +1,41 @@ +package ru.bclib.blocks; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerPlayer; +import net.minecraft.sounds.SoundEvents; +import net.minecraft.sounds.SoundSource; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.RotatedPillarBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.phys.BlockHitResult; + +public class StrippableBarkBlock extends BarkBlock { + private final Block striped; + + public StrippableBarkBlock(MaterialColor color, Block striped) { + super(FabricBlockSettings.copyOf(striped).materialColor(color)); + this.striped = striped; + } + + @Override + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + if (player.getMainHandItem().getItem().is(FabricToolTags.AXES)) { + world.playSound(player, pos, SoundEvents.AXE_STRIP, SoundSource.BLOCKS, 1.0F, 1.0F); + if (!world.isClientSide) { + world.setBlock(pos, striped.defaultBlockState().setValue(RotatedPillarBlock.AXIS, state.getValue(RotatedPillarBlock.AXIS)), 11); + if (!player.isCreative()) { + player.getMainHandItem().hurt(1, world.random, (ServerPlayer) player); + } + } + return InteractionResult.SUCCESS; + } + return InteractionResult.FAIL; + } +} diff --git a/src/main/java/ru/bclib/blocks/TripleTerrainBlock.java b/src/main/java/ru/bclib/blocks/TripleTerrainBlock.java new file mode 100644 index 00000000..57d07c21 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/TripleTerrainBlock.java @@ -0,0 +1,154 @@ +package ru.bclib.blocks; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.Random; + +import org.jetbrains.annotations.Nullable; + +import com.google.common.collect.Lists; +import com.google.common.collect.Maps; + +import net.minecraft.client.renderer.block.model.BlockModel; +import net.minecraft.client.renderer.block.model.MultiVariant; +import net.minecraft.client.renderer.block.model.Variant; +import net.minecraft.client.resources.model.BlockModelRotation; +import net.minecraft.client.resources.model.UnbakedModel; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.InteractionResult; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.Level; +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.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.EnumProperty; +import net.minecraft.world.level.material.MaterialColor; +import net.minecraft.world.phys.BlockHitResult; +import ru.betterend.blocks.BlockProperties; +import ru.betterend.blocks.BlockProperties.TripleShape; +import ru.betterend.client.models.ModelsHelper; +import ru.betterend.client.models.Patterns; + +public class TripleTerrainBlock extends BaseTerrainBlock { + public static final EnumProperty SHAPE = BlockProperties.TRIPLE_SHAPE; + + public TripleTerrainBlock(MaterialColor color) { + super(color); + this.registerDefaultState(this.defaultBlockState().setValue(SHAPE, TripleShape.BOTTOM)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(SHAPE); + } + + @Override + public BlockState getStateForPlacement(BlockPlaceContext ctx) { + Direction dir = ctx.getClickedFace(); + TripleShape shape = dir == Direction.UP ? TripleShape.BOTTOM : dir == Direction.DOWN ? TripleShape.TOP : TripleShape.MIDDLE; + return this.defaultBlockState().setValue(SHAPE, shape); + } + + @Override + public InteractionResult use(BlockState state, Level world, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) { + TripleShape shape = state.getValue(SHAPE); + if (shape == TripleShape.BOTTOM) { + return super.use(state, world, pos, player, hand, hit); + } + return InteractionResult.FAIL; + } + + @Override + public void randomTick(BlockState state, ServerLevel world, BlockPos pos, Random random) { + TripleShape shape = state.getValue(SHAPE); + if (shape == TripleShape.BOTTOM) { + super.randomTick(state, world, pos, random); + } else if (random.nextInt(16) == 0) { + boolean bottom = canStayBottom(world, pos); + if (shape == TripleShape.TOP) { + if (!bottom) { + world.setBlockAndUpdate(pos, Blocks.END_STONE.defaultBlockState()); + } + } + else { + boolean top = canStay(state, world, pos) || isMiddle(world.getBlockState(pos.above())); + if (!top && !bottom) { + world.setBlockAndUpdate(pos, Blocks.END_STONE.defaultBlockState()); + } else if (top && !bottom) { + world.setBlockAndUpdate(pos, state.setValue(SHAPE, TripleShape.BOTTOM)); + } else if (!top) { + world.setBlockAndUpdate(pos, state.setValue(SHAPE, TripleShape.TOP)); + } + } + } + } + + protected boolean canStayBottom(LevelReader world, BlockPos pos) { + BlockPos blockPos = pos.below(); + BlockState blockState = world.getBlockState(blockPos); + if (isMiddle(blockState)) { + return true; + } else if (blockState.getFluidState().getAmount() == 8) { + return false; + } else { + return !blockState.isFaceSturdy(world, blockPos, Direction.UP); + } + } + + @Override + public BlockModel getItemModel(ResourceLocation blockId) { + return getBlockModel(blockId, defaultBlockState()); + } + + @Override + public @Nullable BlockModel getBlockModel(ResourceLocation resourceLocation, BlockState blockState) { + String name = resourceLocation.getPath(); + Optional pattern; + if (isMiddle(blockState)) { + pattern = Patterns.createBlockSimple(name + "_top"); + } else { + Map textures = Maps.newHashMap(); + textures.put("%top%", "betterend:block/" + name + "_top"); + textures.put("%side%", "betterend:block/" + name + "_side"); + textures.put("%bottom%", "minecraft:block/end_stone"); + pattern = Patterns.createJson(Patterns.BLOCK_TOP_SIDE_BOTTOM, textures); + } + return ModelsHelper.fromPattern(pattern); + } + + @Override + public UnbakedModel getModelVariant(ResourceLocation stateId, BlockState blockState, Map modelCache) { + boolean isMiddle = isMiddle(blockState); + String middle = isMiddle ? "_middle" : ""; + ResourceLocation modelId = new ResourceLocation(stateId.getNamespace(), + "block/" + stateId.getPath() + middle); + registerBlockModel(stateId, modelId, blockState, modelCache); + if (isMiddle) { + List variants = Lists.newArrayList(); + for (BlockModelRotation rotation : BlockModelRotation.values()) { + variants.add(new Variant(modelId, rotation.getRotation(), false, 1)); + } + return new MultiVariant(variants); + } else if (blockState.getValue(SHAPE) == TripleShape.TOP) { + return new MultiVariant(Lists.newArrayList( + new Variant(modelId, BlockModelRotation.X180_Y0.getRotation(), false, 1), + new Variant(modelId, BlockModelRotation.X180_Y90.getRotation(), false, 1), + new Variant(modelId, BlockModelRotation.X180_Y180.getRotation(), false, 1), + new Variant(modelId, BlockModelRotation.X180_Y270.getRotation(), false, 1) + )); + } + return ModelsHelper.createRandomTopModel(modelId); + } + + protected boolean isMiddle(BlockState blockState) { + return blockState.is(this) && blockState.getValue(SHAPE) == TripleShape.MIDDLE; + } +} diff --git a/src/main/java/ru/bclib/blocks/UnderwaterPlantBlock.java b/src/main/java/ru/bclib/blocks/UnderwaterPlantBlock.java new file mode 100644 index 00000000..239c974c --- /dev/null +++ b/src/main/java/ru/bclib/blocks/UnderwaterPlantBlock.java @@ -0,0 +1,145 @@ +package ru.bclib.blocks; + +import java.util.List; +import java.util.Random; + +import com.google.common.collect.Lists; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.item.enchantment.Enchantments; +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.BonemealableBlock; +import net.minecraft.world.level.block.LiquidBlockContainer; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Fluid; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.material.Fluids; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import ru.betterend.client.render.ERenderLayer; +import ru.betterend.interfaces.IRenderTypeable; +import ru.betterend.registry.EndBlocks; +import ru.betterend.registry.EndTags; + +public class UnderwaterPlantBlock extends BlockBaseNotFull implements IRenderTypeable, BonemealableBlock, LiquidBlockContainer { + private static final VoxelShape SHAPE = Block.box(4, 0, 4, 12, 14, 12); + + public UnderwaterPlantBlock() { + super(FabricBlockSettings.of(Material.WATER_PLANT) + .breakByTool(FabricToolTags.SHEARS) + .breakByHand(true) + .sound(SoundType.WET_GRASS) + .noCollission()); + } + + public UnderwaterPlantBlock(int light) { + super(FabricBlockSettings.of(Material.WATER_PLANT) + .breakByTool(FabricToolTags.SHEARS) + .breakByHand(true) + .luminance(light) + .sound(SoundType.WET_GRASS) + .noCollission()); + } + + public UnderwaterPlantBlock(Properties settings) { + super(settings); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + Vec3 vec3d = state.getOffset(view, pos); + return SHAPE.move(vec3d.x, vec3d.y, vec3d.z); + } + + @Override + public BlockBehaviour.OffsetType getOffsetType() { + return BlockBehaviour.OffsetType.XZ; + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + BlockState down = world.getBlockState(pos.below()); + state = world.getBlockState(pos); + return isTerrain(down) && state.getFluidState().getType().equals(Fluids.WATER.getSource()); + } + + protected boolean isTerrain(BlockState state) { + return state.is(EndTags.END_GROUND) || state.getBlock() == EndBlocks.ENDSTONE_DUST; + } + + @Override + public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) { + if (!canSurvive(state, world, pos)) { + world.destroyBlock(pos, true); + return Blocks.WATER.defaultBlockState(); + } + else { + return state; + } + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && tool.getItem().is(FabricToolTags.SHEARS) || EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) { + return Lists.newArrayList(new ItemStack(this)); + } + else { + return Lists.newArrayList(); + } + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.CUTOUT; + } + + @Override + public boolean isValidBonemealTarget(BlockGetter world, BlockPos pos, BlockState state, boolean isClient) { + return true; + } + + @Override + public boolean isBonemealSuccess(Level world, Random random, BlockPos pos, BlockState state) { + return true; + } + + @Override + public void performBonemeal(ServerLevel world, Random random, BlockPos pos, BlockState state) { + ItemEntity item = new ItemEntity(world, pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5, new ItemStack(this)); + world.addFreshEntity(item); + } + + @Override + public boolean canPlaceLiquid(BlockGetter world, BlockPos pos, BlockState state, Fluid fluid) { + return false; + } + + @Override + public boolean placeLiquid(LevelAccessor world, BlockPos pos, BlockState state, FluidState fluidState) { + return false; + } + + @Override + public FluidState getFluidState(BlockState state) { + return Fluids.WATER.getSource(false); + } +} diff --git a/src/main/java/ru/bclib/blocks/UnderwaterPlantWithAgeBlock.java b/src/main/java/ru/bclib/blocks/UnderwaterPlantWithAgeBlock.java new file mode 100644 index 00000000..0f8176c4 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/UnderwaterPlantWithAgeBlock.java @@ -0,0 +1,57 @@ +package ru.bclib.blocks; + +import java.util.Random; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.core.BlockPos; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.world.level.WorldGenLevel; +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.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.IntegerProperty; +import net.minecraft.world.level.material.Material; +import ru.betterend.blocks.BlockProperties; + +public abstract class UnderwaterPlantWithAgeBlock extends UnderwaterPlantBlock { + public static final IntegerProperty AGE = BlockProperties.AGE; + + public UnderwaterPlantWithAgeBlock() { + super(FabricBlockSettings.of(Material.WATER_PLANT) + .breakByTool(FabricToolTags.SHEARS) + .breakByHand(true) + .sound(SoundType.WET_GRASS) + .randomTicks() + .noCollission()); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(AGE); + } + + public abstract void grow(WorldGenLevel world, Random random, BlockPos pos); + + @Override + public void performBonemeal(ServerLevel world, Random random, BlockPos pos, BlockState state) { + if (random.nextInt(4) == 0) { + int age = state.getValue(AGE); + if (age < 3) { + world.setBlockAndUpdate(pos, state.setValue(AGE, age + 1)); + } + else { + grow(world, random, pos); + } + } + } + + @Override + public void tick(BlockState state, ServerLevel world, BlockPos pos, Random random) { + super.tick(state, world, pos, random); + if (isBonemealSuccess(world, random, pos, state)) { + performBonemeal(world, random, pos, state); + } + } +} diff --git a/src/main/java/ru/bclib/blocks/UpDownPlantBlock.java b/src/main/java/ru/bclib/blocks/UpDownPlantBlock.java new file mode 100644 index 00000000..0a036e9c --- /dev/null +++ b/src/main/java/ru/bclib/blocks/UpDownPlantBlock.java @@ -0,0 +1,95 @@ +package ru.bclib.blocks; + +import java.util.List; + +import com.google.common.collect.Lists; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.item.enchantment.Enchantments; +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.SoundType; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import ru.betterend.client.render.ERenderLayer; +import ru.betterend.interfaces.IRenderTypeable; +import ru.betterend.registry.EndTags; + +public class UpDownPlantBlock extends BlockBaseNotFull implements IRenderTypeable { + private static final VoxelShape SHAPE = Block.box(4, 0, 4, 12, 16, 12); + + public UpDownPlantBlock() { + super(FabricBlockSettings.of(Material.PLANT) + .breakByTool(FabricToolTags.SHEARS) + .breakByHand(true) + .sound(SoundType.GRASS) + .noCollission()); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + return SHAPE; + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + BlockState down = world.getBlockState(pos.below()); + BlockState up = world.getBlockState(pos.above()); + return (isTerrain(down) || down.getBlock() == this) && (isSupport(up, world, pos) || up.getBlock() == this); + } + + protected boolean isTerrain(BlockState state) { + return state.is(EndTags.END_GROUND); + } + + protected boolean isSupport(BlockState state, LevelReader world, BlockPos pos) { + return canSupportCenter(world, pos.above(), Direction.UP); + } + + @Override + public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) { + if (!canSurvive(state, world, pos)) { + return Blocks.AIR.defaultBlockState(); + } + else { + return state; + } + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && tool.getItem().is(FabricToolTags.SHEARS) || EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) { + return Lists.newArrayList(new ItemStack(this)); + } + else { + return Lists.newArrayList(); + } + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.CUTOUT; + } + + @Override + public void playerDestroy(Level world, Player player, BlockPos pos, BlockState state, BlockEntity blockEntity, ItemStack stack) { + super.playerDestroy(world, player, pos, state, blockEntity, stack); + world.neighborChanged(pos, Blocks.AIR, pos.below()); + } +} diff --git a/src/main/java/ru/bclib/blocks/VineBlock.java b/src/main/java/ru/bclib/blocks/VineBlock.java new file mode 100644 index 00000000..dcd87d41 --- /dev/null +++ b/src/main/java/ru/bclib/blocks/VineBlock.java @@ -0,0 +1,147 @@ +package ru.bclib.blocks; + +import java.util.List; +import java.util.Random; + +import com.google.common.collect.Lists; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.server.level.ServerLevel; +import net.minecraft.tags.BlockTags; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.EnchantmentHelper; +import net.minecraft.world.item.enchantment.Enchantments; +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.BonemealableBlock; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockBehaviour; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.properties.EnumProperty; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.storage.loot.LootContext; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.Vec3; +import net.minecraft.world.phys.shapes.CollisionContext; +import net.minecraft.world.phys.shapes.VoxelShape; +import ru.betterend.blocks.BlockProperties; +import ru.betterend.blocks.BlockProperties.TripleShape; +import ru.betterend.client.render.ERenderLayer; +import ru.betterend.interfaces.IRenderTypeable; +import ru.betterend.util.BlocksHelper; + +public class VineBlock extends BlockBaseNotFull implements IRenderTypeable, BonemealableBlock { + public static final EnumProperty SHAPE = BlockProperties.TRIPLE_SHAPE; + private static final VoxelShape VOXEL_SHAPE = Block.box(2, 0, 2, 14, 16, 14); + + public VineBlock() { + this(0, false); + } + + public VineBlock(int light) { + this(light, false); + } + + public VineBlock(int light, boolean bottomOnly) { + super(FabricBlockSettings.of(Material.PLANT) + .breakByTool(FabricToolTags.SHEARS) + .breakByHand(true) + .sound(SoundType.GRASS) + .lightLevel((state) -> bottomOnly ? state.getValue(SHAPE) == TripleShape.BOTTOM ? light : 0 : light) + .noCollission()); + this.registerDefaultState(this.stateDefinition.any().setValue(SHAPE, TripleShape.BOTTOM)); + } + + @Override + protected void createBlockStateDefinition(StateDefinition.Builder stateManager) { + stateManager.add(SHAPE); + } + + @Override + public VoxelShape getShape(BlockState state, BlockGetter view, BlockPos pos, CollisionContext ePos) { + Vec3 vec3d = state.getOffset(view, pos); + return VOXEL_SHAPE.move(vec3d.x, vec3d.y, vec3d.z); + } + + @Override + public BlockBehaviour.OffsetType getOffsetType() { + return BlockBehaviour.OffsetType.XZ; + } + + public boolean canGenerate(BlockState state, LevelReader world, BlockPos pos) { + return isSupport(state, world, pos); + } + + @Override + public boolean canSurvive(BlockState state, LevelReader world, BlockPos pos) { + return isSupport(state, world, pos); + } + + protected boolean isSupport(BlockState state, LevelReader world, BlockPos pos) { + BlockState up = world.getBlockState(pos.above()); + return up.is(this) || up.is(BlockTags.LEAVES) || canSupportCenter(world, pos.above(), Direction.DOWN); + } + + @Override + public BlockState updateShape(BlockState state, Direction facing, BlockState neighborState, LevelAccessor world, BlockPos pos, BlockPos neighborPos) { + if (!canSurvive(state, world, pos)) { + return Blocks.AIR.defaultBlockState(); + } + else { + if (world.getBlockState(pos.below()).getBlock() != this) + return state.setValue(SHAPE, TripleShape.BOTTOM); + else if (world.getBlockState(pos.above()).getBlock() != this) + return state.setValue(SHAPE, TripleShape.TOP); + return state.setValue(SHAPE, TripleShape.MIDDLE); + } + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + ItemStack tool = builder.getParameter(LootContextParams.TOOL); + if (tool != null && tool.getItem().is(FabricToolTags.SHEARS) || EnchantmentHelper.getItemEnchantmentLevel(Enchantments.SILK_TOUCH, tool) > 0) { + return Lists.newArrayList(new ItemStack(this)); + } + else { + return Lists.newArrayList(); + } + } + + @Override + public ERenderLayer getRenderLayer() { + return ERenderLayer.CUTOUT; + } + + @Override + public boolean isValidBonemealTarget(BlockGetter world, BlockPos pos, BlockState state, boolean isClient) { + while (world.getBlockState(pos).getBlock() == this) { + pos = pos.below(); + } + return world.getBlockState(pos).isAir(); + } + + @Override + public boolean isBonemealSuccess(Level world, Random random, BlockPos pos, BlockState state) { + while (world.getBlockState(pos).getBlock() == this) { + pos = pos.below(); + } + return world.isEmptyBlock(pos); + } + + @Override + public void performBonemeal(ServerLevel world, Random random, BlockPos pos, BlockState state) { + while (world.getBlockState(pos).getBlock() == this) { + pos = pos.below(); + } + world.setBlockAndUpdate(pos, defaultBlockState()); + BlocksHelper.setWithoutUpdate(world, pos, defaultBlockState()); + } +} diff --git a/src/main/java/ru/bclib/blocks/WallMushroomBlock.java b/src/main/java/ru/bclib/blocks/WallMushroomBlock.java new file mode 100644 index 00000000..c4e5fa1d --- /dev/null +++ b/src/main/java/ru/bclib/blocks/WallMushroomBlock.java @@ -0,0 +1,39 @@ +package ru.bclib.blocks; + +import java.util.List; + +import com.google.common.collect.Lists; + +import net.fabricmc.fabric.api.object.builder.v1.block.FabricBlockSettings; +import net.fabricmc.fabric.api.tool.attribute.v1.FabricToolTags; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.LevelReader; +import net.minecraft.world.level.block.SoundType; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.Material; +import net.minecraft.world.level.storage.loot.LootContext; + +public class WallMushroomBlock extends BaseWallPlantBlock { + public WallMushroomBlock(int light) { + super(FabricBlockSettings.of(Material.PLANT) + .breakByTool(FabricToolTags.AXES) + .breakByHand(true) + .luminance(light) + .hardness(0.2F) + .sound(SoundType.GRASS) + .sound(SoundType.WOOD) + .noCollission()); + } + + @Override + public List getDrops(BlockState state, LootContext.Builder builder) { + return Lists.newArrayList(new ItemStack(this)); + } + + @Override + public boolean isSupport(LevelReader world, BlockPos pos, BlockState blockState, Direction direction) { + return blockState.getMaterial().isSolid() && blockState.isFaceSturdy(world, pos, direction); + } +} diff --git a/src/main/java/ru/bclib/client/render/ERenderLayer.java b/src/main/java/ru/bclib/client/render/ERenderLayer.java new file mode 100644 index 00000000..cf0d637d --- /dev/null +++ b/src/main/java/ru/bclib/client/render/ERenderLayer.java @@ -0,0 +1,6 @@ +package ru.bclib.client.render; + +public enum ERenderLayer { + CUTOUT, + TRANSLUCENT; +} diff --git a/src/main/java/ru/bclib/client/sound/BlockSounds.java b/src/main/java/ru/bclib/client/sound/BlockSounds.java new file mode 100644 index 00000000..97c2cd20 --- /dev/null +++ b/src/main/java/ru/bclib/client/sound/BlockSounds.java @@ -0,0 +1,13 @@ +package ru.bclib.client.sound; + +import net.minecraft.sounds.SoundEvents; +import net.minecraft.world.level.block.SoundType; + +public class BlockSounds { + public static final SoundType TERRAIN_SOUND = new SoundType(1.0F, 1.0F, + SoundEvents.STONE_BREAK, + SoundEvents.WART_BLOCK_STEP, + SoundEvents.STONE_PLACE, + SoundEvents.STONE_HIT, + SoundEvents.STONE_FALL); +} diff --git a/src/main/java/ru/bclib/interfaces/IRenderTypeable.java b/src/main/java/ru/bclib/interfaces/IRenderTypeable.java new file mode 100644 index 00000000..145478d3 --- /dev/null +++ b/src/main/java/ru/bclib/interfaces/IRenderTypeable.java @@ -0,0 +1,8 @@ +package ru.bclib.interfaces; + +import ru.bclib.client.render.ERenderLayer; + +public interface IRenderTypeable +{ + public ERenderLayer getRenderLayer(); +}