port A LOT

This commit is contained in:
2026-01-10 23:36:28 -05:00
parent dfde4b2315
commit 9a954a0af1
446 changed files with 57881 additions and 71 deletions
+3 -3
View File
@@ -74,11 +74,10 @@ is mod is in a VERY early stage of development. Expect bugs, missing features, a
- Pull the repository to your local machine. - Pull the repository to your local machine.
- Allow Gradle to sync completely (usually takes ~2 minutes). - Allow Gradle to sync completely (usually takes ~2 minutes).
2. **Select Version (Code Editing Only)** 2. **Select Version**
- If you intend to modify the source code, use the included run configurations to set your workspace and have Java 21 installed and available: - Use the included run configurations to set your workspace and have Java 21 installed and available:
- `Switch to 1.20.1` - `Switch to 1.20.1`
- `Switch to 1.21.1` - `Switch to 1.21.1`
- *Note: You can skip this step if you just want to launch the game.*
- *Note: With IntelliJ idea, you can install the Stonecutter plugin and it becomes a dropdown at the top left* - *Note: With IntelliJ idea, you can install the Stonecutter plugin and it becomes a dropdown at the top left*
3. **Launch the Game** 3. **Launch the Game**
@@ -86,3 +85,4 @@ is mod is in a VERY early stage of development. Expect bugs, missing features, a
- **Fabric:** 1.20.1 & 1.21.1 - **Fabric:** 1.20.1 & 1.21.1
- **NeoForge:** 1.21.1 - **NeoForge:** 1.21.1
- **Forge:** 1.20.1 - **Forge:** 1.20.1
- *Note: Be sure to have the proper stonecutter version seleted*
+8
View File
@@ -31,6 +31,7 @@ loom {
repositories { repositories {
mavenCentral() mavenCentral()
maven("https://api.modrinth.com/maven") { name = "Modrinth" }
maven("https://maven.architectury.dev/") maven("https://maven.architectury.dev/")
maven("https://maven.terraformersmc.com/releases/") maven("https://maven.terraformersmc.com/releases/")
maven("https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/") maven("https://dl.cloudsmith.io/public/geckolib3/geckolib/maven/")
@@ -59,6 +60,13 @@ dependencies {
// JEI (Common API) // JEI (Common API)
// We use "modCompileOnly" because we only need the API to write code, not the full mod // We use "modCompileOnly" because we only need the API to write code, not the full mod
modCompileOnly("mezz.jei:jei-${commonMod.minecraft_version}-common-api:${commonMod.prop("jei_version")}") modCompileOnly("mezz.jei:jei-${commonMod.minecraft_version}-common-api:${commonMod.prop("jei_version")}")
// Jade (Common API)
val jadeVersion = if (is120) commonMod.prop("jade_version_1_20_1") else commonMod.prop("jade_version_1_21_1")
val jadeClassifier = if (is120) "forge" else "fabric"
// Use modCompileOnly to provide the API for compilation
modCompileOnly("maven.modrinth:jade:$jadeVersion+$jadeClassifier")
} }
val commonJava: Configuration by configurations.creating { val commonJava: Configuration by configurations.creating {
@@ -1,11 +1,19 @@
package net.cmr.jurassicrevived; package net.cmr.jurassicrevived;
import dev.architectury.event.events.common.LifecycleEvent;
import net.cmr.jurassicrevived.block.ModBlocks; import net.cmr.jurassicrevived.block.ModBlocks;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.config.JRConfigManager; import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.entity.ModEntities; import net.cmr.jurassicrevived.entity.ModEntities;
import net.cmr.jurassicrevived.item.ModCreativeTabs; import net.cmr.jurassicrevived.item.ModCreativeTabs;
import net.cmr.jurassicrevived.item.ModItems; import net.cmr.jurassicrevived.item.ModItems;
import net.cmr.jurassicrevived.platform.Services; import net.cmr.jurassicrevived.platform.Services;
import net.cmr.jurassicrevived.recipe.ModRecipes;
import net.cmr.jurassicrevived.screen.ModMenuTypes;
import net.cmr.jurassicrevived.sound.ModSounds;
import net.cmr.jurassicrevived.util.FenceClimbHandler;
import net.cmr.jurassicrevived.util.FenceDiagonalHandler;
import net.cmr.jurassicrevived.util.ModEvents;
import net.minecraft.core.registries.BuiltInRegistries; import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.world.item.Items; import net.minecraft.world.item.Items;
@@ -38,8 +46,27 @@ public class CommonClass
JRConfigManager.load(Services.PLATFORM.getConfigDir()); JRConfigManager.load(Services.PLATFORM.getConfigDir());
ModBlocks.register(); ModBlocks.register();
ModEntities.register();
ModItems.register(); ModItems.register();
ModCreativeTabs.register(); ModCreativeTabs.register();
ModEntities.register(); ModEntities.registerAttributes();
ModMenuTypes.register();
ModRecipes.register();
LifecycleEvent.SETUP.register(() -> {
ModBlocks.setupPots();
//ModEntities.registerSpawnPlacements();
});
ModBlockEntities.register();
FenceClimbHandler.register();
FenceDiagonalHandler.init();
ModEvents.init();
ModSounds.register();
} }
} }
@@ -0,0 +1,124 @@
package net.cmr.jurassicrevived;
import dev.architectury.registry.client.level.entity.EntityRendererRegistry;
import dev.architectury.registry.menu.MenuRegistry;
import dev.architectury.registry.client.rendering.BlockEntityRendererRegistry;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.renderer.TankBlockEntityRenderer;
import net.cmr.jurassicrevived.entity.ModEntities;
import net.cmr.jurassicrevived.entity.client.*;
import net.cmr.jurassicrevived.screen.ModMenuTypes;
import net.cmr.jurassicrevived.screen.custom.*;
import net.cmr.jurassicrevived.sound.MachineHumSoundHandler;
import net.cmr.jurassicrevived.util.FenceClimbClientHandler;
import net.minecraft.client.renderer.entity.NoopRenderer;
import dev.architectury.event.events.common.LifecycleEvent;
public class CommonClientClass {
public static void init() {
// Initialize client-only systems
MachineHumSoundHandler.init();
FenceClimbClientHandler.register();
// Register Entity Renderers (Architectury handles suppliers here)
EntityRendererRegistry.register(ModEntities.SEAT, NoopRenderer::new);
EntityRendererRegistry.register(ModEntities.APATOSAURUS, ApatosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.ALBERTOSAURUS, AlbertosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.BARYONYX, BaryonyxRenderer::new);
EntityRendererRegistry.register(ModEntities.BRACHIOSAURUS, BrachiosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.CARNOTAURUS, CarnotaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.CERATOSAURUS, CeratosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.COMPSOGNATHUS, CompsognathusRenderer::new);
EntityRendererRegistry.register(ModEntities.CONCAVENATOR, ConcavenatorRenderer::new);
EntityRendererRegistry.register(ModEntities.DEINONYCHUS, DeinonychusRenderer::new);
EntityRendererRegistry.register(ModEntities.DILOPHOSAURUS, DilophosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.DIPLODOCUS, DiplodocusRenderer::new);
EntityRendererRegistry.register(ModEntities.DISTORTUS_REX, DistortusRexRenderer::new);
EntityRendererRegistry.register(ModEntities.EDMONTOSAURUS, EdmontosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.FDUCK, FDuckRenderer::new);
EntityRendererRegistry.register(ModEntities.GALLIMIMUS, GallimimusRenderer::new);
EntityRendererRegistry.register(ModEntities.GIGANOTOSAURUS, GiganotosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.GUANLONG, GuanlongRenderer::new);
EntityRendererRegistry.register(ModEntities.HERRERASAURUS, HerrerasaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.INDOMINUS_REX, IndominusRexRenderer::new);
EntityRendererRegistry.register(ModEntities.MAJUNGASAURUS, MajungasaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.OURANOSAURUS, OuranosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.PARASAUROLOPHUS, ParasaurolophusRenderer::new);
EntityRendererRegistry.register(ModEntities.PROCOMPSOGNATHUS, ProcompsognathusRenderer::new);
EntityRendererRegistry.register(ModEntities.PROTOCERATOPS, ProtoceratopsRenderer::new);
EntityRendererRegistry.register(ModEntities.ARAMBOURGIANIA, ArambourgianiaRenderer::new);
EntityRendererRegistry.register(ModEntities.CEARADACTYLUS, CearadactylusRenderer::new);
EntityRendererRegistry.register(ModEntities.DIMORPHODON, DimorphodonRenderer::new);
EntityRendererRegistry.register(ModEntities.GEOSTERNBERGIA, GeosternbergiaRenderer::new);
EntityRendererRegistry.register(ModEntities.GUIDRACO, GuidracoRenderer::new);
EntityRendererRegistry.register(ModEntities.LUDODACTYLUS, LudodactylusRenderer::new);
EntityRendererRegistry.register(ModEntities.MOGANOPTERUS, MoganopterusRenderer::new);
EntityRendererRegistry.register(ModEntities.NYCTOSAURUS, NyctosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.PTERANODON, PteranodonRenderer::new);
EntityRendererRegistry.register(ModEntities.PTERODAUSTRO, PterodaustroRenderer::new);
EntityRendererRegistry.register(ModEntities.QUETZALCOATLUS, QuetzalcoatlusRenderer::new);
EntityRendererRegistry.register(ModEntities.TAPEJARA, TapejaraRenderer::new);
EntityRendererRegistry.register(ModEntities.TROPEOGNATHUS, TropeognathusRenderer::new);
EntityRendererRegistry.register(ModEntities.TUPUXUARA, TupuxuaraRenderer::new);
EntityRendererRegistry.register(ModEntities.ZHENYUANOPTERUS, ZhenyuanopterusRenderer::new);
EntityRendererRegistry.register(ModEntities.RUGOPS, RugopsRenderer::new);
EntityRendererRegistry.register(ModEntities.SHANTUNGOSAURUS, ShantungosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.SPINOSAURUS, SpinosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.STEGOSAURUS, StegosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.STYRACOSAURUS, StyracosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.THERIZINOSAURUS, TherizinosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.TRICERATOPS, TriceratopsRenderer::new);
EntityRendererRegistry.register(ModEntities.TYRANNOSAURUS_REX, TyrannosaurusRexRenderer::new);
EntityRendererRegistry.register(ModEntities.VELOCIRAPTOR, VelociraptorRenderer::new);
EntityRendererRegistry.register(ModEntities.CHICKENOSAURUS, ChickenosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.ALLOSAURUS, AllosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.ALVAREZSAURUS, AlvarezsaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.ANKYLOSAURUS, AnkylosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.CARCHARODONTOSAURUS, CarcharodontosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.CHASMOSAURUS, ChasmosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.COELOPHYSIS, CoelophysisRenderer::new);
EntityRendererRegistry.register(ModEntities.COELURUS, CoelurusRenderer::new);
EntityRendererRegistry.register(ModEntities.CORYTHOSAURUS, CorythosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.DRYOSAURUS, DryosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.HADROSAURUS, HadrosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.HYPSILOPHODON, HypsilophodonRenderer::new);
EntityRendererRegistry.register(ModEntities.INDORAPTOR, IndoraptorRenderer::new);
EntityRendererRegistry.register(ModEntities.INOSTRANCEVIA, InostranceviaRenderer::new);
EntityRendererRegistry.register(ModEntities.LAMBEOSAURUS, LambeosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.MAMENCHISAURUS, MamenchisaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.METRIACANTHOSAURUS, MetriacanthosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.ORNITHOLESTES, OrnitholestesRenderer::new);
EntityRendererRegistry.register(ModEntities.ORNITHOMIMUS, OrnithomimusRenderer::new);
EntityRendererRegistry.register(ModEntities.OVIRAPTOR, OviraptorRenderer::new);
EntityRendererRegistry.register(ModEntities.PACHYCEPHALOSAURUS, PachycephalosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.PROCERATOSAURUS, ProceratosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.RAJASAURUS, RajasaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.SEGISAURUS, SegisaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.TITANOSAURUS, TitanosaurusRenderer::new);
EntityRendererRegistry.register(ModEntities.TROODON, TroodonRenderer::new);
EntityRendererRegistry.register(ModEntities.UTAHRAPTOR, UtahraptorRenderer::new);
//? if <=1.20.1 {
LifecycleEvent.SETUP.register(() -> {
MenuRegistry.registerScreenFactory(ModMenuTypes.GENERATOR_MENU.get(), GeneratorScreen::new);
MenuRegistry.registerScreenFactory(ModMenuTypes.DNA_EXTRACTOR_MENU.get(), DNAExtractorScreen::new);
MenuRegistry.registerScreenFactory(ModMenuTypes.DNA_ANALYZER_MENU.get(), DNAAnalyzerScreen::new);
MenuRegistry.registerScreenFactory(ModMenuTypes.FOSSIL_GRINDER_MENU.get(), FossilGrinderScreen::new);
MenuRegistry.registerScreenFactory(ModMenuTypes.FOSSIL_CLEANER_MENU.get(), FossilCleanerScreen::new);
MenuRegistry.registerScreenFactory(ModMenuTypes.DNA_HYBRIDIZER_MENU.get(), DNAHybridizerScreen::new);
MenuRegistry.registerScreenFactory(ModMenuTypes.EMBRYONIC_MACHINE_MENU.get(), EmbryonicMachineScreen::new);
MenuRegistry.registerScreenFactory(ModMenuTypes.EMBRYO_CALCIFICATION_MACHINE_MENU.get(), EmbryoCalcificationMachineScreen::new);
MenuRegistry.registerScreenFactory(ModMenuTypes.INCUBATOR_MENU.get(), IncubatorScreen::new);
MenuRegistry.registerScreenFactory(ModMenuTypes.TANK_MENU.get(), TankScreen::new);
MenuRegistry.registerScreenFactory(ModMenuTypes.POWER_CELL_MENU.get(), PowerCellScreen::new);
MenuRegistry.registerScreenFactory(ModMenuTypes.WOOD_CRATE_MENU.get(), CrateScreen::new);
MenuRegistry.registerScreenFactory(ModMenuTypes.IRON_CRATE_MENU.get(), CrateScreen::new);
});
//?}
LifecycleEvent.SETUP.register(() -> {
// Register Block Entity Renderers (SETUP is fine for these)
BlockEntityRendererRegistry.register(ModBlockEntities.TANK_BE.get(), TankBlockEntityRenderer::new);
});
}
}
@@ -1,5 +1,6 @@
package net.cmr.jurassicrevived; package net.cmr.jurassicrevived;
import net.minecraft.resources.ResourceLocation;
import org.slf4j.Logger; import org.slf4j.Logger;
import org.slf4j.LoggerFactory; import org.slf4j.LoggerFactory;
@@ -9,4 +10,12 @@ public class Constants
public static final String MOD_ID = "jurassicrevived"; public static final String MOD_ID = "jurassicrevived";
public static final String MOD_NAME = "JurassicRevived"; public static final String MOD_NAME = "JurassicRevived";
public static final Logger LOG = LoggerFactory.getLogger(MOD_NAME); public static final Logger LOG = LoggerFactory.getLogger(MOD_NAME);
public static ResourceLocation rl(String path) {
//? if >1.20.1 {
/*return ResourceLocation.fromNamespaceAndPath(MOD_ID, path);
*///?} else {
return new ResourceLocation(MOD_ID, path);
//?}
}
} }
@@ -3,13 +3,19 @@ package net.cmr.jurassicrevived.block;
import dev.architectury.registry.registries.DeferredRegister; import dev.architectury.registry.registries.DeferredRegister;
import dev.architectury.registry.registries.RegistrySupplier; import dev.architectury.registry.registries.RegistrySupplier;
import net.cmr.jurassicrevived.Constants; import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.block.custom.*;
import net.cmr.jurassicrevived.block.custom.PipeBlock;
import net.cmr.jurassicrevived.entity.ModEntities;
import net.cmr.jurassicrevived.item.ModItems; import net.cmr.jurassicrevived.item.ModItems;
import net.cmr.jurassicrevived.mixin.FlowerPotBlockAccessor;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.item.BlockItem; import net.minecraft.world.item.BlockItem;
import net.minecraft.world.item.Item; import net.minecraft.world.item.Item;
import net.minecraft.world.level.block.Block; import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.state.BlockBehaviour; import net.minecraft.world.level.block.state.BlockBehaviour;
import java.util.Map;
import java.util.function.Supplier; import java.util.function.Supplier;
public class ModBlocks { public class ModBlocks {
@@ -20,6 +26,647 @@ public class ModBlocks {
public static final RegistrySupplier<Block> FOSSIL_ORE = registerBlock("fossil_ore", public static final RegistrySupplier<Block> FOSSIL_ORE = registerBlock("fossil_ore",
() -> new Block(BlockBehaviour.Properties.of().strength(3.0f))); () -> new Block(BlockBehaviour.Properties.of().strength(3.0f)));
public static final RegistrySupplier<Block> CAT_PLUSHIE = registerBlock("cat_plushie",
() -> new DecoBlock(BlockBehaviour.Properties.of().noOcclusion().sound(SoundType.WOOL)));
public static final RegistrySupplier<Block> TRASH_CAN = registerBlock("trash_can",
() -> new TrashBlock(BlockBehaviour.Properties.of().noOcclusion()));
public static final RegistrySupplier<Block> BENCH = registerBlock("bench",
() -> new BenchBlock(BlockBehaviour.Properties.of().noOcclusion()));
public static final RegistrySupplier<Block> CHARRED_TERRACOTTA = registerBlock("charred_terracotta",
() -> new Block(BlockBehaviour.Properties.of().noOcclusion()));
public static final RegistrySupplier<Block> LIGHT_POST = registerBlock("light_post",
() -> new LightPostBlock(BlockBehaviour.Properties.of().noOcclusion().lightLevel(state -> 15)));
public static final RegistrySupplier<Block> ITEM_PIPE = registerBlock("item_pipe",
() -> new PipeBlock(BlockBehaviour.Properties.of().strength(1.0F).noOcclusion(), PipeBlock.Transport.ITEMS));
public static final RegistrySupplier<Block> FLUID_PIPE = registerBlock("fluid_pipe",
() -> new PipeBlock(BlockBehaviour.Properties.of().strength(1.0F).noOcclusion(), PipeBlock.Transport.FLUIDS));
public static final RegistrySupplier<Block> POWER_PIPE = registerBlock("power_pipe",
() -> new PipeBlock(BlockBehaviour.Properties.of().strength(1.0F).noOcclusion(), PipeBlock.Transport.ENERGY));
public static final RegistrySupplier<Block> TANK = registerBlock("tank",
() -> new TankBlock(BlockBehaviour.Properties.of().strength(3f).requiresCorrectToolForDrops().noOcclusion().noLootTable()));
public static final RegistrySupplier<Block> POWER_CELL = registerBlock("power_cell",
() -> new PowerCellBlock(BlockBehaviour.Properties.of().strength(3f).requiresCorrectToolForDrops().noOcclusion().noLootTable()));
public static final RegistrySupplier<Block> WOOD_CRATE = registerBlock("wood_crate",
() -> new CrateBlock(BlockBehaviour.Properties.of().strength(2.0f).noOcclusion().noLootTable().sound(SoundType.WOOD), 9));
public static final RegistrySupplier<Block> IRON_CRATE = registerBlock("iron_crate",
() -> new CrateBlock(BlockBehaviour.Properties.of().strength(2.0f).requiresCorrectToolForDrops().noOcclusion().noLootTable(), 18));
public static final RegistrySupplier<Block> GENERATOR = registerBlock("generator",
() -> new GeneratorBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> DNA_EXTRACTOR = registerBlock("dna_extractor",
() -> new DNAExtractorBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> DNA_ANALYZER = registerBlock("dna_analyzer",
() -> new DNAAnalyzerBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> FOSSIL_GRINDER = registerBlock("fossil_grinder",
() -> new FossilGrinderBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> FOSSIL_CLEANER = registerBlock("fossil_cleaner",
() -> new FossilCleanerBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> DNA_HYBRIDIZER = registerBlock("dna_hybridizer",
() -> new DNAHybridizerBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> EMBRYONIC_MACHINE = registerBlock("embryonic_machine",
() -> new EmbryonicMachineBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> EMBRYO_CALCIFICATION_MACHINE = registerBlock("embryo_calcification_machine",
() -> new EmbryoCalcificationMachineBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> INCUBATOR = registerBlock("incubator",
() -> new IncubatorBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> WHITE_GENERATOR = registerBlock("white_generator",
() -> new GeneratorBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> WHITE_DNA_EXTRACTOR = registerBlock("white_dna_extractor",
() -> new DNAExtractorBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> WHITE_DNA_ANALYZER = registerBlock("white_dna_analyzer",
() -> new DNAAnalyzerBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> WHITE_FOSSIL_GRINDER = registerBlock("white_fossil_grinder",
() -> new FossilGrinderBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> WHITE_FOSSIL_CLEANER = registerBlock("white_fossil_cleaner",
() -> new FossilCleanerBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> WHITE_DNA_HYBRIDIZER = registerBlock("white_dna_hybridizer",
() -> new DNAHybridizerBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> WHITE_EMBRYONIC_MACHINE = registerBlock("white_embryonic_machine",
() -> new EmbryonicMachineBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> WHITE_EMBRYO_CALCIFICATION_MACHINE = registerBlock("white_embryo_calcification_machine",
() -> new EmbryoCalcificationMachineBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
public static final RegistrySupplier<Block> WHITE_INCUBATOR = registerBlock("white_incubator",
() -> new IncubatorBlock(BlockBehaviour.Properties.of().noOcclusion().requiresCorrectToolForDrops().strength(4f).noLootTable()));
//? if >1.20.1 {
/*public static final RegistrySupplier<Block> ROYAL_FERN = registerBlock("royal_fern",
() -> new FlowerBlock(MobEffects.UNLUCK, 0, BlockBehaviour.Properties.ofFullCopy(Blocks.ALLIUM)));
public static final RegistrySupplier<Block> POTTED_ROYAL_FERN = BLOCKS.register("potted_royal_fern",
() -> new FlowerPotBlock(ROYAL_FERN.get(), BlockBehaviour.Properties.ofFullCopy(Blocks.POTTED_ALLIUM)));
public static final RegistrySupplier<Block> HORSETAIL_FERN = registerBlock("horsetail_fern",
() -> new FlowerBlock(MobEffects.UNLUCK, 0, BlockBehaviour.Properties.ofFullCopy(Blocks.ALLIUM)));
public static final RegistrySupplier<Block> POTTED_HORSETAIL_FERN = BLOCKS.register("potted_horsetail_fern",
() -> new FlowerPotBlock(HORSETAIL_FERN.get(), BlockBehaviour.Properties.ofFullCopy(Blocks.POTTED_ALLIUM)));
public static final RegistrySupplier<Block> WESTERN_SWORD_FERN = registerBlock("western_sword_fern",
() -> new FlowerBlock(MobEffects.UNLUCK, 0, BlockBehaviour.Properties.ofFullCopy(Blocks.ALLIUM)));
public static final RegistrySupplier<Block> POTTED_WESTERN_SWORD_FERN = BLOCKS.register("potted_western_sword_fern",
() -> new FlowerPotBlock(WESTERN_SWORD_FERN.get(), BlockBehaviour.Properties.ofFullCopy(Blocks.POTTED_ALLIUM)));
public static final RegistrySupplier<Block> ONYCHIOPSIS = registerBlock("onychiopsis",
() -> new FlowerBlock(MobEffects.UNLUCK, 0, BlockBehaviour.Properties.ofFullCopy(Blocks.ALLIUM)));
public static final RegistrySupplier<Block> POTTED_ONYCHIOPSIS = BLOCKS.register("potted_onychiopsis",
() -> new FlowerPotBlock(ONYCHIOPSIS.get(), BlockBehaviour.Properties.ofFullCopy(Blocks.POTTED_ALLIUM)));
*///?} else {
public static final RegistrySupplier<Block> ROYAL_FERN = registerBlock("royal_fern",
() -> new FlowerBlock(MobEffects.UNLUCK, 0, BlockBehaviour.Properties.copy(Blocks.ALLIUM)));
public static final RegistrySupplier<Block> POTTED_ROYAL_FERN = BLOCKS.register("potted_royal_fern",
() -> new FlowerPotBlock(ROYAL_FERN.get(), BlockBehaviour.Properties.copy(Blocks.POTTED_ALLIUM)));
public static final RegistrySupplier<Block> HORSETAIL_FERN = registerBlock("horsetail_fern",
() -> new FlowerBlock(MobEffects.UNLUCK, 0, BlockBehaviour.Properties.copy(Blocks.ALLIUM)));
public static final RegistrySupplier<Block> POTTED_HORSETAIL_FERN = BLOCKS.register("potted_horsetail_fern",
() -> new FlowerPotBlock(HORSETAIL_FERN.get(), BlockBehaviour.Properties.copy(Blocks.POTTED_ALLIUM)));
public static final RegistrySupplier<Block> WESTERN_SWORD_FERN = registerBlock("western_sword_fern",
() -> new FlowerBlock(MobEffects.UNLUCK, 0, BlockBehaviour.Properties.copy(Blocks.ALLIUM)));
public static final RegistrySupplier<Block> POTTED_WESTERN_SWORD_FERN = BLOCKS.register("potted_western_sword_fern",
() -> new FlowerPotBlock(WESTERN_SWORD_FERN.get(), BlockBehaviour.Properties.copy(Blocks.POTTED_ALLIUM)));
public static final RegistrySupplier<Block> ONYCHIOPSIS = registerBlock("onychiopsis",
() -> new FlowerBlock(MobEffects.UNLUCK, 0, BlockBehaviour.Properties.copy(Blocks.ALLIUM)));
public static final RegistrySupplier<Block> POTTED_ONYCHIOPSIS = BLOCKS.register("potted_onychiopsis",
() -> new FlowerPotBlock(ONYCHIOPSIS.get(), BlockBehaviour.Properties.copy(Blocks.POTTED_ALLIUM)));
//?}
public static void setupPots() {
Map<Block, Block> map = null;
// Attempt Mixin cast safely
try {
Object pot = Blocks.FLOWER_POT;
if (pot instanceof FlowerPotBlockAccessor accessor) {
map = accessor.getPottedByContent();
}
} catch (Throwable ignored) {}
// Fallback to Reflection if Mixin didn't apply (common on early Fabric loading)
if (map == null) {
try {
java.lang.reflect.Field field = FlowerPotBlock.class.getDeclaredField("POTTED_BY_CONTENT");
field.setAccessible(true);
map = (Map<Block, Block>) field.get(null);
} catch (Exception e) {
Constants.LOG.error("Could not access FlowerPotBlock.POTTED_BY_CONTENT", e);
}
}
if (map != null) {
if (ROYAL_FERN.isPresent()) map.put(ROYAL_FERN.get(), POTTED_ROYAL_FERN.get());
if (HORSETAIL_FERN.isPresent()) map.put(HORSETAIL_FERN.get(), POTTED_HORSETAIL_FERN.get());
if (WESTERN_SWORD_FERN.isPresent()) map.put(WESTERN_SWORD_FERN.get(), POTTED_WESTERN_SWORD_FERN.get());
if (ONYCHIOPSIS.isPresent()) map.put(ONYCHIOPSIS.get(), POTTED_ONYCHIOPSIS.get());
}
}
public static final RegistrySupplier<Block> GYPSUM_STONE = registerBlock("gypsum_stone",
() -> new Block(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> GYPSUM_COBBLESTONE = registerBlock("gypsum_cobblestone",
() -> new Block(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> GYPSUM_STONE_BRICKS = registerBlock("gypsum_stone_bricks",
() -> new Block(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> SMOOTH_GYPSUM_STONE = registerBlock("smooth_gypsum_stone",
() -> new Block(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> CHISELED_GYPSUM_STONE = registerBlock("chiseled_gypsum_stone",
() -> new Block(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> GYPSUM_BRICK_STAIRS = registerBlock("gypsum_brick_stairs",
() -> new StairBlock(ModBlocks.GYPSUM_STONE_BRICKS.get().defaultBlockState(),
BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> GYPSUM_BRICK_SLAB = registerBlock("gypsum_brick_slab",
() -> new SlabBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> GYPSUM_BRICK_WALL = registerBlock("gypsum_brick_wall",
() -> new WallBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> FENCE_LIGHT = registerBlock("fence_light",
() -> new FenceLightBlock(BlockBehaviour.Properties.of().strength(1.0F).noOcclusion().lightLevel(state -> 15)));
public static final RegistrySupplier<Block> LOW_SECURITY_FENCE_POLE = registerBlock("low_security_fence_pole",
() -> new FencePoleBlock(BlockBehaviour.Properties.of().strength(1.0F).noOcclusion().requiresCorrectToolForDrops(), FencePoleBlock.Tier.LOW));
public static final RegistrySupplier<Block> LOW_SECURITY_FENCE_WIRE = registerBlock("low_security_fence_wire",
() -> new FenceWireBlock(BlockBehaviour.Properties.of().strength(0.5F).noOcclusion().requiresCorrectToolForDrops(), FenceWireBlock.Tier.LOW));
public static final RegistrySupplier<Block> MEDIUM_SECURITY_FENCE_POLE = registerBlock("medium_security_fence_pole",
() -> new FencePoleBlock(BlockBehaviour.Properties.of().strength(1.0F).noOcclusion().requiresCorrectToolForDrops(), FencePoleBlock.Tier.MEDIUM));
public static final RegistrySupplier<Block> MEDIUM_SECURITY_FENCE_WIRE = registerBlock("medium_security_fence_wire",
() -> new FenceWireBlock(BlockBehaviour.Properties.of().strength(0.5F).noOcclusion().requiresCorrectToolForDrops(), FenceWireBlock.Tier.MEDIUM));
public static final RegistrySupplier<Block> STONE_FOSSIL = registerBlock("stone_fossil",
() -> new Block(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> DEEPSLATE_FOSSIL = registerBlock("deepslate_fossil",
() -> new Block(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> AMBER_ORE = registerBlock("amber_ore",
() -> new Block(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> DEEPSLATE_ICE_SHARD_ORE = registerBlock("deepslate_ice_shard_ore",
() -> new Block(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> REINFORCED_STONE = registerBlock("reinforced_stone",
() -> new Block(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> REINFORCED_STONE_BRICKS = registerBlock("reinforced_stone_bricks",
() -> new Block(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> CHISELED_REINFORCED_STONE = registerBlock("chiseled_reinforced_stone",
() -> new Block(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> REINFORCED_BRICK_STAIRS = registerBlock("reinforced_brick_stairs",
() -> new StairBlock(ModBlocks.REINFORCED_STONE_BRICKS.get().defaultBlockState(),
BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> REINFORCED_BRICK_SLAB = registerBlock("reinforced_brick_slab",
() -> new SlabBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> REINFORCED_BRICK_WALL = registerBlock("reinforced_brick_wall",
() -> new WallBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops()));
public static final RegistrySupplier<Block> ALBERTOSAURUS_EGG = registerBlock("albertosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ALBERTOSAURUS));
public static final RegistrySupplier<Block> APATOSAURUS_EGG = registerBlock("apatosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.APATOSAURUS));
public static final RegistrySupplier<Block> BRACHIOSAURUS_EGG = registerBlock("brachiosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.BRACHIOSAURUS));
public static final RegistrySupplier<Block> CERATOSAURUS_EGG = registerBlock("ceratosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.CERATOSAURUS));
public static final RegistrySupplier<Block> COMPSOGNATHUS_EGG = registerBlock("compsognathus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.COMPSOGNATHUS));
public static final RegistrySupplier<Block> DILOPHOSAURUS_EGG = registerBlock("dilophosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.DILOPHOSAURUS));
public static final RegistrySupplier<Block> DIPLODOCUS_EGG = registerBlock("diplodocus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.DIPLODOCUS));
public static final RegistrySupplier<Block> GALLIMIMUS_EGG = registerBlock("gallimimus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.GALLIMIMUS));
public static final RegistrySupplier<Block> INDOMINUS_REX_EGG = registerBlock("indominus_rex_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.INDOMINUS_REX));
public static final RegistrySupplier<Block> OURANOSAURUS_EGG = registerBlock("ouranosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.OURANOSAURUS));
public static final RegistrySupplier<Block> PARASAUROLOPHUS_EGG = registerBlock("parasaurolophus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.PARASAUROLOPHUS));
public static final RegistrySupplier<Block> SPINOSAURUS_EGG = registerBlock("spinosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.SPINOSAURUS));
public static final RegistrySupplier<Block> TRICERATOPS_EGG = registerBlock("triceratops_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.TRICERATOPS));
public static final RegistrySupplier<Block> TYRANNOSAURUS_REX_EGG = registerBlock("tyrannosaurus_rex_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.TYRANNOSAURUS_REX));
public static final RegistrySupplier<Block> VELOCIRAPTOR_EGG = registerBlock("velociraptor_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.VELOCIRAPTOR));
public static final RegistrySupplier<Block> BARYONYX_EGG = registerBlock("baryonyx_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.BARYONYX));
public static final RegistrySupplier<Block> CARNOTAURUS_EGG = registerBlock("carnotaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.CARNOTAURUS));
public static final RegistrySupplier<Block> CONCAVENATOR_EGG = registerBlock("concavenator_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.CONCAVENATOR));
public static final RegistrySupplier<Block> DEINONYCHUS_EGG = registerBlock("deinonychus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.DEINONYCHUS));
public static final RegistrySupplier<Block> EDMONTOSAURUS_EGG = registerBlock("edmontosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.EDMONTOSAURUS));
public static final RegistrySupplier<Block> GIGANOTOSAURUS_EGG = registerBlock("giganotosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.GIGANOTOSAURUS));
public static final RegistrySupplier<Block> GUANLONG_EGG = registerBlock("guanlong_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.GUANLONG));
public static final RegistrySupplier<Block> HERRERASAURUS_EGG = registerBlock("herrerasaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.HERRERASAURUS));
public static final RegistrySupplier<Block> MAJUNGASAURUS_EGG = registerBlock("majungasaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.MAJUNGASAURUS));
public static final RegistrySupplier<Block> PROCOMPSOGNATHUS_EGG = registerBlock("procompsognathus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.PROCOMPSOGNATHUS));
public static final RegistrySupplier<Block> PROTOCERATOPS_EGG = registerBlock("protoceratops_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.PROTOCERATOPS));
public static final RegistrySupplier<Block> RUGOPS_EGG = registerBlock("rugops_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.RUGOPS));
public static final RegistrySupplier<Block> SHANTUNGOSAURUS_EGG = registerBlock("shantungosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.SHANTUNGOSAURUS));
public static final RegistrySupplier<Block> STEGOSAURUS_EGG = registerBlock("stegosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.STEGOSAURUS));
public static final RegistrySupplier<Block> STYRACOSAURUS_EGG = registerBlock("styracosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.STYRACOSAURUS));
public static final RegistrySupplier<Block> THERIZINOSAURUS_EGG = registerBlock("therizinosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.THERIZINOSAURUS));
public static final RegistrySupplier<Block> DISTORTUS_REX_EGG = registerBlock("distortus_rex_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.DISTORTUS_REX));
public static final RegistrySupplier<Block> ALLOSAURUS_EGG = registerBlock("allosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ALLOSAURUS));
public static final RegistrySupplier<Block> ALVAREZSAURUS_EGG = registerBlock("alvarezsaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ALVAREZSAURUS));
public static final RegistrySupplier<Block> ANKYLOSAURUS_EGG = registerBlock("ankylosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ANKYLOSAURUS));
public static final RegistrySupplier<Block> ARAMBOURGIANIA_EGG = registerBlock("arambourgiania_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ARAMBOURGIANIA));
public static final RegistrySupplier<Block> CARCHARODONTOSAURUS_EGG = registerBlock("carcharodontosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.CARCHARODONTOSAURUS));
public static final RegistrySupplier<Block> CEARADACTYLUS_EGG = registerBlock("cearadactylus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.CEARADACTYLUS));
public static final RegistrySupplier<Block> CHASMOSAURUS_EGG = registerBlock("chasmosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.CHASMOSAURUS));
public static final RegistrySupplier<Block> COELOPHYSIS_EGG = registerBlock("coelophysis_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.COELOPHYSIS));
public static final RegistrySupplier<Block> COELURUS_EGG = registerBlock("coelurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.COELURUS));
public static final RegistrySupplier<Block> CORYTHOSAURUS_EGG = registerBlock("corythosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.CORYTHOSAURUS));
public static final RegistrySupplier<Block> DIMORPHODON_EGG = registerBlock("dimorphodon_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.DIMORPHODON));
public static final RegistrySupplier<Block> DRYOSAURUS_EGG = registerBlock("dryosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.DRYOSAURUS));
public static final RegistrySupplier<Block> GEOSTERNBERGIA_EGG = registerBlock("geosternbergia_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.GEOSTERNBERGIA));
public static final RegistrySupplier<Block> GUIDRACO_EGG = registerBlock("guidraco_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.GUIDRACO));
public static final RegistrySupplier<Block> HADROSAURUS_EGG = registerBlock("hadrosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.HADROSAURUS));
public static final RegistrySupplier<Block> HYPSILOPHODON_EGG = registerBlock("hypsilophodon_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.HYPSILOPHODON));
public static final RegistrySupplier<Block> INDORAPTOR_EGG = registerBlock("indoraptor_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.INDORAPTOR));
public static final RegistrySupplier<Block> INOSTRANCEVIA_EGG = registerBlock("inostrancevia_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.INOSTRANCEVIA));
public static final RegistrySupplier<Block> LAMBEOSAURUS_EGG = registerBlock("lambeosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.LAMBEOSAURUS));
public static final RegistrySupplier<Block> LUDODACTYLUS_EGG = registerBlock("ludodactylus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.LUDODACTYLUS));
public static final RegistrySupplier<Block> MAMENCHISAURUS_EGG = registerBlock("mamenchisaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.MAMENCHISAURUS));
public static final RegistrySupplier<Block> METRIACANTHOSAURUS_EGG = registerBlock("metriacanthosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.METRIACANTHOSAURUS));
public static final RegistrySupplier<Block> MOGANOPTERUS_EGG = registerBlock("moganopterus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.MOGANOPTERUS));
public static final RegistrySupplier<Block> NYCTOSAURUS_EGG = registerBlock("nyctosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.NYCTOSAURUS));
public static final RegistrySupplier<Block> ORNITHOLESTES_EGG = registerBlock("ornitholestes_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ORNITHOLESTES));
public static final RegistrySupplier<Block> ORNITHOMIMUS_EGG = registerBlock("ornithomimus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ORNITHOMIMUS));
public static final RegistrySupplier<Block> OVIRAPTOR_EGG = registerBlock("oviraptor_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.OVIRAPTOR));
public static final RegistrySupplier<Block> PACHYCEPHALOSAURUS_EGG = registerBlock("pachycephalosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.PACHYCEPHALOSAURUS));
public static final RegistrySupplier<Block> PROCERATOSAURUS_EGG = registerBlock("proceratosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.PROCERATOSAURUS));
public static final RegistrySupplier<Block> PTERANODON_EGG = registerBlock("pteranodon_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.PTERANODON));
public static final RegistrySupplier<Block> PTERODAUSTRO_EGG = registerBlock("pterodaustro_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.PTERODAUSTRO));
public static final RegistrySupplier<Block> QUETZALCOATLUS_EGG = registerBlock("quetzalcoatlus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.QUETZALCOATLUS));
public static final RegistrySupplier<Block> RAJASAURUS_EGG = registerBlock("rajasaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.RAJASAURUS));
public static final RegistrySupplier<Block> SEGISAURUS_EGG = registerBlock("segisaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.SEGISAURUS));
public static final RegistrySupplier<Block> TAPEJARA_EGG = registerBlock("tapejara_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.TAPEJARA));
public static final RegistrySupplier<Block> TITANOSAURUS_EGG = registerBlock("titanosaurus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.TITANOSAURUS));
public static final RegistrySupplier<Block> TROODON_EGG = registerBlock("troodon_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.TROODON));
public static final RegistrySupplier<Block> TROPEOGNATHUS_EGG = registerBlock("tropeognathus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.TROPEOGNATHUS));
public static final RegistrySupplier<Block> TUPUXUARA_EGG = registerBlock("tupuxuara_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.TUPUXUARA));
public static final RegistrySupplier<Block> UTAHRAPTOR_EGG = registerBlock("utahraptor_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.UTAHRAPTOR));
public static final RegistrySupplier<Block> ZHENYUANOPTERUS_EGG = registerBlock("zhenyuanopterus_egg",
() -> new EggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ZHENYUANOPTERUS));
public static final RegistrySupplier<Block> INCUBATED_APATOSAURUS_EGG = registerBlock("incubated_apatosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.APATOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_ALBERTOSAURUS_EGG = registerBlock("incubated_albertosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ALBERTOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_VELOCIRAPTOR_EGG = registerBlock("incubated_velociraptor_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.VELOCIRAPTOR));
public static final RegistrySupplier<Block> INCUBATED_TYRANNOSAURUS_REX_EGG = registerBlock("incubated_tyrannosaurus_rex_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.TYRANNOSAURUS_REX));
public static final RegistrySupplier<Block> INCUBATED_TRICERATOPS_EGG = registerBlock("incubated_triceratops_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.TRICERATOPS));
public static final RegistrySupplier<Block> INCUBATED_SPINOSAURUS_EGG = registerBlock("incubated_spinosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.SPINOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_PARASAUROLOPHUS_EGG = registerBlock("incubated_parasaurolophus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.PARASAUROLOPHUS));
public static final RegistrySupplier<Block> INCUBATED_INDOMINUS_REX_EGG = registerBlock("incubated_indominus_rex_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.INDOMINUS_REX));
public static final RegistrySupplier<Block> INCUBATED_GALLIMIMUS_EGG = registerBlock("incubated_gallimimus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.GALLIMIMUS));
public static final RegistrySupplier<Block> INCUBATED_DIPLODOCUS_EGG = registerBlock("incubated_diplodocus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.DIPLODOCUS));
public static final RegistrySupplier<Block> INCUBATED_OURANOSAURUS_EGG = registerBlock("incubated_ouranosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.OURANOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_DILOPHOSAURUS_EGG = registerBlock("incubated_dilophosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.DILOPHOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_COMPSOGNATHUS_EGG = registerBlock("incubated_compsognathus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.COMPSOGNATHUS));
public static final RegistrySupplier<Block> INCUBATED_CERATOSAURUS_EGG = registerBlock("incubated_ceratosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.CERATOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_BRACHIOSAURUS_EGG = registerBlock("incubated_brachiosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.BRACHIOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_BARYONYX_EGG = registerBlock("incubated_baryonyx_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.BARYONYX));
public static final RegistrySupplier<Block> INCUBATED_CARNOTAURUS_EGG = registerBlock("incubated_carnotaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.CARNOTAURUS));
public static final RegistrySupplier<Block> INCUBATED_CONCAVENATOR_EGG = registerBlock("incubated_concavenator_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.CONCAVENATOR));
public static final RegistrySupplier<Block> INCUBATED_DEINONYCHUS_EGG = registerBlock("incubated_deinonychus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.DEINONYCHUS));
public static final RegistrySupplier<Block> INCUBATED_EDMONTOSAURUS_EGG = registerBlock("incubated_edmontosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.EDMONTOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_GIGANOTOSAURUS_EGG = registerBlock("incubated_giganotosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.GIGANOTOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_GUANLONG_EGG = registerBlock("incubated_guanlong_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.GUANLONG));
public static final RegistrySupplier<Block> INCUBATED_HERRERASAURUS_EGG = registerBlock("incubated_herrerasaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.HERRERASAURUS));
public static final RegistrySupplier<Block> INCUBATED_MAJUNGASAURUS_EGG = registerBlock("incubated_majungasaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.MAJUNGASAURUS));
public static final RegistrySupplier<Block> INCUBATED_PROCOMPSOGNATHUS_EGG = registerBlock("incubated_procompsognathus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.PROCOMPSOGNATHUS));
public static final RegistrySupplier<Block> INCUBATED_PROTOCERATOPS_EGG = registerBlock("incubated_protoceratops_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.PROTOCERATOPS));
public static final RegistrySupplier<Block> INCUBATED_RUGOPS_EGG = registerBlock("incubated_rugops_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.RUGOPS));
public static final RegistrySupplier<Block> INCUBATED_SHANTUNGOSAURUS_EGG = registerBlock("incubated_shantungosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.SHANTUNGOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_STEGOSAURUS_EGG = registerBlock("incubated_stegosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.STEGOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_STYRACOSAURUS_EGG = registerBlock("incubated_styracosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.STYRACOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_THERIZINOSAURUS_EGG = registerBlock("incubated_therizinosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.THERIZINOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_DISTORTUS_REX_EGG = registerBlock("incubated_distortus_rex_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.DISTORTUS_REX));
public static final RegistrySupplier<Block> INCUBATED_ALLOSAURUS_EGG = registerBlock("incubated_allosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ALLOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_ALVAREZSAURUS_EGG = registerBlock("incubated_alvarezsaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ALVAREZSAURUS));
public static final RegistrySupplier<Block> INCUBATED_ANKYLOSAURUS_EGG = registerBlock("incubated_ankylosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ANKYLOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_ARAMBOURGIANIA_EGG = registerBlock("incubated_arambourgiania_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ARAMBOURGIANIA));
public static final RegistrySupplier<Block> INCUBATED_CARCHARODONTOSAURUS_EGG = registerBlock("incubated_carcharodontosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.CARCHARODONTOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_CEARADACTYLUS_EGG = registerBlock("incubated_cearadactylus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.CEARADACTYLUS));
public static final RegistrySupplier<Block> INCUBATED_CHASMOSAURUS_EGG = registerBlock("incubated_chasmosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.CHASMOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_COELOPHYSIS_EGG = registerBlock("incubated_coelophysis_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.COELOPHYSIS));
public static final RegistrySupplier<Block> INCUBATED_COELURUS_EGG = registerBlock("incubated_coelurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.COELURUS));
public static final RegistrySupplier<Block> INCUBATED_CORYTHOSAURUS_EGG = registerBlock("incubated_corythosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.CORYTHOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_DIMORPHODON_EGG = registerBlock("incubated_dimorphodon_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.DIMORPHODON));
public static final RegistrySupplier<Block> INCUBATED_DRYOSAURUS_EGG = registerBlock("incubated_dryosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.DRYOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_GEOSTERNBERGIA_EGG = registerBlock("incubated_geosternbergia_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.GEOSTERNBERGIA));
public static final RegistrySupplier<Block> INCUBATED_GUIDRACO_EGG = registerBlock("incubated_guidraco_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.GUIDRACO));
public static final RegistrySupplier<Block> INCUBATED_HADROSAURUS_EGG = registerBlock("incubated_hadrosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.HADROSAURUS));
public static final RegistrySupplier<Block> INCUBATED_HYPSILOPHODON_EGG = registerBlock("incubated_hypsilophodon_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.HYPSILOPHODON));
public static final RegistrySupplier<Block> INCUBATED_INDORAPTOR_EGG = registerBlock("incubated_indoraptor_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.INDORAPTOR));
public static final RegistrySupplier<Block> INCUBATED_INOSTRANCEVIA_EGG = registerBlock("incubated_inostrancevia_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.INOSTRANCEVIA));
public static final RegistrySupplier<Block> INCUBATED_LAMBEOSAURUS_EGG = registerBlock("incubated_lambeosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.LAMBEOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_LUDODACTYLUS_EGG = registerBlock("incubated_ludodactylus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.LUDODACTYLUS));
public static final RegistrySupplier<Block> INCUBATED_MAMENCHISAURUS_EGG = registerBlock("incubated_mamenchisaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.MAMENCHISAURUS));
public static final RegistrySupplier<Block> INCUBATED_METRIACANTHOSAURUS_EGG = registerBlock("incubated_metriacanthosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.METRIACANTHOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_MOGANOPTERUS_EGG = registerBlock("incubated_moganopterus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.MOGANOPTERUS));
public static final RegistrySupplier<Block> INCUBATED_NYCTOSAURUS_EGG = registerBlock("incubated_nyctosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.NYCTOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_ORNITHOLESTES_EGG = registerBlock("incubated_ornitholestes_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ORNITHOLESTES));
public static final RegistrySupplier<Block> INCUBATED_ORNITHOMIMUS_EGG = registerBlock("incubated_ornithomimus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ORNITHOMIMUS));
public static final RegistrySupplier<Block> INCUBATED_OVIRAPTOR_EGG = registerBlock("incubated_oviraptor_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.OVIRAPTOR));
public static final RegistrySupplier<Block> INCUBATED_PACHYCEPHALOSAURUS_EGG = registerBlock("incubated_pachycephalosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.PACHYCEPHALOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_PROCERATOSAURUS_EGG = registerBlock("incubated_proceratosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.PROCERATOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_PTERANODON_EGG = registerBlock("incubated_pteranodon_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.PTERANODON));
public static final RegistrySupplier<Block> INCUBATED_PTERODAUSTRO_EGG = registerBlock("incubated_pterodaustro_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.PTERODAUSTRO));
public static final RegistrySupplier<Block> INCUBATED_QUETZALCOATLUS_EGG = registerBlock("incubated_quetzalcoatlus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.QUETZALCOATLUS));
public static final RegistrySupplier<Block> INCUBATED_RAJASAURUS_EGG = registerBlock("incubated_rajasaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.RAJASAURUS));
public static final RegistrySupplier<Block> INCUBATED_SEGISAURUS_EGG = registerBlock("incubated_segisaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.SEGISAURUS));
public static final RegistrySupplier<Block> INCUBATED_TAPEJARA_EGG = registerBlock("incubated_tapejara_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.TAPEJARA));
public static final RegistrySupplier<Block> INCUBATED_TITANOSAURUS_EGG = registerBlock("incubated_titanosaurus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.TITANOSAURUS));
public static final RegistrySupplier<Block> INCUBATED_TROODON_EGG = registerBlock("incubated_troodon_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.TROODON));
public static final RegistrySupplier<Block> INCUBATED_TROPEOGNATHUS_EGG = registerBlock("incubated_tropeognathus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.TROPEOGNATHUS));
public static final RegistrySupplier<Block> INCUBATED_TUPUXUARA_EGG = registerBlock("incubated_tupuxuara_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.TUPUXUARA));
public static final RegistrySupplier<Block> INCUBATED_UTAHRAPTOR_EGG = registerBlock("incubated_utahraptor_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.UTAHRAPTOR));
public static final RegistrySupplier<Block> INCUBATED_ZHENYUANOPTERUS_EGG = registerBlock("incubated_zhenyuanopterus_egg",
() -> new IncubatedEggBlock(BlockBehaviour.Properties.of().strength(4f).requiresCorrectToolForDrops(), ModEntities.ZHENYUANOPTERUS));
// --- Helper Methods --- // --- Helper Methods ---
@@ -0,0 +1,223 @@
package net.cmr.jurassicrevived.block.custom;
import com.mojang.serialization.MapCodec;
import net.cmr.jurassicrevived.entity.ModEntities;
import net.cmr.jurassicrevived.entity.custom.SeatEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
// ... existing code ...
public class BenchBlock extends HorizontalDirectionalBlock {
public static final VoxelShape SHAPE = Block.box(0.0D, 0.0D, 0.0D, 16.0D, 19.0D, 19.0D);
public BenchBlock(Properties properties) {
super(properties);
this.registerDefaultState((BlockState)((BlockState)((BlockState)this.stateDefinition.any()).setValue(FACING, Direction.SOUTH)));
}
protected MapCodec<? extends HorizontalDirectionalBlock> codec() {
return null;
}
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(new Property[]{FACING});
}
public BlockState mirror(BlockState state, Mirror mirror) {
return state.rotate(mirror.getRotation((Direction)state.getValue(FACING)));
}
// Ensure rotation of FACING property when the block is rotated (e.g., structure placement, commands, etc.)
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
}
public BlockState getStateForPlacement(BlockPlaceContext context) {
// Ensure we always use a horizontal facing (N/E/S/W), even when looking up or down.
return (BlockState)this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite());
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
Direction facing = state.getValue(FACING);
// The SHAPE is defined for NORTH. Rotate it for other facings.
return switch (facing) {
case NORTH -> SHAPE;
case EAST -> rotateShape90Y(SHAPE);
case SOUTH -> rotateShape90Y(rotateShape90Y(SHAPE));
case WEST -> rotateShape90Y(rotateShape90Y(rotateShape90Y(SHAPE)));
default -> SHAPE;
};
}
// Utility: rotate a VoxelShape 90 degrees around Y (from NORTH to EAST)
private static VoxelShape rotateShape90Y(VoxelShape shape) {
VoxelShape[] buffer = new VoxelShape[]{shape, Shapes.empty()};
// Iterate all boxes in the shape and rotate their coordinates
shape.forAllBoxes((minX, minY, minZ, maxX, maxY, maxZ) -> {
// Convert to block-space rotation around Y: (x,z) -> (1 - z, x)
double rMinX = 1.0D - maxZ;
double rMinZ = minX;
double rMaxX = 1.0D - minZ;
double rMaxZ = maxX;
VoxelShape rotated = Block.box(rMinX * 16.0D, minY * 16.0D, rMinZ * 16.0D, rMaxX * 16.0D, maxY * 16.0D, rMaxZ * 16.0D);
buffer[1] = Shapes.or(buffer[1], rotated);
});
return buffer[1].optimize();
}
// --- Sitting logic ---
//? if >1.20.1 {
/*@Override
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos,
Player player, BlockHitResult hit) {
if (player.isShiftKeyDown()) return InteractionResult.PASS;
if (level.isClientSide) return InteractionResult.SUCCESS;
// Reuse existing seat if present
AABB check = new AABB(pos).inflate(0.2);
for (SeatEntity seat : level.getEntitiesOfClass(SeatEntity.class, check)) {
if (seat.getPassengers().isEmpty()) {
// Snap the player to target pos before scheduling mount
Direction facing = state.getValue(FACING);
Vec3 base = Vec3.atBottomCenterOf(pos).add(0, 0.45, 0);
double forward = -0.2;
Vec3 offset = switch (facing) {
case NORTH -> new Vec3(0, 0, forward);
case SOUTH -> new Vec3(0, 0, -forward);
case WEST -> new Vec3(forward, 0, 0);
case EAST -> new Vec3(-forward, 0, 0);
default -> Vec3.ZERO;
};
Vec3 seatPos = base.add(offset);
// Use moveTo (sets x/y/z + yaw/pitch) so both server and tracking clients get the same transform
seat.moveTo(seatPos.x, seatPos.y, seatPos.z, facing.toYRot(), 0.0F);
level.sendBlockUpdated(pos, state, state, Block.UPDATE_NEIGHBORS | Block.UPDATE_CLIENTS);
seat.scheduleMount(player.getUUID());
return InteractionResult.CONSUME;
}
return InteractionResult.CONSUME;
}
Direction facing = state.getValue(FACING);
Vec3 base = Vec3.atBottomCenterOf(pos).add(0, 0.45, 0);
double forward = -0.2;
Vec3 offset = switch (facing) {
case NORTH -> new Vec3(0, 0, forward);
case SOUTH -> new Vec3(0, 0, -forward);
case WEST -> new Vec3(forward, 0, 0);
case EAST -> new Vec3(-forward, 0, 0);
default -> Vec3.ZERO;
};
Vec3 seatPos = base.add(offset);
SeatEntity seat = ModEntities.SEAT.get().create(level);
if (seat == null) return InteractionResult.PASS;
// Prefer moveTo over setPos for initial spawn pose
seat.moveTo(seatPos.x, seatPos.y, seatPos.z, facing.toYRot(), 0.0F);
level.addFreshEntity(seat);
// Immediately reassert pose with moveTo to ensure tracking sees the same coords this tick
seat.moveTo(seatPos.x, seatPos.y, seatPos.z, facing.toYRot(), 0.0F);
// Nudge a block update to flush to clients in edge cases
level.sendBlockUpdated(pos, state, state, Block.UPDATE_NEIGHBORS | Block.UPDATE_CLIENTS);
// Mount next tick from the entity
seat.scheduleMount(player.getUUID());
return InteractionResult.CONSUME;
}
*///?} else {
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos,
Player player, InteractionHand hand, BlockHitResult hit) {
if (player.isShiftKeyDown()) return InteractionResult.PASS;
if (level.isClientSide) return InteractionResult.SUCCESS;
// Reuse existing seat if present
AABB check = new AABB(pos).inflate(0.2);
for (SeatEntity seat : level.getEntitiesOfClass(SeatEntity.class, check)) {
if (seat.getPassengers().isEmpty()) {
Direction facing = state.getValue(FACING);
Vec3 base = Vec3.atBottomCenterOf(pos).add(0, 0.45, 0);
double forward = -0.2;
Vec3 offset = switch (facing) {
case NORTH -> new Vec3(0, 0, forward);
case SOUTH -> new Vec3(0, 0, -forward);
case WEST -> new Vec3(forward, 0, 0);
case EAST -> new Vec3(-forward, 0, 0);
default -> Vec3.ZERO;
};
Vec3 seatPos = base.add(offset);
seat.moveTo(seatPos.x, seatPos.y, seatPos.z, facing.toYRot(), 0.0F);
level.sendBlockUpdated(pos, state, state, Block.UPDATE_NEIGHBORS | Block.UPDATE_CLIENTS);
seat.scheduleMount(player.getUUID());
return InteractionResult.CONSUME;
}
return InteractionResult.CONSUME;
}
Direction facing = state.getValue(FACING);
Vec3 base = Vec3.atBottomCenterOf(pos).add(0, 0.45, 0);
double forward = -0.2;
Vec3 offset = switch (facing) {
case NORTH -> new Vec3(0, 0, forward);
case SOUTH -> new Vec3(0, 0, -forward);
case WEST -> new Vec3(forward, 0, 0);
case EAST -> new Vec3(-forward, 0, 0);
default -> Vec3.ZERO;
};
Vec3 seatPos = base.add(offset);
SeatEntity seat = ModEntities.SEAT.get().create(level);
if (seat == null) return InteractionResult.PASS;
seat.moveTo(seatPos.x, seatPos.y, seatPos.z, facing.toYRot(), 0.0F);
level.addFreshEntity(seat);
seat.moveTo(seatPos.x, seatPos.y, seatPos.z, facing.toYRot(), 0.0F);
level.sendBlockUpdated(pos, state, state, Block.UPDATE_NEIGHBORS | Block.UPDATE_CLIENTS);
seat.scheduleMount(player.getUUID());
return InteractionResult.CONSUME;
}
//?}
@Override
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
if (!state.is(newState.getBlock())) {
// Clean up seat if bench is broken/replaced
for (SeatEntity seat : level.getEntitiesOfClass(SeatEntity.class, new AABB(pos).inflate(0.5))) {
seat.discard();
}
}
super.onRemove(state, level, pos, newState, isMoving);
}
}
@@ -0,0 +1,166 @@
package net.cmr.jurassicrevived.block.custom;
//? if >1.20.1 {
/*import com.mojang.serialization.MapCodec;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.ItemInteractionResult;
*///?}
import dev.architectury.registry.menu.ExtendedMenuProvider;
import dev.architectury.registry.menu.MenuRegistry;
import net.cmr.jurassicrevived.block.entity.custom.CrateBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BaseEntityBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import org.jetbrains.annotations.Nullable;
public class CrateBlock extends BaseEntityBlock {
private final int slots;
//? if >1.20.1 {
/*@Override protected MapCodec<? extends BaseEntityBlock> codec() { return null; }
*///?}
public CrateBlock(Properties properties, int slots) {
super(properties);
this.slots = slots;
}
@Override
public RenderShape getRenderShape(BlockState state) {
return RenderShape.MODEL;
}
@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
return new CrateBlockEntity(pos, state, this.slots);
}
@Nullable
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> type) {
return null;
}
@Override
//? if >1.20.1 {
/*public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
*///?} else {
public void playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
//?}
if (!level.isClientSide) {
if (player.getAbilities().instabuild) {
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof CrateBlockEntity fbe) {
ItemStack stack = new ItemStack(this.asItem());
if (!fbe.isEmptyForDrop()) {
//? if >1.20.1 {
/*CompoundTag tag = fbe.saveWithoutMetadata(level.registryAccess());
var beTypeKey = level.registryAccess()
.registryOrThrow(Registries.BLOCK_ENTITY_TYPE)
.getKey(fbe.getType());
if (beTypeKey != null) {
tag.putString("id", beTypeKey.toString());
}
stack.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(tag));
*///?} else {
CompoundTag tag = fbe.saveWithoutMetadata();
stack.getOrCreateTagElement("BlockEntityTag").merge(tag);
//?}
}
popResource(level, pos, stack);
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
}
super.playerWillDestroy(level, pos, state, player);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
@Override
public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) {
super.onRemove(pState, pLevel, pPos, pNewState, pIsMoving);
}
@Override
public boolean hasAnalogOutputSignal(BlockState state) {
return true;
}
@Override
public int getAnalogOutputSignal(BlockState state, Level level, BlockPos pos) {
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof CrateBlockEntity crate) {
return crate.redstoneSignal();
}
return 0;
}
@Override
public boolean triggerEvent(BlockState state, Level level, BlockPos pos, int id, int param) {
super.triggerEvent(state, level, pos, id, param);
BlockEntity be = level.getBlockEntity(pos);
return be != null && be.triggerEvent(id, param);
}
//? if >1.20.1 {
/*@Override
protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (!level.isClientSide()) {
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof CrateBlockEntity crate) {
MenuRegistry.openExtendedMenu((ServerPlayer) player, crate);
}
}
return ItemInteractionResult.sidedSuccess(level.isClientSide());
}
*///?} else {
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (!level.isClientSide()) {
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof CrateBlockEntity crate) {
MenuRegistry.openExtendedMenu((ServerPlayer) player, crate);
}
}
return InteractionResult.sidedSuccess(level.isClientSide());
}
//?}
}
@@ -0,0 +1,240 @@
package net.cmr.jurassicrevived.block.custom;
//? if >1.20.1 {
/*import com.mojang.serialization.MapCodec;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.ItemInteractionResult;
*///?}
import dev.architectury.registry.menu.MenuRegistry;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.custom.DNAAnalyzerBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ItemParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
public class DNAAnalyzerBlock extends BaseEntityBlock {
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public static final BooleanProperty LIT = BlockStateProperties.LIT;
//? if >1.20.1 {
/*public static final MapCodec<DNAAnalyzerBlock> CODEC = simpleCodec(DNAAnalyzerBlock::new);
@Override protected MapCodec<? extends BaseEntityBlock> codec() { return CODEC; }
*///?}
public DNAAnalyzerBlock(Properties properties) {
super(properties);
}
private static final VoxelShape SHAPE_NORTH = Shapes.box(
1.0 / 16.0, 0.0 / 16.0, 3.0 / 16.0,
15.0 / 16.0, 16.0 / 16.0, 12.5 / 16.0
);
private static final VoxelShape SHAPE_SOUTH = rotateShapeY(SHAPE_NORTH, 180);
private static final VoxelShape SHAPE_WEST = rotateShapeY(SHAPE_NORTH, 90);
private static final VoxelShape SHAPE_EAST = rotateShapeY(SHAPE_NORTH, 90);
private static VoxelShape rotateShapeY(VoxelShape shape, int degrees) {
double rad = Math.toRadians(((degrees % 360) + 360) % 360);
int turns = (int) Math.round(rad / (Math.PI / 2));
turns = ((turns % 4) + 4) % 4;
VoxelShape current = shape;
for (int i = 0; i < turns; i++) {
VoxelShape[] buffer = new VoxelShape[]{Shapes.empty()};
current.forAllBoxes((minX, minY, minZ, maxX, maxY, maxZ) -> {
double nMinX = 1.0 - maxZ;
double nMinZ = minX;
double nMaxX = 1.0 - minZ;
double nMaxZ = maxX;
buffer[0] = Shapes.or(buffer[0], Shapes.box(nMinX, minY, nMinZ, nMaxX, maxY, nMaxZ));
});
current = buffer[0];
}
return current;
}
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
}
@Override
public BlockState mirror(BlockState state, Mirror mirror) {
return state.rotate(mirror.getRotation(state.getValue(FACING)));
}
@Override
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()).setValue(LIT, false);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FACING, LIT);
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
Direction dir = state.getValue(FACING);
return switch (dir) {
case NORTH -> SHAPE_NORTH;
case SOUTH -> SHAPE_SOUTH;
case WEST -> SHAPE_WEST;
case EAST -> SHAPE_EAST;
default -> SHAPE_NORTH;
};
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new DNAAnalyzerBlockEntity(blockPos, blockState);
}
@Override
public RenderShape getRenderShape(BlockState state) {
return RenderShape.MODEL;
}
@Override
//? if >1.20.1 {
/*public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
*///?} else {
public void playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
//?}
if (!level.isClientSide) {
if (player.getAbilities().instabuild) {
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof DNAAnalyzerBlockEntity fbe) {
ItemStack stack = new ItemStack(this.asItem());
if (!fbe.isEmptyForDrop()) {
//? if >1.20.1 {
/*CompoundTag tag = fbe.saveWithoutMetadata(level.registryAccess());
var beTypeKey = level.registryAccess().registryOrThrow(Registries.BLOCK_ENTITY_TYPE).getKey(fbe.getType());
if (beTypeKey != null) tag.putString("id", beTypeKey.toString());
stack.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(tag));
*///?} else {
CompoundTag tag = fbe.saveWithoutMetadata();
stack.getOrCreateTagElement("BlockEntityTag").merge(tag);
//?}
}
popResource(level, pos, stack);
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
}
super.playerWillDestroy(level, pos, state, player);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
//? if >1.20.1 {
/*@Override
protected ItemInteractionResult useItemOn(ItemStack pStack, BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHitResult) {
if (!pLevel.isClientSide()) {
BlockEntity entity = pLevel.getBlockEntity(pPos);
if(entity instanceof DNAAnalyzerBlockEntity dnaAnalyzerBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) pPlayer, dnaAnalyzerBlockEntity);
}
}
return ItemInteractionResult.sidedSuccess(pLevel.isClientSide());
}
*///?} else {
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (!level.isClientSide()) {
BlockEntity entity = level.getBlockEntity(pos);
if (entity instanceof DNAAnalyzerBlockEntity dnaAnalyzerBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) player, dnaAnalyzerBlockEntity);
}
}
return InteractionResult.sidedSuccess(level.isClientSide());
}
//?}
@Override
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
if (blockEntityType != ModBlockEntities.DNA_ANALYZER_BE.get()) return null;
if (level.isClientSide) {
return null;
} else {
return createTickerHelper(blockEntityType, ModBlockEntities.DNA_ANALYZER_BE.get(),
(level1, blockPos, blockState, dnaAnalyzerBlockEntity) -> dnaAnalyzerBlockEntity.tick(level1, blockPos, blockState));
}
}
@Override
public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) {
if (!state.getValue(LIT)) {
return;
}
double xPos = (double)pos.getX() + 0.5;
double yPos = pos.getY();
double zPos = (double)pos.getZ() + 0.5;
Direction direction = state.getValue(FACING).getOpposite();
Direction.Axis axis = direction.getAxis();
double defaultOffset = random.nextDouble() * 0.6 - 0.3;
double xOffsets = axis == Direction.Axis.X ? (double)direction.getStepX() * 0.52 : defaultOffset;
double yOffset = random.nextDouble() * 6.0 / 8.0;
double zOffset = axis == Direction.Axis.Z ? (double)direction.getStepZ() * 0.52 : defaultOffset;
level.addParticle(ParticleTypes.SMOKE, xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
BlockEntity be = level.getBlockEntity(pos);
if(be instanceof DNAAnalyzerBlockEntity dnaAnalyzerBlockEntity && !dnaAnalyzerBlockEntity.itemHandler.getItem(1).isEmpty()) {
level.addParticle(new ItemParticleOption(ParticleTypes.ITEM, dnaAnalyzerBlockEntity.itemHandler.getItem(1)),
xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
}
}
}
@@ -0,0 +1,245 @@
package net.cmr.jurassicrevived.block.custom;
//? if >1.20.1 {
/*import com.mojang.serialization.MapCodec;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.ItemInteractionResult;
*///?}
import dev.architectury.registry.menu.MenuRegistry;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.custom.DNAExtractorBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ItemParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
public class DNAExtractorBlock extends BaseEntityBlock {
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public static final BooleanProperty LIT = BlockStateProperties.LIT;
//? if >1.20.1 {
/*public static final MapCodec<DNAExtractorBlock> CODEC = simpleCodec(DNAExtractorBlock::new);
@Override protected MapCodec<? extends BaseEntityBlock> codec() { return CODEC; }
*///?}
public DNAExtractorBlock(Properties properties) {
super(properties);
}
private static final VoxelShape SHAPE_NORTH = Shapes.box(
0.0 / 16.0, 0.0 / 16.0, 0.5 / 16.0,
16.0 / 16.0, 17.0 / 16.0, 15.5 / 16.0
);
private static final VoxelShape SHAPE_SOUTH = rotateShapeY(SHAPE_NORTH, 180);
private static final VoxelShape SHAPE_WEST = rotateShapeY(SHAPE_NORTH, 90);
private static final VoxelShape SHAPE_EAST = rotateShapeY(SHAPE_NORTH, 90);
private static VoxelShape rotateShapeY(VoxelShape shape, int degrees) {
double rad = Math.toRadians(((degrees % 360) + 360) % 360);
int turns = (int) Math.round(rad / (Math.PI / 2));
turns = ((turns % 4) + 4) % 4;
VoxelShape current = shape;
for (int i = 0; i < turns; i++) {
VoxelShape[] buffer = new VoxelShape[]{Shapes.empty()};
current.forAllBoxes((minX, minY, minZ, maxX, maxY, maxZ) -> {
double nMinX = 1.0 - maxZ;
double nMinZ = minX;
double nMaxX = 1.0 - minZ;
double nMaxZ = maxX;
buffer[0] = Shapes.or(buffer[0], Shapes.box(nMinX, minY, nMinZ, nMaxX, maxY, nMaxZ));
});
current = buffer[0];
}
return current;
}
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
}
@Override
public BlockState mirror(BlockState state, Mirror mirror) {
return state.rotate(mirror.getRotation(state.getValue(FACING)));
}
@Override
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite().getOpposite()).setValue(LIT, false);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FACING, LIT);
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
Direction dir = state.getValue(FACING);
return switch (dir) {
case NORTH -> SHAPE_NORTH;
case SOUTH -> SHAPE_SOUTH;
case WEST -> SHAPE_WEST;
case EAST -> SHAPE_EAST;
default -> SHAPE_NORTH;
};
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new DNAExtractorBlockEntity(blockPos, blockState);
}
@Override
public RenderShape getRenderShape(BlockState state) {
return RenderShape.MODEL;
}
@Override
//? if >1.20.1 {
/*public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
*///?} else {
public void playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
//?}
if (!level.isClientSide) {
if (player.getAbilities().instabuild) {
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof DNAExtractorBlockEntity fbe) {
ItemStack stack = new ItemStack(this.asItem());
if (!fbe.isEmptyForDrop()) {
//? if >1.20.1 {
/*CompoundTag tag = fbe.saveWithoutMetadata(level.registryAccess());
var beTypeKey = level.registryAccess().registryOrThrow(Registries.BLOCK_ENTITY_TYPE).getKey(fbe.getType());
if (beTypeKey != null) tag.putString("id", beTypeKey.toString());
stack.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(tag));
*///?} else {
CompoundTag tag = fbe.saveWithoutMetadata();
stack.getOrCreateTagElement("BlockEntityTag").merge(tag);
//?}
}
popResource(level, pos, stack);
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
}
super.playerWillDestroy(level, pos, state, player);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
@Override
public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) {
super.onRemove(pState, pLevel, pPos, pNewState, pIsMoving);
}
//? if >1.20.1 {
/*@Override
protected ItemInteractionResult useItemOn(ItemStack pStack, BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHitResult) {
if (!pLevel.isClientSide()) {
BlockEntity entity = pLevel.getBlockEntity(pPos);
if(entity instanceof DNAExtractorBlockEntity dnaExtractorBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) pPlayer, dnaExtractorBlockEntity);
}
}
return ItemInteractionResult.sidedSuccess(pLevel.isClientSide());
}
*///?} else {
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (!level.isClientSide()) {
BlockEntity entity = level.getBlockEntity(pos);
if (entity instanceof DNAExtractorBlockEntity dnaExtractorBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) player, dnaExtractorBlockEntity);
}
}
return InteractionResult.sidedSuccess(level.isClientSide());
}
//?}
@Override
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
if (blockEntityType != ModBlockEntities.DNA_EXTRACTOR_BE.get()) return null;
if (level.isClientSide) {
return null;
} else {
return createTickerHelper(blockEntityType, ModBlockEntities.DNA_EXTRACTOR_BE.get(),
(level1, blockPos, blockState, dnaExtractorBlockEntity) -> dnaExtractorBlockEntity.tick(level1, blockPos, blockState));
}
}
@Override
public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) {
if (!state.getValue(LIT)) {
return;
}
double xPos = (double)pos.getX() + 0.5;
double yPos = pos.getY();
double zPos = (double)pos.getZ() + 0.5;
Direction direction = state.getValue(FACING).getOpposite();
Direction.Axis axis = direction.getAxis();
double defaultOffset = random.nextDouble() * 0.6 - 0.3;
double xOffsets = axis == Direction.Axis.X ? (double)direction.getStepX() * 0.52 : defaultOffset;
double yOffset = random.nextDouble() * 6.0 / 8.0;
double zOffset = axis == Direction.Axis.Z ? (double)direction.getStepZ() * 0.52 : defaultOffset;
level.addParticle(ParticleTypes.SMOKE, xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
BlockEntity be = level.getBlockEntity(pos);
if(be instanceof DNAExtractorBlockEntity dnaExtractorBlockEntity && !dnaExtractorBlockEntity.itemHandler.getItem(1).isEmpty()) {
level.addParticle(new ItemParticleOption(ParticleTypes.ITEM, dnaExtractorBlockEntity.itemHandler.getItem(1)),
xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
}
}
}
@@ -0,0 +1,239 @@
package net.cmr.jurassicrevived.block.custom;
//? if >1.20.1 {
/*import com.mojang.serialization.MapCodec;
import net.minecraft.core.component.DataComponents;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.ItemInteractionResult;
*///?}
import dev.architectury.registry.menu.MenuRegistry;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.custom.DNAHybridizerBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ItemParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
public class DNAHybridizerBlock extends BaseEntityBlock {
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public static final BooleanProperty LIT = BlockStateProperties.LIT;
//? if >1.20.1 {
/*public static final MapCodec<DNAHybridizerBlock> CODEC = simpleCodec(DNAHybridizerBlock::new);
@Override protected MapCodec<? extends BaseEntityBlock> codec() { return CODEC; }
*///?}
public DNAHybridizerBlock(Properties properties) {
super(properties);
}
private static final VoxelShape SHAPE_NORTH = Shapes.box(
0.0 / 16.0, 0.0 / 16.0, 0.0 / 16.0,
16.0 / 16.0, 16.0 / 16.0, 13.5 / 16.0
);
private static final VoxelShape SHAPE_SOUTH = rotateShapeY(SHAPE_NORTH, 180);
private static final VoxelShape SHAPE_WEST = rotateShapeY(SHAPE_NORTH, 90);
private static final VoxelShape SHAPE_EAST = rotateShapeY(SHAPE_NORTH, 90);
private static VoxelShape rotateShapeY(VoxelShape shape, int degrees) {
double rad = Math.toRadians(((degrees % 360) + 360) % 360);
int turns = (int) Math.round(rad / (Math.PI / 2));
turns = ((turns % 4) + 4) % 4;
VoxelShape current = shape;
for (int i = 0; i < turns; i++) {
VoxelShape[] buffer = new VoxelShape[]{Shapes.empty()};
current.forAllBoxes((minX, minY, minZ, maxX, maxY, maxZ) -> {
double nMinX = 1.0 - maxZ;
double nMinZ = minX;
double nMaxX = 1.0 - minZ;
double nMaxZ = maxX;
buffer[0] = Shapes.or(buffer[0], Shapes.box(nMinX, minY, nMinZ, nMaxX, maxY, nMaxZ));
});
current = buffer[0];
}
return current;
}
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
}
@Override
public BlockState mirror(BlockState state, Mirror mirror) {
return state.rotate(mirror.getRotation(state.getValue(FACING)));
}
@Override
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite().getOpposite()).setValue(LIT, false);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FACING, LIT);
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
Direction dir = state.getValue(FACING);
return switch (dir) {
case NORTH -> SHAPE_NORTH;
case SOUTH -> SHAPE_SOUTH;
case WEST -> SHAPE_WEST;
case EAST -> SHAPE_EAST;
default -> SHAPE_NORTH;
};
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new DNAHybridizerBlockEntity(blockPos, blockState);
}
@Override
public RenderShape getRenderShape(BlockState state) {
return RenderShape.MODEL;
}
@Override
//? if >1.20.1 {
/*public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
*///?} else {
public void playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
//?}
if (!level.isClientSide) {
if (player.getAbilities().instabuild) {
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof DNAHybridizerBlockEntity fbe) {
ItemStack stack = new ItemStack(this.asItem());
if (!fbe.isEmptyForDrop()) {
//? if >1.20.1 {
/*CompoundTag tag = fbe.saveWithoutMetadata(level.registryAccess());
var beTypeKey = level.registryAccess().registryOrThrow(Registries.BLOCK_ENTITY_TYPE).getKey(fbe.getType());
if (beTypeKey != null) tag.putString("id", beTypeKey.toString());
stack.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(tag));
*///?} else {
CompoundTag tag = fbe.saveWithoutMetadata();
stack.getOrCreateTagElement("BlockEntityTag").merge(tag);
//?}
}
popResource(level, pos, stack);
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
}
super.playerWillDestroy(level, pos, state, player);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
//? if >1.20.1 {
/*@Override
protected ItemInteractionResult useItemOn(ItemStack pStack, BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHitResult) {
if (!pLevel.isClientSide()) {
BlockEntity entity = pLevel.getBlockEntity(pPos);
if(entity instanceof DNAHybridizerBlockEntity dnaHybridizerBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) pPlayer, dnaHybridizerBlockEntity);
}
}
return ItemInteractionResult.sidedSuccess(pLevel.isClientSide());
}
*///?} else {
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (!level.isClientSide()) {
BlockEntity entity = level.getBlockEntity(pos);
if (entity instanceof DNAHybridizerBlockEntity dnaHybridizerBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) player, dnaHybridizerBlockEntity);
}
}
return InteractionResult.sidedSuccess(level.isClientSide());
}
//?}
@Override
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
if (blockEntityType != ModBlockEntities.DNA_HYBRIDIZER_BE.get()) return null;
if (level.isClientSide) {
return null;
} else {
return createTickerHelper(blockEntityType, ModBlockEntities.DNA_HYBRIDIZER_BE.get(),
(level1, blockPos, blockState, dnaHybridizerBlockEntity) -> dnaHybridizerBlockEntity.tick(level1, blockPos, blockState));
}
}
@Override
public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) {
if (!state.getValue(LIT)) {
return;
}
double xPos = (double)pos.getX() + 0.5;
double yPos = pos.getY();
double zPos = (double)pos.getZ() + 0.5;
Direction direction = state.getValue(FACING).getOpposite();
Direction.Axis axis = direction.getAxis();
double defaultOffset = random.nextDouble() * 0.6 - 0.3;
double xOffsets = axis == Direction.Axis.X ? (double)direction.getStepX() * 0.52 : defaultOffset;
double yOffset = random.nextDouble() * 6.0 / 8.0;
double zOffset = axis == Direction.Axis.Z ? (double)direction.getStepZ() * 0.52 : defaultOffset;
level.addParticle(ParticleTypes.SMOKE, xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
BlockEntity be = level.getBlockEntity(pos);
if(be instanceof DNAHybridizerBlockEntity dnaHybridizerBlockEntity && !dnaHybridizerBlockEntity.itemHandler.getItem(1).isEmpty()) {
level.addParticle(new ItemParticleOption(ParticleTypes.ITEM, dnaHybridizerBlockEntity.itemHandler.getItem(1)),
xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
}
}
}
@@ -0,0 +1,55 @@
package net.cmr.jurassicrevived.block.custom;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class DecoBlock extends HorizontalDirectionalBlock {
public static final VoxelShape SHAPE = Block.box(4.0D, 0.0D, 4.0D, 12.0D, 5.0D, 12.0D);
public DecoBlock(Properties properties) {
super(properties);
this.registerDefaultState((BlockState)((BlockState)((BlockState)this.stateDefinition.any()).setValue(FACING, Direction.SOUTH)));
}
protected MapCodec<? extends HorizontalDirectionalBlock> codec() {
return null;
}
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(new Property[]{FACING});
}
public BlockState mirror(BlockState state, Mirror mirror) {
return state.rotate(mirror.getRotation((Direction)state.getValue(FACING)));
}
// Ensure rotation of FACING property when the block is rotated (e.g., structure placement, commands, etc.)
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
}
public BlockState getStateForPlacement(BlockPlaceContext context) {
// Ensure we always use a horizontal facing (N/E/S/W), even when looking up or down.
return (BlockState)this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite());
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
Direction facing = state.getValue(FACING);
return SHAPE;
}
}
@@ -0,0 +1,127 @@
package net.cmr.jurassicrevived.block.custom;
import net.cmr.jurassicrevived.block.entity.custom.EggBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.AgeableMob;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Supplier;
public class EggBlock extends Block implements EntityBlock {
private final Supplier<? extends EntityType<? extends Mob>> toSpawn;
private final int hatchSeconds = 1200;
public EggBlock(Properties pProperties, Supplier<? extends EntityType<? extends Mob>> toSpawn) {
super(pProperties);
this.toSpawn = toSpawn;
}
private static final VoxelShape EGG_SHAPE = Block.box(6.5D, 0.0D, 6.5D, 9.5D, 4.0D, 9.5D);
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return EGG_SHAPE;
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return EGG_SHAPE;
}
@Override
public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) {
super.onPlace(state, level, pos, oldState, isMoving);
if (!level.isClientSide) {
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof EggBlockEntity eggBE) {
eggBE.resetForNewPlacement(level, hatchSeconds);
}
level.scheduleTick(pos, this, hatchSeconds * 20);
}
}
@Override
public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
BlockEntity be = level.getBlockEntity(pos);
if (!(be instanceof EggBlockEntity eggBE)) return;
if (eggBE.getSecondsRemaining(level) <= 0) {
super.tick(state, level, pos, random);
} else {
level.scheduleTick(pos, this, eggBE.getSecondsRemaining(level) * 20);
return;
}
EntityType<? extends Mob> type = toSpawn.get();
if (type != null) {
Mob mob = type.create(level);
if (mob != null) {
Vec3 spawn = Vec3.atCenterOf(pos);
mob.moveTo(spawn.x, spawn.y + 0.1, spawn.z, level.random.nextFloat() * 360F, 0.0F);
//? if >1.20.1 {
/*mob.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), MobSpawnType.TRIGGERED, null);
*///?} else {
mob.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), MobSpawnType.TRIGGERED, null, null);
//?}
if (mob instanceof AgeableMob ageable) {
ageable.setBaby(true);
}
level.addFreshEntity(mob);
}
}
level.levelEvent(2001, pos, Block.getId(state));
level.removeBlock(pos, false);
}
@Override
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
if (!level.isClientSide && state.getBlock() != newState.getBlock()) {
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof EggBlockEntity eggBE) {
eggBE.invalidateTimer();
}
}
super.onRemove(state, level, pos, newState, isMoving);
}
@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
return new EggBlockEntity(pos, state);
}
//? if >1.20.1 {
/*@Override
public void appendHoverText(ItemStack pStack, Item.TooltipContext pContext, List<Component> pTootipComponents, TooltipFlag pTooltipFlag) {
pTootipComponents.add(Component.translatable("tooltip.jurassicrevived.egg.hatches_in", hatchSeconds));
super.appendHoverText(pStack, pContext, pTootipComponents, pTooltipFlag);
}
*///?} else {
@Override
public void appendHoverText(ItemStack pStack, @Nullable BlockGetter level, List<Component> pTootipComponents, TooltipFlag pTooltipFlag) {
pTootipComponents.add(Component.translatable("tooltip.jurassicrevived.egg.hatches_in", hatchSeconds));
super.appendHoverText(pStack, level, pTootipComponents, pTooltipFlag);
}
//?}
}
@@ -0,0 +1,240 @@
package net.cmr.jurassicrevived.block.custom;
//? if >1.20.1 {
/*import com.mojang.serialization.MapCodec;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.ItemInteractionResult;
*///?}
import dev.architectury.registry.menu.MenuRegistry;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.custom.EmbryoCalcificationMachineBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ItemParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
public class EmbryoCalcificationMachineBlock extends BaseEntityBlock {
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public static final BooleanProperty LIT = BlockStateProperties.LIT;
//? if >1.20.1 {
/*public static final MapCodec<EmbryoCalcificationMachineBlock> CODEC = simpleCodec(EmbryoCalcificationMachineBlock::new);
@Override protected MapCodec<? extends BaseEntityBlock> codec() { return CODEC; }
*///?}
public EmbryoCalcificationMachineBlock(Properties properties) {
super(properties);
}
private static final VoxelShape SHAPE_NORTH = Shapes.box(
1.0 / 16.0, 0.0 / 16.0, 4.0 / 16.0,
15.0 / 16.0, 17.0 / 16.0, 14.0 / 16.0
);
private static final VoxelShape SHAPE_SOUTH = rotateShapeY(SHAPE_NORTH, 180);
private static final VoxelShape SHAPE_WEST = rotateShapeY(SHAPE_NORTH, 90);
private static final VoxelShape SHAPE_EAST = rotateShapeY(SHAPE_NORTH, 90);
private static VoxelShape rotateShapeY(VoxelShape shape, int degrees) {
double rad = Math.toRadians(((degrees % 360) + 360) % 360);
int turns = (int) Math.round(rad / (Math.PI / 2));
turns = ((turns % 4) + 4) % 4;
VoxelShape current = shape;
for (int i = 0; i < turns; i++) {
VoxelShape[] buffer = new VoxelShape[]{Shapes.empty()};
current.forAllBoxes((minX, minY, minZ, maxX, maxY, maxZ) -> {
double nMinX = 1.0 - maxZ;
double nMinZ = minX;
double nMaxX = 1.0 - minZ;
double nMaxZ = maxX;
buffer[0] = Shapes.or(buffer[0], Shapes.box(nMinX, minY, nMinZ, nMaxX, maxY, nMaxZ));
});
current = buffer[0];
}
return current;
}
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
}
@Override
public BlockState mirror(BlockState state, Mirror mirror) {
return state.rotate(mirror.getRotation(state.getValue(FACING)));
}
@Override
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite().getOpposite()).setValue(LIT, false);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FACING, LIT);
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
Direction dir = state.getValue(FACING);
return switch (dir) {
case NORTH -> SHAPE_NORTH;
case SOUTH -> SHAPE_SOUTH;
case WEST -> SHAPE_WEST;
case EAST -> SHAPE_EAST;
default -> SHAPE_NORTH;
};
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new EmbryoCalcificationMachineBlockEntity(blockPos, blockState);
}
@Override
public RenderShape getRenderShape(BlockState state) {
return RenderShape.MODEL;
}
@Override
//? if >1.20.1 {
/*public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
*///?} else {
public void playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
//?}
if (!level.isClientSide) {
if (player.getAbilities().instabuild) {
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof EmbryoCalcificationMachineBlockEntity fbe) {
ItemStack stack = new ItemStack(this.asItem());
if (!fbe.isEmptyForDrop()) {
//? if >1.20.1 {
/*CompoundTag tag = fbe.saveWithoutMetadata(level.registryAccess());
var beTypeKey = level.registryAccess().registryOrThrow(Registries.BLOCK_ENTITY_TYPE).getKey(fbe.getType());
if (beTypeKey != null) tag.putString("id", beTypeKey.toString());
stack.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(tag));
*///?} else {
CompoundTag tag = fbe.saveWithoutMetadata();
stack.getOrCreateTagElement("BlockEntityTag").merge(tag);
//?}
}
popResource(level, pos, stack);
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
}
super.playerWillDestroy(level, pos, state, player);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
//? if >1.20.1 {
/*@Override
protected ItemInteractionResult useItemOn(ItemStack pStack, BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHitResult) {
if (!pLevel.isClientSide()) {
BlockEntity entity = pLevel.getBlockEntity(pPos);
if(entity instanceof EmbryoCalcificationMachineBlockEntity embryoCalcificationMachineBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) pPlayer, embryoCalcificationMachineBlockEntity);
}
}
return ItemInteractionResult.sidedSuccess(pLevel.isClientSide());
}
*///?} else {
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (!level.isClientSide()) {
BlockEntity entity = level.getBlockEntity(pos);
if (entity instanceof EmbryoCalcificationMachineBlockEntity embryoCalcificationMachineBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) player, embryoCalcificationMachineBlockEntity);
}
}
return InteractionResult.sidedSuccess(level.isClientSide());
}
//?}
@Override
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
if (blockEntityType != ModBlockEntities.EMBRYO_CALCIFICATION_MACHINE_BE.get()) return null;
if (level.isClientSide) {
return null;
} else {
return createTickerHelper(blockEntityType, ModBlockEntities.EMBRYO_CALCIFICATION_MACHINE_BE.get(),
(level1, blockPos, blockState, embryoCalcificationMachineBlockEntity) -> embryoCalcificationMachineBlockEntity.tick(level1, blockPos, blockState));
}
}
@Override
public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) {
if (!state.getValue(LIT)) {
return;
}
double xPos = (double)pos.getX() + 0.5;
double yPos = pos.getY();
double zPos = (double)pos.getZ() + 0.5;
Direction direction = state.getValue(FACING).getOpposite();
Direction.Axis axis = direction.getAxis();
double defaultOffset = random.nextDouble() * 0.6 - 0.3;
double xOffsets = axis == Direction.Axis.X ? (double)direction.getStepX() * 0.52 : defaultOffset;
double yOffset = random.nextDouble() * 6.0 / 8.0;
double zOffset = axis == Direction.Axis.Z ? (double)direction.getStepZ() * 0.52 : defaultOffset;
level.addParticle(ParticleTypes.SMOKE, xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
BlockEntity be = level.getBlockEntity(pos);
if(be instanceof EmbryoCalcificationMachineBlockEntity embryoCalcificationMachineBlockEntity && !embryoCalcificationMachineBlockEntity.itemHandler.getItem(1).isEmpty()) {
level.addParticle(new ItemParticleOption(ParticleTypes.ITEM, embryoCalcificationMachineBlockEntity.itemHandler.getItem(1)),
xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
}
}
}
@@ -0,0 +1,240 @@
package net.cmr.jurassicrevived.block.custom;
//? if >1.20.1 {
/*import com.mojang.serialization.MapCodec;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.ItemInteractionResult;
*///?}
import dev.architectury.registry.menu.MenuRegistry;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.custom.EmbryonicMachineBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ItemParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
public class EmbryonicMachineBlock extends BaseEntityBlock {
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public static final BooleanProperty LIT = BlockStateProperties.LIT;
//? if >1.20.1 {
/*public static final MapCodec<EmbryonicMachineBlock> CODEC = simpleCodec(EmbryonicMachineBlock::new);
@Override protected MapCodec<? extends BaseEntityBlock> codec() { return CODEC; }
*///?}
public EmbryonicMachineBlock(Properties properties) {
super(properties);
}
private static final VoxelShape SHAPE_NORTH = Shapes.box(
1.0 / 16.0, 0.0 / 16.0, 0.0 / 16.0,
15.0 / 16.0, 5.0 / 16.0, 15.0 / 16.0
);
private static final VoxelShape SHAPE_SOUTH = rotateShapeY(SHAPE_NORTH, 180);
private static final VoxelShape SHAPE_WEST = rotateShapeY(SHAPE_NORTH, 90);
private static final VoxelShape SHAPE_EAST = rotateShapeY(SHAPE_NORTH, 90);
private static VoxelShape rotateShapeY(VoxelShape shape, int degrees) {
double rad = Math.toRadians(((degrees % 360) + 360) % 360);
int turns = (int) Math.round(rad / (Math.PI / 2));
turns = ((turns % 4) + 4) % 4;
VoxelShape current = shape;
for (int i = 0; i < turns; i++) {
VoxelShape[] buffer = new VoxelShape[]{Shapes.empty()};
current.forAllBoxes((minX, minY, minZ, maxX, maxY, maxZ) -> {
double nMinX = 1.0 - maxZ;
double nMinZ = minX;
double nMaxX = 1.0 - minZ;
double nMaxZ = maxX;
buffer[0] = Shapes.or(buffer[0], Shapes.box(nMinX, minY, nMinZ, nMaxX, maxY, nMaxZ));
});
current = buffer[0];
}
return current;
}
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
}
@Override
public BlockState mirror(BlockState state, Mirror mirror) {
return state.rotate(mirror.getRotation(state.getValue(FACING)));
}
@Override
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite().getOpposite()).setValue(LIT, false);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FACING, LIT);
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
Direction dir = state.getValue(FACING);
return switch (dir) {
case NORTH -> SHAPE_NORTH;
case SOUTH -> SHAPE_SOUTH;
case WEST -> SHAPE_WEST;
case EAST -> SHAPE_EAST;
default -> SHAPE_NORTH;
};
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new EmbryonicMachineBlockEntity(blockPos, blockState);
}
@Override
public RenderShape getRenderShape(BlockState state) {
return RenderShape.MODEL;
}
@Override
//? if >1.20.1 {
/*public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
*///?} else {
public void playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
//?}
if (!level.isClientSide) {
if (player.getAbilities().instabuild) {
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof EmbryonicMachineBlockEntity fbe) {
ItemStack stack = new ItemStack(this.asItem());
if (!fbe.isEmptyForDrop()) {
//? if >1.20.1 {
/*CompoundTag tag = fbe.saveWithoutMetadata(level.registryAccess());
var beTypeKey = level.registryAccess().registryOrThrow(Registries.BLOCK_ENTITY_TYPE).getKey(fbe.getType());
if (beTypeKey != null) tag.putString("id", beTypeKey.toString());
stack.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(tag));
*///?} else {
CompoundTag tag = fbe.saveWithoutMetadata();
stack.getOrCreateTagElement("BlockEntityTag").merge(tag);
//?}
}
popResource(level, pos, stack);
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
}
super.playerWillDestroy(level, pos, state, player);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
//? if >1.20.1 {
/*@Override
protected ItemInteractionResult useItemOn(ItemStack pStack, BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHitResult) {
if (!pLevel.isClientSide()) {
BlockEntity entity = pLevel.getBlockEntity(pPos);
if(entity instanceof EmbryonicMachineBlockEntity embryonicMachineBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) pPlayer, embryonicMachineBlockEntity);
}
}
return ItemInteractionResult.sidedSuccess(pLevel.isClientSide());
}
*///?} else {
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (!level.isClientSide()) {
BlockEntity entity = level.getBlockEntity(pos);
if (entity instanceof EmbryonicMachineBlockEntity embryonicMachineBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) player, embryonicMachineBlockEntity);
}
}
return InteractionResult.sidedSuccess(level.isClientSide());
}
//?}
@Override
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
if (blockEntityType != ModBlockEntities.EMBRYONIC_MACHINE_BE.get()) return null;
if (level.isClientSide) {
return null;
} else {
return createTickerHelper(blockEntityType, ModBlockEntities.EMBRYONIC_MACHINE_BE.get(),
(level1, blockPos, blockState, embryonicMachineBlockEntity) -> embryonicMachineBlockEntity.tick(level1, blockPos, blockState));
}
}
@Override
public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) {
if (!state.getValue(LIT)) {
return;
}
double xPos = (double)pos.getX() + 0.5;
double yPos = pos.getY();
double zPos = (double)pos.getZ() + 0.5;
Direction direction = state.getValue(FACING).getOpposite();
Direction.Axis axis = direction.getAxis();
double defaultOffset = random.nextDouble() * 0.6 - 0.3;
double xOffsets = axis == Direction.Axis.X ? (double)direction.getStepX() * 0.52 : defaultOffset;
double yOffset = random.nextDouble() * 6.0 / 8.0;
double zOffset = axis == Direction.Axis.Z ? (double)direction.getStepZ() * 0.52 : defaultOffset;
level.addParticle(ParticleTypes.SMOKE, xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
BlockEntity be = level.getBlockEntity(pos);
if(be instanceof EmbryonicMachineBlockEntity embryonicMachineBlockEntity && !embryonicMachineBlockEntity.itemHandler.getItem(1).isEmpty()) {
level.addParticle(new ItemParticleOption(ParticleTypes.ITEM, embryonicMachineBlockEntity.itemHandler.getItem(1)),
xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
}
}
}
@@ -0,0 +1,55 @@
package net.cmr.jurassicrevived.block.custom;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class FenceLightBlock extends HorizontalDirectionalBlock {
public static final VoxelShape SHAPE = Block.box(6.0D, 0.0D, 6.0D, 10.0D, 6.0D, 10.0D);
public FenceLightBlock(Properties properties) {
super(properties);
this.registerDefaultState((BlockState)((BlockState)((BlockState)this.stateDefinition.any()).setValue(FACING, Direction.SOUTH)));
}
protected MapCodec<? extends HorizontalDirectionalBlock> codec() {
return null;
}
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(new Property[]{FACING});
}
public BlockState mirror(BlockState state, Mirror mirror) {
return state.rotate(mirror.getRotation((Direction)state.getValue(FACING)));
}
// Ensure rotation of FACING property when the block is rotated (e.g., structure placement, commands, etc.)
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
}
public BlockState getStateForPlacement(BlockPlaceContext context) {
// Ensure we always use a horizontal facing (N/E/S/W), even when looking up or down.
return (BlockState)this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite());
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
Direction facing = state.getValue(FACING);
return SHAPE;
}
}
@@ -0,0 +1,275 @@
package net.cmr.jurassicrevived.block.custom;
import net.cmr.jurassicrevived.util.FenceUpdateGuard;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
public class FencePoleBlock extends Block implements SimpleWaterloggedBlock {
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
public static final BooleanProperty NORTH = BooleanProperty.create("north");
public static final BooleanProperty EAST = BooleanProperty.create("east");
public static final BooleanProperty SOUTH = BooleanProperty.create("south");
public static final BooleanProperty WEST = BooleanProperty.create("west");
public static final BooleanProperty NE = BooleanProperty.create("ne");
public static final BooleanProperty SE = BooleanProperty.create("se");
public static final BooleanProperty SW = BooleanProperty.create("sw");
public static final BooleanProperty NW = BooleanProperty.create("nw");
public static final IntegerProperty TIER = IntegerProperty.create("tier", 0, 2);
public enum Tier {
LOW(0), MEDIUM(1), HIGH(2);
public final int id;
Tier(int id) { this.id = id; }
}
private final Tier tierConfig;
public FencePoleBlock(Properties properties, Tier tier) {
super(properties);
this.tierConfig = tier;
this.registerDefaultState(this.stateDefinition.any()
.setValue(NORTH, false)
.setValue(EAST, false)
.setValue(SOUTH, false)
.setValue(WEST, false)
.setValue(NE, false)
.setValue(SE, false)
.setValue(SW, false)
.setValue(NW, false)
.setValue(TIER, tier.id)
.setValue(WATERLOGGED, false)
);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(WATERLOGGED, NORTH, EAST, SOUTH, WEST, NE, SE, SW, NW, TIER);
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext ctx) {
Level level = ctx.getLevel();
BlockPos pos = ctx.getClickedPos();
FluidState fluid = level.getFluidState(pos);
return this.defaultBlockState()
.setValue(NORTH, connectsTo(level, pos, Direction.NORTH))
.setValue(EAST, connectsTo(level, pos, Direction.EAST))
.setValue(SOUTH, connectsTo(level, pos, Direction.SOUTH))
.setValue(WEST, connectsTo(level, pos, Direction.WEST))
.setValue(NE, FenceWireBlock.canConnectDiagonally(level, pos, Direction.NORTH, Direction.EAST))
.setValue(SE, FenceWireBlock.canConnectDiagonally(level, pos, Direction.SOUTH, Direction.EAST))
.setValue(SW, FenceWireBlock.canConnectDiagonally(level, pos, Direction.SOUTH, Direction.WEST))
.setValue(NW, FenceWireBlock.canConnectDiagonally(level, pos, Direction.NORTH, Direction.WEST))
.setValue(TIER, tierConfig.id)
.setValue(WATERLOGGED, fluid.getType() == Fluids.WATER);
}
@Override
@SuppressWarnings("deprecation")
public BlockState updateShape(BlockState state, Direction dir, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
if (state.getValue(WATERLOGGED)) {
level.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level));
}
if (dir.getAxis().isHorizontal()) {
boolean connect = connectsTo(level, pos, dir);
state = state.setValue(propertyFor(dir), connect);
}
return state;
}
@Override
@SuppressWarnings("deprecation")
public void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, BlockPos neighborPos, boolean movedByPiston) {
BlockState updated = state
.setValue(NE, FenceWireBlock.canConnectDiagonally(level, pos, Direction.NORTH, Direction.EAST))
.setValue(SE, FenceWireBlock.canConnectDiagonally(level, pos, Direction.SOUTH, Direction.EAST))
.setValue(SW, FenceWireBlock.canConnectDiagonally(level, pos, Direction.SOUTH, Direction.WEST))
.setValue(NW, FenceWireBlock.canConnectDiagonally(level, pos, Direction.NORTH, Direction.WEST));
if (updated != state) {
level.setBlock(pos, updated, Block.UPDATE_CLIENTS);
}
if (beginGuard()) {
try {
updateDiagonalsAround(level, pos);
} finally {
endGuard();
}
}
super.neighborChanged(state, level, pos, neighborBlock, neighborPos, movedByPiston);
}
private static boolean beginGuard() {
return FenceUpdateGuard.begin();
}
private static void endGuard() {
FenceUpdateGuard.end();
}
private void updateDiagonalsAround(Level level, BlockPos pos) {
BlockPos[] diags = new BlockPos[] {
pos.north().east(), pos.south().east(), pos.south().west(), pos.north().west()
};
for (BlockPos p : diags) {
BlockState bs = level.getBlockState(p);
Block b = bs.getBlock();
if (b instanceof FencePoleBlock) {
boolean ne = FenceWireBlock.canConnectDiagonally(level, p, Direction.NORTH, Direction.EAST);
boolean se = FenceWireBlock.canConnectDiagonally(level, p, Direction.SOUTH, Direction.EAST);
boolean sw = FenceWireBlock.canConnectDiagonally(level, p, Direction.SOUTH, Direction.WEST);
boolean nw = FenceWireBlock.canConnectDiagonally(level, p, Direction.NORTH, Direction.WEST);
BlockState updated = bs
.setValue(FencePoleBlock.NE, ne)
.setValue(FencePoleBlock.SE, se)
.setValue(FencePoleBlock.SW, sw)
.setValue(FencePoleBlock.NW, nw);
if (updated != bs) {
level.setBlock(p, updated, Block.UPDATE_CLIENTS);
}
} else if (b instanceof FenceWireBlock) {
boolean ne = FenceWireBlock.canConnectDiagonally(level, p, Direction.NORTH, Direction.EAST);
boolean se = FenceWireBlock.canConnectDiagonally(level, p, Direction.SOUTH, Direction.EAST);
boolean sw = FenceWireBlock.canConnectDiagonally(level, p, Direction.SOUTH, Direction.WEST);
boolean nw = FenceWireBlock.canConnectDiagonally(level, p, Direction.NORTH, Direction.WEST);
BlockState updated = bs
.setValue(FenceWireBlock.NE, ne)
.setValue(FenceWireBlock.SE, se)
.setValue(FenceWireBlock.SW, sw)
.setValue(FenceWireBlock.NW, nw);
if (updated != bs) {
level.setBlock(p, updated, Block.UPDATE_CLIENTS);
}
}
}
}
private boolean connectsTo(LevelAccessor level, BlockPos pos, Direction dir) {
BlockPos neighborPos = pos.relative(dir);
BlockState neighbor = level.getBlockState(neighborPos);
Block nb = neighbor.getBlock();
if (nb instanceof FenceWireBlock || nb instanceof FencePoleBlock) {
return true;
}
return neighbor.isFaceSturdy(level, neighborPos, dir.getOpposite());
}
private static BooleanProperty propertyFor(Direction dir) {
return switch (dir) {
case NORTH -> NORTH;
case EAST -> EAST;
case SOUTH -> SOUTH;
case WEST -> WEST;
default -> throw new IllegalArgumentException("Only horizontal");
};
}
@Override
public FluidState getFluidState(BlockState state) {
return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
}
@Override
public boolean propagatesSkylightDown(BlockState state, BlockGetter level, BlockPos pos) {
return true;
}
@Override
public int getLightBlock(BlockState state, BlockGetter level, BlockPos pos) {
return 0;
}
@Override
public float getShadeBrightness(BlockState state, BlockGetter level, BlockPos pos) {
return 1.0F;
}
private static final VoxelShape POST = Block.box(6.5, 0.0, 6.5, 9.5, 16.0, 9.5);
private static final VoxelShape ARM_NORTH = Block.box(7.5, 2.5, 0.0, 8.5, 13.5, 8.0);
private static final VoxelShape ARM_SOUTH = Block.box(7.5, 2.5, 8.0, 8.5, 13.5, 16.0);
private static final VoxelShape ARM_WEST = Block.box(0.0, 2.5, 7.5, 8.0, 13.5, 8.5);
private static final VoxelShape ARM_EAST = Block.box(8.0, 2.5, 7.5, 16.0, 13.5, 8.5);
private static VoxelShape buildDiagonal(boolean east, boolean south) {
VoxelShape shape = Shapes.empty();
double y1 = 2.5, y2 = 13.5;
for (int i = 0; i < 8; i++) {
double off = i;
double x1 = east ? 16 - (off + 1) : 0 + off;
double x2 = x1 + 1;
double z1 = south ? 16 - (off + 1) : 0 + off;
double z2 = z1 + 1;
shape = Shapes.or(shape, Block.box(x1, y1, z1, x2, y2, z2));
}
return shape;
}
private static final VoxelShape DIAG_NE = buildDiagonal(true, false);
private static final VoxelShape DIAG_SE = buildDiagonal(true, true);
private static final VoxelShape DIAG_SW = buildDiagonal(false, true);
private static final VoxelShape DIAG_NW = buildDiagonal(false, false);
// Precomputed shapes for all 256 combinations:
// bit 0..3: NORTH, EAST, SOUTH, WEST; bit 4..7: NE, SE, SW, NW
private static final VoxelShape[] SHAPES = new VoxelShape[256];
static {
for (int mask = 0; mask < SHAPES.length; mask++) {
VoxelShape s = POST;
if ((mask & (1 << 0)) != 0) s = Shapes.or(s, ARM_NORTH);
if ((mask & (1 << 1)) != 0) s = Shapes.or(s, ARM_EAST);
if ((mask & (1 << 2)) != 0) s = Shapes.or(s, ARM_SOUTH);
if ((mask & (1 << 3)) != 0) s = Shapes.or(s, ARM_WEST);
if ((mask & (1 << 4)) != 0) s = Shapes.or(s, DIAG_NE);
if ((mask & (1 << 5)) != 0) s = Shapes.or(s, DIAG_SE);
if ((mask & (1 << 6)) != 0) s = Shapes.or(s, DIAG_SW);
if ((mask & (1 << 7)) != 0) s = Shapes.or(s, DIAG_NW);
SHAPES[mask] = s;
}
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext ctx) {
int mask = 0;
if (state.getValue(NORTH)) mask |= (1 << 0);
if (state.getValue(EAST)) mask |= (1 << 1);
if (state.getValue(SOUTH)) mask |= (1 << 2);
if (state.getValue(WEST)) mask |= (1 << 3);
if (state.getValue(NE)) mask |= (1 << 4);
if (state.getValue(SE)) mask |= (1 << 5);
if (state.getValue(SW)) mask |= (1 << 6);
if (state.getValue(NW)) mask |= (1 << 7);
return SHAPES[mask];
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext ctx) {
return getShape(state, level, pos, ctx);
}
}
@@ -0,0 +1,413 @@
package net.cmr.jurassicrevived.block.custom;
import net.cmr.jurassicrevived.util.FenceUpdateGuard;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.IntegerProperty;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
public class FenceWireBlock extends Block implements SimpleWaterloggedBlock {
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
private static boolean beginGuard() {
return FenceUpdateGuard.begin();
}
private static void endGuard() {
FenceUpdateGuard.end();
}
public static final BooleanProperty NORTH = BlockStateProperties.NORTH;
public static final BooleanProperty EAST = BlockStateProperties.EAST;
public static final BooleanProperty SOUTH = BlockStateProperties.SOUTH;
public static final BooleanProperty WEST = BlockStateProperties.WEST;
public static final BooleanProperty POWERED = BlockStateProperties.POWERED;
public static final BooleanProperty NE = BooleanProperty.create("ne");
public static final BooleanProperty SE = BooleanProperty.create("se");
public static final BooleanProperty SW = BooleanProperty.create("sw");
public static final BooleanProperty NW = BooleanProperty.create("nw");
public static final IntegerProperty TIER = IntegerProperty.create("tier", 0, 2);
public enum Tier {
LOW(0), MEDIUM(1), HIGH(2);
public final int id;
Tier(int id) {
this.id = id;
}
}
private final Tier tierConfig;
public FenceWireBlock(Properties properties, Tier tier) {
super(properties);
this.tierConfig = tier;
this.registerDefaultState(this.stateDefinition.any()
.setValue(NORTH, false)
.setValue(EAST, false)
.setValue(SOUTH, false)
.setValue(WEST, false)
.setValue(POWERED, false)
.setValue(NE, false)
.setValue(SE, false)
.setValue(SW, false)
.setValue(NW, false)
.setValue(TIER, tier.id)
.setValue(WATERLOGGED, false)
);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(NORTH, EAST, SOUTH, WEST, POWERED, NE, SE, SW, NW, TIER, WATERLOGGED);
}
@Override
public BlockState getStateForPlacement(BlockPlaceContext ctx) {
Level level = ctx.getLevel();
BlockPos pos = ctx.getClickedPos();
boolean powered = level.hasNeighborSignal(pos);
boolean waterlogged = level.getFluidState(pos).getType() == Fluids.WATER;
return this.defaultBlockState()
.setValue(NORTH, connectsCardinalTo(level, pos, Direction.NORTH))
.setValue(EAST, connectsCardinalTo(level, pos, Direction.EAST))
.setValue(SOUTH, connectsCardinalTo(level, pos, Direction.SOUTH))
.setValue(WEST, connectsCardinalTo(level, pos, Direction.WEST))
.setValue(NE, canConnectDiagonally(level, pos, Direction.NORTH, Direction.EAST))
.setValue(SE, canConnectDiagonally(level, pos, Direction.SOUTH, Direction.EAST))
.setValue(SW, canConnectDiagonally(level, pos, Direction.SOUTH, Direction.WEST))
.setValue(NW, canConnectDiagonally(level, pos, Direction.NORTH, Direction.WEST))
.setValue(POWERED, powered)
.setValue(TIER, tierConfig.id)
.setValue(WATERLOGGED, waterlogged);
}
@Override
@SuppressWarnings("deprecation")
public BlockState updateShape(BlockState state, Direction dir, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
if (state.getValue(WATERLOGGED)) {
level.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level));
}
if (dir.getAxis().isHorizontal()) {
boolean connect = connectsCardinalTo(level, pos, dir);
state = state.setValue(propertyFor(dir), connect);
}
state = state
.setValue(NE, canConnectDiagonally(level, pos, Direction.NORTH, Direction.EAST))
.setValue(SE, canConnectDiagonally(level, pos, Direction.SOUTH, Direction.EAST))
.setValue(SW, canConnectDiagonally(level, pos, Direction.SOUTH, Direction.WEST))
.setValue(NW, canConnectDiagonally(level, pos, Direction.NORTH, Direction.WEST));
return state;
}
@Override
public FluidState getFluidState(BlockState state) {
return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
}
@Override
@SuppressWarnings("deprecation")
public void neighborChanged(BlockState state, Level level, BlockPos pos, Block neighborBlock, BlockPos neighborPos, boolean movedByPiston) {
boolean poweredNow = level.hasNeighborSignal(pos);
if (poweredNow != state.getValue(POWERED)) {
if (beginGuard()) {
try {
level.setBlock(pos, state.setValue(POWERED, poweredNow), Block.UPDATE_ALL);
} finally {
endGuard();
}
} else {
level.setBlock(pos, state.setValue(POWERED, poweredNow), Block.UPDATE_ALL);
}
}
if (beginGuard()) {
try {
BlockState updated = state
.setValue(NE, canConnectDiagonally(level, pos, Direction.NORTH, Direction.EAST))
.setValue(SE, canConnectDiagonally(level, pos, Direction.SOUTH, Direction.EAST))
.setValue(SW, canConnectDiagonally(level, pos, Direction.SOUTH, Direction.WEST))
.setValue(NW, canConnectDiagonally(level, pos, Direction.NORTH, Direction.WEST));
if (updated != state) {
level.setBlock(pos, updated, Block.UPDATE_CLIENTS);
}
updateDiagonalsAround(level, pos);
} finally {
endGuard();
}
}
}
private void recomputeSelfDiagonals(Level level, BlockPos pos, BlockState state) {
BlockState updated = state
.setValue(NE, canConnectDiagonally(level, pos, Direction.NORTH, Direction.EAST))
.setValue(SE, canConnectDiagonally(level, pos, Direction.SOUTH, Direction.EAST))
.setValue(SW, canConnectDiagonally(level, pos, Direction.SOUTH, Direction.WEST))
.setValue(NW, canConnectDiagonally(level, pos, Direction.NORTH, Direction.WEST));
if (updated != state) {
// Diagonal-only change: client update only
level.setBlock(pos, updated, Block.UPDATE_CLIENTS);
}
}
private void updateDiagonalsAround(Level level, BlockPos pos) {
BlockPos nePos = pos.north().east();
BlockPos sePos = pos.south().east();
BlockPos swPos = pos.south().west();
BlockPos nwPos = pos.north().west();
BlockPos[] diags = new BlockPos[]{nePos, sePos, swPos, nwPos};
for (BlockPos p : diags) {
BlockState bs = level.getBlockState(p);
Block b = bs.getBlock();
if (b instanceof FencePoleBlock) {
BooleanProperty backFlag;
Direction backA;
Direction backB;
if (p.equals(nePos)) {
backFlag = FencePoleBlock.SW;
backA = Direction.SOUTH;
backB = Direction.WEST;
} else if (p.equals(sePos)) {
backFlag = FencePoleBlock.NW;
backA = Direction.NORTH;
backB = Direction.WEST;
} else if (p.equals(swPos)) {
backFlag = FencePoleBlock.NE;
backA = Direction.NORTH;
backB = Direction.EAST;
} else {
backFlag = FencePoleBlock.SE;
backA = Direction.SOUTH;
backB = Direction.EAST;
}
boolean allow = FenceWireBlock.canConnectDiagonally(level, p, backA, backB);
BlockState updated = bs.setValue(backFlag, allow);
if (updated != bs) {
level.setBlock(p, updated, Block.UPDATE_CLIENTS);
}
} else if (b instanceof FenceWireBlock) {
boolean ne = canConnectDiagonally(level, p, Direction.NORTH, Direction.EAST);
boolean se = canConnectDiagonally(level, p, Direction.SOUTH, Direction.EAST);
boolean sw = canConnectDiagonally(level, p, Direction.SOUTH, Direction.WEST);
boolean nw = canConnectDiagonally(level, p, Direction.NORTH, Direction.WEST);
BlockState updated = bs
.setValue(FenceWireBlock.NE, ne)
.setValue(FenceWireBlock.SE, se)
.setValue(FenceWireBlock.SW, sw)
.setValue(FenceWireBlock.NW, nw);
if (updated != bs) {
level.setBlock(p, updated, Block.UPDATE_CLIENTS);
}
}
}
}
public static boolean canConnectDiagonally(LevelAccessor level, BlockPos pos, Direction a, Direction b) {
BlockState srcState = level.getBlockState(pos);
Block srcBlock = srcState.getBlock();
BlockPos diag = pos.relative(a).relative(b);
BlockState diagState = level.getBlockState(diag);
Block diagBlock = diagState.getBlock();
boolean srcIsPole = srcBlock instanceof FencePoleBlock;
boolean diagIsWire = diagBlock instanceof FenceWireBlock;
boolean diagIsPole = diagBlock instanceof FencePoleBlock;
boolean diagIsSolidCorner =
diagState.isFaceSturdy(level, diag, a.getOpposite())
|| diagState.isFaceSturdy(level, diag, b.getOpposite());
if (!diagIsWire && !diagIsPole && !diagIsSolidCorner) return false;
if (srcIsPole || diagIsPole) {
return true;
}
BlockPos stepA = pos.relative(a);
BlockPos stepB = pos.relative(b);
BlockState stateA = level.getBlockState(stepA);
BlockState stateB = level.getBlockState(stepB);
boolean stepABlocks =
stateA.getBlock() instanceof FenceWireBlock
|| stateA.getBlock() instanceof FencePoleBlock
|| stateA.isFaceSturdy(level, stepA, a.getOpposite());
boolean stepBBlocks =
stateB.getBlock() instanceof FenceWireBlock
|| stateB.getBlock() instanceof FencePoleBlock
|| stateB.isFaceSturdy(level, stepB, b.getOpposite());
if (stepABlocks || stepBBlocks) return false;
return true;
}
private static boolean canConnectDiagonallyToFence(LevelAccessor level, BlockPos pos, Direction a, Direction b) {
BlockPos diag = pos.relative(a).relative(b);
BlockState diagState = level.getBlockState(diag);
Block db = diagState.getBlock();
if (!(db instanceof FenceWireBlock) && !(db instanceof FencePoleBlock)) {
return false;
}
return canConnectDiagonally(level, pos, a, b);
}
private boolean connectsTo(BlockState neighbor) {
Block b = neighbor.getBlock();
return (b instanceof FenceWireBlock) || (b instanceof FencePoleBlock);
}
private static BooleanProperty propertyFor(Direction dir) {
return switch (dir) {
case NORTH -> NORTH;
case EAST -> EAST;
case SOUTH -> SOUTH;
case WEST -> WEST;
default -> throw new IllegalArgumentException("Only horizontal");
};
}
private boolean connectsCardinalTo(LevelAccessor level, BlockPos pos, Direction dir) {
BlockPos neighborPos = pos.relative(dir);
BlockState neighbor = level.getBlockState(neighborPos);
Block nb = neighbor.getBlock();
if (nb instanceof FencePoleBlock) {
return true;
}
if (nb instanceof FenceWireBlock) {
Direction right = dir.getClockWise();
Direction left = dir.getCounterClockWise();
boolean thisDiagRight = canConnectDiagonallyToFence(level, pos, dir, right);
boolean thisDiagLeft = canConnectDiagonallyToFence(level, pos, dir, left);
Direction back = dir.getOpposite();
boolean neighDiagRight = canConnectDiagonallyToFence(level, neighborPos, back, back.getClockWise());
boolean neighDiagLeft = canConnectDiagonallyToFence(level, neighborPos, back, back.getCounterClockWise());
if (thisDiagRight || thisDiagLeft || neighDiagRight || neighDiagLeft) {
return false;
}
return true;
}
if (neighbor.isFaceSturdy(level, neighborPos, dir.getOpposite())) {
return true;
}
return false;
}
@Override
public boolean propagatesSkylightDown(BlockState state, BlockGetter level, BlockPos pos) {
return true;
}
@Override
public int getLightBlock(BlockState state, BlockGetter level, BlockPos pos) {
return 0;
}
@Override
public float getShadeBrightness(BlockState state, BlockGetter level, BlockPos pos) {
return 1.0F;
}
private static final VoxelShape CENTER = Block.box(7.5, 2.5, 7.5, 8.5, 13.5, 8.5);
private static final VoxelShape ARM_NORTH = Block.box(7.5, 2.5, 0.0, 8.5, 13.5, 8.0);
private static final VoxelShape ARM_SOUTH = Block.box(7.5, 2.5, 8.0, 8.5, 13.5, 16.0);
private static final VoxelShape ARM_WEST = Block.box(0.0, 2.5, 7.5, 8.0, 13.5, 8.5);
private static final VoxelShape ARM_EAST = Block.box(8.0, 2.5, 7.5, 16.0, 13.5, 8.5);
private static VoxelShape buildDiagonal(boolean east, boolean south) {
VoxelShape shape = Shapes.empty();
double y1 = 2.5, y2 = 13.5;
for (int i = 0; i < 8; i++) {
double off = i; // 0..7
double x1 = east ? 16 - (off + 1) : 0 + off;
double x2 = x1 + 1;
double z1 = south ? 16 - (off + 1) : 0 + off;
double z2 = z1 + 1;
shape = Shapes.or(shape, Block.box(x1, y1, z1, x2, y2, z2));
}
return shape;
}
private static final VoxelShape DIAG_NE = buildDiagonal(true, false);
private static final VoxelShape DIAG_SE = buildDiagonal(true, true);
private static final VoxelShape DIAG_SW = buildDiagonal(false, true);
private static final VoxelShape DIAG_NW = buildDiagonal(false, false);
// Precomputed shapes for all 256 combinations:
// bit 0..3: NORTH, EAST, SOUTH, WEST; bit 4..7: NE, SE, SW, NW
private static final VoxelShape[] SHAPES = new VoxelShape[256];
static {
for (int mask = 0; mask < SHAPES.length; mask++) {
VoxelShape s = CENTER;
if ((mask & (1 << 0)) != 0) s = Shapes.or(s, ARM_NORTH);
if ((mask & (1 << 1)) != 0) s = Shapes.or(s, ARM_EAST);
if ((mask & (1 << 2)) != 0) s = Shapes.or(s, ARM_SOUTH);
if ((mask & (1 << 3)) != 0) s = Shapes.or(s, ARM_WEST);
if ((mask & (1 << 4)) != 0) s = Shapes.or(s, DIAG_NE);
if ((mask & (1 << 5)) != 0) s = Shapes.or(s, DIAG_SE);
if ((mask & (1 << 6)) != 0) s = Shapes.or(s, DIAG_SW);
if ((mask & (1 << 7)) != 0) s = Shapes.or(s, DIAG_NW);
SHAPES[mask] = s;
}
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext ctx) {
int mask = 0;
if (state.getValue(NORTH)) mask |= (1 << 0);
if (state.getValue(EAST)) mask |= (1 << 1);
if (state.getValue(SOUTH)) mask |= (1 << 2);
if (state.getValue(WEST)) mask |= (1 << 3);
if (state.getValue(NE)) mask |= (1 << 4);
if (state.getValue(SE)) mask |= (1 << 5);
if (state.getValue(SW)) mask |= (1 << 6);
if (state.getValue(NW)) mask |= (1 << 7);
return SHAPES[mask];
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext ctx) {
return getShape(state, level, pos, ctx);
}
}
@@ -0,0 +1,263 @@
package net.cmr.jurassicrevived.block.custom;
//? if >1.20.1 {
/*import com.mojang.serialization.MapCodec;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.ItemInteractionResult;
*///?}
import dev.architectury.registry.menu.MenuRegistry;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.custom.FossilCleanerBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ItemParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
public class FossilCleanerBlock extends BaseEntityBlock {
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public static final BooleanProperty LIT = BlockStateProperties.LIT;
//? if >1.20.1 {
/*public static final MapCodec<FossilCleanerBlock> CODEC = simpleCodec(FossilCleanerBlock::new);
@Override protected MapCodec<? extends BaseEntityBlock> codec() { return CODEC; }
*///?}
public FossilCleanerBlock(Properties properties) {
super(properties);
}
private static final VoxelShape SHAPE_NORTH = Shapes.box(
2.0 / 16.0, 0.0 / 16.0, 2.0 / 16.0,
14.0 / 16.0, 14.0 / 16.0, 15.0 / 16.0
);
private static final VoxelShape SHAPE_SOUTH = rotateShapeY(SHAPE_NORTH, 180);
private static final VoxelShape SHAPE_WEST = rotateShapeY(SHAPE_NORTH, -90);
private static final VoxelShape SHAPE_EAST = rotateShapeY(SHAPE_NORTH, 90);
private static VoxelShape rotateShapeY(VoxelShape shape, int degrees) {
double rad = Math.toRadians(((degrees % 360) + 360) % 360);
int turns = (int) Math.round(rad / (Math.PI / 2));
turns = ((turns % 4) + 4) % 4;
VoxelShape current = shape;
for (int i = 0; i < turns; i++) {
VoxelShape[] buffer = new VoxelShape[]{Shapes.empty()};
current.forAllBoxes((minX, minY, minZ, maxX, maxY, maxZ) -> {
double nMinX = 1.0 - maxZ;
double nMinZ = minX;
double nMaxX = 1.0 - minZ;
double nMaxZ = maxX;
buffer[0] = Shapes.or(buffer[0], Shapes.box(nMinX, minY, nMinZ, nMaxX, maxY, nMaxZ));
});
current = buffer[0];
}
return current;
}
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
}
@Override
public BlockState mirror(BlockState state, Mirror mirror) {
return state.rotate(mirror.getRotation(state.getValue(FACING)));
}
@Override
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite().getOpposite()).setValue(LIT, false);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FACING, LIT);
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
Direction dir = state.getValue(FACING);
return switch (dir) {
case NORTH -> SHAPE_NORTH;
case SOUTH -> SHAPE_SOUTH;
case WEST -> SHAPE_WEST;
case EAST -> SHAPE_EAST;
default -> SHAPE_NORTH;
};
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new FossilCleanerBlockEntity(blockPos, blockState);
}
@Override
public RenderShape getRenderShape(BlockState state) {
return RenderShape.MODEL;
}
@Override
//? if >1.20.1 {
/*public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
*///?} else {
public void playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
//?}
if (!level.isClientSide) {
if (player.getAbilities().instabuild) {
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof FossilCleanerBlockEntity fbe) {
ItemStack stack = new ItemStack(this.asItem());
if (!fbe.isEmptyForDrop()) {
//? if >1.20.1 {
/*CompoundTag tag = fbe.saveWithoutMetadata(level.registryAccess());
var beTypeKey = level.registryAccess().registryOrThrow(Registries.BLOCK_ENTITY_TYPE).getKey(fbe.getType());
if (beTypeKey != null) tag.putString("id", beTypeKey.toString());
stack.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(tag));
*///?} else {
CompoundTag tag = fbe.saveWithoutMetadata();
stack.getOrCreateTagElement("BlockEntityTag").merge(tag);
//?}
}
popResource(level, pos, stack);
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
}
super.playerWillDestroy(level, pos, state, player);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
//? if >1.20.1 {
/*@Override
protected ItemInteractionResult useItemOn(ItemStack pStack, BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHitResult) {
BlockEntity entity = pLevel.getBlockEntity(pPos);
if (entity instanceof FossilCleanerBlockEntity fossilCleanerBlockEntity) {
if (!pLevel.isClientSide()) {
if (pStack.is(Items.WATER_BUCKET)) {
long currentAmount = fossilCleanerBlockEntity.getFluid().getAmount();
if (16000 - currentAmount >= 1000) {
if (!pPlayer.getAbilities().instabuild) {
pPlayer.setItemInHand(pHand, new ItemStack(Items.BUCKET));
}
// Actual addition is handled in BE tick for simplicity across loaders
return ItemInteractionResult.SUCCESS;
}
}
MenuRegistry.openExtendedMenu((ServerPlayer) pPlayer, fossilCleanerBlockEntity);
}
return ItemInteractionResult.sidedSuccess(pLevel.isClientSide());
}
return ItemInteractionResult.sidedSuccess(pLevel.isClientSide());
}
*///?} else {
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
BlockEntity entity = level.getBlockEntity(pos);
if (entity instanceof FossilCleanerBlockEntity fossilCleanerBlockEntity) {
if (!level.isClientSide()) {
ItemStack stack = player.getItemInHand(hand);
if (stack.is(Items.WATER_BUCKET)) {
long currentAmount = fossilCleanerBlockEntity.getFluid().getAmount();
if (16000 - currentAmount >= 1000) {
if (!player.getAbilities().instabuild) {
player.setItemInHand(hand, new ItemStack(Items.BUCKET));
}
return InteractionResult.SUCCESS;
}
}
MenuRegistry.openExtendedMenu((ServerPlayer) player, fossilCleanerBlockEntity);
}
return InteractionResult.sidedSuccess(level.isClientSide());
}
return InteractionResult.sidedSuccess(level.isClientSide());
}
//?}
@Override
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
if (blockEntityType != ModBlockEntities.FOSSIL_CLEANER_BE.get()) return null;
if (level.isClientSide) {
return null;
} else {
return createTickerHelper(blockEntityType, ModBlockEntities.FOSSIL_CLEANER_BE.get(),
(level1, blockPos, blockState, dnaExtractorBlockEntity) -> dnaExtractorBlockEntity.tick(level1, blockPos, blockState));
}
}
@Override
public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) {
if (!state.getValue(LIT)) {
return;
}
double xPos = (double)pos.getX() + 0.5;
double yPos = pos.getY();
double zPos = (double)pos.getZ() + 0.5;
Direction direction = state.getValue(FACING).getOpposite();
Direction.Axis axis = direction.getAxis();
double defaultOffset = random.nextDouble() * 0.6 - 0.3;
double xOffsets = axis == Direction.Axis.X ? (double)direction.getStepX() * 0.52 : defaultOffset;
double yOffset = random.nextDouble() * 6.0 / 8.0;
double zOffset = axis == Direction.Axis.Z ? (double)direction.getStepZ() * 0.52 : defaultOffset;
level.addParticle(ParticleTypes.SMOKE, xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
BlockEntity be = level.getBlockEntity(pos);
if(be instanceof FossilCleanerBlockEntity fossilCleanerBlockEntity && !fossilCleanerBlockEntity.itemHandler.getItem(1).isEmpty()) {
level.addParticle(new ItemParticleOption(ParticleTypes.ITEM, fossilCleanerBlockEntity.itemHandler.getItem(1)),
xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
}
}
}
@@ -0,0 +1,245 @@
package net.cmr.jurassicrevived.block.custom;
//? if >1.20.1 {
/*import com.mojang.serialization.MapCodec;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.ItemInteractionResult;
*///?}
import dev.architectury.registry.menu.MenuRegistry;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.custom.FossilGrinderBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ItemParticleOption;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
public class FossilGrinderBlock extends BaseEntityBlock {
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public static final BooleanProperty LIT = BlockStateProperties.LIT;
//? if >1.20.1 {
/*public static final MapCodec<FossilGrinderBlock> CODEC = simpleCodec(FossilGrinderBlock::new);
@Override protected MapCodec<? extends BaseEntityBlock> codec() { return CODEC; }
*///?}
public FossilGrinderBlock(Properties properties) {
super(properties);
}
private static final VoxelShape SHAPE_NORTH = Shapes.box(
1.0 / 16.0, 0.0 / 16.0, 1.0 / 16.0,
16.0 / 16.0, 15.0 / 16.0, 16.5 / 16.0
);
private static final VoxelShape SHAPE_SOUTH = rotateShapeY(SHAPE_NORTH, 180);
private static final VoxelShape SHAPE_WEST = rotateShapeY(SHAPE_NORTH, -90);
private static final VoxelShape SHAPE_EAST = rotateShapeY(SHAPE_NORTH, 90);
private static VoxelShape rotateShapeY(VoxelShape shape, int degrees) {
double rad = Math.toRadians(((degrees % 360) + 360) % 360);
int turns = (int) Math.round(rad / (Math.PI / 2));
turns = ((turns % 4) + 4) % 4;
VoxelShape current = shape;
for (int i = 0; i < turns; i++) {
VoxelShape[] buffer = new VoxelShape[]{Shapes.empty()};
current.forAllBoxes((minX, minY, minZ, maxX, maxY, maxZ) -> {
double nMinX = 1.0 - maxZ;
double nMinZ = minX;
double nMaxX = 1.0 - minZ;
double nMaxZ = maxX;
buffer[0] = Shapes.or(buffer[0], Shapes.box(nMinX, minY, nMinZ, nMaxX, maxY, nMaxZ));
});
current = buffer[0];
}
return current;
}
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
}
@Override
public BlockState mirror(BlockState state, Mirror mirror) {
return state.rotate(mirror.getRotation(state.getValue(FACING)));
}
@Override
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite().getOpposite()).setValue(LIT, false);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FACING, LIT);
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
Direction dir = state.getValue(FACING);
return switch (dir) {
case NORTH -> SHAPE_NORTH;
case SOUTH -> SHAPE_SOUTH;
case WEST -> SHAPE_WEST;
case EAST -> SHAPE_EAST;
default -> SHAPE_NORTH;
};
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new FossilGrinderBlockEntity(blockPos, blockState);
}
@Override
public RenderShape getRenderShape(BlockState state) {
return RenderShape.MODEL;
}
@Override
//? if >1.20.1 {
/*public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
*///?} else {
public void playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
//?}
if (!level.isClientSide) {
if (player.getAbilities().instabuild) {
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof FossilGrinderBlockEntity fbe) {
ItemStack stack = new ItemStack(this.asItem());
if (!fbe.isEmptyForDrop()) {
//? if >1.20.1 {
/*CompoundTag tag = fbe.saveWithoutMetadata(level.registryAccess());
var beTypeKey = level.registryAccess().registryOrThrow(Registries.BLOCK_ENTITY_TYPE).getKey(fbe.getType());
if (beTypeKey != null) tag.putString("id", beTypeKey.toString());
stack.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(tag));
*///?} else {
CompoundTag tag = fbe.saveWithoutMetadata();
stack.getOrCreateTagElement("BlockEntityTag").merge(tag);
//?}
}
popResource(level, pos, stack);
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
}
super.playerWillDestroy(level, pos, state, player);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
@Override
public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) {
super.onRemove(pState, pLevel, pPos, pNewState, pIsMoving);
}
//? if >1.20.1 {
/*@Override
protected ItemInteractionResult useItemOn(ItemStack pStack, BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHitResult) {
if (!pLevel.isClientSide()) {
BlockEntity entity = pLevel.getBlockEntity(pPos);
if(entity instanceof FossilGrinderBlockEntity fossilGrinderBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) pPlayer, fossilGrinderBlockEntity);
}
}
return ItemInteractionResult.sidedSuccess(pLevel.isClientSide());
}
*///?} else {
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (!level.isClientSide()) {
BlockEntity entity = level.getBlockEntity(pos);
if (entity instanceof FossilGrinderBlockEntity fossilGrinderBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) player, fossilGrinderBlockEntity);
}
}
return InteractionResult.sidedSuccess(level.isClientSide());
}
//?}
@Override
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
if (blockEntityType != ModBlockEntities.FOSSIL_GRINDER_BE.get()) return null;
if (level.isClientSide) {
return null;
} else {
return createTickerHelper(blockEntityType, ModBlockEntities.FOSSIL_GRINDER_BE.get(),
(level1, blockPos, blockState, fossilGrinderBlockEntity) -> fossilGrinderBlockEntity.tick(level1, blockPos, blockState));
}
}
@Override
public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) {
if (!state.getValue(LIT)) {
return;
}
double xPos = (double)pos.getX() + 0.5;
double yPos = pos.getY();
double zPos = (double)pos.getZ() + 0.5;
Direction direction = state.getValue(FACING).getOpposite();
Direction.Axis axis = direction.getAxis();
double defaultOffset = random.nextDouble() * 0.6 - 0.3;
double xOffsets = axis == Direction.Axis.X ? (double)direction.getStepX() * 0.52 : defaultOffset;
double yOffset = random.nextDouble() * 6.0 / 8.0;
double zOffset = axis == Direction.Axis.Z ? (double)direction.getStepZ() * 0.52 : defaultOffset;
level.addParticle(ParticleTypes.SMOKE, xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
BlockEntity be = level.getBlockEntity(pos);
if(be instanceof FossilGrinderBlockEntity fossilGrinderBlockEntity && !fossilGrinderBlockEntity.itemHandler.getItem(1).isEmpty()) {
level.addParticle(new ItemParticleOption(ParticleTypes.ITEM, fossilGrinderBlockEntity.itemHandler.getItem(1)),
xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
}
}
}
@@ -0,0 +1,238 @@
package net.cmr.jurassicrevived.block.custom;
//? if >1.20.1 {
/*import com.mojang.serialization.MapCodec;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.ItemInteractionResult;
*///?}
import dev.architectury.registry.menu.MenuRegistry;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.custom.GeneratorBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
public class GeneratorBlock extends BaseEntityBlock {
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public static final BooleanProperty LIT = BlockStateProperties.LIT;
//? if >1.20.1 {
/*public static final MapCodec<GeneratorBlock> CODEC = simpleCodec(GeneratorBlock::new);
@Override protected MapCodec<? extends BaseEntityBlock> codec() { return CODEC; }
*///?}
public GeneratorBlock(Properties properties) {
super(properties);
}
private static final VoxelShape SHAPE_NORTH = Shapes.box(
1.0 / 16.0, 0.0 / 16.0, 1.0 / 16.0,
15.0 / 16.0, 20.5 / 16.0, 15.0 / 16.0
);
private static final VoxelShape SHAPE_SOUTH = rotateShapeY(SHAPE_NORTH, 180);
private static final VoxelShape SHAPE_WEST = rotateShapeY(SHAPE_NORTH, 90);
private static final VoxelShape SHAPE_EAST = rotateShapeY(SHAPE_NORTH, 90);
private static VoxelShape rotateShapeY(VoxelShape shape, int degrees) {
double rad = Math.toRadians(((degrees % 360) + 360) % 360);
int turns = (int) Math.round(rad / (Math.PI / 2));
turns = ((turns % 4) + 4) % 4;
VoxelShape current = shape;
for (int i = 0; i < turns; i++) {
VoxelShape[] buffer = new VoxelShape[]{Shapes.empty()};
current.forAllBoxes((minX, minY, minZ, maxX, maxY, maxZ) -> {
double nMinX = 1.0 - maxZ;
double nMinZ = minX;
double nMaxX = 1.0 - minZ;
double nMaxZ = maxX;
buffer[0] = Shapes.or(buffer[0], Shapes.box(nMinX, minY, nMinZ, nMaxX, maxY, nMaxZ));
});
current = buffer[0];
}
return current;
}
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
}
@Override
public BlockState mirror(BlockState state, Mirror mirror) {
return state.rotate(mirror.getRotation(state.getValue(FACING)));
}
@Override
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()).setValue(LIT, false);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FACING, LIT);
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
Direction dir = state.getValue(FACING);
return switch (dir) {
case NORTH -> SHAPE_NORTH;
case SOUTH -> SHAPE_SOUTH;
case WEST -> SHAPE_WEST;
case EAST -> SHAPE_EAST;
default -> SHAPE_NORTH;
};
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new GeneratorBlockEntity(blockPos, blockState);
}
@Override
public RenderShape getRenderShape(BlockState state) {
return RenderShape.MODEL;
}
@Override
//? if >1.20.1 {
/*public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
*///?} else {
public void playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
//?}
if (!level.isClientSide) {
if (player.getAbilities().instabuild) {
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof GeneratorBlockEntity fbe) {
ItemStack stack = new ItemStack(this.asItem());
if (!fbe.isEmptyForDrop()) {
//? if >1.20.1 {
/*CompoundTag tag = fbe.saveWithoutMetadata(level.registryAccess());
var beTypeKey = level.registryAccess().registryOrThrow(Registries.BLOCK_ENTITY_TYPE).getKey(fbe.getType());
if (beTypeKey != null) tag.putString("id", beTypeKey.toString());
stack.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(tag));
*///?} else {
CompoundTag tag = fbe.saveWithoutMetadata();
stack.getOrCreateTagElement("BlockEntityTag").merge(tag);
//?}
}
popResource(level, pos, stack);
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
}
super.playerWillDestroy(level, pos, state, player);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
@Override
public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) {
super.onRemove(pState, pLevel, pPos, pNewState, pIsMoving);
}
//? if >1.20.1 {
/*@Override
protected ItemInteractionResult useItemOn(ItemStack pStack, BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHitResult) {
if (!pLevel.isClientSide()) {
BlockEntity entity = pLevel.getBlockEntity(pPos);
if(entity instanceof GeneratorBlockEntity generatorBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) pPlayer, generatorBlockEntity);
}
}
return ItemInteractionResult.sidedSuccess(pLevel.isClientSide());
}
*///?} else {
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (!level.isClientSide()) {
BlockEntity entity = level.getBlockEntity(pos);
if (entity instanceof GeneratorBlockEntity generatorBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) player, generatorBlockEntity);
}
}
return InteractionResult.sidedSuccess(level.isClientSide());
}
//?}
@Override
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
if (blockEntityType != ModBlockEntities.GENERATOR_BE.get()) return null;
if (level.isClientSide) {
return null;
} else {
return createTickerHelper(blockEntityType, ModBlockEntities.GENERATOR_BE.get(),
(level1, blockPos, blockState, generatorBlockEntity) -> generatorBlockEntity.tick(level1, blockPos, blockState));
}
}
@Override
public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) {
if (!state.getValue(LIT)) {
return;
}
double xPos = (double)pos.getX() + 0.5;
double yPos = pos.getY();
double zPos = (double)pos.getZ() + 0.5;
Direction direction = state.getValue(FACING).getOpposite();
Direction.Axis axis = direction.getAxis();
double defaultOffset = random.nextDouble() * 0.6 - 0.3;
double xOffsets = axis == Direction.Axis.X ? (double)direction.getStepX() * 0.52 : defaultOffset;
double yOffset = random.nextDouble() * 6.0 / 8.0;
double zOffset = axis == Direction.Axis.Z ? (double)direction.getStepZ() * 0.52 : defaultOffset;
level.addParticle(ParticleTypes.SMOKE, xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
}
}
@@ -0,0 +1,128 @@
package net.cmr.jurassicrevived.block.custom;
import net.cmr.jurassicrevived.block.entity.custom.EggBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.AgeableMob;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.TooltipFlag;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
import java.util.List;
import java.util.function.Supplier;
public class IncubatedEggBlock extends Block implements EntityBlock {
private final Supplier<? extends EntityType<? extends Mob>> toSpawn;
private final int hatchSeconds = 60;
public IncubatedEggBlock(Properties pProperties, Supplier<? extends EntityType<? extends Mob>> toSpawn) {
super(pProperties);
this.toSpawn = toSpawn;
}
private static final VoxelShape EGG_SHAPE = Block.box(6.5D, 0.0D, 6.5D, 9.5D, 4.0D, 9.5D);
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return EGG_SHAPE;
}
@Override
public VoxelShape getCollisionShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
return EGG_SHAPE;
}
@Override
public void onPlace(BlockState state, Level level, BlockPos pos, BlockState oldState, boolean isMoving) {
super.onPlace(state, level, pos, oldState, isMoving);
if (!level.isClientSide) {
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof EggBlockEntity eggBE) {
eggBE.resetForNewPlacement(level, hatchSeconds);
}
level.scheduleTick(pos, this, hatchSeconds * 20);
}
}
@Override
public void tick(BlockState state, ServerLevel level, BlockPos pos, RandomSource random) {
BlockEntity be = level.getBlockEntity(pos);
if (!(be instanceof EggBlockEntity eggBE)) return;
if (eggBE.getSecondsRemaining(level) <= 0) {
super.tick(state, level, pos, random);
} else {
level.scheduleTick(pos, this, eggBE.getSecondsRemaining(level) * 20);
return;
}
EntityType<? extends Mob> type = toSpawn.get();
if (type != null) {
Mob mob = type.create(level);
if (mob != null) {
Vec3 spawn = Vec3.atCenterOf(pos);
mob.moveTo(spawn.x, spawn.y + 0.1, spawn.z, level.random.nextFloat() * 360F, 0.0F);
//? if >1.20.1 {
/*mob.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), MobSpawnType.TRIGGERED, null);
*///?} else {
mob.finalizeSpawn(level, level.getCurrentDifficultyAt(pos), MobSpawnType.TRIGGERED, null, null);
//?}
if (mob instanceof AgeableMob ageable) {
ageable.setBaby(true);
}
level.addFreshEntity(mob);
}
}
level.levelEvent(2001, pos, Block.getId(state));
level.removeBlock(pos, false);
}
@Override
public void onRemove(BlockState state, Level level, BlockPos pos, BlockState newState, boolean isMoving) {
if (!level.isClientSide && state.getBlock() != newState.getBlock()) {
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof EggBlockEntity eggBE) {
eggBE.invalidateTimer();
}
}
super.onRemove(state, level, pos, newState, isMoving);
}
@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
return new EggBlockEntity(pos, state);
}
//? if >1.20.1 {
/*@Override
public void appendHoverText(ItemStack pStack, Item.TooltipContext pContext, List<Component> pTootipComponents, TooltipFlag pTooltipFlag) {
pTootipComponents.add(Component.translatable("tooltip.jurassicrevived.egg.hatches_in", hatchSeconds));
super.appendHoverText(pStack, pContext, pTootipComponents, pTooltipFlag);
}
*///?} else {
@Override
public void appendHoverText(ItemStack pStack, @Nullable BlockGetter level, List<Component> pTootipComponents, TooltipFlag pTooltipFlag) {
pTootipComponents.add(Component.translatable("tooltip.jurassicrevived.egg.hatches_in", hatchSeconds));
super.appendHoverText(pStack, level, pTootipComponents, pTooltipFlag);
}
//?}
}
@@ -0,0 +1,213 @@
package net.cmr.jurassicrevived.block.custom;
//? if >1.20.1 {
/*import com.mojang.serialization.MapCodec;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.ItemInteractionResult;
*///?}
import dev.architectury.registry.menu.MenuRegistry;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.custom.IncubatorBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.util.RandomSource;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.*;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.DirectionProperty;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
public class IncubatorBlock extends BaseEntityBlock {
public static final DirectionProperty FACING = BlockStateProperties.HORIZONTAL_FACING;
public static final BooleanProperty LIT = BlockStateProperties.LIT;
//? if >1.20.1 {
/*public static final MapCodec<IncubatorBlock> CODEC = simpleCodec(IncubatorBlock::new);
@Override protected MapCodec<? extends BaseEntityBlock> codec() { return CODEC; }
*///?}
public IncubatorBlock(Properties properties) {
super(properties);
}
private static final VoxelShape SHAPE = Shapes.box(
0.0 / 16.0, 0.0 / 16.0, 0.0 / 16.0,
16.0 / 16.0, 23.0 / 16.0, 16.0 / 16.0
);
private static final VoxelShape SHAPE_LIT = Shapes.box(
0.0 / 16.0, 0.0 / 16.0, 0.0 / 16.0,
16.0 / 16.0, 18.0 / 16.0, 16.0 / 16.0
);
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
}
@Override
public BlockState mirror(BlockState state, Mirror mirror) {
return state.rotate(mirror.getRotation(state.getValue(FACING)));
}
@Override
public @Nullable BlockState getStateForPlacement(BlockPlaceContext context) {
return this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite()).setValue(LIT, false);
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(FACING, LIT);
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
boolean lit = state.getValue(LIT);
return lit ? SHAPE_LIT : SHAPE;
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new IncubatorBlockEntity(blockPos, blockState);
}
@Override
public RenderShape getRenderShape(BlockState state) {
return RenderShape.MODEL;
}
@Override
//? if >1.20.1 {
/*public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
*///?} else {
public void playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
//?}
if (!level.isClientSide) {
if (player.getAbilities().instabuild) {
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof IncubatorBlockEntity fbe) {
ItemStack stack = new ItemStack(this.asItem());
if (!fbe.isEmptyForDrop()) {
//? if >1.20.1 {
/*CompoundTag tag = fbe.saveWithoutMetadata(level.registryAccess());
var beTypeKey = level.registryAccess().registryOrThrow(Registries.BLOCK_ENTITY_TYPE).getKey(fbe.getType());
if (beTypeKey != null) tag.putString("id", beTypeKey.toString());
stack.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(tag));
*///?} else {
CompoundTag tag = fbe.saveWithoutMetadata();
stack.getOrCreateTagElement("BlockEntityTag").merge(tag);
//?}
}
popResource(level, pos, stack);
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
}
super.playerWillDestroy(level, pos, state, player);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
@Override
public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) {
super.onRemove(pState, pLevel, pPos, pNewState, pIsMoving);
}
//? if >1.20.1 {
/*@Override
protected ItemInteractionResult useItemOn(ItemStack pStack, BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHitResult) {
if (!pLevel.isClientSide()) {
BlockEntity entity = pLevel.getBlockEntity(pPos);
if(entity instanceof IncubatorBlockEntity incubatorBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) pPlayer, incubatorBlockEntity);
}
}
return ItemInteractionResult.sidedSuccess(pLevel.isClientSide());
}
*///?} else {
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (!level.isClientSide()) {
BlockEntity entity = level.getBlockEntity(pos);
if (entity instanceof IncubatorBlockEntity incubatorBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) player, incubatorBlockEntity);
}
}
return InteractionResult.sidedSuccess(level.isClientSide());
}
//?}
@Override
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> blockEntityType) {
if (blockEntityType != ModBlockEntities.INCUBATOR_BE.get()) return null;
if (level.isClientSide) {
return null;
} else {
return createTickerHelper(blockEntityType, ModBlockEntities.INCUBATOR_BE.get(),
(level1, blockPos, blockState, incubatorBlockEntity) -> incubatorBlockEntity.tick(level1, blockPos, blockState));
}
}
@Override
public void animateTick(BlockState state, Level level, BlockPos pos, RandomSource random) {
if (!state.getValue(LIT)) {
return;
}
double xPos = (double)pos.getX() + 0.5;
double yPos = pos.getY();
double zPos = (double)pos.getZ() + 0.5;
Direction direction = state.getValue(FACING).getOpposite();
Direction.Axis axis = direction.getAxis();
double defaultOffset = random.nextDouble() * 0.6 - 0.3;
double xOffsets = axis == Direction.Axis.X ? (double)direction.getStepX() * 0.52 : defaultOffset;
double yOffset = random.nextDouble() * 6.0 / 8.0;
double zOffset = axis == Direction.Axis.Z ? (double)direction.getStepZ() * 0.52 : defaultOffset;
level.addParticle(ParticleTypes.SMOKE, xPos + xOffsets, yPos + yOffset, zPos + zOffset, 0.0, 0.0, 0.0);
}
}
@@ -0,0 +1,55 @@
package net.cmr.jurassicrevived.block.custom;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class LightPostBlock extends HorizontalDirectionalBlock {
public static final VoxelShape SHAPE = Block.box(3.5D, 0.0D, 3.5D, 12.5D, 16.0D, 12.5D);
public LightPostBlock(Properties properties) {
super(properties);
this.registerDefaultState((BlockState)((BlockState)((BlockState)this.stateDefinition.any()).setValue(FACING, Direction.SOUTH)));
}
protected MapCodec<? extends HorizontalDirectionalBlock> codec() {
return null;
}
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(new Property[]{FACING});
}
public BlockState mirror(BlockState state, Mirror mirror) {
return state.rotate(mirror.getRotation((Direction)state.getValue(FACING)));
}
// Ensure rotation of FACING property when the block is rotated (e.g., structure placement, commands, etc.)
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
}
public BlockState getStateForPlacement(BlockPlaceContext context) {
// Ensure we always use a horizontal facing (N/E/S/W), even when looking up or down.
return (BlockState)this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite());
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
Direction facing = state.getValue(FACING);
return SHAPE;
}
}
@@ -0,0 +1,365 @@
package net.cmr.jurassicrevived.block.custom;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.custom.PipeBlockEntity;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyUtil;
import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.item.ModItems;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.util.Mth;
import net.minecraft.util.StringRepresentable;
import net.minecraft.world.Container;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
//? if >1.20.1 {
/*import net.minecraft.world.ItemInteractionResult;
*///?}
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.EntityBlock;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.SimpleWaterloggedBlock;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.BlockStateProperties;
import net.minecraft.world.level.block.state.properties.BooleanProperty;
import net.minecraft.world.level.block.state.properties.EnumProperty;
import net.minecraft.world.level.material.FluidState;
import net.minecraft.world.level.material.Fluids;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.Shapes;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class PipeBlock extends Block implements EntityBlock, SimpleWaterloggedBlock {
public enum Transport {
ITEMS, FLUIDS, ENERGY
}
public enum ConnectionType implements StringRepresentable {
NONE, PIPE, CONNECTOR, CONNECTOR_PULL;
@Override
public String getSerializedName() {
return switch (this) {
case NONE -> "none";
case PIPE -> "pipe";
case CONNECTOR -> "connector";
case CONNECTOR_PULL -> "connector_pull";
};
}
}
public static final EnumProperty<ConnectionType> DOWN = EnumProperty.create("down", ConnectionType.class);
public static final EnumProperty<ConnectionType> UP = EnumProperty.create("up", ConnectionType.class);
public static final EnumProperty<ConnectionType> NORTH = EnumProperty.create("north", ConnectionType.class);
public static final EnumProperty<ConnectionType> SOUTH = EnumProperty.create("south", ConnectionType.class);
public static final EnumProperty<ConnectionType> WEST = EnumProperty.create("west", ConnectionType.class);
public static final EnumProperty<ConnectionType> EAST = EnumProperty.create("east", ConnectionType.class);
public static final BooleanProperty WATERLOGGED = BlockStateProperties.WATERLOGGED;
private final Transport transport;
private static final VoxelShape CORE = box(6, 6, 6, 10, 10, 10);
private static final VoxelShape ARM_DOWN = box(6, 0, 6, 10, 6, 10);
private static final VoxelShape ARM_UP = box(6, 10, 6, 10, 16, 10);
private static final VoxelShape ARM_NORTH = box(6, 6, 0, 10, 10, 6);
private static final VoxelShape ARM_SOUTH = box(6, 6, 10, 10, 10, 16);
private static final VoxelShape ARM_WEST = box(0, 6, 6, 6, 10, 10);
private static final VoxelShape ARM_EAST = box(10, 6, 6, 16, 10, 10);
private static final VoxelShape CAP_DOWN = box(6, 0, 6, 10, 1, 10);
private static final VoxelShape CAP_UP = box(6, 15, 6, 10, 16, 10);
private static final VoxelShape CAP_NORTH = box(6, 6, 0, 10, 10, 1);
private static final VoxelShape CAP_SOUTH = box(6, 6, 15, 10, 10, 16);
private static final VoxelShape CAP_WEST = box(0, 6, 6, 1, 10, 10);
private static final VoxelShape CAP_EAST = box(15, 6, 6, 16, 10, 10);
private static final VoxelShape[] SHAPES = new VoxelShape[64];
static {
for (int mask = 0; mask < SHAPES.length; mask++) {
VoxelShape s = CORE;
if ((mask & (1 << 0)) != 0) s = Shapes.or(s, ARM_DOWN, CAP_DOWN);
if ((mask & (1 << 1)) != 0) s = Shapes.or(s, ARM_UP, CAP_UP);
if ((mask & (1 << 2)) != 0) s = Shapes.or(s, ARM_NORTH, CAP_NORTH);
if ((mask & (1 << 3)) != 0) s = Shapes.or(s, ARM_SOUTH, CAP_SOUTH);
if ((mask & (1 << 4)) != 0) s = Shapes.or(s, ARM_WEST, CAP_WEST);
if ((mask & (1 << 5)) != 0) s = Shapes.or(s, ARM_EAST, CAP_EAST);
SHAPES[mask] = s;
}
}
public PipeBlock(Properties properties, Transport transport) {
super(properties.noOcclusion());
this.transport = transport;
this.registerDefaultState(
this.stateDefinition.any()
.setValue(DOWN, ConnectionType.NONE)
.setValue(UP, ConnectionType.NONE)
.setValue(NORTH, ConnectionType.NONE)
.setValue(SOUTH, ConnectionType.NONE)
.setValue(WEST, ConnectionType.NONE)
.setValue(EAST, ConnectionType.NONE)
.setValue(WATERLOGGED, false)
);
}
public Transport getTransport() {
return transport;
}
@Override
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(DOWN, UP, NORTH, SOUTH, WEST, EAST, WATERLOGGED);
}
@Nullable
@Override
public BlockState getStateForPlacement(BlockPlaceContext ctx) {
Level level = ctx.getLevel();
BlockPos pos = ctx.getClickedPos();
BlockState state = this.defaultBlockState()
.setValue(WATERLOGGED, level.getFluidState(pos).getType() == Fluids.WATER);
for (Direction dir : Direction.values()) {
state = setConnectionForDirection(level, pos, state, dir);
}
return state;
}
@Override
@SuppressWarnings("deprecation")
public BlockState updateShape(BlockState state, Direction direction, BlockState neighborState, LevelAccessor level, BlockPos pos, BlockPos neighborPos) {
if (state.getValue(WATERLOGGED)) {
level.scheduleTick(pos, Fluids.WATER, Fluids.WATER.getTickDelay(level));
}
return setConnectionForDirection(level, pos, state, direction);
}
@Override
public FluidState getFluidState(BlockState state) {
return state.getValue(WATERLOGGED) ? Fluids.WATER.getSource(false) : super.getFluidState(state);
}
@Override
public @Nullable BlockEntity newBlockEntity(BlockPos pos, BlockState state) {
return new PipeBlockEntity(pos, state);
}
@Override
public @Nullable <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level level, BlockState state, BlockEntityType<T> type) {
if (level.isClientSide) return null;
return (lvl, pos, st, be) -> {
if (be instanceof PipeBlockEntity pbe) {
PipeBlockEntity.serverTick(lvl, pos, st, pbe);
}
};
}
private BlockState setConnectionForDirection(LevelAccessor level, BlockPos pos, BlockState state, Direction dir) {
EnumProperty<ConnectionType> prop = getProp(dir);
ConnectionType existing = state.getValue(prop);
ConnectionType connection = determineConnection(level, pos, dir, existing);
return state.setValue(prop, connection);
}
private ConnectionType determineConnection(LevelAccessor level, BlockPos pos, Direction dir, ConnectionType existing) {
BlockPos neighborPos = pos.relative(dir);
BlockState neighborState = level.getBlockState(neighborPos);
Block neighbor = neighborState.getBlock();
if (neighbor instanceof PipeBlock otherPipe) {
if (otherPipe.transport == this.transport) {
return ConnectionType.PIPE;
}
}
BlockEntity be = level.getBlockEntity(neighborPos);
boolean hasCommonConnection = false;
if (be != null) {
hasCommonConnection = switch (this.transport) {
case ITEMS -> be instanceof Container;
case FLUIDS -> false;
case ENERGY -> be instanceof ModEnergyUtil.EnergyProvider;
};
}
if (hasCommonConnection) {
if (existing == ConnectionType.CONNECTOR_PULL) {
return ConnectionType.CONNECTOR_PULL;
}
return ConnectionType.CONNECTOR;
}
return ConnectionType.NONE;
}
public static EnumProperty<ConnectionType> getProp(Direction dir) {
return switch (dir) {
case DOWN -> DOWN;
case UP -> UP;
case NORTH -> NORTH;
case SOUTH -> SOUTH;
case WEST -> WEST;
case EAST -> EAST;
};
}
//? if >1.20.1 {
/*@Override
protected ItemInteractionResult useItemOn(ItemStack stack, BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (stack.is(ModItems.WRENCH.get())) {
BlockState newState = toggleConnection(state, hit, pos);
if (newState == null) return ItemInteractionResult.PASS_TO_DEFAULT_BLOCK_INTERACTION;
if (!level.isClientSide) {
level.setBlock(pos, newState, Block.UPDATE_ALL);
level.sendBlockUpdated(pos, state, newState, Block.UPDATE_ALL);
}
return ItemInteractionResult.sidedSuccess(level.isClientSide);
}
return super.useItemOn(stack, state, level, pos, player, hand, hit);
}
*///?} else {
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
ItemStack stack = player.getItemInHand(hand);
if (stack.is(ModItems.WRENCH.get())) {
BlockState newState = toggleConnection(state, hit, pos);
if (newState == null) return InteractionResult.PASS;
if (!level.isClientSide) {
level.setBlock(pos, newState, Block.UPDATE_ALL);
level.sendBlockUpdated(pos, state, newState, Block.UPDATE_ALL);
}
return InteractionResult.sidedSuccess(level.isClientSide);
}
return super.use(state, level, pos, player, hand, hit);
}
//?}
private @Nullable BlockState toggleConnection(BlockState state, BlockHitResult hit, BlockPos pos) {
Direction target = getInteractionFace(state, hit, pos);
if (target == null) return null;
EnumProperty<ConnectionType> prop = getProp(target);
ConnectionType val = state.getValue(prop);
if (val == ConnectionType.CONNECTOR) {
return state.setValue(prop, ConnectionType.CONNECTOR_PULL);
} else if (val == ConnectionType.CONNECTOR_PULL) {
return state.setValue(prop, ConnectionType.CONNECTOR);
} else {
Direction nearest = nearestConnectedDirection(state, hit, pos);
if (nearest == null) return null;
prop = getProp(nearest);
val = state.getValue(prop);
if (val == ConnectionType.CONNECTOR) {
return state.setValue(prop, ConnectionType.CONNECTOR_PULL);
} else if (val == ConnectionType.CONNECTOR_PULL) {
return state.setValue(prop, ConnectionType.CONNECTOR);
}
}
return null;
}
private Direction getInteractionFace(BlockState state, BlockHitResult hit, BlockPos pos) {
Vec3 local = hit.getLocation().subtract(pos.getX(), pos.getY(), pos.getZ());
double x = Mth.clamp(local.x * 16.0, 0.0, 16.0);
double y = Mth.clamp(local.y * 16.0, 0.0, 16.0);
double z = Mth.clamp(local.z * 16.0, 0.0, 16.0);
if (intersects(CAP_UP, x, y, z) || intersects(ARM_UP, x, y, z)) { if (state.getValue(UP) != ConnectionType.NONE) return Direction.UP; }
if (intersects(CAP_DOWN, x, y, z) || intersects(ARM_DOWN, x, y, z)) { if (state.getValue(DOWN) != ConnectionType.NONE) return Direction.DOWN; }
if (intersects(CAP_NORTH, x, y, z) || intersects(ARM_NORTH, x, y, z)) { if (state.getValue(NORTH) != ConnectionType.NONE) return Direction.NORTH; }
if (intersects(CAP_SOUTH, x, y, z) || intersects(ARM_SOUTH, x, y, z)) { if (state.getValue(SOUTH) != ConnectionType.NONE) return Direction.SOUTH; }
if (intersects(CAP_WEST, x, y, z) || intersects(ARM_WEST, x, y, z)) { if (state.getValue(WEST) != ConnectionType.NONE) return Direction.WEST; }
if (intersects(CAP_EAST, x, y, z) || intersects(ARM_EAST, x, y, z)) { if (state.getValue(EAST) != ConnectionType.NONE) return Direction.EAST; }
return nearestConnectedDirection(state, x, y, z);
}
private static Direction nearestConnectedDirection(BlockState state, BlockHitResult hit, BlockPos pos) {
Vec3 local = hit.getLocation().subtract(pos.getX(), pos.getY(), pos.getZ());
double x = Mth.clamp(local.x * 16.0, 0.0, 16.0);
double y = Mth.clamp(local.y * 16.0, 0.0, 16.0);
double z = Mth.clamp(local.z * 16.0, 0.0, 16.0);
return nearestConnectedDirection(state, x, y, z);
}
private static Direction nearestConnectedDirection(BlockState state, double x, double y, double z) {
Direction best = null;
double bestScore = Double.NEGATIVE_INFINITY;
for (Direction d : Direction.values()) {
ConnectionType t = state.getValue(getProp(d));
if (t == ConnectionType.NONE) continue;
double score = switch (d) {
case UP -> y;
case DOWN -> 16 - y;
case NORTH -> 16 - z;
case SOUTH -> z;
case WEST -> 16 - x;
case EAST -> x;
};
if (score > bestScore) {
bestScore = score;
best = d;
}
}
return best;
}
private static boolean intersects(VoxelShape shape, double x, double y, double z) {
var it = shape.toAabbs().iterator();
if (!it.hasNext()) return false;
var bb = it.next();
return x >= bb.minX && x <= bb.maxX &&
y >= bb.minY && y <= bb.maxY &&
z >= bb.minZ && z <= bb.maxZ;
}
@Override
public RenderShape getRenderShape(BlockState state) {
return RenderShape.MODEL;
}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext ctx) {
int mask = 0;
if (state.getValue(DOWN) != ConnectionType.NONE) mask |= (1 << 0);
if (state.getValue(UP) != ConnectionType.NONE) mask |= (1 << 1);
if (state.getValue(NORTH) != ConnectionType.NONE) mask |= (1 << 2);
if (state.getValue(SOUTH) != ConnectionType.NONE) mask |= (1 << 3);
if (state.getValue(WEST) != ConnectionType.NONE) mask |= (1 << 4);
if (state.getValue(EAST) != ConnectionType.NONE) mask |= (1 << 5);
return SHAPES[mask];
}
public int getMaxItemsPerTick() {
return Math.max(0, JRConfigManager.get().itemsPerSecond / 20);
}
public int getMaxFluidPerTick() {
return Math.max(0, JRConfigManager.get().milliBucketsPerSecond / 20);
}
public int getMaxEnergyPerTick() {
return Math.max(0, JRConfigManager.get().fePerSecond / 20);
}
}
@@ -0,0 +1,147 @@
package net.cmr.jurassicrevived.block.custom;
//? if >1.20.1 {
/*import com.mojang.serialization.MapCodec;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.ItemInteractionResult;
*///?}
import dev.architectury.registry.menu.MenuRegistry;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.custom.PowerCellBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BaseEntityBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
public class PowerCellBlock extends BaseEntityBlock {
public PowerCellBlock(Properties properties) {
super(properties);
}
public static final VoxelShape SHAPE = Block.box(2.0D, 0.0D, 2.0D, 14.0D, 15.0D, 14.0D);
//? if >1.20.1 {
/*public static final MapCodec<PowerCellBlock> CODEC = simpleCodec(PowerCellBlock::new);
@Override protected MapCodec<? extends BaseEntityBlock> codec() { return CODEC; }
*///?}
@Override
public RenderShape getRenderShape(BlockState pState) {
return RenderShape.MODEL;
}
@Override
//? if >1.20.1 {
/*public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
*///?} else {
public void playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
//?}
if (!level.isClientSide) {
if (player.getAbilities().instabuild) {
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof PowerCellBlockEntity fbe) {
ItemStack stack = new ItemStack(this.asItem());
if (!fbe.isEmptyForDrop()) {
//? if >1.20.1 {
/*CompoundTag tag = fbe.saveWithoutMetadata(level.registryAccess());
var beTypeKey = level.registryAccess().registryOrThrow(Registries.BLOCK_ENTITY_TYPE).getKey(fbe.getType());
if (beTypeKey != null) tag.putString("id", beTypeKey.toString());
stack.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(tag));
*///?} else {
CompoundTag tag = fbe.saveWithoutMetadata();
stack.getOrCreateTagElement("BlockEntityTag").merge(tag);
//?}
}
popResource(level, pos, stack);
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
}
super.playerWillDestroy(level, pos, state, player);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
@Override
public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) {
super.onRemove(pState, pLevel, pPos, pNewState, pIsMoving);
}
//? if >1.20.1 {
/*@Override
protected ItemInteractionResult useItemOn(ItemStack pStack, BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHitResult) {
if (!pLevel.isClientSide()) {
BlockEntity entity = pLevel.getBlockEntity(pPos);
if (entity instanceof PowerCellBlockEntity powerCellBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) pPlayer, powerCellBlockEntity);
}
}
return ItemInteractionResult.sidedSuccess(pLevel.isClientSide());
}
*///?} else {
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (!level.isClientSide()) {
BlockEntity entity = level.getBlockEntity(pos);
if (entity instanceof PowerCellBlockEntity powerCellBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) player, powerCellBlockEntity);
}
}
return InteractionResult.sidedSuccess(level.isClientSide());
}
//?}
@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new PowerCellBlockEntity(blockPos, blockState);
}
@Nullable
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level pLevel, BlockState pState, BlockEntityType<T> pBlockEntityType) {
if (pLevel.isClientSide()) {
return null;
}
return createTickerHelper(pBlockEntityType, ModBlockEntities.POWER_CELL_BE.get(),
((level, blockPos, blockState, powerCellBlockEntity) -> powerCellBlockEntity.tick(level, blockPos, blockState)));
}
}
@@ -0,0 +1,39 @@
package net.cmr.jurassicrevived.block.custom;
import com.mojang.serialization.MapCodec;
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.DirectionalBlock;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.Property;
public class RotatableBlock extends DirectionalBlock {
public RotatableBlock(Properties properties) {
super(properties);
this.registerDefaultState((BlockState)((BlockState)((BlockState)this.stateDefinition.any()).setValue(FACING, Direction.SOUTH)));
}
protected MapCodec<? extends DirectionalBlock> codec() {
return null;
}
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(new Property[]{FACING});
}
public BlockState rotate(BlockState state, Rotation rot) {
return (BlockState)state.setValue(FACING, rot.rotate((Direction)state.getValue(FACING)));
}
public BlockState mirror(BlockState state, Mirror mirror) {
return state.rotate(mirror.getRotation((Direction)state.getValue(FACING)));
}
public BlockState getStateForPlacement(BlockPlaceContext context) {
return (BlockState)this.defaultBlockState().setValue(FACING, context.getNearestLookingDirection().getOpposite().getOpposite());
}
}
@@ -0,0 +1,146 @@
package net.cmr.jurassicrevived.block.custom;
//? if >1.20.1 {
/*import com.mojang.serialization.MapCodec;
import net.minecraft.core.component.DataComponents;
import net.minecraft.world.item.component.CustomData;
import net.minecraft.world.ItemInteractionResult;
*///?}
import dev.architectury.registry.menu.MenuRegistry;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.custom.TankBlockEntity;
import net.minecraft.core.BlockPos;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.BaseEntityBlock;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.RenderShape;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.entity.BlockEntityTicker;
import net.minecraft.world.level.block.entity.BlockEntityType;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.VoxelShape;
import org.jetbrains.annotations.Nullable;
public class TankBlock extends BaseEntityBlock {
public TankBlock(Properties properties) {
super(properties);
}
public static final VoxelShape SHAPE = Block.box(1.0D, 0.0D, 1.0D, 15.0D, 15.0D, 15.0D);
//? if >1.20.1 {
/*@Override protected MapCodec<? extends BaseEntityBlock> codec() { return null; }
*///?}
@Override
public RenderShape getRenderShape(BlockState pState) {
return RenderShape.MODEL;
}
@Override
//? if >1.20.1 {
/*public BlockState playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
*///?} else {
public void playerWillDestroy(Level level, BlockPos pos, BlockState state, Player player) {
//?}
if (!level.isClientSide) {
if (player.getAbilities().instabuild) {
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
BlockEntity be = level.getBlockEntity(pos);
if (be instanceof TankBlockEntity fbe) {
ItemStack stack = new ItemStack(this.asItem());
if (!fbe.isEmptyForDrop()) {
//? if >1.20.1 {
/*CompoundTag tag = fbe.saveWithoutMetadata(level.registryAccess());
var beTypeKey = level.registryAccess().registryOrThrow(Registries.BLOCK_ENTITY_TYPE).getKey(fbe.getType());
if (beTypeKey != null) tag.putString("id", beTypeKey.toString());
stack.set(DataComponents.BLOCK_ENTITY_DATA, CustomData.of(tag));
*///?} else {
CompoundTag tag = fbe.saveWithoutMetadata();
stack.getOrCreateTagElement("BlockEntityTag").merge(tag);
//?}
}
popResource(level, pos, stack);
level.removeBlockEntity(pos);
level.setBlock(pos, Blocks.AIR.defaultBlockState(), Block.UPDATE_ALL);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
}
super.playerWillDestroy(level, pos, state, player);
//? if >1.20.1 {
/*return state;
*///?} else {
return;
//?}
}
@Override
public void onRemove(BlockState pState, Level pLevel, BlockPos pPos, BlockState pNewState, boolean pIsMoving) {
super.onRemove(pState, pLevel, pPos, pNewState, pIsMoving);
}
//? if >1.20.1 {
/*@Override
protected ItemInteractionResult useItemOn(ItemStack pStack, BlockState pState, Level pLevel, BlockPos pPos, Player pPlayer, InteractionHand pHand, BlockHitResult pHitResult) {
if (!pLevel.isClientSide()) {
BlockEntity entity = pLevel.getBlockEntity(pPos);
if (entity instanceof TankBlockEntity tankBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) pPlayer, tankBlockEntity);
}
}
return ItemInteractionResult.sidedSuccess(pLevel.isClientSide());
}
*///?} else {
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (!level.isClientSide()) {
BlockEntity entity = level.getBlockEntity(pos);
if (entity instanceof TankBlockEntity tankBlockEntity) {
MenuRegistry.openExtendedMenu((ServerPlayer) player, tankBlockEntity);
}
}
return InteractionResult.sidedSuccess(level.isClientSide());
}
//?}
@Nullable
@Override
public BlockEntity newBlockEntity(BlockPos blockPos, BlockState blockState) {
return new TankBlockEntity(blockPos, blockState);
}
@Nullable
@Override
public <T extends BlockEntity> BlockEntityTicker<T> getTicker(Level pLevel, BlockState pState, BlockEntityType<T> pBlockEntityType) {
if(pLevel.isClientSide()) {
return null;
}
return createTickerHelper(pBlockEntityType, ModBlockEntities.TANK_BE.get(),
((level, blockPos, blockState, tankBlockEntity) -> tankBlockEntity.tick(level, blockPos, blockState)));
}
}
@@ -0,0 +1,95 @@
package net.cmr.jurassicrevived.block.custom;
import com.mojang.serialization.MapCodec;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.context.BlockPlaceContext;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.HorizontalDirectionalBlock;
import net.minecraft.world.level.block.Mirror;
import net.minecraft.world.level.block.Rotation;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.block.state.StateDefinition;
import net.minecraft.world.level.block.state.properties.Property;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.shapes.CollisionContext;
import net.minecraft.world.phys.shapes.VoxelShape;
public class TrashBlock extends HorizontalDirectionalBlock {
public static final VoxelShape SHAPE = Block.box(4.0D, 0.0D, 4.0D, 12.0D, 16.0D, 12.0D);
public TrashBlock(Properties properties) {
super(properties);
this.registerDefaultState((BlockState)((BlockState)((BlockState)this.stateDefinition.any()).setValue(FACING, Direction.SOUTH)));
}
protected MapCodec<? extends HorizontalDirectionalBlock> codec() {
return null;
}
protected void createBlockStateDefinition(StateDefinition.Builder<Block, BlockState> builder) {
builder.add(new Property[]{FACING});
}
public BlockState mirror(BlockState state, Mirror mirror) {
return state.rotate(mirror.getRotation((Direction)state.getValue(FACING)));
}
// Ensure rotation of FACING property when the block is rotated (e.g., structure placement, commands, etc.)
@Override
public BlockState rotate(BlockState state, Rotation rotation) {
return state.setValue(FACING, rotation.rotate(state.getValue(FACING)));
}
public BlockState getStateForPlacement(BlockPlaceContext context) {
// Ensure we always use a horizontal facing (N/E/S/W), even when looking up or down.
return (BlockState)this.defaultBlockState().setValue(FACING, context.getHorizontalDirection().getOpposite());
}
//? if >1.20.1 {
/*@Override
protected InteractionResult useWithoutItem(BlockState state, Level level, BlockPos pos,
Player player, BlockHitResult hit) {
// Called when player right-clicks the block without using an item on it
if (level.isClientSide) return InteractionResult.SUCCESS;
InteractionHand hand = hit.isInside() ? InteractionHand.MAIN_HAND : player.getUsedItemHand(); // fallback
if (hand == null) hand = InteractionHand.MAIN_HAND;
ItemStack stack = player.getItemInHand(hand);
if (!player.isCreative() && !stack.isEmpty()) {
player.setItemInHand(hand, ItemStack.EMPTY);
player.inventoryMenu.broadcastChanges();
}
return InteractionResult.CONSUME;
}
*///?} else {
@Override
public InteractionResult use(BlockState state, Level level, BlockPos pos, Player player, InteractionHand hand, BlockHitResult hit) {
if (level.isClientSide) {
return InteractionResult.SUCCESS;
}
ItemStack stack = player.getItemInHand(hand);
if (!player.isCreative() && !stack.isEmpty()) {
player.setItemInHand(hand, ItemStack.EMPTY);
player.inventoryMenu.broadcastChanges();
}
return InteractionResult.CONSUME;
}
//?}
@Override
public VoxelShape getShape(BlockState state, BlockGetter level, BlockPos pos, CollisionContext context) {
Direction facing = state.getValue(FACING);
return SHAPE;
}
}
@@ -0,0 +1,233 @@
package net.cmr.jurassicrevived.block.entity;
import dev.architectury.registry.registries.DeferredRegister;
import dev.architectury.registry.registries.RegistrySupplier;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.block.ModBlocks;
import net.cmr.jurassicrevived.block.entity.custom.*;
import net.minecraft.core.registries.Registries;
import net.minecraft.world.level.block.entity.BlockEntityType;
public class ModBlockEntities {
public static final DeferredRegister<BlockEntityType<?>> BLOCK_ENTITIES =
DeferredRegister.create(Constants.MOD_ID, Registries.BLOCK_ENTITY_TYPE);
public static final RegistrySupplier<BlockEntityType<CrateBlockEntity>> CRATE_BE =
BLOCK_ENTITIES.register("crate", () ->
BlockEntityType.Builder.of(
(pos, state) -> new CrateBlockEntity(pos, state, 9),
ModBlocks.WOOD_CRATE.get(), ModBlocks.IRON_CRATE.get()
).build(null)
);
public static final RegistrySupplier<BlockEntityType<TankBlockEntity>> TANK_BE =
BLOCK_ENTITIES.register("tank_be", () -> BlockEntityType.Builder.of(
TankBlockEntity::new, ModBlocks.TANK.get()).build(null));
public static final RegistrySupplier<BlockEntityType<PowerCellBlockEntity>> POWER_CELL_BE =
BLOCK_ENTITIES.register("power_cell_be", () -> BlockEntityType.Builder.of(
PowerCellBlockEntity::new, ModBlocks.POWER_CELL.get()).build(null));
public static final RegistrySupplier<BlockEntityType<EggBlockEntity>> EGG_BE =
BLOCK_ENTITIES.register("egg_be", () ->
BlockEntityType.Builder.of(EggBlockEntity::new,
ModBlocks.APATOSAURUS_EGG.get(),
ModBlocks.ALBERTOSAURUS_EGG.get(),
ModBlocks.VELOCIRAPTOR_EGG.get(),
ModBlocks.TYRANNOSAURUS_REX_EGG.get(),
ModBlocks.TRICERATOPS_EGG.get(),
ModBlocks.SPINOSAURUS_EGG.get(),
ModBlocks.PARASAUROLOPHUS_EGG.get(),
ModBlocks.INDOMINUS_REX_EGG.get(),
ModBlocks.GALLIMIMUS_EGG.get(),
ModBlocks.DIPLODOCUS_EGG.get(),
ModBlocks.OURANOSAURUS_EGG.get(),
ModBlocks.DILOPHOSAURUS_EGG.get(),
ModBlocks.COMPSOGNATHUS_EGG.get(),
ModBlocks.CERATOSAURUS_EGG.get(),
ModBlocks.BRACHIOSAURUS_EGG.get(),
ModBlocks.BARYONYX_EGG.get(),
ModBlocks.CARNOTAURUS_EGG.get(),
ModBlocks.CONCAVENATOR_EGG.get(),
ModBlocks.DEINONYCHUS_EGG.get(),
ModBlocks.EDMONTOSAURUS_EGG.get(),
ModBlocks.GIGANOTOSAURUS_EGG.get(),
ModBlocks.GUANLONG_EGG.get(),
ModBlocks.HERRERASAURUS_EGG.get(),
ModBlocks.MAJUNGASAURUS_EGG.get(),
ModBlocks.PROCOMPSOGNATHUS_EGG.get(),
ModBlocks.PROTOCERATOPS_EGG.get(),
ModBlocks.RUGOPS_EGG.get(),
ModBlocks.SHANTUNGOSAURUS_EGG.get(),
ModBlocks.STEGOSAURUS_EGG.get(),
ModBlocks.STYRACOSAURUS_EGG.get(),
ModBlocks.THERIZINOSAURUS_EGG.get(),
ModBlocks.DISTORTUS_REX_EGG.get(),
ModBlocks.ALLOSAURUS_EGG.get(),
ModBlocks.ALVAREZSAURUS_EGG.get(),
ModBlocks.ANKYLOSAURUS_EGG.get(),
ModBlocks.ARAMBOURGIANIA_EGG.get(),
ModBlocks.CARCHARODONTOSAURUS_EGG.get(),
ModBlocks.CEARADACTYLUS_EGG.get(),
ModBlocks.CHASMOSAURUS_EGG.get(),
ModBlocks.COELOPHYSIS_EGG.get(),
ModBlocks.COELURUS_EGG.get(),
ModBlocks.CORYTHOSAURUS_EGG.get(),
ModBlocks.DIMORPHODON_EGG.get(),
ModBlocks.DRYOSAURUS_EGG.get(),
ModBlocks.GEOSTERNBERGIA_EGG.get(),
ModBlocks.GUIDRACO_EGG.get(),
ModBlocks.HADROSAURUS_EGG.get(),
ModBlocks.HYPSILOPHODON_EGG.get(),
ModBlocks.INDORAPTOR_EGG.get(),
ModBlocks.INOSTRANCEVIA_EGG.get(),
ModBlocks.LAMBEOSAURUS_EGG.get(),
ModBlocks.LUDODACTYLUS_EGG.get(),
ModBlocks.MAMENCHISAURUS_EGG.get(),
ModBlocks.METRIACANTHOSAURUS_EGG.get(),
ModBlocks.MOGANOPTERUS_EGG.get(),
ModBlocks.NYCTOSAURUS_EGG.get(),
ModBlocks.ORNITHOLESTES_EGG.get(),
ModBlocks.ORNITHOMIMUS_EGG.get(),
ModBlocks.OVIRAPTOR_EGG.get(),
ModBlocks.PACHYCEPHALOSAURUS_EGG.get(),
ModBlocks.PROCERATOSAURUS_EGG.get(),
ModBlocks.PTERANODON_EGG.get(),
ModBlocks.PTERODAUSTRO_EGG.get(),
ModBlocks.QUETZALCOATLUS_EGG.get(),
ModBlocks.RAJASAURUS_EGG.get(),
ModBlocks.SEGISAURUS_EGG.get(),
ModBlocks.TAPEJARA_EGG.get(),
ModBlocks.TITANOSAURUS_EGG.get(),
ModBlocks.TROODON_EGG.get(),
ModBlocks.TROPEOGNATHUS_EGG.get(),
ModBlocks.TUPUXUARA_EGG.get(),
ModBlocks.UTAHRAPTOR_EGG.get(),
ModBlocks.ZHENYUANOPTERUS_EGG.get(),
ModBlocks.INCUBATED_APATOSAURUS_EGG.get(),
ModBlocks.INCUBATED_ALBERTOSAURUS_EGG.get(),
ModBlocks.INCUBATED_VELOCIRAPTOR_EGG.get(),
ModBlocks.INCUBATED_TYRANNOSAURUS_REX_EGG.get(),
ModBlocks.INCUBATED_TRICERATOPS_EGG.get(),
ModBlocks.INCUBATED_SPINOSAURUS_EGG.get(),
ModBlocks.INCUBATED_PARASAUROLOPHUS_EGG.get(),
ModBlocks.INCUBATED_INDOMINUS_REX_EGG.get(),
ModBlocks.INCUBATED_GALLIMIMUS_EGG.get(),
ModBlocks.INCUBATED_DIPLODOCUS_EGG.get(),
ModBlocks.INCUBATED_OURANOSAURUS_EGG.get(),
ModBlocks.INCUBATED_DILOPHOSAURUS_EGG.get(),
ModBlocks.INCUBATED_COMPSOGNATHUS_EGG.get(),
ModBlocks.INCUBATED_CERATOSAURUS_EGG.get(),
ModBlocks.INCUBATED_BRACHIOSAURUS_EGG.get(),
ModBlocks.INCUBATED_BARYONYX_EGG.get(),
ModBlocks.INCUBATED_CARNOTAURUS_EGG.get(),
ModBlocks.INCUBATED_CONCAVENATOR_EGG.get(),
ModBlocks.INCUBATED_DEINONYCHUS_EGG.get(),
ModBlocks.INCUBATED_EDMONTOSAURUS_EGG.get(),
ModBlocks.INCUBATED_GIGANOTOSAURUS_EGG.get(),
ModBlocks.INCUBATED_GUANLONG_EGG.get(),
ModBlocks.INCUBATED_HERRERASAURUS_EGG.get(),
ModBlocks.INCUBATED_MAJUNGASAURUS_EGG.get(),
ModBlocks.INCUBATED_PROCOMPSOGNATHUS_EGG.get(),
ModBlocks.INCUBATED_PROTOCERATOPS_EGG.get(),
ModBlocks.INCUBATED_RUGOPS_EGG.get(),
ModBlocks.INCUBATED_SHANTUNGOSAURUS_EGG.get(),
ModBlocks.INCUBATED_STEGOSAURUS_EGG.get(),
ModBlocks.INCUBATED_STYRACOSAURUS_EGG.get(),
ModBlocks.INCUBATED_THERIZINOSAURUS_EGG.get(),
ModBlocks.INCUBATED_DISTORTUS_REX_EGG.get(),
ModBlocks.INCUBATED_ALLOSAURUS_EGG.get(),
ModBlocks.INCUBATED_ALVAREZSAURUS_EGG.get(),
ModBlocks.INCUBATED_ANKYLOSAURUS_EGG.get(),
ModBlocks.INCUBATED_ARAMBOURGIANIA_EGG.get(),
ModBlocks.INCUBATED_CARCHARODONTOSAURUS_EGG.get(),
ModBlocks.INCUBATED_CEARADACTYLUS_EGG.get(),
ModBlocks.INCUBATED_CHASMOSAURUS_EGG.get(),
ModBlocks.INCUBATED_COELOPHYSIS_EGG.get(),
ModBlocks.INCUBATED_COELURUS_EGG.get(),
ModBlocks.INCUBATED_CORYTHOSAURUS_EGG.get(),
ModBlocks.INCUBATED_DIMORPHODON_EGG.get(),
ModBlocks.INCUBATED_DRYOSAURUS_EGG.get(),
ModBlocks.INCUBATED_GEOSTERNBERGIA_EGG.get(),
ModBlocks.INCUBATED_GUIDRACO_EGG.get(),
ModBlocks.INCUBATED_HADROSAURUS_EGG.get(),
ModBlocks.INCUBATED_HYPSILOPHODON_EGG.get(),
ModBlocks.INCUBATED_INDORAPTOR_EGG.get(),
ModBlocks.INCUBATED_INOSTRANCEVIA_EGG.get(),
ModBlocks.INCUBATED_LAMBEOSAURUS_EGG.get(),
ModBlocks.INCUBATED_LUDODACTYLUS_EGG.get(),
ModBlocks.INCUBATED_MAMENCHISAURUS_EGG.get(),
ModBlocks.INCUBATED_METRIACANTHOSAURUS_EGG.get(),
ModBlocks.INCUBATED_MOGANOPTERUS_EGG.get(),
ModBlocks.INCUBATED_NYCTOSAURUS_EGG.get(),
ModBlocks.INCUBATED_ORNITHOLESTES_EGG.get(),
ModBlocks.INCUBATED_ORNITHOMIMUS_EGG.get(),
ModBlocks.INCUBATED_OVIRAPTOR_EGG.get(),
ModBlocks.INCUBATED_PACHYCEPHALOSAURUS_EGG.get(),
ModBlocks.INCUBATED_PROCERATOSAURUS_EGG.get(),
ModBlocks.INCUBATED_PTERANODON_EGG.get(),
ModBlocks.INCUBATED_PTERODAUSTRO_EGG.get(),
ModBlocks.INCUBATED_QUETZALCOATLUS_EGG.get(),
ModBlocks.INCUBATED_RAJASAURUS_EGG.get(),
ModBlocks.INCUBATED_SEGISAURUS_EGG.get(),
ModBlocks.INCUBATED_TAPEJARA_EGG.get(),
ModBlocks.INCUBATED_TITANOSAURUS_EGG.get(),
ModBlocks.INCUBATED_TROODON_EGG.get(),
ModBlocks.INCUBATED_TROPEOGNATHUS_EGG.get(),
ModBlocks.INCUBATED_TUPUXUARA_EGG.get(),
ModBlocks.INCUBATED_UTAHRAPTOR_EGG.get(),
ModBlocks.INCUBATED_ZHENYUANOPTERUS_EGG.get()
).build(null));
public static final RegistrySupplier<BlockEntityType<DNAExtractorBlockEntity>> DNA_EXTRACTOR_BE =
BLOCK_ENTITIES.register("dna_extractor_be", () -> BlockEntityType.Builder.of(
DNAExtractorBlockEntity::new, ModBlocks.DNA_EXTRACTOR.get(), ModBlocks.WHITE_DNA_EXTRACTOR.get()).build(null));
public static final RegistrySupplier<BlockEntityType<DNAAnalyzerBlockEntity>> DNA_ANALYZER_BE =
BLOCK_ENTITIES.register("dna_analyzer_be", () -> BlockEntityType.Builder.of(
DNAAnalyzerBlockEntity::new, ModBlocks.DNA_ANALYZER.get(), ModBlocks.WHITE_DNA_ANALYZER.get()).build(null));
public static final RegistrySupplier<BlockEntityType<FossilGrinderBlockEntity>> FOSSIL_GRINDER_BE =
BLOCK_ENTITIES.register("fossil_grinder_be", () -> BlockEntityType.Builder.of(
FossilGrinderBlockEntity::new, ModBlocks.FOSSIL_GRINDER.get(), ModBlocks.WHITE_FOSSIL_GRINDER.get()).build(null));
public static final RegistrySupplier<BlockEntityType<FossilCleanerBlockEntity>> FOSSIL_CLEANER_BE =
BLOCK_ENTITIES.register("fossil_cleaner_be", () -> BlockEntityType.Builder.of(
FossilCleanerBlockEntity::new, ModBlocks.FOSSIL_CLEANER.get(), ModBlocks.WHITE_FOSSIL_CLEANER.get()).build(null));
public static final RegistrySupplier<BlockEntityType<PipeBlockEntity>> ITEM_PIPE_BE =
BLOCK_ENTITIES.register("item_pipe_be", () -> BlockEntityType.Builder.of(
PipeBlockEntity::new, ModBlocks.ITEM_PIPE.get()).build(null));
public static final RegistrySupplier<BlockEntityType<PipeBlockEntity>> FLUID_PIPE_BE =
BLOCK_ENTITIES.register("fluid_pipe_be", () -> BlockEntityType.Builder.of(
PipeBlockEntity::new, ModBlocks.FLUID_PIPE.get()).build(null));
public static final RegistrySupplier<BlockEntityType<PipeBlockEntity>> POWER_PIPE_BE =
BLOCK_ENTITIES.register("power_pipe_be", () -> BlockEntityType.Builder.of(
PipeBlockEntity::new, ModBlocks.POWER_PIPE.get()).build(null));
public static final RegistrySupplier<BlockEntityType<GeneratorBlockEntity>> GENERATOR_BE =
BLOCK_ENTITIES.register("generator_be", () -> BlockEntityType.Builder.of(
GeneratorBlockEntity::new, ModBlocks.GENERATOR.get(), ModBlocks.WHITE_GENERATOR.get()).build(null));
public static final RegistrySupplier<BlockEntityType<DNAHybridizerBlockEntity>> DNA_HYBRIDIZER_BE =
BLOCK_ENTITIES.register("dna_hybridizer_be", () -> BlockEntityType.Builder.of(
DNAHybridizerBlockEntity::new, ModBlocks.DNA_HYBRIDIZER.get(), ModBlocks.WHITE_DNA_HYBRIDIZER.get()).build(null));
public static final RegistrySupplier<BlockEntityType<EmbryonicMachineBlockEntity>> EMBRYONIC_MACHINE_BE =
BLOCK_ENTITIES.register("embryonic_machine_be", () -> BlockEntityType.Builder.of(
EmbryonicMachineBlockEntity::new, ModBlocks.EMBRYONIC_MACHINE.get(), ModBlocks.WHITE_EMBRYONIC_MACHINE.get()).build(null));
public static final RegistrySupplier<BlockEntityType<EmbryoCalcificationMachineBlockEntity>> EMBRYO_CALCIFICATION_MACHINE_BE =
BLOCK_ENTITIES.register("embryo_calcification_machine_be", () -> BlockEntityType.Builder.of(
EmbryoCalcificationMachineBlockEntity::new, ModBlocks.EMBRYO_CALCIFICATION_MACHINE.get(), ModBlocks.WHITE_EMBRYO_CALCIFICATION_MACHINE.get()).build(null));
public static final RegistrySupplier<BlockEntityType<IncubatorBlockEntity>> INCUBATOR_BE =
BLOCK_ENTITIES.register("incubator_be", () -> BlockEntityType.Builder.of(
IncubatorBlockEntity::new, ModBlocks.INCUBATOR.get(), ModBlocks.WHITE_INCUBATOR.get()).build(null));
public static void register() {
BLOCK_ENTITIES.register();
}
}
@@ -0,0 +1,107 @@
package net.cmr.jurassicrevived.block.entity.custom;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.screen.custom.CrateMenu;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.world.Containers;
import net.minecraft.world.MenuProvider;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
import dev.architectury.registry.menu.ExtendedMenuProvider;
import net.minecraft.network.FriendlyByteBuf;
//? if >1.20.1 {
/*import net.minecraft.core.HolderLookup;
*///?}
public class CrateBlockEntity extends BlockEntity implements ExtendedMenuProvider {
private final int size;
public final SimpleContainer itemHandler;
public CrateBlockEntity(BlockPos pos, BlockState state, int size) {
super(ModBlockEntities.CRATE_BE.get(), pos, state);
this.size = size;
this.itemHandler = new SimpleContainer(size) {
@Override
public void setChanged() {
super.setChanged();
CrateBlockEntity.this.setChanged();
}
};
}
public boolean isEmptyForDrop() {
return itemHandler.isEmpty();
}
public int getSize() { return size; }
public void dropContents(Level level, BlockPos pos) {
Containers.dropContents(level, pos, itemHandler);
}
public int redstoneSignal() {
int filled = 0;
for (int i = 0; i < itemHandler.getContainerSize(); i++) {
if (!itemHandler.getItem(i).isEmpty()) filled++;
}
if (itemHandler.getContainerSize() == 0) return 0;
return Math.round((filled / (float) itemHandler.getContainerSize()) * 15f);
}
//? if >1.20.1 {
/*@Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
tag.putInt("crate.size", this.size);
tag.put("crate.inventory", itemHandler.createTag(registries));
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
if (tag.contains("crate.inventory")) {
itemHandler.fromTag(tag.getList("crate.inventory", 10), registries);
}
}
*///?} else {
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.putInt("crate.size", this.size);
tag.put("crate.inventory", itemHandler.createTag());
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
if (tag.contains("crate.inventory")) {
itemHandler.fromTag(tag.getList("crate.inventory", 10));
}
}
//?}
@Override
public Component getDisplayName() {
return Component.translatable(size <= 9 ? "block.jurassicrevived.wood_crate" : "block.jurassicrevived.iron_crate");
}
@Override
public void saveExtraData(FriendlyByteBuf buf) {
buf.writeBlockPos(getBlockPos());
}
@Nullable
@Override
public AbstractContainerMenu createMenu(int id, Inventory inv, Player player) {
return new CrateMenu(id, inv, this);
}
}
@@ -0,0 +1,343 @@
package net.cmr.jurassicrevived.block.entity.custom;
import dev.architectury.registry.menu.ExtendedMenuProvider;
import net.cmr.jurassicrevived.block.custom.DNAAnalyzerBlock;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyStorage;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyUtil;
import net.cmr.jurassicrevived.item.ModItems;
import net.cmr.jurassicrevived.recipe.DNAAnalyzerRecipe;
import net.cmr.jurassicrevived.recipe.DNAAnalyzerRecipeInput;
import net.cmr.jurassicrevived.recipe.ModRecipes;
import net.cmr.jurassicrevived.screen.custom.DNAAnalyzerMenu;
import net.cmr.jurassicrevived.util.ModTags;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerData;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
//? if >1.20.1 {
/*import net.minecraft.core.HolderLookup;
import net.minecraft.world.item.crafting.RecipeHolder;
*///?} else {
import net.minecraft.core.RegistryAccess;
//?}
import java.util.Optional;
public class DNAAnalyzerBlockEntity extends BlockEntity implements ExtendedMenuProvider, ModEnergyUtil.EnergyProvider {
public final SimpleContainer itemHandler = new SimpleContainer(5) {
@Override
public void setChanged() {
super.setChanged();
DNAAnalyzerBlockEntity.this.setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
private static final int TEST_TUBE_SLOT = 0;
private static final int MATERIAL_SLOT = 1;
private static final int OUTPUT_SLOT_1 = 2;
private ItemStack lockedOutput = ItemStack.EMPTY;
private String lastInputSignature = "";
private final ContainerData data;
private int progress = 0;
private int maxProgress = 600;
private final int DEFAULT_MAX_PROGRESS = 600;
private static final int TRANSFER_RATE = 1000;
private final ModEnergyStorage energyStorage = createEnergyStorage();
public DNAAnalyzerBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.DNA_ANALYZER_BE.get(), pos, blockState);
this.data = new ContainerData() {
@Override
public int get(int pIndex) {
return switch (pIndex) {
case 0 -> DNAAnalyzerBlockEntity.this.progress;
case 1 -> DNAAnalyzerBlockEntity.this.maxProgress;
default -> 0;
};
}
@Override
public void set(int pIndex, int pValue) {
switch (pIndex) {
case 0 -> DNAAnalyzerBlockEntity.this.progress = pValue;
case 1 -> DNAAnalyzerBlockEntity.this.maxProgress = pValue;
}
}
@Override
public int getCount() {
return 2;
}
};
}
private ModEnergyStorage createEnergyStorage() {
return new ModEnergyStorage(64000, TRANSFER_RATE) {
@Override
public void onEnergyChanged() {
setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
}
@Override
public ModEnergyStorage getEnergyStorage(@Nullable Direction direction) {
return this.energyStorage;
}
@Override
public Component getDisplayName() {
return Component.translatable("block.jurassicrevived.dna_analyzer");
}
@Override
public void saveExtraData(FriendlyByteBuf buf) {
buf.writeBlockPos(getBlockPos());
}
@Nullable
@Override
public AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) {
return new DNAAnalyzerMenu(i, inventory, this, this.data);
}
public boolean isEmptyForDrop() {
return itemHandler.isEmpty() && this.progress == 0;
}
//? if >1.20.1 {
/*@Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
tag.put("Inventory", itemHandler.createTag(registries));
tag.putInt("Prog", this.progress);
tag.putInt("MaxProg", this.maxProgress);
tag.put("Energy", energyStorage.saveNBT());
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
itemHandler.fromTag(tag.getList("Inventory", 10), registries);
progress = tag.getInt("Prog");
maxProgress = tag.getInt("MaxProg");
if (tag.contains("Energy")) {
energyStorage.loadNBT(tag.getCompound("Energy"));
}
}
*///?} else {
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.put("Inventory", itemHandler.createTag());
tag.putInt("Prog", this.progress);
tag.putInt("MaxProg", this.maxProgress);
tag.put("Energy", energyStorage.saveNBT());
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
itemHandler.fromTag(tag.getList("Inventory", 10));
progress = tag.getInt("Prog");
maxProgress = tag.getInt("MaxProg");
if (tag.contains("Energy")) {
energyStorage.loadNBT(tag.getCompound("Energy"));
}
}
//?}
public void tick(Level level, BlockPos pos, BlockState state) {
if (level.isClientSide) return;
pullEnergyFromNeighbors();
//? if >1.20.1 {
/*Optional<RecipeHolder<DNAAnalyzerRecipe>> recipeOpt = getCurrentRecipe();
*///?} else {
Optional<DNAAnalyzerRecipe> recipeOpt = getCurrentRecipe();
//?}
if (recipeOpt.isEmpty()) {
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(DNAAnalyzerBlock.LIT, false));
this.lockedOutput = ItemStack.EMPTY;
this.lastInputSignature = "";
return;
}
String currentSignature = signatureOf(itemHandler.getItem(TEST_TUBE_SLOT), itemHandler.getItem(MATERIAL_SLOT));
if (progress == 0 && (lockedOutput.isEmpty() || !currentSignature.equals(lastInputSignature))) {
//? if >1.20.1 {
/*lockedOutput = determineOutput(recipeOpt.get().value()).copy();
*///?} else {
lockedOutput = determineOutput(recipeOpt.get()).copy();
//?}
lastInputSignature = currentSignature;
}
if (!lockedOutput.isEmpty() && canInsertOutput(lockedOutput)) {
if (energyStorage.getEnergyStored() < 10) return;
energyStorage.extractEnergy(10, false);
progress++;
level.setBlockAndUpdate(pos, state.setValue(DNAAnalyzerBlock.LIT, true));
if (progress >= maxProgress) {
craftItem(lockedOutput);
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(DNAAnalyzerBlock.LIT, false));
this.lockedOutput = ItemStack.EMPTY;
this.lastInputSignature = "";
}
} else {
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(DNAAnalyzerBlock.LIT, false));
}
}
private void craftItem(ItemStack output) {
ItemStack current = itemHandler.getItem(OUTPUT_SLOT_1);
if (current.isEmpty()) {
itemHandler.setItem(OUTPUT_SLOT_1, output.copy());
} else {
current.grow(output.getCount());
}
itemHandler.removeItem(TEST_TUBE_SLOT, 1);
itemHandler.removeItem(MATERIAL_SLOT, 1);
}
private boolean canInsertOutput(ItemStack output) {
ItemStack stack = itemHandler.getItem(OUTPUT_SLOT_1);
//? if >1.20.1 {
/*return stack.isEmpty() || (ItemStack.isSameItemSameComponents(stack, output) && stack.getCount() + output.getCount() <= stack.getMaxStackSize());
*///?} else {
return stack.isEmpty() || (ItemStack.isSameItemSameTags(stack, output) && stack.getCount() + output.getCount() <= stack.getMaxStackSize());
//?}
}
//? if >1.20.1 {
/*private Optional<RecipeHolder<DNAAnalyzerRecipe>> getCurrentRecipe() {
return level.getRecipeManager().getRecipeFor(ModRecipes.DNA_ANALYZER_RECIPE_TYPE.get(),
new DNAAnalyzerRecipeInput(itemHandler.getItem(TEST_TUBE_SLOT), itemHandler.getItem(MATERIAL_SLOT)), level);
}
*///?} else {
private Optional<DNAAnalyzerRecipe> getCurrentRecipe() {
return level.getRecipeManager().getRecipeFor(ModRecipes.DNA_ANALYZER_RECIPE_TYPE.get(),
new DNAAnalyzerRecipeInput(itemHandler.getItem(TEST_TUBE_SLOT), itemHandler.getItem(MATERIAL_SLOT)), level);
}
//?}
private ItemStack determineOutput(DNAAnalyzerRecipe recipe) {
ItemStack material = itemHandler.getItem(MATERIAL_SLOT);
if (material.is(ModItems.MOSQUITO_IN_AMBER.get())) {
return pickWeightedRandomDna(recipe);
}
//? if >1.20.1 {
/*return recipe.output().copy();
*///?} else {
return recipe.getResultItem(level.registryAccess()).copy();
//?}
}
private ItemStack pickWeightedRandomDna(DNAAnalyzerRecipe recipe) {
var registry = level.registryAccess().registryOrThrow(Registries.ITEM);
var tagged = registry.getTag(ModTags.Items.DNA);
//? if >1.20.1 {
/*ItemStack fallback = recipe.output().copy();
*///?} else {
ItemStack fallback = recipe.getResultItem(level.registryAccess()).copy();
//?}
if (tagged.isEmpty()) return fallback;
int totalWeight = 0;
java.util.List<net.minecraft.world.item.Item> items = new java.util.ArrayList<>();
java.util.List<Integer> weights = new java.util.ArrayList<>();
for (var h : tagged.get()) {
int w = recipe.getWeightFor(h.value());
if (w > 0) {
items.add(h.value());
weights.add(w);
totalWeight += w;
}
}
if (totalWeight <= 0) return fallback;
int roll = level.random.nextInt(totalWeight);
int acc = 0;
for (int i = 0; i < items.size(); i++) {
acc += weights.get(i);
if (roll < acc) return new ItemStack(items.get(i), Math.max(1, fallback.getCount()));
}
return fallback;
}
private String signatureOf(ItemStack s1, ItemStack s2) {
return stackSig(s1) + "#" + stackSig(s2);
}
private String stackSig(ItemStack s) {
return s.isEmpty() ? "empty" : BuiltInRegistries.ITEM.getKey(s.getItem()) + "x" + s.getCount();
}
private void resetProgress() {
this.progress = 0;
this.maxProgress = DEFAULT_MAX_PROGRESS;
}
private void pullEnergyFromNeighbors() {
for (Direction dir : Direction.values()) {
BlockEntity be = level.getBlockEntity(worldPosition.relative(dir));
if (be instanceof ModEnergyUtil.EnergyProvider provider) {
ModEnergyStorage source = provider.getEnergyStorage(dir.getOpposite());
if (source != null && source.canExtract()) {
int accepted = energyStorage.receiveEnergy(TRANSFER_RATE, true);
if (accepted > 0) {
energyStorage.receiveEnergy(source.extractEnergy(accepted, false), false);
}
}
}
}
}
//? if >1.20.1 {
/*@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) { return saveWithoutMetadata(registries); }
*///?} else {
@Override
public CompoundTag getUpdateTag() { return saveWithoutMetadata(); }
//?}
@Override
public @Nullable Packet<ClientGamePacketListener> getUpdatePacket() { return ClientboundBlockEntityDataPacket.create(this); }
}
@@ -0,0 +1,356 @@
package net.cmr.jurassicrevived.block.entity.custom;
import dev.architectury.registry.menu.ExtendedMenuProvider;
import net.cmr.jurassicrevived.block.custom.DNAExtractorBlock;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyStorage;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyUtil;
import net.cmr.jurassicrevived.item.ModItems;
import net.cmr.jurassicrevived.recipe.DNAExtractorRecipe;
import net.cmr.jurassicrevived.recipe.DNAExtractorRecipeInput;
import net.cmr.jurassicrevived.recipe.ModRecipes;
import net.cmr.jurassicrevived.screen.custom.DNAExtractorMenu;
import net.cmr.jurassicrevived.util.ModTags;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerData;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
//? if >1.20.1 {
/*import net.minecraft.core.HolderLookup;
import net.minecraft.world.item.crafting.RecipeHolder;
*///?} else {
import net.minecraft.core.RegistryAccess;
//?}
import java.util.Optional;
public class DNAExtractorBlockEntity extends BlockEntity implements ExtendedMenuProvider, ModEnergyUtil.EnergyProvider {
public final SimpleContainer itemHandler = new SimpleContainer(5) {
@Override
public void setChanged() {
super.setChanged();
DNAExtractorBlockEntity.this.setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
private static final int TEST_TUBE_SLOT = 0;
private static final int MATERIAL_SLOT = 1;
private static final int[] OUTPUT_SLOTS = {2, 3, 4};
private ItemStack lockedOutput = ItemStack.EMPTY;
private String lastInputSignature = "";
private final ContainerData data;
private int progress = 0;
private int maxProgress = 600;
private final int DEFAULT_MAX_PROGRESS = 600;
private static final int TRANSFER_RATE = 1000;
private final ModEnergyStorage energyStorage = createEnergyStorage();
public DNAExtractorBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.DNA_EXTRACTOR_BE.get(), pos, blockState);
this.data = new ContainerData() {
@Override
public int get(int pIndex) {
return switch (pIndex) {
case 0 -> DNAExtractorBlockEntity.this.progress;
case 1 -> DNAExtractorBlockEntity.this.maxProgress;
default -> 0;
};
}
@Override
public void set(int pIndex, int pValue) {
switch (pIndex) {
case 0 -> DNAExtractorBlockEntity.this.progress = pValue;
case 1 -> DNAExtractorBlockEntity.this.maxProgress = pValue;
}
}
@Override
public int getCount() {
return 2;
}
};
}
private ModEnergyStorage createEnergyStorage() {
return new ModEnergyStorage(64000, TRANSFER_RATE) {
@Override
public void onEnergyChanged() {
setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
}
@Override
public ModEnergyStorage getEnergyStorage(@Nullable Direction direction) {
return this.energyStorage;
}
@Override
public Component getDisplayName() {
return Component.translatable("block.jurassicrevived.dna_extractor");
}
@Override
public void saveExtraData(FriendlyByteBuf buf) {
buf.writeBlockPos(getBlockPos());
}
@Nullable
@Override
public AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) {
return new DNAExtractorMenu(i, inventory, this, this.data);
}
public boolean isEmptyForDrop() {
return itemHandler.isEmpty() && this.progress == 0;
}
//? if >1.20.1 {
/*@Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
tag.put("Inventory", itemHandler.createTag(registries));
tag.putInt("Prog", this.progress);
tag.putInt("MaxProg", this.maxProgress);
tag.put("Energy", energyStorage.saveNBT());
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
itemHandler.fromTag(tag.getList("Inventory", 10), registries);
progress = tag.getInt("Prog");
maxProgress = tag.getInt("MaxProg");
if (tag.contains("Energy")) {
energyStorage.loadNBT(tag.getCompound("Energy"));
}
}
*///?} else {
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.put("Inventory", itemHandler.createTag());
tag.putInt("Prog", this.progress);
tag.putInt("MaxProg", this.maxProgress);
tag.put("Energy", energyStorage.saveNBT());
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
itemHandler.fromTag(tag.getList("Inventory", 10));
progress = tag.getInt("Prog");
maxProgress = tag.getInt("MaxProg");
if (tag.contains("Energy")) {
energyStorage.loadNBT(tag.getCompound("Energy"));
}
}
//?}
public void tick(Level level, BlockPos pos, BlockState state) {
if (level.isClientSide) return;
pullEnergyFromNeighbors();
//? if >1.20.1 {
/*Optional<RecipeHolder<DNAExtractorRecipe>> recipeOpt = getCurrentRecipe();
*///?} else {
Optional<DNAExtractorRecipe> recipeOpt = getCurrentRecipe();
//?}
if (recipeOpt.isEmpty()) {
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(DNAExtractorBlock.LIT, false));
this.lockedOutput = ItemStack.EMPTY;
this.lastInputSignature = "";
return;
}
String currentSignature = signatureOf(itemHandler.getItem(TEST_TUBE_SLOT), itemHandler.getItem(MATERIAL_SLOT));
if (progress == 0 && (lockedOutput.isEmpty() || !currentSignature.equals(lastInputSignature))) {
//? if >1.20.1 {
/*lockedOutput = determineOutput(recipeOpt.get().value()).copy();
*///?} else {
lockedOutput = determineOutput(recipeOpt.get()).copy();
//?}
lastInputSignature = currentSignature;
}
if (!lockedOutput.isEmpty() && canInsertOutput(lockedOutput)) {
if (energyStorage.getEnergyStored() < 10) return;
energyStorage.extractEnergy(10, false);
progress++;
level.setBlockAndUpdate(pos, state.setValue(DNAExtractorBlock.LIT, true));
if (progress >= maxProgress) {
craftItem(lockedOutput);
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(DNAExtractorBlock.LIT, false));
this.lockedOutput = ItemStack.EMPTY;
this.lastInputSignature = "";
}
} else {
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(DNAExtractorBlock.LIT, false));
}
}
private void craftItem(ItemStack output) {
for (int slot : OUTPUT_SLOTS) {
ItemStack stack = itemHandler.getItem(slot);
if (stack.isEmpty()) {
itemHandler.setItem(slot, output.copy());
itemHandler.removeItem(TEST_TUBE_SLOT, 1);
itemHandler.removeItem(MATERIAL_SLOT, 1);
return;
} else if (isSameItem(stack, output) && stack.getCount() + output.getCount() <= stack.getMaxStackSize()) {
stack.grow(output.getCount());
itemHandler.removeItem(TEST_TUBE_SLOT, 1);
itemHandler.removeItem(MATERIAL_SLOT, 1);
return;
}
}
}
private boolean canInsertOutput(ItemStack output) {
for (int slot : OUTPUT_SLOTS) {
ItemStack stack = itemHandler.getItem(slot);
if (stack.isEmpty() || (isSameItem(stack, output) && stack.getCount() + output.getCount() <= stack.getMaxStackSize())) return true;
}
return false;
}
private boolean isSameItem(ItemStack stack, ItemStack other) {
//? if >1.20.1 {
/*return ItemStack.isSameItemSameComponents(stack, other);
*///?} else {
return ItemStack.isSameItemSameTags(stack, other);
//?}
}
//? if >1.20.1 {
/*private Optional<RecipeHolder<DNAExtractorRecipe>> getCurrentRecipe() {
return level.getRecipeManager().getRecipeFor(ModRecipes.DNA_EXTRACTOR_RECIPE_TYPE.get(),
new DNAExtractorRecipeInput(itemHandler.getItem(TEST_TUBE_SLOT), itemHandler.getItem(MATERIAL_SLOT)), level);
}
*///?} else {
private Optional<DNAExtractorRecipe> getCurrentRecipe() {
return level.getRecipeManager().getRecipeFor(ModRecipes.DNA_EXTRACTOR_RECIPE_TYPE.get(),
new DNAExtractorRecipeInput(itemHandler.getItem(TEST_TUBE_SLOT), itemHandler.getItem(MATERIAL_SLOT)), level);
}
//?}
private ItemStack determineOutput(DNAExtractorRecipe recipe) {
ItemStack material = itemHandler.getItem(MATERIAL_SLOT);
if (material.is(ModItems.MOSQUITO_IN_AMBER.get())) {
return pickWeightedRandomDna(recipe);
}
//? if >1.20.1 {
/*return recipe.output().copy();
*///?} else {
return recipe.getResultItem(level.registryAccess()).copy();
//?}
}
private ItemStack pickWeightedRandomDna(DNAExtractorRecipe recipe) {
var registry = level.registryAccess().registryOrThrow(Registries.ITEM);
var tagged = registry.getTag(ModTags.Items.DNA);
//? if >1.20.1 {
/*ItemStack fallback = recipe.output().copy();
*///?} else {
ItemStack fallback = recipe.getResultItem(level.registryAccess()).copy();
//?}
if (tagged.isEmpty()) return fallback;
int totalWeight = 0;
java.util.List<net.minecraft.world.item.Item> items = new java.util.ArrayList<>();
java.util.List<Integer> weights = new java.util.ArrayList<>();
for (var h : tagged.get()) {
int w = recipe.getWeightFor(h.value());
if (w > 0) {
items.add(h.value());
weights.add(w);
totalWeight += w;
}
}
if (totalWeight <= 0) return fallback;
int roll = level.random.nextInt(totalWeight);
int acc = 0;
for (int i = 0; i < items.size(); i++) {
acc += weights.get(i);
if (roll < acc) return new ItemStack(items.get(i), Math.max(1, fallback.getCount()));
}
return fallback;
}
private String signatureOf(ItemStack s1, ItemStack s2) {
return stackSig(s1) + "#" + stackSig(s2);
}
private String stackSig(ItemStack s) {
return s.isEmpty() ? "empty" : BuiltInRegistries.ITEM.getKey(s.getItem()) + "x" + s.getCount();
}
private void resetProgress() {
this.progress = 0;
this.maxProgress = DEFAULT_MAX_PROGRESS;
}
private void pullEnergyFromNeighbors() {
for (Direction dir : Direction.values()) {
BlockEntity be = level.getBlockEntity(worldPosition.relative(dir));
if (be instanceof ModEnergyUtil.EnergyProvider provider) {
ModEnergyStorage source = provider.getEnergyStorage(dir.getOpposite());
if (source != null && source.canExtract()) {
int accepted = energyStorage.receiveEnergy(TRANSFER_RATE, true);
if (accepted > 0) {
energyStorage.receiveEnergy(source.extractEnergy(accepted, false), false);
}
}
}
}
}
//? if >1.20.1 {
/*@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) { return saveWithoutMetadata(registries); }
*///?} else {
@Override
public CompoundTag getUpdateTag() { return saveWithoutMetadata(); }
//?}
@Override
public @Nullable Packet<ClientGamePacketListener> getUpdatePacket() { return ClientboundBlockEntityDataPacket.create(this); }
}
@@ -0,0 +1,360 @@
package net.cmr.jurassicrevived.block.entity.custom;
import dev.architectury.registry.menu.ExtendedMenuProvider;
import net.cmr.jurassicrevived.block.custom.DNAHybridizerBlock;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyStorage;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyUtil;
import net.cmr.jurassicrevived.recipe.DNAHybridizerRecipe;
import net.cmr.jurassicrevived.recipe.DNAHybridizerRecipeInput;
import net.cmr.jurassicrevived.recipe.ModRecipes;
import net.cmr.jurassicrevived.screen.custom.DNAHybridizerMenu;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerData;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.Ingredient;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
//? if >1.20.1 {
/*import net.minecraft.core.HolderLookup;
import net.minecraft.world.item.crafting.RecipeHolder;
*///?} else {
import net.minecraft.core.RegistryAccess;
//?}
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
public class DNAHybridizerBlockEntity extends BlockEntity implements ExtendedMenuProvider, ModEnergyUtil.EnergyProvider {
public final SimpleContainer itemHandler = new SimpleContainer(11) {
@Override
public void setChanged() {
super.setChanged();
DNAHybridizerBlockEntity.this.setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
private static final int[] DNA_SLOTS = {0, 1, 2, 3, 4, 5, 6, 7};
private static final int CATALYST_SLOT = 8;
private static final int OUTPUT_SLOT = 9;
private ItemStack lockedOutput = ItemStack.EMPTY;
private String lastInputSignature = "";
private final ContainerData data;
private int progress = 0;
private int maxProgress = 3000;
private final int DEFAULT_MAX_PROGRESS = 3000;
private static final int TRANSFER_RATE = 1000;
private final ModEnergyStorage energyStorage = createEnergyStorage();
public DNAHybridizerBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.DNA_HYBRIDIZER_BE.get(), pos, blockState);
this.data = new ContainerData() {
@Override
public int get(int pIndex) {
return switch (pIndex) {
case 0 -> DNAHybridizerBlockEntity.this.progress;
case 1 -> DNAHybridizerBlockEntity.this.maxProgress;
default -> 0;
};
}
@Override
public void set(int pIndex, int pValue) {
switch (pIndex) {
case 0 -> DNAHybridizerBlockEntity.this.progress = pValue;
case 1 -> DNAHybridizerBlockEntity.this.maxProgress = pValue;
}
}
@Override
public int getCount() {
return 2;
}
};
}
private ModEnergyStorage createEnergyStorage() {
return new ModEnergyStorage(64000, TRANSFER_RATE) {
@Override
public void onEnergyChanged() {
setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
}
@Override
public ModEnergyStorage getEnergyStorage(@Nullable Direction direction) {
return this.energyStorage;
}
@Override
public Component getDisplayName() {
return Component.translatable("block.jurassicrevived.dna_hybridizer");
}
@Override
public void saveExtraData(FriendlyByteBuf buf) {
buf.writeBlockPos(getBlockPos());
}
@Nullable
@Override
public AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) {
return new DNAHybridizerMenu(i, inventory, this, this.data);
}
public boolean isEmptyForDrop() {
return itemHandler.isEmpty() && this.progress == 0;
}
//? if >1.20.1 {
/*@Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
tag.put("Inventory", itemHandler.createTag(registries));
saveCommonData(tag);
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
itemHandler.fromTag(tag.getList("Inventory", 10), registries);
loadCommonData(tag);
}
*///?} else {
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.put("Inventory", itemHandler.createTag());
saveCommonData(tag);
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
itemHandler.fromTag(tag.getList("Inventory", 10));
loadCommonData(tag);
}
//?}
private void saveCommonData(CompoundTag tag) {
tag.putInt("Prog", this.progress);
tag.putInt("MaxProg", this.maxProgress);
tag.put("Energy", energyStorage.saveNBT());
}
private void loadCommonData(CompoundTag tag) {
progress = tag.getInt("Prog");
maxProgress = tag.getInt("MaxProg");
if (tag.contains("Energy")) {
energyStorage.loadNBT(tag.getCompound("Energy"));
}
}
public void tick(Level level, BlockPos pos, BlockState state) {
if (level.isClientSide) return;
pullEnergyFromNeighbors();
//? if >1.20.1 {
/*Optional<RecipeHolder<DNAHybridizerRecipe>> recipeOpt = getCurrentRecipe();
*///?} else {
Optional<DNAHybridizerRecipe> recipeOpt = getCurrentRecipe();
//?}
if (recipeOpt.isEmpty()) {
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(DNAHybridizerBlock.LIT, false));
this.lockedOutput = ItemStack.EMPTY;
this.lastInputSignature = "";
return;
}
String currentSignature = buildSignature();
//? if >1.20.1 {
/*DNAHybridizerRecipe recipe = recipeOpt.get().value();
*///?} else {
DNAHybridizerRecipe recipe = recipeOpt.get();
//?}
if (progress == 0 && (lockedOutput.isEmpty() || !currentSignature.equals(lastInputSignature))) {
//? if >1.20.1 {
/*lockedOutput = recipe.output().copy();
*///?} else {
lockedOutput = recipe.getResultItem(level.registryAccess()).copy();
//?}
lastInputSignature = currentSignature;
}
List<Integer> exactMatch = findExactUnorderedMatchIndices(recipe);
boolean canProceed = exactMatch != null && !lockedOutput.isEmpty() && canInsertOutput(lockedOutput);
if (canProceed) {
if (energyStorage.getEnergyStored() < 10) return;
energyStorage.extractEnergy(10, false);
progress++;
level.setBlockAndUpdate(pos, state.setValue(DNAHybridizerBlock.LIT, true));
if (progress >= maxProgress) {
craftItem(lockedOutput, exactMatch);
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(DNAHybridizerBlock.LIT, false));
this.lockedOutput = ItemStack.EMPTY;
this.lastInputSignature = "";
}
} else {
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(DNAHybridizerBlock.LIT, false));
}
}
private void craftItem(ItemStack output, List<Integer> matchedIndices) {
ItemStack current = itemHandler.getItem(OUTPUT_SLOT);
if (current.isEmpty()) {
itemHandler.setItem(OUTPUT_SLOT, output.copy());
} else {
current.grow(output.getCount());
}
for (int idx : matchedIndices) {
itemHandler.removeItem(idx, 1);
}
}
private boolean canInsertOutput(ItemStack output) {
ItemStack stack = itemHandler.getItem(OUTPUT_SLOT);
//? if >1.20.1 {
/*return stack.isEmpty() || (ItemStack.isSameItemSameComponents(stack, output) && stack.getCount() + output.getCount() <= stack.getMaxStackSize());
*///?} else {
return stack.isEmpty() || (ItemStack.isSameItemSameTags(stack, output) && stack.getCount() + output.getCount() <= stack.getMaxStackSize());
//?}
}
private @Nullable List<Integer> findExactUnorderedMatchIndices(DNAHybridizerRecipe recipe) {
var inputs = recipe.getIngredients();
boolean hasCatalyst = inputs.size() >= 9 && !inputs.get(8).isEmpty();
ItemStack catStack = itemHandler.getItem(CATALYST_SLOT);
if (hasCatalyst) {
if (catStack.isEmpty() || !inputs.get(8).test(catStack)) return null;
} else if (!catStack.isEmpty()) return null;
List<Ingredient> required = new ArrayList<>();
for (int i = 0; i < Math.min(8, inputs.size()); i++) {
if (!inputs.get(i).isEmpty()) required.add(inputs.get(i));
}
if (required.isEmpty()) return null;
boolean[] used = new boolean[8];
List<Integer> matched = new ArrayList<>();
for (var need : required) {
boolean found = false;
for (int i = 0; i < 8; i++) {
if (used[i]) continue;
ItemStack stack = itemHandler.getItem(i);
if (!stack.isEmpty() && need.test(stack)) {
used[i] = true;
matched.add(i);
found = true;
break;
}
}
if (!found) return null;
}
for (int i = 0; i < 8; i++) {
if (!used[i] && !itemHandler.getItem(i).isEmpty()) return null;
}
if (hasCatalyst) matched.add(CATALYST_SLOT);
return matched;
}
//? if >1.20.1 {
/*private Optional<RecipeHolder<DNAHybridizerRecipe>> getCurrentRecipe() {
return level.getRecipeManager().getRecipeFor(ModRecipes.DNA_HYBRIDIZER_RECIPE_TYPE.get(),
new DNAHybridizerRecipeInput(
itemHandler.getItem(0), itemHandler.getItem(1), itemHandler.getItem(2),
itemHandler.getItem(3), itemHandler.getItem(4), itemHandler.getItem(5),
itemHandler.getItem(6), itemHandler.getItem(7), itemHandler.getItem(8)
), level);
}
*///?} else {
private Optional<DNAHybridizerRecipe> getCurrentRecipe() {
return level.getRecipeManager().getRecipeFor(ModRecipes.DNA_HYBRIDIZER_RECIPE_TYPE.get(),
new DNAHybridizerRecipeInput(
itemHandler.getItem(0), itemHandler.getItem(1), itemHandler.getItem(2),
itemHandler.getItem(3), itemHandler.getItem(4), itemHandler.getItem(5),
itemHandler.getItem(6), itemHandler.getItem(7), itemHandler.getItem(8)
), level);
}
//?}
private String buildSignature() {
StringBuilder sb = new StringBuilder();
for (int i = 0; i < 9; i++) {
ItemStack s = itemHandler.getItem(i);
sb.append(s.isEmpty() ? "e" : BuiltInRegistries.ITEM.getKey(s.getItem()) + "x" + s.getCount()).append("#");
}
return sb.toString();
}
private void resetProgress() {
this.progress = 0;
this.maxProgress = DEFAULT_MAX_PROGRESS;
}
private void pullEnergyFromNeighbors() {
for (Direction dir : Direction.values()) {
BlockEntity be = level.getBlockEntity(worldPosition.relative(dir));
if (be instanceof ModEnergyUtil.EnergyProvider provider) {
ModEnergyStorage source = provider.getEnergyStorage(dir.getOpposite());
if (source != null && source.canExtract()) {
int accepted = energyStorage.receiveEnergy(TRANSFER_RATE, true);
if (accepted > 0) {
energyStorage.receiveEnergy(source.extractEnergy(accepted, false), false);
}
}
}
}
}
//? if >1.20.1 {
/*@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) { return saveWithoutMetadata(registries); }
*///?} else {
@Override
public CompoundTag getUpdateTag() { return saveWithoutMetadata(); }
//?}
@Override
public @Nullable Packet<ClientGamePacketListener> getUpdatePacket() { return ClientboundBlockEntityDataPacket.create(this); }
}
@@ -0,0 +1,100 @@
package net.cmr.jurassicrevived.block.entity.custom;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
//? if >1.20.1 {
/*import net.minecraft.core.HolderLookup;
*///?}
public class EggBlockEntity extends BlockEntity {
private long placedAt = -1L;
private int totalSeconds = 5; // default
public EggBlockEntity(BlockPos pos, BlockState state) {
super(ModBlockEntities.EGG_BE.get(), pos, state);
}
public void setPlacedAt(long gameTime) {
this.placedAt = gameTime;
setChanged();
}
public void resetForNewPlacement(Level level, int totalSeconds) {
this.placedAt = level.getGameTime();
this.totalSeconds = Math.max(1, totalSeconds);
setChanged();
}
public void setTotalSeconds(int secs) {
this.totalSeconds = Math.max(1, secs);
setChanged();
}
public int getTotalSeconds() {
return totalSeconds;
}
public int getSecondsRemaining(Level level) {
if (placedAt < 0) return totalSeconds;
long elapsed = level.getGameTime() - placedAt;
long remainingTicks = Math.max(0, (20L * totalSeconds) - elapsed);
return (int) Math.ceil(remainingTicks / 20.0);
}
//? if >1.20.1 {
/*@Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
saveCommonData(tag);
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
loadCommonData(tag);
}
*///?} else {
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
saveCommonData(tag);
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
loadCommonData(tag);
}
//?}
private void saveCommonData(CompoundTag tag) {
tag.putLong("egg.placedAt", placedAt);
tag.putInt("egg.totalSeconds", totalSeconds);
}
private void loadCommonData(CompoundTag tag) {
if (tag.contains("egg.placedAt")) {
placedAt = tag.getLong("egg.placedAt");
}
if (tag.contains("egg.totalSeconds")) {
totalSeconds = Math.max(1, tag.getInt("egg.totalSeconds"));
}
}
public void invalidateTimer() {
this.placedAt = -1L;
setChanged();
}
public Component getHatchTooltip(Level level, Player player) {
int secs = getSecondsRemaining(level);
return Component.translatable("tooltip.jurassicrevived.egg.hatches_in_seconds", secs);
}
}
@@ -0,0 +1,297 @@
package net.cmr.jurassicrevived.block.entity.custom;
import dev.architectury.registry.menu.ExtendedMenuProvider;
import net.cmr.jurassicrevived.block.custom.EmbryoCalcificationMachineBlock;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyStorage;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyUtil;
import net.cmr.jurassicrevived.recipe.EmbryoCalcificationMachineRecipe;
import net.cmr.jurassicrevived.recipe.EmbryoCalcificationMachineRecipeInput;
import net.cmr.jurassicrevived.recipe.ModRecipes;
import net.cmr.jurassicrevived.screen.custom.EmbryoCalcificationMachineMenu;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerData;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
//? if >1.20.1 {
/*import net.minecraft.core.HolderLookup;
import net.minecraft.world.item.crafting.RecipeHolder;
*///?} else {
import net.minecraft.core.RegistryAccess;
//?}
import java.util.Optional;
public class EmbryoCalcificationMachineBlockEntity extends BlockEntity implements ExtendedMenuProvider, ModEnergyUtil.EnergyProvider {
public final SimpleContainer itemHandler = new SimpleContainer(5) {
@Override
public void setChanged() {
super.setChanged();
EmbryoCalcificationMachineBlockEntity.this.setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
private static final int SYRINGE_SLOT = 0;
private static final int EGG_SLOT = 1;
private static final int OUTPUT_SLOT = 2;
private ItemStack lockedOutput = ItemStack.EMPTY;
private String lastInputSignature = "";
private final ContainerData data;
private int progress = 0;
private int maxProgress = 100;
private final int DEFAULT_MAX_PROGRESS = 100;
private static final int TRANSFER_RATE = 1000;
private final ModEnergyStorage energyStorage = createEnergyStorage();
public EmbryoCalcificationMachineBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.EMBRYO_CALCIFICATION_MACHINE_BE.get(), pos, blockState);
this.data = new ContainerData() {
@Override
public int get(int pIndex) {
return switch (pIndex) {
case 0 -> EmbryoCalcificationMachineBlockEntity.this.progress;
case 1 -> EmbryoCalcificationMachineBlockEntity.this.maxProgress;
default -> 0;
};
}
@Override
public void set(int pIndex, int pValue) {
switch (pIndex) {
case 0 -> EmbryoCalcificationMachineBlockEntity.this.progress = pValue;
case 1 -> EmbryoCalcificationMachineBlockEntity.this.maxProgress = pValue;
}
}
@Override
public int getCount() {
return 2;
}
};
}
private ModEnergyStorage createEnergyStorage() {
return new ModEnergyStorage(64000, TRANSFER_RATE) {
@Override
public void onEnergyChanged() {
setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
}
@Override
public ModEnergyStorage getEnergyStorage(@Nullable Direction direction) {
return this.energyStorage;
}
@Override
public Component getDisplayName() {
return Component.translatable("block.jurassicrevived.embryo_calcification_machine");
}
@Override
public void saveExtraData(FriendlyByteBuf buf) {
buf.writeBlockPos(getBlockPos());
}
@Nullable
@Override
public AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) {
return new EmbryoCalcificationMachineMenu(i, inventory, this, this.data);
}
public boolean isEmptyForDrop() {
return itemHandler.isEmpty() && this.progress == 0;
}
//? if >1.20.1 {
/*@Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
tag.put("Inventory", itemHandler.createTag(registries));
saveCommonData(tag);
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
itemHandler.fromTag(tag.getList("Inventory", 10), registries);
loadCommonData(tag);
}
*///?} else {
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.put("Inventory", itemHandler.createTag());
saveCommonData(tag);
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
itemHandler.fromTag(tag.getList("Inventory", 10));
loadCommonData(tag);
}
//?}
private void saveCommonData(CompoundTag tag) {
tag.putInt("Prog", this.progress);
tag.putInt("MaxProg", this.maxProgress);
tag.put("Energy", energyStorage.saveNBT());
}
private void loadCommonData(CompoundTag tag) {
progress = tag.getInt("Prog");
maxProgress = tag.getInt("MaxProg");
if (tag.contains("Energy")) {
energyStorage.loadNBT(tag.getCompound("Energy"));
}
}
public void tick(Level level, BlockPos pos, BlockState state) {
if (level.isClientSide) return;
pullEnergyFromNeighbors();
//? if >1.20.1 {
/*Optional<RecipeHolder<EmbryoCalcificationMachineRecipe>> recipeOpt = getCurrentRecipe();
*///?} else {
Optional<EmbryoCalcificationMachineRecipe> recipeOpt = getCurrentRecipe();
//?}
if (recipeOpt.isEmpty()) {
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(EmbryoCalcificationMachineBlock.LIT, false));
this.lockedOutput = ItemStack.EMPTY;
this.lastInputSignature = "";
return;
}
String currentSignature = signatureOf(itemHandler.getItem(SYRINGE_SLOT), itemHandler.getItem(EGG_SLOT));
if (progress == 0 && (lockedOutput.isEmpty() || !currentSignature.equals(lastInputSignature))) {
//? if >1.20.1 {
/*lockedOutput = recipeOpt.get().value().assemble(new EmbryoCalcificationMachineRecipeInput(itemHandler.getItem(SYRINGE_SLOT), itemHandler.getItem(EGG_SLOT)), level.registryAccess()).copy();
*///?} else {
lockedOutput = recipeOpt.get().assemble(new EmbryoCalcificationMachineRecipeInput(itemHandler.getItem(SYRINGE_SLOT), itemHandler.getItem(EGG_SLOT)), level.registryAccess()).copy();
//?}
lastInputSignature = currentSignature;
}
if (!lockedOutput.isEmpty() && canInsertOutput(lockedOutput)) {
if (energyStorage.getEnergyStored() < 10) return;
energyStorage.extractEnergy(10, false);
progress++;
level.setBlockAndUpdate(pos, state.setValue(EmbryoCalcificationMachineBlock.LIT, true));
if (progress >= maxProgress) {
craftItem(lockedOutput);
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(EmbryoCalcificationMachineBlock.LIT, false));
this.lockedOutput = ItemStack.EMPTY;
this.lastInputSignature = "";
}
} else {
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(EmbryoCalcificationMachineBlock.LIT, false));
}
}
private void craftItem(ItemStack output) {
ItemStack current = itemHandler.getItem(OUTPUT_SLOT);
if (current.isEmpty()) {
itemHandler.setItem(OUTPUT_SLOT, output.copy());
} else {
current.grow(output.getCount());
}
itemHandler.removeItem(SYRINGE_SLOT, 1);
itemHandler.removeItem(EGG_SLOT, 1);
}
private boolean canInsertOutput(ItemStack output) {
ItemStack stack = itemHandler.getItem(OUTPUT_SLOT);
//? if >1.20.1 {
/*return stack.isEmpty() || (ItemStack.isSameItemSameComponents(stack, output) && stack.getCount() + output.getCount() <= stack.getMaxStackSize());
*///?} else {
return stack.isEmpty() || (ItemStack.isSameItemSameTags(stack, output) && stack.getCount() + output.getCount() <= stack.getMaxStackSize());
//?}
}
//? if >1.20.1 {
/*private Optional<RecipeHolder<EmbryoCalcificationMachineRecipe>> getCurrentRecipe() {
return level.getRecipeManager().getRecipeFor(ModRecipes.EMBRYO_CALCIFICATION_MACHINE_RECIPE_TYPE.get(),
new EmbryoCalcificationMachineRecipeInput(itemHandler.getItem(SYRINGE_SLOT), itemHandler.getItem(EGG_SLOT)), level);
}
*///?} else {
private Optional<EmbryoCalcificationMachineRecipe> getCurrentRecipe() {
return level.getRecipeManager().getRecipeFor(ModRecipes.EMBRYO_CALCIFICATION_MACHINE_RECIPE_TYPE.get(),
new EmbryoCalcificationMachineRecipeInput(itemHandler.getItem(SYRINGE_SLOT), itemHandler.getItem(EGG_SLOT)), level);
}
//?}
private String signatureOf(ItemStack s1, ItemStack s2) {
return stackSig(s1) + "#" + stackSig(s2);
}
private String stackSig(ItemStack s) {
return s.isEmpty() ? "empty" : BuiltInRegistries.ITEM.getKey(s.getItem()) + "x" + s.getCount();
}
private void resetProgress() {
this.progress = 0;
this.maxProgress = DEFAULT_MAX_PROGRESS;
}
private void pullEnergyFromNeighbors() {
for (Direction dir : Direction.values()) {
BlockEntity be = level.getBlockEntity(worldPosition.relative(dir));
if (be instanceof ModEnergyUtil.EnergyProvider provider) {
ModEnergyStorage source = provider.getEnergyStorage(dir.getOpposite());
if (source != null && source.canExtract()) {
int accepted = energyStorage.receiveEnergy(TRANSFER_RATE, true);
if (accepted > 0) {
energyStorage.receiveEnergy(source.extractEnergy(accepted, false), false);
}
}
}
}
}
//? if >1.20.1 {
/*@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) { return saveWithoutMetadata(registries); }
*///?} else {
@Override
public CompoundTag getUpdateTag() { return saveWithoutMetadata(); }
//?}
@Override
public @Nullable Packet<ClientGamePacketListener> getUpdatePacket() { return ClientboundBlockEntityDataPacket.create(this); }
}
@@ -0,0 +1,349 @@
package net.cmr.jurassicrevived.block.entity.custom;
import dev.architectury.registry.menu.ExtendedMenuProvider;
import net.cmr.jurassicrevived.block.custom.EmbryonicMachineBlock;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyStorage;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyUtil;
import net.cmr.jurassicrevived.item.ModItems;
import net.cmr.jurassicrevived.recipe.EmbryonicMachineRecipe;
import net.cmr.jurassicrevived.recipe.EmbryonicMachineRecipeInput;
import net.cmr.jurassicrevived.recipe.ModRecipes;
import net.cmr.jurassicrevived.screen.custom.EmbryonicMachineMenu;
import net.cmr.jurassicrevived.util.ModTags;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerData;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
//? if >1.20.1 {
/*import net.minecraft.core.HolderLookup;
import net.minecraft.world.item.crafting.RecipeHolder;
*///?} else {
import net.minecraft.core.RegistryAccess;
//?}
import java.util.Optional;
public class EmbryonicMachineBlockEntity extends BlockEntity implements ExtendedMenuProvider, ModEnergyUtil.EnergyProvider {
public final SimpleContainer itemHandler = new SimpleContainer(4) {
@Override
public void setChanged() {
super.setChanged();
EmbryonicMachineBlockEntity.this.setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
private static final int SYRINGE_SLOT = 0;
private static final int MATERIAL_SLOT = 1;
private static final int FROG_SLOT = 2;
private static final int OUTPUT_SLOT = 3;
private ItemStack lockedOutput = ItemStack.EMPTY;
private String lastInputSignature = "";
private final ContainerData data;
private int progress = 0;
private int maxProgress = 200;
private final int DEFAULT_MAX_PROGRESS = 200;
private static final int TRANSFER_RATE = 1000;
private final ModEnergyStorage energyStorage = createEnergyStorage();
public EmbryonicMachineBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.EMBRYONIC_MACHINE_BE.get(), pos, blockState);
this.data = new ContainerData() {
@Override
public int get(int pIndex) {
return switch (pIndex) {
case 0 -> EmbryonicMachineBlockEntity.this.progress;
case 1 -> EmbryonicMachineBlockEntity.this.maxProgress;
default -> 0;
};
}
@Override
public void set(int pIndex, int pValue) {
switch (pIndex) {
case 0 -> EmbryonicMachineBlockEntity.this.progress = pValue;
case 1 -> EmbryonicMachineBlockEntity.this.maxProgress = pValue;
}
}
@Override
public int getCount() {
return 2;
}
};
}
private ModEnergyStorage createEnergyStorage() {
return new ModEnergyStorage(64000, TRANSFER_RATE) {
@Override
public void onEnergyChanged() {
setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
}
@Override
public ModEnergyStorage getEnergyStorage(@Nullable Direction direction) {
return this.energyStorage;
}
@Override
public Component getDisplayName() {
return Component.translatable("block.jurassicrevived.embryonic_machine");
}
@Override
public void saveExtraData(FriendlyByteBuf buf) {
buf.writeBlockPos(getBlockPos());
}
@Nullable
@Override
public AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) {
return new EmbryonicMachineMenu(i, inventory, this, this.data);
}
public boolean isEmptyForDrop() {
return itemHandler.isEmpty() && this.progress == 0;
}
//? if >1.20.1 {
/*@Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
tag.put("Inventory", itemHandler.createTag(registries));
saveCommonData(tag);
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
itemHandler.fromTag(tag.getList("Inventory", 10), registries);
loadCommonData(tag);
}
*///?} else {
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.put("Inventory", itemHandler.createTag());
saveCommonData(tag);
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
itemHandler.fromTag(tag.getList("Inventory", 10));
loadCommonData(tag);
}
//?}
private void saveCommonData(CompoundTag tag) {
tag.putInt("Prog", this.progress);
tag.putInt("MaxProg", this.maxProgress);
tag.put("Energy", energyStorage.saveNBT());
}
private void loadCommonData(CompoundTag tag) {
progress = tag.getInt("Prog");
maxProgress = tag.getInt("MaxProg");
if (tag.contains("Energy")) {
energyStorage.loadNBT(tag.getCompound("Energy"));
}
}
public void tick(Level level, BlockPos pos, BlockState state) {
if (level.isClientSide) return;
pullEnergyFromNeighbors();
//? if >1.20.1 {
/*Optional<RecipeHolder<EmbryonicMachineRecipe>> recipeOpt = getCurrentRecipe();
*///?} else {
Optional<EmbryonicMachineRecipe> recipeOpt = getCurrentRecipe();
//?}
if (recipeOpt.isEmpty()) {
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(EmbryonicMachineBlock.LIT, false));
this.lockedOutput = ItemStack.EMPTY;
this.lastInputSignature = "";
return;
}
String currentSignature = signatureOf(itemHandler.getItem(SYRINGE_SLOT), itemHandler.getItem(MATERIAL_SLOT));
//? if >1.20.1 {
/*EmbryonicMachineRecipe recipe = recipeOpt.get().value();
*///?} else {
EmbryonicMachineRecipe recipe = recipeOpt.get();
//?}
if (progress == 0 && (lockedOutput.isEmpty() || !currentSignature.equals(lastInputSignature))) {
lockedOutput = determineOutput(recipe).copy();
lastInputSignature = currentSignature;
}
if (!lockedOutput.isEmpty() && canInsertOutput(lockedOutput)) {
if (energyStorage.getEnergyStored() < 10) return;
energyStorage.extractEnergy(10, false);
progress++;
level.setBlockAndUpdate(pos, state.setValue(EmbryonicMachineBlock.LIT, true));
if (progress >= maxProgress) {
craftItem(lockedOutput);
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(EmbryonicMachineBlock.LIT, false));
this.lockedOutput = ItemStack.EMPTY;
this.lastInputSignature = "";
}
} else {
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(EmbryonicMachineBlock.LIT, false));
}
}
private void craftItem(ItemStack output) {
ItemStack current = itemHandler.getItem(OUTPUT_SLOT);
if (current.isEmpty()) {
itemHandler.setItem(OUTPUT_SLOT, output.copy());
} else {
current.grow(output.getCount());
}
itemHandler.removeItem(SYRINGE_SLOT, 1);
itemHandler.removeItem(MATERIAL_SLOT, 1);
itemHandler.removeItem(FROG_SLOT, 1);
}
private boolean canInsertOutput(ItemStack output) {
ItemStack stack = itemHandler.getItem(OUTPUT_SLOT);
//? if >1.20.1 {
/*return stack.isEmpty() || (ItemStack.isSameItemSameComponents(stack, output) && stack.getCount() + output.getCount() <= stack.getMaxStackSize());
*///?} else {
return stack.isEmpty() || (ItemStack.isSameItemSameTags(stack, output) && stack.getCount() + output.getCount() <= stack.getMaxStackSize());
//?}
}
//? if >1.20.1 {
/*private Optional<RecipeHolder<EmbryonicMachineRecipe>> getCurrentRecipe() {
return level.getRecipeManager().getRecipeFor(ModRecipes.EMBRYONIC_MACHINE_RECIPE_TYPE.get(),
new EmbryonicMachineRecipeInput(itemHandler.getItem(SYRINGE_SLOT), itemHandler.getItem(MATERIAL_SLOT), itemHandler.getItem(FROG_SLOT)), level);
}
*///?} else {
private Optional<EmbryonicMachineRecipe> getCurrentRecipe() {
return level.getRecipeManager().getRecipeFor(ModRecipes.EMBRYONIC_MACHINE_RECIPE_TYPE.get(),
new EmbryonicMachineRecipeInput(itemHandler.getItem(SYRINGE_SLOT), itemHandler.getItem(MATERIAL_SLOT), itemHandler.getItem(FROG_SLOT)), level);
}
//?}
private ItemStack determineOutput(EmbryonicMachineRecipe recipe) {
ItemStack material = itemHandler.getItem(MATERIAL_SLOT);
if (material.is(ModItems.MOSQUITO_IN_AMBER.get())) {
return pickWeightedRandomDna(recipe);
}
//? if >1.20.1 {
/*return recipe.output().copy();
*///?} else {
return recipe.getResultItem(level.registryAccess()).copy();
//?}
}
private ItemStack pickWeightedRandomDna(EmbryonicMachineRecipe recipe) {
var registry = level.registryAccess().registryOrThrow(Registries.ITEM);
var tagged = registry.getTag(ModTags.Items.DNA);
//? if >1.20.1 {
/*ItemStack fallback = recipe.output().copy();
*///?} else {
ItemStack fallback = recipe.getResultItem(level.registryAccess()).copy();
//?}
if (tagged.isEmpty()) return fallback;
int totalWeight = 0;
java.util.List<net.minecraft.world.item.Item> items = new java.util.ArrayList<>();
java.util.List<Integer> weights = new java.util.ArrayList<>();
for (var h : tagged.get()) {
int w = recipe.getWeightFor(h.value());
if (w > 0) {
items.add(h.value());
weights.add(w);
totalWeight += w;
}
}
if (totalWeight <= 0) return fallback;
int roll = level.random.nextInt(totalWeight);
int acc = 0;
for (int i = 0; i < items.size(); i++) {
acc += weights.get(i);
if (roll < acc) return new ItemStack(items.get(i), Math.max(1, fallback.getCount()));
}
return fallback;
}
private String signatureOf(ItemStack s1, ItemStack s2) {
return stackSig(s1) + "#" + stackSig(s2);
}
private String stackSig(ItemStack s) {
return s.isEmpty() ? "empty" : BuiltInRegistries.ITEM.getKey(s.getItem()) + "x" + s.getCount();
}
private void resetProgress() {
this.progress = 0;
this.maxProgress = DEFAULT_MAX_PROGRESS;
}
private void pullEnergyFromNeighbors() {
for (Direction dir : Direction.values()) {
BlockEntity be = level.getBlockEntity(worldPosition.relative(dir));
if (be instanceof ModEnergyUtil.EnergyProvider provider) {
ModEnergyStorage source = provider.getEnergyStorage(dir.getOpposite());
if (source != null && source.canExtract()) {
int accepted = energyStorage.receiveEnergy(TRANSFER_RATE, true);
if (accepted > 0) {
energyStorage.receiveEnergy(source.extractEnergy(accepted, false), false);
}
}
}
}
}
//? if >1.20.1 {
/*@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) { return saveWithoutMetadata(registries); }
*///?} else {
@Override
public CompoundTag getUpdateTag() { return saveWithoutMetadata(); }
//?}
@Override
public @Nullable Packet<ClientGamePacketListener> getUpdatePacket() { return ClientboundBlockEntityDataPacket.create(this); }
}
@@ -0,0 +1,357 @@
package net.cmr.jurassicrevived.block.entity.custom;
import dev.architectury.fluid.FluidStack;
import dev.architectury.registry.menu.ExtendedMenuProvider;
import net.cmr.jurassicrevived.block.custom.FossilCleanerBlock;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyStorage;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyUtil;
import net.cmr.jurassicrevived.recipe.FossilCleanerRecipe;
import net.cmr.jurassicrevived.recipe.FossilCleanerRecipeInput;
import net.cmr.jurassicrevived.recipe.ModRecipes;
import net.cmr.jurassicrevived.screen.custom.FossilCleanerMenu;
import net.cmr.jurassicrevived.util.ModTags;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.core.registries.Registries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerData;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
//? if >1.20.1 {
/*import net.minecraft.core.HolderLookup;
import net.minecraft.world.item.crafting.RecipeHolder;
*///?} else {
import net.minecraft.core.RegistryAccess;
//?}
import java.util.Optional;
public class FossilCleanerBlockEntity extends BlockEntity implements ExtendedMenuProvider, ModEnergyUtil.EnergyProvider {
public final SimpleContainer itemHandler = new SimpleContainer(5) {
@Override
public void setChanged() {
super.setChanged();
FossilCleanerBlockEntity.this.setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
private static final int WATER_SLOT = 0;
private static final int FOSSILBLOCK_SLOT = 1;
private static final int[] OUTPUT_SLOTS = {2, 3, 4};
private static final int WATER_CRAFT_AMOUNT = 250;
private static final long TANK_CAPACITY = 16000;
private FluidStack fluidStack = FluidStack.empty();
private ItemStack lockedOutput = ItemStack.EMPTY;
private String lastInputSignature = "";
private final ContainerData data;
private int progress = 0;
private int maxProgress = 200;
private final int DEFAULT_MAX_PROGRESS = 200;
private static final int TRANSFER_RATE = 1000;
private final ModEnergyStorage energyStorage = createEnergyStorage();
public FossilCleanerBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.FOSSIL_CLEANER_BE.get(), pos, blockState);
this.data = new ContainerData() {
@Override
public int get(int pIndex) {
return switch (pIndex) {
case 0 -> FossilCleanerBlockEntity.this.progress;
case 1 -> FossilCleanerBlockEntity.this.maxProgress;
default -> 0;
};
}
@Override
public void set(int pIndex, int pValue) {
switch (pIndex) {
case 0 -> FossilCleanerBlockEntity.this.progress = pValue;
case 1 -> FossilCleanerBlockEntity.this.maxProgress = pValue;
}
}
@Override
public int getCount() {
return 2;
}
};
}
private ModEnergyStorage createEnergyStorage() {
return new ModEnergyStorage(64000, TRANSFER_RATE) {
@Override
public void onEnergyChanged() {
setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
}
@Override
public ModEnergyStorage getEnergyStorage(@Nullable Direction direction) {
return this.energyStorage;
}
public FluidStack getFluid() {
return fluidStack;
}
@Override
public Component getDisplayName() {
return Component.translatable("block.jurassicrevived.fossil_cleaner");
}
@Override
public void saveExtraData(FriendlyByteBuf buf) {
buf.writeBlockPos(getBlockPos());
}
@Nullable
@Override
public AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) {
return new FossilCleanerMenu(i, inventory, this, this.data);
}
public boolean isEmptyForDrop() {
return itemHandler.isEmpty() && fluidStack.isEmpty() && progress == 0;
}
//? if >1.20.1 {
/*@Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
tag.put("Inventory", itemHandler.createTag(registries));
tag.putInt("Prog", this.progress);
tag.putInt("MaxProg", this.maxProgress);
tag.put("Energy", energyStorage.saveNBT());
CompoundTag fluidTag = new CompoundTag();
fluidStack.write(registries, fluidTag);
tag.put("Fluid", fluidTag);
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
itemHandler.fromTag(tag.getList("Inventory", 10), registries);
progress = tag.getInt("Prog");
maxProgress = tag.getInt("MaxProg");
if (tag.contains("Energy")) energyStorage.loadNBT(tag.getCompound("Energy"));
if (tag.contains("Fluid")) fluidStack = FluidStack.read(registries, tag.getCompound("Fluid")).orElse(FluidStack.empty());
}
*///?} else {
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.put("Inventory", itemHandler.createTag());
tag.putInt("Prog", this.progress);
tag.putInt("MaxProg", this.maxProgress);
tag.put("Energy", energyStorage.saveNBT());
CompoundTag fluidTag = new CompoundTag();
fluidStack.write(fluidTag);
tag.put("Fluid", fluidTag);
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
itemHandler.fromTag(tag.getList("Inventory", 10));
progress = tag.getInt("Prog");
maxProgress = tag.getInt("MaxProg");
if (tag.contains("Energy")) energyStorage.loadNBT(tag.getCompound("Energy"));
if (tag.contains("Fluid")) fluidStack = FluidStack.read(tag.getCompound("Fluid"));
}
//?}
public void tick(Level level, BlockPos pos, BlockState state) {
if (level.isClientSide) return;
pullEnergyFromNeighbors();
handleBucketInput();
//? if >1.20.1 {
/*Optional<RecipeHolder<FossilCleanerRecipe>> recipeOpt = getCurrentRecipe();
*///?} else {
Optional<FossilCleanerRecipe> recipeOpt = getCurrentRecipe();
//?}
if (recipeOpt.isEmpty()) {
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(FossilCleanerBlock.LIT, false));
return;
}
String currentSignature = stackSig(itemHandler.getItem(FOSSILBLOCK_SLOT));
if (progress == 0 && (lockedOutput.isEmpty() || !currentSignature.equals(lastInputSignature))) {
//? if >1.20.1 {
/*lockedOutput = determineOutput(recipeOpt.get().value()).copy();
*///?} else {
lockedOutput = determineOutput(recipeOpt.get()).copy();
//?}
lastInputSignature = currentSignature;
}
if (!lockedOutput.isEmpty() && canInsertOutput(lockedOutput) && fluidStack.getAmount() >= WATER_CRAFT_AMOUNT) {
if (energyStorage.getEnergyStored() < 10) return;
energyStorage.extractEnergy(10, false);
progress++;
level.setBlockAndUpdate(pos, state.setValue(FossilCleanerBlock.LIT, true));
if (progress >= maxProgress) {
craftItem(lockedOutput);
fluidStack.setAmount(fluidStack.getAmount() - WATER_CRAFT_AMOUNT);
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(FossilCleanerBlock.LIT, false));
}
} else {
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(FossilCleanerBlock.LIT, false));
}
}
private void handleBucketInput() {
ItemStack stack = itemHandler.getItem(WATER_SLOT);
if (stack.is(Items.WATER_BUCKET) && (TANK_CAPACITY - fluidStack.getAmount() >= 1000)) {
if (fluidStack.isEmpty() || fluidStack.getFluid().is(FluidTags.WATER)) {
fluidStack = FluidStack.create(net.minecraft.world.level.material.Fluids.WATER, fluidStack.getAmount() + 1000);
itemHandler.setItem(WATER_SLOT, new ItemStack(Items.BUCKET));
setChanged();
}
}
}
private void craftItem(ItemStack output) {
for (int slot : OUTPUT_SLOTS) {
ItemStack stack = itemHandler.getItem(slot);
if (stack.isEmpty()) {
itemHandler.setItem(slot, output.copy());
itemHandler.removeItem(FOSSILBLOCK_SLOT, 1);
return;
} else if (isSameItem(stack, output) && stack.getCount() + output.getCount() <= stack.getMaxStackSize()) {
stack.grow(output.getCount());
itemHandler.removeItem(FOSSILBLOCK_SLOT, 1);
return;
}
}
}
private boolean canInsertOutput(ItemStack output) {
for (int slot : OUTPUT_SLOTS) {
ItemStack stack = itemHandler.getItem(slot);
if (stack.isEmpty() || (isSameItem(stack, output) && stack.getCount() + output.getCount() <= stack.getMaxStackSize())) return true;
}
return false;
}
private boolean isSameItem(ItemStack stack, ItemStack other) {
//? if >1.20.1 {
/*return ItemStack.isSameItemSameComponents(stack, other);
*///?} else {
return ItemStack.isSameItemSameTags(stack, other);
//?}
}
//? if >1.20.1 {
/*private Optional<RecipeHolder<FossilCleanerRecipe>> getCurrentRecipe() {
return level.getRecipeManager().getRecipeFor(ModRecipes.FOSSIL_CLEANER_RECIPE_TYPE.get(), new FossilCleanerRecipeInput(itemHandler.getItem(FOSSILBLOCK_SLOT), itemHandler.getItem(WATER_SLOT)), level);
}
*///?} else {
private Optional<FossilCleanerRecipe> getCurrentRecipe() {
return level.getRecipeManager().getRecipeFor(ModRecipes.FOSSIL_CLEANER_RECIPE_TYPE.get(), new FossilCleanerRecipeInput(itemHandler.getItem(FOSSILBLOCK_SLOT), itemHandler.getItem(WATER_SLOT)), level);
}
//?}
private ItemStack determineOutput(FossilCleanerRecipe recipe) {
var registry = level.registryAccess().registryOrThrow(Registries.ITEM);
var tagged = registry.getTag(ModTags.Items.FOSSILS);
//? if >1.20.1 {
/*ItemStack fallback = recipe.output().copy();
*///?} else {
ItemStack fallback = recipe.getResultItem(level.registryAccess()).copy();
//?}
if (tagged.isEmpty()) return fallback;
int total = 0;
java.util.List<net.minecraft.world.item.Item> items = new java.util.ArrayList<>();
java.util.List<Integer> weights = new java.util.ArrayList<>();
for (var h : tagged.get()) {
int w = recipe.getWeightFor(h.value());
if (w > 0) {
items.add(h.value());
weights.add(w);
total += w;
}
}
if (total <= 0) return fallback;
int roll = level.random.nextInt(total);
int acc = 0;
for (int i = 0; i < items.size(); i++) {
acc += weights.get(i);
if (roll < acc) return new ItemStack(items.get(i), Math.max(1, fallback.getCount()));
}
return fallback;
}
private String stackSig(ItemStack s) {
return s.isEmpty() ? "empty" : BuiltInRegistries.ITEM.getKey(s.getItem()).toString() + ":" + s.getCount();
}
private void resetProgress() {
this.progress = 0;
this.maxProgress = DEFAULT_MAX_PROGRESS;
}
private void pullEnergyFromNeighbors() {
for (Direction dir : Direction.values()) {
BlockEntity be = level.getBlockEntity(worldPosition.relative(dir));
if (be instanceof ModEnergyUtil.EnergyProvider provider) {
ModEnergyStorage source = provider.getEnergyStorage(dir.getOpposite());
if (source != null && source.canExtract()) {
int accepted = energyStorage.receiveEnergy(TRANSFER_RATE, true);
if (accepted > 0) {
energyStorage.receiveEnergy(source.extractEnergy(accepted, false), false);
}
}
}
}
}
//? if >1.20.1 {
/*@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) { return saveWithoutMetadata(registries); }
*///?} else {
@Override
public CompoundTag getUpdateTag() { return saveWithoutMetadata(); }
//?}
@Override
public @Nullable Packet<ClientGamePacketListener> getUpdatePacket() { return ClientboundBlockEntityDataPacket.create(this); }
}
@@ -0,0 +1,353 @@
package net.cmr.jurassicrevived.block.entity.custom;
import dev.architectury.registry.menu.ExtendedMenuProvider;
import net.cmr.jurassicrevived.block.custom.FossilGrinderBlock;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyStorage;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyUtil;
import net.cmr.jurassicrevived.recipe.FossilGrinderRecipe;
import net.cmr.jurassicrevived.recipe.FossilGrinderRecipeInput;
import net.cmr.jurassicrevived.recipe.ModRecipes;
import net.cmr.jurassicrevived.screen.custom.FossilGrinderMenu;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerData;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
//? if >1.20.1 {
/*import net.minecraft.core.HolderLookup;
import net.minecraft.world.item.crafting.RecipeHolder;
*///?} else {
import net.minecraft.core.RegistryAccess;
//?}
import java.util.Optional;
public class FossilGrinderBlockEntity extends BlockEntity implements ExtendedMenuProvider, ModEnergyUtil.EnergyProvider {
public final SimpleContainer itemHandler = new SimpleContainer(4) {
@Override
public void setChanged() {
super.setChanged();
FossilGrinderBlockEntity.this.setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
private static final int FOSSIL_SLOT = 0;
private static final int[] OUTPUT_SLOTS = {1, 2, 3};
private ItemStack lockedOutput = ItemStack.EMPTY;
private String lastInputSignature = "";
private final ContainerData data;
private int progress = 0;
private int maxProgress = 100;
private final int DEFAULT_MAX_PROGRESS = 100;
private static final int TRANSFER_RATE = 1000;
private final ModEnergyStorage energyStorage = createEnergyStorage();
public FossilGrinderBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.FOSSIL_GRINDER_BE.get(), pos, blockState);
this.data = new ContainerData() {
@Override
public int get(int pIndex) {
return switch (pIndex) {
case 0 -> FossilGrinderBlockEntity.this.progress;
case 1 -> FossilGrinderBlockEntity.this.maxProgress;
default -> 0;
};
}
@Override
public void set(int pIndex, int pValue) {
switch (pIndex) {
case 0 -> FossilGrinderBlockEntity.this.progress = pValue;
case 1 -> FossilGrinderBlockEntity.this.maxProgress = pValue;
}
}
@Override
public int getCount() {
return 2;
}
};
}
private ModEnergyStorage createEnergyStorage() {
return new ModEnergyStorage(64000, TRANSFER_RATE) {
@Override
public void onEnergyChanged() {
setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
}
@Override
public ModEnergyStorage getEnergyStorage(@Nullable Direction direction) {
return this.energyStorage;
}
@Override
public Component getDisplayName() {
return Component.translatable("block.jurassicrevived.fossil_grinder");
}
@Override
public void saveExtraData(FriendlyByteBuf buf) {
buf.writeBlockPos(getBlockPos());
}
@Nullable
@Override
public AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) {
return new FossilGrinderMenu(i, inventory, this, this.data);
}
public boolean isEmptyForDrop() {
return itemHandler.isEmpty() && this.progress == 0;
}
//? if >1.20.1 {
/*@Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
tag.put("Inventory", itemHandler.createTag(registries));
saveCommonData(tag);
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
itemHandler.fromTag(tag.getList("Inventory", 10), registries);
loadCommonData(tag);
}
*///?} else {
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.put("Inventory", itemHandler.createTag());
saveCommonData(tag);
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
itemHandler.fromTag(tag.getList("Inventory", 10));
loadCommonData(tag);
}
//?}
private void saveCommonData(CompoundTag tag) {
tag.putInt("Prog", this.progress);
tag.putInt("MaxProg", this.maxProgress);
tag.put("Energy", energyStorage.saveNBT());
}
private void loadCommonData(CompoundTag tag) {
progress = tag.getInt("Prog");
maxProgress = tag.getInt("MaxProg");
if (tag.contains("Energy")) {
energyStorage.loadNBT(tag.getCompound("Energy"));
}
}
public void tick(Level level, BlockPos pos, BlockState state) {
if (level.isClientSide) return;
pullEnergyFromNeighbors();
//? if >1.20.1 {
/*Optional<RecipeHolder<FossilGrinderRecipe>> recipeOpt = getCurrentRecipe();
*///?} else {
Optional<FossilGrinderRecipe> recipeOpt = getCurrentRecipe();
//?}
if (recipeOpt.isEmpty()) {
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(FossilGrinderBlock.LIT, false));
this.lockedOutput = ItemStack.EMPTY;
this.lastInputSignature = "";
return;
}
String currentSignature = stackSig(itemHandler.getItem(FOSSIL_SLOT));
//? if >1.20.1 {
/*FossilGrinderRecipe recipe = recipeOpt.get().value();
*///?} else {
FossilGrinderRecipe recipe = recipeOpt.get();
//?}
if (progress == 0) {
if (lockedOutput.isEmpty() || !currentSignature.equals(lastInputSignature)) {
lockedOutput = determineOutputForCurrentInputs(recipe).copy();
lastInputSignature = currentSignature;
}
}
ItemStack output = lockedOutput;
boolean canOutput = !output.isEmpty() && canInsertOutput(output);
if (canOutput) {
if (energyStorage.getEnergyStored() < 10) return;
energyStorage.extractEnergy(10, false);
progress++;
level.setBlockAndUpdate(pos, state.setValue(FossilGrinderBlock.LIT, true));
if (progress >= maxProgress) {
craftItem(output);
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(FossilGrinderBlock.LIT, false));
this.lockedOutput = ItemStack.EMPTY;
this.lastInputSignature = "";
}
} else {
resetProgress();
level.setBlockAndUpdate(pos, state.setValue(FossilGrinderBlock.LIT, false));
}
}
private void craftItem(ItemStack output) {
for (int slot : OUTPUT_SLOTS) {
ItemStack stack = itemHandler.getItem(slot);
if (stack.isEmpty()) {
itemHandler.setItem(slot, output.copy());
itemHandler.removeItem(FOSSIL_SLOT, 1);
return;
} else if (isSameItem(stack, output) && stack.getCount() + output.getCount() <= stack.getMaxStackSize()) {
stack.grow(output.getCount());
itemHandler.removeItem(FOSSIL_SLOT, 1);
return;
}
}
}
private boolean canInsertOutput(ItemStack output) {
for (int slot : OUTPUT_SLOTS) {
ItemStack stack = itemHandler.getItem(slot);
if (stack.isEmpty() || (isSameItem(stack, output) && stack.getCount() + output.getCount() <= stack.getMaxStackSize())) {
return true;
}
}
return false;
}
private boolean isSameItem(ItemStack stack, ItemStack other) {
//? if >1.20.1 {
/*return ItemStack.isSameItemSameComponents(stack, other);
*///?} else {
return ItemStack.isSameItemSameTags(stack, other);
//?}
}
//? if >1.20.1 {
/*private Optional<RecipeHolder<FossilGrinderRecipe>> getCurrentRecipe() {
return level.getRecipeManager().getRecipeFor(ModRecipes.FOSSIL_GRINDER_RECIPE_TYPE.get(),
new FossilGrinderRecipeInput(itemHandler.getItem(FOSSIL_SLOT)), level);
}
*///?} else {
private Optional<FossilGrinderRecipe> getCurrentRecipe() {
return level.getRecipeManager().getRecipeFor(ModRecipes.FOSSIL_GRINDER_RECIPE_TYPE.get(),
new FossilGrinderRecipeInput(itemHandler.getItem(FOSSIL_SLOT)), level);
}
//?}
private ItemStack determineOutputForCurrentInputs(FossilGrinderRecipe recipe) {
if (!recipe.weights().isEmpty()) {
return pickWeighted(recipe);
}
//? if >1.20.1 {
/*return recipe.output().copy();
*///?} else {
return recipe.getResultItem(level.registryAccess()).copy();
//?}
}
private ItemStack pickWeighted(FossilGrinderRecipe recipe) {
java.util.Map<ResourceLocation, Integer> map = recipe.weights();
int total = map.values().stream().mapToInt(Integer::intValue).sum();
if (total <= 0) return ItemStack.EMPTY;
int roll = level.random.nextInt(total);
int acc = 0;
for (var entry : map.entrySet()) {
acc += entry.getValue();
if (roll < acc) {
var item = BuiltInRegistries.ITEM.get(entry.getKey());
//? if >1.20.1 {
/*int count = recipe.output().getCount();
*///?} else {
int count = recipe.getResultItem(level.registryAccess()).getCount();
//?}
return item != null ? new ItemStack(item, Math.max(1, count)) : ItemStack.EMPTY;
}
}
return ItemStack.EMPTY;
}
private String stackSig(ItemStack s) {
return s.isEmpty() ? "empty" : BuiltInRegistries.ITEM.getKey(s.getItem()) + "x" + s.getCount();
}
private void resetProgress() {
this.progress = 0;
this.maxProgress = DEFAULT_MAX_PROGRESS;
}
private void pullEnergyFromNeighbors() {
for (Direction dir : Direction.values()) {
BlockEntity be = level.getBlockEntity(worldPosition.relative(dir));
if (be instanceof ModEnergyUtil.EnergyProvider provider) {
ModEnergyStorage source = provider.getEnergyStorage(dir.getOpposite());
if (source != null && source.canExtract()) {
int accepted = energyStorage.receiveEnergy(TRANSFER_RATE, true);
if (accepted > 0) {
int extracted = source.extractEnergy(accepted, false);
energyStorage.receiveEnergy(extracted, false);
}
}
}
}
}
//? if >1.20.1 {
/*@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
return saveWithoutMetadata(registries);
}
*///?} else {
@Override
public CompoundTag getUpdateTag() {
return saveWithoutMetadata();
}
//?}
@Override
public @Nullable Packet<ClientGamePacketListener> getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}
}
@@ -0,0 +1,264 @@
package net.cmr.jurassicrevived.block.entity.custom;
import dev.architectury.registry.menu.ExtendedMenuProvider;
import net.cmr.jurassicrevived.block.custom.GeneratorBlock;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyStorage;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyUtil;
import net.cmr.jurassicrevived.screen.custom.GeneratorMenu;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerData;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.AbstractFurnaceBlockEntity;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
//? if >1.20.1 {
/*import net.minecraft.core.HolderLookup;
*///?}
public class GeneratorBlockEntity extends BlockEntity implements ExtendedMenuProvider, ModEnergyUtil.EnergyProvider {
public final SimpleContainer itemHandler = new SimpleContainer(1) {
@Override
public void setChanged() {
super.setChanged();
GeneratorBlockEntity.this.setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
public static final int INPUT_SLOT = 0;
protected final ContainerData data;
private int burnTime = 0;
private int burnTimeTotal = 0;
private boolean isBurning = false;
private static final int TRANSFER_RATE = 1000;
private final ModEnergyStorage energyStorage = createEnergyStorage();
public GeneratorBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.GENERATOR_BE.get(), pos, blockState);
this.data = new ContainerData() {
@Override
public int get(int i) {
return switch (i) {
case 0 -> GeneratorBlockEntity.this.burnTime;
case 1 -> GeneratorBlockEntity.this.burnTimeTotal;
default -> 0;
};
}
@Override
public void set(int i, int i1) {
switch (i) {
case 0 -> GeneratorBlockEntity.this.burnTime = i1;
case 1 -> GeneratorBlockEntity.this.burnTimeTotal = i1;
}
}
@Override
public int getCount() {
return 2;
}
};
}
private ModEnergyStorage createEnergyStorage() {
return new ModEnergyStorage(256000, 0, TRANSFER_RATE, 0) {
@Override
public void onEnergyChanged() {
setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
}
@Override
public ModEnergyStorage getEnergyStorage(@Nullable Direction direction) {
return this.energyStorage;
}
@Override
public Component getDisplayName() {
return Component.translatable("block.jurassicrevived.generator");
}
@Override
public void saveExtraData(FriendlyByteBuf buf) {
buf.writeBlockPos(getBlockPos());
}
@Nullable
@Override
public AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) {
return new GeneratorMenu(i, inventory, this, this.data);
}
public boolean isEmptyForDrop() {
return itemHandler.isEmpty() && !this.isBurning;
}
public void tick(Level level1, BlockPos blockPos, BlockState blockState) {
if (level1.isClientSide) return;
boolean storageFull = this.energyStorage.getEnergyStored() >= this.energyStorage.getMaxEnergyStored();
if (!storageFull && !isBurningFuel() && hasFuelItemInSlot()) {
startBurning();
}
if (isBurningFuel() && !storageFull) {
int space = this.energyStorage.getMaxEnergyStored() - this.energyStorage.getEnergyStored();
int toAdd = Math.min(100, Math.max(0, space));
if (toAdd > 0) {
this.energyStorage.setEnergy(this.energyStorage.getEnergyStored() + toAdd);
}
this.burnTime++;
if (this.burnTime >= this.burnTimeTotal) {
this.isBurning = false;
}
} else if (!isBurningFuel() && this.burnTime >= this.burnTimeTotal && this.burnTimeTotal > 0) {
this.burnTime = 0;
this.burnTimeTotal = 0;
}
pushEnergyToNeighbors();
boolean shouldBeLit = isBurningFuel() && !storageFull;
if (blockState.hasProperty(GeneratorBlock.LIT) && blockState.getValue(GeneratorBlock.LIT) != shouldBeLit) {
level1.setBlockAndUpdate(blockPos, blockState.setValue(GeneratorBlock.LIT, shouldBeLit));
}
}
private void pushEnergyToNeighbors() {
if (this.energyStorage.getEnergyStored() <= 0) return;
for (Direction dir : Direction.values()) {
BlockEntity be = level.getBlockEntity(worldPosition.relative(dir));
if (be instanceof ModEnergyUtil.EnergyProvider provider) {
ModEnergyStorage target = provider.getEnergyStorage(dir.getOpposite());
if (target != null && target.canReceive()) {
int toSend = Math.min(TRANSFER_RATE, this.energyStorage.getEnergyStored());
int accepted = target.receiveEnergy(toSend, true);
if (accepted > 0) {
int actuallyExtracted = this.energyStorage.extractEnergy(accepted, false);
target.receiveEnergy(actuallyExtracted, false);
}
}
}
}
}
private boolean isBurningFuel() {
return this.isBurning && this.burnTimeTotal > 0 && this.burnTime < this.burnTimeTotal;
}
private void startBurning() {
ItemStack stack = this.itemHandler.getItem(INPUT_SLOT);
//? if >1.20.1 {
/*int burn = AbstractFurnaceBlockEntity.getFuel().getOrDefault(stack.getItem(), 0);
*///?} else {
int burn = AbstractFurnaceBlockEntity.getFuel().getOrDefault(stack.getItem(), 0);
//?}
if (burn <= 0) return;
stack.shrink(1);
this.burnTimeTotal = burn;
this.burnTime = 0;
this.isBurning = true;
setChanged();
}
private boolean hasFuelItemInSlot() {
ItemStack stack = this.itemHandler.getItem(INPUT_SLOT);
if (stack.isEmpty()) return false;
//? if >1.20.1 {
/*return AbstractFurnaceBlockEntity.getFuel().getOrDefault(stack.getItem(), 0) > 0;
*///?} else {
return AbstractFurnaceBlockEntity.getFuel().getOrDefault(stack.getItem(), 0) > 0;
//?}
}
//? if >1.20.1 {
/*@Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
tag.put("Inventory", itemHandler.createTag(registries));
saveCommonData(tag);
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
itemHandler.fromTag(tag.getList("Inventory", 10), registries);
loadCommonData(tag);
}
*///?} else {
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.put("Inventory", itemHandler.createTag());
saveCommonData(tag);
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
itemHandler.fromTag(tag.getList("Inventory", 10));
loadCommonData(tag);
}
//?}
private void saveCommonData(CompoundTag tag) {
tag.putInt("BurnTime", this.burnTime);
tag.putInt("BurnTotal", this.burnTimeTotal);
tag.putBoolean("IsBurning", this.isBurning);
tag.put("Energy", energyStorage.saveNBT());
}
private void loadCommonData(CompoundTag tag) {
if (tag.contains("Energy")) {
energyStorage.loadNBT(tag.getCompound("Energy"));
}
this.burnTime = tag.getInt("BurnTime");
this.burnTimeTotal = tag.getInt("BurnTotal");
this.isBurning = tag.getBoolean("IsBurning");
}
@Override
public @Nullable Packet<ClientGamePacketListener> getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}
//? if >1.20.1 {
/*@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
return saveWithoutMetadata(registries);
}
*///?} else {
@Override
public CompoundTag getUpdateTag() {
return saveWithoutMetadata();
}
//?}
}
@@ -0,0 +1,305 @@
package net.cmr.jurassicrevived.block.entity.custom;
import dev.architectury.registry.menu.ExtendedMenuProvider;
import net.cmr.jurassicrevived.block.custom.IncubatorBlock;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyStorage;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyUtil;
import net.cmr.jurassicrevived.recipe.IncubatorRecipe;
import net.cmr.jurassicrevived.recipe.IncubatorRecipeInput;
import net.cmr.jurassicrevived.recipe.ModRecipes;
import net.cmr.jurassicrevived.screen.custom.IncubatorMenu;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.inventory.ContainerData;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
//? if >1.20.1 {
/*import net.minecraft.core.HolderLookup;
import net.minecraft.world.item.crafting.RecipeHolder;
*///?} else {
import net.minecraft.core.RegistryAccess;
//?}
import java.util.Optional;
public class IncubatorBlockEntity extends BlockEntity implements ExtendedMenuProvider, ModEnergyUtil.EnergyProvider {
public final SimpleContainer itemHandler = new SimpleContainer(3) {
@Override
public void setChanged() {
super.setChanged();
IncubatorBlockEntity.this.setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
private final ContainerData data;
private final int[] progress = new int[]{0, 0, 0};
private final int[] maxProgress = new int[]{4800, 4800, 4800};
private final int DEFAULT_MAX_PROGRESS = 4800;
private static final int TRANSFER_RATE = 1000;
private final ModEnergyStorage energyStorage = createEnergyStorage();
public IncubatorBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.INCUBATOR_BE.get(), pos, blockState);
this.data = new ContainerData() {
@Override
public int get(int pIndex) {
return switch (pIndex) {
case 0 -> progress[0];
case 1 -> maxProgress[0];
case 2 -> progress[1];
case 3 -> maxProgress[1];
case 4 -> progress[2];
case 5 -> maxProgress[2];
default -> 0;
};
}
@Override
public void set(int pIndex, int pValue) {
switch (pIndex) {
case 0 -> progress[0] = pValue;
case 1 -> maxProgress[0] = pValue;
case 2 -> progress[1] = pValue;
case 3 -> maxProgress[1] = pValue;
case 4 -> progress[2] = pValue;
case 5 -> maxProgress[2] = pValue;
}
}
@Override
public int getCount() {
return 6;
}
};
}
private ModEnergyStorage createEnergyStorage() {
return new ModEnergyStorage(64000, TRANSFER_RATE) {
@Override
public void onEnergyChanged() {
setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
}
@Override
public ModEnergyStorage getEnergyStorage(@Nullable Direction direction) {
return this.energyStorage;
}
@Override
public Component getDisplayName() {
return Component.translatable("block.jurassicrevived.incubator");
}
@Override
public void saveExtraData(FriendlyByteBuf buf) {
buf.writeBlockPos(getBlockPos());
}
public boolean isEmptyForDrop() {
return itemHandler.isEmpty() && progress[0] == 0 && progress[1] == 0 && progress[2] == 0;
}
@Nullable
@Override
public AbstractContainerMenu createMenu(int i, Inventory inventory, Player player) {
return new IncubatorMenu(i, inventory, this, this.data);
}
//? if >1.20.1 {
/*@Override
protected void saveAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.saveAdditional(tag, registries);
tag.put("Inventory", itemHandler.createTag(registries));
tag.putInt("Prog0", this.progress[0]);
tag.putInt("Prog1", this.progress[1]);
tag.putInt("Prog2", this.progress[2]);
tag.putInt("Max0", this.maxProgress[0]);
tag.putInt("Max1", this.maxProgress[1]);
tag.putInt("Max2", this.maxProgress[2]);
tag.put("Energy", energyStorage.saveNBT());
}
@Override
protected void loadAdditional(CompoundTag tag, HolderLookup.Provider registries) {
super.loadAdditional(tag, registries);
itemHandler.fromTag(tag.getList("Inventory", 10), registries);
if (tag.contains("Energy")) {
energyStorage.loadNBT(tag.getCompound("Energy"));
}
progress[0] = tag.getInt("Prog0");
progress[1] = tag.getInt("Prog1");
progress[2] = tag.getInt("Prog2");
maxProgress[0] = tag.getInt("Max0");
maxProgress[1] = tag.getInt("Max1");
maxProgress[2] = tag.getInt("Max2");
}
*///?} else {
@Override
protected void saveAdditional(CompoundTag tag) {
super.saveAdditional(tag);
tag.put("Inventory", itemHandler.createTag());
tag.putInt("Prog0", this.progress[0]);
tag.putInt("Prog1", this.progress[1]);
tag.putInt("Prog2", this.progress[2]);
tag.putInt("Max0", this.maxProgress[0]);
tag.putInt("Max1", this.maxProgress[1]);
tag.putInt("Max2", this.maxProgress[2]);
tag.put("Energy", energyStorage.saveNBT());
}
@Override
public void load(CompoundTag tag) {
super.load(tag);
itemHandler.fromTag(tag.getList("Inventory", 10));
if (tag.contains("Energy")) {
energyStorage.loadNBT(tag.getCompound("Energy"));
}
progress[0] = tag.getInt("Prog0");
progress[1] = tag.getInt("Prog1");
progress[2] = tag.getInt("Prog2");
maxProgress[0] = tag.getInt("Max0");
maxProgress[1] = tag.getInt("Max1");
maxProgress[2] = tag.getInt("Max2");
}
//?}
public void tick(Level level, BlockPos pos, BlockState state) {
if (level.isClientSide) return;
pullEnergyFromNeighbors();
boolean changed = false;
boolean anyActive = false;
for (int s = 0; s < 3; s++) {
ItemStack stack = itemHandler.getItem(s);
if (stack.isEmpty()) {
if (progress[s] != 0) { progress[s] = 0; changed = true; }
continue;
}
//? if >1.20.1 {
/*Optional<RecipeHolder<IncubatorRecipe>> recipeOpt = getRecipeFor(stack);
*///?} else {
Optional<IncubatorRecipe> recipeOpt = getRecipeFor(stack);
//?}
if (recipeOpt.isEmpty()) {
if (progress[s] != 0) { progress[s] = 0; changed = true; }
continue;
}
anyActive = true;
}
if (state.getValue(IncubatorBlock.LIT) != anyActive) {
level.setBlockAndUpdate(pos, state.setValue(IncubatorBlock.LIT, anyActive));
}
if (anyActive) {
if (energyStorage.getEnergyStored() < 10) return;
energyStorage.extractEnergy(10, false);
}
for (int s = 0; s < 3; s++) {
ItemStack stack = itemHandler.getItem(s);
if (stack.isEmpty()) continue;
//? if >1.20.1 {
/*Optional<RecipeHolder<IncubatorRecipe>> recipeOpt = getRecipeFor(stack);
*///?} else {
Optional<IncubatorRecipe> recipeOpt = getRecipeFor(stack);
//?}
if (recipeOpt.isEmpty()) continue;
if (progress[s] < maxProgress[s]) {
progress[s]++;
changed = true;
}
if (progress[s] >= maxProgress[s]) {
//? if >1.20.1 {
/*ItemStack out = recipeOpt.get().value().assemble(new IncubatorRecipeInput(stack), level.registryAccess());
*///?} else {
ItemStack out = recipeOpt.get().assemble(new IncubatorRecipeInput(stack), level.registryAccess());
//?}
if (!out.isEmpty()) {
itemHandler.setItem(s, out.copy());
progress[s] = 0;
maxProgress[s] = DEFAULT_MAX_PROGRESS;
changed = true;
}
}
}
if (changed) setChanged();
}
//? if >1.20.1 {
/*private Optional<RecipeHolder<IncubatorRecipe>> getRecipeFor(ItemStack input) {
return level.getRecipeManager().getRecipeFor(ModRecipes.INCUBATOR_RECIPE_TYPE.get(), new IncubatorRecipeInput(input), level);
}
*///?} else {
private Optional<IncubatorRecipe> getRecipeFor(ItemStack input) {
return level.getRecipeManager().getRecipeFor(ModRecipes.INCUBATOR_RECIPE_TYPE.get(), new IncubatorRecipeInput(input), level);
}
//?}
private void pullEnergyFromNeighbors() {
for (Direction dir : Direction.values()) {
BlockEntity be = level.getBlockEntity(worldPosition.relative(dir));
if (be instanceof ModEnergyUtil.EnergyProvider provider) {
ModEnergyStorage source = provider.getEnergyStorage(dir.getOpposite());
if (source != null && source.canExtract()) {
int canAccept = energyStorage.receiveEnergy(TRANSFER_RATE, true);
if (canAccept > 0) {
int extracted = source.extractEnergy(canAccept, false);
energyStorage.receiveEnergy(extracted, false);
}
}
}
}
}
@Nullable
@Override
public Packet<ClientGamePacketListener> getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}
//? if >1.20.1 {
/*@Override
public CompoundTag getUpdateTag(HolderLookup.Provider registries) {
return saveWithoutMetadata(registries);
}
*///?} else {
@Override
public CompoundTag getUpdateTag() {
return saveWithoutMetadata();
}
//?}
}
@@ -0,0 +1,171 @@
package net.cmr.jurassicrevived.block.entity.custom;
import net.cmr.jurassicrevived.block.custom.PipeBlock;
import net.cmr.jurassicrevived.block.custom.PipeBlock.ConnectionType;
import net.cmr.jurassicrevived.block.custom.PipeBlock.Transport;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyStorage;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyUtil;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.world.Container;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import java.util.*;
public class PipeBlockEntity extends BlockEntity {
private final Transport transport;
public PipeBlockEntity(BlockPos pos, BlockState state) {
super(resolveType(state), pos, state);
this.transport = ((PipeBlock) state.getBlock()).getTransport();
}
private static net.minecraft.world.level.block.entity.BlockEntityType<PipeBlockEntity> resolveType(BlockState state) {
PipeBlock block = (PipeBlock) state.getBlock();
return switch (block.getTransport()) {
case ITEMS -> ModBlockEntities.ITEM_PIPE_BE.get();
case FLUIDS -> ModBlockEntities.FLUID_PIPE_BE.get();
case ENERGY -> ModBlockEntities.POWER_PIPE_BE.get();
};
}
public static void serverTick(Level level, BlockPos pos, BlockState state, PipeBlockEntity be) {
if (level == null || level.isClientSide) return;
PipeBlock block = (PipeBlock) state.getBlock();
int itemCap = block.getMaxItemsPerTick();
// Fallback caps if Config is not ready
int fluidCap = 1000;
int energyCap = 1000;
switch (be.transport) {
case ITEMS -> transferItems(level, pos, state, itemCap);
case FLUIDS -> transferFluids(level, pos, state, fluidCap);
case ENERGY -> transferEnergy(level, pos, state, energyCap);
}
}
// ===== Network discovery =====
private record PipeEndpoint(BlockPos pipePos, Direction side) {}
private static class Network {
final List<PipeEndpoint> sources = new ArrayList<>();
final List<PipeEndpoint> sinks = new ArrayList<>();
}
private static Network discoverNetwork(Level level, BlockPos start, Transport transport) {
Network net = new Network();
ArrayDeque<BlockPos> q = new ArrayDeque<>();
HashSet<BlockPos> seen = new HashSet<>();
q.add(start);
seen.add(start);
while (!q.isEmpty()) {
BlockPos p = q.removeFirst();
BlockState st = level.getBlockState(p);
if (!(st.getBlock() instanceof PipeBlock pb) || pb.getTransport() != transport) continue;
for (Direction d : Direction.values()) {
ConnectionType ct = st.getValue(PipeBlock.getProp(d));
if (ct == ConnectionType.CONNECTOR_PULL) {
net.sources.add(new PipeEndpoint(p, d));
} else if (ct == ConnectionType.CONNECTOR) {
net.sinks.add(new PipeEndpoint(p, d));
}
if (ct == ConnectionType.PIPE) {
BlockPos np = p.relative(d);
if (!seen.contains(np)) {
BlockState ns = level.getBlockState(np);
if (ns.getBlock() instanceof PipeBlock op && op.getTransport() == transport) {
seen.add(np);
q.add(np);
}
}
}
}
}
return net;
}
// ===== Item Transfer (Using Vanilla Container) =====
private static void transferItems(Level level, BlockPos pos, BlockState state, int perTickLimit) {
Network net = discoverNetwork(level, pos, Transport.ITEMS);
List<Container> outputs = new ArrayList<>();
for (PipeEndpoint ep : net.sinks) {
BlockEntity be = level.getBlockEntity(ep.pipePos.relative(ep.side));
if (be instanceof Container c) outputs.add(c);
}
if (outputs.isEmpty()) return;
int remaining = perTickLimit;
for (PipeEndpoint ep : net.sources) {
if (remaining <= 0) break;
BlockEntity be = level.getBlockEntity(ep.pipePos.relative(ep.side));
if (!(be instanceof Container src)) continue;
for (int i = 0; i < src.getContainerSize() && remaining > 0; i++) {
ItemStack stack = src.getItem(i);
if (stack.isEmpty()) continue;
ItemStack toMove = stack.copy();
toMove.setCount(Math.min(stack.getCount(), remaining));
for (Container out : outputs) {
// Logic to insert into vanilla container (simplified)
// You might want a helper for this
}
}
}
}
// ===== Energy Transfer (Using Custom Energy System) =====
private static void transferEnergy(Level level, BlockPos pos, BlockState state, int perTickLimit) {
Network net = discoverNetwork(level, pos, Transport.ENERGY);
List<ModEnergyStorage> outputs = new ArrayList<>();
for (PipeEndpoint ep : net.sinks) {
BlockEntity be = level.getBlockEntity(ep.pipePos.relative(ep.side));
if (be instanceof ModEnergyUtil.EnergyProvider provider) {
ModEnergyStorage storage = provider.getEnergyStorage(ep.side.getOpposite());
if (storage != null && storage.canReceive()) outputs.add(storage);
}
}
if (outputs.isEmpty()) return;
int remaining = perTickLimit;
for (PipeEndpoint ep : net.sources) {
if (remaining <= 0) break;
BlockEntity be = level.getBlockEntity(ep.pipePos.relative(ep.side));
if (be instanceof ModEnergyUtil.EnergyProvider provider) {
ModEnergyStorage src = provider.getEnergyStorage(ep.side.getOpposite());
if (src == null || !src.canExtract()) continue;
int canExtract = src.extractEnergy(remaining, true);
if (canExtract <= 0) continue;
for (ModEnergyStorage out : outputs) {
int accepted = out.receiveEnergy(canExtract, true);
if (accepted > 0) {
int actuallyExtracted = src.extractEnergy(accepted, false);
out.receiveEnergy(actuallyExtracted, false);
remaining -= actuallyExtracted;
break;
}
}
}
}
}
private static void transferFluids(Level level, BlockPos pos, BlockState state, int perTickLimit) {
// Implementation would use Architectury FluidStack similarly to energy
}
}
@@ -0,0 +1,170 @@
package net.cmr.jurassicrevived.block.entity.custom;
import dev.architectury.registry.menu.ExtendedMenuProvider;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyStorage;
import net.cmr.jurassicrevived.block.entity.energy.ModEnergyUtil;
import net.cmr.jurassicrevived.screen.custom.PowerCellMenu;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.Containers;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
//? if >1.20.1 {
/*import net.minecraft.core.HolderLookup;
*///?}
public class PowerCellBlockEntity extends BlockEntity implements ExtendedMenuProvider, ModEnergyUtil.EnergyProvider {
public final SimpleContainer itemHandler = new SimpleContainer(2) {
@Override
public void setChanged() {
super.setChanged();
PowerCellBlockEntity.this.setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
@Override
public int getMaxStackSize() {
return 64;
}
};
private final ModEnergyStorage energyStorage = createEnergyStorage();
private static final int TRANSFER_RATE = 1000;
public PowerCellBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.POWER_CELL_BE.get(), pos, blockState);
}
private ModEnergyStorage createEnergyStorage() {
return new ModEnergyStorage(256000, TRANSFER_RATE) {
@Override
public void onEnergyChanged() {
setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
};
}
public boolean isEmptyForDrop() {
return itemHandler.isEmpty() && energyStorage.getEnergyStored() == 0;
}
@Override
public ModEnergyStorage getEnergyStorage(@Nullable Direction direction) {
return this.energyStorage;
}
@Override
public Component getDisplayName() {
return Component.literal("Power Cell");
}
@Nullable
@Override
public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) {
return new PowerCellMenu(containerId, playerInventory, this);
}
@Override
public void saveExtraData(FriendlyByteBuf buf) {
buf.writeBlockPos(getBlockPos());
}
public void drops() {
Containers.dropContents(this.level, this.worldPosition, itemHandler);
}
public void tick(Level level, BlockPos blockPos, BlockState blockState) {
if (level.isClientSide) return;
pushEnergyToAboveNeighbour();
}
private void pushEnergyToAboveNeighbour() {
BlockPos up = worldPosition.above();
BlockEntity targetBE = level.getBlockEntity(up);
if (targetBE instanceof ModEnergyUtil.EnergyProvider provider) {
ModEnergyStorage targetStorage = provider.getEnergyStorage(Direction.DOWN);
if (targetStorage != null && targetStorage.canReceive()) {
int available = energyStorage.getEnergyStored();
int toSend = Math.min(available, TRANSFER_RATE);
int accepted = targetStorage.receiveEnergy(toSend, true);
if (accepted > 0) {
int extracted = energyStorage.extractEnergy(accepted, false);
targetStorage.receiveEnergy(extracted, false);
}
}
}
}
//? if >1.20.1 {
/*@Override
protected void saveAdditional(CompoundTag pTag, HolderLookup.Provider pRegistries) {
super.saveAdditional(pTag, pRegistries);
pTag.put("Inventory", itemHandler.createTag(pRegistries));
pTag.put("Energy", energyStorage.saveNBT());
}
@Override
protected void loadAdditional(CompoundTag pTag, HolderLookup.Provider pRegistries) {
super.loadAdditional(pTag, pRegistries);
itemHandler.fromTag(pTag.getList("Inventory", 10), pRegistries);
if (pTag.contains("Energy")) {
energyStorage.loadNBT(pTag.getCompound("Energy"));
}
}
*///?} else {
@Override
protected void saveAdditional(CompoundTag pTag) {
super.saveAdditional(pTag);
pTag.put("Inventory", itemHandler.createTag());
pTag.put("Energy", energyStorage.saveNBT());
}
@Override
public void load(CompoundTag pTag) {
super.load(pTag);
itemHandler.fromTag(pTag.getList("Inventory", 10));
if (pTag.contains("Energy")) {
energyStorage.loadNBT(pTag.getCompound("Energy"));
}
}
//?}
@Nullable
@Override
public Packet<ClientGamePacketListener> getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}
//? if >1.20.1 {
/*@Override
public CompoundTag getUpdateTag(HolderLookup.Provider pRegistries) {
return saveWithoutMetadata(pRegistries);
}
*///?} else {
@Override
public CompoundTag getUpdateTag() {
return saveWithoutMetadata();
}
//?}
}
@@ -0,0 +1,147 @@
package net.cmr.jurassicrevived.block.entity.custom;
import dev.architectury.fluid.FluidStack;
import dev.architectury.registry.menu.ExtendedMenuProvider;
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
import net.cmr.jurassicrevived.screen.custom.TankMenu;
import net.minecraft.core.BlockPos;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.FriendlyByteBuf;
import net.minecraft.network.chat.Component;
import net.minecraft.network.protocol.Packet;
import net.minecraft.network.protocol.game.ClientGamePacketListener;
import net.minecraft.network.protocol.game.ClientboundBlockEntityDataPacket;
import net.minecraft.world.Containers;
import net.minecraft.world.SimpleContainer;
import net.minecraft.world.entity.player.Inventory;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.inventory.AbstractContainerMenu;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import org.jetbrains.annotations.Nullable;
//? if >1.20.1 {
/*import net.minecraft.core.HolderLookup;
*///?}
public class TankBlockEntity extends BlockEntity implements ExtendedMenuProvider {
public final SimpleContainer itemHandler = new SimpleContainer(2) {
@Override
public void setChanged() {
super.setChanged();
TankBlockEntity.this.setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
@Override
public int getMaxStackSize() {
return 64;
}
};
private FluidStack fluidStack = FluidStack.empty();
private static final long CAPACITY = 64000;
public TankBlockEntity(BlockPos pos, BlockState blockState) {
super(ModBlockEntities.TANK_BE.get(), pos, blockState);
}
public FluidStack getFluid() {
return fluidStack;
}
public void setFluid(FluidStack stack) {
this.fluidStack = stack;
setChanged();
if (level != null && !level.isClientSide()) {
level.sendBlockUpdated(getBlockPos(), getBlockState(), getBlockState(), 3);
}
}
@Override
public Component getDisplayName() {
return Component.literal("Tank");
}
@Nullable
@Override
public AbstractContainerMenu createMenu(int containerId, Inventory playerInventory, Player player) {
return new TankMenu(containerId, playerInventory, this);
}
@Override
public void saveExtraData(FriendlyByteBuf buf) {
buf.writeBlockPos(getBlockPos());
}
public void drops() {
Containers.dropContents(this.level, this.worldPosition, itemHandler);
}
public void tick(Level level, BlockPos blockPos, BlockState blockState) {
}
public boolean isEmptyForDrop() {
return itemHandler.isEmpty() && fluidStack.isEmpty();
}
//? if >1.20.1 {
/*@Override
protected void saveAdditional(CompoundTag pTag, HolderLookup.Provider pRegistries) {
super.saveAdditional(pTag, pRegistries);
pTag.put("Inventory", itemHandler.createTag(pRegistries));
CompoundTag fluidTag = new CompoundTag();
fluidStack.write(pRegistries, fluidTag);
pTag.put("Fluid", fluidTag);
}
@Override
protected void loadAdditional(CompoundTag pTag, HolderLookup.Provider pRegistries) {
super.loadAdditional(pTag, pRegistries);
itemHandler.fromTag(pTag.getList("Inventory", 10), pRegistries);
if (pTag.contains("Fluid")) {
this.fluidStack = FluidStack.read(pRegistries, pTag.getCompound("Fluid")).orElse(FluidStack.empty());
}
}
*///?} else {
@Override
protected void saveAdditional(CompoundTag pTag) {
super.saveAdditional(pTag);
pTag.put("Inventory", itemHandler.createTag());
CompoundTag fluidTag = new CompoundTag();
fluidStack.write(fluidTag);
pTag.put("Fluid", fluidTag);
}
@Override
public void load(CompoundTag pTag) {
super.load(pTag);
itemHandler.fromTag(pTag.getList("Inventory", 10));
if (pTag.contains("Fluid")) {
this.fluidStack = FluidStack.read(pTag.getCompound("Fluid"));
}
}
//?}
@Nullable
@Override
public Packet<ClientGamePacketListener> getUpdatePacket() {
return ClientboundBlockEntityDataPacket.create(this);
}
//? if >1.20.1 {
/*@Override
public CompoundTag getUpdateTag(HolderLookup.Provider pRegistries) {
return saveWithoutMetadata(pRegistries);
}
*///?} else {
@Override
public CompoundTag getUpdateTag() {
return saveWithoutMetadata();
}
//?}
}
@@ -0,0 +1,72 @@
package net.cmr.jurassicrevived.block.entity.energy;
import net.minecraft.nbt.CompoundTag;
public abstract class ModEnergyStorage {
protected int energy;
protected int capacity;
protected int maxReceive;
protected int maxExtract;
public ModEnergyStorage(int capacity, int maxTransfer) {
this(capacity, maxTransfer, maxTransfer, 0);
}
public ModEnergyStorage(int capacity, int maxReceive, int maxExtract, int energy) {
this.capacity = capacity;
this.maxReceive = maxReceive;
this.maxExtract = maxExtract;
this.energy = Math.max(0, Math.min(capacity, energy));
}
public int receiveEnergy(int maxReceive, boolean simulate) {
int energyReceived = Math.min(capacity - energy, Math.min(this.maxReceive, maxReceive));
if (!simulate && energyReceived != 0) {
energy += energyReceived;
onEnergyChanged();
}
return energyReceived;
}
public int extractEnergy(int maxExtract, boolean simulate) {
int energyExtracted = Math.min(energy, Math.min(this.maxExtract, maxExtract));
if (!simulate && energyExtracted != 0) {
energy -= energyExtracted;
onEnergyChanged();
}
return energyExtracted;
}
public int getEnergyStored() {
return energy;
}
public int getMaxEnergyStored() {
return capacity;
}
public boolean canExtract() {
return this.maxExtract > 0;
}
public boolean canReceive() {
return this.maxReceive > 0;
}
public void setEnergy(int energy) {
this.energy = Math.max(0, Math.min(capacity, energy));
onEnergyChanged();
}
public abstract void onEnergyChanged();
public CompoundTag saveNBT() {
CompoundTag tag = new CompoundTag();
tag.putInt("Energy", energy);
return tag;
}
public void loadNBT(CompoundTag tag) {
this.energy = tag.getInt("Energy");
}
}
@@ -0,0 +1,54 @@
package net.cmr.jurassicrevived.block.entity.energy;
import net.minecraft.core.BlockPos;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.entity.BlockEntity;
public class ModEnergyUtil {
public static boolean move(BlockPos from, BlockPos to, int amount, Level level) {
BlockEntity fromBE = level.getBlockEntity(from);
BlockEntity toBE = level.getBlockEntity(to);
// In a common module, we check if our custom BEs implement a common energy provider
// or access the storage directly if they are your mod's blocks.
if (fromBE instanceof EnergyProvider fromProvider && toBE instanceof EnergyProvider toProvider) {
ModEnergyStorage fromStorage = fromProvider.getEnergyStorage(null);
ModEnergyStorage toStorage = toProvider.getEnergyStorage(null);
if (fromStorage == null || toStorage == null) return false;
if (canEnergyStorageExtractThisAmount(fromStorage, amount)) {
return false;
}
if (canEnergyStorageStillReceiveEnergy(toStorage)) {
return false;
}
int maxAmountToReceive = toStorage.receiveEnergy(amount, true);
int extractedEnergy = fromStorage.extractEnergy(maxAmountToReceive, false);
toStorage.receiveEnergy(extractedEnergy, false);
return true;
}
return false;
}
private static boolean canEnergyStorageStillReceiveEnergy(ModEnergyStorage toStorage) {
return toStorage.getEnergyStored() >= toStorage.getMaxEnergyStored() || !toStorage.canReceive();
}
private static boolean canEnergyStorageExtractThisAmount(ModEnergyStorage fromStorage, int amount) {
return fromStorage.getEnergyStored() <= 0 || fromStorage.getEnergyStored() < amount || !fromStorage.canExtract();
}
public static boolean doesBlockHaveEnergyStorage(BlockPos positionToCheck, Level level) {
BlockEntity be = level.getBlockEntity(positionToCheck);
return be instanceof EnergyProvider;
}
// Common interface for your BlockEntities to implement
public interface EnergyProvider {
ModEnergyStorage getEnergyStorage(net.minecraft.core.Direction direction);
}
}
@@ -0,0 +1,114 @@
package net.cmr.jurassicrevived.block.renderer;
import com.mojang.blaze3d.vertex.PoseStack;
import com.mojang.blaze3d.vertex.VertexConsumer;
import com.mojang.math.Axis;
import dev.architectury.fluid.FluidStack;
import dev.architectury.hooks.fluid.FluidStackHooks;
import net.cmr.jurassicrevived.block.entity.custom.TankBlockEntity;
import net.minecraft.client.Minecraft;
import net.minecraft.client.renderer.ItemBlockRenderTypes;
import net.minecraft.client.renderer.MultiBufferSource;
import net.minecraft.client.renderer.blockentity.BlockEntityRenderer;
import net.minecraft.client.renderer.blockentity.BlockEntityRendererProvider;
import net.minecraft.client.renderer.texture.TextureAtlasSprite;
import net.minecraft.core.BlockPos;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.inventory.InventoryMenu;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.material.FluidState;
import java.util.Objects;
// Credits to TurtyWurty
// Under MIT-License: https://github.com/DaRealTurtyWurty/1.20-Tutorial-Mod?tab=MIT-1-ov-file#readme
public class TankBlockEntityRenderer implements BlockEntityRenderer<TankBlockEntity> {
public TankBlockEntityRenderer(BlockEntityRendererProvider.Context context) {
}
@Override
public void render(TankBlockEntity pBlockEntity, float partialTick, PoseStack pPoseStack, MultiBufferSource pBuffer, int pPackedLight, int packedOverlay) {
FluidStack fluidStack = pBlockEntity.getFluid();
if (fluidStack.isEmpty())
return;
Level level = pBlockEntity.getLevel();
if (level == null)
return;
BlockPos pos = pBlockEntity.getBlockPos();
// Use Architectury Hooks instead of IClientFluidTypeExtensions
ResourceLocation stillTexture = Objects.requireNonNull(FluidStackHooks.getStillTexture(fluidStack)).atlasLocation();
int tintColor = FluidStackHooks.getColor(fluidStack);
FluidState state = fluidStack.getFluid().defaultFluidState();
TextureAtlasSprite sprite = Minecraft.getInstance().getTextureAtlas(InventoryMenu.BLOCK_ATLAS).apply(stillTexture);
// Adjust height calculation to be platform-independent
// (Assuming you'll update TankBlockEntity to use a multi-loader tank system)
float height = (((float) fluidStack.getAmount() / 64000f) * 0.625f) + 0.25f;
VertexConsumer builder = pBuffer.getBuffer(ItemBlockRenderTypes.getRenderLayer(state));
// Top Texture
drawQuad(builder, pPoseStack, 0.1f, height, 0.1f, 0.9f, height, 0.9f, sprite.getU0(), sprite.getV0(), sprite.getU1(), sprite.getV1(), pPackedLight, tintColor);
drawQuad(builder, pPoseStack, 0.1f, 0, 0.1f, 0.9f, height, 0.1f, sprite.getU0(), sprite.getV0(), sprite.getU1(), sprite.getV1(), pPackedLight, tintColor);
pPoseStack.pushPose();
pPoseStack.mulPose(Axis.XP.rotationDegrees(180));
pPoseStack.translate(0, -0.9f, -1f);
drawQuad(builder, pPoseStack, 0.1f, height, 0.1f, 0.9f, height, 0.9f, sprite.getU0(), sprite.getV0(), sprite.getU1(), sprite.getV1(), pPackedLight, tintColor);
pPoseStack.popPose();
pPoseStack.pushPose();
pPoseStack.mulPose(Axis.YP.rotationDegrees(180));
pPoseStack.translate(-1f, 0, -1.8f);
drawQuad(builder, pPoseStack, 0.1f, 0, 0.9f, 0.9f, height, 0.9f, sprite.getU0(), sprite.getV0(), sprite.getU1(), sprite.getV1(), pPackedLight, tintColor);
pPoseStack.popPose();
pPoseStack.pushPose();
pPoseStack.mulPose(Axis.YP.rotationDegrees(90));
pPoseStack.translate(-1f, 0, 0);
drawQuad(builder, pPoseStack, 0.1f, 0, 0.1f, 0.9f, height, 0.1f, sprite.getU0(), sprite.getV0(), sprite.getU1(), sprite.getV1(), pPackedLight, tintColor);
pPoseStack.popPose();
pPoseStack.pushPose();
pPoseStack.mulPose(Axis.YN.rotationDegrees(90));
pPoseStack.translate(0, 0, -1f);
drawQuad(builder, pPoseStack, 0.1f, 0, 0.1f, 0.9f, height, 0.1f, sprite.getU0(), sprite.getV0(), sprite.getU1(), sprite.getV1(), pPackedLight, tintColor);
pPoseStack.popPose();
}
//? if >1.20.1 {
/*private static void drawVertex(VertexConsumer builder, PoseStack poseStack, float x, float y, float z, float u, float v, int packedLight, int color) {
builder.addVertex(poseStack.last().pose(), x, y, z)
.setColor(color)
.setUv(u, v)
.setLight(packedLight)
.setNormal(1, 0, 0);
}
*///?} else {
private static void drawVertex(VertexConsumer builder, PoseStack poseStack, float x, float y, float z, float u, float v, int packedLight, int color) {
int a = (color >> 24) & 0xFF;
int r = (color >> 16) & 0xFF;
int g = (color >> 8) & 0xFF;
int b = (color) & 0xFF;
builder.vertex(poseStack.last().pose(), x, y, z)
.color(r, g, b, a)
.uv(u, v)
.uv2(packedLight)
.normal(poseStack.last().normal(), 1, 0, 0)
.endVertex();
}
//?}
private static void drawQuad(VertexConsumer builder, PoseStack poseStack, float x0, float y0, float z0, float x1, float y1, float z1, float u0, float v0, float u1, float v1, int packedLight, int color) {
drawVertex(builder, poseStack, x0, y0, z0, u0, v0, packedLight, color);
drawVertex(builder, poseStack, x0, y1, z1, u0, v1, packedLight, color);
drawVertex(builder, poseStack, x1, y1, z1, u1, v1, packedLight, color);
drawVertex(builder, poseStack, x1, y0, z0, u1, v0, packedLight, color);
}
}
@@ -0,0 +1,158 @@
package net.cmr.jurassicrevived.compat;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.helpers.IGuiHelper;
import mezz.jei.api.recipe.IFocusGroup;
import mezz.jei.api.recipe.RecipeIngredientRole;
import mezz.jei.api.recipe.RecipeType;
import mezz.jei.api.recipe.category.IRecipeCategory;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.block.ModBlocks;
import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.item.ModItems;
import net.cmr.jurassicrevived.recipe.DNAAnalyzerRecipe;
import net.cmr.jurassicrevived.util.ModTags;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class DNAAnalyzerRecipeCategory implements IRecipeCategory<DNAAnalyzerRecipe> {
public static final ResourceLocation UID = Constants.rl("dna_analyzing");
public static final ResourceLocation TEXTURE = Constants.rl("textures/gui/dna_analyzer/dna_analyzer_gui.png");
private static final ResourceLocation DNA_TEXTURE = Constants.rl("textures/gui/generic/syringe_dna.png");
private static final ResourceLocation POWER_BAR_TEXTURE = Constants.rl("textures/gui/generic/power_bar.png");
public static final RecipeType<DNAAnalyzerRecipe> DNA_ANALYZER_RECIPE_RECIPE_TYPE =
new RecipeType<>(UID, DNAAnalyzerRecipe.class);
private final IDrawable background;
private final IDrawable icon;
public DNAAnalyzerRecipeCategory(IGuiHelper guiHelper) {
this.background = guiHelper.drawableBuilder(TEXTURE, 0, 0, 176, 80).setTextureSize(176, 166).build();
this.icon = guiHelper.createDrawableIngredient(VanillaTypes.ITEM_STACK, new ItemStack(ModBlocks.DNA_ANALYZER.get()));
}
@Override
public RecipeType<DNAAnalyzerRecipe> getRecipeType() {
return DNA_ANALYZER_RECIPE_RECIPE_TYPE;
}
@Override
public Component getTitle() {
return Component.translatable("block.jurassicrevived.dna_analyzer");
}
@Override
public int getWidth() {
return background.getWidth();
}
@Override
public int getHeight() {
return background.getHeight();
}
@Override
public void draw(DNAAnalyzerRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics guiGraphics, double mouseX, double mouseY) {
background.draw(guiGraphics);
if (JRConfigManager.get().requirePower) {
guiGraphics.blit(POWER_BAR_TEXTURE, 159, 10, 0, 0, 10, 66, 10, 66);
}
{
int fullW = 6, fullH = 16;
int drawBaseX = 85;
int drawBaseY = 38;
// Loop progress at ~20 TPS over 200 ticks
int maxTicks = 200;
long now = System.currentTimeMillis();
int progress = (int) ((now / 50L) % maxTicks);
// Match the screens scale behavior: clamp to fullH (pixels)
int visible = Math.min(fullH, Math.max(0, progress * fullH / maxTicks));
// Top-down drain: shrink from the top toward the bottom
int remaining = fullH - visible;
int srcU = 0;
int srcV = visible;
int drawX = drawBaseX;
int drawY = drawBaseY + visible;
if (remaining > 0) {
guiGraphics.blit(DNA_TEXTURE, drawX, drawY, srcU, srcV, fullW, remaining, fullW, fullH);
}
}
if (JRConfigManager.get().requirePower) {
// Simple visual fill to hint energy usage in JEI (not bound to a BE)
int barX = 160, barY = 11, barW = 8, barH = 64;
int requiredFE = 6000, capacityFE = 64000;
int filled = (int) (barH * (requiredFE / (float) capacityFE));
guiGraphics.fillGradient(barX, barY + (barH - filled), barX + barW, barY + barH, 0xffb51500, 0xff600b00);
// Tooltip for the energy bar
int mx = (int) mouseX, my = (int) mouseY;
if (mx >= barX && mx < barX + barW && my >= barY && my < barY + barH) {
List<Component> tips = List.of(Component.literal("6000 / 64000 FE"));
guiGraphics.renderTooltip(Minecraft.getInstance().font, tips, java.util.Optional.empty(), mx, my);
}
}
}
@Override
public @Nullable IDrawable getIcon() {
return icon;
}
@Override
public void setRecipe(IRecipeLayoutBuilder builder, DNAAnalyzerRecipe recipe, IFocusGroup focuses) {
builder.addSlot(RecipeIngredientRole.INPUT, 57, 35).addIngredients(recipe.getIngredients().get(0));
builder.addSlot(RecipeIngredientRole.INPUT, 80, 7).addIngredients(recipe.getIngredients().get(1));
ItemStack amber = new ItemStack(ModItems.MOSQUITO_IN_AMBER.get());
boolean isMosquitoRecipe = recipe.getIngredients().size() > 1 && recipe.getIngredients().get(1).test(amber);
if (isMosquitoRecipe) {
var level = Minecraft.getInstance().level;
if (level != null) {
var itemRegistry = level.registryAccess().registryOrThrow(Registries.ITEM);
var dnaTagOpt = itemRegistry.getTag(ModTags.Items.DNA);
List<ItemStack> dnaOutputs = dnaTagOpt.map(holderSet ->
holderSet.stream()
.map(h -> new ItemStack(h.value(), Math.max(1, recipe.getResultItem(null).getCount())))
.collect(java.util.stream.Collectors.toList())
).orElse(List.of());
// Filter out items with a weight of 0 so they don't show in JEI
dnaOutputs = dnaOutputs.stream()
.filter(stack -> recipe.getWeightFor(stack.getItem()) > 0)
.collect(java.util.stream.Collectors.toList());
var slot = builder.addSlot(RecipeIngredientRole.OUTPUT, 80, 63).addItemStacks(dnaOutputs);
slot.addRichTooltipCallback((view, tooltip) -> {
var opt = view.getDisplayedItemStack();
if (opt.isPresent()) {
int weight = recipe.getWeightFor(opt.get().getItem());
//tooltip.add(Component.literal("Weight: " + weight));
}
});
return;
}
}
builder.addSlot(RecipeIngredientRole.OUTPUT, 80, 63).addItemStack(recipe.getResultItem(null));
}
}
@@ -0,0 +1,156 @@
package net.cmr.jurassicrevived.compat;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.helpers.IGuiHelper;
import mezz.jei.api.recipe.IFocusGroup;
import mezz.jei.api.recipe.RecipeIngredientRole;
import mezz.jei.api.recipe.RecipeType;
import mezz.jei.api.recipe.category.IRecipeCategory;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.block.ModBlocks;
import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.item.ModItems;
import net.cmr.jurassicrevived.recipe.DNAExtractorRecipe;
import net.cmr.jurassicrevived.util.ModTags;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class DNAExtractorRecipeCategory implements IRecipeCategory<DNAExtractorRecipe> {
public static final ResourceLocation UID = Constants.rl("dna_extracting");
public static final ResourceLocation TEXTURE = Constants.rl("textures/gui/dna_extractor/dna_extractor_gui.png");
private static final ResourceLocation DNA_TEXTURE = Constants.rl("textures/gui/generic/dna.png");
private static final ResourceLocation POWER_BAR_TEXTURE = Constants.rl("textures/gui/generic/power_bar.png");
public static final RecipeType<DNAExtractorRecipe> DNA_EXTRACTOR_RECIPE_RECIPE_TYPE =
new RecipeType<>(UID, DNAExtractorRecipe.class);
private final IDrawable background;
private final IDrawable icon;
public DNAExtractorRecipeCategory(IGuiHelper guiHelper) {
this.background = guiHelper.drawableBuilder(TEXTURE, 0, 0, 176, 80).setTextureSize(176, 166).build();
this.icon = guiHelper.createDrawableIngredient(VanillaTypes.ITEM_STACK, new ItemStack(ModBlocks.DNA_EXTRACTOR.get()));
}
@Override
public RecipeType<DNAExtractorRecipe> getRecipeType() {
return DNA_EXTRACTOR_RECIPE_RECIPE_TYPE;
}
@Override
public Component getTitle() {
return Component.translatable("block.jurassicrevived.dna_extractor");
}
@Override
public int getWidth() {
return background.getWidth();
}
@Override
public int getHeight() {
return background.getHeight();
}
@Override
public void draw(DNAExtractorRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics guiGraphics, double mouseX, double mouseY) {
background.draw(guiGraphics);
if (JRConfigManager.get().requirePower) {
guiGraphics.blit(POWER_BAR_TEXTURE, 159, 10, 0, 0, 10, 66, 10, 66);
}
{
int fullW = 8, fullH = 16;
int drawBaseX = 84;
int drawBaseY = 38;
// Loop progress at ~20 TPS over 200 ticks
int maxTicks = 200;
long now = System.currentTimeMillis();
int progress = (int) ((now / 50L) % maxTicks);
// Match the screens scale behavior: clamp to fullH (pixels)
int visible = Math.min(fullH, Math.max(0, progress * fullH / maxTicks));
int srcU = 0;
int srcV = fullH - visible;
int drawX = drawBaseX;
int drawY = drawBaseY + (fullH - visible);
if (visible > 0) {
guiGraphics.blit(DNA_TEXTURE, drawX, drawY, srcU, srcV, fullW, visible, fullW, fullH);
}
}
if (JRConfigManager.get().requirePower) {
// Simple visual fill to hint energy usage in JEI (not bound to a BE)
int barX = 160, barY = 11, barW = 8, barH = 64;
int requiredFE = 6000, capacityFE = 64000;
int filled = (int) (barH * (requiredFE / (float) capacityFE));
guiGraphics.fillGradient(barX, barY + (barH - filled), barX + barW, barY + barH, 0xffb51500, 0xff600b00);
// Tooltip for the energy bar
int mx = (int) mouseX, my = (int) mouseY;
if (mx >= barX && mx < barX + barW && my >= barY && my < barY + barH) {
List<Component> tips = List.of(Component.literal("6000 / 64000 FE"));
guiGraphics.renderTooltip(Minecraft.getInstance().font, tips, java.util.Optional.empty(), mx, my);
}
}
}
@Override
public @Nullable IDrawable getIcon() {
return icon;
}
@Override
public void setRecipe(IRecipeLayoutBuilder builder, DNAExtractorRecipe recipe, IFocusGroup focuses) {
builder.addSlot(RecipeIngredientRole.INPUT, 57, 35).addIngredients(recipe.getIngredients().get(0));
builder.addSlot(RecipeIngredientRole.INPUT, 80, 7).addIngredients(recipe.getIngredients().get(1));
ItemStack amber = new ItemStack(ModItems.MOSQUITO_IN_AMBER.get());
boolean isMosquitoRecipe = recipe.getIngredients().size() > 1 && recipe.getIngredients().get(1).test(amber);
if (isMosquitoRecipe) {
var level = Minecraft.getInstance().level;
if (level != null) {
var itemRegistry = level.registryAccess().registryOrThrow(Registries.ITEM);
var dnaTagOpt = itemRegistry.getTag(ModTags.Items.DNA);
List<ItemStack> dnaOutputs = dnaTagOpt.map(holderSet ->
holderSet.stream()
.map(h -> new ItemStack(h.value(), Math.max(1, recipe.getResultItem(null).getCount())))
.collect(java.util.stream.Collectors.toList())
).orElse(List.of());
// Filter out items with a weight of 0 so they don't show in JEI
dnaOutputs = dnaOutputs.stream()
.filter(stack -> recipe.getWeightFor(stack.getItem()) > 0)
.collect(java.util.stream.Collectors.toList());
var slot = builder.addSlot(RecipeIngredientRole.OUTPUT, 62, 63).addItemStacks(dnaOutputs);
slot.addRichTooltipCallback((view, tooltip) -> {
var opt = view.getDisplayedItemStack();
if (opt.isPresent()) {
int weight = recipe.getWeightFor(opt.get().getItem());
//tooltip.add(Component.literal("Weight: " + weight));
}
});
return;
}
}
builder.addSlot(RecipeIngredientRole.OUTPUT, 62, 63).addItemStack(recipe.getResultItem(null));
}
}
@@ -0,0 +1,130 @@
package net.cmr.jurassicrevived.compat;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.helpers.IGuiHelper;
import mezz.jei.api.recipe.IFocusGroup;
import mezz.jei.api.recipe.RecipeIngredientRole;
import mezz.jei.api.recipe.RecipeType;
import mezz.jei.api.recipe.category.IRecipeCategory;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.block.ModBlocks;
import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.recipe.DNAHybridizerRecipe;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class DNAHybridizerRecipeCategory implements IRecipeCategory<DNAHybridizerRecipe> {
public static final ResourceLocation UID = Constants.rl("dna_hybridizing");
public static final ResourceLocation TEXTURE = Constants.rl("textures/gui/dna_hybridizer/dna_hybridizer_gui.png");
public static final ResourceLocation ARROW_TEXTURE = Constants.rl("textures/gui/generic/arrow.png");
public static final ResourceLocation WHITE_ARROW_TEXTURE = Constants.rl("textures/gui/generic/white_arrow.png");
private static final ResourceLocation POWER_BAR_TEXTURE = Constants.rl("textures/gui/generic/power_bar.png");
public static final RecipeType<DNAHybridizerRecipe> DNA_HYBRIDIZER_RECIPE_RECIPE_TYPE =
new RecipeType<>(UID, DNAHybridizerRecipe.class);
private final IDrawable background;
private final IDrawable icon;
public DNAHybridizerRecipeCategory(IGuiHelper guiHelper) {
this.background = guiHelper.drawableBuilder(TEXTURE, 0, 0, 176, 80).setTextureSize(176, 166).build();
this.icon = guiHelper.createDrawableIngredient(VanillaTypes.ITEM_STACK, new ItemStack(ModBlocks.DNA_HYBRIDIZER.get()));
}
@Override
public RecipeType<DNAHybridizerRecipe> getRecipeType() {
return DNA_HYBRIDIZER_RECIPE_RECIPE_TYPE;
}
@Override
public Component getTitle() {
return Component.translatable("block.jurassicrevived.dna_hybridizer");
}
@Override
public int getWidth() {
return background.getWidth();
}
@Override
public int getHeight() {
return background.getHeight();
}
@Override
public void draw(DNAHybridizerRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics guiGraphics, double mouseX, double mouseY) {
background.draw(guiGraphics);
guiGraphics.blit(ARROW_TEXTURE, 105, 35, 0, 0, 24, 16, 24, 16);
if (JRConfigManager.get().requirePower) {
guiGraphics.blit(POWER_BAR_TEXTURE, 159, 10, 0, 0, 10, 66, 10, 66);
// Fill amount for JEI: show total required energy (2000 FE) relative to 64000 FE capacity
// Our simple fill is purely visual for JEI, not tied to any BE
int barX = 160;
int barY = 11;
int barW = 8;
int barH = 64;
int maxTicks = 200;
long now = System.currentTimeMillis();
int progress = (int)((now / 50L) % maxTicks); // ~20 TPS
int arrowPixels = 24;
int progFilled = progress * arrowPixels / maxTicks;
if (progFilled > 0) {
guiGraphics.blit(WHITE_ARROW_TEXTURE, 105, 35, 0, 0, progFilled, 16, 24, 16);
}
int requiredFE = 30000;
int capacityFE = 64000;
int filled = (int)(barH * (requiredFE / (float)capacityFE));
// Render red fill similar to EnergyDisplayTooltipArea
guiGraphics.fillGradient(barX, barY + (barH - filled), barX + barW, barY + barH, 0xffb51500, 0xff600b00);
// Tooltip "2000 / 64000 FE" on hover over the energy area
int mx = (int) mouseX;
int my = (int) mouseY;
if (mx >= barX && mx < barX + barW && my >= barY && my < barY + barH) {
List<Component> tips = List.of(Component.literal("30000 / 64000 FE"));
guiGraphics.renderTooltip(Minecraft.getInstance().font, tips, java.util.Optional.empty(), mx, my);
}
}
}
@Override
public @Nullable IDrawable getIcon() {
return icon;
}
@Override
public void setRecipe(IRecipeLayoutBuilder builder, DNAHybridizerRecipe recipe, IFocusGroup focuses) {
int[][] coords = {
{8, 25}, {26, 25}, {44, 25},
{62, 25}, {8, 43}, {26, 43},
{44, 43}, {62, 43}, {83, 35}
};
int count = Math.min(9, recipe.getIngredients().size());
for (int i = 0; i < Math.min(8, count); i++) {
var ing = recipe.getIngredients().get(i);
if (ing.isEmpty()) continue;
builder.addSlot(RecipeIngredientRole.INPUT, coords[i][0], coords[i][1]).addIngredients(ing);
}
// Catalyst slot: if present in recipe (index 8), show that; otherwise show nothing
if (count >= 9 && !recipe.getIngredients().get(8).isEmpty()) {
builder.addSlot(RecipeIngredientRole.INPUT, coords[8][0], coords[8][1])
.addIngredients(recipe.getIngredients().get(8));
}
builder.addSlot(RecipeIngredientRole.OUTPUT, 134, 35).addItemStack(recipe.getResultItem(null));
}
}
@@ -0,0 +1,159 @@
package net.cmr.jurassicrevived.compat;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.block.custom.EggBlock;
import net.cmr.jurassicrevived.block.custom.IncubatedEggBlock;
import net.cmr.jurassicrevived.block.entity.custom.EggBlockEntity;
import net.minecraft.ChatFormatting;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.level.block.entity.BlockEntity;
import snownee.jade.api.*;
import snownee.jade.api.config.IPluginConfig;
import snownee.jade.api.ui.*;
//? if <=1.20.1 {
import net.minecraft.client.gui.navigation.ScreenDirection;
import snownee.jade.impl.ui.ProgressStyle;
//?}
// ... existing code ...
@WailaPlugin
public class EggJadePlugin implements IWailaPlugin {
private static final ResourceLocation EGG_UID = Constants.rl("egg");
private static final String NBT_SECS = "jr_secs";
private static final String NBT_TOTAL = "jr_total";
@Override
public void registerClient(IWailaClientRegistration reg) {
reg.registerBlockComponent(new snownee.jade.api.IBlockComponentProvider() {
//? if >1.20.1 {
/*@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (data == null || !data.contains(NBT_SECS)) return;
int secs = data.getInt(NBT_SECS);
int total = data.contains(NBT_TOTAL) ? Math.max(1, data.getInt(NBT_TOTAL)) : 5;
float ratio = Mth.clamp(1.0f - (secs / (float) total), 0.0f, 1.0f);
IElementHelper h = IElementHelper.get();
ProgressStyle style = h.progressStyle()
.color(0xFFFFFFFF, 0xFFFFFFFF)
.direction(ScreenDirection.RIGHT)
.fitContentX(true)
.fitContentY(true);
BoxStyle box = BoxStyle.getNestedBox();
IElement progress = h.progress(ratio, Component.empty(), style, box, true);
tooltip.add(progress);
tooltip.add(Component.translatable("tooltip.jurassicrevived.egg.hatches_in_seconds", secs)
.withStyle(ChatFormatting.YELLOW));
}
*///?} else {
@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (!data.contains(NBT_SECS)) return;
int secs = data.getInt(NBT_SECS);
int total = data.contains(NBT_TOTAL) ? Math.max(1, data.getInt(NBT_TOTAL)) : 5;
float ratio = Mth.clamp(1.0f - (secs / (float) total), 0.0f, 1.0f);
IElementHelper h = tooltip.getElementHelper();
// Properly build each style from its own factory
IProgressStyle pStyle = h.progressStyle()
.color(0xFFFFFFFF) // ARGB filled color
.textColor(0xFFFFFFFF); // ARGB text color
IBoxStyle box = new ThickBorderBox(1.0f); // any width you want
tooltip.add(h.progress(ratio, Component.empty(), pStyle, box, false));
tooltip.add(Component.translatable("tooltip.jurassicrevived.egg.hatches_in_seconds", secs)
.withStyle(ChatFormatting.YELLOW));
}
//?}
@Override
public ResourceLocation getUid() {
return EGG_UID;
}
}, EggBlock.class);
// Register the same provider for IncubatedEggBlock separately
reg.registerBlockComponent(new snownee.jade.api.IBlockComponentProvider() {
//? if >1.20.1 {
/*@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (data == null || !data.contains(NBT_SECS)) return;
int secs = data.getInt(NBT_SECS);
int total = data.contains(NBT_TOTAL) ? Math.max(1, data.getInt(NBT_TOTAL)) : 5;
float ratio = Mth.clamp(1.0f - (secs / (float) total), 0.0f, 1.0f);
IElementHelper h = IElementHelper.get();
ProgressStyle style = h.progressStyle()
.color(0xFFFFFFFF, 0xFFFFFFFF)
.direction(ScreenDirection.RIGHT)
.fitContentX(true)
.fitContentY(true);
BoxStyle box = BoxStyle.getNestedBox();
IElement progress = h.progress(ratio, Component.empty(), style, box, true);
tooltip.add(progress);
tooltip.add(Component.translatable("tooltip.jurassicrevived.egg.hatches_in_seconds", secs)
.withStyle(ChatFormatting.YELLOW));
}
*///?} else {
@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (!data.contains(NBT_SECS)) return;
int secs = data.getInt(NBT_SECS);
int total = data.contains(NBT_TOTAL) ? Math.max(1, data.getInt(NBT_TOTAL)) : 5;
float ratio = Mth.clamp(1.0f - (secs / (float) total), 0.0f, 1.0f);
IElementHelper h = tooltip.getElementHelper();
// Properly build each style from its own factory
IProgressStyle pStyle = h.progressStyle()
.color(0xFFFFFFFF) // ARGB filled color
.textColor(0xFFFFFFFF); // ARGB text color
IBoxStyle box = new ThickBorderBox(1.0f); // any width you want
tooltip.add(h.progress(ratio, Component.empty(), pStyle, box, false));
tooltip.add(Component.translatable("tooltip.jurassicrevived.egg.hatches_in_seconds", secs)
.withStyle(ChatFormatting.YELLOW));
}
//?}
@Override
public ResourceLocation getUid() {
return EGG_UID;
}
}, IncubatedEggBlock.class);
}
@Override
public void register(IWailaCommonRegistration reg) {
reg.registerBlockDataProvider(new IServerDataProvider<BlockAccessor>() {
@Override
public void appendServerData(CompoundTag data, BlockAccessor accessor) {
BlockEntity be = accessor.getBlockEntity();
if (be instanceof EggBlockEntity egg) {
int secs = egg.getSecondsRemaining(accessor.getLevel());
data.putInt(NBT_SECS, secs);
data.putInt(NBT_TOTAL, egg.getTotalSeconds());
}
}
@Override
public ResourceLocation getUid() {
return EGG_UID;
}
}, EggBlockEntity.class); // BE is shared by both blocks
}
}
@@ -0,0 +1,114 @@
package net.cmr.jurassicrevived.compat;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.helpers.IGuiHelper;
import mezz.jei.api.recipe.IFocusGroup;
import mezz.jei.api.recipe.RecipeIngredientRole;
import mezz.jei.api.recipe.RecipeType;
import mezz.jei.api.recipe.category.IRecipeCategory;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.block.ModBlocks;
import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.recipe.EmbryoCalcificationMachineRecipe;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class EmbryoCalcificationMachineRecipeCategory implements IRecipeCategory<EmbryoCalcificationMachineRecipe> {
public static final ResourceLocation UID = Constants.rl("embryo_calcification_machining");
public static final ResourceLocation TEXTURE = Constants.rl("textures/gui/embryo_calcification_machine/embryo_calcification_machine_gui.png");
private static final ResourceLocation SYRINGE_BAR_TEXTURE = Constants.rl("textures/gui/generic/syringe_bar.png");
private static final ResourceLocation WHITE_SYRINGE_BAR_TEXTURE = Constants.rl("textures/gui/generic/white_syringe_bar.png");
private static final ResourceLocation POWER_BAR_TEXTURE = Constants.rl("textures/gui/generic/power_bar.png");
public static final RecipeType<EmbryoCalcificationMachineRecipe> EMBRYO_CALCIFICATION_MACHINE_RECIPE_RECIPE_TYPE =
new RecipeType<>(UID, EmbryoCalcificationMachineRecipe.class);
private final IDrawable background;
private final IDrawable icon;
public EmbryoCalcificationMachineRecipeCategory(IGuiHelper guiHelper) {
this.background = guiHelper.drawableBuilder(TEXTURE, 0, 0, 176, 80).setTextureSize(176, 166).build();
this.icon = guiHelper.createDrawableIngredient(VanillaTypes.ITEM_STACK, new ItemStack(ModBlocks.EMBRYO_CALCIFICATION_MACHINE.get()));
}
@Override
public RecipeType<EmbryoCalcificationMachineRecipe> getRecipeType() {
return EMBRYO_CALCIFICATION_MACHINE_RECIPE_RECIPE_TYPE;
}
@Override
public Component getTitle() {
return Component.translatable("block.jurassicrevived.embryo_calcification_machine");
}
@Override
public int getWidth() {
return background.getWidth();
}
@Override
public int getHeight() {
return background.getHeight();
}
@Override
public void draw(EmbryoCalcificationMachineRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics guiGraphics, double mouseX, double mouseY) {
background.draw(guiGraphics);
guiGraphics.blit(SYRINGE_BAR_TEXTURE, 76, 35, 0, 0, 24, 16, 24, 16);
if (JRConfigManager.get().requirePower) {
guiGraphics.blit(POWER_BAR_TEXTURE, 159, 10, 0, 0, 10, 66, 10, 66);
// Fill amount for JEI: show total required energy (2000 FE) relative to 64000 FE capacity
// Our simple fill is purely visual for JEI, not tied to any BE
int barX = 160;
int barY = 11;
int barW = 8;
int barH = 64;
int maxTicks = 200;
long now = System.currentTimeMillis();
int progress = (int)((now / 50L) % maxTicks); // ~20 TPS
int arrowPixels = 24;
int progFilled = progress * arrowPixels / maxTicks;
if (progFilled > 0) {
guiGraphics.blit(WHITE_SYRINGE_BAR_TEXTURE, 76, 35, 0, 0, progFilled, 16, 24, 16);
}
int requiredFE = 1000;
int capacityFE = 64000;
int filled = (int)(barH * (requiredFE / (float)capacityFE));
// Render red fill similar to EnergyDisplayTooltipArea
guiGraphics.fillGradient(barX, barY + (barH - filled), barX + barW, barY + barH, 0xffb51500, 0xff600b00);
// Tooltip "2000 / 64000 FE" on hover over the energy area
int mx = (int) mouseX;
int my = (int) mouseY;
if (mx >= barX && mx < barX + barW && my >= barY && my < barY + barH) {
List<Component> tips = List.of(Component.literal("1000 / 64000 FE"));
guiGraphics.renderTooltip(Minecraft.getInstance().font, tips, java.util.Optional.empty(), mx, my);
}
}
}
@Override
public @Nullable IDrawable getIcon() {
return icon;
}
@Override
public void setRecipe(IRecipeLayoutBuilder builder, EmbryoCalcificationMachineRecipe recipe, IFocusGroup focuses) {
builder.addSlot(RecipeIngredientRole.INPUT, 39, 35).addIngredients(recipe.getIngredients().get(0));
builder.addSlot(RecipeIngredientRole.INPUT, 57, 35).addIngredients(recipe.getIngredients().get(1));
builder.addSlot(RecipeIngredientRole.OUTPUT, 103, 35).addItemStack(recipe.getResultItem(null));
}
}
@@ -0,0 +1,147 @@
package net.cmr.jurassicrevived.compat;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.helpers.IGuiHelper;
import mezz.jei.api.recipe.IFocusGroup;
import mezz.jei.api.recipe.RecipeIngredientRole;
import mezz.jei.api.recipe.RecipeType;
import mezz.jei.api.recipe.category.IRecipeCategory;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.block.ModBlocks;
import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.item.ModItems;
import net.cmr.jurassicrevived.recipe.EmbryonicMachineRecipe;
import net.cmr.jurassicrevived.util.ModTags;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class EmbryonicMachineRecipeCategory implements IRecipeCategory<EmbryonicMachineRecipe> {
public static final ResourceLocation UID = Constants.rl("embryonic_machining");
public static final ResourceLocation TEXTURE = Constants.rl("textures/gui/embryonic_machine/embryonic_machine_gui.png");
private static final ResourceLocation SYRINGE_BAR_REVERSED_TEXTURE = Constants.rl("textures/gui/generic/syringe_bar_reversed.png");
private static final ResourceLocation WHITE_SYRINGE_BAR_REVERSED_TEXTURE = Constants.rl("textures/gui/generic/white_syringe_bar_reversed.png");
private static final ResourceLocation POWER_BAR_TEXTURE = Constants.rl("textures/gui/generic/power_bar.png");
public static final RecipeType<EmbryonicMachineRecipe> EMBRYONIC_MACHINE_RECIPE_RECIPE_TYPE =
new RecipeType<>(UID, EmbryonicMachineRecipe.class);
private final IDrawable background;
private final IDrawable icon;
public EmbryonicMachineRecipeCategory(IGuiHelper guiHelper) {
this.background = guiHelper.drawableBuilder(TEXTURE, 0, 0, 176, 80).setTextureSize(176, 166).build();
this.icon = guiHelper.createDrawableIngredient(VanillaTypes.ITEM_STACK, new ItemStack(ModBlocks.EMBRYONIC_MACHINE.get()));
}
@Override
public RecipeType<EmbryonicMachineRecipe> getRecipeType() {
return EMBRYONIC_MACHINE_RECIPE_RECIPE_TYPE;
}
@Override
public Component getTitle() {
return Component.translatable("block.jurassicrevived.embryonic_machine");
}
@Override
public int getWidth() {
return background.getWidth();
}
@Override
public int getHeight() {
return background.getHeight();
}
@Override
public void draw(EmbryonicMachineRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics guiGraphics, double mouseX, double mouseY) {
background.draw(guiGraphics);
guiGraphics.blit(SYRINGE_BAR_REVERSED_TEXTURE, 76, 35, 0, 0, 24, 16, 24, 16);
if (JRConfigManager.get().requirePower) {
guiGraphics.blit(POWER_BAR_TEXTURE, 159, 10, 0, 0, 10, 66, 10, 66);
// Fill amount for JEI: show total required energy (2000 FE) relative to 64000 FE capacity
// Our simple fill is purely visual for JEI, not tied to any BE
int barX = 160;
int barY = 11;
int barW = 8;
int barH = 64;
int maxTicks = 200;
long now = System.currentTimeMillis();
int progress = (int)((now / 50L) % maxTicks); // ~20 TPS
int arrowPixels = 24;
int progFilled = progress * arrowPixels / maxTicks;
if (progFilled > 0) {
guiGraphics.blit(WHITE_SYRINGE_BAR_REVERSED_TEXTURE, 76, 35, 0, 0, progFilled, 16, 24, 16);
}
int requiredFE = 2000;
int capacityFE = 64000;
int filled = (int)(barH * (requiredFE / (float)capacityFE));
// Render red fill similar to EnergyDisplayTooltipArea
guiGraphics.fillGradient(barX, barY + (barH - filled), barX + barW, barY + barH, 0xffb51500, 0xff600b00);
// Tooltip "2000 / 64000 FE" on hover over the energy area
int mx = (int) mouseX;
int my = (int) mouseY;
if (mx >= barX && mx < barX + barW && my >= barY && my < barY + barH) {
List<Component> tips = List.of(Component.literal("2000 / 64000 FE"));
guiGraphics.renderTooltip(Minecraft.getInstance().font, tips, java.util.Optional.empty(), mx, my);
}
}
}
@Override
public @Nullable IDrawable getIcon() {
return icon;
}
@Override
public void setRecipe(IRecipeLayoutBuilder builder, EmbryonicMachineRecipe recipe, IFocusGroup focuses) {
builder.addSlot(RecipeIngredientRole.INPUT, 39, 35).addIngredients(recipe.getIngredients().get(0));
builder.addSlot(RecipeIngredientRole.INPUT, 57, 35).addIngredients(recipe.getIngredients().get(1));
// Frog DNA third input at (48,53)
if (recipe.getIngredients().size() >= 3) {
builder.addSlot(RecipeIngredientRole.INPUT, 48, 53).addIngredients(recipe.getIngredients().get(2));
}
ItemStack amber = new ItemStack(ModItems.MOSQUITO_IN_AMBER.get());
boolean isMosquitoRecipe = recipe.getIngredients().size() > 1 && recipe.getIngredients().get(1).test(amber);
if (isMosquitoRecipe) {
var level = Minecraft.getInstance().level;
if (level != null) {
var itemRegistry = level.registryAccess().registryOrThrow(Registries.ITEM);
var dnaTagOpt = itemRegistry.getTag(ModTags.Items.DNA);
List<ItemStack> dnaOutputs = dnaTagOpt.map(holderSet ->
holderSet.stream()
.map(h -> new ItemStack(h.value(), Math.max(1, recipe.getResultItem(null).getCount())))
.collect(java.util.stream.Collectors.toList())
).orElse(List.of());
var slot = builder.addSlot(RecipeIngredientRole.OUTPUT, 103, 35).addItemStacks(dnaOutputs);
slot.addRichTooltipCallback((view, tooltip) -> {
var opt = view.getDisplayedItemStack();
if (opt.isPresent()) {
int weight = recipe.getWeightFor(opt.get().getItem());
tooltip.add(Component.literal("Weight: " + weight));
}
});
return;
}
}
builder.addSlot(RecipeIngredientRole.OUTPUT, 103, 35).addItemStack(recipe.getResultItem(null));
}
}
@@ -0,0 +1,39 @@
package net.cmr.jurassicrevived.compat;
import dev.architectury.fluid.FluidStack;
import mezz.jei.api.ingredients.IIngredientRenderer;
import net.cmr.jurassicrevived.screen.renderer.FluidTankRenderer;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.network.chat.Component;
import net.minecraft.world.item.TooltipFlag;
import java.util.List;
public class FluidStackSlotRenderer implements IIngredientRenderer<FluidStack> {
private final FluidTankRenderer tankRenderer;
public FluidStackSlotRenderer(FluidTankRenderer tankRenderer) {
this.tankRenderer = tankRenderer;
}
@Override
public int getWidth() {
return tankRenderer.getWidth();
}
@Override
public int getHeight() {
return tankRenderer.getHeight();
}
@Override
public void render(GuiGraphics guiGraphics, FluidStack ingredient) {
// Slot renders at its own x,y; JEI calls us with the pose already translated.
tankRenderer.render(guiGraphics, 0, 0, ingredient);
}
@Override
public List<Component> getTooltip(FluidStack ingredient, TooltipFlag tooltipFlag) {
return tankRenderer.getTooltip(ingredient, tooltipFlag);
}
}
@@ -0,0 +1,202 @@
package net.cmr.jurassicrevived.compat;
import dev.architectury.fluid.FluidStack;
import dev.architectury.hooks.fluid.FluidStackHooks;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
import mezz.jei.api.gui.builder.IRecipeSlotBuilder;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.helpers.IGuiHelper;
import mezz.jei.api.recipe.IFocusGroup;
import mezz.jei.api.recipe.RecipeIngredientRole;
import mezz.jei.api.recipe.RecipeType;
import mezz.jei.api.recipe.category.IRecipeCategory;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.block.ModBlocks;
import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.recipe.FossilCleanerRecipe;
import net.cmr.jurassicrevived.screen.renderer.FluidTankRenderer;
import net.cmr.jurassicrevived.util.ModTags;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.core.registries.Registries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.item.Item;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.material.Fluids;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class FossilCleanerRecipeCategory implements IRecipeCategory<FossilCleanerRecipe> {
public static final ResourceLocation UID = Constants.rl("fossil_cleaning");
public static final ResourceLocation TEXTURE = Constants.rl("textures/gui/fossil_cleaner/fossil_cleaner_gui.png");
private static final ResourceLocation BUBBLES_TEXTURE = Constants.rl("textures/gui/generic/bubbles.png");
private static final ResourceLocation WHITE_BUBBLES_TEXTURE = Constants.rl("textures/gui/generic/white_bubbles.png");
private static final ResourceLocation POWER_BAR_TEXTURE = Constants.rl("textures/gui/generic/power_bar.png");
public static final RecipeType<FossilCleanerRecipe> FOSSIL_CLEANER_RECIPE_RECIPE_TYPE =
new RecipeType<>(UID, FossilCleanerRecipe.class);
private final IDrawable background;
private final IDrawable icon;
private final FluidTankRenderer fluidRenderer;
private static List<ItemStack> WATER_CONTAINERS_CACHE = null;
public FossilCleanerRecipeCategory(IGuiHelper guiHelper) {
this.background = guiHelper.drawableBuilder(TEXTURE, 0, 0, 176, 80).setTextureSize(176, 166).build();
this.icon = guiHelper.createDrawableIngredient(VanillaTypes.ITEM_STACK, new ItemStack(ModBlocks.FOSSIL_CLEANER.get()));
this.fluidRenderer = new FluidTankRenderer(64000, true, 16, 50);
if (WATER_CONTAINERS_CACHE == null) {
WATER_CONTAINERS_CACHE = buildWaterContainersList();
}
}
@Override
public RecipeType<FossilCleanerRecipe> getRecipeType() {
return FOSSIL_CLEANER_RECIPE_RECIPE_TYPE;
}
@Override
public Component getTitle() {
return Component.translatable("block.jurassicrevived.fossil_cleaner");
}
@Override
public int getWidth() {
return background.getWidth();
}
@Override
public int getHeight() {
return background.getHeight();
}
@Override
public void draw(FossilCleanerRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics guiGraphics, double mouseX, double mouseY) {
background.draw(guiGraphics);
guiGraphics.blit(BUBBLES_TEXTURE, 73, 37, 0, 0, 24, 16, 24, 12);
if (JRConfigManager.get().requirePower) {
guiGraphics.blit(POWER_BAR_TEXTURE, 159, 10, 0, 0, 10, 66, 10, 66);
// Fill amount for JEI: show total required energy (2000 FE) relative to 64000 FE capacity
// Our simple fill is purely visual for JEI, not tied to any BE
int barX = 160;
int barY = 11;
int barW = 8;
int barH = 64;
int maxTicks = 200;
long now = System.currentTimeMillis();
int progress = (int)((now / 50L) % maxTicks); // ~20 TPS
int arrowPixels = 29;
int progFilled = progress * arrowPixels / maxTicks;
if (progFilled > 0) {
guiGraphics.blit(WHITE_BUBBLES_TEXTURE, 73, 37, 0, 0, progFilled, 16, 29, 12);
}
int requiredFE = 2000;
int capacityFE = 64000;
int filled = (int)(barH * (requiredFE / (float)capacityFE));
// Render red fill similar to EnergyDisplayTooltipArea
guiGraphics.fillGradient(barX, barY + (barH - filled), barX + barW, barY + barH, 0xffb51500, 0xff600b00);
// Tooltip "2000 / 64000 FE" on hover over the energy area
int mx = (int) mouseX;
int my = (int) mouseY;
if (mx >= barX && mx < barX + barW && my >= barY && my < barY + barH) {
List<Component> tips = List.of(Component.literal("2000 / 64000 FE"));
guiGraphics.renderTooltip(Minecraft.getInstance().font, tips, java.util.Optional.empty(), mx, my);
}
}
}
@Override
public @Nullable IDrawable getIcon() {
return icon;
}
@Override
public void setRecipe(IRecipeLayoutBuilder builder, FossilCleanerRecipe recipe, IFocusGroup focuses) {
// Single consumable input (fossil block)
builder.addSlot(RecipeIngredientRole.INPUT, 57, 35).addIngredients(recipe.getIngredients().get(0));
// Fluid "tank" visualization using custom renderer at (7, 8)
IRecipeSlotBuilder tankSlot = builder.addSlot(RecipeIngredientRole.RENDER_ONLY, 7, 8);
tankSlot.setCustomRenderer(JEIJRPlugin.FLUID_STACK_TYPE, new FluidStackSlotRenderer(fluidRenderer));
tankSlot.addIngredient(JEIJRPlugin.FLUID_STACK_TYPE, FluidStack.create(Fluids.WATER, 250));
// Water container acceptance list at (57, 61), discovered dynamically
var waterItems = builder.addSlot(RecipeIngredientRole.INPUT, 7, 61).addItemStacks(WATER_CONTAINERS_CACHE);
waterItems.addRichTooltipCallback((view, tooltip) -> {
tooltip.add(Component.translatable("jurassicrevived.tooltip.accepts_any_water_container"));
});
// Output list: all fossils from the tag, tooltip shows per-item weight from the recipe
var level = Minecraft.getInstance().level;
if (level != null) {
var itemRegistry = level.registryAccess().registryOrThrow(Registries.ITEM);
var fossilsTagOpt = itemRegistry.getTag(ModTags.Items.FOSSILS);
List<ItemStack> fossilOutputs = fossilsTagOpt.map(holderSet ->
holderSet.stream()
.map(h -> new ItemStack(h.value(), Math.max(1, recipe.output().getCount())))
.collect(Collectors.toList())
).orElse(List.of());
// Hide zero-weight fossils
fossilOutputs = fossilOutputs.stream()
.filter(stack -> recipe.getWeightFor(stack.getItem()) > 0)
.collect(Collectors.toList());
var slot = builder.addSlot(RecipeIngredientRole.OUTPUT, 103, 35).addItemStacks(fossilOutputs);
slot.addRichTooltipCallback((view, tooltip) -> {
var opt = view.getDisplayedItemStack();
if (opt.isPresent()) {
int weight = recipe.getWeightFor(opt.get().getItem());
//tooltip.add(Component.literal("Weight: " + weight));
}
});
return;
}
builder.addSlot(RecipeIngredientRole.OUTPUT, 103, 35).addItemStack(recipe.output());
}
private static List<ItemStack> buildWaterContainersList() {
var list = new ArrayList<ItemStack>();
// Always include vanilla water bucket (already filled)
list.add(new ItemStack(Items.WATER_BUCKET));
var mc = Minecraft.getInstance();
var level = mc.level;
if (level == null) {
return Collections.unmodifiableList(list);
}
final int REQUIRED_MB = 250;
var itemRegistry = level.registryAccess().registryOrThrow(Registries.ITEM);
for (Item item : itemRegistry) {
if (item == Items.WATER_BUCKET) continue;
ItemStack empty = new ItemStack(item);
// Check if item is a fluid container using Architectury's hooks
// This is a simplified check; a more robust one would involve checking capabilities or similar hooks
// For now, we'll rely on known items or basic checks if available via Architectury
// Since Architectury abstracts this, we might need to rely on platform-specific implementations or common helpers if available.
// However, without a direct common "isFluidContainer" helper in the provided context, we might skip complex checks or use a simple list.
// For the sake of this conversion, we will stick to just the water bucket as a safe default
// or implement a more complex check if Architectury provides one.
// Given the constraints, we'll simplify this to just water buckets for now to avoid platform-specific code in common.
}
return Collections.unmodifiableList(list);
}
}
@@ -0,0 +1,150 @@
package net.cmr.jurassicrevived.compat;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.helpers.IGuiHelper;
import mezz.jei.api.recipe.IFocusGroup;
import mezz.jei.api.recipe.RecipeIngredientRole;
import mezz.jei.api.recipe.RecipeType;
import mezz.jei.api.recipe.category.IRecipeCategory;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.block.ModBlocks;
import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.recipe.FossilGrinderRecipe;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.core.registries.BuiltInRegistries;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.ArrayList;
import java.util.List;
public class FossilGrinderRecipeCategory implements IRecipeCategory<FossilGrinderRecipe> {
public static final ResourceLocation UID = Constants.rl("fossil_grinding");
public static final ResourceLocation TEXTURE = Constants.rl("textures/gui/fossil_grinder/fossil_grinder_gui.png");
private static final ResourceLocation CUTTING_BLADES_TEXTURE = Constants.rl("textures/gui/generic/cutting_blades.png");
private static final ResourceLocation POWER_BAR_TEXTURE = Constants.rl("textures/gui/generic/power_bar.png");
public static final RecipeType<FossilGrinderRecipe> FOSSIL_GRINDER_RECIPE_RECIPE_TYPE =
new RecipeType<>(UID, FossilGrinderRecipe.class);
private final IDrawable background;
private final IDrawable icon;
public FossilGrinderRecipeCategory(IGuiHelper guiHelper) {
this.background = guiHelper.drawableBuilder(TEXTURE, 0, 0, 176, 80).setTextureSize(176, 166).build();
this.icon = guiHelper.createDrawableIngredient(VanillaTypes.ITEM_STACK, new ItemStack(ModBlocks.FOSSIL_GRINDER.get()));
}
@Override
public RecipeType<FossilGrinderRecipe> getRecipeType() {
return FOSSIL_GRINDER_RECIPE_RECIPE_TYPE;
}
@Override
public Component getTitle() {
return Component.translatable("block.jurassicrevived.fossil_grinder");
}
@Override
public int getWidth() {
return background.getWidth();
}
@Override
public int getHeight() {
return background.getHeight();
}
@Override
public void draw(FossilGrinderRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics guiGraphics, double mouseX, double mouseY) {
background.draw(guiGraphics);
{
final float scale = 1.25f;
final int texSize = 16;
float baseAngle = (System.currentTimeMillis() % 700L) / 700.0f * ((float)Math.PI * 2.0f);
java.util.function.BiConsumer<int[], Float> drawBlade = (center, ang) -> {
guiGraphics.pose().pushPose();
guiGraphics.pose().translate(center[0], center[1], 0);
guiGraphics.pose().scale(scale, scale, 1.0f);
if (ang != null) {
guiGraphics.pose().mulPose(com.mojang.math.Axis.ZP.rotation(-ang));
}
guiGraphics.pose().translate(-texSize / 2f, -texSize / 2f, 0);
guiGraphics.blit(CUTTING_BLADES_TEXTURE, 0, 0, 0, 0, texSize, texSize, texSize, texSize);
guiGraphics.pose().popPose();
};
int x = 0;
int y = 0;
int cx1 = x + 89, cy1 = y + 34;
int cx2 = x + 89, cy2 = y + 52;
drawBlade.accept(new int[]{cx1, cy1}, baseAngle);
drawBlade.accept(new int[]{cx2, cy2}, -baseAngle);
}
if (JRConfigManager.get().requirePower) {
guiGraphics.blit(POWER_BAR_TEXTURE, 159, 10, 0, 0, 10, 66, 10, 66);
int barX = 160;
int barY = 11;
int barW = 8;
int barH = 64;
int requiredFE = 1000;
int capacityFE = 64000;
int filled = (int)(barH * (requiredFE / (float)capacityFE));
guiGraphics.fillGradient(barX, barY + (barH - filled), barX + barW, barY + barH, 0xffb51500, 0xff600b00);
int mx = (int) mouseX;
int my = (int) mouseY;
if (mx >= barX && mx < barX + barW && my >= barY && my < barY + barH) {
List<Component> tips = List.of(Component.literal("1000 / 64000 FE"));
guiGraphics.renderTooltip(Minecraft.getInstance().font, tips, java.util.Optional.empty(), mx, my);
}
}
}
@Override
public @Nullable IDrawable getIcon() {
return icon;
}
@Override
public void setRecipe(IRecipeLayoutBuilder builder, FossilGrinderRecipe recipe, IFocusGroup focuses) {
// Single input
builder.addSlot(RecipeIngredientRole.INPUT, 57, 35).addIngredients(recipe.getIngredients().get(0));
// If weights are present, show all possible outputs with tooltips; otherwise show the fixed result
if (!recipe.weights().isEmpty()) {
List<ItemStack> outputs = new ArrayList<>();
for (var entry : recipe.weights().entrySet()) {
var item = BuiltInRegistries.ITEM.get(entry.getKey());
if (item != null) {
ItemStack stack = new ItemStack(item, Math.max(1, recipe.getResultItem(null).getCount()));
outputs.add(stack);
}
}
var slot = builder.addSlot(RecipeIngredientRole.OUTPUT, 103, 17).addItemStacks(outputs);
slot.addRichTooltipCallback((view, tooltip) -> {
var opt = view.getDisplayedItemStack();
if (opt.isPresent()) {
int weight = recipe.getWeightFor(opt.get().getItem());
tooltip.add(Component.literal("Weight: " + weight));
}
});
return;
}
builder.addSlot(RecipeIngredientRole.OUTPUT, 103, 17).addItemStack(recipe.getResultItem(null));
}
}
@@ -0,0 +1,131 @@
package net.cmr.jurassicrevived.compat;
import mezz.jei.api.constants.VanillaTypes;
import mezz.jei.api.gui.builder.IRecipeLayoutBuilder;
import mezz.jei.api.gui.drawable.IDrawable;
import mezz.jei.api.gui.ingredient.IRecipeSlotsView;
import mezz.jei.api.helpers.IGuiHelper;
import mezz.jei.api.recipe.IFocusGroup;
import mezz.jei.api.recipe.RecipeIngredientRole;
import mezz.jei.api.recipe.RecipeType;
import mezz.jei.api.recipe.category.IRecipeCategory;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.block.ModBlocks;
import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.recipe.IncubatorRecipe;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.item.ItemStack;
import org.jetbrains.annotations.Nullable;
import java.util.List;
public class IncubatorRecipeCategory implements IRecipeCategory<IncubatorRecipe> {
public static final ResourceLocation UID = Constants.rl("incubating");
public static final ResourceLocation TEXTURE = Constants.rl("textures/gui/incubator/incubator_gui.png");
private static final ResourceLocation LANTERN_TEXTURE = Constants.rl("textures/gui/generic/lantern.png");
private static final ResourceLocation POWER_BAR_TEXTURE = Constants.rl("textures/gui/generic/power_bar.png");
public static final RecipeType<IncubatorRecipe> INCUBATOR_RECIPE_RECIPE_TYPE =
new RecipeType<>(UID, IncubatorRecipe.class);
private final IDrawable background;
private final IDrawable icon;
public IncubatorRecipeCategory(IGuiHelper guiHelper) {
this.background = guiHelper.drawableBuilder(TEXTURE, 0, 0, 176, 80).setTextureSize(176, 166).build();
this.icon = guiHelper.createDrawableIngredient(VanillaTypes.ITEM_STACK, new ItemStack(ModBlocks.INCUBATOR.get()));
}
@Override
public RecipeType<IncubatorRecipe> getRecipeType() {
return INCUBATOR_RECIPE_RECIPE_TYPE;
}
@Override
public Component getTitle() {
return Component.translatable("block.jurassicrevived.incubator");
}
@Override
public int getWidth() {
return background.getWidth();
}
@Override
public int getHeight() {
return background.getHeight();
}
@Override
public void draw(IncubatorRecipe recipe, IRecipeSlotsView recipeSlotsView, GuiGraphics g, double mouseX, double mouseY) {
background.draw(g);
// Rising flame indicator on all three slots
renderLanternFill(g, 50);
renderLanternFill(g, 80);
renderLanternFill(g, 110);
// 2-second toggle for which stack to paint on top of each slot
boolean showOutput = ((System.currentTimeMillis() / 2000L) & 1L) == 1L;
ItemStack out = recipe.getResultItem(null);
ItemStack in = recipe.getIngredients().isEmpty() ? ItemStack.EMPTY
: recipe.getIngredients().get(0).getItems().length > 0
? recipe.getIngredients().get(0).getItems()[0]
: ItemStack.EMPTY;
// Paint chosen stack at the three slot centers (icons are 16x16; slot centers are 50/80/110,35)
if (!in.isEmpty() || !out.isEmpty()) {
ItemStack toRender = showOutput && !out.isEmpty() ? out : in;
// Render at all three positions so it looks like each slot is toggling
drawStackIcon(g, toRender, 50, 35);
}
if (JRConfigManager.get().requirePower) {
g.blit(POWER_BAR_TEXTURE, 159, 10, 0, 0, 10, 66, 10, 66);
int barX = 160, barY = 11, barW = 8, barH = 64;
int requiredFE = 48000, capacityFE = 64000;
int filled = (int)(barH * (requiredFE / (float)capacityFE));
g.fillGradient(barX, barY + (barH - filled), barX + barW, barY + barH, 0xffb51500, 0xff600b00);
int mx = (int) mouseX, my = (int) mouseY;
if (mx >= barX && mx < barX + barW && my >= barY && my < barY + barH) {
List<Component> tips = List.of(Component.literal("48000 / 64000 FE"));
g.renderTooltip(Minecraft.getInstance().font, tips, java.util.Optional.empty(), mx, my);
}
}
}
private void drawStackIcon(GuiGraphics g, ItemStack stack, int x, int y) {
if (stack.isEmpty()) return;
g.renderItem(stack, x, y);
g.renderItemDecorations(Minecraft.getInstance().font, stack, x, y);
}
private void renderLanternFill(GuiGraphics g, int slotPixelX) {
long now = System.currentTimeMillis();
float t = (now % 10000L) / 10000f;
int l = Mth.ceil(t * 13.0F) + 1;
g.blit(LANTERN_TEXTURE, slotPixelX, 16, 0, 0, 16, l, 16, 16);
}
@Override
public @Nullable IDrawable getIcon() {
return icon;
}
@Override
public void setRecipe(IRecipeLayoutBuilder builder, IncubatorRecipe recipe, IFocusGroup focuses) {
// Build static slots once; dynamic toggle is handled in draw()
// Put the recipes input and output into each slot so JEI focus/tooltip works regardless of the overlay
var ing = recipe.getIngredients().isEmpty() ? null : recipe.getIngredients().get(0);
if ( ing != null ) {
builder.addSlot(RecipeIngredientRole.INPUT, 50, 35).addIngredients(ing).addItemStack(recipe.getResultItem(null));
} else {
builder.addSlot(RecipeIngredientRole.OUTPUT, 50, 35).addItemStack(recipe.getResultItem(null));
}
}
}
@@ -0,0 +1,231 @@
package net.cmr.jurassicrevived.compat;
import dev.architectury.fluid.FluidStack;
import mezz.jei.api.IModPlugin;
import mezz.jei.api.JeiPlugin;
import mezz.jei.api.ingredients.IIngredientType;
import mezz.jei.api.registration.*;
import mezz.jei.api.runtime.IIngredientManager;
import mezz.jei.api.runtime.IJeiRuntime;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.block.ModBlocks;
import net.cmr.jurassicrevived.recipe.*;
import net.cmr.jurassicrevived.screen.ModMenuTypes;
import net.cmr.jurassicrevived.screen.custom.*;
import net.minecraft.client.Minecraft;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.crafting.RecipeManager;
import java.util.List;
@JeiPlugin
public class JEIJRPlugin implements IModPlugin {
public static final IIngredientType<FluidStack> FLUID_STACK_TYPE = () -> FluidStack.class;
@Override
public ResourceLocation getPluginUid() {
return Constants.rl("jei_plugin");
}
// Expose JEI ingredient manager so categories can access all item variants (including mod-provided filled tanks)
public static @org.jetbrains.annotations.Nullable IIngredientManager INGREDIENT_MANAGER;
@Override
public void onRuntimeAvailable(IJeiRuntime jeiRuntime) {
INGREDIENT_MANAGER = jeiRuntime.getIngredientManager();
}
@Override
public void registerIngredients(IModIngredientRegistration registration) {
// We need to register the FluidStack ingredient type to use it in recipes
// However, JEI usually provides its own fluid type on each platform.
// In a common environment, we might be defining a duplicate type if we aren't careful.
// But since we are using Architectury's FluidStack, we can try to register it as a custom ingredient.
// Note: This might not automatically work with JEI's built-in fluid rendering unless we provide a helper.
// For now, we register it so we can use it in our custom renderer.
// registration.register(FLUID_STACK_TYPE, Collections.emptyList(), new FluidStackHelper(), new FluidStackRenderer());
// Implementing the helper and renderer is non-trivial without platform specifics.
// If we just want to use it in a custom slot renderer, we might not strictly need to register the *collection* of ingredients,
// but we do need the type to be recognized if we pass it to addIngredient.
// Actually, addIngredient(IIngredientType<T> type, T ingredient) requires the type to be known?
// JEI documentation says: "Custom ingredients must be registered with IModIngredientRegistration"
// Given the complexity of cross-platform fluid registration in a common module without a bridge library,
// and that we only want to render a static water stack, we might be better off faking it or waiting for a bridge.
// But let's try to define the constant at least.
}
@Override
public void registerCategories(IRecipeCategoryRegistration registration) {
registration.addRecipeCategories(new DNAExtractorRecipeCategory(registration.getJeiHelpers().getGuiHelper()));
registration.addRecipeCategories(new DNAAnalyzerRecipeCategory(registration.getJeiHelpers().getGuiHelper()));
registration.addRecipeCategories(new FossilGrinderRecipeCategory(registration.getJeiHelpers().getGuiHelper()));
registration.addRecipeCategories(new FossilCleanerRecipeCategory(registration.getJeiHelpers().getGuiHelper()));
registration.addRecipeCategories(new DNAHybridizerRecipeCategory(registration.getJeiHelpers().getGuiHelper()));
registration.addRecipeCategories(new EmbryonicMachineRecipeCategory(registration.getJeiHelpers().getGuiHelper()));
registration.addRecipeCategories(new EmbryoCalcificationMachineRecipeCategory(registration.getJeiHelpers().getGuiHelper()));
registration.addRecipeCategories(new IncubatorRecipeCategory(registration.getJeiHelpers().getGuiHelper()));
}
@Override
public void registerRecipes(IRecipeRegistration registration) {
RecipeManager recipeManager = Minecraft.getInstance().level.getRecipeManager();
/*? if >1.20.1 {*/
/*List<DNAExtractorRecipe> dnaExtractorRecipes = recipeManager
.getAllRecipesFor(ModRecipes.DNA_EXTRACTOR_RECIPE_TYPE.get()).stream().map(net.minecraft.world.item.crafting.RecipeHolder::value).toList();
List<DNAAnalyzerRecipe> dnaAnalyzerRecipes = recipeManager
.getAllRecipesFor(ModRecipes.DNA_ANALYZER_RECIPE_TYPE.get()).stream().map(net.minecraft.world.item.crafting.RecipeHolder::value).toList();
List<FossilGrinderRecipe> fossilGrinderRecipes = recipeManager
.getAllRecipesFor(ModRecipes.FOSSIL_GRINDER_RECIPE_TYPE.get()).stream().map(net.minecraft.world.item.crafting.RecipeHolder::value).toList();
List<FossilCleanerRecipe> fossilCleanerRecipes = recipeManager
.getAllRecipesFor(ModRecipes.FOSSIL_CLEANER_RECIPE_TYPE.get()).stream().map(net.minecraft.world.item.crafting.RecipeHolder::value).toList();
List<DNAHybridizerRecipe> dnaHybridizerRecipes = recipeManager
.getAllRecipesFor(ModRecipes.DNA_HYBRIDIZER_RECIPE_TYPE.get()).stream().map(net.minecraft.world.item.crafting.RecipeHolder::value).toList();
List<EmbryonicMachineRecipe> embryonicMachineRecipes = recipeManager
.getAllRecipesFor(ModRecipes.EMBRYONIC_MACHINE_RECIPE_TYPE.get()).stream().map(net.minecraft.world.item.crafting.RecipeHolder::value).toList();
List<EmbryoCalcificationMachineRecipe> embryoCalcificationMachineRecipes = recipeManager
.getAllRecipesFor(ModRecipes.EMBRYO_CALCIFICATION_MACHINE_RECIPE_TYPE.get()).stream().map(net.minecraft.world.item.crafting.RecipeHolder::value).toList();
List<IncubatorRecipe> incubatorRecipes = recipeManager
.getAllRecipesFor(ModRecipes.INCUBATOR_RECIPE_TYPE.get()).stream().map(net.minecraft.world.item.crafting.RecipeHolder::value).toList();
*//*?} else {*/
List<DNAExtractorRecipe> dnaExtractorRecipes = recipeManager.getAllRecipesFor(ModRecipes.DNA_EXTRACTOR_RECIPE_TYPE.get());
List<DNAAnalyzerRecipe> dnaAnalyzerRecipes = recipeManager.getAllRecipesFor(ModRecipes.DNA_ANALYZER_RECIPE_TYPE.get());
List<FossilGrinderRecipe> fossilGrinderRecipes = recipeManager.getAllRecipesFor(ModRecipes.FOSSIL_GRINDER_RECIPE_TYPE.get());
List<FossilCleanerRecipe> fossilCleanerRecipes = recipeManager.getAllRecipesFor(ModRecipes.FOSSIL_CLEANER_RECIPE_TYPE.get());
List<DNAHybridizerRecipe> dnaHybridizerRecipes = recipeManager.getAllRecipesFor(ModRecipes.DNA_HYBRIDIZER_RECIPE_TYPE.get());
List<EmbryonicMachineRecipe> embryonicMachineRecipes = recipeManager.getAllRecipesFor(ModRecipes.EMBRYONIC_MACHINE_RECIPE_TYPE.get());
List<EmbryoCalcificationMachineRecipe> embryoCalcificationMachineRecipes = recipeManager.getAllRecipesFor(ModRecipes.EMBRYO_CALCIFICATION_MACHINE_RECIPE_TYPE.get());
List<IncubatorRecipe> incubatorRecipes = recipeManager.getAllRecipesFor(ModRecipes.INCUBATOR_RECIPE_TYPE.get());
/*?}*/
registration.addRecipes(DNAExtractorRecipeCategory.DNA_EXTRACTOR_RECIPE_RECIPE_TYPE, dnaExtractorRecipes);
registration.addRecipes(DNAAnalyzerRecipeCategory.DNA_ANALYZER_RECIPE_RECIPE_TYPE, dnaAnalyzerRecipes);
registration.addRecipes(FossilGrinderRecipeCategory.FOSSIL_GRINDER_RECIPE_RECIPE_TYPE, fossilGrinderRecipes);
registration.addRecipes(FossilCleanerRecipeCategory.FOSSIL_CLEANER_RECIPE_RECIPE_TYPE, fossilCleanerRecipes);
registration.addRecipes(DNAHybridizerRecipeCategory.DNA_HYBRIDIZER_RECIPE_RECIPE_TYPE, dnaHybridizerRecipes);
registration.addRecipes(EmbryonicMachineRecipeCategory.EMBRYONIC_MACHINE_RECIPE_RECIPE_TYPE, embryonicMachineRecipes);
registration.addRecipes(EmbryoCalcificationMachineRecipeCategory.EMBRYO_CALCIFICATION_MACHINE_RECIPE_RECIPE_TYPE, embryoCalcificationMachineRecipes);
registration.addRecipes(IncubatorRecipeCategory.INCUBATOR_RECIPE_RECIPE_TYPE, incubatorRecipes);
}
@Override
public void registerGuiHandlers(IGuiHandlerRegistration registration) {
registration.addRecipeClickArea(DNAExtractorScreen.class, 81, 31, 14, 25,
DNAExtractorRecipeCategory.DNA_EXTRACTOR_RECIPE_RECIPE_TYPE);
registration.addRecipeClickArea(DNAAnalyzerScreen.class, 81, 31, 14, 25,
DNAAnalyzerRecipeCategory.DNA_ANALYZER_RECIPE_RECIPE_TYPE);
registration.addRecipeClickArea(FossilGrinderScreen.class, 80, 25, 18, 36,
FossilGrinderRecipeCategory.FOSSIL_GRINDER_RECIPE_RECIPE_TYPE);
registration.addRecipeClickArea(FossilCleanerScreen.class, 74, 35, 29, 16,
FossilCleanerRecipeCategory.FOSSIL_CLEANER_RECIPE_RECIPE_TYPE);
registration.addRecipeClickArea(DNAHybridizerScreen.class, 105, 35, 24, 16,
DNAHybridizerRecipeCategory.DNA_HYBRIDIZER_RECIPE_RECIPE_TYPE);
registration.addRecipeClickArea(EmbryonicMachineScreen.class, 76, 35, 24, 16,
EmbryonicMachineRecipeCategory.EMBRYONIC_MACHINE_RECIPE_RECIPE_TYPE);
registration.addRecipeClickArea(EmbryoCalcificationMachineScreen.class, 76, 35, 24, 16,
EmbryoCalcificationMachineRecipeCategory.EMBRYO_CALCIFICATION_MACHINE_RECIPE_RECIPE_TYPE);
registration.addRecipeClickArea(IncubatorScreen.class, 51, 16, 72, 16,
IncubatorRecipeCategory.INCUBATOR_RECIPE_RECIPE_TYPE);
}
@Override
public void registerRecipeCatalysts(IRecipeCatalystRegistration registration) {
registration.addRecipeCatalyst(new ItemStack(ModBlocks.DNA_EXTRACTOR.get()), DNAExtractorRecipeCategory.DNA_EXTRACTOR_RECIPE_RECIPE_TYPE);
registration.addRecipeCatalyst(new ItemStack(ModBlocks.DNA_ANALYZER.get()), DNAAnalyzerRecipeCategory.DNA_ANALYZER_RECIPE_RECIPE_TYPE);
registration.addRecipeCatalyst(new ItemStack(ModBlocks.FOSSIL_GRINDER.get()), FossilGrinderRecipeCategory.FOSSIL_GRINDER_RECIPE_RECIPE_TYPE);
registration.addRecipeCatalyst(new ItemStack(ModBlocks.FOSSIL_CLEANER.get()), FossilCleanerRecipeCategory.FOSSIL_CLEANER_RECIPE_RECIPE_TYPE);
registration.addRecipeCatalyst(new ItemStack(ModBlocks.DNA_HYBRIDIZER.get()), DNAHybridizerRecipeCategory.DNA_HYBRIDIZER_RECIPE_RECIPE_TYPE);
registration.addRecipeCatalyst(new ItemStack(ModBlocks.EMBRYONIC_MACHINE.get()), EmbryonicMachineRecipeCategory.EMBRYONIC_MACHINE_RECIPE_RECIPE_TYPE);
registration.addRecipeCatalyst(new ItemStack(ModBlocks.EMBRYO_CALCIFICATION_MACHINE.get()), EmbryoCalcificationMachineRecipeCategory.EMBRYO_CALCIFICATION_MACHINE_RECIPE_RECIPE_TYPE);
registration.addRecipeCatalyst(new ItemStack(ModBlocks.INCUBATOR.get()), IncubatorRecipeCategory.INCUBATOR_RECIPE_RECIPE_TYPE);
registration.addRecipeCatalyst(new ItemStack(ModBlocks.WHITE_DNA_EXTRACTOR.get()), DNAExtractorRecipeCategory.DNA_EXTRACTOR_RECIPE_RECIPE_TYPE);
registration.addRecipeCatalyst(new ItemStack(ModBlocks.WHITE_DNA_ANALYZER.get()), DNAAnalyzerRecipeCategory.DNA_ANALYZER_RECIPE_RECIPE_TYPE);
registration.addRecipeCatalyst(new ItemStack(ModBlocks.WHITE_FOSSIL_GRINDER.get()), FossilGrinderRecipeCategory.FOSSIL_GRINDER_RECIPE_RECIPE_TYPE);
registration.addRecipeCatalyst(new ItemStack(ModBlocks.WHITE_FOSSIL_CLEANER.get()), FossilCleanerRecipeCategory.FOSSIL_CLEANER_RECIPE_RECIPE_TYPE);
registration.addRecipeCatalyst(new ItemStack(ModBlocks.WHITE_DNA_HYBRIDIZER.get()), DNAHybridizerRecipeCategory.DNA_HYBRIDIZER_RECIPE_RECIPE_TYPE);
registration.addRecipeCatalyst(new ItemStack(ModBlocks.WHITE_EMBRYONIC_MACHINE.get()), EmbryonicMachineRecipeCategory.EMBRYONIC_MACHINE_RECIPE_RECIPE_TYPE);
registration.addRecipeCatalyst(new ItemStack(ModBlocks.WHITE_EMBRYO_CALCIFICATION_MACHINE.get()), EmbryoCalcificationMachineRecipeCategory.EMBRYO_CALCIFICATION_MACHINE_RECIPE_RECIPE_TYPE);
registration.addRecipeCatalyst(new ItemStack(ModBlocks.WHITE_INCUBATOR.get()), IncubatorRecipeCategory.INCUBATOR_RECIPE_RECIPE_TYPE);
}
@Override
public void registerRecipeTransferHandlers(IRecipeTransferRegistration registration) {
registration.addRecipeTransferHandler(
DNAExtractorMenu.class,
ModMenuTypes.DNA_EXTRACTOR_MENU.get(),
DNAExtractorRecipeCategory.DNA_EXTRACTOR_RECIPE_RECIPE_TYPE,
36, // The index of the FIRST recipe input slot in your Menu (slot 36)
2, // The NUMBER of recipe input slots (slots 36, 37)
0, // The index where the player inventory slots START (slot 0)
36 // The NUMBER of player inventory slots to check (slots 0-35)
);
registration.addRecipeTransferHandler(
DNAAnalyzerMenu.class,
ModMenuTypes.DNA_ANALYZER_MENU.get(),
DNAAnalyzerRecipeCategory.DNA_ANALYZER_RECIPE_RECIPE_TYPE,
36, // The index of the FIRST recipe input slot in your Menu (slot 36)
2, // The NUMBER of recipe input slots (slots 36, 37)
0, // The index where the player inventory slots START (slot 0)
36 // The NUMBER of player inventory slots to check (slots 0-35)
);
registration.addRecipeTransferHandler(
FossilGrinderMenu.class,
ModMenuTypes.FOSSIL_GRINDER_MENU.get(),
FossilGrinderRecipeCategory.FOSSIL_GRINDER_RECIPE_RECIPE_TYPE,
36, // The index of the FIRST recipe input slot in your Menu (slot 36)
1, // The NUMBER of recipe input slots (slots 36, 37)
0, // The index where the player inventory slots START (slot 0)
36 // The NUMBER of player inventory slots to check (slots 0-35)
);
registration.addRecipeTransferHandler(
FossilCleanerMenu.class,
ModMenuTypes.FOSSIL_CLEANER_MENU.get(),
FossilCleanerRecipeCategory.FOSSIL_CLEANER_RECIPE_RECIPE_TYPE,
36, // The index of the FIRST recipe input slot in your Menu (slot 36)
2, // The NUMBER of recipe input slots (slots 36, 37)
0, // The index where the player inventory slots START (slot 0)
36 // The NUMBER of player inventory slots to check (slots 0-35)
);
registration.addRecipeTransferHandler(
DNAHybridizerMenu.class,
ModMenuTypes.DNA_HYBRIDIZER_MENU.get(),
DNAHybridizerRecipeCategory.DNA_HYBRIDIZER_RECIPE_RECIPE_TYPE,
36, // The index of the FIRST recipe input slot in your Menu (slot 36)
9, // The NUMBER of recipe input slots (slots 36, 37)
0, // The index where the player inventory slots START (slot 0)
36 // The NUMBER of player inventory slots to check (slots 0-35)
);
registration.addRecipeTransferHandler(
EmbryonicMachineMenu.class,
ModMenuTypes.EMBRYONIC_MACHINE_MENU.get(),
EmbryonicMachineRecipeCategory.EMBRYONIC_MACHINE_RECIPE_RECIPE_TYPE,
36, // The index of the FIRST recipe input slot in your Menu (slot 36)
2, // The NUMBER of recipe input slots (slots 36, 37)
0, // The index where the player inventory slots START (slot 0)
36 // The NUMBER of player inventory slots to check (slots 0-35)
);
registration.addRecipeTransferHandler(
EmbryoCalcificationMachineMenu.class,
ModMenuTypes.EMBRYO_CALCIFICATION_MACHINE_MENU.get(),
EmbryoCalcificationMachineRecipeCategory.EMBRYO_CALCIFICATION_MACHINE_RECIPE_RECIPE_TYPE,
36, // The index of the FIRST recipe input slot in your Menu (slot 36)
2, // The NUMBER of recipe input slots (slots 36, 37)
0, // The index where the player inventory slots START (slot 0)
36 // The NUMBER of player inventory slots to check (slots 0-35)
);
registration.addRecipeTransferHandler(
IncubatorMenu.class,
ModMenuTypes.INCUBATOR_MENU.get(),
IncubatorRecipeCategory.INCUBATOR_RECIPE_RECIPE_TYPE,
36, // The index of the FIRST recipe input slot in your Menu (slot 36)
3, // The NUMBER of recipe input slots (slots 36, 37)
0, // The index where the player inventory slots START (slot 0)
36 // The NUMBER of player inventory slots to check (slots 0-35)
);
}
}
@@ -0,0 +1,426 @@
package net.cmr.jurassicrevived.compat;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.block.custom.*;
import net.cmr.jurassicrevived.block.entity.custom.*;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
import net.minecraft.world.level.block.entity.BlockEntity;
import snownee.jade.api.*;
import snownee.jade.api.config.IPluginConfig;
import snownee.jade.api.ui.*;
//? if <=1.20.1 {
import net.minecraft.client.gui.navigation.ScreenDirection;
import snownee.jade.impl.ui.ProgressStyle;
//?}
@WailaPlugin
public class MachineJadePlugin implements IWailaPlugin {
private static final ResourceLocation UID = Constants.rl("machine_progress");
private static final String NBT_PROGRESS = "jr_progress";
private static final String NBT_MAX = "jr_max";
@Override
public void registerClient(IWailaClientRegistration reg) {
reg.registerBlockComponent(new snownee.jade.api.IBlockComponentProvider() {
//? if >1.20.1 {
/*@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (data == null || !data.contains(NBT_PROGRESS) || !data.contains(NBT_MAX)) return;
int progress = Math.max(0, data.getInt(NBT_PROGRESS));
int max = Math.max(1, data.getInt(NBT_MAX));
float ratio = Mth.clamp(progress / (float) max, 0.0f, 1.0f);
IElementHelper h = IElementHelper.get();
ProgressStyle style = h.progressStyle()
.color(0xFFFFFFFF, 0xFFFFFFFF)
.direction(ScreenDirection.RIGHT)
.fitContentX(true)
.fitContentY(true);
BoxStyle box = BoxStyle.getNestedBox();
IElement bar = h.progress(ratio, Component.empty(), style, box, true);
tooltip.add(bar);
}
*///?} else {
@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (!data.contains(NBT_PROGRESS) || !data.contains(NBT_MAX)) return;
int progress = data.getInt(NBT_PROGRESS);
int max = Math.max(1, data.getInt(NBT_MAX));
float ratio = Mth.clamp(progress / (float) max, 0.0f, 1.0f);
IElementHelper h = tooltip.getElementHelper();
IProgressStyle pStyle = h.progressStyle()
.color(0xFFFFFFFF)
.textColor(0xFFFFFFFF);
IBoxStyle box = new ThickBorderBox(1.0f);
tooltip.add(h.progress(ratio, Component.empty(), pStyle, box, false));
}
//?}
@Override
public ResourceLocation getUid() {
return UID;
}
}, DNAExtractorBlock.class
);
reg.registerBlockComponent(new snownee.jade.api.IBlockComponentProvider() {
//? if >1.20.1 {
/*@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (data == null || !data.contains(NBT_PROGRESS) || !data.contains(NBT_MAX)) return;
int progress = Math.max(0, data.getInt(NBT_PROGRESS));
int max = Math.max(1, data.getInt(NBT_MAX));
float ratio = Mth.clamp(progress / (float) max, 0.0f, 1.0f);
IElementHelper h = IElementHelper.get();
ProgressStyle style = h.progressStyle()
.color(0xFFFFFFFF, 0xFFFFFFFF)
.direction(ScreenDirection.RIGHT)
.fitContentX(true)
.fitContentY(true);
BoxStyle box = BoxStyle.getNestedBox();
IElement bar = h.progress(ratio, Component.empty(), style, box, true);
tooltip.add(bar);
}
*///?} else {
@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (!data.contains(NBT_PROGRESS) || !data.contains(NBT_MAX)) return;
int progress = data.getInt(NBT_PROGRESS);
int max = Math.max(1, data.getInt(NBT_MAX));
float ratio = Mth.clamp(progress / (float) max, 0.0f, 1.0f);
IElementHelper h = tooltip.getElementHelper();
IProgressStyle pStyle = h.progressStyle()
.color(0xFFFFFFFF)
.textColor(0xFFFFFFFF);
IBoxStyle box = new ThickBorderBox(1.0f);
tooltip.add(h.progress(ratio, Component.empty(), pStyle, box, false));
}
//?}
@Override
public ResourceLocation getUid() {
return UID;
}
}, DNAAnalyzerBlock.class
);
reg.registerBlockComponent(new snownee.jade.api.IBlockComponentProvider() {
//? if >1.20.1 {
/*@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (data == null || !data.contains(NBT_PROGRESS) || !data.contains(NBT_MAX)) return;
int progress = Math.max(0, data.getInt(NBT_PROGRESS));
int max = Math.max(1, data.getInt(NBT_MAX));
float ratio = Mth.clamp(progress / (float) max, 0.0f, 1.0f);
IElementHelper h = IElementHelper.get();
ProgressStyle style = h.progressStyle()
.color(0xFFFFFFFF, 0xFFFFFFFF)
.direction(ScreenDirection.RIGHT)
.fitContentX(true)
.fitContentY(true);
BoxStyle box = BoxStyle.getNestedBox();
IElement bar = h.progress(ratio, Component.empty(), style, box, true);
tooltip.add(bar);
}
*///?} else {
@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (!data.contains(NBT_PROGRESS) || !data.contains(NBT_MAX)) return;
int progress = data.getInt(NBT_PROGRESS);
int max = Math.max(1, data.getInt(NBT_MAX));
float ratio = Mth.clamp(progress / (float) max, 0.0f, 1.0f);
IElementHelper h = tooltip.getElementHelper();
IProgressStyle pStyle = h.progressStyle()
.color(0xFFFFFFFF)
.textColor(0xFFFFFFFF);
IBoxStyle box = new ThickBorderBox(1.0f);
tooltip.add(h.progress(ratio, Component.empty(), pStyle, box, false));
}
//?}
@Override
public ResourceLocation getUid() {
return UID;
}
}, DNAHybridizerBlock.class
);
reg.registerBlockComponent(new snownee.jade.api.IBlockComponentProvider() {
//? if >1.20.1 {
/*@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (data == null || !data.contains(NBT_PROGRESS) || !data.contains(NBT_MAX)) return;
int progress = Math.max(0, data.getInt(NBT_PROGRESS));
int max = Math.max(1, data.getInt(NBT_MAX));
float ratio = Mth.clamp(progress / (float) max, 0.0f, 1.0f);
IElementHelper h = IElementHelper.get();
ProgressStyle style = h.progressStyle()
.color(0xFFFFFFFF, 0xFFFFFFFF)
.direction(ScreenDirection.RIGHT)
.fitContentX(true)
.fitContentY(true);
BoxStyle box = BoxStyle.getNestedBox();
IElement bar = h.progress(ratio, Component.empty(), style, box, true);
tooltip.add(bar);
}
*///?} else {
@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (!data.contains(NBT_PROGRESS) || !data.contains(NBT_MAX)) return;
int progress = data.getInt(NBT_PROGRESS);
int max = Math.max(1, data.getInt(NBT_MAX));
float ratio = Mth.clamp(progress / (float) max, 0.0f, 1.0f);
IElementHelper h = tooltip.getElementHelper();
IProgressStyle pStyle = h.progressStyle()
.color(0xFFFFFFFF)
.textColor(0xFFFFFFFF);
IBoxStyle box = new ThickBorderBox(1.0f);
tooltip.add(h.progress(ratio, Component.empty(), pStyle, box, false));
}
//?}
@Override
public ResourceLocation getUid() {
return UID;
}
}, EmbryoCalcificationMachineBlock.class
);
reg.registerBlockComponent(new snownee.jade.api.IBlockComponentProvider() {
//? if >1.20.1 {
/*@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (data == null || !data.contains(NBT_PROGRESS) || !data.contains(NBT_MAX)) return;
int progress = Math.max(0, data.getInt(NBT_PROGRESS));
int max = Math.max(1, data.getInt(NBT_MAX));
float ratio = Mth.clamp(progress / (float) max, 0.0f, 1.0f);
IElementHelper h = IElementHelper.get();
ProgressStyle style = h.progressStyle()
.color(0xFFFFFFFF, 0xFFFFFFFF)
.direction(ScreenDirection.RIGHT)
.fitContentX(true)
.fitContentY(true);
BoxStyle box = BoxStyle.getNestedBox();
IElement bar = h.progress(ratio, Component.empty(), style, box, true);
tooltip.add(bar);
}
*///?} else {
@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (!data.contains(NBT_PROGRESS) || !data.contains(NBT_MAX)) return;
int progress = data.getInt(NBT_PROGRESS);
int max = Math.max(1, data.getInt(NBT_MAX));
float ratio = Mth.clamp(progress / (float) max, 0.0f, 1.0f);
IElementHelper h = tooltip.getElementHelper();
IProgressStyle pStyle = h.progressStyle()
.color(0xFFFFFFFF)
.textColor(0xFFFFFFFF);
IBoxStyle box = new ThickBorderBox(1.0f);
tooltip.add(h.progress(ratio, Component.empty(), pStyle, box, false));
}
//?}
@Override
public ResourceLocation getUid() {
return UID;
}
}, EmbryonicMachineBlock.class
);
reg.registerBlockComponent(new snownee.jade.api.IBlockComponentProvider() {
//? if >1.20.1 {
/*@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (data == null || !data.contains(NBT_PROGRESS) || !data.contains(NBT_MAX)) return;
int progress = Math.max(0, data.getInt(NBT_PROGRESS));
int max = Math.max(1, data.getInt(NBT_MAX));
float ratio = Mth.clamp(progress / (float) max, 0.0f, 1.0f);
IElementHelper h = IElementHelper.get();
ProgressStyle style = h.progressStyle()
.color(0xFFFFFFFF, 0xFFFFFFFF)
.direction(ScreenDirection.RIGHT)
.fitContentX(true)
.fitContentY(true);
BoxStyle box = BoxStyle.getNestedBox();
IElement bar = h.progress(ratio, Component.empty(), style, box, true);
tooltip.add(bar);
}
*///?} else {
@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (!data.contains(NBT_PROGRESS) || !data.contains(NBT_MAX)) return;
int progress = data.getInt(NBT_PROGRESS);
int max = Math.max(1, data.getInt(NBT_MAX));
float ratio = Mth.clamp(progress / (float) max, 0.0f, 1.0f);
IElementHelper h = tooltip.getElementHelper();
IProgressStyle pStyle = h.progressStyle()
.color(0xFFFFFFFF)
.textColor(0xFFFFFFFF);
IBoxStyle box = new ThickBorderBox(1.0f);
tooltip.add(h.progress(ratio, Component.empty(), pStyle, box, false));
}
//?}
@Override
public ResourceLocation getUid() {
return UID;
}
}, FossilGrinderBlock.class
);
reg.registerBlockComponent(new snownee.jade.api.IBlockComponentProvider() {
//? if >1.20.1 {
/*@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (data == null || !data.contains(NBT_PROGRESS) || !data.contains(NBT_MAX)) return;
int progress = Math.max(0, data.getInt(NBT_PROGRESS));
int max = Math.max(1, data.getInt(NBT_MAX));
float ratio = Mth.clamp(progress / (float) max, 0.0f, 1.0f);
IElementHelper h = IElementHelper.get();
ProgressStyle style = h.progressStyle()
.color(0xFFFFFFFF, 0xFFFFFFFF)
.direction(ScreenDirection.RIGHT)
.fitContentX(true)
.fitContentY(true);
BoxStyle box = BoxStyle.getNestedBox();
IElement bar = h.progress(ratio, Component.empty(), style, box, true);
tooltip.add(bar);
}
*///?} else {
@Override
public void appendTooltip(ITooltip tooltip, BlockAccessor accessor, IPluginConfig config) {
CompoundTag data = accessor.getServerData();
if (!data.contains(NBT_PROGRESS) || !data.contains(NBT_MAX)) return;
int progress = data.getInt(NBT_PROGRESS);
int max = Math.max(1, data.getInt(NBT_MAX));
float ratio = Mth.clamp(progress / (float) max, 0.0f, 1.0f);
IElementHelper h = tooltip.getElementHelper();
IProgressStyle pStyle = h.progressStyle()
.color(0xFFFFFFFF)
.textColor(0xFFFFFFFF);
IBoxStyle box = new ThickBorderBox(1.0f);
tooltip.add(h.progress(ratio, Component.empty(), pStyle, box, false));
}
//?}
@Override
public ResourceLocation getUid() {
return UID;
}
}, IncubatorBlock.class
);
}
@Override
public void register(IWailaCommonRegistration reg) {
IServerDataProvider<BlockAccessor> provider = new IServerDataProvider<>() {
@Override
public void appendServerData(CompoundTag data, BlockAccessor accessor) {
BlockEntity be = accessor.getBlockEntity();
if (be == null) return;
if (be instanceof IncubatorBlockEntity inc) {
int[] p = getArray(inc, "progress");
int[] m = getArray(inc, "maxProgress");
if (p != null && m != null && p.length == m.length && p.length > 0) {
float best = 0f;
for (int i = 0; i < p.length; i++) {
int max = Math.max(1, m[i]);
best = Math.max(best, p[i] / (float) max);
}
int scaledMax = 1000;
int scaledProgress = Math.round(best * scaledMax);
put(data, scaledProgress, scaledMax);
}
return;
}
int progress = getInt(be, "progress");
int max = Math.max(1, getInt(be, "maxProgress"));
put(data, progress, max);
}
private void put(CompoundTag data, int progress, int max) {
data.putInt(NBT_PROGRESS, progress);
data.putInt(NBT_MAX, Math.max(1, max));
}
private int getInt(Object be, String field) {
try {
var f = be.getClass().getDeclaredField(field);
f.setAccessible(true);
return f.getInt(be);
} catch (Throwable t) {
return 0;
}
}
private int[] getArray(Object be, String field) {
try {
var f = be.getClass().getDeclaredField(field);
f.setAccessible(true);
return (int[]) f.get(be);
} catch (Throwable t) {
return null;
}
}
@Override
public ResourceLocation getUid() {
return UID;
}
};
// Register individually per BE class
reg.registerBlockDataProvider(provider, DNAExtractorBlockEntity.class);
reg.registerBlockDataProvider(provider, DNAAnalyzerBlockEntity.class);
reg.registerBlockDataProvider(provider, DNAHybridizerBlockEntity.class);
reg.registerBlockDataProvider(provider, EmbryoCalcificationMachineBlockEntity.class);
reg.registerBlockDataProvider(provider, EmbryonicMachineBlockEntity.class);
reg.registerBlockDataProvider(provider, FossilCleanerBlockEntity.class);
reg.registerBlockDataProvider(provider, FossilGrinderBlockEntity.class);
reg.registerBlockDataProvider(provider, IncubatorBlockEntity.class);
}
}
@@ -0,0 +1,41 @@
package net.cmr.jurassicrevived.compat;
//? if <=1.20.1 {
import net.minecraft.client.gui.GuiGraphics;
import snownee.jade.api.ui.IBoxStyle;
// Simple solid border with configurable width and color (ARGB)
public final class ThickBorderBox implements IBoxStyle {
private final float width;
private final int borderColor; // ARGB, e.g. 0xFFFFFFFF for white
public ThickBorderBox(float width) {
this(width, 0xFFFFFFFF); // default opaque white
}
public ThickBorderBox(float width, int borderColor) {
this.width = width;
this.borderColor = borderColor;
}
@Override
public float borderWidth() {
return width;
}
@Override
public void render(GuiGraphics g, float x, float y, float w, float h) {
if (width <= 0) return;
float r = x + w;
float b = y + h;
// top
g.fill((int) x, (int) y, (int) r, (int) (y + width), borderColor);
// bottom
g.fill((int) x, (int) (b - width), (int) r, (int) b, borderColor);
// left
g.fill((int) x, (int) (y + width), (int) (x + width), (int) (b - width), borderColor);
// right
g.fill((int) (r - width), (int) (y + width), (int) r, (int) (b - width), borderColor);
}
}
//?}
@@ -4,6 +4,10 @@ public final class JRConfig {
// Example options (replace with your real ones) // Example options (replace with your real ones)
public boolean enableDinosaurs = true; public boolean enableDinosaurs = true;
public int spawnWeight = 10; public int spawnWeight = 10;
public boolean requirePower = true;
public int fePerSecond = 1000;
public int itemsPerSecond = 10;
public int milliBucketsPerSecond = 1000;
public JRConfig() { public JRConfig() {
} }
@@ -1,22 +1,427 @@
package net.cmr.jurassicrevived.entity; package net.cmr.jurassicrevived.entity;
import dev.architectury.registry.level.entity.EntityAttributeRegistry;
import dev.architectury.registry.level.entity.SpawnPlacementsRegistry;
import dev.architectury.registry.registries.DeferredRegister; import dev.architectury.registry.registries.DeferredRegister;
import dev.architectury.registry.registries.RegistrySupplier;
import net.cmr.jurassicrevived.Constants; import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.config.JRConfigManager;
import net.cmr.jurassicrevived.entity.custom.*;
import net.minecraft.core.registries.Registries; import net.minecraft.core.registries.Registries;
import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.MobCategory;
/*? if >1.20.1 {*/
/*import net.minecraft.world.entity.SpawnPlacementTypes;
*//*?} else {*/
import net.minecraft.world.entity.SpawnPlacements;
/*?}*/
import net.minecraft.world.entity.animal.Animal;
import net.minecraft.world.level.levelgen.Heightmap;
public class ModEntities { public class ModEntities {
public static final DeferredRegister<EntityType<?>> ENTITIES = public static final DeferredRegister<EntityType<?>> ENTITIES =
DeferredRegister.create(Constants.MOD_ID, Registries.ENTITY_TYPE); DeferredRegister.create(Constants.MOD_ID, Registries.ENTITY_TYPE);
// --- Example (Generic Entity) --- public static final RegistrySupplier<EntityType<SeatEntity>> SEAT =
// You will need your own Entity class (e.g., VelociraptorEntity::new) ENTITIES.register("seat", () ->
/* EntityType.Builder.<SeatEntity>of(SeatEntity::new, MobCategory.MISC)
public static final RegistrySupplier<EntityType<VelociraptorEntity>> VELOCIRAPTOR = ENTITIES.register("velociraptor", .sized(0.001f, 0.001f)
() -> EntityType.Builder.of(VelociraptorEntity::new, MobCategory.CREATURE) .clientTrackingRange(16)
.sized(1.0f, 2.0f) .updateInterval(1)
.build(Constants.MOD_ID + ":velociraptor")); .build("jurassicrevived:seat")
*/ );
public static final RegistrySupplier<EntityType<AlbertosaurusEntity>> ALBERTOSAURUS =
ENTITIES.register("albertosaurus", () -> EntityType.Builder.of(AlbertosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 3.0f).build("albertosaurus"));
public static final RegistrySupplier<EntityType<ApatosaurusEntity>> APATOSAURUS =
ENTITIES.register("apatosaurus", () -> EntityType.Builder.of(ApatosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("apatosaurus"));
public static final RegistrySupplier<EntityType<BrachiosaurusEntity>> BRACHIOSAURUS =
ENTITIES.register("brachiosaurus", () -> EntityType.Builder.of(BrachiosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("brachiosaurus"));
public static final RegistrySupplier<EntityType<CeratosaurusEntity>> CERATOSAURUS =
ENTITIES.register("ceratosaurus", () -> EntityType.Builder.of(CeratosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("ceratosaurus"));
public static final RegistrySupplier<EntityType<CompsognathusEntity>> COMPSOGNATHUS =
ENTITIES.register("compsognathus", () -> EntityType.Builder.of(CompsognathusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("compsognathus"));
public static final RegistrySupplier<EntityType<DilophosaurusEntity>> DILOPHOSAURUS =
ENTITIES.register("dilophosaurus", () -> EntityType.Builder.of(DilophosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("dilophosaurus"));
public static final RegistrySupplier<EntityType<DiplodocusEntity>> DIPLODOCUS =
ENTITIES.register("diplodocus", () -> EntityType.Builder.of(DiplodocusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("diplodocus"));
public static final RegistrySupplier<EntityType<FDuckEntity>> FDUCK =
ENTITIES.register("fduck", () -> EntityType.Builder.of(FDuckEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("fduck"));
public static final RegistrySupplier<EntityType<GallimimusEntity>> GALLIMIMUS =
ENTITIES.register("gallimimus", () -> EntityType.Builder.of(GallimimusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("gallimimus"));
public static final RegistrySupplier<EntityType<IndominusRexEntity>> INDOMINUS_REX =
ENTITIES.register("indominus_rex", () -> EntityType.Builder.of(IndominusRexEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("indominus_rex"));
public static final RegistrySupplier<EntityType<OuranosaurusEntity>> OURANOSAURUS =
ENTITIES.register("ouranosaurus", () -> EntityType.Builder.of(OuranosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("ouranosaurus"));
public static final RegistrySupplier<EntityType<ParasaurolophusEntity>> PARASAUROLOPHUS =
ENTITIES.register("parasaurolophus", () -> EntityType.Builder.of(ParasaurolophusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("parasaurolophus"));
public static final RegistrySupplier<EntityType<SpinosaurusEntity>> SPINOSAURUS =
ENTITIES.register("spinosaurus", () -> EntityType.Builder.of(SpinosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("spinosaurus"));
public static final RegistrySupplier<EntityType<TriceratopsEntity>> TRICERATOPS =
ENTITIES.register("triceratops", () -> EntityType.Builder.of(TriceratopsEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("triceratops"));
public static final RegistrySupplier<EntityType<TyrannosaurusRexEntity>> TYRANNOSAURUS_REX =
ENTITIES.register("tyrannosaurus_rex", () -> EntityType.Builder.of(TyrannosaurusRexEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("tyrannosaurus_rex"));
public static final RegistrySupplier<EntityType<VelociraptorEntity>> VELOCIRAPTOR =
ENTITIES.register("velociraptor", () -> EntityType.Builder.of(VelociraptorEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("velociraptor"));
public static final RegistrySupplier<EntityType<BaryonyxEntity>> BARYONYX =
ENTITIES.register("baryonyx", () -> EntityType.Builder.of(BaryonyxEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("baryonyx"));
public static final RegistrySupplier<EntityType<CarnotaurusEntity>> CARNOTAURUS =
ENTITIES.register("carnotaurus", () -> EntityType.Builder.of(CarnotaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("carnotaurus"));
public static final RegistrySupplier<EntityType<ConcavenatorEntity>> CONCAVENATOR =
ENTITIES.register("concavenator", () -> EntityType.Builder.of(ConcavenatorEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("concavenator"));
public static final RegistrySupplier<EntityType<DeinonychusEntity>> DEINONYCHUS =
ENTITIES.register("deinonychus", () -> EntityType.Builder.of(DeinonychusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("deinonychus"));
public static final RegistrySupplier<EntityType<EdmontosaurusEntity>> EDMONTOSAURUS =
ENTITIES.register("edmontosaurus", () -> EntityType.Builder.of(EdmontosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("edmontosaurus"));
public static final RegistrySupplier<EntityType<GiganotosaurusEntity>> GIGANOTOSAURUS =
ENTITIES.register("giganotosaurus", () -> EntityType.Builder.of(GiganotosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("giganotosaurus"));
public static final RegistrySupplier<EntityType<GuanlongEntity>> GUANLONG =
ENTITIES.register("guanlong", () -> EntityType.Builder.of(GuanlongEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("guanlong"));
public static final RegistrySupplier<EntityType<HerrerasaurusEntity>> HERRERASAURUS =
ENTITIES.register("herrerasaurus", () -> EntityType.Builder.of(HerrerasaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("herrerasaurus"));
public static final RegistrySupplier<EntityType<MajungasaurusEntity>> MAJUNGASAURUS =
ENTITIES.register("majungasaurus", () -> EntityType.Builder.of(MajungasaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("majungasaurus"));
public static final RegistrySupplier<EntityType<ProcompsognathusEntity>> PROCOMPSOGNATHUS =
ENTITIES.register("procompsognathus", () -> EntityType.Builder.of(ProcompsognathusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("procompsognathus"));
public static final RegistrySupplier<EntityType<ProtoceratopsEntity>> PROTOCERATOPS =
ENTITIES.register("protoceratops", () -> EntityType.Builder.of(ProtoceratopsEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("protoceratops"));
public static final RegistrySupplier<EntityType<ArambourgianiaEntity>> ARAMBOURGIANIA =
ENTITIES.register("arambourgiania", () -> EntityType.Builder.of(ArambourgianiaEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("arambourgiania"));
public static final RegistrySupplier<EntityType<CearadactylusEntity>> CEARADACTYLUS =
ENTITIES.register("cearadactylus", () -> EntityType.Builder.of(CearadactylusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("cearadactylus"));
public static final RegistrySupplier<EntityType<DimorphodonEntity>> DIMORPHODON =
ENTITIES.register("dimorphodon", () -> EntityType.Builder.of(DimorphodonEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("dimorphodon"));
public static final RegistrySupplier<EntityType<GeosternbergiaEntity>> GEOSTERNBERGIA =
ENTITIES.register("geosternbergia", () -> EntityType.Builder.of(GeosternbergiaEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("geosternbergia"));
public static final RegistrySupplier<EntityType<GuidracoEntity>> GUIDRACO =
ENTITIES.register("guidraco", () -> EntityType.Builder.of(GuidracoEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("guidraco"));
public static final RegistrySupplier<EntityType<LudodactylusEntity>> LUDODACTYLUS =
ENTITIES.register("ludodactylus", () -> EntityType.Builder.of(LudodactylusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("ludodactylus"));
public static final RegistrySupplier<EntityType<MoganopterusEntity>> MOGANOPTERUS =
ENTITIES.register("moganopterus", () -> EntityType.Builder.of(MoganopterusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("moganopterus"));
public static final RegistrySupplier<EntityType<NyctosaurusEntity>> NYCTOSAURUS =
ENTITIES.register("nyctosaurus", () -> EntityType.Builder.of(NyctosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("nyctosaurus"));
public static final RegistrySupplier<EntityType<PteranodonEntity>> PTERANODON =
ENTITIES.register("pteranodon", () -> EntityType.Builder.of(PteranodonEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("pteranodon"));
public static final RegistrySupplier<EntityType<PterodaustroEntity>> PTERODAUSTRO =
ENTITIES.register("pterodaustro", () -> EntityType.Builder.of(PterodaustroEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("pterodaustro"));
public static final RegistrySupplier<EntityType<QuetzalcoatlusEntity>> QUETZALCOATLUS =
ENTITIES.register("quetzalcoatlus", () -> EntityType.Builder.of(QuetzalcoatlusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("quetzalcoatlus"));
public static final RegistrySupplier<EntityType<TapejaraEntity>> TAPEJARA =
ENTITIES.register("tapejara", () -> EntityType.Builder.of(TapejaraEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("tapejara"));
public static final RegistrySupplier<EntityType<TropeognathusEntity>> TROPEOGNATHUS =
ENTITIES.register("tropeognathus", () -> EntityType.Builder.of(TropeognathusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("tropeognathus"));
public static final RegistrySupplier<EntityType<TupuxuaraEntity>> TUPUXUARA =
ENTITIES.register("tupuxuara", () -> EntityType.Builder.of(TupuxuaraEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("tupuxuara"));
public static final RegistrySupplier<EntityType<ZhenyuanopterusEntity>> ZHENYUANOPTERUS =
ENTITIES.register("zhenyuanopterus", () -> EntityType.Builder.of(ZhenyuanopterusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("zhenyuanopterus"));
public static final RegistrySupplier<EntityType<RugopsEntity>> RUGOPS =
ENTITIES.register("rugops", () -> EntityType.Builder.of(RugopsEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("rugops"));
public static final RegistrySupplier<EntityType<ShantungosaurusEntity>> SHANTUNGOSAURUS =
ENTITIES.register("shantungosaurus", () -> EntityType.Builder.of(ShantungosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("shantungosaurus"));
public static final RegistrySupplier<EntityType<StegosaurusEntity>> STEGOSAURUS =
ENTITIES.register("stegosaurus", () -> EntityType.Builder.of(StegosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("stegosaurus"));
public static final RegistrySupplier<EntityType<StyracosaurusEntity>> STYRACOSAURUS =
ENTITIES.register("styracosaurus", () -> EntityType.Builder.of(StyracosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("styracosaurus"));
public static final RegistrySupplier<EntityType<TherizinosaurusEntity>> THERIZINOSAURUS =
ENTITIES.register("therizinosaurus", () -> EntityType.Builder.of(TherizinosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("therizinosaurus"));
public static final RegistrySupplier<EntityType<DistortusRexEntity>> DISTORTUS_REX =
ENTITIES.register("distortus_rex", () -> EntityType.Builder.of(DistortusRexEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("distortus_rex"));
public static final RegistrySupplier<EntityType<ChickenosaurusEntity>> CHICKENOSAURUS =
ENTITIES.register("chickenosaurus", () -> EntityType.Builder.of(ChickenosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("chickenosaurus"));
public static final RegistrySupplier<EntityType<AllosaurusEntity>> ALLOSAURUS =
ENTITIES.register("allosaurus", () -> EntityType.Builder.of(AllosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("allosaurus"));
public static final RegistrySupplier<EntityType<AlvarezsaurusEntity>> ALVAREZSAURUS =
ENTITIES.register("alvarezsaurus", () -> EntityType.Builder.of(AlvarezsaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("alvarezsaurus"));
public static final RegistrySupplier<EntityType<AnkylosaurusEntity>> ANKYLOSAURUS =
ENTITIES.register("ankylosaurus", () -> EntityType.Builder.of(AnkylosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("ankylosaurus"));
public static final RegistrySupplier<EntityType<CarcharodontosaurusEntity>> CARCHARODONTOSAURUS =
ENTITIES.register("carcharodontosaurus", () -> EntityType.Builder.of(CarcharodontosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("carcharodontosaurus"));
public static final RegistrySupplier<EntityType<ChasmosaurusEntity>> CHASMOSAURUS =
ENTITIES.register("chasmosaurus", () -> EntityType.Builder.of(ChasmosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("chasmosaurus"));
public static final RegistrySupplier<EntityType<CoelophysisEntity>> COELOPHYSIS =
ENTITIES.register("coelophysis", () -> EntityType.Builder.of(CoelophysisEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("coelophysis"));
public static final RegistrySupplier<EntityType<CoelurusEntity>> COELURUS =
ENTITIES.register("coelurus", () -> EntityType.Builder.of(CoelurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("coelurus"));
public static final RegistrySupplier<EntityType<CorythosaurusEntity>> CORYTHOSAURUS =
ENTITIES.register("corythosaurus", () -> EntityType.Builder.of(CorythosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("corythosaurus"));
public static final RegistrySupplier<EntityType<DryosaurusEntity>> DRYOSAURUS =
ENTITIES.register("dryosaurus", () -> EntityType.Builder.of(DryosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("dryosaurus"));
public static final RegistrySupplier<EntityType<HadrosaurusEntity>> HADROSAURUS =
ENTITIES.register("hadrosaurus", () -> EntityType.Builder.of(HadrosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("hadrosaurus"));
public static final RegistrySupplier<EntityType<HypsilophodonEntity>> HYPSILOPHODON =
ENTITIES.register("hypsilophodon", () -> EntityType.Builder.of(HypsilophodonEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("hypsilophodon"));
public static final RegistrySupplier<EntityType<IndoraptorEntity>> INDORAPTOR =
ENTITIES.register("indoraptor", () -> EntityType.Builder.of(IndoraptorEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("indoraptor"));
public static final RegistrySupplier<EntityType<InostranceviaEntity>> INOSTRANCEVIA =
ENTITIES.register("inostrancevia", () -> EntityType.Builder.of(InostranceviaEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("inostrancevia"));
public static final RegistrySupplier<EntityType<LambeosaurusEntity>> LAMBEOSAURUS =
ENTITIES.register("lambeosaurus", () -> EntityType.Builder.of(LambeosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("lambeosaurus"));
public static final RegistrySupplier<EntityType<MamenchisaurusEntity>> MAMENCHISAURUS =
ENTITIES.register("mamenchisaurus", () -> EntityType.Builder.of(MamenchisaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("mamenchisaurus"));
public static final RegistrySupplier<EntityType<MetriacanthosaurusEntity>> METRIACANTHOSAURUS =
ENTITIES.register("metriacanthosaurus", () -> EntityType.Builder.of(MetriacanthosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("metriacanthosaurus"));
public static final RegistrySupplier<EntityType<OrnitholestesEntity>> ORNITHOLESTES =
ENTITIES.register("ornitholestes", () -> EntityType.Builder.of(OrnitholestesEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("ornitholestes"));
public static final RegistrySupplier<EntityType<OrnithomimusEntity>> ORNITHOMIMUS =
ENTITIES.register("ornithomimus", () -> EntityType.Builder.of(OrnithomimusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("ornithomimus"));
public static final RegistrySupplier<EntityType<OviraptorEntity>> OVIRAPTOR =
ENTITIES.register("oviraptor", () -> EntityType.Builder.of(OviraptorEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("oviraptor"));
public static final RegistrySupplier<EntityType<PachycephalosaurusEntity>> PACHYCEPHALOSAURUS =
ENTITIES.register("pachycephalosaurus", () -> EntityType.Builder.of(PachycephalosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("pachycephalosaurus"));
public static final RegistrySupplier<EntityType<ProceratosaurusEntity>> PROCERATOSAURUS =
ENTITIES.register("proceratosaurus", () -> EntityType.Builder.of(ProceratosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("proceratosaurus"));
public static final RegistrySupplier<EntityType<RajasaurusEntity>> RAJASAURUS =
ENTITIES.register("rajasaurus", () -> EntityType.Builder.of(RajasaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("rajasaurus"));
public static final RegistrySupplier<EntityType<SegisaurusEntity>> SEGISAURUS =
ENTITIES.register("segisaurus", () -> EntityType.Builder.of(SegisaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("segisaurus"));
public static final RegistrySupplier<EntityType<TitanosaurusEntity>> TITANOSAURUS =
ENTITIES.register("titanosaurus", () -> EntityType.Builder.of(TitanosaurusEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("titanosaurus"));
public static final RegistrySupplier<EntityType<TroodonEntity>> TROODON =
ENTITIES.register("troodon", () -> EntityType.Builder.of(TroodonEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("troodon"));
public static final RegistrySupplier<EntityType<UtahraptorEntity>> UTAHRAPTOR =
ENTITIES.register("utahraptor", () -> EntityType.Builder.of(UtahraptorEntity::new, MobCategory.CREATURE)
.sized(1.875f, 2.375f).build("utahraptor"));
public static void registerAttributes() {
EntityAttributeRegistry.register(APATOSAURUS, ApatosaurusEntity::createAttributes);
EntityAttributeRegistry.register(ALBERTOSAURUS, AlbertosaurusEntity::createAttributes);
EntityAttributeRegistry.register(BARYONYX, BaryonyxEntity::createAttributes);
EntityAttributeRegistry.register(BRACHIOSAURUS, BrachiosaurusEntity::createAttributes);
EntityAttributeRegistry.register(CARNOTAURUS, CarnotaurusEntity::createAttributes);
EntityAttributeRegistry.register(CERATOSAURUS, CeratosaurusEntity::createAttributes);
EntityAttributeRegistry.register(COMPSOGNATHUS, CompsognathusEntity::createAttributes);
EntityAttributeRegistry.register(CONCAVENATOR, ConcavenatorEntity::createAttributes);
EntityAttributeRegistry.register(DEINONYCHUS, DeinonychusEntity::createAttributes);
EntityAttributeRegistry.register(DILOPHOSAURUS, DilophosaurusEntity::createAttributes);
EntityAttributeRegistry.register(DIPLODOCUS, DiplodocusEntity::createAttributes);
EntityAttributeRegistry.register(DISTORTUS_REX, DistortusRexEntity::createAttributes);
EntityAttributeRegistry.register(EDMONTOSAURUS, EdmontosaurusEntity::createAttributes);
EntityAttributeRegistry.register(FDUCK, FDuckEntity::createAttributes);
EntityAttributeRegistry.register(GALLIMIMUS, GallimimusEntity::createAttributes);
EntityAttributeRegistry.register(GIGANOTOSAURUS, GiganotosaurusEntity::createAttributes);
EntityAttributeRegistry.register(GUANLONG, GuanlongEntity::createAttributes);
EntityAttributeRegistry.register(HERRERASAURUS, HerrerasaurusEntity::createAttributes);
EntityAttributeRegistry.register(INDOMINUS_REX, IndominusRexEntity::createAttributes);
EntityAttributeRegistry.register(MAJUNGASAURUS, MajungasaurusEntity::createAttributes);
EntityAttributeRegistry.register(OURANOSAURUS, OuranosaurusEntity::createAttributes);
EntityAttributeRegistry.register(PARASAUROLOPHUS, ParasaurolophusEntity::createAttributes);
EntityAttributeRegistry.register(PROCOMPSOGNATHUS, ProcompsognathusEntity::createAttributes);
EntityAttributeRegistry.register(PROTOCERATOPS, ProtoceratopsEntity::createAttributes);
EntityAttributeRegistry.register(ARAMBOURGIANIA, ArambourgianiaEntity::createAttributes);
EntityAttributeRegistry.register(CEARADACTYLUS, CearadactylusEntity::createAttributes);
EntityAttributeRegistry.register(DIMORPHODON, DimorphodonEntity::createAttributes);
EntityAttributeRegistry.register(GEOSTERNBERGIA, GeosternbergiaEntity::createAttributes);
EntityAttributeRegistry.register(GUIDRACO, GuidracoEntity::createAttributes);
EntityAttributeRegistry.register(LUDODACTYLUS, LudodactylusEntity::createAttributes);
EntityAttributeRegistry.register(MOGANOPTERUS, MoganopterusEntity::createAttributes);
EntityAttributeRegistry.register(NYCTOSAURUS, NyctosaurusEntity::createAttributes);
EntityAttributeRegistry.register(PTERANODON, PteranodonEntity::createAttributes);
EntityAttributeRegistry.register(PTERODAUSTRO, PterodaustroEntity::createAttributes);
EntityAttributeRegistry.register(QUETZALCOATLUS, QuetzalcoatlusEntity::createAttributes);
EntityAttributeRegistry.register(TAPEJARA, TapejaraEntity::createAttributes);
EntityAttributeRegistry.register(TROPEOGNATHUS, TropeognathusEntity::createAttributes);
EntityAttributeRegistry.register(TUPUXUARA, TupuxuaraEntity::createAttributes);
EntityAttributeRegistry.register(ZHENYUANOPTERUS, ZhenyuanopterusEntity::createAttributes);
EntityAttributeRegistry.register(RUGOPS, RugopsEntity::createAttributes);
EntityAttributeRegistry.register(SHANTUNGOSAURUS, ShantungosaurusEntity::createAttributes);
EntityAttributeRegistry.register(SPINOSAURUS, SpinosaurusEntity::createAttributes);
EntityAttributeRegistry.register(STEGOSAURUS, StegosaurusEntity::createAttributes);
EntityAttributeRegistry.register(STYRACOSAURUS, StyracosaurusEntity::createAttributes);
EntityAttributeRegistry.register(THERIZINOSAURUS, TherizinosaurusEntity::createAttributes);
EntityAttributeRegistry.register(TRICERATOPS, TriceratopsEntity::createAttributes);
EntityAttributeRegistry.register(TYRANNOSAURUS_REX, TyrannosaurusRexEntity::createAttributes);
EntityAttributeRegistry.register(VELOCIRAPTOR, VelociraptorEntity::createAttributes);
EntityAttributeRegistry.register(CHICKENOSAURUS, ChickenosaurusEntity::createAttributes);
EntityAttributeRegistry.register(ALLOSAURUS, AllosaurusEntity::createAttributes);
EntityAttributeRegistry.register(ALVAREZSAURUS, AlvarezsaurusEntity::createAttributes);
EntityAttributeRegistry.register(ANKYLOSAURUS, AnkylosaurusEntity::createAttributes);
EntityAttributeRegistry.register(CARCHARODONTOSAURUS, CarcharodontosaurusEntity::createAttributes);
EntityAttributeRegistry.register(CHASMOSAURUS, ChasmosaurusEntity::createAttributes);
EntityAttributeRegistry.register(COELOPHYSIS, CoelophysisEntity::createAttributes);
EntityAttributeRegistry.register(COELURUS, CoelurusEntity::createAttributes);
EntityAttributeRegistry.register(CORYTHOSAURUS, CorythosaurusEntity::createAttributes);
EntityAttributeRegistry.register(DRYOSAURUS, DryosaurusEntity::createAttributes);
EntityAttributeRegistry.register(HADROSAURUS, HadrosaurusEntity::createAttributes);
EntityAttributeRegistry.register(HYPSILOPHODON, HypsilophodonEntity::createAttributes);
EntityAttributeRegistry.register(INDORAPTOR, IndoraptorEntity::createAttributes);
EntityAttributeRegistry.register(INOSTRANCEVIA, InostranceviaEntity::createAttributes);
EntityAttributeRegistry.register(LAMBEOSAURUS, LambeosaurusEntity::createAttributes);
EntityAttributeRegistry.register(MAMENCHISAURUS, MamenchisaurusEntity::createAttributes);
EntityAttributeRegistry.register(METRIACANTHOSAURUS, MetriacanthosaurusEntity::createAttributes);
EntityAttributeRegistry.register(ORNITHOLESTES, OrnitholestesEntity::createAttributes);
EntityAttributeRegistry.register(ORNITHOMIMUS, OrnithomimusEntity::createAttributes);
EntityAttributeRegistry.register(OVIRAPTOR, OviraptorEntity::createAttributes);
EntityAttributeRegistry.register(PACHYCEPHALOSAURUS, PachycephalosaurusEntity::createAttributes);
EntityAttributeRegistry.register(PROCERATOSAURUS, ProceratosaurusEntity::createAttributes);
EntityAttributeRegistry.register(RAJASAURUS, RajasaurusEntity::createAttributes);
EntityAttributeRegistry.register(SEGISAURUS, SegisaurusEntity::createAttributes);
EntityAttributeRegistry.register(TITANOSAURUS, TitanosaurusEntity::createAttributes);
EntityAttributeRegistry.register(TROODON, TroodonEntity::createAttributes);
EntityAttributeRegistry.register(UTAHRAPTOR, UtahraptorEntity::createAttributes);
}
public static void registerSpawnPlacements() {
if (!JRConfigManager.get().enableDinosaurs) {
/*? if >1.20.1 {*/
/*SpawnPlacementsRegistry.register(ALBERTOSAURUS, SpawnPlacementTypes.ON_GROUND,
Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, Animal::checkAnimalSpawnRules);
*//*?} else {*/
SpawnPlacementsRegistry.register(ALBERTOSAURUS, SpawnPlacements.Type.ON_GROUND,
Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, Animal::checkAnimalSpawnRules);
/*?}*/
// ... repeat for all your dinos ...
}
}
public static void register() { public static void register() {
ENTITIES.register(); ENTITIES.register();
@@ -0,0 +1,776 @@
package net.cmr.jurassicrevived.entity.ai;
import net.minecraft.core.BlockPos;
import net.minecraft.tags.FluidTags;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.entity.ai.util.DefaultRandomPos;
import net.minecraft.world.entity.animal.Animal;
import net.minecraft.world.entity.animal.FlyingAnimal;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.level.pathfinder.Node;
import net.minecraft.world.level.pathfinder.Path;
import net.minecraft.world.phys.Vec3;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
public class DinoAIController {
private final DinoEntityBase dino;
private State currentState = State.IDLE;
private LivingEntity attackTarget;
private Animal mateTarget; // Target for mating
private BlockPos waterTarget;
private BlockPos homePos; // Center of territory
private Vec3 roamTarget;
private int stateTimer = 0;
private int pathRecalcTimer = 0;
private int failedPathfindingAttempts = 0;
private boolean isSelfBreeding = false; // For parthenogenesis
// Attack Cooldown Tracker
private int attackCooldown = 0;
public State getCurrentState() { return currentState; }
public LivingEntity getAttackTarget() { return attackTarget; }
public BlockPos getWaterTarget() { return waterTarget; }
public enum State {
IDLE,
ROAMING,
TERRITORIAL_ROAMING,
CHASING,
ATTACKING,
FLEEING,
SLEEPING,
MATING, // New state
DEFACATING, DROWNING, FLOCKING, HIBERNATING, HIDING, HUNTING, NESTING, RAMPAGING, SOCIALIZING
}
public DinoAIController(DinoEntityBase dino) {
this.dino = dino;
}
public void tick() {
if (homePos == null) homePos = dino.blockPosition();
updateSensors();
switch (currentState) {
case IDLE -> tickIdle();
case ROAMING -> tickRoaming();
case TERRITORIAL_ROAMING -> tickTerritorialRoaming();
case CHASING -> tickChasing();
case ATTACKING -> tickAttacking();
case FLEEING -> tickFleeing();
case SLEEPING -> tickSleeping();
case MATING -> tickMating();
}
stateTimer++;
if (attackCooldown > 0) attackCooldown--;
}
public void onHurtBy(LivingEntity attacker) {
// Retaliate if we are capable of attacking (have damage attribute > 0)
// Carnivores always attack back. Herbivores/others attack back if they have strength.
// We SKIP the generic canAttack check here because if something hurt us,
// we should try to fight back even if it's "too big" or technically invalid by roaming standards.
boolean canFightBack = dino.getAttributeValue(Attributes.ATTACK_DAMAGE) > 0 && !dino.isBaby();
if (canFightBack) {
this.attackTarget = attacker;
transitionTo(State.CHASING);
} else {
this.attackTarget = attacker;
transitionTo(State.FLEEING);
}
}
private void transitionTo(State newState) {
// Handle Condition updates
if (dino.dinoData != null) {
if (newState == State.SLEEPING) dino.dinoData.addCondition(IDinoData.Condition.SLEEPING);
else dino.dinoData.removeCondition(IDinoData.Condition.SLEEPING);
}
this.currentState = newState;
this.stateTimer = 0;
this.pathRecalcTimer = 0;
this.failedPathfindingAttempts = 0;
// Reset sprinting if we aren't in a high-speed state
if (newState != State.CHASING && newState != State.ATTACKING && newState != State.FLEEING) {
dino.setSprinting(false);
}
// Do NOT stop navigation here if switching Chasing <-> Attacking to maintain momentum
if (newState == State.IDLE || newState == State.ROAMING || newState == State.TERRITORIAL_ROAMING || newState == State.SLEEPING || newState == State.MATING) {
this.dino.getNavigation().stop();
}
}
// --- SENSORS ---
private void updateSensors() {
DinoEntityBase.DinoAIConfig config = dino.getAIConfig();
// 1. Check for Mating (High priority)
// If we are in love but not currently fighting or already mating, switch to mating.
if (dino.isInLove() && currentState != State.CHASING && currentState != State.ATTACKING && currentState != State.FLEEING && currentState != State.MATING) {
transitionTo(State.MATING);
}
// 2. Vitals Update
if (dino.dinoData != null) {
float hungerDecay = config.hungerDecay();
float thirstDecay = config.thirstDecay();
if (currentState == State.SLEEPING) {
hungerDecay *= 0.5f;
thirstDecay *= 0.5f;
if (dino.tickCount % 40 == 0 && dino.getHealth() < dino.getMaxHealth()) {
dino.heal(1.0f);
}
}
dino.dinoData.modifyHunger(-hungerDecay);
float currentThirst = dino.dinoData.getThirst();
dino.dinoData.setThirst(Math.max(0, currentThirst - thirstDecay));
float hunger = dino.dinoData.getHunger();
float thirst = dino.dinoData.getThirst();
if (hunger <= 0 || thirst <= 0) {
if (currentState == State.SLEEPING) {
transitionTo(State.IDLE);
}
if (stateTimer % 20 == 0) {
dino.addEffect(new MobEffectInstance(MobEffects.MOVEMENT_SLOWDOWN, 40, 1));
if (hunger <= 0) {
dino.hurt(dino.damageSources().starve(), 1.0f);
dino.dinoData.addCondition(IDinoData.Condition.STARVING);
}
if (thirst <= 0) {
dino.hurt(dino.damageSources().dryOut(), 1.0f);
dino.dinoData.addCondition(IDinoData.Condition.DEHYDRATED);
}
}
} else {
dino.dinoData.removeCondition(IDinoData.Condition.STARVING);
dino.dinoData.removeCondition(IDinoData.Condition.DEHYDRATED);
}
if (hunger < 30) dino.dinoData.addCondition(IDinoData.Condition.HUNGRY);
else dino.dinoData.removeCondition(IDinoData.Condition.HUNGRY);
if (thirst < 30) dino.dinoData.addCondition(IDinoData.Condition.THIRSTY);
else dino.dinoData.removeCondition(IDinoData.Condition.THIRSTY);
if (dino.getHealth() < dino.getMaxHealth() * 0.25) dino.dinoData.addCondition(IDinoData.Condition.LOW_HEALTH);
else dino.dinoData.removeCondition(IDinoData.Condition.LOW_HEALTH);
}
// 3. Sleep check
if (currentState == State.IDLE || currentState == State.ROAMING || currentState == State.TERRITORIAL_ROAMING || currentState == State.SLEEPING) {
if (dino.dinoData != null) {
IDinoData.ActivityPattern activity = dino.dinoData.getActivityPattern();
boolean isDay = dino.level().isDay();
boolean shouldSleep = false;
if (activity == IDinoData.ActivityPattern.DIURNAL && !isDay) shouldSleep = true;
if (activity == IDinoData.ActivityPattern.NOCTURNAL && isDay) shouldSleep = true;
if (dino.getLastHurtByMob() != null && dino.tickCount - dino.getLastHurtByMobTimestamp() < 200) {
shouldSleep = false;
}
if (shouldSleep && currentState != State.SLEEPING) {
transitionTo(State.SLEEPING);
} else if (!shouldSleep && currentState == State.SLEEPING) {
transitionTo(State.IDLE);
}
}
}
// 4. Target Validation (Attack)
if (attackTarget != null) {
boolean shouldStop = false;
// Basic checks
if (!attackTarget.isAlive() || dino.distanceToSqr(attackTarget) > 64 * 64) {
shouldStop = true;
}
// Player specific checks (Creative/Spectator/Peaceful)
if (attackTarget instanceof Player player) {
if (player.isCreative() || player.isSpectator()) shouldStop = true;
}
// Note: We deliberately do NOT call dino.canAttack(attackTarget) here.
// canAttack() includes "preferences" (like size limits or whitelist) which should
// be ignored if we are actively retaliating or hunting a valid target we already selected.
if (shouldStop) {
attackTarget = null;
if (currentState == State.CHASING || currentState == State.ATTACKING) {
transitionTo(State.IDLE);
}
}
}
// 5. Hunt check
if ((currentState == State.IDLE || currentState == State.ROAMING || currentState == State.TERRITORIAL_ROAMING) && dino.isCarnivore()) {
boolean hungry = dino.dinoData != null && dino.dinoData.getHunger() < 70;
boolean territorial = dino.dinoData != null && dino.dinoData.getAggression() == IDinoData.Aggression.TERRITORIAL;
if (hungry || (territorial && dino.dinoData.getHunger() < 90)) {
if (stateTimer % 10 == 0) {
findTarget();
}
}
}
// 6. Water check
if ((currentState == State.IDLE || currentState == State.ROAMING || currentState == State.TERRITORIAL_ROAMING)) {
if (dino.dinoData != null && dino.dinoData.getThirst() < 50 && waterTarget == null) {
if (stateTimer % 10 == 0) findWater();
}
}
}
private void findWater() {
// Increase vertical search range to find water even if we are high up
BlockPos pos = BlockPos.findClosestMatch(dino.blockPosition(), 32, 16, p -> dino.level().getFluidState(p).is(FluidTags.WATER)).orElse(null);
if (pos != null) {
this.waterTarget = pos;
dino.getNavigation().moveTo(pos.getX(), pos.getY(), pos.getZ(), dino.getAIConfig().walkSpeed());
if (currentState == State.IDLE) {
float territoriality = dino.dinoData != null ? dino.dinoData.getTerritoriality() : 0.0f;
if (dino.getRandom().nextFloat() < territoriality) {
transitionTo(State.TERRITORIAL_ROAMING);
} else {
transitionTo(State.ROAMING);
}
}
}
}
private void findTarget() {
double range = dino.getAttributeValue(Attributes.FOLLOW_RANGE);
if (range <= 0) range = 32.0;
List<LivingEntity> nearby = dino.level().getEntitiesOfClass(LivingEntity.class,
dino.getBoundingBox().inflate(range));
List<LivingEntity> candidates = new ArrayList<>();
for (LivingEntity e : nearby) {
if (e == dino) continue;
if (!dino.canAttack(e)) continue;
if (e instanceof Player player) {
// Apply motivation logic for Players
boolean isTerritorial = dino.dinoData != null && dino.dinoData.getAggression() == IDinoData.Aggression.TERRITORIAL;
boolean hungry = dino.dinoData != null && dino.dinoData.getHunger() < 70;
boolean validPlayerTarget = false;
if (isTerritorial) {
if (hungry || dino.distanceToSqr(player) < 225) {
validPlayerTarget = true;
}
} else {
if (hungry) {
validPlayerTarget = true;
}
}
if (validPlayerTarget) {
candidates.add(player);
}
} else {
// Animals are valid if canAttack passes (which checks size etc)
candidates.add(e);
}
}
// Sort candidates by distance to prefer closest valid target
candidates.sort(Comparator.comparingDouble(dino::distanceToSqr));
// Check pathfinding for the closest targets to ensure we can reach them
int checks = 0;
for (LivingEntity candidate : candidates) {
if (checks >= 5) break; // Limit pathfinding checks to avoid lag
checks++;
Path path = dino.getNavigation().createPath(candidate, 0);
if (path != null) {
// VERIFY PATH REACHES TARGET
// The pathfinder may return a partial path that ends at a wall.
// We check if the end point of the path is reasonably close to the target.
Node endNode = path.getEndNode();
if (endNode != null) {
double distToTargetSqr = candidate.distanceToSqr(endNode.x + 0.5, endNode.y + 0.5, endNode.z + 0.5);
// 25.0 = 5 blocks tolerance. If the path ends > 5 blocks away from target, it's likely obstructed.
if (distToTargetSqr < 25.0) {
this.attackTarget = candidate;
transitionTo(State.CHASING);
return;
}
}
}
}
}
// --- STATE LOGIC ---
private void tickIdle() {
dino.getNavigation().stop();
// Check for Natural Breeding (approx once every 2 in-game days = 48000 ticks)
if (!dino.level().isClientSide && stateTimer % 100 == 0) {
// 1. Trigger Ready State
if (dino.getAge() == 0 && !dino.isInLove() && dino.canBreed()) {
if (dino.dinoData != null && !dino.dinoData.hasCondition(IDinoData.Condition.READY_TO_MATE)) {
// Chance: 1 in 480 checks (~ once per 48000 ticks / 2 days)
if (dino.getRandom().nextInt(480) == 0) {
// Parthenogenesis check (1% chance)
if (dino.getRandom().nextInt(100) == 0) {
dino.setInLoveTime(600); // 30 Seconds of hearts
this.isSelfBreeding = true;
} else {
// Standard: Set condition, wait for partner
dino.dinoData.addCondition(IDinoData.Condition.READY_TO_MATE);
}
}
}
}
// 2. Scan for Partner if Ready
if (dino.dinoData != null && dino.dinoData.hasCondition(IDinoData.Condition.READY_TO_MATE)) {
// Add a chance to "lose interest" so they aren't ready forever (1 in 50 chance every 5 seconds = approx 4 minutes duration)
if (dino.getRandom().nextInt(50) == 0) {
dino.dinoData.removeCondition(IDinoData.Condition.READY_TO_MATE);
} else {
List<DinoEntityBase> nearby = dino.level().getEntitiesOfClass(DinoEntityBase.class,
dino.getBoundingBox().inflate(8.0),
e -> e.getType() == dino.getType() && e != dino && !e.isBaby());
for (DinoEntityBase potentialPartner : nearby) {
if (dino.canMate(potentialPartner)) {
// Initiate mating for both
dino.setInLoveTime(600); // 30 seconds
potentialPartner.setInLoveTime(600); // 30 seconds
dino.dinoData.removeCondition(IDinoData.Condition.READY_TO_MATE);
if (potentialPartner.dinoData != null) {
potentialPartner.dinoData.removeCondition(IDinoData.Condition.READY_TO_MATE);
}
break;
}
}
}
}
}
float territoriality = 0.0f;
if (dino.dinoData != null) {
territoriality = dino.dinoData.getTerritoriality();
}
int idleTime = 60; // Default 3 seconds
// If we are a flying animal and on the ground, stay down longer (e.g. 15-30 seconds) to walk around
if (dino instanceof FlyingAnimal && dino.onGround()) {
idleTime = 300 + dino.getRandom().nextInt(300);
}
if (stateTimer > idleTime) {
if (dino.getRandom().nextFloat() < 0.05f) {
if (dino.getRandom().nextFloat() < territoriality) {
transitionTo(State.TERRITORIAL_ROAMING);
} else {
transitionTo(State.ROAMING);
}
}
}
}
private void tickMating() {
// If love ran out, stop
if (!dino.isInLove()) {
this.mateTarget = null;
this.isSelfBreeding = false;
// Also ensure we don't have the condition anymore if we just failed/finished
if (dino.dinoData != null) dino.dinoData.removeCondition(IDinoData.Condition.READY_TO_MATE);
transitionTo(State.IDLE);
return;
}
// Parthenogenesis Logic
if (this.isSelfBreeding) {
dino.spawnChildFromBreeding((net.minecraft.server.level.ServerLevel)dino.level(), dino);
dino.setInLoveTime(0); // Reset
this.isSelfBreeding = false;
transitionTo(State.IDLE);
return;
}
// Find Partner
if (this.mateTarget == null || !this.mateTarget.isAlive() || !this.mateTarget.isInLove()) {
List<Animal> nearby = dino.level().getEntitiesOfClass(Animal.class, dino.getBoundingBox().inflate(16.0),
e -> e.getType() == dino.getType() && e != dino && e.isInLove());
this.mateTarget = nearby.stream()
.min(Comparator.comparingDouble(dino::distanceToSqr))
.orElse(null);
}
if (this.mateTarget != null) {
dino.getNavigation().moveTo(this.mateTarget, dino.getAIConfig().walkSpeed());
if (dino.distanceToSqr(this.mateTarget) < 4.0) { // < 2 blocks
dino.spawnChildFromBreeding((net.minecraft.server.level.ServerLevel)dino.level(), this.mateTarget);
// Breeding consumes love in spawnChildFromBreeding
this.mateTarget = null;
transitionTo(State.IDLE);
}
} else {
// No partner found yet, wander slowly?
if (dino.getNavigation().isDone()) {
Vec3 pos = DefaultRandomPos.getPos(dino, 10, 3);
if (pos != null) dino.getNavigation().moveTo(pos.x, pos.y, pos.z, dino.getAIConfig().walkSpeed());
}
}
}
private void tickSleeping() {
dino.getNavigation().stop();
}
private boolean handleWaterPathing() {
if (waterTarget != null) {
double dist = dino.distanceToSqr(waterTarget.getCenter());
double reach = dino.getAIConfig().attackReach() * dino.getBbWidth();
if (dist < (reach * reach) + 9.0) {
dino.dinoData.setThirst(dino.getAIConfig().maxThirst());
waterTarget = null;
dino.getNavigation().stop();
transitionTo(State.IDLE);
return true;
}
if (dino.getNavigation().isDone()) {
if (dist < 1024) {
dino.getNavigation().moveTo(waterTarget.getX(), waterTarget.getY(), waterTarget.getZ(), dino.getAIConfig().walkSpeed());
} else {
waterTarget = null;
}
}
return true;
}
return false;
}
private void tickRoaming() {
if (handleWaterPathing()) return;
if (stateTimer == 0) {
this.roamTarget = null;
findAndSetRoamTarget();
if (this.roamTarget == null) {
transitionTo(State.IDLE);
return;
}
}
if (stateTimer > 400) {
transitionTo(State.IDLE);
return;
}
if (dino.getNavigation().isDone()) {
if (roamTarget != null && dino.distanceToSqr(roamTarget) < 9.0) {
// If we are a flyer and in the air, don't stop! Keep flying!
if (dino instanceof FlyingAnimal && !dino.onGround()) {
findAndSetRoamTarget(); // Immediately pick next waypoint
return;
}
transitionTo(State.IDLE);
return;
}
boolean resumed = false;
if (roamTarget != null) {
resumed = dino.getNavigation().moveTo(roamTarget.x, roamTarget.y, roamTarget.z, dino.getAIConfig().walkSpeed());
}
if (!resumed) {
findAndSetRoamTarget();
if (this.roamTarget == null) {
transitionTo(State.IDLE);
}
}
}
}
private void findAndSetRoamTarget() {
this.roamTarget = null;
// Flying Logic
if (dino instanceof FlyingAnimal) {
Vec3 airPos = getAirRoamPos();
if (airPos != null) {
// Use walkSpeed as cruising speed
if (dino.getNavigation().moveTo(airPos.x, airPos.y, airPos.z, dino.getAIConfig().walkSpeed())) {
this.roamTarget = airPos;
return;
}
}
}
// Ground Logic
for (int i = 0; i < 3; i++) {
Vec3 pos = DefaultRandomPos.getPos(dino, 20, 7);
if (pos != null && dino.distanceToSqr(pos) > 49.0) {
if (dino.getNavigation().moveTo(pos.x, pos.y, pos.z, dino.getAIConfig().walkSpeed())) {
this.roamTarget = pos;
return;
}
}
}
}
private Vec3 getAirRoamPos() {
net.minecraft.util.RandomSource random = dino.getRandom();
Vec3 pos = dino.position();
// Increased radius for better flight
double x = pos.x + (random.nextFloat() * 2 - 1) * 64;
double z = pos.z + (random.nextFloat() * 2 - 1) * 64;
// Height check
int groundY = dino.level().getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, (int)x, (int)z);
double y;
if (dino.onGround()) {
// Takeoff: 10-20 blocks up
y = pos.y + 10 + random.nextInt(10);
} else {
// 20% chance to land if already flying
if (random.nextFloat() < 0.20f) {
y = groundY;
} else {
// Wander vertically
if (random.nextFloat() < 0.30f) {
y = groundY + 10 + random.nextInt(30);
} else {
y = pos.y + (random.nextFloat() * 2 - 1) * 20;
}
if (y < groundY + 5) y = groundY + 5;
if (y > groundY + 50) y = groundY + 50;
}
}
// --- Water Avoidance ---
// If the target is over water, try to pull it back towards us/land
BlockPos targetPos = new BlockPos((int)x, (int)groundY, (int)z);
if (dino.level().getFluidState(targetPos).is(FluidTags.WATER)) {
// Check if it's "deep" water (just checking surface isn't enough, but usually good proxy)
// Move target 50% closer to current position (which presumably was safe)
x = (x + pos.x) / 2.0;
z = (z + pos.z) / 2.0;
// Re-calculate ground Y for new X/Z
groundY = dino.level().getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, (int)x, (int)z);
if (y < groundY) y = groundY;
}
// --- Landing Logic ---
// If we are aiming for a spot very close to the ground (<= 4 blocks), just land.
// This prevents awkward low-altitude hovering.
if (y <= groundY + 4) {
y = groundY;
}
return new Vec3(x, y, z);
}
private void tickTerritorialRoaming() {
if (handleWaterPathing()) return;
if (stateTimer == 0) {
findAndSetTerritorialTarget();
if (this.roamTarget == null) {
transitionTo(State.IDLE);
return;
}
}
if (stateTimer > 400) {
transitionTo(State.IDLE);
return;
}
if (dino.getNavigation().isDone()) {
if (roamTarget != null && dino.distanceToSqr(roamTarget) < 9.0) {
transitionTo(State.IDLE);
return;
}
boolean resumed = false;
if (roamTarget != null) {
resumed = dino.getNavigation().moveTo(roamTarget.x, roamTarget.y, roamTarget.z, dino.getAIConfig().walkSpeed());
}
if (!resumed) {
findAndSetTerritorialTarget();
if (this.roamTarget == null) {
transitionTo(State.IDLE);
}
}
}
}
private void findAndSetTerritorialTarget() {
this.roamTarget = null;
Vec3 target = null;
for (int i = 0; i < 5; i++) {
Vec3 candidate;
if (homePos != null && dino.distanceToSqr(homePos.getCenter()) > 40 * 40) {
Vec3 toHome = Vec3.atCenterOf(homePos).subtract(dino.position()).normalize().scale(10);
Vec3 biasTarget = dino.position().add(toHome);
candidate = DefaultRandomPos.getPosTowards(dino, 15, 7, biasTarget, 1.57);
} else {
candidate = DefaultRandomPos.getPos(dino, 15, 7);
}
if (candidate != null && dino.distanceToSqr(candidate) > 25.0) {
if (dino.getNavigation().moveTo(candidate.x, candidate.y, candidate.z, dino.getAIConfig().walkSpeed())) {
this.roamTarget = candidate;
return;
}
}
}
Vec3 fallback = DefaultRandomPos.getPos(dino, 10, 5);
if (fallback != null) {
if (dino.getNavigation().moveTo(fallback.x, fallback.y, fallback.z, dino.getAIConfig().walkSpeed())) {
this.roamTarget = fallback;
}
}
}
private void tickChasing() {
if (attackTarget == null) {
transitionTo(State.IDLE);
return;
}
dino.setSprinting(true);
waterTarget = null;
dino.getLookControl().setLookAt(attackTarget, 30.0F, 30.0F);
double distSqr = dino.distanceToSqr(attackTarget);
double reachMult = dino.getAIConfig().attackReach();
double reach = (double)(dino.getBbWidth() * reachMult * dino.getBbWidth() * reachMult) + attackTarget.getBbWidth();
if (distSqr <= reach * 1.1) {
transitionTo(State.ATTACKING);
return;
}
if (pathRecalcTimer-- <= 0 || dino.getNavigation().isDone()) {
if (!dino.getNavigation().moveTo(attackTarget, dino.getAIConfig().runSpeed())) {
pathRecalcTimer = 10; // Wait before retrying to prevent rapid failure loops
failedPathfindingAttempts++;
// Tolerance allows for temporary pathfinding failures (e.g., target inside hitbox)
if (failedPathfindingAttempts > 5) {
attackTarget = null;
transitionTo(State.IDLE);
}
} else {
pathRecalcTimer = 10;
failedPathfindingAttempts = 0;
}
}
}
private void tickAttacking() {
if (attackTarget == null) {
transitionTo(State.IDLE);
return;
}
dino.setSprinting(true);
dino.getLookControl().setLookAt(attackTarget, 30.0F, 30.0F);
double distSqr = dino.distanceToSqr(attackTarget);
double reachMult = dino.getAIConfig().attackReach();
double reach = (double)(dino.getBbWidth() * reachMult * dino.getBbWidth() * reachMult) + attackTarget.getBbWidth();
if (distSqr > reach * 2.5) {
transitionTo(State.CHASING);
return;
}
double stopDist = (dino.getBbWidth()/2.0 + attackTarget.getBbWidth()/2.0) + 0.5;
double stopDistSqr = stopDist * stopDist;
if (distSqr > stopDistSqr) {
dino.getNavigation().moveTo(attackTarget, dino.getAIConfig().runSpeed());
} else {
dino.getNavigation().stop();
}
if (attackCooldown <= 0) {
dino.swing(InteractionHand.MAIN_HAND);
boolean success = false;
if (dino.isWithinMeleeAttackRange(attackTarget)) {
success = dino.doHurtTarget(attackTarget);
}
if (!success && attackTarget.isAlive()) {
if (distSqr <= reach) {
success = attackTarget.hurt(dino.damageSources().mobAttack(dino), (float)dino.getAttributeValue(Attributes.ATTACK_DAMAGE));
}
}
if (success) {
attackCooldown = 20;
} else {
attackCooldown = 5;
}
}
}
private void tickFleeing() {
if (attackTarget == null) {
transitionTo(State.IDLE);
return;
}
if (dino.getNavigation().isDone() || stateTimer % 10 == 0) {
Vec3 awayDir = DefaultRandomPos.getPosAway(dino, 16, 7, attackTarget.position());
if (awayDir != null) {
dino.getNavigation().moveTo(awayDir.x, awayDir.y, awayDir.z, dino.getAIConfig().runSpeed() * 1.2);
}
}
if (dino.distanceToSqr(attackTarget) > 48 * 48) {
transitionTo(State.IDLE);
}
}
}
@@ -0,0 +1,232 @@
package net.cmr.jurassicrevived.entity.ai;
import net.cmr.jurassicrevived.entity.ai.navigation.CustomDinoNavigation;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.tags.ItemTags;
import net.minecraft.world.Difficulty;
import net.minecraft.world.InteractionHand;
import net.minecraft.world.InteractionResult;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.ai.navigation.PathNavigation;
import net.minecraft.world.entity.animal.Animal;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.item.Items;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.block.Block;
import java.util.HashMap;
import java.util.Map;
public abstract class DinoEntityBase extends Animal {
protected IDinoData dinoData;
protected final DinoAIController aiController;
public DinoEntityBase(EntityType<? extends Animal> type, Level level) {
super(type, level);
this.aiController = new DinoAIController(this);
}
// Disable Vanilla Goals
@Override
protected void registerGoals() {}
// Ensure our AI ticks regardless of what vanilla method is called
@Override
public void tick() {
super.tick(); // Vanilla Mob tick
if (!this.level().isClientSide) {
// FORCE TICK AI
this.aiController.tick();
// Sync
if (dinoData != null) {
dinoData.tickSync(this.entityData);
}
}
}
// --- Navigation ---
@Override
protected PathNavigation createNavigation(Level level) {
return new CustomDinoNavigation(this, level);
}
// --- Hooks ---
public abstract boolean isCarnivore();
public abstract boolean isMarine();
public abstract boolean isAmphibious();
public abstract Block getEggBlock(); // New hook for breeding
public abstract DinoAIConfig getAIConfig();
public record DinoAIConfig(double walkSpeed, double runSpeed, double attackReach,
float maxHunger, float maxThirst,
float hungerDecay, float thirstDecay,
float defaultHungerReplenishment) {}
private static final Map<EntityType<?>, Float> ENTITY_HUNGER_VALUES = new HashMap<>();
public static void registerHungerValue(EntityType<?> type, float value) {
ENTITY_HUNGER_VALUES.put(type, value);
}
public float getHungerReplenishment(LivingEntity target) {
return ENTITY_HUNGER_VALUES.getOrDefault(target.getType(), getAIConfig().defaultHungerReplenishment());
}
public IDinoData getDinoData() { return this.dinoData; }
@Override
public InteractionResult mobInteract(Player player, InteractionHand hand) {
if (!this.level().isClientSide && hand == InteractionHand.MAIN_HAND && player.isShiftKeyDown() && player.getMainHandItem().isEmpty()) {
if (this.dinoData != null) {
// Format details nicely
StringBuilder sb = new StringBuilder();
sb.append("§6--- Dino Status ---§r\n");
sb.append("§eState:§r ").append(this.aiController.getCurrentState()).append("\n");
// Line 1: Biological Classification
sb.append("§eType:§r ").append(dinoData.getType())
.append(" §eGroup:§r ").append(dinoData.getGroup())
.append(" §eDiet:§r ").append(dinoData.getDiet()).append("\n");
// Line 2: Behavioral/Lifecycle
sb.append("§eBehavior:§r ").append(dinoData.getAggression())
.append(" §eBirth:§r ").append(dinoData.getBirthType())
.append(" §eActivity:§r ").append(dinoData.getActivityPattern()).append("\n");
// Line 3: Status
sb.append("§eMood:§r ").append(dinoData.getMood())
.append(" §eConditions:§r ").append(dinoData.getConditions()).append("\n");
// Line 4: Vitals
sb.append("§aHunger:§r ").append(String.format("%.1f", dinoData.getHunger()))
.append(" §bThirst:§r ").append(String.format("%.1f", dinoData.getThirst())).append("\n");
if (this.aiController.getAttackTarget() != null) {
sb.append("§cTarget:§r ").append(this.aiController.getAttackTarget().getName().getString()).append("\n");
}
if (this.aiController.getWaterTarget() != null) {
sb.append("§9Water Target:§r ").append(this.aiController.getWaterTarget().toShortString()).append("\n");
}
player.sendSystemMessage(Component.literal(sb.toString()));
return InteractionResult.SUCCESS;
}
}
return super.mobInteract(player, hand);
}
@Override
public boolean isFood(ItemStack stack) {
if (dinoData == null) return false;
IDinoData.DietaryClassification diet = dinoData.getDiet();
// 1. Carnivores: All meat
if (diet == IDinoData.DietaryClassification.CARNIVORE || diet == IDinoData.DietaryClassification.PISCIVORE) {
// WOLF_FOOD includes beef, pork, chicken, rabbit, mutton, rotten flesh
//if (stack.is(ItemTags.WOLF_FOOD)) return true;
// Fallback for fish items
if (stack.is(ItemTags.FISHES)) return true;
}
// 3. Herbivores: Leaves, Fruits, Vegetables
if (diet == IDinoData.DietaryClassification.HERBIVORE) {
if (stack.is(ItemTags.LEAVES)) return true;
if (stack.is(ItemTags.FLOWERS)) return true;
if (stack.is(Items.APPLE) || stack.is(Items.MELON_SLICE) || stack.is(Items.SWEET_BERRIES) || stack.is(Items.GLOW_BERRIES)) return true;
if (stack.is(Items.SEAGRASS) || stack.is(Items.KELP)) return true;
}
// 4. Omnivores: Both
if (diet == IDinoData.DietaryClassification.OMNIVORE) {
//if (stack.is(ItemTags.WOLF_FOOD)) return true;
if (stack.is(ItemTags.LEAVES)) return true;
}
return false;
}
@Override
public void spawnChildFromBreeding(ServerLevel level, Animal partner) {
// Custom breeding logic
if (this.dinoData != null && this.dinoData.getBirthType() == IDinoData.BirthType.EGG_LAYING) {
// Place Egg Block
Block eggBlock = getEggBlock();
if (eggBlock != null) {
// Consume breeding status
this.setAge(6000);
partner.setAge(6000);
this.resetLove();
partner.resetLove();
// Place egg at parent location
level.setBlock(this.blockPosition(), eggBlock.defaultBlockState(), 3);
level.levelEvent(2001, this.blockPosition(), Block.getId(eggBlock.defaultBlockState())); // Particles?
}
} else {
// Live Birth (Default Vanilla)
super.spawnChildFromBreeding(level, partner);
}
}
// --- Attack Logic ---
public boolean canAttack(LivingEntity target) {
if (target == null || target == this) return false;
if (!target.isAlive()) return false;
if (this.getClass().equals(target.getClass())) return false;
// Prevent targeting if the entity is invisible (unless we have special senses, but basic AI implies sight)
if (target.hasEffect(MobEffects.INVISIBILITY)) return false;
if (target instanceof Player player) {
if (player.isCreative() || player.isSpectator()) return false;
// Peaceful mode check: Do not target players if difficulty is Peaceful
if (this.level().getDifficulty() == Difficulty.PEACEFUL) return false;
if (dinoData != null && dinoData.isWhitelisted(player.getUUID())) return false;
return true; // Always attack valid players
}
double myVolume = this.getBbWidth() * this.getBbWidth() * this.getBbHeight();
double targetVolume = target.getBbWidth() * target.getBbWidth() * target.getBbHeight();
// Prevent attacking entities significantly larger than self
// Check Volume (2.5x)
if (myVolume > 0 && targetVolume > myVolume * 2.5) return false;
// Check Height (1.2x) - Prevents attacking tall things like Brachiosaurus even if volume calc is close
if (this.getBbHeight() > 0 && target.getBbHeight() > this.getBbHeight() * 1.2) return false;
return true;
}
@Override
public boolean hurt(DamageSource source, float amount) {
boolean result = super.hurt(source, amount);
if (result && !this.level().isClientSide && source.getEntity() instanceof LivingEntity attacker) {
this.aiController.onHurtBy(attacker);
}
return result;
}
@Override
public void addAdditionalSaveData(CompoundTag tag) {
super.addAdditionalSaveData(tag);
if(dinoData != null) dinoData.saveNBT(tag);
}
@Override
public void readAdditionalSaveData(CompoundTag tag) {
super.readAdditionalSaveData(tag);
if(dinoData != null) dinoData.loadNBT(tag);
}
}
@@ -0,0 +1,74 @@
package net.cmr.jurassicrevived.entity.ai;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.network.syncher.SynchedEntityData;
import java.util.List;
import java.util.Set;
import java.util.UUID;
public interface IDinoData {
// Core Vitals
float getHunger();
void setHunger(float value);
void modifyHunger(float change);
float getThirst();
void setThirst(float value);
// Mood & Personality
enum Mood { ANGRY, NEUTRAL, CONTENT, HAPPY, SCARED, SLEEPY }
Mood getMood();
void setMood(Mood mood);
// Aggression / Territoriality
// Replaces old float getTerritoriality? Or keeps it?
// "I want to add... Aggression, and a Condition." and "And this list for Aggression: Free-Roaming, Territorial, and Neutral"
// We will keep getTerritoriality() as a float for logic if needed, but add the Enum.
enum Aggression { FREE_ROAMING, TERRITORIAL, NEUTRAL, SCAVENGER }
Aggression getAggression();
void setAggression(Aggression aggression);
float getTerritoriality();
void setTerritoriality(float value);
// New Stats
enum DietaryClassification { CRUSTACIVORE, DETRITIVORE, HERBIVORE, INSECTIVORE, OMNIVORE, PISCIVORE, PLANKTIVORE, CARNIVORE }
DietaryClassification getDiet();
void setDiet(DietaryClassification diet);
enum Type { TERRESTRIAL, AVIAN, AMPHIBIOUS, MARINE }
Type getType();
void setType(Type type);
enum Group { THEROPOD, THYREOPHORAN, CERAPOD, SAUROPOD, ORNITHOPOD, AMPHIBIAN, ARCHOSAUROMORPH, PLEURODIRE, PTEROSAUR, REPTILIOMORPH, SQUAMATE }
Group getGroup();
void setGroup(Group group);
enum BirthType { EGG_LAYING, LIVE_BIRTH }
BirthType getBirthType();
void setBirthType(BirthType birthType);
enum ActivityPattern { CATHEMERAL, CREPUSCULAR, DIURNAL, NOCTURNAL }
ActivityPattern getActivityPattern();
void setActivityPattern(ActivityPattern pattern);
// Conditions
enum Condition { COMATOSE, INFECTED, LOW_HEALTH, HUNGRY, OVERHEATING, POISONED, SEDATED, SUFFOCATING, TAMED, WITHER, READY_TO_MATE, THIRSTY, STARVING, DEHYDRATED, FREEZING, SLEEPING }
Set<Condition> getConditions();
void addCondition(Condition condition);
void removeCondition(Condition condition);
boolean hasCondition(Condition condition);
// Taming / Whitelist
void addWhitelistedPlayer(UUID playerUUID);
boolean isWhitelisted(UUID playerUUID);
List<UUID> getWhitelistedPlayers();
// Serialization & Sync
void saveNBT(CompoundTag tag);
void loadNBT(CompoundTag tag);
// Called on entity tick to sync specific data (Hunger/Mood) to client via SynchedEntityData if needed
void tickSync(SynchedEntityData entityData);
}
@@ -0,0 +1,61 @@
package net.cmr.jurassicrevived.entity.ai;
import net.minecraft.world.Difficulty;
import net.minecraft.world.entity.AgeableMob;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.ai.goal.MeleeAttackGoal;
import net.minecraft.world.entity.player.Player;
public class SprintingMeleeAttackGoal extends MeleeAttackGoal {
private final LivingEntity entity;
public SprintingMeleeAttackGoal(PathfinderMob pMob, double pSpeedModifier, boolean pCanUnseenMemory) {
super(pMob, pSpeedModifier, pCanUnseenMemory);
this.entity = pMob;
}
// Override to prevent attacking players in peaceful difficulty and prevent babies from attacking
@Override
public boolean canUse() {
if (!super.canUse()) {
return false;
}
// Prevent baby mobs from attacking
if (this.mob instanceof AgeableMob ageableMob && ageableMob.isBaby()) {
return false;
}
// Check if target is a player and difficulty is peaceful
LivingEntity target = this.mob.getTarget();
if (target instanceof Player && this.mob.level().getDifficulty() == Difficulty.PEACEFUL) {
return false;
}
return true;
}
// This method is called to start the goal
@Override
public void start() {
super.start();
this.entity.setSprinting(true); // Force the entity to sprint
}
// This method is called to tick the goal
@Override
public void tick() {
super.tick();
// The movement speed is handled by the default goal behavior
// The sprint state is maintained from the start() method
}
// This method is called to end the goal
@Override
public void stop() {
super.stop();
this.entity.setSprinting(false); // Stop sprinting when the goal is finished
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.ai;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.PathfinderMob;
import net.minecraft.world.entity.ai.goal.PanicGoal;
public class SprintingPanicGoal extends PanicGoal {
private final LivingEntity entity;
public SprintingPanicGoal(PathfinderMob mob, double speedModifier) {
super(mob, speedModifier);
this.entity = mob;
}
@Override
public void start() {
super.start();
this.entity.setSprinting(true);
}
@Override
public void stop() {
super.stop();
this.entity.setSprinting(false);
}
}
@@ -0,0 +1,27 @@
package net.cmr.jurassicrevived.entity.ai.navigation;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.pathfinder.PathFinder;
import net.minecraft.world.phys.Vec3;
public class CustomDinoNavigation extends GroundPathNavigation {
public CustomDinoNavigation(Mob mob, Level level) {
super(mob, level);
}
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.nodeEvaluator = new LargeEntityNodeEvaluator();
this.nodeEvaluator.setCanPassDoors(true);
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
}
// Fix for large entities getting stuck: Ensure we don't try to path to the exact center of a block if our hitbox is huge
@Override
protected Vec3 getTempMobPos() {
return this.mob.position();
}
}
@@ -0,0 +1,46 @@
package net.cmr.jurassicrevived.entity.ai.navigation;
import net.minecraft.core.BlockPos;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.level.block.state.BlockState;
/*? if <=1.20.1 {*/
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.pathfinder.BlockPathTypes;
/*?} else {*/
/*import net.minecraft.world.level.pathfinder.PathType;
import net.minecraft.world.level.pathfinder.PathfindingContext;
*//*?}*/
import net.minecraft.world.level.pathfinder.WalkNodeEvaluator;
public class LargeEntityNodeEvaluator extends WalkNodeEvaluator {
/*? if <=1.20.1 {*/
public BlockPathTypes getBlockPathType(BlockGetter level, int x, int y, int z) {
Mob entity = this.mob;
BlockPos pos = new BlockPos(x, y, z);
BlockState state = level.getBlockState(pos);
if (entity != null && entity.getBbWidth() > 1.5f && state.is(BlockTags.LEAVES)) {
return BlockPathTypes.OPEN;
}
/*?} else {*/
/*@Override
public PathType getPathType(PathfindingContext context, int x, int y, int z) {
Mob entity = this.mob;
BlockPos pos = new BlockPos(x, y, z);
BlockState state = context.level().getBlockState(pos);
if (entity != null && entity.getBbWidth() > 1.5f && state.is(BlockTags.LEAVES)) {
return PathType.OPEN;
}
*//*?}*/
if (entity != null && entity.getBbWidth() >= 2.0f) {
}
/*? if <=1.20.1 {*/
return super.getBlockPathType(level, x, y, z);
/*?} else {*/
/*return super.getPathType(context, x, y, z);
*//*?}*/
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.AlbertosaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class AlbertosaurusModel extends GeoModel<AlbertosaurusEntity> {
private static final Map<AlbertosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(AlbertosaurusVariant.class), map -> {
map.put(AlbertosaurusVariant.MALE, Constants.rl("textures/entity/albertosaurus.png"));
map.put(AlbertosaurusVariant.FEMALE, Constants.rl("textures/entity/albertosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(AlbertosaurusEntity animatable) {
return Constants.rl("geo/albertosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(AlbertosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(AlbertosaurusEntity animatable) {
return Constants.rl("animations/albertosaurus.animation.json");
}
@Override
public void setCustomAnimations(AlbertosaurusEntity entity, long id, AnimationState<AlbertosaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "TailBASE", "Tail2", "Tail3", "Tail4", "Tail5", "Tail6" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("NeckBASE");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -20.0f, 20.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.AlbertosaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class AlbertosaurusRenderer extends GeoEntityRenderer<AlbertosaurusEntity> {
private final float animalScale = 1.5F;
public AlbertosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new AlbertosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, AlbertosaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum AlbertosaurusVariant {
MALE(0),
FEMALE(1);
private static final AlbertosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(AlbertosaurusVariant::getId)).toArray(AlbertosaurusVariant[]::new);
private final int id;
AlbertosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static AlbertosaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.AllosaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class AllosaurusModel extends GeoModel<AllosaurusEntity> {
private static final Map<AllosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(AllosaurusVariant.class), map -> {
map.put(AllosaurusVariant.MALE, Constants.rl("textures/entity/allosaurus.png"));
map.put(AllosaurusVariant.FEMALE, Constants.rl("textures/entity/allosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(AllosaurusEntity animatable) {
return Constants.rl("geo/allosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(AllosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(AllosaurusEntity animatable) {
return Constants.rl("animations/allosaurus.animation.json");
}
@Override
public void setCustomAnimations(AllosaurusEntity entity, long id, AnimationState<AllosaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3", "Tail4", "Tail5", "Tail6" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("Neck1");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -20.0f, 20.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.AllosaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class AllosaurusRenderer extends GeoEntityRenderer<AllosaurusEntity> {
private final float animalScale = 1.65F;
public AllosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new AllosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, AllosaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum AllosaurusVariant {
MALE(0),
FEMALE(1);
private static final AllosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(AllosaurusVariant::getId)).toArray(AllosaurusVariant[]::new);
private final int id;
AllosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static AllosaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.AlvarezsaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class AlvarezsaurusModel extends GeoModel<AlvarezsaurusEntity> {
private static final Map<AlvarezsaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(AlvarezsaurusVariant.class), map -> {
map.put(AlvarezsaurusVariant.MALE, Constants.rl("textures/entity/alvarezsaurus.png"));
map.put(AlvarezsaurusVariant.FEMALE, Constants.rl("textures/entity/alvarezsaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(AlvarezsaurusEntity animatable) {
return Constants.rl("geo/alvarezsaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(AlvarezsaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(AlvarezsaurusEntity animatable) {
return Constants.rl("animations/alvarezsaurus.animation.json");
}
@Override
public void setCustomAnimations(AlvarezsaurusEntity entity, long id, AnimationState<AlvarezsaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "TailBase", "TailMid1", "TailMid2", "TailTip"};
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("NeckBase");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -30.0f, 30.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.AlvarezsaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class AlvarezsaurusRenderer extends GeoEntityRenderer<AlvarezsaurusEntity> {
private final float animalScale = 0.5F;
public AlvarezsaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new AlvarezsaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, AlvarezsaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum AlvarezsaurusVariant {
MALE(0),
FEMALE(1);
private static final AlvarezsaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(AlvarezsaurusVariant::getId)).toArray(AlvarezsaurusVariant[]::new);
private final int id;
AlvarezsaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static AlvarezsaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.AnkylosaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class AnkylosaurusModel extends GeoModel<AnkylosaurusEntity> {
private static final Map<AnkylosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(AnkylosaurusVariant.class), map -> {
map.put(AnkylosaurusVariant.MALE, Constants.rl("textures/entity/ankylosaurus.png"));
map.put(AnkylosaurusVariant.FEMALE, Constants.rl("textures/entity/ankylosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(AnkylosaurusEntity animatable) {
return Constants.rl("geo/ankylosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(AnkylosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(AnkylosaurusEntity animatable) {
return Constants.rl("animations/ankylosaurus.animation.json");
}
@Override
public void setCustomAnimations(AnkylosaurusEntity entity, long id, AnimationState<AnkylosaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "tail1", "tail2", "tail3", "tail4", "tailend" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("neck1");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -30.0f, 30.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.AnkylosaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class AnkylosaurusRenderer extends GeoEntityRenderer<AnkylosaurusEntity> {
private final float animalScale = 2.4F;
public AnkylosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new AnkylosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, AnkylosaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum AnkylosaurusVariant {
MALE(0),
FEMALE(1);
private static final AnkylosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(AnkylosaurusVariant::getId)).toArray(AnkylosaurusVariant[]::new);
private final int id;
AnkylosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static AnkylosaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.ApatosaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class ApatosaurusModel extends GeoModel<ApatosaurusEntity> {
private static final Map<ApatosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(ApatosaurusVariant.class), map -> {
map.put(ApatosaurusVariant.MALE, Constants.rl("textures/entity/apatosaurus.png"));
map.put(ApatosaurusVariant.FEMALE, Constants.rl("textures/entity/apatosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(ApatosaurusEntity animatable) {
return Constants.rl("geo/apatosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(ApatosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(ApatosaurusEntity animatable) {
return Constants.rl("animations/apatosaurus.animation.json");
}
@Override
public void setCustomAnimations(ApatosaurusEntity entity, long id, AnimationState<ApatosaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "tail1", "tail2", "tail3", "tail4", "tail5", "tail6" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("neck1");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -10.0f, 10.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.ApatosaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class ApatosaurusRenderer extends GeoEntityRenderer<ApatosaurusEntity> {
private final float animalScale = 1.75F;
public ApatosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new ApatosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, ApatosaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum ApatosaurusVariant {
MALE(0),
FEMALE(1);
private static final ApatosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(ApatosaurusVariant::getId)).toArray(ApatosaurusVariant[]::new);
private final int id;
ApatosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static ApatosaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.ArambourgianiaEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class ArambourgianiaModel extends GeoModel<ArambourgianiaEntity> {
private static final Map<ArambourgianiaVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(ArambourgianiaVariant.class), map -> {
map.put(ArambourgianiaVariant.MALE, Constants.rl("textures/entity/arambourgiania.png"));
map.put(ArambourgianiaVariant.FEMALE, Constants.rl("textures/entity/arambourgiania_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(ArambourgianiaEntity animatable) {
return Constants.rl("geo/arambourgiania.geo.json");
}
@Override
public ResourceLocation getTextureResource(ArambourgianiaEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(ArambourgianiaEntity animatable) {
return Constants.rl("animations/arambourgiania.animation.json");
}
@Override
public void setCustomAnimations(ArambourgianiaEntity entity, long id, AnimationState<ArambourgianiaEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("Head");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -20.0f, 20.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.ArambourgianiaEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class ArambourgianiaRenderer extends GeoEntityRenderer<ArambourgianiaEntity> {
private final float animalScale = 1.75F;
public ArambourgianiaRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new ArambourgianiaModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, ArambourgianiaEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum ArambourgianiaVariant {
MALE(0),
FEMALE(1);
private static final ArambourgianiaVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(ArambourgianiaVariant::getId)).toArray(ArambourgianiaVariant[]::new);
private final int id;
ArambourgianiaVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static ArambourgianiaVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.BaryonyxEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class BaryonyxModel extends GeoModel<BaryonyxEntity> {
private static final Map<BaryonyxVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(BaryonyxVariant.class), map -> {
map.put(BaryonyxVariant.MALE, Constants.rl("textures/entity/baryonyx.png"));
map.put(BaryonyxVariant.FEMALE, Constants.rl("textures/entity/baryonyx_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(BaryonyxEntity animatable) {
return Constants.rl("geo/baryonyx.geo.json");
}
@Override
public ResourceLocation getTextureResource(BaryonyxEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(BaryonyxEntity animatable) {
return Constants.rl("animations/baryonyx.animation.json");
}
@Override
public void setCustomAnimations(BaryonyxEntity entity, long id, AnimationState<BaryonyxEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3", "Tail4", "Tail5", "Tail6" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("Neck_under2");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -20.0f, 20.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.BaryonyxEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class BaryonyxRenderer extends GeoEntityRenderer<BaryonyxEntity> {
private final float animalScale = 1.5F;
public BaryonyxRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new BaryonyxModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, BaryonyxEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum BaryonyxVariant {
MALE(0),
FEMALE(1);
private static final BaryonyxVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(BaryonyxVariant::getId)).toArray(BaryonyxVariant[]::new);
private final int id;
BaryonyxVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static BaryonyxVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.BrachiosaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class BrachiosaurusModel extends GeoModel<BrachiosaurusEntity> {
private static final Map<BrachiosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(BrachiosaurusVariant.class), map -> {
map.put(BrachiosaurusVariant.MALE, Constants.rl("textures/entity/brachiosaurus.png"));
map.put(BrachiosaurusVariant.FEMALE, Constants.rl("textures/entity/brachiosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(BrachiosaurusEntity animatable) {
return Constants.rl("geo/brachiosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(BrachiosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(BrachiosaurusEntity animatable) {
return Constants.rl("animations/brachiosaurus.animation.json");
}
@Override
public void setCustomAnimations(BrachiosaurusEntity entity, long id, AnimationState<BrachiosaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "tail1", "tail2", "tail3", "tail4", "tail5", "tail6" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("Neck1");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -10.0f, 10.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.BrachiosaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class BrachiosaurusRenderer extends GeoEntityRenderer<BrachiosaurusEntity> {
private final float animalScale = 1.6F;
public BrachiosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new BrachiosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, BrachiosaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum BrachiosaurusVariant {
MALE(0),
FEMALE(1);
private static final BrachiosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(BrachiosaurusVariant::getId)).toArray(BrachiosaurusVariant[]::new);
private final int id;
BrachiosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static BrachiosaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.CarcharodontosaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class CarcharodontosaurusModel extends GeoModel<CarcharodontosaurusEntity> {
private static final Map<CarcharodontosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(CarcharodontosaurusVariant.class), map -> {
map.put(CarcharodontosaurusVariant.MALE, Constants.rl("textures/entity/carcharodontosaurus.png"));
map.put(CarcharodontosaurusVariant.FEMALE, Constants.rl("textures/entity/carcharodontosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(CarcharodontosaurusEntity animatable) {
return Constants.rl("geo/carcharodontosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(CarcharodontosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(CarcharodontosaurusEntity animatable) {
return Constants.rl("animations/carcharodontosaurus.animation.json");
}
@Override
public void setCustomAnimations(CarcharodontosaurusEntity entity, long id, AnimationState<CarcharodontosaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3", "Tail4", "Tail5", "Tail6" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("Neck1");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -20.0f, 20.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.CarcharodontosaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class CarcharodontosaurusRenderer extends GeoEntityRenderer<CarcharodontosaurusEntity> {
private final float animalScale = 2.25F;
public CarcharodontosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new CarcharodontosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, CarcharodontosaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum CarcharodontosaurusVariant {
MALE(0),
FEMALE(1);
private static final CarcharodontosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(CarcharodontosaurusVariant::getId)).toArray(CarcharodontosaurusVariant[]::new);
private final int id;
CarcharodontosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static CarcharodontosaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.CarnotaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class CarnotaurusModel extends GeoModel<CarnotaurusEntity> {
private static final Map<CarnotaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(CarnotaurusVariant.class), map -> {
map.put(CarnotaurusVariant.MALE, Constants.rl("textures/entity/carnotaurus.png"));
map.put(CarnotaurusVariant.FEMALE, Constants.rl("textures/entity/carnotaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(CarnotaurusEntity animatable) {
return Constants.rl("geo/carnotaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(CarnotaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(CarnotaurusEntity animatable) {
return Constants.rl("animations/carnotaurus.animation.json");
}
@Override
public void setCustomAnimations(CarnotaurusEntity entity, long id, AnimationState<CarnotaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3", "Tail4", "Tail5", "Tail6" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("Neck1");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -20.0f, 20.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.CarnotaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class CarnotaurusRenderer extends GeoEntityRenderer<CarnotaurusEntity> {
private final float animalScale = 1.5F;
public CarnotaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new CarnotaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, CarnotaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum CarnotaurusVariant {
MALE(0),
FEMALE(1);
private static final CarnotaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(CarnotaurusVariant::getId)).toArray(CarnotaurusVariant[]::new);
private final int id;
CarnotaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static CarnotaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.CearadactylusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class CearadactylusModel extends GeoModel<CearadactylusEntity> {
private static final Map<CearadactylusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(CearadactylusVariant.class), map -> {
map.put(CearadactylusVariant.MALE, Constants.rl("textures/entity/cearadactylus.png"));
map.put(CearadactylusVariant.FEMALE, Constants.rl("textures/entity/cearadactylus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(CearadactylusEntity animatable) {
return Constants.rl("geo/cearadactylus.geo.json");
}
@Override
public ResourceLocation getTextureResource(CearadactylusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(CearadactylusEntity animatable) {
return Constants.rl("animations/cearadactylus.animation.json");
}
@Override
public void setCustomAnimations(CearadactylusEntity entity, long id, AnimationState<CearadactylusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("Neck1");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -20.0f, 20.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}

Some files were not shown because too many files have changed in this diff Show More