diff --git a/src/main/java/com/fureniku/metropolis/RegistrationBase.java b/src/main/java/com/fureniku/metropolis/RegistrationBase.java index 43e9753..0fa0b69 100644 --- a/src/main/java/com/fureniku/metropolis/RegistrationBase.java +++ b/src/main/java/com/fureniku/metropolis/RegistrationBase.java @@ -35,6 +35,11 @@ public abstract class RegistrationBase { protected final DeferredRegister itemRegistry; protected final DeferredRegister creativeTabs; + /** + * Used by registration groups + */ + public final String modid; + private HashMap> block_map = new HashMap<>(); private HashMap> item_map = new HashMap<>(); @@ -44,6 +49,7 @@ public abstract class RegistrationBase { * @param modEventBus the event bus */ public RegistrationBase(String modid, IEventBus modEventBus) { + this.modid = modid; blockRegistry = DeferredRegister.create(ForgeRegistries.BLOCKS, modid); itemRegistry = DeferredRegister.create(ForgeRegistries.ITEMS, modid); creativeTabs = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, modid); diff --git a/src/main/java/com/fureniku/metropolis/RegistrationGroup.java b/src/main/java/com/fureniku/metropolis/RegistrationGroup.java index f6dba55..9220874 100644 --- a/src/main/java/com/fureniku/metropolis/RegistrationGroup.java +++ b/src/main/java/com/fureniku/metropolis/RegistrationGroup.java @@ -1,8 +1,10 @@ package com.fureniku.metropolis; +import com.fureniku.metropolis.datagen.TextureSet; import com.fureniku.metropolis.utils.CreativeTabSet; import com.fureniku.metropolis.utils.Debug; import net.minecraft.core.registries.Registries; +import net.minecraft.resources.ResourceLocation; import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.CreativeModeTab; import net.minecraft.world.item.Item; @@ -87,4 +89,44 @@ protected RegistryObject getBlock(String key) { protected RegistryObject getItem(String key) { return registration.getItem(key); } + + /** + * Get a resource location for the named resource. Override to provide subfolders, else it defaults to getLocFull. + * @param name the name of the resource you're getting in the location. + * @return resource location + */ + protected ResourceLocation getLoc(String name) { + return getLocFull(name); + } + + /** + * Get a resource location for the named resource. Always returns the contextual root (blocks/, models/ etc). + * Override getLoc if you consistently want a subfolder. + * @param name the name of the resource you're getting in the location. + * @return resource location + */ + protected final ResourceLocation getLocFull(String name) { + return new ResourceLocation(registration.modid, name); + } + + /** + * Get a TextureSet for the texture name at the named resource locaation. + * Calls getLoc(), so will use defined subfolders. + * @param name The name of the texture for a face, passed to the model json + * @param loc The name of the texture file (without png) + * @return TextureSet + */ + protected TextureSet texture(String name, String loc) { + return texture(name, getLoc(loc)); + } + + /** + * Get a TextureSet for the texture name at the named resource locaation. + * @param name The name of the texture for a face, passed to the model json + * @param loc ResourceLocation for the texture file + * @return TextureSet + */ + protected TextureSet texture(String name, ResourceLocation loc) { + return new TextureSet(name, loc); + } } diff --git a/src/main/java/com/fureniku/metropolis/blocks/IToggleable.java b/src/main/java/com/fureniku/metropolis/blocks/IToggleable.java new file mode 100644 index 0000000..b48b481 --- /dev/null +++ b/src/main/java/com/fureniku/metropolis/blocks/IToggleable.java @@ -0,0 +1,12 @@ +package com.fureniku.metropolis.blocks; + +import net.minecraft.core.BlockPos; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.state.BlockState; + +public interface IToggleable { + + void toggleBlock(Level level, BlockPos pos, BlockState state); + + void setToggledState(Level level, BlockPos pos, BlockState state, boolean toggled); +} diff --git a/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockConnectingHorizontal.java b/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockConnectingHorizontal.java index 35ce58f..6e21140 100644 --- a/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockConnectingHorizontal.java +++ b/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockConnectingHorizontal.java @@ -1,6 +1,7 @@ package com.fureniku.metropolis.blocks.decorative; import com.fureniku.metropolis.blocks.decorative.builders.MetroBlockDecorativeConnectingBuilder; +import com.fureniku.metropolis.blocks.decorative.helpers.HelperBase; import com.fureniku.metropolis.datagen.MetroBlockStateProvider; import com.fureniku.metropolis.datagen.TextureSet; import com.fureniku.metropolis.enums.BlockConnectionType; @@ -20,6 +21,8 @@ import net.neoforged.neoforge.client.model.generators.BlockModelBuilder; import net.neoforged.neoforge.registries.RegistryObject; +import java.util.ArrayList; + public class MetroBlockConnectingHorizontal extends MetroBlockDecorative { protected static final BooleanProperty NORTH = BlockStateProperties.NORTH; @@ -45,6 +48,8 @@ public MetroBlockConnectingHorizontal(Properties props, String modelName, String _connectedModelName = connectedModelName; _itemModelName = itemModelName; + public MetroBlockConnectingHorizontal(Properties props, VoxelShape shape, String modelDir, String modelName, BlockOffsetDirection offsetDirection, boolean checkUp, boolean checkDown, TextureSet... textures) { + super(props, shape, modelDir, modelName, new ArrayList(), textures); _checkUp = checkUp; _checkDown = checkDown; _centerFourSided = centerFourSided; @@ -57,7 +62,7 @@ public MetroBlockConnectingHorizontal(Properties props, String modelName, String } public MetroBlockConnectingHorizontal(MetroBlockDecorativeConnectingBuilder builder) { - this(builder.getProps(), builder.getModelName(), builder.getConnectedModelName(), builder.getItemModelName(), builder.getOffsetDirection(), builder.getCheckUp(), builder.getCheckDown(), builder.getCenterFourSided(), builder.getIndependentModelsPerSide(), builder.getShapes(), builder.getTag(), builder.getConnectionType(), builder.getTextures()); + this(builder.getProps(), builder.getShape(), builder.getModelDirectory(), builder.getModelName(), builder.getOffsetDirection(), builder.getCheckUp(), builder.getCheckDown(), builder.getTextures()); } public String getTag() { diff --git a/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockDecorative.java b/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockDecorative.java index 62a6b79..4bcce51 100644 --- a/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockDecorative.java +++ b/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockDecorative.java @@ -1,101 +1,101 @@ package com.fureniku.metropolis.blocks.decorative; +import com.fureniku.metropolis.blocks.IToggleable; import com.fureniku.metropolis.blocks.MetroBlockBase; import com.fureniku.metropolis.blocks.decorative.builders.MetroBlockDecorativeBuilder; +import com.fureniku.metropolis.blocks.decorative.helpers.*; import com.fureniku.metropolis.datagen.MetroBlockStateProvider; import com.fureniku.metropolis.datagen.TextureSet; import com.fureniku.metropolis.enums.BlockOffsetDirection; +import com.fureniku.metropolis.enums.ToggleType; +import com.fureniku.metropolis.utils.SimpleUtils; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; 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.Blocks; import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateDefinition; +import net.minecraft.world.level.block.state.StateHolder; +import net.minecraft.world.level.block.state.properties.BooleanProperty; import net.minecraft.world.phys.Vec3; import net.minecraft.world.phys.shapes.VoxelShape; import net.neoforged.neoforge.client.model.generators.BlockModelBuilder; import net.neoforged.neoforge.registries.RegistryObject; +import java.util.ArrayList; import java.util.Optional; -public class MetroBlockDecorative extends MetroBlockBase { +public class MetroBlockDecorative extends MetroBlockBase implements IToggleable { private final VoxelShape BLOCK_SHAPE; protected TextureSet[] _resources; protected String _modelName; - private BlockOffsetDirection _offsetDirection = BlockOffsetDirection.NONE; + protected String _modelDir; - public MetroBlockDecorative(Properties props, VoxelShape shape, String modelName, BlockOffsetDirection offsetDirection, TextureSet... textures) { - super(offsetDirection == BlockOffsetDirection.NONE ? props : props.dynamicShape()); + private final ArrayList _helpers; + + private RotationHelper _rotationHelper; + private ToggleHelper _toggleHelper; + private OffsetHelper _offsetHelper; + + public MetroBlockDecorative(Properties props, VoxelShape shape, String modelDir, String modelName, ArrayList helpers, TextureSet... textures) { + super(SimpleUtils.containsType(helpers, OffsetHelper.class) ? props.dynamicShape() : props); BLOCK_SHAPE = shape; _resources = textures; + _modelDir = modelDir; _modelName = modelName; - if (offsetDirection != BlockOffsetDirection.NONE) { - _offsetDirection = offsetDirection; + _helpers = helpers; + BlockState stateHolder = this.getStateDefinition().any(); + + for (int i = 0; i < _helpers.size(); i++) { + stateHolder = _helpers.get(i).setDefaultState(stateHolder); + assignHelper(helpers.get(i)); } - } - public MetroBlockDecorative(MetroBlockDecorativeBuilder builder) { - this(builder.getProps(), builder.getShape(), builder.getModelName(), builder.getOffsetDirection(), builder.getTextures()); + this.registerDefaultState(stateHolder); } - @Override - protected Vec3 getOffset(BlockState blockState, BlockGetter level, BlockPos pos) { - double x = 0; - double y = 0; - double z = 0; - switch (_offsetDirection) { - case NONE -> { - return Vec3.ZERO; - } - case DOWN -> { - y = getOffsetBlockPosValue(level, pos.below(), Direction.Axis.Y, true); - } - case BACK -> { - } - case LEFT -> { - } - case FORWARD -> { - } - case RIGHT -> { - } - case UP -> { - y = getOffsetBlockPosValue(level, pos.above(), Direction.Axis.Y, false); - } + private void assignHelper(HelperBase helper) { + switch (helper.getType()) { + case OFFSET -> _offsetHelper = (OffsetHelper) helper; + case ROTATION -> _rotationHelper = (RotationHelper) helper; + case TOGGLE -> _toggleHelper = (ToggleHelper) helper; } - double finalX = x; - double finalY = y; - double finalZ = z; - blockState.offsetFunction = Optional.of((state, lvl, blockPos) -> new Vec3(finalX, finalY, finalZ)); - return new Vec3(x, y, z); } - private double getOffsetBlockPosValue(BlockGetter level, BlockPos pos, Direction.Axis axis, boolean positive) { - if (level.getBlockState(pos).getBlock() == Blocks.AIR) { - return 0; - } - VoxelShape shape = level.getBlockState(pos).getShape(level, pos); - return positive ? (1 - shape.max(axis)) * -1 : shape.min(axis); + @Override + protected void createBlockState(StateDefinition.Builder builder) { + if (_toggleHelper != null) builder.add(_toggleHelper.TOGGLED); + if (_rotationHelper != null) builder.add(_rotationHelper.DIRECTION); } @Override protected VoxelShape getShapeFromBlockState(BlockState pState) { + //TODO how do we handle this now return BLOCK_SHAPE; } @Override public void generateBlockState(RegistryObject blockRegistryObject, MetroBlockStateProvider blockStateProvider) { Block block = blockRegistryObject.get(); - BlockModelBuilder bmb = blockStateProvider.getModelFilesWithTexture(block, "", "blocks/decorative/" + _modelName, _resources[0].getTexture()); - if (_resources.length > 1) { - for (int i = 1; i < _resources.length; i++) { - bmb = bmb.texture(_resources[i].getKey(), _resources[i].getTexture()); + BlockModelBuilder bmb = prepareModels(block, blockStateProvider); + boolean generatedBlock = false; + for (int i = 0; i < _helpers.size(); i++) { + if (_helpers.get(i) instanceof HelperBlockstate) { + HelperBlockstate helper = (HelperBlockstate) _helpers.get(i); + //helper.generateBlockstate(); + generatedBlock = true; } } blockStateProvider.simpleBlockWithItem(block, applyTexturesToModels(bmb)[0]); } + //TODO resolve protected BlockModelBuilder applyTexturesToModel(BlockModelBuilder bmb) { if (_resources.length > 1) { for (int i = 1; i < _resources.length; i++) { @@ -105,6 +105,7 @@ protected BlockModelBuilder applyTexturesToModel(BlockModelBuilder bmb) { return bmb; } +//TODO resolve protected BlockModelBuilder[] applyTexturesToModels(BlockModelBuilder... bmb) { if (_resources.length > 1) { for (int i = 0; i < bmb.length; i++) { @@ -114,5 +115,67 @@ protected BlockModelBuilder[] applyTexturesToModels(BlockModelBuilder... bmb) { } } return bmb; + if (!generatedBlock) { + blockStateProvider.simpleBlock(block, bmb); + } + blockStateProvider.simpleBlockItem(block, bmb); + } + +//TODO resolve + public BlockModelBuilder prepareModels(Block block, MetroBlockStateProvider blockStateProvider) { + BlockModelBuilder bmb; + if (_modelName == null || _resources == null) { + bmb = blockStateProvider.getModelFilesWithTexture(block, "", _modelDir + block.getName(), blockStateProvider.modLoc("blocks/decorative/" + block.getName())); + } else { + bmb = blockStateProvider.getModelFilesWithTexture(block, "", _modelDir + _modelName, _resources[0].getTexture()); + if (_resources.length > 1) { + for (int i = 1; i < _resources.length; i++) { + bmb = bmb.texture(_resources[i].getKey(), _resources[i].getTexture()); + } + } + } + return bmb; + } + + //region Offset + @Override + protected Vec3 getOffset(BlockState blockState, BlockGetter level, BlockPos pos) { + if (_offsetHelper != null) { + Vec3 offset = _offsetHelper.getOffset(level, pos); + blockState.offsetFunction = Optional.of((state, lvl, blockPos) -> offset); + return offset; + } + return Vec3.ZERO; + } + //endregion + + //region Togglable + @Override + public void onNeighbourChanged(BlockState state, Level level, BlockPos pos, Block block, BlockPos neighborPos) { + if (_toggleHelper != null) { + _toggleHelper.neighbourChanged(state, level, pos, this); + } + } + + @Override + protected void onRightClickRemote(BlockState state, BlockPos pos, Player player) { + if (_toggleHelper != null) { + _toggleHelper.rightClick(state, pos, player, this); + } + } + + @Override + public void toggleBlock(Level level, BlockPos pos, BlockState state) { + if (_toggleHelper != null) { + _toggleHelper.toggleBlock(state); + } + } + + @Override + public void setToggledState(Level level, BlockPos pos, BlockState state, boolean toggled) { + if (_toggleHelper != null) { + setBlock(level, pos, _toggleHelper.setToggledState(state, toggled)); + } } + //endregion } diff --git a/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockDecorativeRotatable.java b/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockDecorativeRotatable.java index d3a0272..8600f40 100644 --- a/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockDecorativeRotatable.java +++ b/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockDecorativeRotatable.java @@ -32,8 +32,8 @@ public class MetroBlockDecorativeRotatable extends MetroBlockDecorative { * @param props * @param shape */ - public MetroBlockDecorativeRotatable(Properties props, VoxelShape shape, String modelName, BlockOffsetDirection offsetDirection, TextureSet... textures) { - super(props, shape, modelName, offsetDirection, textures); + public MetroBlockDecorativeRotatable(Properties props, VoxelShape shape, String modelDir, String modelName, BlockOffsetDirection offsetDirection, TextureSet... textures) { + super(props, shape, modelDir, modelName, offsetDirection, textures); BLOCK_SHAPE_NORTH = shape; BLOCK_SHAPE_EAST = ShapeUtils.rotateVoxelShape(shape, Direction.EAST); BLOCK_SHAPE_SOUTH = ShapeUtils.rotateVoxelShape(shape, Direction.SOUTH); @@ -42,23 +42,12 @@ public MetroBlockDecorativeRotatable(Properties props, VoxelShape shape, String } public MetroBlockDecorativeRotatable(MetroBlockDecorativeBuilder builder) { - this(builder.getProps(), builder.getShape(), builder.getModelName(), builder.getOffsetDirection(), builder.getTextures()); + this(builder.getProps(), builder.getShape(), builder.getModelDirectory(), builder.getModelName(), builder.getOffsetDirection(), builder.getTextures()); } @Override public void generateBlockState(RegistryObject blockRegistryObject, MetroBlockStateProvider blockStateProvider) { - Block block = blockRegistryObject.get(); - BlockModelBuilder bmb; - if (_modelName == null || _resources == null) { - bmb = blockStateProvider.getModelFilesWithTexture(block, "", "blocks/decorative/" + block.getName(), blockStateProvider.modLoc("blocks/decorative/" + block.getName())); - } else { - bmb = blockStateProvider.getModelFilesWithTexture(block, "", "blocks/decorative/" + _modelName, _resources[0].getTexture()); - if (_resources.length > 1) { - for (int i = 1; i < _resources.length; i++) { - bmb = bmb.texture(_resources[i].getKey(), _resources[i].getTexture()); - } - } - } + prepareModels(blockRegistryObject.get(), blockStateProvider); blockStateProvider.horizontalBlock(block, bmb); } diff --git a/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockDecorativeRotatableFloorWall.java b/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockDecorativeRotatableFloorWall.java new file mode 100644 index 0000000..9c1334e --- /dev/null +++ b/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockDecorativeRotatableFloorWall.java @@ -0,0 +1,98 @@ +package com.fureniku.metropolis.blocks.decorative; + +import com.fureniku.metropolis.blocks.decorative.builders.MetroBlockDecorativeBuilder; +import com.fureniku.metropolis.datagen.MetroBlockStateProvider; +import com.fureniku.metropolis.datagen.TextureSet; +import com.fureniku.metropolis.enums.BlockOffsetDirection; +import com.fureniku.metropolis.enums.DirectionFloorWall; +import com.fureniku.metropolis.utils.SimpleUtils; +import net.minecraft.core.Direction; +import net.minecraft.world.item.context.BlockPlaceContext; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.HorizontalDirectionalBlock; +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.block.state.properties.EnumProperty; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.neoforged.neoforge.client.model.generators.BlockModelBuilder; +import net.neoforged.neoforge.registries.RegistryObject; + +/** + * Decorative non-full block which can be rotated + */ +public class MetroBlockDecorativeRotatableFloorWall extends MetroBlockDecorative { + + public static final EnumProperty DIRECTION = EnumProperty.create("direction", DirectionFloorWall.class); + private final VoxelShape BLOCK_SHAPE_FLOOR; + private final VoxelShape BLOCK_SHAPE_NORTH; + private final VoxelShape BLOCK_SHAPE_EAST; + private final VoxelShape BLOCK_SHAPE_SOUTH; + private final VoxelShape BLOCK_SHAPE_WEST; + + /** + * Constructor for decorative blocks which have a specific shape. This shape is rotated automatically. + * @param props + * @param shapeFloor The shape of the object when on the floor. The same shape is used for all rotations. + * @param shapeWall The shape of the object on the wall. Rotated automatically from the north side shape. + */ + public MetroBlockDecorativeRotatableFloorWall(Properties props, VoxelShape shapeFloor, VoxelShape shapeWall, String modelDir, String modelName, BlockOffsetDirection offsetDirection, TextureSet... textures) { + super(props, shapeFloor, modelDir, modelName, offsetDirection, textures); + BLOCK_SHAPE_FLOOR = shapeFloor; + BLOCK_SHAPE_NORTH = shapeWall; + BLOCK_SHAPE_EAST = SimpleUtils.rotateVoxelShape(shapeWall, Direction.EAST); + BLOCK_SHAPE_SOUTH = SimpleUtils.rotateVoxelShape(shapeWall, Direction.SOUTH); + BLOCK_SHAPE_WEST = SimpleUtils.rotateVoxelShape(shapeWall, Direction.WEST); + this.registerDefaultState(this.stateDefinition.any().setValue(DIRECTION, DirectionFloorWall.FLOOR_NORTH)); + } + + @Override + public void generateBlockState(RegistryObject blockRegistryObject, MetroBlockStateProvider blockStateProvider) { + Block block = blockRegistryObject.get(); + BlockModelBuilder bmb; + if (_modelName == null || _resources == null) { + bmb = blockStateProvider.getModelFilesWithTexture(block, "", _modelDir + block.getName(), blockStateProvider.modLoc("blocks/decorative/" + block.getName())); + } else { + bmb = blockStateProvider.getModelFilesWithTexture(block, "", _modelDir + _modelName, _resources[0].getTexture()); + if (_resources.length > 1) { + for (int i = 1; i < _resources.length; i++) { + bmb = bmb.texture(_resources[i].getKey(), _resources[i].getTexture()); + } + } + } + + blockStateProvider.horizontalFloorWallBlock(block, bmb); + } + + @Override + protected BlockState getPlacementState(BlockPlaceContext context) { + BlockState blockstate; + if (context.getClickedFace().getAxis() != Direction.Axis.Y) { + blockstate = this.defaultBlockState().setValue(DIRECTION, DirectionFloorWall.getWallDirection(context.getHorizontalDirection())); + } else { + blockstate = this.defaultBlockState().setValue(DIRECTION, DirectionFloorWall.getFloorDirection(context.getHorizontalDirection())); + } + + return blockstate; + } + + @Override + protected void createBlockState(StateDefinition.Builder builder) { + builder.add(DIRECTION); + } + + @Override + protected VoxelShape getShapeFromBlockState(BlockState pState) { + switch (pState.getValue(DIRECTION)) { + case WALL_NORTH: + return BLOCK_SHAPE_NORTH; + case WALL_EAST: + return BLOCK_SHAPE_EAST; + case WALL_SOUTH: + return BLOCK_SHAPE_SOUTH; + case WALL_WEST: + return BLOCK_SHAPE_WEST; + } + return BLOCK_SHAPE_FLOOR; + } +} diff --git a/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockDecorativeRotatableToggle.java b/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockDecorativeRotatableToggle.java index 322921f..792acca 100644 --- a/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockDecorativeRotatableToggle.java +++ b/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockDecorativeRotatableToggle.java @@ -56,17 +56,17 @@ public class MetroBlockDecorativeRotatableToggle extends MetroBlockDecorativeRot * @param item * @param textures */ - public MetroBlockDecorativeRotatableToggle(Properties props, VoxelShape shape, VoxelShape shapeToggled, String modelName, String toggledModelName, Item item, BlockOffsetDirection offsetDirection, TextureSet... textures) { - this(props, shape, shapeToggled, modelName, toggledModelName, ToggleType.ITEM, offsetDirection, textures); + public MetroBlockDecorativeRotatableToggle(Properties props, VoxelShape shape, VoxelShape shapeToggled, String modelDir, String modelName, String toggledModelName, Item item, BlockOffsetDirection offsetDirection, TextureSet... textures) { + this(props, shape, shapeToggled, modelDir, modelName, toggledModelName, ToggleType.ITEM, offsetDirection, textures); _item = item; } public MetroBlockDecorativeRotatableToggle(MetroBlockDecorativeToggleBuilder builder, Item item) { - this(builder.getProps(), builder.getShape(), builder.getShapeToggled(), builder.getModelName(), builder.getToggledModelName(), item, builder.getOffsetDirection(), builder.getTextures()); + this(builder.getProps(), builder.getShape(), builder.getShapeToggled(), builder.getModelDirectory(), builder.getModelName(), builder.getToggledModelName(), item, builder.getOffsetDirection(), builder.getTextures()); } public MetroBlockDecorativeRotatableToggle(MetroBlockDecorativeToggleBuilder builder) { - this(builder.getProps(), builder.getShape(), builder.getShapeToggled(), builder.getModelName(), builder.getToggledModelName(), builder.getToggleType(), builder.getOffsetDirection(), builder.getTextures()); + this(builder.getProps(), builder.getShape(), builder.getShapeToggled(), builder.getModelDirectory(), builder.getModelName(), builder.getToggledModelName(), builder.getToggleType(), builder.getOffsetDirection(), builder.getTextures()); } /** @@ -74,8 +74,8 @@ public MetroBlockDecorativeRotatableToggle(MetroBlockDecorativeToggleBuilder bui * @param props * @param shape */ - public MetroBlockDecorativeRotatableToggle(Properties props, VoxelShape shape, VoxelShape shapeToggled, String modelName, String toggledModelName, ToggleType type, BlockOffsetDirection offsetDirection, TextureSet... textures) { - super(props, shape, modelName, offsetDirection, textures); + public MetroBlockDecorativeRotatableToggle(Properties props, VoxelShape shape, VoxelShape shapeToggled, String modelDir, String modelName, String toggledModelName, ToggleType type, BlockOffsetDirection offsetDirection, TextureSet... textures) { + super(props, shape, modelDir, modelName, offsetDirection, textures); BLOCK_SHAPE_NORTH = shape; BLOCK_SHAPE_EAST = ShapeUtils.rotateVoxelShape(shape, Direction.EAST); BLOCK_SHAPE_SOUTH = ShapeUtils.rotateVoxelShape(shape, Direction.SOUTH); @@ -91,13 +91,7 @@ public MetroBlockDecorativeRotatableToggle(Properties props, VoxelShape shape, V _type = type; } - protected void toggleBlock(Level level, BlockPos pos, BlockState state) { - setToggledState(level, pos, state, !state.getValue(TOGGLED)); - } - protected void setToggledState(Level level, BlockPos pos, BlockState state, boolean toggled) { - setBlock(level, pos, state.setValue(TOGGLED, toggled)); - } @Override public void generateBlockState(RegistryObject blockRegistryObject, MetroBlockStateProvider blockStateProvider) { diff --git a/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockGenerated.java b/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockGenerated.java index 41a2124..c55171f 100644 --- a/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockGenerated.java +++ b/src/main/java/com/fureniku/metropolis/blocks/decorative/MetroBlockGenerated.java @@ -12,8 +12,8 @@ */ public class MetroBlockGenerated extends MetroBlockDecorative { - public MetroBlockGenerated(Properties props, VoxelShape shape, String modelName, BlockOffsetDirection offsetDirection, TextureSet... textures) { - super(props, shape, modelName, offsetDirection, textures); + public MetroBlockGenerated(Properties props, VoxelShape shape, String modelDir, String modelName, BlockOffsetDirection offsetDirection, TextureSet... textures) { + super(props, shape, modelDir, modelName, offsetDirection, textures); } public static void registerBlockStates() { diff --git a/src/main/java/com/fureniku/metropolis/blocks/decorative/builders/MetroBlockDecorativeBuilder.java b/src/main/java/com/fureniku/metropolis/blocks/decorative/builders/MetroBlockDecorativeBuilder.java index e1df838..b656ca4 100644 --- a/src/main/java/com/fureniku/metropolis/blocks/decorative/builders/MetroBlockDecorativeBuilder.java +++ b/src/main/java/com/fureniku/metropolis/blocks/decorative/builders/MetroBlockDecorativeBuilder.java @@ -14,6 +14,7 @@ public class MetroBlockDecorativeBuilder { + return Vec3.ZERO; + } + case DOWN -> { + y = getOffsetBlockPosValue(level, pos.below(), Direction.Axis.Y, true); + } + case BACK -> { + } + case LEFT -> { + } + case FORWARD -> { + } + case RIGHT -> { + } + case UP -> { + y = getOffsetBlockPosValue(level, pos.above(), Direction.Axis.Y, false); + } + } + double finalX = x; + double finalY = y; + double finalZ = z; + return new Vec3(x, y, z); + } + + private double getOffsetBlockPosValue(BlockGetter level, BlockPos pos, Direction.Axis axis, boolean positive) { + if (level.getBlockState(pos).getBlock() == Blocks.AIR) { + return 0; + } + VoxelShape shape = level.getBlockState(pos).getShape(level, pos); + return positive ? (1 - shape.max(axis)) * -1 : shape.min(axis); + } +} diff --git a/src/main/java/com/fureniku/metropolis/blocks/decorative/helpers/RotationHelper.java b/src/main/java/com/fureniku/metropolis/blocks/decorative/helpers/RotationHelper.java new file mode 100644 index 0000000..04a8f01 --- /dev/null +++ b/src/main/java/com/fureniku/metropolis/blocks/decorative/helpers/RotationHelper.java @@ -0,0 +1,44 @@ +package com.fureniku.metropolis.blocks.decorative.helpers; + +import com.fureniku.metropolis.blocks.decorative.MetroBlockDecorative; +import com.fureniku.metropolis.enums.HelperType; +import com.fureniku.metropolis.utils.SimpleUtils; +import net.minecraft.core.Direction; +import net.minecraft.world.level.block.HorizontalDirectionalBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.StateHolder; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.level.block.state.properties.Property; +import net.minecraft.world.phys.shapes.VoxelShape; + +public class RotationHelper extends HelperBlockstate { + + public static final DirectionProperty DIRECTION = HorizontalDirectionalBlock.FACING; + private final VoxelShape BLOCK_SHAPE_NORTH; + private final VoxelShape BLOCK_SHAPE_EAST; + private final VoxelShape BLOCK_SHAPE_SOUTH; + private final VoxelShape BLOCK_SHAPE_WEST; + + public RotationHelper(VoxelShape shape, MetroBlockDecorative block, StateHolder state) { + BLOCK_SHAPE_NORTH = shape; + BLOCK_SHAPE_EAST = SimpleUtils.rotateVoxelShape(shape, Direction.EAST); + BLOCK_SHAPE_SOUTH = SimpleUtils.rotateVoxelShape(shape, Direction.SOUTH); + BLOCK_SHAPE_WEST = SimpleUtils.rotateVoxelShape(shape, Direction.WEST); + } + + @Override + public BlockState setDefaultState(BlockState state) { + state.setValue(DIRECTION, Direction.NORTH); + return state; + } + + @Override + public HelperType getType() { + return HelperType.ROTATION; + } + + @Override + public Property getProperty() { + return DIRECTION; + } +} diff --git a/src/main/java/com/fureniku/metropolis/blocks/decorative/helpers/ToggleHelper.java b/src/main/java/com/fureniku/metropolis/blocks/decorative/helpers/ToggleHelper.java new file mode 100644 index 0000000..c4f190a --- /dev/null +++ b/src/main/java/com/fureniku/metropolis/blocks/decorative/helpers/ToggleHelper.java @@ -0,0 +1,109 @@ +package com.fureniku.metropolis.blocks.decorative.helpers; + +import com.fureniku.metropolis.blocks.MetroBlockBase; +import com.fureniku.metropolis.blocks.decorative.MetroBlockDecorative; +import com.fureniku.metropolis.datagen.MetroBlockStateProvider; +import com.fureniku.metropolis.datagen.TextureSet; +import com.fureniku.metropolis.enums.HelperType; +import com.fureniku.metropolis.enums.ToggleType; +import net.minecraft.core.BlockPos; +import net.minecraft.world.InteractionHand; +import net.minecraft.world.entity.player.Player; +import net.minecraft.world.item.Item; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BooleanProperty; +import net.minecraft.world.level.block.state.properties.Property; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.neoforged.neoforge.client.model.generators.BlockModelBuilder; +import net.neoforged.neoforge.client.model.generators.ConfiguredModel; + +public class ToggleHelper extends HelperBlockstate { + + public static final BooleanProperty TOGGLED = BooleanProperty.create("toggled"); + private VoxelShape BLOCK_SHAPE_TOGGLED; + protected String _toggledModelName; + private Item _item; + private ToggleType _type; + + public ToggleHelper(VoxelShape shape, String modelName, ToggleType type) { + BLOCK_SHAPE_TOGGLED = shape; + _toggledModelName = modelName; + _type = type; + } + + public ToggleHelper(VoxelShape shape, String modelName, Item item) { + this(shape, modelName, ToggleType.ITEM); + _item = item; + } + + @Override + public BlockState setDefaultState(BlockState state) { + state.setValue(TOGGLED, false); + return state; + } + + @Override + public HelperType getType() { + return HelperType.TOGGLE; + } + + @Override + public Property getProperty() { + return TOGGLED; + } + + public void neighbourChanged(BlockState state, Level level, BlockPos pos, MetroBlockDecorative block) { + if (!level.isClientSide && (_type == ToggleType.REDSTONE || _type == ToggleType.REDSTONE_INTERACT)) { + block.setToggledState(level, pos, state, level.hasNeighborSignal(pos)); + } + } + + public void rightClick(BlockState state, BlockPos pos, Player player, MetroBlockDecorative block) { + switch (_type) { + case REDSTONE_INTERACT: + case INTERACT: + block.toggleBlock(player.level(), pos, state); + return; + case ITEM: + if (_item != null) { + ItemStack heldItem = player.getItemInHand(InteractionHand.MAIN_HAND); + if (heldItem.getItem() == _item) { + block.toggleBlock(player.level(), pos, state); + } + } + } + } + + public BlockState toggleBlock(BlockState state) { + return setToggledState(state, !state.getValue(TOGGLED)); + } + + public BlockState setToggledState(BlockState state, boolean toggled) { + return state.setValue(TOGGLED, toggled); + } + + @Override + public void generateBlockstate(TextureSet[] resources, String modelName, Block block, MetroBlockStateProvider blockStateProvider) { + BlockModelBuilder modelNormal = blockStateProvider.getModelFilesWithTexture(block, "_standard", "blocks/decorative/" + modelName, resources[0].getTexture()); + BlockModelBuilder modelToggled = blockStateProvider.getModelFilesWithTexture(block, "_toggled", "blocks/decorative/" + _toggledModelName, resources[0].getTexture()); + + if (resources.length > 1) { + for (int i = 1; i < resources.length; i++) { + modelNormal = modelNormal.texture(resources[i].getKey(), resources[i].getTexture()); + modelToggled = modelToggled.texture(resources[i].getKey(), resources[i].getTexture()); + } + } + + BlockModelBuilder finalModelNormal = modelNormal; + BlockModelBuilder finalModelToggled = modelToggled; + blockStateProvider.getVariantBuilder(block) + .forAllStates(state -> ConfiguredModel.builder() + .modelFile(state.getValue(TOGGLED) ? finalModelToggled : finalModelNormal) + .build()); + + blockStateProvider.simpleBlockItem(block, modelNormal); + } +} diff --git a/src/main/java/com/fureniku/metropolis/datagen/MetroBlockStateProvider.java b/src/main/java/com/fureniku/metropolis/datagen/MetroBlockStateProvider.java index 224223d..0c47d24 100644 --- a/src/main/java/com/fureniku/metropolis/datagen/MetroBlockStateProvider.java +++ b/src/main/java/com/fureniku/metropolis/datagen/MetroBlockStateProvider.java @@ -3,6 +3,7 @@ import com.fureniku.metropolis.RegistrationBase; import com.fureniku.metropolis.blocks.MetroBlockBase; import com.fureniku.metropolis.blocks.decorative.MetroBlockDecorativeRotatable; +import com.fureniku.metropolis.blocks.decorative.MetroBlockDecorativeRotatableFloorWall; import com.fureniku.metropolis.client.rendering.MetroLoaderBuilder; import com.fureniku.metropolis.utils.Debug; import net.minecraft.core.Direction; @@ -11,12 +12,15 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.CrossCollisionBlock; import net.minecraft.world.level.block.PipeBlock; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.block.state.properties.BlockStateProperties; import net.neoforged.neoforge.client.model.generators.*; import net.neoforged.neoforge.common.data.ExistingFileHelper; import net.neoforged.neoforge.registries.ForgeRegistries; import net.neoforged.neoforge.registries.RegistryObject; import java.util.Collection; +import java.util.function.Function; /** * Metropolis Block State Provider. Should be usable as-is without needing to subclass. @@ -55,6 +59,15 @@ public void horizontalBlock(Block block, ModelFile model) { simpleBlockItem(block, model); } + public void horizontalFloorWallBlock(Block block, ModelFile model) { + getVariantBuilder(block) + .forAllStates(state -> ConfiguredModel.builder() + .modelFile(model) + .rotationX(state.getValue(MetroBlockDecorativeRotatableFloorWall.DIRECTION).toXRot()) + .rotationY(state.getValue(MetroBlockDecorativeRotatableFloorWall.DIRECTION).toYRot() + 180) + .build()); + } + /** * Get a modelled block, with a matching itemblock. Uses your blocks name to get the matching texture. * @param block Your block diff --git a/src/main/java/com/fureniku/metropolis/enums/DirectionFloorWall.java b/src/main/java/com/fureniku/metropolis/enums/DirectionFloorWall.java new file mode 100644 index 0000000..6e91eaf --- /dev/null +++ b/src/main/java/com/fureniku/metropolis/enums/DirectionFloorWall.java @@ -0,0 +1,71 @@ +package com.fureniku.metropolis.enums; + +import net.minecraft.core.Direction; +import net.minecraft.util.StringRepresentable; + +public enum DirectionFloorWall implements StringRepresentable { + FLOOR_NORTH, + FLOOR_EAST, + FLOOR_SOUTH, + FLOOR_WEST, + WALL_NORTH, + WALL_EAST, + WALL_SOUTH, + WALL_WEST; + + @Override + public String getSerializedName() { + return this.name().toLowerCase(); + } + + public static DirectionFloorWall getWallDirection(Direction dir) { + return getDirection(dir, WALL_NORTH, WALL_EAST, WALL_SOUTH, WALL_WEST); + } + + public static DirectionFloorWall getFloorDirection(Direction dir) { + return getDirection(dir, FLOOR_NORTH, FLOOR_EAST, FLOOR_SOUTH, FLOOR_WEST); + } + + private static DirectionFloorWall getDirection(Direction dir, DirectionFloorWall north, DirectionFloorWall east, DirectionFloorWall south, DirectionFloorWall west) { + switch (dir) { + case NORTH -> { + return north; + } + case EAST -> { + return east; + } + case SOUTH -> { + return south; + } + case WEST -> { + return west; + } + } + return FLOOR_NORTH; + } + + public int toXRot() { + if (this == FLOOR_NORTH || this == FLOOR_EAST || this == FLOOR_SOUTH || this == FLOOR_WEST) { + return 0; + } + return 90; + } + + public int toYRot() { + switch (this) { + case FLOOR_NORTH: + case WALL_NORTH: + return 0; + case FLOOR_EAST: + case WALL_EAST: + return 90; + case FLOOR_SOUTH: + case WALL_SOUTH: + return 180; + case FLOOR_WEST: + case WALL_WEST: + return 270; + } + return 0; + } +} diff --git a/src/main/java/com/fureniku/metropolis/enums/HelperType.java b/src/main/java/com/fureniku/metropolis/enums/HelperType.java new file mode 100644 index 0000000..a778872 --- /dev/null +++ b/src/main/java/com/fureniku/metropolis/enums/HelperType.java @@ -0,0 +1,7 @@ +package com.fureniku.metropolis.enums; + +public enum HelperType { + TOGGLE, + ROTATION, + OFFSET +} diff --git a/src/main/java/com/fureniku/metropolis/utils/SimpleUtils.java b/src/main/java/com/fureniku/metropolis/utils/SimpleUtils.java index ad937c1..e3a5a76 100644 --- a/src/main/java/com/fureniku/metropolis/utils/SimpleUtils.java +++ b/src/main/java/com/fureniku/metropolis/utils/SimpleUtils.java @@ -8,6 +8,8 @@ import net.minecraft.world.level.block.Block; import net.minecraft.world.phys.shapes.VoxelShape; +import java.util.ArrayList; + public class SimpleUtils { //This class was technically the start of Metropolis. @@ -53,4 +55,63 @@ public static ItemStack createDamagedItemStack(ItemLike item, int damage) { stack.setDamageValue(damage); return stack; } + + /** + * Rotate a VoxelShape horizontally to the new direction. Assumes the passed one is facing north, + * so east is 90, south 180 and west 270 degrees. + * @param shape Original shape + * @param newDirection Target direction + * @return New shape + */ + /*public static VoxelShape rotateVoxelShape(VoxelShape shape, Direction newDirection) { + if (newDirection != Direction.NORTH) { + double xMin = shape.min(Direction.Axis.X); + double xMax = 1 - shape.max(Direction.Axis.X); + double yMin = shape.min(Direction.Axis.Y); + double yMax = 1 - shape.max(Direction.Axis.Y); + double zMin = shape.min(Direction.Axis.Z); + double zMax = 1 - shape.max(Direction.Axis.Z); + switch (newDirection) { + case EAST: + return createNewShape(zMax, yMin, xMin, zMin, yMax, xMax); + case SOUTH: + return createNewShape(xMax, yMin, zMax, xMin, yMax, zMin); + case WEST: + return createNewShape(zMin, yMin, xMax, zMax, yMax, xMin); + } + } + return shape; + } + + private static VoxelShape createNewShape(double xMin, double yMin, double zMin, double xMax, double yMax, double zMax) { + return Block.box(xMin * 16, yMin * 16, zMin * 16, 16 - (xMax * 16), 16 - (yMax * 16), 16 - (zMax * 16)); + } + */ //TODO moved to shape utils? + + /** + * Get a VoxelShape and convert it to a printable string with the min/max points of each axis. + * @param shape VoxelShape + * @return A string to print with the logger + */ + public static String getPrintableShape(VoxelShape shape) { + return "X: " + shape.min(Direction.Axis.X) + " - " + shape.max(Direction.Axis.X) + + " Y: " + shape.min(Direction.Axis.Y) + " - " + shape.max(Direction.Axis.Y) + + " Z: " + shape.min(Direction.Axis.Z) + " - " + shape.max(Direction.Axis.Z); + } + + /** + * Check if an arraylist contains an object which is an instance of a subclass of the list type. + * @param list The arraylist + * @param type the type to check + * @return whether an instance of that object exists in the list + * @param + */ + public static boolean containsType(ArrayList list, Class type) { + for (T obj : list) { + if (type.isInstance(obj)) { + return true; + } + } + return false; + } }