Datageneration

This commit is contained in:
Curle 2023-06-30 03:22:41 +01:00
parent df73be9c56
commit 3f83b88cde
78 changed files with 2124 additions and 57 deletions

View File

@ -15,21 +15,15 @@ base {
// Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17. // Mojang ships Java 17 to end users in 1.18+, so your mod should target Java 17.
java.toolchain.languageVersion = JavaLanguageVersion.of(17) java.toolchain.languageVersion = JavaLanguageVersion.of(17)
// Include resources generated by data generators.
sourceSets {
data
main.resources { srcDirs += 'src/generated/resources' }
}
println "Java: ${System.getProperty 'java.version'}, JVM: ${System.getProperty 'java.vm.version'} (${System.getProperty 'java.vendor'}), Arch: ${System.getProperty 'os.arch'}" println "Java: ${System.getProperty 'java.version'}, JVM: ${System.getProperty 'java.vm.version'} (${System.getProperty 'java.vendor'}), Arch: ${System.getProperty 'os.arch'}"
minecraft { minecraft {
// The mappings can be changed at any time and must be in the following format.
// Channel: Version:
// official MCVersion Official field/method names from Mojang mapping files
// parchment YYYY.MM.DD-MCVersion Open community-sourced parameter names and javadocs layered on top of official
//
// You must be aware of the Mojang license when using the 'official' or 'parchment' mappings.
// See more information here: https://github.com/MinecraftForge/MCPConfig/blob/master/Mojang.md
//
// Parchment is an unofficial project maintained by ParchmentMC, separate from MinecraftForge
// Additional setup is needed to use their mappings: https://parchmentmc.org/docs/getting-started
//
// Use non-default mappings at your own risk. They may not always work.
// Simply re-run your setup task after changing the mappings to update your workspace.
mappings channel: mapping_channel, version: mapping_version mappings channel: mapping_channel, version: mapping_version
// When true, this property will have all Eclipse/IntelliJ IDEA run configurations run the "prepareX" task for the given run configuration before launching the game. // When true, this property will have all Eclipse/IntelliJ IDEA run configurations run the "prepareX" task for the given run configuration before launching the game.
@ -42,19 +36,6 @@ minecraft {
// See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html // See https://docs.gradle.org/current/dsl/org.gradle.language.jvm.tasks.ProcessResources.html
copyIdeResources = true copyIdeResources = true
// When true, this property will add the folder name of all declared run configurations to generated IDE run configurations.
// The folder name can be set on a run configuration using the "folderName" property.
// By default, the folder name of a run configuration is the name of the Gradle project containing it.
// generateRunFolders = true
// This property enables access transformers for use in development.
// They will be applied to the Minecraft artifact.
// The access transformer file can be anywhere in the project.
// However, it must be at "META-INF/accesstransformer.cfg" in the final mod jar to be loaded by Forge.
// This default location is a best practice to automatically put the file in the right place in the final jar.
// See https://docs.minecraftforge.net/en/latest/advanced/accesstransformers/ for more information.
// accessTransformer = file('src/main/resources/META-INF/accesstransformer.cfg')
// Default run configurations. // Default run configurations.
// These can be tweaked, removed, or duplicated as needed. // These can be tweaked, removed, or duplicated as needed.
runs { runs {
@ -99,27 +80,30 @@ minecraft {
} }
data { data {
ideaModule "${project.name}.data"
// example of overriding the workingDirectory set in configureEach above // example of overriding the workingDirectory set in configureEach above
workingDirectory project.file('run-data') workingDirectory project.file('run-data')
// Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources. // Specify the modid for data generation, where to output the resulting resource, and where to look for existing resources.
args '--mod', mod_id, '--all', '--output', file('src/generated/resources/'), '--existing', file('src/main/resources/') args '--mod', 'engage', '--all'
args '--output', sourceSets.data.resources.srcDirs[0].toString()
args '--existing', sourceSets.main.resources.srcDirs[0].toString()
mods {
"${mod_id}" {
source sourceSets.main
source sourceSets.data
}
}
} }
} }
} }
// Include resources generated by data generators.
sourceSets.main.resources { srcDir 'src/generated/resources' }
repositories { repositories {
// Put repositories for dependencies here }
// ForgeGradle automatically adds the Forge maven and Maven Central for you
// If you have mod jar dependencies in ./libs, you can declare them as a repository like so. configurations {
// See https://docs.gradle.org/current/userguide/declaring_repositories.html#sub:flat_dir_resolver dataImplementation.extendsFrom implementation
// flatDir {
// dir 'libs'
// }
} }
dependencies { dependencies {
@ -130,20 +114,8 @@ dependencies {
// then special handling is done to allow a setup of a vanilla dependency without the use of an external repository. // then special handling is done to allow a setup of a vanilla dependency without the use of an external repository.
minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}" minecraft "net.minecraftforge:forge:${minecraft_version}-${forge_version}"
// Example mod dependency with JEI - using fg.deobf() ensures the dependency is remapped to your development mappings
// The JEI API is declared for compile time use, while the full JEI artifact is used at runtime
// compileOnly fg.deobf("mezz.jei:jei-${mc_version}-common-api:${jei_version}")
// compileOnly fg.deobf("mezz.jei:jei-${mc_version}-forge-api:${jei_version}")
// runtimeOnly fg.deobf("mezz.jei:jei-${mc_version}-forge:${jei_version}")
// Example mod dependency using a mod jar from ./libs with a flat dir repository dataImplementation sourceSets.main.output
// This maps to ./libs/coolmod-${mc_version}-${coolmod_version}.jar
// The group id is ignored when searching -- in this case, it is "blank"
// implementation fg.deobf("blank:coolmod-${mc_version}:${coolmod_version}")
// For more info:
// http://www.gradle.org/docs/current/userguide/artifact_dependencies_tutorial.html
// http://www.gradle.org/docs/current/userguide/dependency_management.html
} }
// This block of code expands all declared replace properties in the specified resource targets. // This block of code expands all declared replace properties in the specified resource targets.

View File

@ -0,0 +1,45 @@
package uk.gemwire.engage.data;
import net.minecraft.data.DataGenerator;
import net.minecraftforge.common.data.BlockTagsProvider;
import net.minecraftforge.data.event.GatherDataEvent;
import net.minecraftforge.eventbus.api.SubscribeEvent;
import net.minecraftforge.fml.common.Mod;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.apache.logging.log4j.Marker;
import org.apache.logging.log4j.MarkerManager;
import org.jetbrains.annotations.NotNull;
import uk.gemwire.engage.data.blockstate.BlockStateProvider;
import uk.gemwire.engage.data.lang.EnglishProvider;
import uk.gemwire.engage.data.loot_tables.LootTableProvider;
import uk.gemwire.engage.data.model.block.BlockModelProvider;
import uk.gemwire.engage.data.model.item.ItemModelProvider;
import uk.gemwire.engage.data.sprites.SpriteSourceProvider;
import uk.gemwire.engage.data.tags.block.BlockTagProvider;
@Mod.EventBusSubscriber(modid = "engage", bus = Mod.EventBusSubscriber.Bus.MOD)
public class Datagen {
public static final Marker DATAGEN = MarkerManager.getMarker("DATAGEN");
public static final Logger LOGGER = LogManager.getLogger();
@SubscribeEvent
public static void onGatherData(@NotNull GatherDataEvent event) {
LOGGER.info(DATAGEN, "Gathering data providers");
DataGenerator generator = event.getGenerator();
LOGGER.info(DATAGEN, "Adding data providers for server data");
//generator.addProvider(event.includeServer(), new RecipeProviders(generator));
//generator.addProvider(event.includeServer(), new AdvancementsProvider(generator));
//generator.addProvider(event.includeServer(), new GLMProvider(generator));
generator.addProvider(event.includeServer(), new LootTableProvider(event.getGenerator().getPackOutput()));
generator.addProvider(event.includeServer(), new BlockTagProvider(event.getGenerator().getPackOutput(), event.getLookupProvider(), event.getExistingFileHelper()));
LOGGER.info(DATAGEN, "Adding data providers for client assets");
generator.addProvider(event.includeClient(), new EnglishProvider(event.getGenerator().getPackOutput()));
generator.addProvider(event.includeClient(), new SpriteSourceProvider(event.getGenerator().getPackOutput(), event.getExistingFileHelper()));
generator.addProvider(event.includeClient(), new ItemModelProvider(event.getGenerator().getPackOutput(), event.getExistingFileHelper()));
generator.addProvider(event.includeClient(), new BlockModelProvider(event.getGenerator().getPackOutput(), event.getExistingFileHelper()));
generator.addProvider(event.includeClient(), new BlockStateProvider(event.getGenerator().getPackOutput(), event.getExistingFileHelper()));
}
}

View File

@ -0,0 +1,68 @@
package uk.gemwire.engage.data.blockstate;
import net.minecraft.core.Direction;
import net.minecraft.data.PackOutput;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraftforge.client.model.generators.BlockStateProvider;
import net.minecraftforge.client.model.generators.ConfiguredModel;
import net.minecraftforge.client.model.generators.ModelFile;
import net.minecraftforge.client.model.generators.VariantBlockStateBuilder;
import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.registries.ForgeRegistries;
import org.jetbrains.annotations.NotNull;
import uk.gemwire.engage.data.model.block.BaseBlockModelProvider;
import uk.gemwire.engage.registries.fluid.FluidDeferredRegister;
import uk.gemwire.engage.registries.fluid.FluidRegistryObject;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Function;
public abstract class BaseBlockStateProvider<PROVIDER extends BaseBlockModelProvider> extends BlockStateProvider {
private final String modid;
private final PROVIDER modelProvider;
public BaseBlockStateProvider(PackOutput output, String modid, ExistingFileHelper existingFileHelper,
BiFunction<PackOutput, ExistingFileHelper, PROVIDER> providerCreator) {
super(output, modid, existingFileHelper);
this.modid = modid;
modelProvider = providerCreator.apply(output, existingFileHelper);
}
@NotNull
@Override
public String getName() {
return "Block state provider: " + modid;
}
@Override
public PROVIDER models() {
return modelProvider;
}
protected void registerFluidBlockStates(List<FluidRegistryObject<? extends FluidDeferredRegister.MekanismFluidType, ?, ?, ?, ?>> fluidROs) {
for (FluidRegistryObject<? extends FluidDeferredRegister.MekanismFluidType, ?, ?, ?, ?> fluidRO : fluidROs) {
simpleBlock(fluidRO.getBlock(), models().getBuilder(ForgeRegistries.BLOCKS.getKey(fluidRO.getBlock()).getPath()).texture("particle",
fluidRO.getFluidType().stillTexture));
}
}
/**
* Like directionalBlock but allows us to skip specific properties
*/
protected void directionalBlock(Block block, Function<BlockState, ModelFile> modelFunc, int angleOffset, Property<?>... toSkip) {
getVariantBuilder(block).forAllStatesExcept(state -> {
Direction dir = state.getValue(BlockStateProperties.FACING);
return ConfiguredModel.builder()
.modelFile(modelFunc.apply(state))
.rotationX(dir == Direction.DOWN ? 180 : dir.getAxis().isHorizontal() ? 90 : 0)
.rotationY(dir.getAxis().isVertical() ? 0 : (((int) dir.toYRot()) + angleOffset) % 360)
.build();
}, toSkip);
}
}

View File

@ -0,0 +1,33 @@
package uk.gemwire.engage.data.blockstate;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.registries.ForgeRegistries;
import uk.gemwire.engage.data.model.block.BlockModelProvider;
import uk.gemwire.engage.registries.Blocks;
import uk.gemwire.engage.registries.Fluids;
import java.util.List;
public class BlockStateProvider extends BaseBlockStateProvider<BlockModelProvider> {
public BlockStateProvider(PackOutput output, ExistingFileHelper existingFileHelper) {
super(output, "engage", existingFileHelper, BlockModelProvider::new);
}
private ResourceLocation key(Block block) {
return ForgeRegistries.BLOCKS.getKey(block);
}
private String name(Block block) {
return key(block).getPath();
}
@Override
protected void registerStatesAndModels() {
registerFluidBlockStates(List.of(Fluids.HEAVY_WATER_FLUID, Fluids.DEUTERIUM_FLUID, Fluids.DEUTERIUM_SLUSH_FLUID, Fluids.ANTIDEUTERIUM_FLUID, Fluids.WARP_PLASMA_FLUID, Fluids.PROPANE_FLUID));
simpleBlockWithItem(Blocks.BERYLLITE_ORE_BLOCK.get(), models().cubeAll(name(Blocks.BERYLLITE_ORE_BLOCK.get()), new ResourceLocation("minecraft:block/coal_ore")));
}
}

View File

@ -0,0 +1,31 @@
package uk.gemwire.engage.data.lang;
import net.minecraft.data.PackOutput;
import net.minecraftforge.common.data.LanguageProvider;
import uk.gemwire.engage.registries.Fluids;
import uk.gemwire.engage.registries.Items;
import uk.gemwire.engage.registries.fluid.FluidRegistryObject;
public class EnglishProvider extends LanguageProvider {
public EnglishProvider(PackOutput generatorIn) {
super(generatorIn, "engage", "en_us");
}
@Override
protected void addTranslations() {
add(Items.BERYLLITE_ORE_ITEM.get(), "Beryllite Ore");
add(Fluids.HEAVY_WATER_FLUID, "Heavy Water");
add(Fluids.PROPANE_FLUID, "Propane");
add(Fluids.DEUTERIUM_FLUID, "Deuterium");
add(Fluids.DEUTERIUM_SLUSH_FLUID, "Supercooled Deuterium");
add(Fluids.ANTIDEUTERIUM_FLUID, "Antideuterium");
add(Fluids.WARP_PLASMA_FLUID, "Warp Plasma");
}
void add(FluidRegistryObject<?, ?, ?, ?, ?> fluid, String name) {
add(fluid.getBlock(), name);
add(fluid.getBucket(), name + " Bucket");
}
}

View File

@ -0,0 +1,21 @@
package uk.gemwire.engage.data.loot_tables;
import net.minecraft.data.PackOutput;
import net.minecraft.data.loot.LootTableProvider;
import net.minecraft.resources.ResourceLocation;
import java.util.Collections;
import java.util.List;
import java.util.Set;
// Yoinked from Mekanism
public abstract class BaseLootProvider extends LootTableProvider {
protected BaseLootProvider(PackOutput output, List<SubProviderEntry> subProviders) {
this(output, Collections.emptySet(), subProviders);
}
protected BaseLootProvider(PackOutput output, Set<ResourceLocation> requiredTables, List<LootTableProvider.SubProviderEntry> subProviders) {
super(output, requiredTables, subProviders);
}
}

View File

@ -0,0 +1,16 @@
package uk.gemwire.engage.data.loot_tables;
import net.minecraft.data.PackOutput;
import net.minecraft.world.level.storage.loot.parameters.LootContextParamSets;
import uk.gemwire.engage.data.loot_tables.block.BlockLootTables;
import java.util.List;
public class LootTableProvider extends BaseLootProvider {
public LootTableProvider(PackOutput pack) {
super(pack, List.of(
new SubProviderEntry(BlockLootTables::new, LootContextParamSets.BLOCK)
));
}
}

View File

@ -0,0 +1,183 @@
package uk.gemwire.engage.data.loot_tables.block;
import it.unimi.dsi.fastutil.objects.ReferenceArraySet;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import net.minecraft.advancements.critereon.EnchantmentPredicate;
import net.minecraft.advancements.critereon.ItemPredicate;
import net.minecraft.advancements.critereon.MinMaxBounds;
import net.minecraft.advancements.critereon.StatePropertiesPredicate;
import net.minecraft.data.loot.BlockLootSubProvider;
import net.minecraft.world.flag.FeatureFlags;
import net.minecraft.world.item.enchantment.Enchantments;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.SlabBlock;
import net.minecraft.world.level.block.state.properties.SlabType;
import net.minecraft.world.level.storage.loot.LootPool;
import net.minecraft.world.level.storage.loot.LootTable;
import net.minecraft.world.level.storage.loot.entries.LootItem;
import net.minecraft.world.level.storage.loot.entries.LootPoolEntryContainer;
import net.minecraft.world.level.storage.loot.functions.ApplyBonusCount;
import net.minecraft.world.level.storage.loot.functions.SetItemCountFunction;
import net.minecraft.world.level.storage.loot.predicates.*;
import net.minecraft.world.level.storage.loot.providers.number.ConstantValue;
import net.minecraft.world.level.storage.loot.providers.number.NumberProvider;
import net.minecraft.world.level.storage.loot.providers.number.UniformGenerator;
import org.jetbrains.annotations.NotNull;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Set;
import java.util.function.Function;
import static net.minecraft.world.level.storage.loot.LootTable.Builder;
public abstract class BaseBlockLootTables extends BlockLootSubProvider {
private static final LootItemCondition.Builder HAS_SILK_TOUCH = MatchTool.toolMatches(ItemPredicate.Builder.item()
.hasEnchantment(new EnchantmentPredicate(Enchantments.SILK_TOUCH, MinMaxBounds.Ints.atLeast(1))));
private final Set<Block> knownBlocks = new ReferenceOpenHashSet<>();
//Note: We use an array set as we never expect this to have more than a few elements (in reality it only ever has one)
private final Set<Block> toSkip = new ReferenceArraySet<>();
protected BaseBlockLootTables() {
//Note: We manually handle explosion resistance on a case by case basis dynamically
super(Collections.emptySet(), FeatureFlags.VANILLA_SET);
}
@Override
protected void add(@NotNull Block block, @NotNull LootTable.Builder table) {
//Overwrite the core register method to add to our list of known blocks
super.add(block, table);
knownBlocks.add(block);
}
@NotNull
@Override
protected Iterable<Block> getKnownBlocks() {
return knownBlocks;
}
protected boolean skipBlock(Block block) {
//Skip any blocks that we already registered a table for or have marked to skip
return knownBlocks.contains(block) || toSkip.contains(block);
}
protected LootTable.Builder createOreDrop(Block block, ItemLike item) {
return createSilkTouchDispatchTable(block, applyExplosionDecay(block, LootItem.lootTableItem(item.asItem())
.apply(ApplyBonusCount.addOreBonusCount(Enchantments.BLOCK_FORTUNE))
));
}
protected LootTable.Builder droppingWithFortuneOrRandomly(Block block, ItemLike item, UniformGenerator range) {
return createSilkTouchDispatchTable(block, applyExplosionDecay(block, LootItem.lootTableItem(item.asItem())
.apply(SetItemCountFunction.setCount(range))
.apply(ApplyBonusCount.addOreBonusCount(Enchantments.BLOCK_FORTUNE))
));
}
//IBlockProvider versions of BlockLootTable methods, modified to support varargs
protected void dropSelf(List<Block> blocks) {
for (Block block : blocks) {
if (!skipBlock(block)) {
dropSelf(block);
}
}
}
protected void add(Function<Block, LootTable.Builder> factory, Collection<? extends Block> blocks) {
for (Block block : blocks) {
add(block, factory);
}
}
protected void add(Function<Block, Builder> factory, Block... blocks) {
for (Block block : blocks) {
add(block, factory);
}
}
/**
* Like vanilla's {@link BlockLootSubProvider#applyExplosionCondition(ItemLike, ConditionUserBuilder)} except with a boolean for if it is explosion resistant.
*/
private static <T extends ConditionUserBuilder<T>> T applyExplosionCondition(boolean explosionResistant, ConditionUserBuilder<T> condition) {
return explosionResistant ? condition.unwrap() : condition.when(ExplosionCondition.survivesExplosion());
}
/**
* Like vanilla's {@link BlockLootSubProvider#createSlabItemTable(Block)} except with a named pool
*/
@NotNull
@Override
protected LootTable.Builder createSlabItemTable(@NotNull Block slab) {
return LootTable.lootTable().withPool(LootPool.lootPool()
.name("main")
.setRolls(ConstantValue.exactly(1))
.add(applyExplosionDecay(slab, LootItem.lootTableItem(slab)
.apply(SetItemCountFunction.setCount(ConstantValue.exactly(2))
.when(LootItemBlockStatePropertyCondition.hasBlockStateProperties(slab)
.setProperties(StatePropertiesPredicate.Builder.properties().hasProperty(SlabBlock.TYPE, SlabType.DOUBLE)))
)
)
)
);
}
/**
* Like vanilla's {@link BlockLootSubProvider#dropOther(Block, ItemLike)} except with a named pool
*/
@Override
public void dropOther(@NotNull Block block, @NotNull ItemLike drop) {
add(block, createSingleItemTable(drop));
}
/**
* Like vanilla's {@link BlockLootSubProvider#createSingleItemTable(ItemLike)} except with a named pool
*/
@NotNull
@Override
public LootTable.Builder createSingleItemTable(@NotNull ItemLike item) {
return LootTable.lootTable().withPool(applyExplosionCondition(item, LootPool.lootPool()
.name("main")
.setRolls(ConstantValue.exactly(1))
.add(LootItem.lootTableItem(item))
));
}
/**
* Like vanilla's {@link BlockLootSubProvider#createSingleItemTableWithSilkTouch(Block, ItemLike, NumberProvider)} except with a named pool
*/
@NotNull
@Override
protected LootTable.Builder createSingleItemTableWithSilkTouch(@NotNull Block block, @NotNull ItemLike item, @NotNull NumberProvider range) {
return createSilkTouchDispatchTable(block, applyExplosionDecay(block, LootItem.lootTableItem(item).apply(SetItemCountFunction.setCount(range))));
}
/**
* Like vanilla's {@link BlockLootSubProvider#createSilkTouchDispatchTable(Block, LootPoolEntryContainer.Builder)} except with a named pool
*/
@NotNull
protected static LootTable.Builder createSilkTouchDispatchTable(@NotNull Block block, @NotNull LootPoolEntryContainer.Builder<?> builder) {
return createSelfDropDispatchTable(block, HAS_SILK_TOUCH, builder);
}
/**
* Like vanilla's {@link BlockLootSubProvider#createSelfDropDispatchTable(Block, LootItemCondition.Builder, LootPoolEntryContainer.Builder)} except with a named pool
*/
@NotNull
protected static LootTable.Builder createSelfDropDispatchTable(@NotNull Block block, @NotNull LootItemCondition.Builder conditionBuilder,
@NotNull LootPoolEntryContainer.Builder<?> entry) {
return LootTable.lootTable().withPool(LootPool.lootPool()
.name("main")
.setRolls(ConstantValue.exactly(1))
.add(LootItem.lootTableItem(block)
.when(conditionBuilder)
.otherwise(entry)
)
);
}
}

View File

@ -0,0 +1,12 @@
package uk.gemwire.engage.data.loot_tables.block;
import uk.gemwire.engage.registries.Blocks;
import uk.gemwire.engage.registries.Items;
public class BlockLootTables extends BaseBlockLootTables {
@Override
protected void generate() {
add(block -> createOreDrop(block, Items.RAW_BERYLLITE_ITEM.get()), Blocks.BERYLLITE_ORE_BLOCK.get());
}
}

View File

@ -0,0 +1,34 @@
package uk.gemwire.engage.data.model.block;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.PackType;
import net.minecraftforge.client.model.generators.BlockModelBuilder;
import net.minecraftforge.client.model.generators.BlockModelProvider;
import net.minecraftforge.common.data.ExistingFileHelper;
import org.jetbrains.annotations.NotNull;
public abstract class BaseBlockModelProvider extends BlockModelProvider {
public BaseBlockModelProvider(PackOutput output, String modid, ExistingFileHelper existingFileHelper) {
super(output, modid, existingFileHelper);
}
@NotNull
@Override
public String getName() {
return "Block model provider: " + modid;
}
public BlockModelBuilder sideBottomTop(String name, ResourceLocation parent, ResourceLocation texture) {
return withExistingParent(name, parent)
.texture("side", texture)
.texture("bottom", texture)
.texture("top", texture);
}
public boolean textureExists(ResourceLocation texture) {
return existingFileHelper.exists(texture, PackType.CLIENT_RESOURCES, ".png", "textures");
}
}

View File

@ -0,0 +1,17 @@
package uk.gemwire.engage.data.model.block;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.common.data.ExistingFileHelper;
public class BlockModelProvider extends BaseBlockModelProvider {
public BlockModelProvider(PackOutput output, ExistingFileHelper existingFileHelper) {
super(output, "engage", existingFileHelper);
}
@Override
protected void registerModels() {
}
}

View File

@ -0,0 +1,100 @@
package uk.gemwire.engage.data.model.item;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.server.packs.PackType;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.client.model.generators.ItemModelBuilder;
import net.minecraftforge.client.model.generators.ItemModelProvider;
import net.minecraftforge.client.model.generators.loaders.DynamicFluidContainerModelBuilder;
import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.registries.ForgeRegistries;
import org.jetbrains.annotations.NotNull;
import uk.gemwire.engage.registries.fluid.FluidDeferredRegister;
import uk.gemwire.engage.registries.fluid.FluidRegistryObject;
public abstract class BaseItemModelProvider extends ItemModelProvider {
protected BaseItemModelProvider(PackOutput output, String modid, ExistingFileHelper existingFileHelper) {
super(output, modid, existingFileHelper);
}
private String blockPath(Block block) {
return ForgeRegistries.BLOCKS.getKey(block).getPath();
}
private String itemPath(Item item) {
return ForgeRegistries.ITEMS.getKey(item).getPath();
}
@NotNull
@Override
public String getName() {
return "Item model provider: " + modid;
}
public boolean textureExists(ResourceLocation texture) {
return existingFileHelper.exists(texture, PackType.CLIENT_RESOURCES, ".png", "textures");
}
protected ResourceLocation itemTexture(ItemLike itemProvider) {
return modLoc("item/" + itemPath(itemProvider.asItem()));
}
protected void registerGenerated(ItemLike... itemProviders) {
for (ItemLike itemProvider : itemProviders) {
generated(itemProvider);
}
}
protected void registerBuckets(FluidDeferredRegister register) {
for (FluidRegistryObject<?, ?, ?, ?, ?> fluidRegistryObject : register.getAllFluids()) {
registerBucket(fluidRegistryObject);
}
}
protected ItemModelBuilder generated(ItemLike itemProvider) {
return generated(itemProvider, itemTexture(itemProvider));
}
protected ItemModelBuilder generated(ItemLike itemProvider, ResourceLocation texture) {
return withExistingParent(itemPath(itemProvider.asItem()), "item/generated").texture("layer0", texture);
}
protected ItemModelBuilder resource(ItemLike itemProvider, String type) {
//TODO: Try to come up with a better solution to this. Currently we have an empty texture for layer zero so that we can set
// the tint only on layer one so that we only end up having the tint show for this fallback texture
ItemModelBuilder modelBuilder = generated(itemProvider, modLoc("item/empty")).texture("layer1", modLoc("item/" + type));
ResourceLocation overlay = modLoc("item/" + type + "_overlay");
if (textureExists(overlay)) {
//If we have an overlay type for that resource type then add that as another layer
modelBuilder = modelBuilder.texture("layer2", overlay);
}
return modelBuilder;
}
protected void registerHandheld(ItemLike... itemProviders) {
for (ItemLike itemProvider : itemProviders) {
handheld(itemProvider);
}
}
protected ItemModelBuilder handheld(ItemLike itemProvider) {
return handheld(itemProvider, itemTexture(itemProvider));
}
protected ItemModelBuilder handheld(ItemLike itemProvider, ResourceLocation texture) {
return withExistingParent(itemPath(itemProvider.asItem()), "item/handheld").texture("layer0", texture);
}
//Note: This isn't the best way to do this in terms of model file validation, but it works
protected void registerBucket(FluidRegistryObject<?, ?, ?, ?, ?> fluidRO) {
withExistingParent(ForgeRegistries.ITEMS.getKey(fluidRO.getBucket()).getPath(), new ResourceLocation("forge", "item/bucket"))
.customLoader(DynamicFluidContainerModelBuilder::begin)
.fluid(fluidRO.getStillFluid());
}
}

View File

@ -0,0 +1,45 @@
package uk.gemwire.engage.data.model.item;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import uk.gemwire.engage.registries.Blocks;
import uk.gemwire.engage.registries.Fluids;
import uk.gemwire.engage.registries.Items;
public class ItemModelProvider extends BaseItemModelProvider {
public ItemModelProvider(PackOutput output, ExistingFileHelper existingFileHelper) {
super(output, "engage", existingFileHelper);
}
private ResourceLocation key(Block block) {
return ForgeRegistries.BLOCKS.getKey(block);
}
private String name(Block block) {
return key(block).getPath();
}
private ResourceLocation key(Item item) {
return ForgeRegistries.ITEMS.getKey(item);
}
private String name(Item item) {
return key(item).getPath();
}
@Override
protected void registerModels() {
registerBuckets(Fluids.FLUIDS_REGISTRY);
generated(Items.RAW_BERYLLITE_ITEM.get(), new ResourceLocation("minecraft:item/gold_ingot"));
}
void blockParent(RegistryObject<Item> item, RegistryObject<Block> block) {
withExistingParent(name(item.get()), modLoc(BLOCK_FOLDER + name(block.get())));
}
}

View File

@ -0,0 +1,48 @@
package uk.gemwire.engage.data.sprites;
import net.minecraft.client.renderer.texture.atlas.sources.DirectoryLister;
import net.minecraft.client.renderer.texture.atlas.sources.SingleFile;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.common.data.SpriteSourceProvider;
import uk.gemwire.engage.registries.fluid.FluidDeferredRegister;
import uk.gemwire.engage.registries.fluid.FluidRegistryObject;
import java.util.HashSet;
import java.util.Optional;
import java.util.Set;
public class BaseSpriteSourceProvider extends SpriteSourceProvider {
private final Set<ResourceLocation> trackedSingles = new HashSet<>();
protected BaseSpriteSourceProvider(PackOutput output, String modid, ExistingFileHelper fileHelper) {
super(output, fileHelper, modid);
}
protected void addFiles(SourceList atlas, ResourceLocation... resourceLocations) {
for (ResourceLocation rl : resourceLocations) {
//Only add this source if we haven't already added it as a direct single file source
if (trackedSingles.add(rl)) {
atlas.addSource(new SingleFile(rl, Optional.empty()));
}
}
}
protected void addFluids(SourceList atlas, FluidDeferredRegister register) {
for (FluidRegistryObject<? extends FluidDeferredRegister.MekanismFluidType, ?, ?, ?, ?> fluidRO : register.getAllFluids()) {
FluidDeferredRegister.MekanismFluidType fluidType = fluidRO.getFluidType();
addFiles(atlas, fluidType.stillTexture, fluidType.flowingTexture, fluidType.overlayTexture, fluidType.renderOverlayTexture);
}
}
protected void addDirectory(SourceList atlas, String directory, String spritePrefix) {
atlas.addSource(new DirectoryLister(directory, spritePrefix));
}
@Override
protected void addSources() {
}
}

View File

@ -0,0 +1,19 @@
package uk.gemwire.engage.data.sprites;
import net.minecraft.data.PackOutput;
import net.minecraftforge.common.data.ExistingFileHelper;
import uk.gemwire.engage.registries.Fluids;
public class SpriteSourceProvider extends BaseSpriteSourceProvider {
public SpriteSourceProvider(PackOutput output, ExistingFileHelper fileHelper) {
super(output, "engage", fileHelper);
}
@Override
protected void addSources() {
SourceList atlas = atlas(BLOCKS_ATLAS);
addFluids(atlas, Fluids.FLUIDS_REGISTRY);
}
}

View File

@ -0,0 +1,202 @@
package uk.gemwire.engage.data.tags;
import it.unimi.dsi.fastutil.objects.Object2ObjectLinkedOpenHashMap;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.Registry;
import net.minecraft.core.registries.Registries;
import net.minecraft.data.CachedOutput;
import net.minecraft.data.DataProvider;
import net.minecraft.data.PackOutput;
import net.minecraft.data.tags.TagsProvider;
import net.minecraft.resources.ResourceKey;
import net.minecraft.tags.TagBuilder;
import net.minecraft.tags.TagKey;
import net.minecraft.world.damagesource.DamageType;
import net.minecraft.world.effect.MobEffect;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.ItemLike;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.gameevent.GameEvent;
import net.minecraft.world.level.material.Fluid;
import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.IForgeRegistry;
import org.jetbrains.annotations.NotNull;
import uk.gemwire.engage.registries.fluid.FluidRegistryObject;
import javax.annotation.Nullable;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.function.Function;
public abstract class BaseTagProvider implements DataProvider {
private final Map<ResourceKey<? extends Registry<?>>, Map<TagKey<?>, TagBuilder>> supportedTagTypes = new Object2ObjectLinkedOpenHashMap<>();
private final Set<Block> knownHarvestRequirements = new ReferenceOpenHashSet<>();
private final CompletableFuture<HolderLookup.Provider> lookupProvider;
private final ExistingFileHelper existingFileHelper;
private final PackOutput output;
private final String modid;
protected BaseTagProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> lookupProvider, String modid, @Nullable ExistingFileHelper existingFileHelper) {
this.output = output;
this.modid = modid;
this.lookupProvider = lookupProvider;
this.existingFileHelper = existingFileHelper;
}
@NotNull
@Override
public String getName() {
return "Tags: " + modid;
}
protected abstract void registerTags(HolderLookup.Provider registries);
protected List<Block> getAllBlocks() {
return Collections.emptyList();
}
protected void hasHarvestData(Block block) {
knownHarvestRequirements.add(block);
}
@NotNull
@Override
public CompletableFuture<?> run(@NotNull CachedOutput cache) {
return this.lookupProvider.thenApply(registries -> {
supportedTagTypes.values().forEach(Map::clear);
registerTags(registries);
return registries;
}).thenCompose(registries -> {
for (Block block : getAllBlocks()) {
if (block.defaultBlockState().requiresCorrectToolForDrops() && !knownHarvestRequirements.contains(block)) {
throw new IllegalStateException("Missing harvest tool type for block '" + ForgeRegistries.BLOCKS.getKey(block) + "' that requires the correct tool for drops.");
}
}
List<CompletableFuture<?>> futures = new ArrayList<>();
supportedTagTypes.forEach((registry, tagTypeMap) -> {
if (!tagTypeMap.isEmpty()) {
//Create a dummy provider and pass all our collected data through to it
futures.add(new TagsProvider(output, registry, lookupProvider, modid, existingFileHelper) {
@Override
protected void addTags(@NotNull HolderLookup.Provider lookupProvider) {
//Add each tag builder to the wrapped provider's builder
tagTypeMap.forEach((tag, tagBuilder) -> builders.put(tag.location(), tagBuilder));
}
}.run(cache));
}
});
return CompletableFuture.allOf(futures.toArray(CompletableFuture[]::new));
});
}
private <TYPE> Map<TagKey<?>, TagBuilder> getTagTypeMap(ResourceKey<? extends Registry<TYPE>> registry) {
return supportedTagTypes.computeIfAbsent(registry, type -> new Object2ObjectLinkedOpenHashMap<>());
}
private <TYPE> TagBuilder getTagBuilder(ResourceKey<? extends Registry<TYPE>> registry, TagKey<TYPE> tag) {
return getTagTypeMap(registry).computeIfAbsent(tag, ignored -> TagBuilder.create());
}
protected <TYPE> EngageTagBuilder<TYPE, ?> getBuilder(ResourceKey<? extends Registry<TYPE>> registry, TagKey<TYPE> tag) {
return new EngageTagBuilder<>(getTagBuilder(registry, tag), modid);
}
protected <TYPE> IntrinsicEngageTagBuilder<TYPE> getBuilder(ResourceKey<? extends Registry<TYPE>> registry, Function<TYPE, ResourceKey<TYPE>> keyExtractor, TagKey<TYPE> tag) {
return new IntrinsicEngageTagBuilder<>(keyExtractor, getTagBuilder(registry, tag), modid);
}
protected <TYPE> IntrinsicEngageTagBuilder<TYPE> getBuilder(IForgeRegistry<TYPE> registry, TagKey<TYPE> tag) {
return new IntrinsicEngageTagBuilder<>(element -> registry.getResourceKey(element).orElseThrow(), getTagBuilder(registry.getRegistryKey(), tag), modid);
}
protected IntrinsicEngageTagBuilder<Item> getItemBuilder(TagKey<Item> tag) {
return getBuilder(ForgeRegistries.ITEMS, tag);
}
protected IntrinsicEngageTagBuilder<Block> getBlockBuilder(TagKey<Block> tag) {
return getBuilder(ForgeRegistries.BLOCKS, tag);
}
protected IntrinsicEngageTagBuilder<EntityType<?>> getEntityTypeBuilder(TagKey<EntityType<?>> tag) {
return getBuilder(ForgeRegistries.ENTITY_TYPES, tag);
}
protected IntrinsicEngageTagBuilder<Fluid> getFluidBuilder(TagKey<Fluid> tag) {
return getBuilder(ForgeRegistries.FLUIDS, tag);
}
protected IntrinsicEngageTagBuilder<BlockEntityType<?>> getTileEntityTypeBuilder(TagKey<BlockEntityType<?>> tag) {
return getBuilder(ForgeRegistries.BLOCK_ENTITY_TYPES, tag);
}
protected IntrinsicEngageTagBuilder<GameEvent> getGameEventBuilder(TagKey<GameEvent> tag) {
return getBuilder(Registries.GAME_EVENT, gameEvent -> gameEvent.builtInRegistryHolder().key(), tag);
}
protected EngageTagBuilder<DamageType, ?> getDamageTypeBuilder(TagKey<DamageType> tag) {
return getBuilder(Registries.DAMAGE_TYPE, tag);
}
protected IntrinsicEngageTagBuilder<MobEffect> getMobEffectBuilder(TagKey<MobEffect> tag) {
return getBuilder(ForgeRegistries.MOB_EFFECTS, tag);
}
protected void addToTag(TagKey<Item> tag, ItemLike... itemProviders) {
getItemBuilder(tag).addTyped(ItemLike::asItem, itemProviders);
}
protected void addToTag(TagKey<Block> tag, Block... blocks) {
getBlockBuilder(tag).addTyped(b -> b, blocks);
}
@SafeVarargs
protected final void addToTag(TagKey<Block> blockTag, Map<?, ? extends Block>... blocks) {
IntrinsicEngageTagBuilder<Block> tagBuilder = getBlockBuilder(blockTag);
for (Map<?, ? extends Block> entry : blocks) {
for (Block value : entry.values()) {
tagBuilder.add(value);
}
}
}
protected void addToHarvestTag(TagKey<Block> tag, Block... blockProviders) {
IntrinsicEngageTagBuilder<Block> tagBuilder = getBlockBuilder(tag);
for (Block block : blockProviders) {
tagBuilder.add(block);
hasHarvestData(block);
}
}
@SafeVarargs
protected final void addToHarvestTag(TagKey<Block> blockTag, Map<?, ? extends Block>... blockProviders) {
IntrinsicEngageTagBuilder<Block> tagBuilder = getBlockBuilder(blockTag);
for (Map<?, ? extends Block> blockProvider : blockProviders) {
for (Block block : blockProvider.values()) {
tagBuilder.add(block);
hasHarvestData(block);
}
}
}
protected void addToTags(TagKey<Item> itemTag, TagKey<Block> blockTag, Block... blockProviders) {
IntrinsicEngageTagBuilder<Item> itemTagBuilder = getItemBuilder(itemTag);
IntrinsicEngageTagBuilder<Block> blockTagBuilder = getBlockBuilder(blockTag);
for (Block blockProvider : blockProviders) {
itemTagBuilder.add(blockProvider.asItem());
blockTagBuilder.add(blockProvider);
}
}
protected void addToTag(TagKey<Fluid> tag, FluidRegistryObject<?, ?, ?, ?, ?>... fluidRegistryObjects) {
IntrinsicEngageTagBuilder<Fluid> tagBuilder = getFluidBuilder(tag);
for (FluidRegistryObject<?, ?, ?, ?, ?> fluidRO : fluidRegistryObjects) {
tagBuilder.add(fluidRO.getStillFluid(), fluidRO.getFlowingFluid());
}
}
}

