diff --git a/src/main/java/fathertoast/specialmobs/client/ClientRegister.java b/src/main/java/fathertoast/specialmobs/client/ClientRegister.java index c57754d..58e8008 100644 --- a/src/main/java/fathertoast/specialmobs/client/ClientRegister.java +++ b/src/main/java/fathertoast/specialmobs/client/ClientRegister.java @@ -40,6 +40,7 @@ public class ClientRegister { registerFamilyRenderers( MobFamily.SILVERFISH, SpecialSilverfishRenderer::new ); registerFamilyRenderers( MobFamily.ENDERMAN, SpecialEndermanRenderer::new ); //registerFamilyRenderers( MobFamily.WITCH, SpecialWitchRenderer::new ); + registerFamilyRenderers( MobFamily.BLAZE, SpecialBlazeRenderer::new ); // Species overrides registerSpeciesRenderer( NinjaSkeletonEntity.SPECIES, NinjaSkeletonRenderer::new ); diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialBlazeRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialBlazeRenderer.java new file mode 100644 index 0000000..474cde4 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialBlazeRenderer.java @@ -0,0 +1,44 @@ +package fathertoast.specialmobs.client.renderer.entity; + +import com.mojang.blaze3d.matrix.MatrixStack; +import fathertoast.specialmobs.common.entity.ISpecialMob; +import mcp.MethodsReturnNonnullByDefault; +import net.minecraft.client.renderer.entity.BlazeRenderer; +import net.minecraft.client.renderer.entity.EntityRendererManager; +import net.minecraft.entity.monster.BlazeEntity; +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 SpecialBlazeRenderer extends BlazeRenderer { + + private final float baseShadowRadius; + + public SpecialBlazeRenderer( EntityRendererManager rendererManager ) { + super( rendererManager ); + baseShadowRadius = shadowRadius; + // Unneeded, the whole model is full brightness + //addLayer( new SpecialMobEyesLayer<>( this ) ); + // Model doesn't support size parameter + //addLayer( new SpecialMobOverlayLayer<>( this, new SilverfishModel<>( 0.25F ) ) ); + } + + @Override + public ResourceLocation getTextureLocation( BlazeEntity entity ) { + return ((ISpecialMob) entity).getSpecialData().getTexture(); + } + + @Override + protected void scale( BlazeEntity 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/client/renderer/entity/SpecialCreeperRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialCreeperRenderer.java index da7c327..ba1cade 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialCreeperRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialCreeperRenderer.java @@ -27,12 +27,12 @@ public class SpecialCreeperRenderer extends CreeperRenderer { public SpecialCreeperRenderer( EntityRendererManager rendererManager ) { super( rendererManager ); baseShadowRadius = shadowRadius; + // Get rid of this one since we have our own implementation + layers.removeIf( ( layer ) -> layer instanceof CreeperChargeLayer ); + addLayer( new SpecialMobEyesLayer<>( this ) ); addLayer( new SpecialMobOverlayLayer<>( this, new CreeperModel<>( 0.25F ) ) ); - addLayer( new SpecialCreeperChargeLayer<>(this, new CreeperModel<>( 2.0F )) ); - - // Get rid of this one since we have our own implementation - layers.removeIf((layer) -> layer instanceof CreeperChargeLayer); + addLayer( new SpecialCreeperChargeLayer<>( this, new CreeperModel<>( 2.0F ) ) ); } @Override diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialEndermanRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialEndermanRenderer.java index e5a8e59..b61e9c7 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialEndermanRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialEndermanRenderer.java @@ -7,6 +7,7 @@ import fathertoast.specialmobs.common.entity.ISpecialMob; import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.EndermanRenderer; import net.minecraft.client.renderer.entity.EntityRendererManager; +import net.minecraft.client.renderer.entity.layers.EndermanEyesLayer; import net.minecraft.client.renderer.entity.model.EndermanModel; import net.minecraft.entity.monster.EndermanEntity; import net.minecraft.util.ResourceLocation; @@ -24,9 +25,11 @@ public class SpecialEndermanRenderer extends EndermanRenderer { public SpecialEndermanRenderer( EntityRendererManager rendererManager ) { super( rendererManager ); - layers.remove( layers.size() - 2 ); // Remove vanilla eyes layer + // Get rid of this one since we have our own implementation + layers.removeIf( ( layer ) -> layer instanceof EndermanEyesLayer ); + baseShadowRadius = shadowRadius; - //addLayer( new SpecialMobEyesLayer<>( this ) );//TODO temp until textures are fixed + addLayer( new SpecialMobEyesLayer<>( this ) ); addLayer( new SpecialMobOverlayLayer<>( this, new EndermanModel<>( 0.25F ) ) ); } diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialMagmaCubeRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialMagmaCubeRenderer.java index 4076359..fad4675 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialMagmaCubeRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialMagmaCubeRenderer.java @@ -1,7 +1,6 @@ package fathertoast.specialmobs.client.renderer.entity; import com.mojang.blaze3d.matrix.MatrixStack; -import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobEyesLayer; import fathertoast.specialmobs.common.entity.ISpecialMob; import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.EntityRendererManager; @@ -23,7 +22,8 @@ public class SpecialMagmaCubeRenderer extends MagmaCubeRenderer { public SpecialMagmaCubeRenderer( EntityRendererManager rendererManager ) { super( rendererManager ); baseShadowRadius = shadowRadius; - addLayer( new SpecialMobEyesLayer<>( this ) ); + // Unneeded, the whole model is full brightness + //addLayer( new SpecialMobEyesLayer<>( this ) ); // Model doesn't support size parameter //addLayer( new SpecialMobOverlayLayer<>( this, new MagmaCubeModel<>( 0.25F ) ) ); } diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSpiderRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSpiderRenderer.java index 9b86e05..2a7764e 100644 --- a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSpiderRenderer.java +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSpiderRenderer.java @@ -6,6 +6,7 @@ import fathertoast.specialmobs.common.entity.ISpecialMob; import mcp.MethodsReturnNonnullByDefault; import net.minecraft.client.renderer.entity.EntityRendererManager; import net.minecraft.client.renderer.entity.SpiderRenderer; +import net.minecraft.client.renderer.entity.layers.SpiderEyesLayer; import net.minecraft.entity.monster.SpiderEntity; import net.minecraft.util.ResourceLocation; import net.minecraftforge.api.distmarker.Dist; @@ -22,7 +23,9 @@ public class SpecialSpiderRenderer extends SpiderRenderer { public SpecialSpiderRenderer( EntityRendererManager rendererManager ) { super( rendererManager ); - layers.remove( layers.size() - 1 ); // Remove vanilla eyes layer + // Get rid of this one since we have our own implementation + layers.removeIf( ( layer ) -> layer instanceof SpiderEyesLayer ); + baseShadowRadius = shadowRadius; addLayer( new SpecialMobEyesLayer<>( this ) ); // Model doesn't support size parameter diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java index ed13090..1a6e869 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java @@ -100,10 +100,10 @@ public class MobFamily { // "Baby", "Fighter", "King", "Queen", "Unholy" // ); - // public static final MobFamily BLAZE = new MobFamily<>( - // "Blaze", "blazes", 0xF6B201, new EntityType[] { EntityType.BLAZE }, - // "Cinder", "Conflagration", "Ember", "Hellfire", "Inferno", "Jolt", "Wildfire" - // ); + public static final MobFamily BLAZE = new MobFamily<>( + "Blaze", "blazes", 0xF6B201, new EntityType[] { EntityType.BLAZE }, + "Cinder", "Conflagration", "Ember", "Hellfire", "Inferno", "Jolt", "Wildfire" + ); static { final HashMap, MobFamily> classToFamilyMap = new HashMap<>(); diff --git a/src/main/java/fathertoast/specialmobs/common/entity/ai/SpecialBlazeAttackGoal.java b/src/main/java/fathertoast/specialmobs/common/entity/ai/SpecialBlazeAttackGoal.java new file mode 100644 index 0000000..2c3b2ef --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/ai/SpecialBlazeAttackGoal.java @@ -0,0 +1,102 @@ +package fathertoast.specialmobs.common.entity.ai; + +import fathertoast.specialmobs.common.entity.SpecialMobData; +import fathertoast.specialmobs.common.entity.blaze._SpecialBlazeEntity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.goal.Goal; + +import java.util.EnumSet; + +/** + * The attack AI used by blazes. This handles both ranged and melee behaviors. + */ +public class SpecialBlazeAttackGoal extends Goal { + + private final _SpecialBlazeEntity blaze; + + private int attackStep; + private int attackTime; + private int lastSeen; + + public SpecialBlazeAttackGoal( _SpecialBlazeEntity entity ) { + blaze = entity; + setFlags( EnumSet.of( Goal.Flag.MOVE, Goal.Flag.LOOK ) ); + } + + /** @return Returns true if this AI can be activated. */ + @Override + public boolean canUse() { + final LivingEntity target = blaze.getTarget(); + return target != null && target.isAlive() && blaze.canAttack( target ); + } + + /** Called when this AI is activated. */ + @Override + public void start() { + attackStep = 0; + } + + /** Called when this AI is deactivated. */ + @Override + public void stop() { + blaze.setCharged( false ); + lastSeen = 0; + } + + /** Called each tick while this AI is active. */ + @Override + public void tick() { + attackTime--; + + final LivingEntity target = blaze.getTarget(); + if( target == null ) return; + + final SpecialMobData<_SpecialBlazeEntity> data = blaze.getSpecialData(); + + final boolean canSee = blaze.getSensing().canSee( target ); + if( canSee ) lastSeen = 0; + else lastSeen++; + + final double distanceSqr = blaze.distanceToSqr( target ); + final float rangeSq = data.rangedAttackMaxRange * data.rangedAttackMaxRange; + if( distanceSqr < getAttackReachSqr( target ) ) { + if( canSee && attackTime <= 0 ) { + attackTime = 20; + blaze.doHurtTarget( target ); + } + blaze.getMoveControl().setWantedPosition( target.getX(), target.getY(), target.getZ(), 1.0 ); + } + else if( distanceSqr < rangeSq && canSee ) { + if( attackTime <= 0 ) { + attackStep++; + + if( attackStep == 1 ) { + attackTime = data.rangedAttackMaxCooldown - data.rangedAttackCooldown; + blaze.setCharged( true ); + } + else if( attackStep <= 1 + blaze.fireballBurstCount ) { + attackTime = blaze.fireballBurstDelay; + } + else { + attackTime = data.rangedAttackCooldown; + attackStep = 0; + blaze.setCharged( false ); + } + + if( attackStep > 1 ) { + blaze.performRangedAttack( target, attackStep ); + } + } + blaze.getLookControl().setLookAt( target, 10.0F, 10.0F ); + } + else if( lastSeen < 5 ) { + blaze.getMoveControl().setWantedPosition( target.getX(), target.getY(), target.getZ(), 1.0 ); + } + + super.tick(); + } + + protected double getAttackReachSqr( LivingEntity target ) { + return blaze.getBbWidth() * blaze.getBbWidth() * 4.0F + target.getBbWidth() + 2.0F; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/blaze/CinderBlazeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/blaze/CinderBlazeEntity.java new file mode 100644 index 0000000..e889568 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/blaze/CinderBlazeEntity.java @@ -0,0 +1,78 @@ +package fathertoast.specialmobs.common.entity.blaze; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.util.AttributeHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import mcp.MethodsReturnNonnullByDefault; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.attributes.AttributeModifierMap; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.item.Items; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class CinderBlazeEntity extends _SpecialBlazeEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 0.5F, 0.9F ); + return new BestiaryInfo( 0xFFC0CB, BestiaryInfo.BaseWeight.DISABLED ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialBlazeEntity.createAttributes() ) + .addAttribute( Attributes.ATTACK_DAMAGE, -2.0 ) + .multAttribute( Attributes.MOVEMENT_SPEED, 1.3 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Cinder", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + loot.addCommonDrop( "common", Items.BLAZE_POWDER, 1 ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return CinderBlazeEntity::new; } + + + //--------------- Variant-Specific Implementations ---------------- + + public CinderBlazeEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setBaseScale( 0.5F ); + xpReward = 1; + } + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + getSpecialData().rangedAttackDamage -= 1.0F; + disableRangedAI(); + } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + protected void onVariantAttack( Entity target ) { + target.setSecondsOnFire( 4 ); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/blaze/ConflagrationBlazeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/blaze/ConflagrationBlazeEntity.java new file mode 100644 index 0000000..aff3e7a --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/blaze/ConflagrationBlazeEntity.java @@ -0,0 +1,141 @@ +package fathertoast.specialmobs.common.entity.blaze; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.MobHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import mcp.MethodsReturnNonnullByDefault; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.attributes.AttributeModifier; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.entity.ai.attributes.ModifiableAttributeInstance; +import net.minecraft.entity.projectile.SnowballEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.potion.PotionUtils; +import net.minecraft.potion.Potions; +import net.minecraft.util.DamageSource; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.UUID; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class ConflagrationBlazeEntity extends _SpecialBlazeEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 0.9F, 2.7F ); + return new BestiaryInfo( 0xFFF87E, BestiaryInfo.BaseWeight.LOW ); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Conflagration", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.FIRE_CHARGE ); + loot.addRareDrop( "rare", PotionUtils.setPotion( new ItemStack( Items.POTION ), Potions.FIRE_RESISTANCE ) ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return ConflagrationBlazeEntity::new; } + + + //--------------- Variant-Specific Implementations ---------------- + + /** The damage boost to apply from growth level. */ + private static final AttributeModifier DAMAGE_BOOST = new AttributeModifier( UUID.fromString( "70457CAB-AA09-4E1C-B44B-99DD4A2A836D" ), + "Feeding damage boost", 1.0, AttributeModifier.Operation.ADDITION ); + + /** The level of increased attack damage gained. */ + private int growthLevel; + + public ConflagrationBlazeEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setBaseScale( 1.5F ); + xpReward += 4; + } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + protected void onVariantAttack( Entity target ) { + if( target instanceof LivingEntity ) { + MobHelper.stealLife( this, (LivingEntity) target, 2.0F ); + } + } + + /** @return Attempts to damage this entity; returns true if the hit was successful. */ + @Override + public boolean hurt( DamageSource source, float amount ) { + if( isInvulnerableTo( source ) || fireImmune() && source.isFire() ) return false; + + if( !source.isExplosion() && !source.isMagic() && !DamageSource.DROWN.getMsgId().equals( source.getMsgId() ) && + !(source.getDirectEntity() instanceof SnowballEntity) ) { + + if( !level.isClientSide() && growthLevel < 7 ) { + growthLevel++; + + getSpecialData().rangedAttackDamage += 0.5F; + getSpecialData().rangedAttackCooldown -= 4; + getSpecialData().rangedAttackMaxCooldown -= 4; + if( growthLevel == 7 ) fireballBurstCount++; + + updateFeedingLevels(); + } + amount /= 2.0F; + } + return super.hurt( source, amount ); + } + + /** Recalculates the modifiers associated with this entity's feeding level counters. */ + private void updateFeedingLevels() { + if( level != null && !level.isClientSide ) { + final ModifiableAttributeInstance damage = getAttribute( Attributes.ATTACK_DAMAGE ); + //noinspection ConstantConditions + damage.removeModifier( DAMAGE_BOOST.getId() ); + if( growthLevel > 0 ) { + damage.addPermanentModifier( new AttributeModifier( DAMAGE_BOOST.getId(), DAMAGE_BOOST.getName(), + DAMAGE_BOOST.getAmount() * growthLevel, DAMAGE_BOOST.getOperation() ) ); + } + } + } + + /** Override to save data to this entity's NBT data. */ + @Override + public void addVariantSaveData( CompoundNBT saveTag ) { + saveTag.putByte( References.TAG_GROWTH_LEVEL, (byte) growthLevel ); + } + + /** Override to load data from this entity's NBT data. */ + @Override + public void readVariantSaveData( CompoundNBT saveTag ) { + if( saveTag.contains( References.TAG_GROWTH_LEVEL, References.NBT_TYPE_NUMERICAL ) ) + growthLevel = saveTag.getByte( References.TAG_GROWTH_LEVEL ); + updateFeedingLevels(); + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "conflagration" ) + }; + + /** @return All default textures for this entity. */ + @Override + public ResourceLocation[] getDefaultTextures() { return TEXTURES; } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/blaze/EmberBlazeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/blaze/EmberBlazeEntity.java new file mode 100644 index 0000000..64a58c9 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/blaze/EmberBlazeEntity.java @@ -0,0 +1,89 @@ +package fathertoast.specialmobs.common.entity.blaze; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.MobHelper; +import fathertoast.specialmobs.common.util.AttributeHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import mcp.MethodsReturnNonnullByDefault; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.attributes.AttributeModifierMap; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.item.Items; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class EmberBlazeEntity extends _SpecialBlazeEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0x000000 ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialBlazeEntity.createAttributes() ) + .addAttribute( Attributes.MAX_HEALTH, 10.0 ) + .addAttribute( Attributes.ARMOR, 10.0 ) + .multAttribute( Attributes.MOVEMENT_SPEED, 1.3 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Ember", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.COAL, 1 ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return EmberBlazeEntity::new; } + + + //--------------- Variant-Specific Implementations ---------------- + + public EmberBlazeEntity( EntityType entityType, World world ) { + super( entityType, world ); + xpReward += 2; + } + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + disableRangedAI(); + } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + protected void onVariantAttack( Entity target ) { + if( target instanceof LivingEntity ) { + MobHelper.causeLifeLoss( (LivingEntity) target, 2.0F ); + } + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "ember" ) + }; + + /** @return All default textures for this entity. */ + @Override + public ResourceLocation[] getDefaultTextures() { return TEXTURES; } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/blaze/HellfireBlazeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/blaze/HellfireBlazeEntity.java new file mode 100644 index 0000000..c674a65 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/blaze/HellfireBlazeEntity.java @@ -0,0 +1,116 @@ +package fathertoast.specialmobs.common.entity.blaze; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.util.AttributeHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import mcp.MethodsReturnNonnullByDefault; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.attributes.AttributeModifierMap; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.entity.projectile.FireballEntity; +import net.minecraft.item.Items; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.MathHelper; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class HellfireBlazeEntity extends _SpecialBlazeEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 0.7F, 1.99F ); + return new BestiaryInfo( 0xDDDDDD ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialBlazeEntity.createAttributes() ) + .addAttribute( Attributes.MAX_HEALTH, 10.0 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Hellfire", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.GUNPOWDER ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return HellfireBlazeEntity::new; } + + + //--------------- Variant-Specific Implementations ---------------- + + /** The base explosion strength of this blaze's fireballs. */ + private int explosionPower = 2; + + public HellfireBlazeEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setBaseScale( 1.1F ); + xpReward += 2; + } + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + getSpecialData().rangedAttackSpread = 0.0F; + setRangedAI( 1, 0, 60, 100, 40.0F ); + } + + /** Called to attack the target with a ranged attack. */ + @Override + public void performRangedAttack( LivingEntity target, float damageMulti ) { + if( !isSilent() ) level.levelEvent( null, 1018, blockPosition(), 0 ); + + final float accelVariance = MathHelper.sqrt( distanceTo( target ) ) * getSpecialData().rangedAttackSpread / 28.0F; + final double dX = target.getX() - getX() + getRandom().nextGaussian() * accelVariance; + final double dY = target.getY( 0.5 ) - getY( 0.5 ); + final double dZ = target.getZ() - getZ() + getRandom().nextGaussian() * accelVariance; + + final FireballEntity fireball = new FireballEntity( level, this, dX, dY, dZ ); + fireball.explosionPower = explosionPower; + fireball.setPos( fireball.getX(), getY( 0.5 ) + 0.5, fireball.getZ() ); + level.addFreshEntity( fireball ); + } + + /** Override to save data to this entity's NBT data. */ + @Override + public void addVariantSaveData( CompoundNBT saveTag ) { + saveTag.putByte( References.TAG_EXPLOSION_POWER, (byte) explosionPower ); + } + + /** Override to load data from this entity's NBT data. */ + @Override + public void readVariantSaveData( CompoundNBT saveTag ) { + if( saveTag.contains( References.TAG_EXPLOSION_POWER, References.NBT_TYPE_NUMERICAL ) ) + explosionPower = saveTag.getByte( References.TAG_EXPLOSION_POWER ); + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "hellfire" ) + }; + + /** @return All default textures for this entity. */ + @Override + public ResourceLocation[] getDefaultTextures() { return TEXTURES; } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/blaze/InfernoBlazeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/blaze/InfernoBlazeEntity.java new file mode 100644 index 0000000..a251cc2 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/blaze/InfernoBlazeEntity.java @@ -0,0 +1,89 @@ +package fathertoast.specialmobs.common.entity.blaze; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.MobHelper; +import fathertoast.specialmobs.common.util.AttributeHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import mcp.MethodsReturnNonnullByDefault; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.attributes.AttributeModifierMap; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.item.Items; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class InfernoBlazeEntity extends _SpecialBlazeEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0xF14F00 ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialBlazeEntity.createAttributes() ) + .addAttribute( Attributes.MAX_HEALTH, 10.0 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Inferno", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.BLAZE_POWDER ); + loot.addSemicommonDrop( "semicommon", Items.FIRE_CHARGE ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return InfernoBlazeEntity::new; } + + + //--------------- Variant-Specific Implementations ---------------- + + public InfernoBlazeEntity( EntityType entityType, World world ) { + super( entityType, world ); + xpReward += 2; + } + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + getSpecialData().rangedAttackSpread *= 3.0F; + setRangedAI( 12, 2, 80, 100, 20.0F ); + } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + protected void onVariantAttack( Entity target ) { + if( target instanceof LivingEntity ) { + MobHelper.causeLifeLoss( (LivingEntity) target, 2.0F ); + } + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "inferno" ) + }; + + /** @return All default textures for this entity. */ + @Override + public ResourceLocation[] getDefaultTextures() { return TEXTURES; } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/blaze/JoltBlazeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/blaze/JoltBlazeEntity.java new file mode 100644 index 0000000..bfd3bcd --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/blaze/JoltBlazeEntity.java @@ -0,0 +1,189 @@ +package fathertoast.specialmobs.common.entity.blaze; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.MobHelper; +import fathertoast.specialmobs.common.util.AttributeHelper; +import fathertoast.specialmobs.common.util.ExplosionHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import mcp.MethodsReturnNonnullByDefault; +import net.minecraft.block.BlockState; +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.ai.attributes.AttributeModifierMap; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.entity.effect.LightningBoltEntity; +import net.minecraft.item.Items; +import net.minecraft.tags.FluidTags; +import net.minecraft.util.DamageSource; +import net.minecraft.util.Direction; +import net.minecraft.util.IndirectEntityDamageSource; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.util.math.vector.Vector3d; +import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; +import net.minecraftforge.event.ForgeEventFactory; +import net.minecraftforge.event.entity.living.EntityTeleportEvent; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class JoltBlazeEntity extends _SpecialBlazeEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0x499CAE ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialBlazeEntity.createAttributes() ) + .addAttribute( Attributes.MAX_HEALTH, 10.0 ) + .addAttribute( Attributes.ARMOR, 10.0 ) + .multAttribute( Attributes.MOVEMENT_SPEED, 1.3 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Jolt", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.REDSTONE ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return JoltBlazeEntity::new; } + + + //--------------- Variant-Specific Implementations ---------------- + + public JoltBlazeEntity( EntityType entityType, World world ) { + super( entityType, world ); + xpReward += 2; + } + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + disableRangedAI(); + } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + protected void onVariantAttack( Entity target ) { + if( target instanceof LivingEntity ) { + MobHelper.causeLifeLoss( (LivingEntity) target, 2.0F ); + } + } + + /** Called each tick to update this entity's movement. */ + @Override + public void aiStep() { + if( !level.isClientSide() && isAlive() && getTarget() != null && random.nextInt( 20 ) == 0 && + distanceToSqr( getTarget() ) > 256.0 ) { + for( int i = 0; i < 16; i++ ) { + if( teleportTowards( getTarget() ) ) break; + } + } + super.aiStep(); + } + + /** @return Attempts to damage this entity; returns true if the hit was successful. */ + @Override + public boolean hurt( DamageSource source, float amount ) { + if( isInvulnerableTo( source ) || fireImmune() && source.isFire() ) return false; + + if( source instanceof IndirectEntityDamageSource ) { + for( int i = 0; i < 64; i++ ) { + if( teleport() ) return true; + } + return false; + } + + final boolean success = super.hurt( source, amount ); + if( !level.isClientSide() && getHealth() > 0.0F ) { + if( source.getEntity() instanceof LivingEntity ) { + for( int i = 0; i < 16; i++ ) { + if( teleport() ) break; + } + } + else if( random.nextInt( 10 ) != 0 ) { + teleport(); + } + } + return success; + } + + /** @return Teleports this "enderman" to a random nearby position; returns true if successful. */ + protected boolean teleport() { + if( level.isClientSide() || !isAlive() ) return false; + + final double x = getX() + (random.nextDouble() - 0.5) * 32.0; + final double y = getY() + (double) (random.nextInt( 16 ) - 8); + final double z = getZ() + (random.nextDouble() - 0.5) * 32.0; + return teleport( x, y, z ); + } + + /** @return Teleports this "enderman" towards another entity; returns true if successful. */ + protected boolean teleportTowards( Entity target ) { + final Vector3d directionFromTarget = new Vector3d( + getX() - target.getX(), + getY( 0.5 ) - target.getEyeY(), + getZ() - target.getZ() ) + .normalize(); + + final double x = getX() + (random.nextDouble() - 0.5) * 8.0 - directionFromTarget.x * 16.0; + final double y = getY() + (double) (random.nextInt( 8 ) - 2) - directionFromTarget.y * 16.0; + final double z = getZ() + (random.nextDouble() - 0.5) * 8.0 - directionFromTarget.z * 16.0; + return teleport( x, y, z ); + } + + /** @return Teleports this "enderman" to a new position; returns true if successful. */ + protected boolean teleport( double x, double y, double z ) { + final BlockPos.Mutable pos = new BlockPos.Mutable( x, y, z ); + + while( pos.getY() > 0 && !level.getBlockState( pos ).getMaterial().blocksMotion() ) { + pos.move( Direction.DOWN ); + } + + final BlockState block = level.getBlockState( pos ); + if( !block.getMaterial().blocksMotion() || block.getFluidState().is( FluidTags.WATER ) ) return false; + + EntityTeleportEvent.EnderEntity event = ForgeEventFactory.onEnderTeleport( this, x, y, z ); + if( event.isCanceled() ) return false; + + final boolean success = randomTeleport( event.getTargetX(), event.getTargetY(), event.getTargetZ(), false ); + if( success ) { + ExplosionHelper.spawnLightning( level, xo, yo, zo ); + ExplosionHelper.spawnLightning( level, getX(), getY(), getZ() ); + } + return success; + } + + /** Called when this entity is struck by lightning. */ + @Override + public void thunderHit( ServerWorld world, LightningBoltEntity lightningBolt ) { } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "jolt" ) + }; + + /** @return All default textures for this entity. */ + @Override + public ResourceLocation[] getDefaultTextures() { return TEXTURES; } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/blaze/WildfireBlazeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/blaze/WildfireBlazeEntity.java new file mode 100644 index 0000000..60a7cf3 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/blaze/WildfireBlazeEntity.java @@ -0,0 +1,169 @@ +package fathertoast.specialmobs.common.entity.blaze; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.util.AttributeHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import mcp.MethodsReturnNonnullByDefault; +import net.minecraft.entity.*; +import net.minecraft.entity.ai.attributes.AttributeModifierMap; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.item.Items; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.IServerWorld; +import net.minecraft.world.World; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class WildfireBlazeEntity extends _SpecialBlazeEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 0.9F, 2.7F ); + return new BestiaryInfo( 0xF4EE32 ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialBlazeEntity.createAttributes() ) + .addAttribute( Attributes.MAX_HEALTH, 20.0 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Wildfire", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.COAL, 1 ); + loot.addUncommonDrop( "uncommon", Items.BLAZE_SPAWN_EGG ); + } + + @SpecialMob.Factory + public static EntityType.IFactory getVariantFactory() { return WildfireBlazeEntity::new; } + + + //--------------- Variant-Specific Implementations ---------------- + + /** The number of babies spawned on death. */ + private int babies; + /** The number of extra babies that can be spawned by attacks. */ + private int extraBabies; + + public WildfireBlazeEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setBaseScale( 1.5F ); + getSpecialData().setRegenerationTime( 40 ); + xpReward += 2; + + babies = 2 + random.nextInt( 3 ); + extraBabies = 3 + random.nextInt( 4 ); + } + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + getSpecialData().rangedAttackSpread *= 0.1F; + setRangedAI( 1, 0, 30, 50, 20.0F ); + } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + protected void onVariantAttack( Entity target ) { + target.setSecondsOnFire( 8 ); + } + + /** Called to attack the target with a ranged attack. */ + @Override + public void performRangedAttack( LivingEntity target, float damageMulti ) { + if( !level.isClientSide() && extraBabies > 0 && random.nextInt( 2 ) == 0 ) { + extraBabies--; + + final double vX = target.getX() - getX(); + final double vZ = target.getZ() - getZ(); + final double vH = Math.sqrt( vX * vX + vZ * vZ ); + spawnBaby( vX / vH * 0.8 + getDeltaMovement().x * 0.2, vZ / vH * 0.8 + getDeltaMovement().z * 0.2, null ); + spawnAnim(); + if( !isSilent() ) level.levelEvent( null, 1018, blockPosition(), 0 ); + } + else { + super.performRangedAttack( target, damageMulti ); + } + } + + /** Called to remove this entity from the world. Includes death, unloading, interdimensional travel, etc. */ + @Override + public void remove( boolean keepData ) { + //noinspection deprecation + if( isDeadOrDying() && !removed && level instanceof IServerWorld ) { // Same conditions as slime splitting + // Spawn babies on death + final int babiesToSpawn = babies + extraBabies; + ILivingEntityData groupData = null; + for( int i = 0; i < babiesToSpawn; i++ ) { + groupData = spawnBaby( (random.nextDouble() - 0.5) * 0.3, (random.nextDouble() - 0.5) * 0.3, groupData ); + } + spawnAnim(); + if( !isSilent() ) level.levelEvent( null, 1018, blockPosition(), 0 ); + } + super.remove( keepData ); + } + + /** Helper method to simplify spawning babies. */ + @Nullable + private ILivingEntityData spawnBaby( double vX, double vZ, @Nullable ILivingEntityData groupData ) { + final CinderBlazeEntity baby = CinderBlazeEntity.SPECIES.entityType.get().create( level ); + if( baby == null ) return groupData; + + baby.copyPosition( this ); + baby.yHeadRot = yRot; + baby.yBodyRot = yRot; + groupData = baby.finalizeSpawn( (IServerWorld) level, level.getCurrentDifficultyAt( blockPosition() ), + SpawnReason.MOB_SUMMONED, groupData, null ); + baby.setTarget( getTarget() ); + + baby.setDeltaMovement( vX, 0.0, vZ ); + baby.setOnGround( false ); + + level.addFreshEntity( baby ); + return groupData; + } + + /** Override to save data to this entity's NBT data. */ + @Override + public void addVariantSaveData( CompoundNBT saveTag ) { + saveTag.putByte( References.TAG_BABIES, (byte) babies ); + saveTag.putByte( References.TAG_EXTRA_BABIES, (byte) extraBabies ); + } + + /** Override to load data from this entity's NBT data. */ + @Override + public void readVariantSaveData( CompoundNBT saveTag ) { + if( saveTag.contains( References.TAG_BABIES, References.NBT_TYPE_NUMERICAL ) ) + babies = saveTag.getByte( References.TAG_BABIES ); + if( saveTag.contains( References.TAG_EXTRA_BABIES, References.NBT_TYPE_NUMERICAL ) ) + extraBabies = saveTag.getByte( References.TAG_EXTRA_BABIES ); + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "wildfire" ) + }; + + /** @return All default textures for this entity. */ + @Override + public ResourceLocation[] getDefaultTextures() { return TEXTURES; } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/blaze/_SpecialBlazeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/blaze/_SpecialBlazeEntity.java new file mode 100644 index 0000000..a5fa153 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/blaze/_SpecialBlazeEntity.java @@ -0,0 +1,297 @@ +package fathertoast.specialmobs.common.entity.blaze; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.MobFamily; +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.entity.ai.AIHelper; +import fathertoast.specialmobs.common.entity.ai.SpecialBlazeAttackGoal; +import fathertoast.specialmobs.common.entity.ai.SpecialHurtByTargetGoal; +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.BlazeEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.projectile.SmallFireballEntity; +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.MathHelper; +import net.minecraft.util.math.vector.Vector3d; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.IServerWorld; +import net.minecraft.world.World; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class _SpecialBlazeEntity extends BlazeEntity implements IRangedAttackMob, ISpecialMob<_SpecialBlazeEntity> { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.SpeciesReference + public static MobFamily.Species<_SpecialBlazeEntity> SPECIES; + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0xFFF87E ); //TODO - TEMP: base size = 0.6F, 1.8F + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return BlazeEntity.createAttributes(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Blaze", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void addBaseLoot( LootTableBuilder loot ) { + loot.addLootTable( "main", EntityType.BLAZE.getDefaultLootTable() ); + } + + @SpecialMob.Factory + public static EntityType.IFactory<_SpecialBlazeEntity> getFactory() { return _SpecialBlazeEntity::new; } + + + //--------------- Variant-Specific Breakouts ---------------- + + public _SpecialBlazeEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().initialize(); + getSpecialData().setDamagedByWater( true ); + } + + /** Called in the MobEntity.class constructor to initialize AI goals. */ + @Override + protected void registerGoals() { + super.registerGoals(); + AIHelper.removeGoals( goalSelector, 4 ); // BlazeEntity.FireballAttackGoal + goalSelector.addGoal( 4, new SpecialBlazeAttackGoal( this ) ); + AIHelper.replaceHurtByTarget( this, new SpecialHurtByTargetGoal( this, BlazeEntity.class ).setAlertOthers() ); + + setRangedAI( 3, 6, 60, 100, 48.0F ); + registerVariantGoals(); + } + + /** Override to change this entity's AI goals. */ + protected void registerVariantGoals() { } + + /** Helper method to set the ranged attack AI more easily. */ + protected void disableRangedAI() { + setRangedAI( 0, 6, 60, 100, 0.0F ); + } + + /** Helper method to set the ranged attack AI more easily. */ + protected void setRangedAI( int burstCount, int burstDelay, int chargeTime, int cooldownTime, float range ) { + fireballBurstCount = burstCount; + fireballBurstDelay = burstDelay; + + getSpecialData().rangedAttackCooldown = cooldownTime; + getSpecialData().rangedAttackMaxCooldown = cooldownTime + chargeTime; + getSpecialData().rangedAttackMaxRange = range; + } + + /** 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 ) { } + + /** 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( _SpecialBlazeEntity.class, DataSerializers.FLOAT ); + + // The amount of fireballs in each burst. + public int fireballBurstCount; + // The ticks between each shot in a burst. + public int fireballBurstDelay; + + /** Called from the Entity.class constructor to define data watcher variables. */ + @Override + protected void defineSynchedData() { + super.defineSynchedData(); + specialData = new SpecialMobData<>( this, SCALE, 1.0F ); + } + + /** Called to attack the target with a ranged attack. */ + @Override + public void performRangedAttack( LivingEntity target, float damageMulti ) { + if( !isSilent() ) level.levelEvent( null, 1018, blockPosition(), 0 ); + + final float accelVariance = MathHelper.sqrt( distanceTo( target ) ) * getSpecialData().rangedAttackSpread / 28.0F; + final double dX = target.getX() - getX() + getRandom().nextGaussian() * accelVariance; + final double dY = target.getY( 0.5 ) - getY( 0.5 ); + final double dZ = target.getZ() - getZ() + getRandom().nextGaussian() * accelVariance; + + final SmallFireballEntity fireball = new SmallFireballEntity( level, this, dX, dY, dZ ); + fireball.setPos( fireball.getX(), getY( 0.5 ) + 0.5, fireball.getZ() ); + level.addFreshEntity( fireball ); + } + + /** Called on spawn to initialize properties based on the world, difficulty, and the group it spawns with. */ + @Nullable + public ILivingEntityData finalizeSpawn( IServerWorld world, DifficultyInstance difficulty, SpawnReason spawnReason, + @Nullable ILivingEntityData groupData, @Nullable CompoundNBT eggTag ) { + groupData = super.finalizeSpawn( world, difficulty, spawnReason, groupData, eggTag ); + // TODO ranged attack + return groupData; + } + + + //--------------- ISpecialMob Implementation ---------------- + + private SpecialMobData<_SpecialBlazeEntity> specialData; + + /** @return This mob's special data. */ + @Override + public SpecialMobData<_SpecialBlazeEntity> 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 + "blaze/" + type + ".png" ); + } + + private static final ResourceLocation[] TEXTURES = { new ResourceLocation( "textures/entity/blaze.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( source.getDirectEntity() instanceof SnowballEntity ) { + if( isSensitiveToWater() ) amount = Math.max( 3.0F, amount ); + else amount = 0.0F; // Allow blazes to be insensitive to snowballs + } + 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 ); + + saveTag.putByte( References.TAG_BURST_COUNT, (byte) fireballBurstCount ); + saveTag.putByte( References.TAG_BURST_DELAY, (byte) fireballBurstDelay ); + + 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 ); + + if( saveTag.contains( References.TAG_BURST_COUNT, References.NBT_TYPE_NUMERICAL ) ) + fireballBurstCount = saveTag.getByte( References.TAG_BURST_COUNT ); + if( saveTag.contains( References.TAG_BURST_DELAY, References.NBT_TYPE_NUMERICAL ) ) + fireballBurstDelay = saveTag.getByte( References.TAG_BURST_DELAY ); + + getSpecialData().readFromNBT( saveTag ); + readVariantSaveData( saveTag ); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java index 611a092..d10b0f2 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java @@ -189,11 +189,11 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob< /** Called when this entity is struck by lightning. */ @Override public void thunderHit( ServerWorld world, LightningBoltEntity lightningBolt ) { - if (!isPowered() && random.nextDouble() < 0.1D) - setSupercharged(true); - + if( !isPowered() && random.nextDouble() < 0.1D ) + setSupercharged( true ); + super.thunderHit( world, lightningBolt ); - + // Make it less likely for charged "explode while burning" creepers to immediately explode if( explodesWhileBurning() ) clearFire(); } @@ -222,13 +222,13 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob< private static final byte EXPLODE_FLAG_ON_FIRE = 0b0010; /** The bit for "explodes when shot". */ private static final byte EXPLODE_FLAG_WHEN_SHOT = 0b0100; - - + + /** @return True if this creeper is super charged. */ public boolean isSupercharged() { return entityData.get( IS_SUPERCHARGED ); } - + /** Sets this creeper's supercharged state to the given value. */ public void setSupercharged( boolean superCharged ) { entityData.set( IS_SUPERCHARGED, superCharged ); @@ -385,8 +385,9 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob< super.addAdditionalSaveData( tag ); final CompoundNBT saveTag = SpecialMobData.getSaveLocation( tag ); - + saveTag.putBoolean( References.TAG_SUPERCHARGED, isSupercharged() ); + saveTag.putBoolean( References.TAG_DRY_EXPLODE, cannotExplodeWhileWet() ); saveTag.putBoolean( References.TAG_WHEN_BURNING_EXPLODE, explodesWhileBurning() ); saveTag.putBoolean( References.TAG_WHEN_SHOT_EXPLODE, explodesWhenShot() ); @@ -401,9 +402,10 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob< super.readAdditionalSaveData( tag ); final CompoundNBT saveTag = SpecialMobData.getSaveLocation( tag ); - - setSupercharged( saveTag.getBoolean(References.TAG_SUPERCHARGED) ); - + + if( saveTag.contains( References.TAG_SUPERCHARGED, References.NBT_TYPE_NUMERICAL ) ) + setSupercharged( saveTag.getBoolean( References.TAG_SUPERCHARGED ) ); + if( saveTag.contains( References.TAG_DRY_EXPLODE, References.NBT_TYPE_NUMERICAL ) ) setCannotExplodeWhileWet( saveTag.getBoolean( References.TAG_DRY_EXPLODE ) ); if( saveTag.contains( References.TAG_WHEN_BURNING_EXPLODE, References.NBT_TYPE_NUMERICAL ) ) diff --git a/src/main/java/fathertoast/specialmobs/common/entity/spider/HungrySpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/HungrySpiderEntity.java index ff7b816..65697db 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/spider/HungrySpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/HungrySpiderEntity.java @@ -71,13 +71,6 @@ public class HungrySpiderEntity extends _SpecialSpiderEntity { //--------------- Variant-Specific Implementations ---------------- - public HungrySpiderEntity( EntityType entityType, World world ) { - super( entityType, world ); - getSpecialData().setBaseScale( 1.5F ); - getSpecialData().setRegenerationTime( 40 ); - xpReward += 2; - } - /** The damage boost to apply from growth level. */ private static final AttributeModifier DAMAGE_BOOST = new AttributeModifier( UUID.fromString( "70457CAB-AA09-4E1C-B44B-99DD4A2A836D" ), "Feeding damage boost", 1.0, AttributeModifier.Operation.ADDITION ); @@ -90,6 +83,13 @@ public class HungrySpiderEntity extends _SpecialSpiderEntity { /** The level of increased max health gained. */ private int maxHealthStacks; + public HungrySpiderEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setBaseScale( 1.5F ); + getSpecialData().setRegenerationTime( 40 ); + xpReward += 2; + } + /** Override to change this entity's AI goals. */ @Override protected void registerVariantGoals() { diff --git a/src/main/java/fathertoast/specialmobs/common/entity/spider/MotherSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/MotherSpiderEntity.java index 0f135fe..86d5f2e 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/spider/MotherSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/MotherSpiderEntity.java @@ -74,7 +74,7 @@ public class MotherSpiderEntity extends _SpecialSpiderEntity { xpReward += 1; babies = 2 + random.nextInt( 3 ); - extraBabies = 2 + random.nextInt( 3 ); + extraBabies = 3 + random.nextInt( 4 ); } /** The number of babies spawned on death. */ diff --git a/src/main/java/fathertoast/specialmobs/common/util/References.java b/src/main/java/fathertoast/specialmobs/common/util/References.java index 6146621..d979f81 100644 --- a/src/main/java/fathertoast/specialmobs/common/util/References.java +++ b/src/main/java/fathertoast/specialmobs/common/util/References.java @@ -22,8 +22,6 @@ public final class References { //--------------- ENTITY EVENTS ---------------- // Used in World#broadcastEntityEvent(Entity, byte) then executed by Entity#handleEntityEvent(byte) - public static final byte EVENT_SHIELD_BLOCK_SOUND = 29; - public static final byte EVENT_SHIELD_BREAK_SOUND = 30; public static final byte EVENT_TELEPORT_TRAIL_PARTICLES = 46; @@ -60,13 +58,17 @@ public final class References { public static final String TAG_WATER_DAMAGE = "WaterDamage"; public static final String TAG_STICKY_IMMUNE = "StickyImmune"; public static final String TAG_POTION_IMMUNE = "PotionImmune"; - public static final String TAG_SUPERCHARGED = "Supercharged"; // Creepers + public static final String TAG_SUPERCHARGED = "Supercharged"; public static final String TAG_DRY_EXPLODE = "CannotExplodeWhileWet"; public static final String TAG_WHEN_BURNING_EXPLODE = "ExplodesWhileBurning"; public static final String TAG_WHEN_SHOT_EXPLODE = "ExplodesWhenShot"; + // Blazes + public static final String TAG_BURST_COUNT = "FireballBurstCount"; + public static final String TAG_BURST_DELAY = "FireballBurstDelay"; + // Baby-able families - Skeletons, Wither Skeletons public static final String TAG_IS_BABY = "IsBaby"; @@ -75,13 +77,14 @@ public final class References { public static final String TAG_EXTRA_BABIES = "ExtraBabies"; // Splitting Creeper, Mother (Cave) Spider // Growing mobs - public static final String TAG_GROWTH_LEVEL = "GrowthLevel"; // Hungry Spider + public static final String TAG_GROWTH_LEVEL = "GrowthLevel"; // Hungry Spider, Conflagration Blaze public static final String TAG_HEALTH_STACKS = "HealthStacks"; // Hungry Spider // Misc. public static final String TAG_FUSE_TIME = "FuseTime"; // Blackberry Slime, Volatile Magma Cube public static final String TAG_AMMO = "Ammo"; // Web (Cave) Spider public static final String TAG_IS_FAKE = "IsFake"; // Mirage Enderman + public static final String TAG_EXPLOSION_POWER = "ExplosionPower"; // Hellfire Blaze //--------------- INTERNATIONALIZATION ---------------- diff --git a/src/main/resources/META-INF/accesstransformer.cfg b/src/main/resources/META-INF/accesstransformer.cfg index b04d0d8..85f32e9 100644 --- a/src/main/resources/META-INF/accesstransformer.cfg +++ b/src/main/resources/META-INF/accesstransformer.cfg @@ -18,4 +18,7 @@ protected net.minecraft.entity.monster.CreeperEntity func_190741_do()V # spawnLi # Endermen protected net.minecraft.entity.monster.EndermanEntity func_70816_c(Lnet/minecraft/entity/Entity;)Z # teleportTowards(Entity) -protected net.minecraft.entity.monster.EndermanEntity func_70825_j(DDD)Z # teleport(x,y,z) \ No newline at end of file +protected net.minecraft.entity.monster.EndermanEntity func_70825_j(DDD)Z # teleport(x,y,z) + +# Blazes +public net.minecraft.entity.monster.BlazeEntity func_70844_e(Z)V # setCharged(value) \ No newline at end of file