port A LOT

This commit is contained in:
2026-01-10 23:36:28 -05:00
parent dfde4b2315
commit 9a954a0af1
446 changed files with 57881 additions and 71 deletions
@@ -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);
}
}
@@ -0,0 +1,27 @@
package net.cmr.jurassicrevived.entity.ai.navigation;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.ai.navigation.GroundPathNavigation;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.pathfinder.PathFinder;
import net.minecraft.world.phys.Vec3;
public class CustomDinoNavigation extends GroundPathNavigation {
public CustomDinoNavigation(Mob mob, Level level) {
super(mob, level);
}
@Override
protected PathFinder createPathFinder(int maxVisitedNodes) {
this.nodeEvaluator = new LargeEntityNodeEvaluator();
this.nodeEvaluator.setCanPassDoors(true);
return new PathFinder(this.nodeEvaluator, maxVisitedNodes);
}
// Fix for large entities getting stuck: Ensure we don't try to path to the exact center of a block if our hitbox is huge
@Override
protected Vec3 getTempMobPos() {
return this.mob.position();
}
}
@@ -0,0 +1,46 @@
package net.cmr.jurassicrevived.entity.ai.navigation;
import net.minecraft.core.BlockPos;
import net.minecraft.tags.BlockTags;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.level.block.state.BlockState;
/*? if <=1.20.1 {*/
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.pathfinder.BlockPathTypes;
/*?} else {*/
/*import net.minecraft.world.level.pathfinder.PathType;
import net.minecraft.world.level.pathfinder.PathfindingContext;
*//*?}*/
import net.minecraft.world.level.pathfinder.WalkNodeEvaluator;
public class LargeEntityNodeEvaluator extends WalkNodeEvaluator {
/*? if <=1.20.1 {*/
public BlockPathTypes getBlockPathType(BlockGetter level, int x, int y, int z) {
Mob entity = this.mob;
BlockPos pos = new BlockPos(x, y, z);
BlockState state = level.getBlockState(pos);
if (entity != null && entity.getBbWidth() > 1.5f && state.is(BlockTags.LEAVES)) {
return BlockPathTypes.OPEN;
}
/*?} else {*/
/*@Override
public PathType getPathType(PathfindingContext context, int x, int y, int z) {
Mob entity = this.mob;
BlockPos pos = new BlockPos(x, y, z);
BlockState state = context.level().getBlockState(pos);
if (entity != null && entity.getBbWidth() > 1.5f && state.is(BlockTags.LEAVES)) {
return PathType.OPEN;
}
*//*?}*/
if (entity != null && entity.getBbWidth() >= 2.0f) {
}
/*? if <=1.20.1 {*/
return super.getBlockPathType(level, x, y, z);
/*?} else {*/
/*return super.getPathType(context, x, y, z);
*//*?}*/
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.AlbertosaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class AlbertosaurusModel extends GeoModel<AlbertosaurusEntity> {
private static final Map<AlbertosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(AlbertosaurusVariant.class), map -> {
map.put(AlbertosaurusVariant.MALE, Constants.rl("textures/entity/albertosaurus.png"));
map.put(AlbertosaurusVariant.FEMALE, Constants.rl("textures/entity/albertosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(AlbertosaurusEntity animatable) {
return Constants.rl("geo/albertosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(AlbertosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(AlbertosaurusEntity animatable) {
return Constants.rl("animations/albertosaurus.animation.json");
}
@Override
public void setCustomAnimations(AlbertosaurusEntity entity, long id, AnimationState<AlbertosaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "TailBASE", "Tail2", "Tail3", "Tail4", "Tail5", "Tail6" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("NeckBASE");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -20.0f, 20.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.AlbertosaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class AlbertosaurusRenderer extends GeoEntityRenderer<AlbertosaurusEntity> {
private final float animalScale = 1.5F;
public AlbertosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new AlbertosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, AlbertosaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum AlbertosaurusVariant {
MALE(0),
FEMALE(1);
private static final AlbertosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(AlbertosaurusVariant::getId)).toArray(AlbertosaurusVariant[]::new);
private final int id;
AlbertosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static AlbertosaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.AllosaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class AllosaurusModel extends GeoModel<AllosaurusEntity> {
private static final Map<AllosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(AllosaurusVariant.class), map -> {
map.put(AllosaurusVariant.MALE, Constants.rl("textures/entity/allosaurus.png"));
map.put(AllosaurusVariant.FEMALE, Constants.rl("textures/entity/allosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(AllosaurusEntity animatable) {
return Constants.rl("geo/allosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(AllosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(AllosaurusEntity animatable) {
return Constants.rl("animations/allosaurus.animation.json");
}
@Override
public void setCustomAnimations(AllosaurusEntity entity, long id, AnimationState<AllosaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3", "Tail4", "Tail5", "Tail6" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("Neck1");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -20.0f, 20.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.AllosaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class AllosaurusRenderer extends GeoEntityRenderer<AllosaurusEntity> {
private final float animalScale = 1.65F;
public AllosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new AllosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, AllosaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum AllosaurusVariant {
MALE(0),
FEMALE(1);
private static final AllosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(AllosaurusVariant::getId)).toArray(AllosaurusVariant[]::new);
private final int id;
AllosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static AllosaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.AlvarezsaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class AlvarezsaurusModel extends GeoModel<AlvarezsaurusEntity> {
private static final Map<AlvarezsaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(AlvarezsaurusVariant.class), map -> {
map.put(AlvarezsaurusVariant.MALE, Constants.rl("textures/entity/alvarezsaurus.png"));
map.put(AlvarezsaurusVariant.FEMALE, Constants.rl("textures/entity/alvarezsaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(AlvarezsaurusEntity animatable) {
return Constants.rl("geo/alvarezsaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(AlvarezsaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(AlvarezsaurusEntity animatable) {
return Constants.rl("animations/alvarezsaurus.animation.json");
}
@Override
public void setCustomAnimations(AlvarezsaurusEntity entity, long id, AnimationState<AlvarezsaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "TailBase", "TailMid1", "TailMid2", "TailTip"};
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("NeckBase");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -30.0f, 30.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.AlvarezsaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class AlvarezsaurusRenderer extends GeoEntityRenderer<AlvarezsaurusEntity> {
private final float animalScale = 0.5F;
public AlvarezsaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new AlvarezsaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, AlvarezsaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum AlvarezsaurusVariant {
MALE(0),
FEMALE(1);
private static final AlvarezsaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(AlvarezsaurusVariant::getId)).toArray(AlvarezsaurusVariant[]::new);
private final int id;
AlvarezsaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static AlvarezsaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.AnkylosaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class AnkylosaurusModel extends GeoModel<AnkylosaurusEntity> {
private static final Map<AnkylosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(AnkylosaurusVariant.class), map -> {
map.put(AnkylosaurusVariant.MALE, Constants.rl("textures/entity/ankylosaurus.png"));
map.put(AnkylosaurusVariant.FEMALE, Constants.rl("textures/entity/ankylosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(AnkylosaurusEntity animatable) {
return Constants.rl("geo/ankylosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(AnkylosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(AnkylosaurusEntity animatable) {
return Constants.rl("animations/ankylosaurus.animation.json");
}
@Override
public void setCustomAnimations(AnkylosaurusEntity entity, long id, AnimationState<AnkylosaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "tail1", "tail2", "tail3", "tail4", "tailend" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("neck1");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -30.0f, 30.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.AnkylosaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class AnkylosaurusRenderer extends GeoEntityRenderer<AnkylosaurusEntity> {
private final float animalScale = 2.4F;
public AnkylosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new AnkylosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, AnkylosaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum AnkylosaurusVariant {
MALE(0),
FEMALE(1);
private static final AnkylosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(AnkylosaurusVariant::getId)).toArray(AnkylosaurusVariant[]::new);
private final int id;
AnkylosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static AnkylosaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.ApatosaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class ApatosaurusModel extends GeoModel<ApatosaurusEntity> {
private static final Map<ApatosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(ApatosaurusVariant.class), map -> {
map.put(ApatosaurusVariant.MALE, Constants.rl("textures/entity/apatosaurus.png"));
map.put(ApatosaurusVariant.FEMALE, Constants.rl("textures/entity/apatosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(ApatosaurusEntity animatable) {
return Constants.rl("geo/apatosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(ApatosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(ApatosaurusEntity animatable) {
return Constants.rl("animations/apatosaurus.animation.json");
}
@Override
public void setCustomAnimations(ApatosaurusEntity entity, long id, AnimationState<ApatosaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "tail1", "tail2", "tail3", "tail4", "tail5", "tail6" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("neck1");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -10.0f, 10.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.ApatosaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class ApatosaurusRenderer extends GeoEntityRenderer<ApatosaurusEntity> {
private final float animalScale = 1.75F;
public ApatosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new ApatosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, ApatosaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum ApatosaurusVariant {
MALE(0),
FEMALE(1);
private static final ApatosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(ApatosaurusVariant::getId)).toArray(ApatosaurusVariant[]::new);
private final int id;
ApatosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static ApatosaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.ArambourgianiaEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class ArambourgianiaModel extends GeoModel<ArambourgianiaEntity> {
private static final Map<ArambourgianiaVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(ArambourgianiaVariant.class), map -> {
map.put(ArambourgianiaVariant.MALE, Constants.rl("textures/entity/arambourgiania.png"));
map.put(ArambourgianiaVariant.FEMALE, Constants.rl("textures/entity/arambourgiania_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(ArambourgianiaEntity animatable) {
return Constants.rl("geo/arambourgiania.geo.json");
}
@Override
public ResourceLocation getTextureResource(ArambourgianiaEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(ArambourgianiaEntity animatable) {
return Constants.rl("animations/arambourgiania.animation.json");
}
@Override
public void setCustomAnimations(ArambourgianiaEntity entity, long id, AnimationState<ArambourgianiaEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("Head");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -20.0f, 20.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.ArambourgianiaEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class ArambourgianiaRenderer extends GeoEntityRenderer<ArambourgianiaEntity> {
private final float animalScale = 1.75F;
public ArambourgianiaRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new ArambourgianiaModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, ArambourgianiaEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum ArambourgianiaVariant {
MALE(0),
FEMALE(1);
private static final ArambourgianiaVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(ArambourgianiaVariant::getId)).toArray(ArambourgianiaVariant[]::new);
private final int id;
ArambourgianiaVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static ArambourgianiaVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.BaryonyxEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class BaryonyxModel extends GeoModel<BaryonyxEntity> {
private static final Map<BaryonyxVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(BaryonyxVariant.class), map -> {
map.put(BaryonyxVariant.MALE, Constants.rl("textures/entity/baryonyx.png"));
map.put(BaryonyxVariant.FEMALE, Constants.rl("textures/entity/baryonyx_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(BaryonyxEntity animatable) {
return Constants.rl("geo/baryonyx.geo.json");
}
@Override
public ResourceLocation getTextureResource(BaryonyxEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(BaryonyxEntity animatable) {
return Constants.rl("animations/baryonyx.animation.json");
}
@Override
public void setCustomAnimations(BaryonyxEntity entity, long id, AnimationState<BaryonyxEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3", "Tail4", "Tail5", "Tail6" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("Neck_under2");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -20.0f, 20.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.BaryonyxEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class BaryonyxRenderer extends GeoEntityRenderer<BaryonyxEntity> {
private final float animalScale = 1.5F;
public BaryonyxRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new BaryonyxModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, BaryonyxEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum BaryonyxVariant {
MALE(0),
FEMALE(1);
private static final BaryonyxVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(BaryonyxVariant::getId)).toArray(BaryonyxVariant[]::new);
private final int id;
BaryonyxVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static BaryonyxVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.BrachiosaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class BrachiosaurusModel extends GeoModel<BrachiosaurusEntity> {
private static final Map<BrachiosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(BrachiosaurusVariant.class), map -> {
map.put(BrachiosaurusVariant.MALE, Constants.rl("textures/entity/brachiosaurus.png"));
map.put(BrachiosaurusVariant.FEMALE, Constants.rl("textures/entity/brachiosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(BrachiosaurusEntity animatable) {
return Constants.rl("geo/brachiosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(BrachiosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(BrachiosaurusEntity animatable) {
return Constants.rl("animations/brachiosaurus.animation.json");
}
@Override
public void setCustomAnimations(BrachiosaurusEntity entity, long id, AnimationState<BrachiosaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "tail1", "tail2", "tail3", "tail4", "tail5", "tail6" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("Neck1");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -10.0f, 10.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.BrachiosaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class BrachiosaurusRenderer extends GeoEntityRenderer<BrachiosaurusEntity> {
private final float animalScale = 1.6F;
public BrachiosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new BrachiosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, BrachiosaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum BrachiosaurusVariant {
MALE(0),
FEMALE(1);
private static final BrachiosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(BrachiosaurusVariant::getId)).toArray(BrachiosaurusVariant[]::new);
private final int id;
BrachiosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static BrachiosaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.CarcharodontosaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class CarcharodontosaurusModel extends GeoModel<CarcharodontosaurusEntity> {
private static final Map<CarcharodontosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(CarcharodontosaurusVariant.class), map -> {
map.put(CarcharodontosaurusVariant.MALE, Constants.rl("textures/entity/carcharodontosaurus.png"));
map.put(CarcharodontosaurusVariant.FEMALE, Constants.rl("textures/entity/carcharodontosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(CarcharodontosaurusEntity animatable) {
return Constants.rl("geo/carcharodontosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(CarcharodontosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(CarcharodontosaurusEntity animatable) {
return Constants.rl("animations/carcharodontosaurus.animation.json");
}
@Override
public void setCustomAnimations(CarcharodontosaurusEntity entity, long id, AnimationState<CarcharodontosaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3", "Tail4", "Tail5", "Tail6" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("Neck1");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -20.0f, 20.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.CarcharodontosaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class CarcharodontosaurusRenderer extends GeoEntityRenderer<CarcharodontosaurusEntity> {
private final float animalScale = 2.25F;
public CarcharodontosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new CarcharodontosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, CarcharodontosaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum CarcharodontosaurusVariant {
MALE(0),
FEMALE(1);
private static final CarcharodontosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(CarcharodontosaurusVariant::getId)).toArray(CarcharodontosaurusVariant[]::new);
private final int id;
CarcharodontosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static CarcharodontosaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.CarnotaurusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class CarnotaurusModel extends GeoModel<CarnotaurusEntity> {
private static final Map<CarnotaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(CarnotaurusVariant.class), map -> {
map.put(CarnotaurusVariant.MALE, Constants.rl("textures/entity/carnotaurus.png"));
map.put(CarnotaurusVariant.FEMALE, Constants.rl("textures/entity/carnotaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(CarnotaurusEntity animatable) {
return Constants.rl("geo/carnotaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(CarnotaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(CarnotaurusEntity animatable) {
return Constants.rl("animations/carnotaurus.animation.json");
}
@Override
public void setCustomAnimations(CarnotaurusEntity entity, long id, AnimationState<CarnotaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3", "Tail4", "Tail5", "Tail6" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("Neck1");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -20.0f, 20.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import com.mojang.blaze3d.vertex.PoseStack;
import net.cmr.jurassicrevived.entity.custom.CarnotaurusEntity;
import net.minecraft.client.renderer.entity.EntityRendererProvider;
import net.minecraft.util.Mth;
import software.bernie.geckolib.cache.object.BakedGeoModel;
import software.bernie.geckolib.renderer.GeoEntityRenderer;
public class CarnotaurusRenderer extends GeoEntityRenderer<CarnotaurusEntity> {
private final float animalScale = 1.5F;
public CarnotaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new CarnotaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, CarnotaurusEntity animatable, BakedGeoModel model, boolean isReRender, float partialTick, int packedLight, int packedOverlay) {
poseStack.scale(animalScale, animalScale, animalScale);
if(animatable.isBaby()) {
float growthProgress = Mth.clamp((24000.0F + animatable.getSyncedAge()) / 24000.0F, 0.0F, 1.0F);
float scale = Mth.lerp(growthProgress, 0.2F, 1.0F);
poseStack.scale(scale, scale, scale);
}
}
}
@@ -0,0 +1,26 @@
package net.cmr.jurassicrevived.entity.client;
import java.util.Arrays;
import java.util.Comparator;
public enum CarnotaurusVariant {
MALE(0),
FEMALE(1);
private static final CarnotaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(CarnotaurusVariant::getId)).toArray(CarnotaurusVariant[]::new);
private final int id;
CarnotaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static CarnotaurusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,112 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.CearadactylusEntity;
import net.minecraft.Util;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.util.Mth;
/*? if <=1.20.1 {*/
import software.bernie.geckolib.core.animation.AnimationState;
/*?} else {*/
/*import software.bernie.geckolib.animation.AnimationState;
*//*?}*/
import software.bernie.geckolib.cache.object.GeoBone;
import software.bernie.geckolib.constant.DataTickets;
import software.bernie.geckolib.model.GeoModel;
import java.util.Map;
public class CearadactylusModel extends GeoModel<CearadactylusEntity> {
private static final Map<CearadactylusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(CearadactylusVariant.class), map -> {
map.put(CearadactylusVariant.MALE, Constants.rl("textures/entity/cearadactylus.png"));
map.put(CearadactylusVariant.FEMALE, Constants.rl("textures/entity/cearadactylus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(CearadactylusEntity animatable) {
return Constants.rl("geo/cearadactylus.geo.json");
}
@Override
public ResourceLocation getTextureResource(CearadactylusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(CearadactylusEntity animatable) {
return Constants.rl("animations/cearadactylus.animation.json");
}
@Override
public void setCustomAnimations(CearadactylusEntity entity, long id, AnimationState<CearadactylusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3" };
int n = tailBones.length;
if (appliedYaw == null || appliedYaw.length != n) {
appliedYaw = new float[n];
appliedRoll = new float[n];
}
// 1) Clear previous offsets (from the last entity rendered with this model instance)
for (int i = 0; i < n; i++) {
if (appliedYaw[i] == 0.0f && appliedRoll[i] == 0.0f) continue;
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
if (appliedYaw[i] != 0.0f) bone.setRotY(bone.getRotY() - appliedYaw[i]);
if (appliedRoll[i] != 0.0f) bone.setRotZ(bone.getRotZ() - appliedRoll[i]);
appliedYaw[i] = 0.0f;
appliedRoll[i] = 0.0f;
}
// 2) Interpolated sway for extra smoothness between ticks
float sway = entity.getTailSwayOffset(state.getPartialTick()); // [-1, 1]
// Tuning
float maxYawDeg = 22.0f; // increased max sweep
float swayGain = 1.35f; // amplifies overall power
float rollFraction = 0.40f; // slightly stronger roll for heft
float deg2rad = (float)Math.PI / 180f;
// Direction: positive sway (left turn) -> tail swings right (negative yaw)
// Flip the sign here if the sway feels inverted
float baseYaw = sway * maxYawDeg * deg2rad;
float baseRoll = -baseYaw * rollFraction;
float[] weights = { 1.00f, 0.78f, 0.58f, 0.42f, 0.30f, 0.22f };
for (int i = 0; i < n; i++) {
GeoBone bone = (GeoBone) getAnimationProcessor().getBone(tailBones[i]);
if (bone == null) continue;
float w = weights[i];
float yaw = baseYaw * w;
float roll = baseRoll * w;
// OVERRIDE animations on Y/Z only: keep the model's predefined X bend intact
// Do NOT reset rotX here, so the upward bend stays
bone.setRotY(yaw);
bone.setRotZ(roll);
appliedYaw[i] = yaw;
appliedRoll[i] = roll;
}
GeoBone head = (GeoBone) getAnimationProcessor().getBone("Neck1");
if (head != null) {
var entityData = state.getData(DataTickets.ENTITY_MODEL_DATA);
float clampedYawDeg = Mth.clamp(entityData.netHeadYaw(), -20.0f, 20.0f);
head.setRotY(clampedYawDeg * Mth.DEG_TO_RAD);
}
}
}
@@ -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];
}
}
@@ -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.CeratosaurusEntity;
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 CeratosaurusModel extends GeoModel<CeratosaurusEntity> {
private static final Map<CeratosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(CeratosaurusVariant.class), map -> {
map.put(CeratosaurusVariant.MALE, Constants.rl("textures/entity/ceratosaurus.png"));
map.put(CeratosaurusVariant.FEMALE, Constants.rl("textures/entity/ceratosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(CeratosaurusEntity animatable) {
return Constants.rl("geo/ceratosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(CeratosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(CeratosaurusEntity animatable) {
return Constants.rl("animations/ceratosaurus.animation.json");
}
@Override
public void setCustomAnimations(CeratosaurusEntity entity, long id, AnimationState<CeratosaurusEntity> 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("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.CeratosaurusEntity;
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 CeratosaurusRenderer extends GeoEntityRenderer<CeratosaurusEntity> {
private final float animalScale = 1.75F;
public CeratosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new CeratosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, CeratosaurusEntity 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 CeratosaurusVariant {
MALE(0),
FEMALE(1);
private static final CeratosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(CeratosaurusVariant::getId)).toArray(CeratosaurusVariant[]::new);
private final int id;
CeratosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static CeratosaurusVariant 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.ChasmosaurusEntity;
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 ChasmosaurusModel extends GeoModel<ChasmosaurusEntity> {
private static final Map<ChasmosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(ChasmosaurusVariant.class), map -> {
map.put(ChasmosaurusVariant.MALE, Constants.rl("textures/entity/chasmosaurus.png"));
map.put(ChasmosaurusVariant.FEMALE, Constants.rl("textures/entity/chasmosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(ChasmosaurusEntity animatable) {
return Constants.rl("geo/chasmosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(ChasmosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(ChasmosaurusEntity animatable) {
return Constants.rl("animations/chasmosaurus.animation.json");
}
@Override
public void setCustomAnimations(ChasmosaurusEntity entity, long id, AnimationState<ChasmosaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3", "Tail4", "Tail5" };
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("Neck");
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.ChasmosaurusEntity;
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 ChasmosaurusRenderer extends GeoEntityRenderer<ChasmosaurusEntity> {
private final float animalScale = 1.25F;
public ChasmosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new ChasmosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, ChasmosaurusEntity 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 ChasmosaurusVariant {
MALE(0),
FEMALE(1);
private static final ChasmosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(ChasmosaurusVariant::getId)).toArray(ChasmosaurusVariant[]::new);
private final int id;
ChasmosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static ChasmosaurusVariant 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.ChickenosaurusEntity;
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 ChickenosaurusModel extends GeoModel<ChickenosaurusEntity> {
private static final Map<ChickenosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(ChickenosaurusVariant.class), map -> {
map.put(ChickenosaurusVariant.MALE, Constants.rl("textures/entity/chickenosaurus.png"));
map.put(ChickenosaurusVariant.FEMALE, Constants.rl("textures/entity/chickenosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(ChickenosaurusEntity animatable) {
return Constants.rl("geo/chickenosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(ChickenosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(ChickenosaurusEntity animatable) {
return Constants.rl("animations/chickenosaurus.animation.json");
}
@Override
public void setCustomAnimations(ChickenosaurusEntity entity, long id, AnimationState<ChickenosaurusEntity> 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("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.ChickenosaurusEntity;
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 ChickenosaurusRenderer extends GeoEntityRenderer<ChickenosaurusEntity> {
private final float animalScale = 1.0F;
public ChickenosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new ChickenosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, ChickenosaurusEntity 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 ChickenosaurusVariant {
MALE(0),
FEMALE(1);
private static final ChickenosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(ChickenosaurusVariant::getId)).toArray(ChickenosaurusVariant[]::new);
private final int id;
ChickenosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static ChickenosaurusVariant 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.CoelophysisEntity;
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 CoelophysisModel extends GeoModel<CoelophysisEntity> {
private static final Map<CoelophysisVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(CoelophysisVariant.class), map -> {
map.put(CoelophysisVariant.MALE, Constants.rl("textures/entity/coelophysis.png"));
map.put(CoelophysisVariant.FEMALE, Constants.rl("textures/entity/coelophysis_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(CoelophysisEntity animatable) {
return Constants.rl("geo/coelophysis.geo.json");
}
@Override
public ResourceLocation getTextureResource(CoelophysisEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(CoelophysisEntity animatable) {
return Constants.rl("animations/coelophysis.animation.json");
}
@Override
public void setCustomAnimations(CoelophysisEntity entity, long id, AnimationState<CoelophysisEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "tail1", "tail2", "tail3", "tail4", "Tail5" };
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.CoelophysisEntity;
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 CoelophysisRenderer extends GeoEntityRenderer<CoelophysisEntity> {
private final float animalScale = 0.9F;
public CoelophysisRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new CoelophysisModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, CoelophysisEntity 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 CoelophysisVariant {
MALE(0),
FEMALE(1);
private static final CoelophysisVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(CoelophysisVariant::getId)).toArray(CoelophysisVariant[]::new);
private final int id;
CoelophysisVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static CoelophysisVariant 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.CoelurusEntity;
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 CoelurusModel extends GeoModel<CoelurusEntity> {
private static final Map<CoelurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(CoelurusVariant.class), map -> {
map.put(CoelurusVariant.MALE, Constants.rl("textures/entity/coelurus.png"));
map.put(CoelurusVariant.FEMALE, Constants.rl("textures/entity/coelurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(CoelurusEntity animatable) {
return Constants.rl("geo/coelurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(CoelurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(CoelurusEntity animatable) {
return Constants.rl("animations/coelurus.animation.json");
}
@Override
public void setCustomAnimations(CoelurusEntity entity, long id, AnimationState<CoelurusEntity> 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(), -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.CoelurusEntity;
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 CoelurusRenderer extends GeoEntityRenderer<CoelurusEntity> {
private final float animalScale = 0.5F;
public CoelurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new CoelurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, CoelurusEntity 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 CoelurusVariant {
MALE(0),
FEMALE(1);
private static final CoelurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(CoelurusVariant::getId)).toArray(CoelurusVariant[]::new);
private final int id;
CoelurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static CoelurusVariant 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.CompsognathusEntity;
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 CompsognathusModel extends GeoModel<CompsognathusEntity> {
private static final Map<CompsognathusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(CompsognathusVariant.class), map -> {
map.put(CompsognathusVariant.MALE, Constants.rl("textures/entity/compsognathus.png"));
map.put(CompsognathusVariant.FEMALE, Constants.rl("textures/entity/compsognathus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(CompsognathusEntity animatable) {
return Constants.rl("geo/compsognathus.geo.json");
}
@Override
public ResourceLocation getTextureResource(CompsognathusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(CompsognathusEntity animatable) {
return Constants.rl("animations/compsognathus.animation.json");
}
@Override
public void setCustomAnimations(CompsognathusEntity entity, long id, AnimationState<CompsognathusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3", "Tail4" };
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("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.CompsognathusEntity;
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 CompsognathusRenderer extends GeoEntityRenderer<CompsognathusEntity> {
private final float animalScale = 0.35F;
public CompsognathusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new CompsognathusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, CompsognathusEntity 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 CompsognathusVariant {
MALE(0),
FEMALE(1);
private static final CompsognathusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(CompsognathusVariant::getId)).toArray(CompsognathusVariant[]::new);
private final int id;
CompsognathusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static CompsognathusVariant 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.ConcavenatorEntity;
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 ConcavenatorModel extends GeoModel<ConcavenatorEntity> {
private static final Map<ConcavenatorVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(ConcavenatorVariant.class), map -> {
map.put(ConcavenatorVariant.MALE, Constants.rl("textures/entity/concavenator.png"));
map.put(ConcavenatorVariant.FEMALE, Constants.rl("textures/entity/concavenator_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(ConcavenatorEntity animatable) {
return Constants.rl("geo/concavenator.geo.json");
}
@Override
public ResourceLocation getTextureResource(ConcavenatorEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(ConcavenatorEntity animatable) {
return Constants.rl("animations/concavenator.animation.json");
}
@Override
public void setCustomAnimations(ConcavenatorEntity entity, long id, AnimationState<ConcavenatorEntity> 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.ConcavenatorEntity;
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 ConcavenatorRenderer extends GeoEntityRenderer<ConcavenatorEntity> {
private final float animalScale = 1.2F;
public ConcavenatorRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new ConcavenatorModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, ConcavenatorEntity 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 ConcavenatorVariant {
MALE(0),
FEMALE(1);
private static final ConcavenatorVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(ConcavenatorVariant::getId)).toArray(ConcavenatorVariant[]::new);
private final int id;
ConcavenatorVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static ConcavenatorVariant 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.CorythosaurusEntity;
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 CorythosaurusModel extends GeoModel<CorythosaurusEntity> {
private static final Map<CorythosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(CorythosaurusVariant.class), map -> {
map.put(CorythosaurusVariant.MALE, Constants.rl("textures/entity/corythosaurus.png"));
map.put(CorythosaurusVariant.FEMALE, Constants.rl("textures/entity/corythosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(CorythosaurusEntity animatable) {
return Constants.rl("geo/corythosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(CorythosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(CorythosaurusEntity animatable) {
return Constants.rl("animations/corythosaurus.animation.json");
}
@Override
public void setCustomAnimations(CorythosaurusEntity entity, long id, AnimationState<CorythosaurusEntity> 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");
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.CorythosaurusEntity;
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 CorythosaurusRenderer extends GeoEntityRenderer<CorythosaurusEntity> {
private final float animalScale = 1.25F;
public CorythosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new CorythosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, CorythosaurusEntity 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 CorythosaurusVariant {
MALE(0),
FEMALE(1);
private static final CorythosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(CorythosaurusVariant::getId)).toArray(CorythosaurusVariant[]::new);
private final int id;
CorythosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static CorythosaurusVariant 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.DeinonychusEntity;
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 DeinonychusModel extends GeoModel<DeinonychusEntity> {
private static final Map<DeinonychusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(DeinonychusVariant.class), map -> {
map.put(DeinonychusVariant.MALE, Constants.rl("textures/entity/deinonychus.png"));
map.put(DeinonychusVariant.FEMALE, Constants.rl("textures/entity/deinonychus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(DeinonychusEntity animatable) {
return Constants.rl("geo/deinonychus.geo.json");
}
@Override
public ResourceLocation getTextureResource(DeinonychusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(DeinonychusEntity animatable) {
return Constants.rl("animations/deinonychus.animation.json");
}
@Override
public void setCustomAnimations(DeinonychusEntity entity, long id, AnimationState<DeinonychusEntity> 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("body1");
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.DeinonychusEntity;
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 DeinonychusRenderer extends GeoEntityRenderer<DeinonychusEntity> {
private final float animalScale = 1.0F;
public DeinonychusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new DeinonychusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, DeinonychusEntity 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 DeinonychusVariant {
MALE(0),
FEMALE(1);
private static final DeinonychusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(DeinonychusVariant::getId)).toArray(DeinonychusVariant[]::new);
private final int id;
DeinonychusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static DeinonychusVariant 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.DilophosaurusEntity;
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 DilophosaurusModel extends GeoModel<DilophosaurusEntity> {
private static final Map<DilophosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(DilophosaurusVariant.class), map -> {
map.put(DilophosaurusVariant.MALE, Constants.rl("textures/entity/dilophosaurus.png"));
map.put(DilophosaurusVariant.FEMALE, Constants.rl("textures/entity/dilophosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(DilophosaurusEntity animatable) {
return Constants.rl("geo/dilophosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(DilophosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(DilophosaurusEntity animatable) {
return Constants.rl("animations/dilophosaurus.animation.json");
}
@Override
public void setCustomAnimations(DilophosaurusEntity entity, long id, AnimationState<DilophosaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "TailBASE", "Tail2", "Tail3", "Tail4", "Tail5", "Tail6", "Tail7" };
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, 0.16f };
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.DilophosaurusEntity;
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 DilophosaurusRenderer extends GeoEntityRenderer<DilophosaurusEntity> {
private final float animalScale = 1.0F;
public DilophosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new DilophosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, DilophosaurusEntity 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 DilophosaurusVariant {
MALE(0),
FEMALE(1);
private static final DilophosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(DilophosaurusVariant::getId)).toArray(DilophosaurusVariant[]::new);
private final int id;
DilophosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static DilophosaurusVariant 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.DimorphodonEntity;
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 DimorphodonModel extends GeoModel<DimorphodonEntity> {
private static final Map<DimorphodonVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(DimorphodonVariant.class), map -> {
map.put(DimorphodonVariant.MALE, Constants.rl("textures/entity/dimorphodon.png"));
map.put(DimorphodonVariant.FEMALE, Constants.rl("textures/entity/dimorphodon_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(DimorphodonEntity animatable) {
return Constants.rl("geo/dimorphodon.geo.json");
}
@Override
public ResourceLocation getTextureResource(DimorphodonEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(DimorphodonEntity animatable) {
return Constants.rl("animations/dimorphodon.animation.json");
}
@Override
public void setCustomAnimations(DimorphodonEntity entity, long id, AnimationState<DimorphodonEntity> 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("Neck");
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.DimorphodonEntity;
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 DimorphodonRenderer extends GeoEntityRenderer<DimorphodonEntity> {
private final float animalScale = 0.4F;
public DimorphodonRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new DimorphodonModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, DimorphodonEntity 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 DimorphodonVariant {
MALE(0),
FEMALE(1);
private static final DimorphodonVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(DimorphodonVariant::getId)).toArray(DimorphodonVariant[]::new);
private final int id;
DimorphodonVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static DimorphodonVariant 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.DiplodocusEntity;
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 DiplodocusModel extends GeoModel<DiplodocusEntity> {
private static final Map<DiplodocusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(DiplodocusVariant.class), map -> {
map.put(DiplodocusVariant.MALE, Constants.rl("textures/entity/diplodocus.png"));
map.put(DiplodocusVariant.FEMALE, Constants.rl("textures/entity/diplodocus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(DiplodocusEntity animatable) {
return Constants.rl("geo/diplodocus.geo.json");
}
@Override
public ResourceLocation getTextureResource(DiplodocusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(DiplodocusEntity animatable) {
return Constants.rl("animations/diplodocus.animation.json");
}
@Override
public void setCustomAnimations(DiplodocusEntity entity, long id, AnimationState<DiplodocusEntity> 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.DiplodocusEntity;
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 DiplodocusRenderer extends GeoEntityRenderer<DiplodocusEntity> {
private final float animalScale = 2.5F;
public DiplodocusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new DiplodocusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, DiplodocusEntity 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 DiplodocusVariant {
MALE(0),
FEMALE(1);
private static final DiplodocusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(DiplodocusVariant::getId)).toArray(DiplodocusVariant[]::new);
private final int id;
DiplodocusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static DiplodocusVariant 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.DistortusRexEntity;
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 DistortusRexModel extends GeoModel<DistortusRexEntity> {
private static final Map<DistortusRexVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(DistortusRexVariant.class), map -> {
map.put(DistortusRexVariant.MALE, Constants.rl("textures/entity/distortus_rex.png"));
map.put(DistortusRexVariant.FEMALE, Constants.rl("textures/entity/distortus_rex_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(DistortusRexEntity animatable) {
return Constants.rl("geo/distortus_rex.geo.json");
}
@Override
public ResourceLocation getTextureResource(DistortusRexEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(DistortusRexEntity animatable) {
return Constants.rl("animations/distortus_rex.animation.json");
}
@Override
public void setCustomAnimations(DistortusRexEntity entity, long id, AnimationState<DistortusRexEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3", "Tail4", "Tail5", "Tail6", "Tail7" };
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, 0.16f };
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("Chest");
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.DistortusRexEntity;
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 DistortusRexRenderer extends GeoEntityRenderer<DistortusRexEntity> {
private final float animalScale = 2.75F;
public DistortusRexRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new DistortusRexModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, DistortusRexEntity 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 DistortusRexVariant {
MALE(0),
FEMALE(1);
private static final DistortusRexVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(DistortusRexVariant::getId)).toArray(DistortusRexVariant[]::new);
private final int id;
DistortusRexVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static DistortusRexVariant 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.DryosaurusEntity;
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 DryosaurusModel extends GeoModel<DryosaurusEntity> {
private static final Map<DryosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(DryosaurusVariant.class), map -> {
map.put(DryosaurusVariant.MALE, Constants.rl("textures/entity/dryosaurus.png"));
map.put(DryosaurusVariant.FEMALE, Constants.rl("textures/entity/dryosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(DryosaurusEntity animatable) {
return Constants.rl("geo/dryosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(DryosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(DryosaurusEntity animatable) {
return Constants.rl("animations/dryosaurus.animation.json");
}
@Override
public void setCustomAnimations(DryosaurusEntity entity, long id, AnimationState<DryosaurusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tail1", "Tail2", "Tail3", "Tail4", "Tail5", "Tail6", "Tail7" };
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, 0.16f };
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("Body4");
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.DryosaurusEntity;
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 DryosaurusRenderer extends GeoEntityRenderer<DryosaurusEntity> {
private final float animalScale = 0.35F;
public DryosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new DryosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, DryosaurusEntity 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 DryosaurusVariant {
MALE(0),
FEMALE(1);
private static final DryosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(DryosaurusVariant::getId)).toArray(DryosaurusVariant[]::new);
private final int id;
DryosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static DryosaurusVariant 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.EdmontosaurusEntity;
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 EdmontosaurusModel extends GeoModel<EdmontosaurusEntity> {
private static final Map<EdmontosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(EdmontosaurusVariant.class), map -> {
map.put(EdmontosaurusVariant.MALE, Constants.rl("textures/entity/edmontosaurus.png"));
map.put(EdmontosaurusVariant.FEMALE, Constants.rl("textures/entity/edmontosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(EdmontosaurusEntity animatable) {
return Constants.rl("geo/edmontosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(EdmontosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(EdmontosaurusEntity animatable) {
return Constants.rl("animations/edmontosaurus.animation.json");
}
@Override
public void setCustomAnimations(EdmontosaurusEntity entity, long id, AnimationState<EdmontosaurusEntity> 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");
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.EdmontosaurusEntity;
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 EdmontosaurusRenderer extends GeoEntityRenderer<EdmontosaurusEntity> {
private final float animalScale = 1.5F;
public EdmontosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new EdmontosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, EdmontosaurusEntity 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 EdmontosaurusVariant {
MALE(0),
FEMALE(1);
private static final EdmontosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(EdmontosaurusVariant::getId)).toArray(EdmontosaurusVariant[]::new);
private final int id;
EdmontosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static EdmontosaurusVariant 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.FDuckEntity;
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 FDuckModel extends GeoModel<FDuckEntity> {
private static final Map<FDuckVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(FDuckVariant.class), map -> {
map.put(FDuckVariant.MALE, Constants.rl("textures/entity/fduck.png"));
map.put(FDuckVariant.FEMALE, Constants.rl("textures/entity/fduck_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(FDuckEntity animatable) {
return Constants.rl("geo/fduck.geo.json");
}
@Override
public ResourceLocation getTextureResource(FDuckEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(FDuckEntity animatable) {
return Constants.rl("animations/fduck.animation.json");
}
@Override
public void setCustomAnimations(FDuckEntity entity, long id, AnimationState<FDuckEntity> 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("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.FDuckEntity;
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 FDuckRenderer extends GeoEntityRenderer<FDuckEntity> {
private final float animalScale = 1.0F;
public FDuckRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new FDuckModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, FDuckEntity 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 FDuckVariant {
MALE(0),
FEMALE(1);
private static final FDuckVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(FDuckVariant::getId)).toArray(FDuckVariant[]::new);
private final int id;
FDuckVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static FDuckVariant 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.GallimimusEntity;
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 GallimimusModel extends GeoModel<GallimimusEntity> {
private static final Map<GallimimusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(GallimimusVariant.class), map -> {
map.put(GallimimusVariant.MALE, Constants.rl("textures/entity/gallimimus.png"));
map.put(GallimimusVariant.FEMALE, Constants.rl("textures/entity/gallimimus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(GallimimusEntity animatable) {
return Constants.rl("geo/gallimimus.geo.json");
}
@Override
public ResourceLocation getTextureResource(GallimimusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(GallimimusEntity animatable) {
return Constants.rl("animations/gallimimus.animation.json");
}
@Override
public void setCustomAnimations(GallimimusEntity entity, long id, AnimationState<GallimimusEntity> state) {
super.setCustomAnimations(entity, id, state);
String[] tailBones = { "Tailpart1", "Tailpart2", "Tailpart3", "Tailpart4", "Tailpart5", "Tailpart6" };
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("Neckpart1");
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.GallimimusEntity;
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 GallimimusRenderer extends GeoEntityRenderer<GallimimusEntity> {
private final float animalScale = 1.0F;
public GallimimusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new GallimimusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, GallimimusEntity 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 GallimimusVariant {
MALE(0),
FEMALE(1);
private static final GallimimusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(GallimimusVariant::getId)).toArray(GallimimusVariant[]::new);
private final int id;
GallimimusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static GallimimusVariant byId(int id) {
return BY_ID[id % BY_ID.length];
}
}
@@ -0,0 +1,114 @@
package net.cmr.jurassicrevived.entity.client;
import com.google.common.collect.Maps;
import net.cmr.jurassicrevived.Constants;
import net.cmr.jurassicrevived.entity.custom.GeosternbergiaEntity;
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 GeosternbergiaModel extends GeoModel<GeosternbergiaEntity> {
private static final Map<GeosternbergiaVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(GeosternbergiaVariant.class), map -> {
map.put(GeosternbergiaVariant.MALE, Constants.rl("textures/entity/geosternbergia.png"));
map.put(GeosternbergiaVariant.FEMALE, Constants.rl("textures/entity/geosternbergia_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(GeosternbergiaEntity animatable) {
return Constants.rl("geo/geosternbergia.geo.json");
}
@Override
public ResourceLocation getTextureResource(GeosternbergiaEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(GeosternbergiaEntity animatable) {
//? if >1.20.1 {
/*return ResourceLocation.fromNamespaceAndPath(Constants.MOD_ID, "animations/geosternbergia.animation.json");
*///?} else {
return new ResourceLocation(Constants.MOD_ID, "animations/geosternbergia.animation.json");
//?}
}
public void setCustomAnimations(GeosternbergiaEntity entity, long id, AnimationState<GeosternbergiaEntity> 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("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.GeosternbergiaEntity;
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 GeosternbergiaRenderer extends GeoEntityRenderer<GeosternbergiaEntity> {
private final float animalScale = 1.0F;
public GeosternbergiaRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new GeosternbergiaModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, GeosternbergiaEntity 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 GeosternbergiaVariant {
MALE(0),
FEMALE(1);
private static final GeosternbergiaVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(GeosternbergiaVariant::getId)).toArray(GeosternbergiaVariant[]::new);
private final int id;
GeosternbergiaVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static GeosternbergiaVariant 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.GiganotosaurusEntity;
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 GiganotosaurusModel extends GeoModel<GiganotosaurusEntity> {
private static final Map<GiganotosaurusVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(GiganotosaurusVariant.class), map -> {
map.put(GiganotosaurusVariant.MALE, Constants.rl("textures/entity/giganotosaurus.png"));
map.put(GiganotosaurusVariant.FEMALE, Constants.rl("textures/entity/giganotosaurus_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(GiganotosaurusEntity animatable) {
return Constants.rl("geo/giganotosaurus.geo.json");
}
@Override
public ResourceLocation getTextureResource(GiganotosaurusEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(GiganotosaurusEntity animatable) {
return Constants.rl("animations/giganotosaurus.animation.json");
}
@Override
public void setCustomAnimations(GiganotosaurusEntity entity, long id, AnimationState<GiganotosaurusEntity> 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.GiganotosaurusEntity;
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 GiganotosaurusRenderer extends GeoEntityRenderer<GiganotosaurusEntity> {
private final float animalScale = 2.1F;
public GiganotosaurusRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new GiganotosaurusModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, GiganotosaurusEntity 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 GiganotosaurusVariant {
MALE(0),
FEMALE(1);
private static final GiganotosaurusVariant[] BY_ID = Arrays.stream(values()).sorted(
Comparator.comparingInt(GiganotosaurusVariant::getId)).toArray(GiganotosaurusVariant[]::new);
private final int id;
GiganotosaurusVariant(int id) {
this.id = id;
}
public int getId() {
return id;
}
public static GiganotosaurusVariant 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.GuanlongEntity;
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 GuanlongModel extends GeoModel<GuanlongEntity> {
private static final Map<GuanlongVariant, ResourceLocation> LOCATION_BY_VARIANT =
Util.make(Maps.newEnumMap(GuanlongVariant.class), map -> {
map.put(GuanlongVariant.MALE, Constants.rl("textures/entity/guanlong.png"));
map.put(GuanlongVariant.FEMALE, Constants.rl("textures/entity/guanlong_female.png"));
});
// Model-local "currently applied" offsets; cleared before each entity render
private float[] appliedYaw = null;
private float[] appliedRoll = null;
@Override
public ResourceLocation getModelResource(GuanlongEntity animatable) {
return Constants.rl("geo/guanlong.geo.json");
}
@Override
public ResourceLocation getTextureResource(GuanlongEntity animatable) {
return LOCATION_BY_VARIANT.get(animatable.getVariant());
}
@Override
public ResourceLocation getAnimationResource(GuanlongEntity animatable) {
return Constants.rl("animations/guanlong.animation.json");
}
@Override
public void setCustomAnimations(GuanlongEntity entity, long id, AnimationState<GuanlongEntity> 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.GuanlongEntity;
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 GuanlongRenderer extends GeoEntityRenderer<GuanlongEntity> {
private final float animalScale = 0.75F;
public GuanlongRenderer(EntityRendererProvider.Context renderManager) {
super(renderManager, new GuanlongModel());
}
@Override
public void scaleModelForRender(float widthScale, float heightScale, PoseStack poseStack, GuanlongEntity 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);
}
}
}

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