View File

@ -0,0 +1,108 @@
package uk.gemwire.engage.data.tags;
import java.util.function.Consumer;
import java.util.function.Function;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagBuilder;
import net.minecraft.tags.TagEntry;
import net.minecraft.tags.TagKey;
// Stolen from Mekanism
public class EngageTagBuilder<TYPE, BUILDER extends EngageTagBuilder<TYPE, BUILDER>> {
protected final TagBuilder builder;
protected final String modID;
public EngageTagBuilder(TagBuilder builder, String modID) {
this.builder = builder;
this.modID = modID;
}
@SuppressWarnings("unchecked")
private BUILDER getThis() {
return (BUILDER) this;
}
@SafeVarargs
public final BUILDER add(TagKey<TYPE>... tags) {
return apply(builder::addTag, TagKey::location, tags);
}
public BUILDER add(TagEntry tag) {
builder.add(tag);
return getThis();
}
@SafeVarargs
public final BUILDER add(ResourceKey<TYPE>... keys) {
return add(ResourceKey::location, keys);
}
@SafeVarargs
public final <T> BUILDER add(Function<T, ResourceLocation> locationGetter, T... elements) {
return apply(builder::addElement, locationGetter, elements);
}
public BUILDER replace() {
return replace(true);
}
public BUILDER replace(boolean value) {
builder.replace(value);
return getThis();
}
public BUILDER addOptional(ResourceLocation... locations) {
return addOptional(Function.identity(), locations);
}
@SafeVarargs
public final <T> BUILDER addOptional(Function<T, ResourceLocation> locationGetter, T... elements) {
return add(TagEntry::optionalElement, locationGetter, elements);
}
@SafeVarargs
public final BUILDER addOptionalTag(TagKey<TYPE>... tags) {
return addOptionalTag(TagKey::location, tags);
}
public BUILDER addOptionalTag(ResourceLocation... locations) {
return addOptionalTag(Function.identity(), locations);
}
@SafeVarargs
public final <T> BUILDER addOptionalTag(Function<T, ResourceLocation> locationGetter, T... elements) {
return add(TagEntry::optionalTag, locationGetter, elements);
}
@SafeVarargs
private <T> BUILDER add(Function<ResourceLocation, TagEntry> entryCreator, Function<T, ResourceLocation> locationGetter, T... elements) {
return apply(rl -> add(entryCreator.apply(rl)), locationGetter, elements);
}
public BUILDER remove(ResourceLocation... locations) {
return remove(Function.identity(), locations);
}
@SafeVarargs
public final <T> BUILDER remove(Function<T, ResourceLocation> locationGetter, T... elements) {
return apply(rl -> builder.removeElement(rl, modID), locationGetter, elements);
}
@SafeVarargs
public final BUILDER remove(TagKey<TYPE>... tags) {
for (TagKey<TYPE> tag : tags) {
builder.removeTag(tag.location(), modID);
}
return getThis();
}
@SafeVarargs
protected final <T> BUILDER apply(Consumer<ResourceLocation> consumer, Function<T, ResourceLocation> locationGetter, T... elements) {
for (T element : elements) {
consumer.accept(locationGetter.apply(element));
}
return getThis();
}
}

