diff --git a/src/main/java/fathertoast/specialmobs/client/ClientRegister.java b/src/main/java/fathertoast/specialmobs/client/ClientRegister.java index d713382..8a29c55 100644 --- a/src/main/java/fathertoast/specialmobs/client/ClientRegister.java +++ b/src/main/java/fathertoast/specialmobs/client/ClientRegister.java @@ -8,6 +8,7 @@ import fathertoast.specialmobs.client.renderer.entity.SpecialZombieRenderer; import fathertoast.specialmobs.client.renderer.entity.*; import fathertoast.specialmobs.common.bestiary.MobFamily; import fathertoast.specialmobs.common.core.SpecialMobs; +import fathertoast.specialmobs.common.entity.skeleton.NinjaSkeletonEntity; import mcp.MethodsReturnNonnullByDefault; import net.minecraft.entity.LivingEntity; import net.minecraftforge.api.distmarker.Dist; @@ -28,7 +29,8 @@ public class ClientRegister { public static void onClientSetup( FMLClientSetupEvent event ) { registerEntityRenderers(); } - + + @SuppressWarnings("all") private static void registerEntityRenderers() { // Family-based renderers registerFamilyRenderers( MobFamily.CREEPER, SpecialCreeperRenderer::new ); @@ -40,11 +42,25 @@ public class ClientRegister { registerFamilyRenderers( MobFamily.CAVE_SPIDER, SpecialSpiderRenderer::new ); registerFamilyRenderers( MobFamily.SILVERFISH, SpecialSilverfishRenderer::new ); registerFamilyRenderers( MobFamily.ENDERMAN, SpecialEndermanRenderer::new ); + + // Custom renderers + registerRenderer( NinjaSkeletonEntity.class, NinjaSkeletonRenderer::new); } - private static void registerFamilyRenderers( MobFamily family, IRenderFactory renderFactory ) { + private static void registerFamilyRenderers(MobFamily family, IRenderFactory renderFactory ) { RenderingRegistry.registerEntityRenderingHandler( family.vanillaReplacement.entityType.get(), renderFactory ); for( MobFamily.Species species : family.variants ) - RenderingRegistry.registerEntityRenderingHandler( species.entityType.get(), renderFactory ); + if ( !species.hasCustomRenderer ) + RenderingRegistry.registerEntityRenderingHandler(species.entityType.get(), renderFactory); + } + + private static void registerRenderer(Class entityClass, IRenderFactory renderFactory) { + MobFamily.Species species = MobFamily.findSpecies(entityClass); + + if (species == null) { + SpecialMobs.LOG.error("Could not register renderer for entity class {}, as no belonging mob species was found.", entityClass.getSimpleName()); + return; + } + RenderingRegistry.registerEntityRenderingHandler(species.entityType.get(), renderFactory); } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/NinjaSkeletonRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/NinjaSkeletonRenderer.java new file mode 100644 index 0000000..220f051 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/NinjaSkeletonRenderer.java @@ -0,0 +1,68 @@ +package fathertoast.specialmobs.client.renderer.entity; + +import com.mojang.blaze3d.matrix.MatrixStack; +import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobEyesLayer; +import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobOverlayLayer; +import fathertoast.specialmobs.common.entity.ISpecialMob; +import fathertoast.specialmobs.common.entity.ai.INinja; +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.client.Minecraft; +import net.minecraft.client.renderer.BlockRendererDispatcher; +import net.minecraft.client.renderer.IRenderTypeBuffer; +import net.minecraft.client.renderer.entity.EntityRendererManager; +import net.minecraft.client.renderer.entity.SkeletonRenderer; +import net.minecraft.client.renderer.entity.model.SkeletonModel; +import net.minecraft.client.renderer.texture.OverlayTexture; +import net.minecraft.entity.monster.AbstractSkeletonEntity; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.client.model.data.EmptyModelData; + +public class NinjaSkeletonRenderer extends SkeletonRenderer { + + private final float baseShadowRadius; + private BlockRendererDispatcher blockRenderer; + + public NinjaSkeletonRenderer(EntityRendererManager rendererManager ) { + super( rendererManager ); + baseShadowRadius = shadowRadius = shadowStrength = 0.0F; + this.blockRenderer = Minecraft.getInstance().getBlockRenderer(); + addLayer( new SpecialMobEyesLayer<>( this ) ); + addLayer( new SpecialMobOverlayLayer<>( this, new SkeletonModel<>( ) ) ); + } + + @Override + public void render(AbstractSkeletonEntity skeletonEntity, float f1, float f2, MatrixStack matrixStack, IRenderTypeBuffer buffer, int i) { + BlockState disguiseState = ((INinja)skeletonEntity).getDisguiseBlock(); + + if (disguiseState == null) { + super.render(skeletonEntity, f1, f2, matrixStack, buffer, i); + } + else { + renderBlockDisguise((AbstractSkeletonEntity & INinja) skeletonEntity, disguiseState, f1, f2, matrixStack, buffer, i); + } + } + + private void renderBlockDisguise(T ninja, BlockState state, float f1, float f2, MatrixStack matrixStack, IRenderTypeBuffer buffer, int i) { + matrixStack.pushPose(); + + matrixStack.translate(-0.5D, 0.0D, -0.5D); + this.blockRenderer.renderBlock(Blocks.LECTERN.defaultBlockState(), matrixStack, buffer, i, OverlayTexture.NO_OVERLAY, EmptyModelData.INSTANCE); + + matrixStack.popPose(); + } + + @Override + public ResourceLocation getTextureLocation(AbstractSkeletonEntity entity ) { + return ((ISpecialMob) entity).getSpecialData().getTexture(); + } + + @Override + protected void scale(AbstractSkeletonEntity entity, MatrixStack matrixStack, float partialTick ) { + super.scale( entity, matrixStack, partialTick ); + + final float scale = ((ISpecialMob) entity).getSpecialData().getRenderScale(); + shadowRadius = baseShadowRadius * scale; + matrixStack.scale( scale, scale, scale ); + } +} diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java index 4b20497..3c1e3b1 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java @@ -2,10 +2,12 @@ package fathertoast.specialmobs.common.bestiary; import fathertoast.specialmobs.common.core.register.SMEntities; import fathertoast.specialmobs.common.core.register.SMItems; +import fathertoast.specialmobs.common.entity.ISpecialMob; import fathertoast.specialmobs.common.util.AnnotationHelper; import fathertoast.specialmobs.common.util.References; import mcp.MethodsReturnNonnullByDefault; import net.minecraft.block.Block; +import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.monster.*; @@ -54,11 +56,11 @@ public class MobFamily { public static final MobFamily SKELETON = new MobFamily<>( "Skeleton", "skeletons", 0xC1C1C1, new EntityType[] { EntityType.SKELETON, EntityType.STRAY }, - "Brute", "Fire", "Gatling", "Giant", "Knight", /*"Ninja",*/ "Poison", "Sniper", /*"Spitfire",*/ "Stray" + "Brute", "Fire", "Gatling", "Giant", "Knight", "Ninja", "Poison", "Sniper", /*"Spitfire",*/ "Stray" ); public static final MobFamily WITHER_SKELETON = new MobFamily<>( "WitherSkeleton", "wither skeletons", 0x141414, new EntityType[] { EntityType.WITHER_SKELETON }, - "Brute", "Gatling", "Giant", "Knight", /*"Ninja",*/ "Sniper"//, "Spitfire" + "Brute", "Gatling", "Giant", "Knight", "Ninja", "Sniper"//, "Spitfire" ); public static final MobFamily SLIME = new MobFamily<>( @@ -129,6 +131,16 @@ public class MobFamily { /** @return A list of all species. */ public static List> getAllSpecies() { return SPECIES_LIST; } + + @SuppressWarnings("unchecked") + @Nullable + public static Species findSpecies(Class entityClass) { + for (Species species : getAllSpecies()) { + if (species.entityClass == entityClass) + return (Species) species; + } + return null; + } /** @return The family of mobs that can replace the passed entity; returns null if the entity is not replaceable. */ @Nullable @@ -237,11 +249,14 @@ public class MobFamily { public final RegistryObject> entityType; /** This species's spawn egg item, wrapped in its registry object. */ public final RegistryObject spawnEgg; + + /** Whether this species has a custom renderer. */ + public final boolean hasCustomRenderer; /** Constructs a new mob species. For vanilla replacements, the variant name is null. */ private Species( MobFamily parentFamily, String packageRoot, @Nullable String variantName ) { final boolean vanillaReplacement = variantName == null; - + family = parentFamily; specialVariantName = variantName; name = vanillaReplacement ? parentFamily.name : variantName + parentFamily.name; @@ -257,9 +272,10 @@ public class MobFamily { // Initialize deferred registry objects entityType = SMEntities.register( name.toLowerCase( Locale.ROOT ), entityTypeBuilder ); spawnEgg = SMItems.registerSpawnEgg( entityType, parentFamily.eggBaseColor, bestiaryInfo.eggSpotsColor ); + hasCustomRenderer = AnnotationHelper.hasCustomRenderer( entityClass ); AnnotationHelper.injectEntityTypeHolder( this ); } - + /** Finds the entity class based on a standard format. */ private Class findClass( String format, String packageRoot ) { try { diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/SpecialMob.java b/src/main/java/fathertoast/specialmobs/common/bestiary/SpecialMob.java index 13bdb15..74868bf 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/SpecialMob.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/SpecialMob.java @@ -36,7 +36,9 @@ public @interface SpecialMob { */ @Retention( RetentionPolicy.RUNTIME ) @Target( ElementType.CONSTRUCTOR ) - @interface Constructor { } + @interface Constructor { + boolean hasCustomRenderer() default false; + } /** * REQUIRED. This is called during registration to collect static properties of the mob needed for the bestiary diff --git a/src/main/java/fathertoast/specialmobs/common/entity/ai/INinja.java b/src/main/java/fathertoast/specialmobs/common/entity/ai/INinja.java index 529149a..fa97537 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/ai/INinja.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/ai/INinja.java @@ -11,10 +11,10 @@ import javax.annotation.Nullable; public interface INinja { /** @return Whether this ninja is currently immobile. */ - boolean isImmobile(); + boolean isHiding(); /** Sets this ninja's immovable state. When activated, the entity is 'snapped' to the nearest block position. */ - void setImmobile( boolean value ); + void setHiding( boolean value ); /** @return The block being hidden (rendered) as, or null if not hiding. */ @Nullable diff --git a/src/main/java/fathertoast/specialmobs/common/entity/skeleton/NinjaSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/NinjaSkeletonEntity.java index 1813c6d..08d960a 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/skeleton/NinjaSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/NinjaSkeletonEntity.java @@ -61,7 +61,7 @@ public class NinjaSkeletonEntity extends _SpecialSkeletonEntity implements INinj Blocks.INFESTED_CRACKED_STONE_BRICKS, Blocks.INFESTED_MOSSY_STONE_BRICKS, Blocks.INFESTED_CHISELED_STONE_BRICKS ); } - @SpecialMob.Constructor + @SpecialMob.Constructor(hasCustomRenderer = true) public NinjaSkeletonEntity( EntityType entityType, World world ) { super( entityType, world ); xpReward += 2; @@ -120,6 +120,8 @@ public class NinjaSkeletonEntity extends _SpecialSkeletonEntity implements INinj if( !level.isClientSide() ) { if( canHide ) { //EntityAINinja.startHiding( this ); TODO + this.setHiding(true); + this.setDisguiseBlock(Blocks.DIRT.defaultBlockState()); } else if( onGround && getDisguiseBlock() == null && (getTarget() == null || getTarget() instanceof PlayerEntity && ((PlayerEntity) getTarget()).isCreative()) ) { @@ -132,22 +134,24 @@ public class NinjaSkeletonEntity extends _SpecialSkeletonEntity implements INinj // // Moves this entity. // @Override TODO // public void move( MoverType type, double x, double y, double z ) { - // if( isImmobile() && type != MoverType.PISTON ) { + // if( isHiding() && type != MoverType.PISTON ) { // motionY = 0.0; // } // else { // super.move( type, x, y, z ); // } // } - // - // // Returns true if this entity should push and be pushed by other entities when colliding. - // @Override - // public boolean canBePushed() { return !isImmobile(); } + + /** Returns true if this entity should push and be pushed by other entities when colliding. */ + @Override + public boolean isPushable() { + return super.isPushable() && !isHiding(); + } /** Sets this entity on fire for a specific duration. */ @Override public void setRemainingFireTicks( int ticks ) { - if( !isImmobile() ) super.setRemainingFireTicks( ticks ); + if( !isHiding() ) super.setRemainingFireTicks( ticks ); } /** Reveals this ninja and sets its target so that it doesn't immediately re-disguise itself. */ @@ -186,12 +190,12 @@ public class NinjaSkeletonEntity extends _SpecialSkeletonEntity implements INinj /** @return Whether this ninja is currently immobile. */ @Override - public boolean isImmobile() { return getEntityData().get( IS_HIDING ); } + public boolean isHiding() { return getEntityData().get( IS_HIDING ); } /** Sets this ninja's immovable state. When activated, the entity is 'snapped' to the nearest block position. */ @Override - public void setImmobile( boolean value ) { - if( value != isImmobile() ) { + public void setHiding( boolean value ) { + if( value != isHiding() ) { getEntityData().set( IS_HIDING, value ); if( value ) { clearFire(); diff --git a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/NinjaWitherSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/NinjaWitherSkeletonEntity.java index e2b72bf..6650dfe 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/NinjaWitherSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/NinjaWitherSkeletonEntity.java @@ -61,7 +61,7 @@ public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity impl Blocks.INFESTED_CRACKED_STONE_BRICKS, Blocks.INFESTED_MOSSY_STONE_BRICKS, Blocks.INFESTED_CHISELED_STONE_BRICKS ); } - @SpecialMob.Constructor + @SpecialMob.Constructor(hasCustomRenderer = true) public NinjaWitherSkeletonEntity( EntityType entityType, World world ) { super( entityType, world ); xpReward += 2; @@ -132,7 +132,7 @@ public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity impl // // Moves this entity. // @Override TODO // public void move( MoverType type, double x, double y, double z ) { - // if( isImmobile() && type != MoverType.PISTON ) { + // if( isHiding() && type != MoverType.PISTON ) { // motionY = 0.0; // } // else { @@ -142,12 +142,12 @@ public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity impl // // // Returns true if this entity should push and be pushed by other entities when colliding. // @Override - // public boolean canBePushed() { return !isImmobile(); } + // public boolean canBePushed() { return !isHiding(); } /** Sets this entity on fire for a specific duration. */ @Override public void setRemainingFireTicks( int ticks ) { - if( !isImmobile() ) super.setRemainingFireTicks( ticks ); + if( !isHiding() ) super.setRemainingFireTicks( ticks ); } /** Reveals this ninja and sets its target so that it doesn't immediately re-disguise itself. */ @@ -157,7 +157,7 @@ public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity impl } private static final ResourceLocation[] TEXTURES = { - new ResourceLocation( "textures/entity/skeleton/skeleton.png" ), + new ResourceLocation( "textures/entity/skeleton/wither_skeleton.png" ), null, GET_TEXTURE_PATH( "ninja_overlay" ) }; @@ -186,12 +186,12 @@ public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity impl /** @return Whether this ninja is currently immobile. */ @Override - public boolean isImmobile() { return getEntityData().get( IS_HIDING ); } + public boolean isHiding() { return getEntityData().get( IS_HIDING ); } /** Sets this ninja's immovable state. When activated, the entity is 'snapped' to the nearest block position. */ @Override - public void setImmobile( boolean value ) { - if( value != isImmobile() ) { + public void setHiding( boolean value ) { + if( value != isHiding() ) { getEntityData().set( IS_HIDING, value ); if( value ) { clearFire(); diff --git a/src/main/java/fathertoast/specialmobs/common/util/AnnotationHelper.java b/src/main/java/fathertoast/specialmobs/common/util/AnnotationHelper.java index 179e71b..3d18990 100644 --- a/src/main/java/fathertoast/specialmobs/common/util/AnnotationHelper.java +++ b/src/main/java/fathertoast/specialmobs/common/util/AnnotationHelper.java @@ -104,6 +104,19 @@ public final class AnnotationHelper { throw new RuntimeException( "Entity class for " + species.name + " has invalid loot table builder method", ex ); } } + + /** + * Returns the boolean value for custom rendering from the target class' Constructor annotation. + * {@link SpecialMob.Constructor#hasCustomRenderer()} + */ + public static boolean hasCustomRenderer( Class entityClass ) { + if (entityClass.isAnnotationPresent(SpecialMob.Constructor.class)) { + return entityClass.getDeclaredAnnotation(SpecialMob.Constructor.class).hasCustomRenderer(); + } + else { + return false; + } + } //--------------- RAW ANNOTATION METHODS ----------------