port A LOT
This commit is contained in:
@@ -1,11 +1,19 @@
|
||||
package net.cmr.jurassicrevived;
|
||||
|
||||
import dev.architectury.event.events.common.LifecycleEvent;
|
||||
import net.cmr.jurassicrevived.block.ModBlocks;
|
||||
import net.cmr.jurassicrevived.block.entity.ModBlockEntities;
|
||||
import net.cmr.jurassicrevived.config.JRConfigManager;
|
||||
import net.cmr.jurassicrevived.entity.ModEntities;
|
||||
import net.cmr.jurassicrevived.item.ModCreativeTabs;
|
||||
import net.cmr.jurassicrevived.item.ModItems;
|
||||
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.world.item.Items;
|
||||
|
||||
@@ -38,8 +46,27 @@ public class CommonClass
|
||||
JRConfigManager.load(Services.PLATFORM.getConfigDir());
|
||||
|
||||
ModBlocks.register();
|
||||
ModEntities.register();
|
||||
ModItems.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;
|
||||
|
||||
import net.minecraft.resources.ResourceLocation;
|
||||
import org.slf4j.Logger;
|
||||
import org.slf4j.LoggerFactory;
|
||||
|
||||
@@ -9,4 +10,12 @@ public class Constants
|
||||
public static final String MOD_ID = "jurassicrevived";
|
||||
public static final String MOD_NAME = "JurassicRevived";
|
||||
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.RegistrySupplier;
|
||||
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.mixin.FlowerPotBlockAccessor;
|
||||
import net.minecraft.core.registries.Registries;
|
||||
import net.minecraft.world.effect.MobEffects;
|
||||
import net.minecraft.world.item.BlockItem;
|
||||
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 java.util.Map;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
public class ModBlocks {
|
||||
@@ -20,6 +26,647 @@ public class ModBlocks {
|
||||
public static final RegistrySupplier<Block> FOSSIL_ORE = registerBlock("fossil_ore",
|
||||
() -> 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 ---
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
//?}
|
||||
}
|
||||
+240
@@ -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();
|
||||
}
|
||||
}
|
||||
+107
@@ -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);
|
||||
}
|
||||
}
|
||||
+343
@@ -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); }
|
||||
}
|
||||
+356
@@ -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); }
|
||||
}
|
||||
+360
@@ -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);
|
||||
}
|
||||
}
|
||||
+297
@@ -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); }
|
||||
}
|
||||
+349
@@ -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); }
|
||||
}
|
||||
+357
@@ -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); }
|
||||
}
|
||||
+353
@@ -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);
|
||||
}
|
||||
}
|
||||
+264
@@ -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();
|
||||
}
|
||||
//?}
|
||||
}
|
||||
+305
@@ -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();
|
||||
}
|
||||
//?}
|
||||
}
|
||||
+171
@@ -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
|
||||
}
|
||||
}
|
||||
+170
@@ -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();
|
||||
}
|
||||
//?}
|
||||
}
|
||||
+147
@@ -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();
|
||||
}
|
||||
//?}
|
||||
}
|
||||
+72
@@ -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);
|
||||
}
|
||||
}
|
||||
+114
@@ -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 screen’s 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 screen’s 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
|
||||
}
|
||||
}
|
||||
+114
@@ -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));
|
||||
}
|
||||
}
|
||||
+147
@@ -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 recipe’s 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)
|
||||
public boolean enableDinosaurs = true;
|
||||
public int spawnWeight = 10;
|
||||
public boolean requirePower = true;
|
||||
public int fePerSecond = 1000;
|
||||
public int itemsPerSecond = 10;
|
||||
public int milliBucketsPerSecond = 1000;
|
||||
|
||||
public JRConfig() {
|
||||
}
|
||||
|
||||
@@ -1,22 +1,427 @@
|
||||
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.RegistrySupplier;
|
||||
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.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 static final DeferredRegister<EntityType<?>> ENTITIES =
|
||||
DeferredRegister.create(Constants.MOD_ID, Registries.ENTITY_TYPE);
|
||||
|
||||
// --- Example (Generic Entity) ---
|
||||
// You will need your own Entity class (e.g., VelociraptorEntity::new)
|
||||
/*
|
||||
public static final RegistrySupplier<EntityType<VelociraptorEntity>> VELOCIRAPTOR = ENTITIES.register("velociraptor",
|
||||
() -> EntityType.Builder.of(VelociraptorEntity::new, MobCategory.CREATURE)
|
||||
.sized(1.0f, 2.0f)
|
||||
.build(Constants.MOD_ID + ":velociraptor"));
|
||||
*/
|
||||
public static final RegistrySupplier<EntityType<SeatEntity>> SEAT =
|
||||
ENTITIES.register("seat", () ->
|
||||
EntityType.Builder.<SeatEntity>of(SeatEntity::new, MobCategory.MISC)
|
||||
.sized(0.001f, 0.001f)
|
||||
.clientTrackingRange(16)
|
||||
.updateInterval(1)
|
||||
.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() {
|
||||
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);
|
||||
}
|
||||
}
|
||||
+27
@@ -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();
|
||||
}
|
||||
}
|
||||
+46
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -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];
|
||||
}
|
||||
}
|
||||
+112
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
+26
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
package net.cmr.jurassicrevived.entity.client;
|
||||
|
||||
import com.mojang.blaze3d.vertex.PoseStack;
|
||||
import net.cmr.jurassicrevived.entity.custom.CearadactylusEntity;
|
||||
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 CearadactylusRenderer extends GeoEntityRenderer<CearadactylusEntity> {
|
||||
private final float animalScale = 1.0F;
|
||||
public CearadactylusRenderer(EntityRendererProvider.Context renderManager) {
|
||||
super(renderManager, new CearadactylusModel());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, CearadactylusEntity 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 CearadactylusVariant {
|
||||
MALE(0),
|
||||
FEMALE(1);
|
||||
|
||||
private static final CearadactylusVariant[] BY_ID = Arrays.stream(values()).sorted(
|
||||
Comparator.comparingInt(CearadactylusVariant::getId)).toArray(CearadactylusVariant[]::new);
|
||||
|
||||
private final int id;
|
||||
|
||||
CearadactylusVariant(int id) {
|
||||
this.id = id;
|
||||
}
|
||||
|
||||
public int getId() {
|
||||
return id;
|
||||
}
|
||||
|
||||
public static CearadactylusVariant byId(int id) {
|
||||
return BY_ID[id % BY_ID.length];
|
||||
}
|
||||
}
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user