View File

@ -0,0 +1,47 @@
package uk.gemwire.engage.data.tags;
import net.minecraft.resources.ResourceKey;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.TagBuilder;
import java.util.function.Function;
import java.util.function.Supplier;
public class IntrinsicEngageTagBuilder<TYPE> extends EngageTagBuilder<TYPE, IntrinsicEngageTagBuilder<TYPE>> {
private final Function<TYPE, ResourceKey<TYPE>> keyExtractor;
public IntrinsicEngageTagBuilder (Function<TYPE, ResourceKey<TYPE>> keyExtractor, TagBuilder builder, String modID) {
super(builder, modID);
this.keyExtractor = keyExtractor;
}
@SafeVarargs
public final IntrinsicEngageTagBuilder<TYPE> add(Supplier<TYPE>... elements) {
return addTyped(Supplier::get, elements);
}
public ResourceLocation getKey(TYPE element) {
return keyExtractor.apply(element).location();
}
@SafeVarargs
public final IntrinsicEngageTagBuilder<TYPE> add(TYPE... elements) {
return add(this::getKey, elements);
}
@SafeVarargs
public final <T> IntrinsicEngageTagBuilder<TYPE> addTyped(Function<T, TYPE> converter, T... elements) {
return add(converter.andThen(this::getKey), elements);
}
@SafeVarargs
public final IntrinsicEngageTagBuilder<TYPE> addOptional(TYPE... elements) {
return addOptional(this::getKey, elements);
}
@SafeVarargs
public final IntrinsicEngageTagBuilder<TYPE> remove(TYPE... elements) {
return remove(this::getKey, elements);
}
}

