diff --git a/src/main/java/com/fureniku/metropolis/RegistrationBase.java b/src/main/java/com/fureniku/metropolis/RegistrationBase.java index eb73182..c256193 100644 --- a/src/main/java/com/fureniku/metropolis/RegistrationBase.java +++ b/src/main/java/com/fureniku/metropolis/RegistrationBase.java @@ -7,6 +7,8 @@ import net.minecraft.world.level.block.Block; import net.neoforged.bus.api.IEventBus; import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.event.lifecycle.FMLClientSetupEvent; +import net.neoforged.fml.event.lifecycle.FMLCommonSetupEvent; import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; import net.neoforged.neoforge.registries.DeferredRegister; import net.neoforged.neoforge.registries.ForgeRegistries; @@ -39,6 +41,8 @@ public RegistrationBase(String modid, IEventBus modEventBus) { blockRegistry = DeferredRegister.create(ForgeRegistries.BLOCKS, modid); itemRegistry = DeferredRegister.create(ForgeRegistries.ITEMS, modid); creativeTabs = DeferredRegister.create(Registries.CREATIVE_MODE_TAB, modid); + modEventBus.addListener(this::common); + modEventBus.addListener(this::client); modEventBus.addListener(this::buildCreativeTabs); blockRegistry.register(modEventBus); itemRegistry.register(modEventBus); @@ -163,4 +167,51 @@ public void buildCreativeTabs(BuildCreativeModeTabContentsEvent event) { } } } + + @SubscribeEvent + private void common(final FMLCommonSetupEvent event) { + generateCreativeTabs(); + commonSetup(event); + } + + @SubscribeEvent + private void client(final FMLClientSetupEvent event) { + clientSetup(event); + } + + /** + * Common setup event. Handle setup stuff for client and server here. + * @param event Common setup event + */ + protected abstract void commonSetup(final FMLCommonSetupEvent event); + + /** + * Client setup event. Handle client-only setup here (e.g. rendering) + * @param event Client setup event + */ + protected abstract void clientSetup(final FMLClientSetupEvent event); + + /** + * Getter for the block deferred register + * @return + */ + public DeferredRegister getBlockDeferredRegister() { + return blockRegistry; + } + + /** + * Getter for the item deferred register + * @return + */ + public DeferredRegister getItemDeferredRegister() { + return itemRegistry; + } + + /** + * Getter for the creative tab deferred register + * @return + */ + public DeferredRegister getCreativeTabDeferredRegister() { + return creativeTabs; + } } diff --git a/src/main/java/com/fureniku/metropolis/RegistrationGroup.java b/src/main/java/com/fureniku/metropolis/RegistrationGroup.java new file mode 100644 index 0000000..99a467b --- /dev/null +++ b/src/main/java/com/fureniku/metropolis/RegistrationGroup.java @@ -0,0 +1,88 @@ +package com.fureniku.metropolis; + +import com.fureniku.metropolis.utils.CreativeTabSet; +import com.fureniku.metropolis.utils.Debug; +import net.minecraft.core.registries.Registries; +import net.minecraft.world.item.BlockItem; +import net.minecraft.world.item.CreativeModeTab; +import net.minecraft.world.item.Item; +import net.minecraft.world.level.block.Block; +import net.neoforged.bus.api.IEventBus; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.neoforge.event.BuildCreativeModeTabContentsEvent; +import net.neoforged.neoforge.registries.DeferredRegister; +import net.neoforged.neoforge.registries.ForgeRegistries; +import net.neoforged.neoforge.registries.RegistryObject; + +import javax.annotation.Nullable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.function.Supplier; + +/** + * Class to keep RegistrationBase derivitive classes a bit cleaner. Group your blocks into these classes, if you want to. + */ +public abstract class RegistrationGroup { + + protected final RegistrationBase registration; + + /** + * Constructor. + * @param registrationBase the core registration class of your mod + */ + public RegistrationGroup(RegistrationBase registrationBase) { + registration = registrationBase; + } + + /** + * Initialize the registration, and register all your stuff. Cannot be called from the constructor! + * Handle the creation of blocks in here - either creating blocksets, or call registerBlockSet(name, BlockClass::new) + * Handle the creation of items + * Handle the creation of creative tabs (new creativetabset, pass creativeTabs as first arg) + * Do anything else you want at this stage. + * @param modEventBus + */ + public abstract void init(IEventBus modEventBus); + + /** + * Add all your blocks to the created creative tabs in here! + *

It may be neater to create a function for each tab, and call those functions from here.

+ *

Tab registry is done using yourTab.addItem(ItemStack)

+ *

A good example of achieving that here could be yourTab.addItem(getItem("item_name").get().getDefaultInstance())

+ *

If you're using BlockSets, you can instead call blockSet.registerToCreativeTab(yourTab) to add all blocks in the set to the tab.

+ */ + public abstract void generateCreativeTabs(); + + /** + * Create an ArrayList of all your creative tabs here. + * @return an ArrayList{@literal } with all your creative tabs in + */ + public abstract ArrayList getCreativeTabs(); + + /** + * Passthrough to register a blockset + * @param name blockset name + * @param blockClass block's class + */ + protected void registerBlockSet(String name, Supplier blockClass) { + registration.registerBlockSet(name, blockClass); + } + + /** + * Passthrough to get a block + * @param key block name + * @return registry object of the block + */ + protected RegistryObject getBlock(String key) { + return registration.getBlock(key); + } + + /** + * Passthrough to get a item + * @param key item name + * @return registry object of the item + */ + protected RegistryObject getItem(String key) { + return registration.getItem(key); + } +} diff --git a/src/main/java/com/fureniku/metropolis/blocks/MetroBlockBase.java b/src/main/java/com/fureniku/metropolis/blocks/MetroBlockBase.java index 56ecd45..302da44 100644 --- a/src/main/java/com/fureniku/metropolis/blocks/MetroBlockBase.java +++ b/src/main/java/com/fureniku/metropolis/blocks/MetroBlockBase.java @@ -90,6 +90,7 @@ protected void onRightClickRemote(BlockState state, BlockPos pos, Player player) /** * Create the blockstate file for this block! Used by datagen. Override if the block is anything other than a normal, boring, full size block. + * For blocks that you don't want to datagen, override this with no function body to skip. * @param blockRegistryObject the block's registryobject * @param blockStateProvider the blockstate provider class, for some helper functions. */ diff --git a/src/main/java/com/fureniku/metropolis/blocks/MetroBlockDecorative.java b/src/main/java/com/fureniku/metropolis/blocks/MetroBlockDecorative.java new file mode 100644 index 0000000..887eab6 --- /dev/null +++ b/src/main/java/com/fureniku/metropolis/blocks/MetroBlockDecorative.java @@ -0,0 +1,74 @@ +package com.fureniku.metropolis.blocks; + +import com.fureniku.metropolis.datagen.MetroBlockStateProvider; +import com.fureniku.metropolis.datagen.TextureSet; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.neoforged.neoforge.client.model.generators.BlockModelBuilder; +import net.neoforged.neoforge.registries.RegistryObject; + +public class MetroBlockDecorative extends MetroBlockBase { + + private final VoxelShape BLOCK_SHAPE; + private TextureSet[] _resources; + private String _modelName; + + public MetroBlockDecorative(Properties props, String modelName, ResourceLocation resource) { + this(props, 16, 16, modelName, new TextureSet("texture", resource)); + } + + public MetroBlockDecorative(Properties props, float height, String modelName, ResourceLocation resource) { + this(props, height, 16, modelName, new TextureSet("texture", resource)); + } + + public MetroBlockDecorative(Properties props, float height, float width, String modelName, ResourceLocation resource) { + this(props, height, width, modelName, new TextureSet("texture", resource)); + } + + public MetroBlockDecorative(Properties props, VoxelShape shape, String modelName, ResourceLocation resource) { + this(props, shape, modelName, new TextureSet("texture", resource)); + } + + public MetroBlockDecorative(Properties props, String modelName, TextureSet... textures) { + this(props, 16, 16, modelName, textures); + } + + public MetroBlockDecorative(Properties props, float height, String modelName, TextureSet... textures) { + this(props, height, 16, modelName, textures); + } + + public MetroBlockDecorative(Properties props, float height, float width, String modelName, TextureSet... textures) { + super(props); + float inset = (16-width)/2; + BLOCK_SHAPE = Block.box(inset, 0, inset, 16-inset, height, 16-inset); + _resources = textures; + _modelName = modelName; + } + + public MetroBlockDecorative(Properties props, VoxelShape shape, String modelName, TextureSet... textures) { + super(props); + BLOCK_SHAPE = shape; + _resources = textures; + _modelName = modelName; + } + + @Override + protected VoxelShape getShapeFromBlockState(BlockState pState) { + 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()); + } + } + + blockStateProvider.simpleBlockWithItem(block, bmb); + } +} diff --git a/src/main/java/com/fureniku/metropolis/blocks/MetroBlockDecorativeRotatable.java b/src/main/java/com/fureniku/metropolis/blocks/MetroBlockDecorativeRotatable.java new file mode 100644 index 0000000..33c52f9 --- /dev/null +++ b/src/main/java/com/fureniku/metropolis/blocks/MetroBlockDecorativeRotatable.java @@ -0,0 +1,61 @@ +package com.fureniku.metropolis.blocks; + +import com.fureniku.metropolis.datagen.MetroBlockStateProvider; +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.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.BooleanProperty; +import net.minecraft.world.level.block.state.properties.DirectionProperty; +import net.minecraft.world.phys.shapes.VoxelShape; +import net.neoforged.neoforge.registries.RegistryObject; + +public class MetroBlockDecorativeRotatable extends MetroBlockBase { + + protected static final DirectionProperty DIRECTION = DirectionProperty.create("rotation", Direction.Plane.HORIZONTAL); + private final VoxelShape BLOCK_SHAPE; + + public MetroBlockDecorativeRotatable(Properties props) { + this(props, 16, 16); + } + + public MetroBlockDecorativeRotatable(Properties props, float height) { + this(props, height, 16); + } + + public MetroBlockDecorativeRotatable(Properties props, float height, float width) { + this(props, width, height, width); + } + + public MetroBlockDecorativeRotatable(Properties props, VoxelShape shape) { + super(props); + BLOCK_SHAPE = shape; + this.registerDefaultState(this.defaultBlockState().setValue(DIRECTION, Direction.NORTH)); + } + + public MetroBlockDecorativeRotatable(Properties props, float sizeX, float sizeY, float sizeZ) { + super(props); + float insetX = (16-sizeX)/2; + float insetZ = (16-sizeZ)/2; + BLOCK_SHAPE = Block.box(insetX, 0, insetZ, 16-insetX, 16-sizeY, 16-insetZ); + this.registerDefaultState(this.defaultBlockState().setValue(DIRECTION, Direction.NORTH)); + } + + @Override + public void generateBlockState(RegistryObject blockRegistryObject, MetroBlockStateProvider blockStateProvider) { + blockStateProvider.simpleBlockWithItem(blockRegistryObject.get()); + } + + @Override + protected BlockState getPlacementState(BlockPlaceContext context) { + BlockState blockstate = this.defaultBlockState().setValue(DIRECTION, context.getHorizontalDirection()); + return blockstate; + } + + @Override + protected void createBlockState(StateDefinition.Builder builder) { + builder.add(DIRECTION); + } +} diff --git a/src/main/java/com/fureniku/metropolis/datagen/MetroBlockStateProvider.java b/src/main/java/com/fureniku/metropolis/datagen/MetroBlockStateProvider.java index 4c8ee95..d897a7c 100644 --- a/src/main/java/com/fureniku/metropolis/datagen/MetroBlockStateProvider.java +++ b/src/main/java/com/fureniku/metropolis/datagen/MetroBlockStateProvider.java @@ -39,6 +39,14 @@ public void simpleBlockWithItem(Block block) { simpleBlockWithItem(block, cubeAll(block)); } + /** + * Get a modelled block, with a matching itemblock. Uses your blocks name to get the matching texture. + * @param block Your block + */ + public void blockWithItem(Block block, ResourceLocation loc) { + simpleBlockWithItem(block, models().withExistingParent(name(block), loc)); + } + /** * Get a model for a block with a single texture. * @param block your block diff --git a/src/main/java/com/fureniku/metropolis/datagen/TextureSet.java b/src/main/java/com/fureniku/metropolis/datagen/TextureSet.java new file mode 100644 index 0000000..72dc1c4 --- /dev/null +++ b/src/main/java/com/fureniku/metropolis/datagen/TextureSet.java @@ -0,0 +1,22 @@ +package com.fureniku.metropolis.datagen; + +import net.minecraft.resources.ResourceLocation; + +public class TextureSet { + + private String _name; + private ResourceLocation _location; + + public TextureSet(String key, ResourceLocation value) { + _name = key; + _location = value; + } + + public String getKey() { + return _name; + } + + public ResourceLocation getTexture() { + return _location; + } +}