diff --git a/src/main/java/fathertoast/specialmobs/client/ClientRegister.java b/src/main/java/fathertoast/specialmobs/client/ClientRegister.java index e01d6c9..daaffa9 100644 --- a/src/main/java/fathertoast/specialmobs/client/ClientRegister.java +++ b/src/main/java/fathertoast/specialmobs/client/ClientRegister.java @@ -1,10 +1,5 @@ package fathertoast.specialmobs.client; -import fathertoast.specialmobs.client.renderer.entity.SpecialCreeperRenderer; -import fathertoast.specialmobs.client.renderer.entity.SpecialSilverfishRenderer; -import fathertoast.specialmobs.client.renderer.entity.SpecialSkeletonRenderer; -import fathertoast.specialmobs.client.renderer.entity.SpecialSpiderRenderer; -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; @@ -43,6 +38,7 @@ public class ClientRegister { registerFamilyRenderers( MobFamily.CAVE_SPIDER, SpecialSpiderRenderer::new ); registerFamilyRenderers( MobFamily.SILVERFISH, SpecialSilverfishRenderer::new ); registerFamilyRenderers( MobFamily.ENDERMAN, SpecialEndermanRenderer::new ); + //registerFamilyRenderers( MobFamily.WITCH, SpecialWitchRenderer::new ); // Custom renderers registerRenderer( NinjaSkeletonEntity.class, NinjaSkeletonRenderer::new ); @@ -52,17 +48,14 @@ public class ClientRegister { private static void registerFamilyRenderers( MobFamily family, IRenderFactory renderFactory ) { RenderingRegistry.registerEntityRenderingHandler( family.vanillaReplacement.entityType.get(), renderFactory ); for( MobFamily.Species species : family.variants ) - //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; - } + if( species == null ) + throw new IllegalArgumentException( "Could not register renderer for entity class '" + entityClass.getSimpleName() + "', as no belonging mob species was found." ); RenderingRegistry.registerEntityRenderingHandler( species.entityType.get(), renderFactory ); } } \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialWitchRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialWitchRenderer.java new file mode 100644 index 0000000..87b2cd0 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialWitchRenderer.java @@ -0,0 +1,46 @@ +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 mcp.MethodsReturnNonnullByDefault; +import net.minecraft.client.renderer.entity.EntityRendererManager; +import net.minecraft.client.renderer.entity.WitchRenderer; +import net.minecraft.client.renderer.entity.model.WitchModel; +import net.minecraft.entity.monster.WitchEntity; +import net.minecraft.util.ResourceLocation; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@OnlyIn( Dist.CLIENT ) +public class SpecialWitchRenderer extends WitchRenderer { + + private final float baseShadowRadius; + + public SpecialWitchRenderer( EntityRendererManager rendererManager ) { + super( rendererManager ); + baseShadowRadius = shadowRadius; + addLayer( new SpecialMobEyesLayer<>( this ) ); + // Note: Overlay scaling only applies to base villager model + addLayer( new SpecialMobOverlayLayer<>( this, new WitchModel<>( 0.25F ) ) ); + } + + @Override + public ResourceLocation getTextureLocation( WitchEntity entity ) { + return ((ISpecialMob) entity).getSpecialData().getTexture(); + } + + @Override + protected void scale( WitchEntity 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 ); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java index ff0bb36..7f920d8 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java @@ -93,7 +93,7 @@ public class MobFamily { // public static final MobFamily WITCH = new MobFamily<>( // "Witch", "witches", 0x340000, new EntityType[] { EntityType.WITCH }, - // "Domination", "Shadows", "Undead", "Wilds", "Wind"//Note - should wind be able to walk on water? + // "Domination"//, "Shadows", "Undead", "Wilds", "Wind"//Note - should wind be able to walk on water? // ); // public static final MobFamily GHAST = new MobFamily<>( diff --git a/src/main/java/fathertoast/specialmobs/common/entity/witch/_SpecialWitchEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/witch/_SpecialWitchEntity.java new file mode 100644 index 0000000..b165504 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/witch/_SpecialWitchEntity.java @@ -0,0 +1,230 @@ +package fathertoast.specialmobs.common.entity.witch; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.core.SpecialMobs; +import fathertoast.specialmobs.common.entity.ISpecialMob; +import fathertoast.specialmobs.common.entity.SpecialMobData; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import mcp.MethodsReturnNonnullByDefault; +import net.minecraft.block.BlockState; +import net.minecraft.entity.*; +import net.minecraft.entity.ai.attributes.AttributeModifierMap; +import net.minecraft.entity.monster.WitchEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.projectile.SnowballEntity; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.network.datasync.DataParameter; +import net.minecraft.network.datasync.DataSerializers; +import net.minecraft.network.datasync.EntityDataManager; +import net.minecraft.potion.EffectInstance; +import net.minecraft.util.DamageSource; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.vector.Vector3d; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class _SpecialWitchEntity extends WitchEntity implements ISpecialMob<_SpecialWitchEntity> { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0xA80E0E ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return WitchEntity.createAttributes(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Witch", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void addBaseLoot( LootTableBuilder loot ) { + loot.addLootTable( "main", EntityType.WITCH.getDefaultLootTable() ); + } + + @SpecialMob.Constructor + public _SpecialWitchEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().initialize(); + } + + + //--------------- Variant-Specific Breakouts ---------------- + + /** Called in the MobEntity.class constructor to initialize AI goals. */ + @Override + protected void registerGoals() { + super.registerGoals(); + registerVariantGoals(); + } + + /** Override to change this entity's AI goals. */ + protected void registerVariantGoals() { } + + /** Called when this entity successfully damages a target to apply on-hit effects. */ + @Override + public void doEnchantDamageEffects( LivingEntity attacker, Entity target ) { + onVariantAttack( target ); + super.doEnchantDamageEffects( attacker, target ); + } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + protected void onVariantAttack( Entity target ) { } + + //TODO + + /** Override to save data to this entity's NBT data. */ + public void addVariantSaveData( CompoundNBT saveTag ) { } + + /** Override to load data from this entity's NBT data. */ + public void readVariantSaveData( CompoundNBT saveTag ) { } + + + //--------------- Family-Specific Implementations ---------------- + + /** The parameter for special mob render scale. */ + private static final DataParameter SCALE = EntityDataManager.defineId( _SpecialWitchEntity.class, DataSerializers.FLOAT ); + + /** Called from the Entity.class constructor to define data watcher variables. */ + @Override + protected void defineSynchedData() { + super.defineSynchedData(); + specialData = new SpecialMobData<>( this, SCALE, 1.0F ); + } + + //TODO + + + //--------------- ISpecialMob Implementation ---------------- + + private SpecialMobData<_SpecialWitchEntity> specialData; + + /** @return This mob's special data. */ + @Override + public SpecialMobData<_SpecialWitchEntity> getSpecialData() { return specialData; } + + /** @return The experience that should be dropped by this entity. */ + @Override + public final int getExperience() { return xpReward; } + + /** Sets the experience that should be dropped by this entity. */ + @Override + public final void setExperience( int xp ) { xpReward = xp; } + + static ResourceLocation GET_TEXTURE_PATH( String type ) { + return SpecialMobs.resourceLoc( SpecialMobs.TEXTURE_PATH + "witch/" + type + ".png" ); + } + + private static final ResourceLocation[] TEXTURES = { + new ResourceLocation( "textures/entity/witch.png" ) + }; + + /** @return All default textures for this entity. */ + @Override + public ResourceLocation[] getDefaultTextures() { return TEXTURES; } + + + //--------------- SpecialMobData Hooks ---------------- + + /** Called each tick to update this entity's movement. */ + @Override + public void aiStep() { + super.aiStep(); + getSpecialData().tick(); + } + + /** @return The eye height of this entity when standing. */ + @Override + protected float getStandingEyeHeight( Pose pose, EntitySize size ) { + return super.getStandingEyeHeight( pose, size ) * getSpecialData().getBaseScale() * (isBaby() ? 0.53448F : 1.0F); + } + + /** @return Whether this entity is immune to fire damage. */ + @Override + public boolean fireImmune() { return getSpecialData().isImmuneToFire(); } + + /** Sets this entity on fire for a specific duration. */ + @Override + public void setRemainingFireTicks( int ticks ) { + if( !getSpecialData().isImmuneToBurning() ) super.setRemainingFireTicks( ticks ); + } + + /** @return True if this entity can be leashed. */ + @Override + public boolean canBeLeashed( PlayerEntity player ) { return !isLeashed() && getSpecialData().allowLeashing(); } + + /** Sets this entity 'stuck' inside a block, such as a cobweb or sweet berry bush. Mod blocks could use this as a speed boost. */ + @Override + public void makeStuckInBlock( BlockState block, Vector3d speedMulti ) { + if( getSpecialData().canBeStuckIn( block ) ) super.makeStuckInBlock( block, speedMulti ); + } + + /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */ + @Override + public boolean causeFallDamage( float distance, float damageMultiplier ) { + return super.causeFallDamage( distance, damageMultiplier * getSpecialData().getFallDamageMultiplier() ); + } + + /** @return True if this entity should NOT trigger pressure plates or tripwires. */ + @Override + public boolean isIgnoringBlockTriggers() { return getSpecialData().ignorePressurePlates(); } + + /** @return True if this entity can breathe underwater. */ + @Override + public boolean canBreatheUnderwater() { return getSpecialData().canBreatheInWater(); } + + /** @return True if this entity can be pushed by (flowing) fluids. */ + @Override + public boolean isPushedByFluid() { return !getSpecialData().ignoreWaterPush(); } + + /** @return True if this entity takes damage while wet. */ + @Override + public boolean isSensitiveToWater() { return getSpecialData().isDamagedByWater(); } + + /** @return Attempts to damage this entity; returns true if the hit was successful. */ + @Override + public boolean hurt( DamageSource source, float amount ) { + if( isSensitiveToWater() && source.getDirectEntity() instanceof SnowballEntity ) { + amount = Math.max( 3.0F, amount ); + } + return super.hurt( source, amount ); + } + + /** @return True if the effect can be applied to this entity. */ + @Override + public boolean canBeAffected( EffectInstance effect ) { return getSpecialData().isPotionApplicable( effect ); } + + /** Saves data to this entity's base NBT compound that is specific to its subclass. */ + @Override + public void addAdditionalSaveData( CompoundNBT tag ) { + super.addAdditionalSaveData( tag ); + + final CompoundNBT saveTag = SpecialMobData.getSaveLocation( tag ); + + getSpecialData().writeToNBT( saveTag ); + addVariantSaveData( saveTag ); + } + + /** Loads data from this entity's base NBT compound that is specific to its subclass. */ + @Override + public void readAdditionalSaveData( CompoundNBT tag ) { + super.readAdditionalSaveData( tag ); + + final CompoundNBT saveTag = SpecialMobData.getSaveLocation( tag ); + + getSpecialData().readFromNBT( saveTag ); + readVariantSaveData( saveTag ); + } +} \ No newline at end of file