View File

@ -0,0 +1,69 @@
package uk.gemwire.engage.data.tags.block;
import net.minecraft.core.HolderLookup;
import net.minecraft.data.PackOutput;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.BlockTags;
import net.minecraft.tags.ItemTags;
import net.minecraft.tags.TagKey;
import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block;
import net.minecraftforge.common.Tags;
import net.minecraftforge.common.data.ExistingFileHelper;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import uk.gemwire.engage.data.tags.BaseTagProvider;
import uk.gemwire.engage.data.tags.IntrinsicEngageTagBuilder;
import uk.gemwire.engage.registries.Blocks;
import javax.annotation.Nullable;
import java.util.List;
import java.util.concurrent.CompletableFuture;
public class BlockTagProvider extends BaseTagProvider {
public BlockTagProvider(PackOutput output, CompletableFuture<HolderLookup.Provider> lookupProvider, @Nullable ExistingFileHelper existingFileHelper) {
super(output, lookupProvider, "engage", existingFileHelper);
}
@Override
protected List<Block> getAllBlocks() {
return Blocks.BLOCKS_REGISTRY.getEntries().stream().map(RegistryObject::get).toList();
}
@Override
protected void registerTags(HolderLookup.Provider registries) {
addOres();
addHarvests();
}
private void addOres() {
List<Block> ores = List.of(Blocks.BERYLLITE_ORE_BLOCK.get());
for (Block ore : ores) {
TagKey<Item> itemTag = ItemTags.create(new ResourceLocation("forge:ores/" + ForgeRegistries.ITEMS.getKey(ore.asItem()).getPath()));
TagKey<Block> blockTag = BlockTags.create(new ResourceLocation("forge:ores/" + ForgeRegistries.BLOCKS.getKey(ore).getPath()));
addToTags(itemTag, blockTag, ore);
getItemBuilder(Tags.Items.ORES).add(itemTag);
getBlockBuilder(Tags.Blocks.ORES).add(blockTag);
addToTags(Tags.Items.ORE_RATES_SINGULAR, Tags.Blocks.ORE_RATES_SINGULAR, ore);
//addToTags(Tags.Items.ORES_IN_GROUND_DEEPSLATE, Tags.Blocks.ORES_IN_GROUND_DEEPSLATE, oreBlockType.deepslate());
addToTags(Tags.Items.ORES_IN_GROUND_STONE, Tags.Blocks.ORES_IN_GROUND_STONE, ore);
addToTag(BlockTags.OVERWORLD_CARVER_REPLACEABLES, ore);
addToTag(BlockTags.SNAPS_GOAT_HORN, ore);
}
}
private void addHarvests() {
addIronToolBlock(Blocks.BERYLLITE_ORE_BLOCK.get());
}
private void addIronToolBlock(Block b) {
addToHarvestTag(BlockTags.MINEABLE_WITH_PICKAXE, b);
getBlockBuilder(BlockTags.NEEDS_IRON_TOOL).add(b);
hasHarvestData(b);
}
}

View File

@ -0,0 +1,8 @@
// 1.20.1 2023-06-30T03:19:18.0172666 Item model provider: engage
b3abd1796143a506e2c7f2ffdefeb61e235a8689 assets/engage/models/item/antideuterium_bucket.json
cfeee2aee8fab94a821a49be3ce0d110c857e1b1 assets/engage/models/item/beryllite_raw.json
ca322f154c4d4ab99647bfa2ebb939b9284fad9a assets/engage/models/item/deuterium_bucket.json
725bfdd287ed277d7b78d3a295ea5a71b63b40fa assets/engage/models/item/deuterium_slush_bucket.json
50ee03ab71a0bc46941091c9c8cb8f9b516368c7 assets/engage/models/item/heavy_water_bucket.json
226a78ddeaaecb840cb16d7b6236f62978adbb72 assets/engage/models/item/propane_bucket.json
2537ffcc0380ed93e47909ce598b76ee501bb5f3 assets/engage/models/item/warp_plasma_bucket.json

View File

@ -0,0 +1,2 @@
// 1.20.1 2023-06-30T03:04:16.2684959 Loot Tables
22933ac7663f965e6fe3c560231b4bcb76c43bfa data/engage/loot_tables/blocks/beryllite_ore.json

View File

@ -0,0 +1,2 @@
// 1.20.1 2023-06-30T03:04:16.2664787 atlases generator for engage
11e6e1f83c37071af466a42d29a662f54c2253c8 assets/minecraft/atlases/blocks.json

View File

@ -0,0 +1 @@
// 1.20.1 2023-06-30T03:19:18.0192671 Block model provider: engage

View File

@ -0,0 +1,16 @@
// 1.20.1 2023-06-30T03:19:18.0203012 Block state provider: engage
4af9c861d7727e9ee266ca6e10d9c78c9e4104ba assets/engage/blockstates/antideuterium.json
163e87835e33814e2cad190c2265142f42e18ba9 assets/engage/blockstates/beryllite_ore.json
38881f2039561be97bb6ea53d31ab9db54875fe5 assets/engage/blockstates/deuterium.json
620e8e678622f335115b86475f40e518de4f9cba assets/engage/blockstates/deuterium_slush.json
8c9558de31b0459d70da1d07b499138e6248e38c assets/engage/blockstates/heavy_water.json
b58e421acd852f85bc9fee8f837609c8471b90b7 assets/engage/blockstates/propane.json
9b31876df3da1b32ac5639c48cab2ce60cc876ad assets/engage/blockstates/warp_plasma.json
f077c95c66d98f808227df298031ba934827bbfc assets/engage/models/block/antideuterium.json
9356d6cfcd705b3474cec35599e7709fb9f0954a assets/engage/models/block/beryllite_ore.json
f077c95c66d98f808227df298031ba934827bbfc assets/engage/models/block/deuterium.json
f077c95c66d98f808227df298031ba934827bbfc assets/engage/models/block/deuterium_slush.json
f077c95c66d98f808227df298031ba934827bbfc assets/engage/models/block/heavy_water.json
f077c95c66d98f808227df298031ba934827bbfc assets/engage/models/block/propane.json
f077c95c66d98f808227df298031ba934827bbfc assets/engage/models/block/warp_plasma.json
45314b766210255f87067ff2a93913e89b07f7ec assets/engage/models/item/beryllite_ore.json

View File

@ -0,0 +1,13 @@
// 1.20.1 2023-06-30T03:04:16.2714951 Tags: engage
36ddf7e815c8651764d9b434226769617f395c02 data/forge/tags/blocks/ores.json
af866d91e488f4393cdfb764b61ac425837ec4a6 data/forge/tags/blocks/ores/beryllite_ore.json
af866d91e488f4393cdfb764b61ac425837ec4a6 data/forge/tags/blocks/ores_in_ground/stone.json
af866d91e488f4393cdfb764b61ac425837ec4a6 data/forge/tags/blocks/ore_rates/singular.json
36ddf7e815c8651764d9b434226769617f395c02 data/forge/tags/items/ores.json
af866d91e488f4393cdfb764b61ac425837ec4a6 data/forge/tags/items/ores/beryllite_ore.json
af866d91e488f4393cdfb764b61ac425837ec4a6 data/forge/tags/items/ores_in_ground/stone.json
af866d91e488f4393cdfb764b61ac425837ec4a6 data/forge/tags/items/ore_rates/singular.json
af866d91e488f4393cdfb764b61ac425837ec4a6 data/minecraft/tags/blocks/mineable/pickaxe.json
af866d91e488f4393cdfb764b61ac425837ec4a6 data/minecraft/tags/blocks/needs_iron_tool.json
af866d91e488f4393cdfb764b61ac425837ec4a6 data/minecraft/tags/blocks/overworld_carver_replaceables.json
af866d91e488f4393cdfb764b61ac425837ec4a6 data/minecraft/tags/blocks/snaps_goat_horn.json

View File

@ -0,0 +1,2 @@
// 1.20.1 2023-06-30T03:04:16.2694935 Languages: en_us
50b8d51f4adf81848b9ed702904feddb9c747a09 assets/engage/lang/en_us.json

View File

@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "engage:block/antideuterium"
}
}
}

View File

@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "engage:block/deuterium"
}
}
}

View File

@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "engage:block/deuterium_slush"
}
}
}

View File

@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "engage:block/heavy_water"
}
}
}

View File

@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "engage:block/propane"
}
}
}

View File

@ -0,0 +1,7 @@
{
"variants": {
"": {
"model": "engage:block/warp_plasma"
}
}
}

View File

@ -0,0 +1,15 @@
{
"block.engage.antideuterium": "Antideuterium",
"block.engage.beryllite_ore": "Beryllite Ore",
"block.engage.deuterium": "Deuterium",
"block.engage.deuterium_slush": "Supercooled Deuterium",
"block.engage.heavy_water": "Heavy Water",
"block.engage.propane": "Propane",
"block.engage.warp_plasma": "Warp Plasma",
"item.engage.antideuterium_bucket": "Antideuterium Bucket",
"item.engage.deuterium_bucket": "Deuterium Bucket",
"item.engage.deuterium_slush_bucket": "Supercooled Deuterium Bucket",
"item.engage.heavy_water_bucket": "Heavy Water Bucket",
"item.engage.propane_bucket": "Propane Bucket",
"item.engage.warp_plasma_bucket": "Warp Plasma Bucket"
}

View File

@ -0,0 +1,5 @@
{
"textures": {
"particle": "engage:liquid/liquid"
}
}

View File

@ -0,0 +1,5 @@
{
"textures": {
"particle": "engage:liquid/liquid"
}
}

View File

@ -0,0 +1,5 @@
{
"textures": {
"particle": "engage:liquid/liquid"
}
}

View File

@ -0,0 +1,5 @@
{
"textures": {
"particle": "engage:liquid/liquid"
}
}

View File

@ -0,0 +1,5 @@
{
"textures": {
"particle": "engage:liquid/liquid"
}
}

View File

@ -0,0 +1,5 @@
{
"textures": {
"particle": "engage:liquid/liquid"
}
}

View File

@ -0,0 +1,5 @@
{
"parent": "forge:item/bucket",
"fluid": "engage:antideuterium",
"loader": "forge:fluid_container"
}

View File

@ -0,0 +1,5 @@
{
"parent": "forge:item/bucket",
"fluid": "engage:deuterium",
"loader": "forge:fluid_container"
}

View File

@ -0,0 +1,5 @@
{
"parent": "forge:item/bucket",
"fluid": "engage:deuterium_slush",
"loader": "forge:fluid_container"
}

View File

@ -0,0 +1,5 @@
{
"parent": "forge:item/bucket",
"fluid": "engage:heavy_water",
"loader": "forge:fluid_container"
}

View File

@ -0,0 +1,5 @@
{
"parent": "forge:item/bucket",
"fluid": "engage:propane",
"loader": "forge:fluid_container"
}

View File

@ -0,0 +1,5 @@
{
"parent": "forge:item/bucket",
"fluid": "engage:warp_plasma",
"loader": "forge:fluid_container"
}

View File

@ -0,0 +1,20 @@
{
"sources": [
{
"type": "minecraft:single",
"resource": "engage:liquid/liquid"
},
{
"type": "minecraft:single",
"resource": "engage:liquid/liquid_flow"
},
{
"type": "minecraft:single",
"resource": "minecraft:block/water_overlay"
},
{
"type": "minecraft:single",
"resource": "minecraft:misc/underwater"
}
]
}

View File

@ -1,5 +1,4 @@
{ {
"replace": false,
"values": [ "values": [
"engage:beryllite_ore" "engage:beryllite_ore"
] ]

View File

@ -0,0 +1,5 @@
{
"values": [
"#forge:ores/beryllite_ore"
]
}

View File

@ -1,5 +1,4 @@
{ {
"replace": false,
"values": [ "values": [
"engage:beryllite_ore" "engage:beryllite_ore"
] ]

View File

@ -1,5 +1,4 @@
{ {
"replace": false,
"values": [ "values": [
"engage:beryllite_ore" "engage:beryllite_ore"
] ]

View File

@ -0,0 +1,5 @@
{
"values": [
"engage:beryllite_ore"
]
}

View File

@ -0,0 +1,5 @@
{
"values": [
"#forge:ores/beryllite_ore"
]
}

View File

@ -0,0 +1,5 @@
{
"values": [
"engage:beryllite_ore"
]
}

View File

@ -0,0 +1,5 @@
{
"values": [
"engage:beryllite_ore"
]
}

View File

@ -1,5 +1,4 @@
{ {
"replace": false,
"values": [ "values": [
"engage:beryllite_ore" "engage:beryllite_ore"
] ]

View File

@ -1,5 +1,4 @@
{ {
"replace": false,
"values": [ "values": [
"engage:beryllite_ore" "engage:beryllite_ore"
] ]

View File

@ -1,5 +1,4 @@
{ {
"replace": false,
"values": [ "values": [
"engage:beryllite_ore" "engage:beryllite_ore"
] ]

View File

@ -1,5 +1,4 @@
{ {
"replace": false,
"values": [ "values": [
"engage:beryllite_ore" "engage:beryllite_ore"
] ]

View File

@ -4,8 +4,44 @@ import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fml.common.Mod; import net.minecraftforge.fml.common.Mod;
import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext; import net.minecraftforge.fml.javafmlmod.FMLJavaModLoadingContext;
import uk.gemwire.engage.registries.Blocks; import uk.gemwire.engage.registries.Blocks;
import uk.gemwire.engage.registries.Fluids;
import uk.gemwire.engage.registries.Items; import uk.gemwire.engage.registries.Items;
import uk.gemwire.engage.registries.Worldgen;
import uk.gemwire.engage.registries.fluid.FluidDeferredRegister;
/**
* Engage adds four varieties of Warp Core for power generation, based on:
* - The Defiant
* - The Enterprise-D
* - Voyager
* - Kelvin-timeline Enterprise
*
* The Warp structure itself has 7 components:
* - Casing
* - Reaction Gel
* - Reaction Chamber
* - Beryllite Containment Unit
* - Input port
* - Output vent
* - Status Indicator / Control Unit
*
* To power the Warp Core, you need:
* - Supercooled Deuterium
* (obtained by putting deuterium gas through a coolant loop)
* (deuterium obtained by electrolysis of heavy water)
* (heavy water obtained from deep water)
* - Contained antideuterium
* (contained by magnetic containment unit)
* (obtained by ???)
*
* The reaction generates Warp Plasma, which must be vented through hollow tubes (EPS Conduit), towards a consumer.
* Large structures may be created that convert the plasma to usable power.
* Power Nacelles:
* - Casing
* - Warp Gel
* = Power port
*
*/
@Mod("engage") @Mod("engage")
public class Engage { public class Engage {
@ -14,5 +50,8 @@ public class Engage {
// registry classes // registry classes
Items.ITEMS_REGISTRY.register(bus); Items.ITEMS_REGISTRY.register(bus);
Blocks.BLOCKS_REGISTRY.register(bus); Blocks.BLOCKS_REGISTRY.register(bus);
Fluids.FLUIDS_REGISTRY.register(bus);
Worldgen.CARVER_REGISTRY.register(bus);
Worldgen.BIOME_MODIFIER_SERIALIZERS.register(bus);
} }
} }

View File

@ -0,0 +1,28 @@
package uk.gemwire.engage.registries;
import uk.gemwire.engage.registries.fluid.FluidDeferredRegister;
import uk.gemwire.engage.registries.fluid.FluidRegistryObject;
public class Fluids {
public static final FluidDeferredRegister FLUIDS_REGISTRY = new FluidDeferredRegister("engage");
// Heavy Water
public static final FluidRegistryObject<? extends FluidDeferredRegister.MekanismFluidType, ?, ?, ?, ?> HEAVY_WATER_FLUID = FLUIDS_REGISTRY.register("heavy_water", properties -> properties.tint(0xFFFF3232));
// Deuterium
public static final FluidRegistryObject<? extends FluidDeferredRegister.MekanismFluidType, ?, ?, ?, ?> DEUTERIUM_FLUID = FLUIDS_REGISTRY.register("deuterium", properties -> properties.tint(0xFFFF3232));
// Deuterium Slush
public static final FluidRegistryObject<? extends FluidDeferredRegister.MekanismFluidType, ?, ?, ?, ?> DEUTERIUM_SLUSH_FLUID = FLUIDS_REGISTRY.register("deuterium_slush", properties -> properties.tint(0xFFFF3232));
// Antideuterium
public static final FluidRegistryObject<? extends FluidDeferredRegister.MekanismFluidType, ?, ?, ?, ?> ANTIDEUTERIUM_FLUID = FLUIDS_REGISTRY.register("antideuterium", properties -> properties.tint(0xFFFF3232));
// Warp Plasma
public static final FluidRegistryObject<? extends FluidDeferredRegister.MekanismFluidType, ?, ?, ?, ?> WARP_PLASMA_FLUID = FLUIDS_REGISTRY.register("warp_plasma", properties -> properties.tint(0xFFFF3232));
// Propane (refrigerant)
public static final FluidRegistryObject<? extends FluidDeferredRegister.MekanismFluidType, ?, ?, ?, ?> PROPANE_FLUID = FLUIDS_REGISTRY.register("propane", properties -> properties.tint(0xFFFF3232));
}

View File

@ -0,0 +1,30 @@
package uk.gemwire.engage.registries;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.levelgen.GenerationStep;
import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver;
import net.minecraft.world.level.levelgen.carver.WorldCarver;
import net.minecraftforge.common.world.BiomeModifier;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import net.minecraftforge.registries.RegistryObject;
import uk.gemwire.engage.worldgen.PropaneCaveCarver;
import uk.gemwire.engage.worldgen.PropaneCaveCarverConfiguration;
import uk.gemwire.engage.worldgen.modifiers.AddCarverBiomeModifier;
public class Worldgen {
public static final DeferredRegister<WorldCarver<?>> CARVER_REGISTRY = DeferredRegister.create(ForgeRegistries.WORLD_CARVERS, "engage");
public static final DeferredRegister<Codec<? extends BiomeModifier>> BIOME_MODIFIER_SERIALIZERS = DeferredRegister.create(ForgeRegistries.Keys.BIOME_MODIFIER_SERIALIZERS, "engage");
public static final RegistryObject<WorldCarver<?>> PROPANE_CARVER = CARVER_REGISTRY.register("propane_cave", () -> new PropaneCaveCarver(PropaneCaveCarverConfiguration.CODEC));
public static final RegistryObject<Codec<AddCarverBiomeModifier>> ADD_CARVERS_BIOME_MODIFIER_TYPE = Worldgen.BIOME_MODIFIER_SERIALIZERS.register("add_carvers", () ->
RecordCodecBuilder.create(builder -> builder.group(
Biome.LIST_CODEC.fieldOf("biomes").forGetter(AddCarverBiomeModifier::biomes),
ConfiguredWorldCarver.LIST_CODEC.fieldOf("carvers").forGetter(AddCarverBiomeModifier::carvers),
GenerationStep.Carving.CODEC.fieldOf("step").forGetter(AddCarverBiomeModifier::step)
).apply(builder, AddCarverBiomeModifier::new))
);
}

View File

@ -0,0 +1,254 @@
package uk.gemwire.engage.registries.fluid;
import net.minecraft.Util;
import net.minecraft.client.Minecraft;
import net.minecraft.core.BlockPos;
import net.minecraft.core.BlockSource;
import net.minecraft.core.dispenser.DefaultDispenseItemBehavior;
import net.minecraft.core.dispenser.DispenseItemBehavior;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.world.item.*;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.DispenserBlock;
import net.minecraft.world.level.block.LiquidBlock;
import net.minecraft.world.level.block.state.BlockBehaviour;
import net.minecraft.world.level.material.Fluid;
import net.minecraft.world.level.material.MapColor;
import net.minecraft.world.level.material.PushReaction;
import net.minecraftforge.client.extensions.common.IClientFluidTypeExtensions;
import net.minecraftforge.common.SoundActions;
import net.minecraftforge.eventbus.api.IEventBus;
import net.minecraftforge.fluids.FluidStack;
import net.minecraftforge.fluids.FluidType;
import net.minecraftforge.fluids.ForgeFlowingFluid;
import net.minecraftforge.registries.DeferredRegister;
import net.minecraftforge.registries.ForgeRegistries;
import org.jetbrains.annotations.NotNull;
import javax.annotation.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.function.BiFunction;
import java.util.function.Consumer;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
// Yoinked from https://github.com/mekanism/Mekanism/blob/1.20.x/src/main/java/mekanism/common/registration/impl/FluidDeferredRegister.java
public class FluidDeferredRegister {
private static final ResourceLocation OVERLAY = new ResourceLocation("block/water_overlay");
private static final ResourceLocation RENDER_OVERLAY = new ResourceLocation("misc/underwater");
private static final ResourceLocation LIQUID = new ResourceLocation("engage", "liquid/liquid");
private static final ResourceLocation LIQUID_FLOW = new ResourceLocation("engage", "liquid/liquid_flow");
//Copy of/based off of vanilla's lava/water bucket dispense behavior
private static final DispenseItemBehavior BUCKET_DISPENSE_BEHAVIOR = new DefaultDispenseItemBehavior() {
@NotNull
@Override
public ItemStack execute(@NotNull BlockSource source, @NotNull ItemStack stack) {
Level world = source.getLevel();
DispensibleContainerItem bucket = (DispensibleContainerItem) stack.getItem();
BlockPos pos = source.getPos().relative(source.getBlockState().getValue(DispenserBlock.FACING));
if (bucket.emptyContents(null, world, pos, null)) {
bucket.checkExtraContent(null, world, stack, pos);
return new ItemStack(Items.BUCKET);
}
return super.execute(source, stack);
}
};
public static FluidType.Properties getMekBaseBuilder() {
return FluidType.Properties.create()
.sound(SoundActions.BUCKET_FILL, SoundEvents.BUCKET_FILL)
.sound(SoundActions.BUCKET_EMPTY, SoundEvents.BUCKET_EMPTY);
}
private final List<FluidRegistryObject<? extends MekanismFluidType, ?, ?, ?, ?>> allFluids = new ArrayList<>();
private final DeferredRegister<FluidType> fluidTypeRegister;
private final DeferredRegister<Fluid> fluidRegister;
private final DeferredRegister<Block> blockRegister;
private final DeferredRegister<Item> itemRegister;
private final String modid;
public FluidDeferredRegister(String modid) {
this.modid = modid;
blockRegister = DeferredRegister.create(ForgeRegistries.BLOCKS, modid);
fluidRegister = DeferredRegister.create(ForgeRegistries.FLUIDS, modid);
fluidTypeRegister = DeferredRegister.create(ForgeRegistries.Keys.FLUID_TYPES, modid);
itemRegister = DeferredRegister.create(ForgeRegistries.ITEMS, modid);
}
public FluidRegistryObject<MekanismFluidType, ForgeFlowingFluid.Source, ForgeFlowingFluid.Flowing, LiquidBlock, BucketItem> registerLiquidChemical(IChemicalConstant constants) {
int density = Math.round(constants.getDensity());
return register(constants.getName(), properties -> properties
.temperature(Math.round(constants.getTemperature()))
.density(density)
.viscosity(density)
.lightLevel(constants.getLightLevel()), renderProperties -> renderProperties
.tint(constants.getColor())
);
}
public FluidRegistryObject<MekanismFluidType, ForgeFlowingFluid.Source, ForgeFlowingFluid.Flowing, LiquidBlock, BucketItem> register(String name, UnaryOperator<FluidTypeRenderProperties> renderProperties) {
return register(name, UnaryOperator.identity(), renderProperties);
}
public FluidRegistryObject<MekanismFluidType, ForgeFlowingFluid.Source, ForgeFlowingFluid.Flowing, LiquidBlock, BucketItem> register(String name, UnaryOperator<FluidType.Properties> properties,
UnaryOperator<FluidTypeRenderProperties> renderProperties) {
return register(name, BucketItem::new, properties, renderProperties);
}
public <BUCKET extends BucketItem> FluidRegistryObject<MekanismFluidType, ForgeFlowingFluid.Source, ForgeFlowingFluid.Flowing, LiquidBlock, BUCKET> register(String name, BucketCreator<BUCKET> bucketCreator,
UnaryOperator<FluidType.Properties> fluidProperties, UnaryOperator<FluidTypeRenderProperties> renderProperties) {
return register(name, fluidProperties.apply(getMekBaseBuilder()), renderProperties.apply(FluidTypeRenderProperties.builder()), bucketCreator,
MekanismFluidType::new);
}
public <TYPE extends MekanismFluidType, BUCKET extends BucketItem> FluidRegistryObject<TYPE, ForgeFlowingFluid.Source, ForgeFlowingFluid.Flowing, LiquidBlock, BUCKET> register(String name,
FluidType.Properties properties, FluidTypeRenderProperties renderProperties, BucketCreator<BUCKET> bucketCreator,
BiFunction<FluidType.Properties, FluidTypeRenderProperties, TYPE> fluidTypeCreator) {
String flowingName = "flowing_" + name;
String bucketName = name + "_bucket";
//Set the translation string to the same as the block
properties.descriptionId(Util.makeDescriptionId("block", new ResourceLocation(modid, name)));
//Create the registry object and let the values init to null as before we actually call get on them, we will update the backing values
FluidRegistryObject<TYPE, ForgeFlowingFluid.Source, ForgeFlowingFluid.Flowing, LiquidBlock, BUCKET> fluidRegistryObject = new FluidRegistryObject<>();
//Pass in suppliers that are wrapped instead of direct references to the registry objects, so that when we update the registry object to
// point to a new object it gets updated properly.
ForgeFlowingFluid.Properties fluidProperties = new ForgeFlowingFluid.Properties(fluidRegistryObject::getFluidType, fluidRegistryObject::getStillFluid,
fluidRegistryObject::getFlowingFluid).bucket(fluidRegistryObject::getBucket).block(fluidRegistryObject::getBlock);
//Update the references to objects that are retrieved from the deferred registers
fluidRegistryObject.updateFluidType(fluidTypeRegister.register(name, () -> fluidTypeCreator.apply(properties, renderProperties)));
fluidRegistryObject.updateStill(fluidRegister.register(name, () -> new ForgeFlowingFluid.Source(fluidProperties)));
fluidRegistryObject.updateFlowing(fluidRegister.register(flowingName, () -> new ForgeFlowingFluid.Flowing(fluidProperties)));
fluidRegistryObject.updateBucket(itemRegister.register(bucketName, () -> bucketCreator.create(fluidRegistryObject::getStillFluid,
new Item.Properties().stacksTo(1).craftRemainder(Items.BUCKET))));
//Note: The block properties used here is a copy of the ones for water
fluidRegistryObject.updateBlock(blockRegister.register(name, () -> new LiquidBlock(fluidRegistryObject::getStillFluid, BlockBehaviour.Properties.of()
.noCollission().strength(100.0F).noLootTable().replaceable().pushReaction(PushReaction.DESTROY).liquid().mapColor(MapColor.WATER))));
allFluids.add(fluidRegistryObject);
return fluidRegistryObject;
}
public void register(IEventBus bus) {
blockRegister.register(bus);
fluidRegister.register(bus);
fluidTypeRegister.register(bus);
itemRegister.register(bus);
}
public List<FluidRegistryObject<? extends MekanismFluidType, ?, ?, ?, ?>> getAllFluids() {
return Collections.unmodifiableList(allFluids);
}
public void registerBucketDispenserBehavior() {
for (FluidRegistryObject<?, ?, ?, ?, ?> fluidRO : getAllFluids()) {
DispenserBlock.registerBehavior(fluidRO.getBucket(), BUCKET_DISPENSE_BEHAVIOR);
}
}
@FunctionalInterface
public interface BucketCreator<BUCKET extends BucketItem> {
BUCKET create(Supplier<? extends Fluid> supplier, Item.Properties builder);
}
public static class FluidTypeRenderProperties {
private ResourceLocation stillTexture = LIQUID;
private ResourceLocation flowingTexture = LIQUID_FLOW;
//For now all our fluids use the same "overlay" for being against glass as vanilla water.
private ResourceLocation overlayTexture = OVERLAY;
private ResourceLocation renderOverlayTexture = RENDER_OVERLAY;
private int color = 0xFFFFFFFF;
private FluidTypeRenderProperties() {
}
public static FluidTypeRenderProperties builder() {
return new FluidTypeRenderProperties();
}
public FluidTypeRenderProperties texture(ResourceLocation still, ResourceLocation flowing) {
this.stillTexture = still;
this.flowingTexture = flowing;
return this;
}
public FluidTypeRenderProperties texture(ResourceLocation still, ResourceLocation flowing, ResourceLocation overlay) {
this.stillTexture = still;
this.flowingTexture = flowing;
this.overlayTexture = overlay;
return this;
}
public FluidTypeRenderProperties renderOverlay(ResourceLocation renderOverlay) {
this.renderOverlayTexture = renderOverlay;
return this;
}
public FluidTypeRenderProperties tint(int color) {
this.color = color;
return this;
}
}
public static class MekanismFluidType extends FluidType {
public final ResourceLocation stillTexture;
public final ResourceLocation flowingTexture;
public final ResourceLocation overlayTexture;
public final ResourceLocation renderOverlayTexture;
private final int color;
public MekanismFluidType(FluidType.Properties properties, FluidTypeRenderProperties renderProperties) {
super(properties);
this.stillTexture = renderProperties.stillTexture;
this.flowingTexture = renderProperties.flowingTexture;
this.overlayTexture = renderProperties.overlayTexture;
this.renderOverlayTexture = renderProperties.renderOverlayTexture;
this.color = renderProperties.color;
}
@Override
public boolean isVaporizedOnPlacement(Level level, BlockPos pos, FluidStack stack) {
//TODO - 1.19: Decide on this for our fluids for now default to not vaporizing
return false;
}
@Override
public void initializeClient(Consumer<IClientFluidTypeExtensions> consumer) {
consumer.accept(new IClientFluidTypeExtensions() {
@Override
public ResourceLocation getStillTexture() {
return stillTexture;
}
@Override
public ResourceLocation getFlowingTexture() {
return flowingTexture;
}
@Override
public ResourceLocation getOverlayTexture() {
return overlayTexture;
}
@Nullable
@Override
public ResourceLocation getRenderOverlayTexture(Minecraft mc) {
return renderOverlayTexture;
}
@Override
public int getTintColor() {
return color;
}
});
}
}
}

View File

@ -0,0 +1,66 @@
package uk.gemwire.engage.registries.fluid;
import net.minecraft.world.item.BucketItem;
import net.minecraft.world.level.block.LiquidBlock;
import net.minecraft.world.level.material.Fluid;
import net.minecraftforge.fluids.FluidType;
import net.minecraftforge.registries.RegistryObject;
import java.util.Objects;
// Yoinked from https://github.com/mekanism/Mekanism/blob/1.20.x/src/main/java/mekanism/common/registration/impl/FluidRegistryObject.java
public class FluidRegistryObject<TYPE extends FluidType, STILL extends Fluid, FLOWING extends Fluid, BLOCK extends LiquidBlock, BUCKET extends BucketItem> {
private RegistryObject<TYPE> fluidTypeRO;
private RegistryObject<STILL> stillRO;
private RegistryObject<FLOWING> flowingRO;
private RegistryObject<BLOCK> blockRO;
private RegistryObject<BUCKET> bucketRO;
public TYPE getFluidType() {
return fluidTypeRO.get();
}
public STILL getStillFluid() {
return stillRO.get();
}
public FLOWING getFlowingFluid() {
return flowingRO.get();
}
public BLOCK getBlock() {
return blockRO.get();
}
public BUCKET getBucket() {
return bucketRO.get();
}
//Make sure these update methods are package local as only the FluidDeferredRegister should be messing with them
void updateFluidType(RegistryObject<TYPE> fluidTypeRO) {
this.fluidTypeRO = Objects.requireNonNull(fluidTypeRO);
}
void updateStill(RegistryObject<STILL> stillRO) {
this.stillRO = Objects.requireNonNull(stillRO);
}
void updateFlowing(RegistryObject<FLOWING> flowingRO) {
this.flowingRO = Objects.requireNonNull(flowingRO);
}
void updateBlock(RegistryObject<BLOCK> blockRO) {
this.blockRO = Objects.requireNonNull(blockRO);
}
void updateBucket(RegistryObject<BUCKET> bucketRO) {
this.bucketRO = Objects.requireNonNull(bucketRO);
}
public STILL getFluid() {
//Default our fluid to being the still variant
return getStillFluid();
}
}

View File

@ -0,0 +1,30 @@
package uk.gemwire.engage.registries.fluid;
// Yoinked from https://github.com/mekanism/Mekanism/blob/1.20.x/src/main/java/mekanism/common/base/IChemicalConstant.java
public interface IChemicalConstant {
/**
* @return The name of the chemical
*/
String getName();
/**
* @return Visual color in ARGB format
*/
int getColor();
/**
* @return Temperature in Kelvin that the chemical exists as a liquid
*/
float getTemperature();
/**
* @return Density as a liquid in kg/m^3
*/
float getDensity();
/**
* @return Brightness
*/
int getLightLevel();
}

View File

@ -0,0 +1,211 @@
package uk.gemwire.engage.worldgen;
import com.mojang.serialization.Codec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Holder;
import net.minecraft.core.SectionPos;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.ChunkPos;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.chunk.CarvingMask;
import net.minecraft.world.level.chunk.ChunkAccess;
import net.minecraft.world.level.levelgen.Aquifer;
import net.minecraft.world.level.levelgen.DensityFunction;
import net.minecraft.world.level.levelgen.carver.CarverConfiguration;
import net.minecraft.world.level.levelgen.carver.CarvingContext;
import net.minecraft.world.level.levelgen.carver.WorldCarver;
import org.apache.commons.lang3.mutable.MutableBoolean;
import uk.gemwire.engage.registries.Fluids;
import javax.annotation.Nullable;
import java.util.function.Function;
public class PropaneCaveCarver extends WorldCarver<PropaneCaveCarverConfiguration> {
public PropaneCaveCarver(Codec<PropaneCaveCarverConfiguration> p_159194_) {
super(p_159194_);
}
public boolean isStartChunk(PropaneCaveCarverConfiguration p_224894_, RandomSource p_224895_) {
return p_224895_.nextFloat() <= p_224894_.probability;
}
/**
* Redirect CarveState to place Propane instead of Lava.
*/
@Nullable
private BlockState getCarveState(CarvingContext p_159419_, PropaneCaveCarverConfiguration p_159420_, BlockPos p_159421_, Aquifer p_159422_) {
if (p_159421_.getY() <= p_159420_.lavaLevel.resolveY(p_159419_)) {
return Fluids.PROPANE_FLUID.getBlock().defaultBlockState();
} else {
BlockState blockstate = p_159422_.computeSubstance(new DensityFunction.SinglePointContext(p_159421_.getX(), p_159421_.getY(), p_159421_.getZ()), 0.0D);
if (blockstate == null) {
return isDebugEnabled(p_159420_) ? p_159420_.debugSettings.getBarrierState() : null;
} else {
return isDebugEnabled(p_159420_) ? getDebugState(p_159420_, blockstate) : blockstate;
}
}
}
@Override
protected boolean carveBlock(CarvingContext p_190744_, PropaneCaveCarverConfiguration p_190745_, ChunkAccess p_190746_, Function<BlockPos, Holder<Biome>> p_190747_, CarvingMask p_190748_, BlockPos.MutableBlockPos p_190749_, BlockPos.MutableBlockPos p_190750_, Aquifer p_190751_, MutableBoolean p_190752_) {
BlockState blockstate = p_190746_.getBlockState(p_190749_);
if (blockstate.is(Blocks.GRASS_BLOCK) || blockstate.is(Blocks.MYCELIUM)) {
p_190752_.setTrue();
}
if (!this.canReplaceBlock(p_190745_, blockstate) && !isDebugEnabled(p_190745_)) {
return false;
} else {
BlockState blockstate1 = (p_190749_.getY() <= p_190745_.lavaLevel.resolveY(p_190744_)) ? Fluids.PROPANE_FLUID.getBlock().defaultBlockState() :this.getCarveState(p_190744_, p_190745_, p_190749_, p_190751_);
if (blockstate1 == null) {
return false;
} else {
p_190746_.setBlockState(p_190749_, blockstate1, false);
if (p_190751_.shouldScheduleFluidUpdate() && !blockstate1.getFluidState().isEmpty()) {
p_190746_.markPosForPostprocessing(p_190749_);
}
if (p_190752_.isTrue()) {
p_190750_.setWithOffset(p_190749_, Direction.DOWN);
if (p_190746_.getBlockState(p_190750_).is(Blocks.DIRT)) {
p_190744_.topMaterial(p_190747_, p_190746_, p_190750_, !blockstate1.getFluidState().isEmpty()).ifPresent((p_284918_) -> {
p_190746_.setBlockState(p_190750_, p_284918_, false);
if (!p_284918_.getFluidState().isEmpty()) {
p_190746_.markPosForPostprocessing(p_190750_);
}
});
}
}
return true;
}
}
}
private static boolean isDebugEnabled(CarverConfiguration p_159424_) {
return p_159424_.debugSettings.isDebugMode();
}
private static BlockState getDebugState(CarverConfiguration p_159382_, BlockState p_159383_) {
if (p_159383_.is(Blocks.AIR)) {
return p_159382_.debugSettings.getAirState();
} else if (p_159383_.is(Blocks.WATER)) {
BlockState blockstate = p_159382_.debugSettings.getWaterState();
return blockstate.hasProperty(BlockStateProperties.WATERLOGGED) ? blockstate.setValue(BlockStateProperties.WATERLOGGED, Boolean.valueOf(true)) : blockstate;
} else {
return p_159383_.is(Blocks.LAVA) ? p_159382_.debugSettings.getLavaState() : p_159383_;
}
}
public boolean carve(CarvingContext p_224885_, PropaneCaveCarverConfiguration p_224886_, ChunkAccess p_224887_, Function<BlockPos, Holder<Biome>> p_224888_, RandomSource p_224889_, Aquifer p_224890_, ChunkPos p_224891_, CarvingMask p_224892_) {
int i = SectionPos.sectionToBlockCoord(this.getRange() * 2 - 1);
int j = p_224889_.nextInt(p_224889_.nextInt(p_224889_.nextInt(this.getCaveBound()) + 1) + 1);
for(int k = 0; k < j; ++k) {
double d0 = (double)p_224891_.getBlockX(p_224889_.nextInt(16));
double d1 = (double)p_224886_.y.sample(p_224889_, p_224885_);
double d2 = (double)p_224891_.getBlockZ(p_224889_.nextInt(16));
double d3 = (double)p_224886_.horizontalRadiusMultiplier.sample(p_224889_);
double d4 = (double)p_224886_.verticalRadiusMultiplier.sample(p_224889_);
double d5 = (double)p_224886_.floorLevel.sample(p_224889_);
WorldCarver.CarveSkipChecker worldcarver$carveskipchecker = (p_159202_, p_159203_, p_159204_, p_159205_, p_159206_) -> {
return shouldSkip(p_159203_, p_159204_, p_159205_, d5);
};
int l = 1;
if (p_224889_.nextInt(4) == 0) {
double d6 = (double)p_224886_.yScale.sample(p_224889_);
float f1 = 1.0F + p_224889_.nextFloat() * 6.0F;
this.createRoom(p_224885_, p_224886_, p_224887_, p_224888_, p_224890_, d0, d1, d2, f1, d6, p_224892_, worldcarver$carveskipchecker);
l += p_224889_.nextInt(4);
}
for(int k1 = 0; k1 < l; ++k1) {
float f = p_224889_.nextFloat() * ((float)Math.PI * 2F);
float f3 = (p_224889_.nextFloat() - 0.5F) / 4.0F;
float f2 = this.getThickness(p_224889_);
int i1 = i - p_224889_.nextInt(i / 4);
int j1 = 0;
this.createTunnel(p_224885_, p_224886_, p_224887_, p_224888_, p_224889_.nextLong(), p_224890_, d0, d1, d2, d3, d4, f2, f, f3, 0, i1, this.getYScale(), p_224892_, worldcarver$carveskipchecker);
}
}
return true;
}
protected int getCaveBound() {
return 15;
}
protected float getThickness(RandomSource p_224871_) {
float f = p_224871_.nextFloat() * 2.0F + p_224871_.nextFloat();
if (p_224871_.nextInt(10) == 0) {
f *= p_224871_.nextFloat() * p_224871_.nextFloat() * 3.0F + 1.0F;
}
return f;
}
protected double getYScale() {
return 1.0D;
}
protected void createRoom(CarvingContext p_190691_, PropaneCaveCarverConfiguration p_190692_, ChunkAccess p_190693_, Function<BlockPos, Holder<Biome>> p_190694_, Aquifer p_190695_, double p_190696_, double p_190697_, double p_190698_, float p_190699_, double p_190700_, CarvingMask p_190701_, WorldCarver.CarveSkipChecker p_190702_) {
double d0 = 1.5D + (double)(Mth.sin(((float)Math.PI / 2F)) * p_190699_);
double d1 = d0 * p_190700_;
this.carveEllipsoid(p_190691_, p_190692_, p_190693_, p_190694_, p_190695_, p_190696_ + 1.0D, p_190697_, p_190698_, d0, d1, p_190701_, p_190702_);
}
protected void createTunnel(CarvingContext p_190671_, PropaneCaveCarverConfiguration p_190672_, ChunkAccess p_190673_, Function<BlockPos, Holder<Biome>> p_190674_, long p_190675_, Aquifer p_190676_, double p_190677_, double p_190678_, double p_190679_, double p_190680_, double p_190681_, float p_190682_, float p_190683_, float p_190684_, int p_190685_, int p_190686_, double p_190687_, CarvingMask p_190688_, WorldCarver.CarveSkipChecker p_190689_) {
RandomSource randomsource = RandomSource.create(p_190675_);
int i = randomsource.nextInt(p_190686_ / 2) + p_190686_ / 4;
boolean flag = randomsource.nextInt(6) == 0;
float f = 0.0F;
float f1 = 0.0F;
for(int j = p_190685_; j < p_190686_; ++j) {
double d0 = 1.5D + (double)(Mth.sin((float)Math.PI * (float)j / (float)p_190686_) * p_190682_);
double d1 = d0 * p_190687_;
float f2 = Mth.cos(p_190684_);
p_190677_ += (double)(Mth.cos(p_190683_) * f2);
p_190678_ += (double)Mth.sin(p_190684_);
p_190679_ += (double)(Mth.sin(p_190683_) * f2);
p_190684_ *= flag ? 0.92F : 0.7F;
p_190684_ += f1 * 0.1F;
p_190683_ += f * 0.1F;
f1 *= 0.9F;
f *= 0.75F;
f1 += (randomsource.nextFloat() - randomsource.nextFloat()) * randomsource.nextFloat() * 2.0F;
f += (randomsource.nextFloat() - randomsource.nextFloat()) * randomsource.nextFloat() * 4.0F;
if (j == i && p_190682_ > 1.0F) {
this.createTunnel(p_190671_, p_190672_, p_190673_, p_190674_, randomsource.nextLong(), p_190676_, p_190677_, p_190678_, p_190679_, p_190680_, p_190681_, randomsource.nextFloat() * 0.5F + 0.5F, p_190683_ - ((float)Math.PI / 2F), p_190684_ / 3.0F, j, p_190686_, 1.0D, p_190688_, p_190689_);
this.createTunnel(p_190671_, p_190672_, p_190673_, p_190674_, randomsource.nextLong(), p_190676_, p_190677_, p_190678_, p_190679_, p_190680_, p_190681_, randomsource.nextFloat() * 0.5F + 0.5F, p_190683_ + ((float)Math.PI / 2F), p_190684_ / 3.0F, j, p_190686_, 1.0D, p_190688_, p_190689_);
return;
}
if (randomsource.nextInt(4) != 0) {
if (!canReach(p_190673_.getPos(), p_190677_, p_190679_, j, p_190686_, p_190682_)) {
return;
}
this.carveEllipsoid(p_190671_, p_190672_, p_190673_, p_190674_, p_190676_, p_190677_, p_190678_, p_190679_, d0 * p_190680_, d1 * p_190681_, p_190688_, p_190689_);
}
}
}
private static boolean shouldSkip(double p_159196_, double p_159197_, double p_159198_, double p_159199_) {
if (p_159197_ <= p_159199_) {
return true;
} else {
return p_159196_ * p_159196_ + p_159197_ * p_159197_ + p_159198_ * p_159198_ >= 1.0D;
}
}
}

View File

@ -0,0 +1,44 @@
package uk.gemwire.engage.worldgen;
import com.mojang.serialization.Codec;
import com.mojang.serialization.codecs.RecordCodecBuilder;
import net.minecraft.core.HolderSet;
import net.minecraft.util.valueproviders.FloatProvider;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.levelgen.VerticalAnchor;
import net.minecraft.world.level.levelgen.carver.CarverConfiguration;
import net.minecraft.world.level.levelgen.carver.CarverDebugSettings;
import net.minecraft.world.level.levelgen.heightproviders.HeightProvider;
public class PropaneCaveCarverConfiguration extends CarverConfiguration {
public static final Codec<PropaneCaveCarverConfiguration> CODEC = RecordCodecBuilder.create((p_159184_) -> {
return p_159184_.group(CarverConfiguration.CODEC.forGetter((p_159192_) -> {
return p_159192_;
}), FloatProvider.CODEC.fieldOf("horizontal_radius_multiplier").forGetter((p_159190_) -> {
return p_159190_.horizontalRadiusMultiplier;
}), FloatProvider.CODEC.fieldOf("vertical_radius_multiplier").forGetter((p_159188_) -> {
return p_159188_.verticalRadiusMultiplier;
}), FloatProvider.codec(-1.0F, 1.0F).fieldOf("floor_level").forGetter((p_159186_) -> {
return p_159186_.floorLevel;
})).apply(p_159184_, PropaneCaveCarverConfiguration::new);
});
public final FloatProvider horizontalRadiusMultiplier;
public final FloatProvider verticalRadiusMultiplier;
public final FloatProvider floorLevel;
public PropaneCaveCarverConfiguration(float p_224853_, HeightProvider p_224854_, FloatProvider p_224855_, VerticalAnchor p_224856_, CarverDebugSettings p_224857_, HolderSet<Block> p_224858_, FloatProvider p_224859_, FloatProvider p_224860_, FloatProvider p_224861_) {
super(p_224853_, p_224854_, p_224855_, p_224856_, p_224857_, p_224858_);
this.horizontalRadiusMultiplier = p_224859_;
this.verticalRadiusMultiplier = p_224860_;
this.floorLevel = p_224861_;
}
public PropaneCaveCarverConfiguration(float p_224863_, HeightProvider p_224864_, FloatProvider p_224865_, VerticalAnchor p_224866_, HolderSet<Block> p_224867_, FloatProvider p_224868_, FloatProvider p_224869_, FloatProvider p_224870_) {
this(p_224863_, p_224864_, p_224865_, p_224866_, CarverDebugSettings.DEFAULT, p_224867_, p_224868_, p_224869_, p_224870_);
}
public PropaneCaveCarverConfiguration(CarverConfiguration p_159179_, FloatProvider p_159180_, FloatProvider p_159181_, FloatProvider p_159182_) {
this(p_159179_.probability, p_159179_.y, p_159179_.yScale, p_159179_.lavaLevel, p_159179_.debugSettings, p_159179_.replaceable, p_159180_, p_159181_, p_159182_);
}
}

View File

@ -0,0 +1,31 @@
package uk.gemwire.engage.worldgen.modifiers;
import com.mojang.serialization.Codec;
import net.minecraft.core.Holder;
import net.minecraft.core.HolderSet;
import net.minecraft.world.level.biome.Biome;
import net.minecraft.world.level.levelgen.GenerationStep;
import net.minecraft.world.level.levelgen.carver.ConfiguredWorldCarver;
import net.minecraftforge.common.world.BiomeGenerationSettingsBuilder;
import net.minecraftforge.common.world.BiomeModifier;
import net.minecraftforge.common.world.ModifiableBiomeInfo;
import uk.gemwire.engage.registries.Worldgen;
public record AddCarverBiomeModifier(HolderSet<Biome> biomes, HolderSet<ConfiguredWorldCarver<?>> carvers, GenerationStep.Carving step) implements BiomeModifier {
@Override
public void modify(Holder<Biome> biome, Phase phase, ModifiableBiomeInfo.BiomeInfo.Builder builder)
{
if (phase == Phase.ADD && this.biomes.contains(biome))
{
BiomeGenerationSettingsBuilder generationSettings = builder.getGenerationSettings();
this.carvers.forEach(holder -> generationSettings.addCarver(this.step, holder));
}
}
@Override
public Codec<? extends BiomeModifier> codec()
{
return Worldgen.ADD_CARVERS_BIOME_MODIFIER_TYPE.get();
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 5.0 KiB

View File

@ -0,0 +1,5 @@
{
"animation": {
"frametime": 2
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.4 KiB

View File

@ -0,0 +1,5 @@
{
"animation": {
"frametime": 2
}
}

View File

@ -0,0 +1,6 @@
{
"type": "engage:add_carvers",
"biomes": "#minecraft:is_overworld",
"carvers": "engage:propane_cave",
"step": "air"
}

View File

@ -0,0 +1,49 @@
{
"type": "engage:propane_cave",
"config": {
"debug_settings": {
},
"floor_level": {
"type": "minecraft:uniform",
"value": {
"max_exclusive": -0.4,
"min_inclusive": -1.0
}
},
"horizontal_radius_multiplier": {
"type": "minecraft:uniform",
"value": {
"max_exclusive": 1.4,
"min_inclusive": 0.7
}
},
"lava_level": {
"below_top": 3
},
"probability": 0.005,
"replaceable": "#minecraft:overworld_carver_replaceables",
"vertical_radius_multiplier": {
"type": "minecraft:uniform",
"value": {
"max_exclusive": 1.3,
"min_inclusive": 0.8
}
},
"y": {
"type": "minecraft:uniform",
"max_inclusive": {
"absolute": 180
},
"min_inclusive": {
"above_bottom": 8
}
},
"yScale": {
"type": "minecraft:uniform",
"value": {
"max_exclusive": 0.9,
"min_inclusive": 0.1
}
}
}
}