diff --git a/src/main/java/fathertoast/specialmobs/client/ClientRegister.java b/src/main/java/fathertoast/specialmobs/client/ClientRegister.java index e547963..50bce61 100644 --- a/src/main/java/fathertoast/specialmobs/client/ClientRegister.java +++ b/src/main/java/fathertoast/specialmobs/client/ClientRegister.java @@ -2,6 +2,7 @@ package fathertoast.specialmobs.client; import fathertoast.specialmobs.client.renderer.entity.SpecialCreeperRenderer; import fathertoast.specialmobs.client.renderer.entity.SpecialSkeletonRenderer; +import fathertoast.specialmobs.client.renderer.entity.SpecialSpiderRenderer; import fathertoast.specialmobs.common.bestiary.MobFamily; import fathertoast.specialmobs.common.core.SpecialMobs; import mcp.MethodsReturnNonnullByDefault; @@ -29,6 +30,8 @@ public class ClientRegister { // Family-based renderers registerFamilyRenderers( MobFamily.CREEPER, SpecialCreeperRenderer::new ); //registerFamilyRenderers( MobFamily.SKELETON, SpecialSkeletonRenderer::new ); + registerFamilyRenderers( MobFamily.SPIDER, SpecialSpiderRenderer::new ); + //registerFamilyRenderers( MobFamily.CAVE_SPIDER, SpecialSpiderRenderer::new ); } private static void registerFamilyRenderers( MobFamily family, IRenderFactory renderFactory ) { diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSpiderRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSpiderRenderer.java new file mode 100644 index 0000000..070762f --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSpiderRenderer.java @@ -0,0 +1,50 @@ +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.CreeperRenderer; +import net.minecraft.client.renderer.entity.EntityRendererManager; +import net.minecraft.client.renderer.entity.SpiderRenderer; +import net.minecraft.client.renderer.entity.model.CreeperModel; +import net.minecraft.client.renderer.entity.model.SpiderModel; +import net.minecraft.entity.monster.CreeperEntity; +import net.minecraft.entity.monster.SpiderEntity; +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 SpecialSpiderRenderer extends SpiderRenderer { + + private final float baseShadowRadius; + + public SpecialSpiderRenderer( EntityRendererManager rendererManager ) { + super( rendererManager ); + layers.remove( layers.size() - 1 ); // Remove vanilla eyes layer + baseShadowRadius = shadowRadius; + addLayer( new SpecialMobEyesLayer<>( this ) ); + // Model doesn't support size parameter + //addLayer( new SpecialMobOverlayLayer<>( this, new SpiderModel<>( 0.25F ) ) ); + } + + @Override + public ResourceLocation getTextureLocation( SpiderEntity entity ) { + return ((ISpecialMob) entity).getSpecialData().getTexture(); + } + + @Override + protected void scale( SpiderEntity 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/BestiaryInfo.java b/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java index 3982da4..e1b5925 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/BestiaryInfo.java @@ -1,22 +1,14 @@ package fathertoast.specialmobs.common.bestiary; -//import net.minecraft.init.Biomes; - -import net.minecraft.world.DimensionType; - -import java.lang.annotation.ElementType; -import java.lang.annotation.Retention; -import java.lang.annotation.RetentionPolicy; -import java.lang.annotation.Target; - public class BestiaryInfo { public enum BaseWeight { DEFAULT( 600 ), - HIGHEST( DEFAULT.value * 8 ), - HIGH( DEFAULT.value * 4 ), + DISABLED( 0 ), + LOWEST( DEFAULT.value / 8 ), LOW( DEFAULT.value / 4 ), - LOWEST( DEFAULT.value / 8 ); + HIGH( DEFAULT.value * 4 ), + HIGHEST( DEFAULT.value * 8 ); public final int value; @@ -24,6 +16,7 @@ public class BestiaryInfo { } //TODO default themes + // public static final EnvironmentListConfig DEFAULT_THEME_FIRE = new EnvironmentListConfig( // new TargetEnvironment.TargetBiomeGroup( "desert", BestiaryInfo.BASE_WEIGHT_COMMON ), // new TargetEnvironment.TargetBiomeGroup( "savanna", BestiaryInfo.BASE_WEIGHT_COMMON ), diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java index 517b454..a3b84c6 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java @@ -10,6 +10,7 @@ import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.monster.AbstractSkeletonEntity; import net.minecraft.entity.monster.CreeperEntity; +import net.minecraft.entity.monster.SpiderEntity; import net.minecraft.util.math.BlockPos; import net.minecraft.world.World; import net.minecraftforge.common.ForgeSpawnEggItem; @@ -71,10 +72,10 @@ public class MobFamily { // "Flying", "Hardened", "Sticky", "Volatile" // ); - // public static final MobFamily SPIDER = new MobFamily<>( - // "Spider", "spiders", 0x342D27, new EntityType[] { EntityType.SPIDER }, - // "Baby", "Desert", "Flying", "Giant", "Hungry", "Mother", "Pale", "Poison", "Web", "Witch" - // ); + public static final MobFamily SPIDER = new MobFamily<>( + "Spider", "spiders", 0x342D27, new EntityType[] { EntityType.SPIDER }, + "Baby", "Desert", "Flying", "Giant", "Hungry", "Mother", "Pale", "Poison", "Web", "Witch" + ); // public static final MobFamily CAVE_SPIDER = new MobFamily<>( // "CaveSpider", "cave spiders", 0x0C424E, new EntityType[] { EntityType.CAVE_SPIDER }, // "Baby", "Flying", "Mother", "Web", "Witch" diff --git a/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java b/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java new file mode 100644 index 0000000..53ac18b --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java @@ -0,0 +1,184 @@ +package fathertoast.specialmobs.common.entity; + +import net.minecraft.enchantment.EnchantmentHelper; +import net.minecraft.entity.CreatureAttribute; +import net.minecraft.entity.Entity; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.*; +import net.minecraft.potion.EffectInstance; +import net.minecraft.potion.EffectType; +import net.minecraft.potion.Effects; +import net.minecraft.util.DamageSource; +import net.minecraft.world.Difficulty; +import net.minecraft.world.World; + +import java.util.ArrayList; +import java.util.Random; + +public final class MobHelper { + + /** Pool of effects to choose from for plague-type mobs to apply on hit. Duration is a multiplier. */ + private static final EffectInstance[] PLAGUE_EFFECTS = { + new EffectInstance( Effects.MOVEMENT_SLOWDOWN, 2, 0 ), + new EffectInstance( Effects.DIG_SLOWDOWN, 2, 1 ), + new EffectInstance( Effects.BLINDNESS, 1, 0 ), + new EffectInstance( Effects.HUNGER, 2, 0 ), + new EffectInstance( Effects.WEAKNESS, 1, 0 ), + new EffectInstance( Effects.POISON, 1, 0 ), + new EffectInstance( Effects.CONFUSION, 2, 0 ) // Keep this option last for easy disable (by config) + }; + + /** Pool of effects to choose from for witch-spider-type mobs to apply on hit. Duration is a multiplier. */ + private static final EffectInstance[] WITCH_EFFECTS = { + new EffectInstance( Effects.MOVEMENT_SLOWDOWN, 1, 1 ), + new EffectInstance( Effects.DIG_SLOWDOWN, 2, 1 ), + new EffectInstance( Effects.DAMAGE_RESISTANCE, 1, -3 ), + new EffectInstance( Effects.BLINDNESS, 1, 0 ), + new EffectInstance( Effects.HUNGER, 2, 0 ), + new EffectInstance( Effects.WEAKNESS, 1, 0 ), + new EffectInstance( Effects.WITHER, 1, 0 ), + new EffectInstance( Effects.LEVITATION, 1, 1 ), + new EffectInstance( Effects.POISON, 1, 0 ) // Keep this option last for easy disable (by cave spiders) + }; + + /** @return True if the damage source can deal normal damage to vampire-type mobs (e.g., wooden or smiting weapons). */ + public static boolean isDamageSourceIneffectiveAgainstVampires( DamageSource source ) { + if( source != null ) { + if( source.isBypassMagic() || source.isBypassInvul() ) return false; + + final Entity attacker = source.getEntity(); + if( attacker instanceof LivingEntity ) { + final ItemStack weapon = ((LivingEntity) attacker).getMainHandItem(); + return !isWoodenTool( weapon ) && !hasSmite( weapon ); + } + } + return true; + } + + /** @return True if the given item is made of wood. */ + private static boolean isWoodenTool( ItemStack item ) { + //TODO Consider Tinkers compat - striking component must be wood + return !item.isEmpty() && item.getItem() instanceof TieredItem && ((TieredItem) item.getItem()).getTier() == ItemTier.WOOD; + } + + /** @return True if the given item deals bonus damage against undead. */ + private static boolean hasSmite( ItemStack item ) { + //TODO Consider Tinkers compat if this doesn't already work - must have smite modifier + return EnchantmentHelper.getDamageBonus( item, CreatureAttribute.UNDEAD ) > 0.0F; + } + + /** + * Reduces the target's life directly. Will not reduce health below 1. + * + * @param target The entity to cause life loss on. + * @param amount The amount of life loss to inflict. + */ + public static void causeLifeLoss( LivingEntity target, float amount ) { + final float currentHealth = target.getHealth(); + if( currentHealth > 1.0F ) { + target.setHealth( Math.max( 1.0F, currentHealth - amount ) ); + } + } + + /** + * Reduces the target's life directly and heals the attacker by the amount lost. + * Will not reduce health below 1. + * + * @param attacker The entity causing the life steal. + * @param target The entity to steal life from. + * @param amount The amount of life steal to inflict. + */ + public static void stealLife( LivingEntity attacker, LivingEntity target, float amount ) { + final float currentHealth = target.getHealth(); + if( currentHealth > 1.0F ) { + final float newHealth = Math.max( 1.0F, currentHealth - amount ); + target.setHealth( newHealth ); + attacker.heal( currentHealth - newHealth ); + } + } + + /** + * Removes one random food item from the player's inventory and returns it. + * Returns an empty stack if there is no food in the player's inventory. + * + * @param player The player to steal from. + * @return The item removed from the player's inventory. + */ + public static ItemStack stealRandomFood( PlayerEntity player ) { + final ArrayList foodSlots = new ArrayList<>(); + for( int slot = 0; slot < player.inventory.getContainerSize(); slot++ ) { + final ItemStack item = player.inventory.getItem( slot ); + if( !item.isEmpty() && item.getItem().getFoodProperties() != null ) foodSlots.add( slot ); + } + if( !foodSlots.isEmpty() ) { + return player.inventory.removeItem( foodSlots.get( player.getRandom().nextInt( foodSlots.size() ) ), 1 ); + } + return ItemStack.EMPTY; + } + + /** + * Steals a non-harmful potion effect from a target and applies it to the attacker with a minimum duration of 5 seconds. + * + * @param attacker The entity stealing an effect. + * @param target The entity to steal an effect from. + */ + public static void stealBuffEffect( LivingEntity attacker, LivingEntity target ) { + if( !attacker.level.isClientSide() ) { + for( EffectInstance potion : target.getActiveEffects() ) { + if( potion != null && potion.getEffect().getCategory() != EffectType.HARMFUL && potion.getAmplifier() >= 0 ) { + target.removeEffect( potion.getEffect() ); + attacker.addEffect( new EffectInstance( potion.getEffect(), + Math.max( potion.getDuration(), 200 ), potion.getAmplifier() ) ); + return; + } + } + } + } + + /** @return The base debuff duration. */ + public static int getDebuffDuration( Difficulty difficulty ) { + switch( difficulty ) { + case PEACEFUL: + case EASY: + return 60; + case NORMAL: + return 140; + default: + return 300; + } + } + + /** + * Makes a random potion effect for a plague-type mob to apply on hit. + * + * @param random The rng to draw from. + * @param world The context. + * @return A newly created potion effect instance we can apply to an entity. + */ + public static EffectInstance nextPlagueEffect( Random random, World world ) { + final int duration = MobHelper.getDebuffDuration( world.getDifficulty() ); + + //EffectInstance potion = POTIONS_PLAGUE[random.nextInt( POTIONS_PLAGUE.length - (Config.get().GENERAL.DISABLE_NAUSEA ? 1 : 0) )]; TODO config + EffectInstance potion = PLAGUE_EFFECTS[random.nextInt( PLAGUE_EFFECTS.length )]; + return new EffectInstance( potion.getEffect(), duration * potion.getDuration(), potion.getAmplifier() ); + } + + /** + * Makes a random potion effect for a witch-spider-type mob to apply on hit, + * optionally including poison in the effect pool. + *

+ * For example, witch cave spiders do not include poison in the pool because they apply poison already. + * + * @param random The rng to draw from. + * @param world The context. + * @param includePoison Whether to include poison in the potion pool. + * @return A newly created potion effect instance we can apply to an entity. + */ + public static EffectInstance nextWitchSpiderEffect( Random random, World world, boolean includePoison ) { + final int duration = MobHelper.getDebuffDuration( world.getDifficulty() ); + + EffectInstance potion = WITCH_EFFECTS[random.nextInt( WITCH_EFFECTS.length - (includePoison ? 0 : 1) )]; + return new EffectInstance( potion.getEffect(), duration * potion.getDuration(), potion.getAmplifier() ); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/creeper/DrowningCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/DrowningCreeperEntity.java index 4725775..db8f22d 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/DrowningCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/DrowningCreeperEntity.java @@ -23,6 +23,7 @@ import net.minecraft.util.Direction; import net.minecraft.util.ResourceLocation; import net.minecraft.util.math.BlockPos; import net.minecraft.world.Explosion; +import net.minecraft.world.IServerWorld; import net.minecraft.world.World; import net.minecraft.world.spawner.WorldEntitySpawner; @@ -146,6 +147,7 @@ public class DrowningCreeperEntity extends _SpecialCreeperEntity { /** Helper method to simplify spawning pufferfish. */ private void spawnPufferfish( BlockPos pos ) { + if( !(level instanceof IServerWorld) ) return; final PufferfishEntity lePuffPuff = EntityType.PUFFERFISH.create( level ); if( lePuffPuff != null ) { lePuffPuff.setPos( pos.getX() + 0.5, pos.getY() + 0.5, pos.getZ() + 0.5 ); diff --git a/src/main/java/fathertoast/specialmobs/common/entity/creeper/GravelCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/GravelCreeperEntity.java index e0212f5..cb01b26 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/GravelCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/GravelCreeperEntity.java @@ -71,7 +71,7 @@ public class GravelCreeperEntity extends _SpecialCreeperEntity { if( !explosion.initializeExplosion() ) return; explosion.finalizeExplosion(); - if( explosionMode == Explosion.Mode.NONE ) return; + if( explosionMode == Explosion.Mode.NONE || level.isClientSide() ) return; final float throwPower = explosionPower + 4.0F; final int count = (int) Math.ceil( throwPower * throwPower * 3.5F ); @@ -94,14 +94,6 @@ public class GravelCreeperEntity extends _SpecialCreeperEntity { } } - private static final ResourceLocation[] TEXTURES = { - GET_TEXTURE_PATH( "gravel" ) - }; - - /** @return All default textures for this entity. */ - @Override - public ResourceLocation[] getDefaultTextures() { return TEXTURES; } - /** @return Attempts to damage this entity; returns true if the hit was successful. */ @Override public boolean hurt( DamageSource source, float amount ) { @@ -111,4 +103,12 @@ public class GravelCreeperEntity extends _SpecialCreeperEntity { } return super.hurt( source, amount ); } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "gravel" ) + }; + + /** @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/creeper/SplittingCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/SplittingCreeperEntity.java index 49cf6ca..796b0a7 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/SplittingCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/SplittingCreeperEntity.java @@ -8,6 +8,7 @@ import fathertoast.specialmobs.common.util.References; import fathertoast.specialmobs.datagen.loot.LootTableBuilder; import mcp.MethodsReturnNonnullByDefault; import net.minecraft.entity.EntityType; +import net.minecraft.entity.ILivingEntityData; import net.minecraft.entity.LivingEntity; import net.minecraft.entity.SpawnReason; import net.minecraft.entity.ai.attributes.AttributeModifierMap; @@ -19,6 +20,7 @@ import net.minecraft.util.SoundEvents; import net.minecraft.world.IServerWorld; import net.minecraft.world.World; +import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault @@ -72,36 +74,47 @@ public class SplittingCreeperEntity extends _SpecialCreeperEntity { protected void makeVariantExplosion( float explosionPower ) { ExplosionHelper.explode( this, explosionPower, false, false ); + if( !(level instanceof IServerWorld) ) return; + final int babiesToSpawn = extraBabies + (int) (explosionPower * explosionPower) / 2; + ILivingEntityData groupData = null; for( int i = 0; i < babiesToSpawn; i++ ) { - final MiniCreeperEntity baby = MiniCreeperEntity.ENTITY_TYPE.get().create( level ); - if( baby == null ) continue; - - baby.copyPosition( this ); - baby.yHeadRot = yRot; - baby.yBodyRot = yRot; - baby.finalizeSpawn( (IServerWorld) level, level.getCurrentDifficultyAt( blockPosition() ), SpawnReason.MOB_SUMMONED, null, null ); - baby.setTarget( getTarget() ); - if( isPowered() ) baby.getEntityData().set( DATA_IS_POWERED, true ); - - baby.setDeltaMovement( - (random.nextDouble() - 0.5) * explosionPower / 3.0, - 0.3 + 0.3 * random.nextDouble(), // Used to cause floor clip bug; remove if it happens again - (random.nextDouble() - 0.5) * explosionPower / 3.0 ); - baby.setOnGround( false ); - - level.addFreshEntity( baby ); + groupData = spawnBaby( explosionPower / 3.0F, groupData ); } playSound( SoundEvents.EGG_THROW, 1.0F, 2.0F / (random.nextFloat() * 0.4F + 0.8F) ); } + /** Helper method to simplify spawning babies. */ + @Nullable + private ILivingEntityData spawnBaby( float speed, @Nullable ILivingEntityData groupData ) { + final MiniCreeperEntity baby = MiniCreeperEntity.ENTITY_TYPE.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() ); + if( isPowered() ) baby.getEntityData().set( DATA_IS_POWERED, true ); + + baby.setDeltaMovement( + (random.nextDouble() - 0.5) * speed, + 0.3 + 0.3 * random.nextDouble(), // Used to cause floor clip bug; remove if it happens again + (random.nextDouble() - 0.5) * speed ); + 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_EXTRA_BABIES, (byte) extraBabies ); } - /** Override to load data from this entity's NBT data. */ + /** Override to load data from this entity's NBT data. */ @Override public void readVariantSaveData( CompoundNBT saveTag ) { if( saveTag.contains( References.TAG_EXTRA_BABIES, References.NBT_TYPE_NUMERICAL ) ) 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 c7290f0..ab30294 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java @@ -81,6 +81,19 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob< /** Override to change this entity's AI goals. */ protected void registerVariantGoals() { } + /** Called to melee attack the target. */ + @Override + public boolean doHurtTarget( Entity target ) { + if( super.doHurtTarget( target ) ) { + onVariantAttack( target ); + return true; + } + return false; + } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + protected void onVariantAttack( Entity target ) { } + /** Called to perform this creeper's explosion 'attack'. */ @Override protected void explodeCreeper() { @@ -133,7 +146,7 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob< /** 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. */ + /** Override to load data from this entity's NBT data. */ public void readVariantSaveData( CompoundNBT saveTag ) { } @@ -187,6 +200,7 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob< if( explodesWhileBurning() ) clearFire(); } + /** 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 ) { @@ -289,16 +303,6 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob< getSpecialData().tick(); } - // // Called to attack the target. - // @Override - // public boolean attackEntityAsMob( Entity target ) { - // if( super.attackEntityAsMob( target ) ) { - // onTypeAttack( target ); - // return true; - // } - // return false; - // } - /** @return The eye height of this entity when standing. */ @Override protected float getStandingEyeHeight( Pose pose, EntitySize size ) { diff --git a/src/main/java/fathertoast/specialmobs/common/entity/skeleton/_SpecialSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/_SpecialSkeletonEntity.java index 58bb2a7..b027e1b 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/skeleton/_SpecialSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/_SpecialSkeletonEntity.java @@ -97,10 +97,23 @@ public class _SpecialSkeletonEntity extends AbstractSkeletonEntity implements IS // TODO variant shooting + /** Called to melee attack the target. */ + @Override + public boolean doHurtTarget( Entity target ) { + if( super.doHurtTarget( target ) ) { + onVariantAttack( target ); + return true; + } + return false; + } + + /** 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. */ + /** Override to load data from this entity's NBT data. */ public void readVariantSaveData( CompoundNBT saveTag ) { } @@ -120,12 +133,7 @@ public class _SpecialSkeletonEntity extends AbstractSkeletonEntity implements IS entityData.define( IS_BABY, false ); } - @Override - protected void populateDefaultEquipmentSlots( DifficultyInstance difficulty ) { - super.populateDefaultEquipmentSlots( difficulty ); - setItemSlot( EquipmentSlotType.MAINHAND, new ItemStack( Items.BOW ) ); - } - + /** 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 ) { @@ -134,6 +142,15 @@ public class _SpecialSkeletonEntity extends AbstractSkeletonEntity implements IS return groupData; } + /** Called during spawn finalization to set starting equipment. */ + @Override + protected void populateDefaultEquipmentSlots( DifficultyInstance difficulty ) { + super.populateDefaultEquipmentSlots( difficulty ); + if( random.nextDouble() < 0.05 ) { //TODO config + setItemSlot( EquipmentSlotType.MAINHAND, new ItemStack( Items.IRON_SWORD ) ); + } + } + /** Called to change */ @Override public void reassessWeaponGoal() { @@ -205,7 +222,7 @@ public class _SpecialSkeletonEntity extends AbstractSkeletonEntity implements IS public void setBaby( boolean value ) { getEntityData().set( IS_BABY, value ); if( level != null && !level.isClientSide ) { - ModifiableAttributeInstance attributeInstance = getAttribute( Attributes.MOVEMENT_SPEED ); + final ModifiableAttributeInstance attributeInstance = getAttribute( Attributes.MOVEMENT_SPEED ); //noinspection ConstantConditions attributeInstance.removeModifier( BABY_SPEED_BOOST ); if( value ) { @@ -279,16 +296,6 @@ public class _SpecialSkeletonEntity extends AbstractSkeletonEntity implements IS getSpecialData().tick(); } - // // Called to attack the target. - // @Override - // public boolean attackEntityAsMob( Entity target ) { - // if( super.attackEntityAsMob( target ) ) { - // onTypeAttack( target ); - // return true; - // } - // return false; - // } - /** @return The eye height of this entity when standing. */ @Override protected float getStandingEyeHeight( Pose pose, EntitySize size ) { diff --git a/src/main/java/fathertoast/specialmobs/common/entity/spider/BabySpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/BabySpiderEntity.java new file mode 100644 index 0000000..064cf49 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/BabySpiderEntity.java @@ -0,0 +1,68 @@ +package fathertoast.specialmobs.common.entity.spider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +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.item.Items; +import net.minecraft.world.World; +import net.minecraftforge.fml.RegistryObject; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class BabySpiderEntity extends _SpecialSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.TypeHolder + public static RegistryObject> ENTITY_TYPE; + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 0.6F, 0.4F ); + return new BestiaryInfo( 0xFFC0CB, BestiaryInfo.BaseWeight.DISABLED ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialSpiderEntity.createAttributes() ) + .multAttribute( Attributes.MAX_HEALTH, 0.25 ) + .addAttribute( Attributes.ATTACK_DAMAGE, -1.0 ) + .multAttribute( Attributes.MOVEMENT_SPEED, 1.3 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Baby Spider", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + loot.addCommonDrop( "common", Items.STRING, 1 ); + } + + @SpecialMob.Constructor + public BabySpiderEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setBaseScale( 0.4F ); + getSpecialData().rangedAttackDamage -= 1.0F; + getSpecialData().rangedAttackMaxRange = 0.0F; + xpReward = 1; + } + + + //--------------- Variant-Specific Implementations ---------------- + + // None +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/spider/DesertSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/DesertSpiderEntity.java new file mode 100644 index 0000000..0ed5573 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/DesertSpiderEntity.java @@ -0,0 +1,91 @@ +package fathertoast.specialmobs.common.entity.spider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +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.potion.EffectInstance; +import net.minecraft.potion.Effects; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class DesertSpiderEntity extends _SpecialSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 0.95F, 0.8F ); + return new BestiaryInfo( 0xE6DDAC ); + //TODO theme - desert + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialSpiderEntity.createAttributes() ) + .addAttribute( Attributes.MAX_HEALTH, 4.0 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Desert Spider", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.LEATHER ); + } + + @SpecialMob.Constructor + public DesertSpiderEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setBaseScale( 0.8F ); + xpReward += 2; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + if( target instanceof LivingEntity ) { + final LivingEntity livingTarget = (LivingEntity) target; + final int duration = MobHelper.getDebuffDuration( level.getDifficulty() ); + + if( true ) { //TODO config + livingTarget.addEffect( new EffectInstance( Effects.CONFUSION, duration, 0 ) ); + } + livingTarget.addEffect( new EffectInstance( Effects.BLINDNESS, duration, 0 ) ); + + livingTarget.addEffect( new EffectInstance( Effects.MOVEMENT_SLOWDOWN, duration, 2 ) ); + livingTarget.addEffect( new EffectInstance( Effects.DAMAGE_RESISTANCE, duration, -3 ) ); // 40% inc damage taken + } + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "desert" ), + GET_TEXTURE_PATH( "desert_eyes" ) + }; + + /** @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/spider/FlyingSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/FlyingSpiderEntity.java new file mode 100644 index 0000000..4f29939 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/FlyingSpiderEntity.java @@ -0,0 +1,77 @@ +package fathertoast.specialmobs.common.entity.spider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.ai.SpecialLeapAtTargetGoal; +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.item.Items; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class FlyingSpiderEntity extends _SpecialSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0x6388B2 ); + //TODO theme - mountain + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialSpiderEntity.createAttributes() ) + .multAttribute( Attributes.MOVEMENT_SPEED, 1.2 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Flying Spider", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.FEATHER ); + } + + @SpecialMob.Constructor + public FlyingSpiderEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setFallDamageMultiplier( 0.0F ); + xpReward += 2; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + goalSelector.addGoal( 3, new SpecialLeapAtTargetGoal( + this, 10, 6.0F, 12.0F, 2.0F, 2.0F ) ); + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "flying" ), + GET_TEXTURE_PATH( "flying_eyes" ) + }; + + /** @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/spider/GiantSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/GiantSpiderEntity.java new file mode 100644 index 0000000..23ab55f --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/GiantSpiderEntity.java @@ -0,0 +1,63 @@ +package fathertoast.specialmobs.common.entity.spider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +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.item.Items; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class GiantSpiderEntity extends _SpecialSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 1.9F, 1.3F ); + return new BestiaryInfo( 0xA80E0E ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialSpiderEntity.createAttributes() ) + .addAttribute( Attributes.MAX_HEALTH, 16.0 ) + .addAttribute( Attributes.ATTACK_DAMAGE, 2.0 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Giant Spider", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addGuaranteedDrop( "base", Items.STRING, 2 ); + } + + @SpecialMob.Constructor + public GiantSpiderEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setBaseScale( 1.5F ); + getSpecialData().rangedAttackDamage += 1.0F; + xpReward += 1; + } + + + //--------------- Variant-Specific Implementations ---------------- + + // None +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/spider/HungrySpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/HungrySpiderEntity.java new file mode 100644 index 0000000..eb5ad34 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/HungrySpiderEntity.java @@ -0,0 +1,152 @@ +package fathertoast.specialmobs.common.entity.spider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +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.AttributeModifier; +import net.minecraft.entity.ai.attributes.AttributeModifierMap; +import net.minecraft.entity.ai.attributes.Attributes; +import net.minecraft.entity.ai.attributes.ModifiableAttributeInstance; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.Food; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundEvents; +import net.minecraft.world.World; +import net.minecraftforge.event.ForgeEventFactory; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.UUID; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class HungrySpiderEntity extends _SpecialSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 1.9F, 1.3F ); + return new BestiaryInfo( 0x799C65 ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialSpiderEntity.createAttributes() ) + .addAttribute( Attributes.MAX_HEALTH, 4.0 ) + .addAttribute( Attributes.ATTACK_DAMAGE, -1.0 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Hungry Spider", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.BONE ); + loot.addUncommonDrop( "uncommon", Items.APPLE, Items.BEETROOT, Items.ROTTEN_FLESH, Items.CHICKEN, Items.RABBIT, Items.COOKIE ); + } + + @SpecialMob.Constructor + public HungrySpiderEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setBaseScale( 1.5F ); + getSpecialData().setRegenerationTime( 40 ); + getSpecialData().rangedAttackMaxRange = 0.0F; + xpReward += 2; + } + + + //--------------- 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 health boost to apply from max health stacks. */ + private static final AttributeModifier HEALTH_BOOST = new AttributeModifier( UUID.fromString( "D22A70EF-7C71-4BC5-8B23-7045728FD84F" ), + "Feeding health boost", 2.0, AttributeModifier.Operation.ADDITION ); + + /** The level of increased attack damage gained. */ + private int growthLevel; + /** The level of increased max health gained. */ + private int maxHealthStacks; + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + if( !level.isClientSide() && target instanceof PlayerEntity && ForgeEventFactory.getMobGriefingEvent( level, this ) ) { + final ItemStack food = MobHelper.stealRandomFood( (PlayerEntity) target ); + if( !food.isEmpty() ) { + final float previousHealth = getMaxHealth(); + if( maxHealthStacks < 32 ) maxHealthStacks++; + if( growthLevel < 7 ) growthLevel++; + updateFeedingLevels(); + setHealth( getHealth() + getMaxHealth() - previousHealth ); + + final Food foodStats = food.getItem().getFoodProperties(); + heal( Math.max( foodStats == null ? 0.0F : foodStats.getNutrition(), 1.0F ) ); + playSound( SoundEvents.PLAYER_BURP, 0.5F, random.nextFloat() * 0.1F + 0.9F ); + } + } + } + + private void updateFeedingLevels() { + if( level != null && !level.isClientSide ) { + final ModifiableAttributeInstance health = getAttribute( Attributes.MAX_HEALTH ); + final ModifiableAttributeInstance damage = getAttribute( Attributes.ATTACK_DAMAGE ); + //noinspection ConstantConditions + health.removeModifier( HEALTH_BOOST.getId() ); + //noinspection ConstantConditions + damage.removeModifier( DAMAGE_BOOST.getId() ); + if( maxHealthStacks > 0 ) { + // Health, in particular, must be permanent to avoid health getting throttled on reload + health.addPermanentModifier( new AttributeModifier( HEALTH_BOOST.getId(), HEALTH_BOOST.getName(), + HEALTH_BOOST.getAmount() * maxHealthStacks, HEALTH_BOOST.getOperation() ) ); + } + 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_HEALTH_STACKS, (byte) maxHealthStacks ); + 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_HEALTH_STACKS, References.NBT_TYPE_NUMERICAL ) ) + maxHealthStacks = saveTag.getByte( References.TAG_HEALTH_STACKS ); + 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( "hungry" ), + GET_TEXTURE_PATH( "hungry_eyes" ) + }; + + /** @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/spider/MotherSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/MotherSpiderEntity.java new file mode 100644 index 0000000..01939be --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/MotherSpiderEntity.java @@ -0,0 +1,156 @@ +package fathertoast.specialmobs.common.entity.spider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +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.ILivingEntityData; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.SpawnReason; +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.DamageSource; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundEvents; +import net.minecraft.world.IServerWorld; +import net.minecraft.world.World; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class MotherSpiderEntity extends _SpecialSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 1.8F, 1.2F ); + return new BestiaryInfo( 0xB300B3 ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialSpiderEntity.createAttributes() ) + .addAttribute( Attributes.MAX_HEALTH, 16.0 ) + .addAttribute( Attributes.ATTACK_DAMAGE, 3.0 ) + .addAttribute( Attributes.ARMOR, 6.0 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Mother Spider", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addUncommonDrop( "uncommon", Items.SPIDER_SPAWN_EGG ); + } + + @SpecialMob.Constructor + public MotherSpiderEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setBaseScale( 1.4F ); + getSpecialData().setRegenerationTime( 30 ); + getSpecialData().rangedAttackDamage += 1.5F; + xpReward += 1; + babies = 2 + random.nextInt( 3 ); + extraBabies = 2 + random.nextInt( 3 ); + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** The number of babies spawned on death. */ + private int babies; + /** The number of extra babies that can be spawned from hits. */ + private int extraBabies; + + /** @return Attempts to damage this entity; returns true if the hit was successful. */ + @Override + public boolean hurt( DamageSource source, float amount ) { + if( super.hurt( source, amount ) ) { + // Spawn babies when damaged + if( extraBabies > 0 && amount > 1.0F && level instanceof IServerWorld && random.nextFloat() < 0.33F ) { + extraBabies--; + spawnBaby( 0.66F, null ); + } + return true; + } + return false; + } + + /** Called each tick to update this entity while it's dead. */ + @Override + protected void tickDeath() { + if( deathTime == 19 && level instanceof IServerWorld ) { // At 19, the entity will be immediately removed upon call to super method + // Spawn babies on death + final int babiesToSpawn = babies + extraBabies; + ILivingEntityData groupData = null; + for( int i = 0; i < babiesToSpawn; i++ ) { + groupData = spawnBaby( 0.33F, groupData ); + } + playSound( SoundEvents.EGG_THROW, 1.0F, 2.0F / (random.nextFloat() * 0.4F + 0.8F) ); + } + + super.tickDeath(); + } + + /** Helper method to simplify spawning babies. */ + @Nullable + private ILivingEntityData spawnBaby( float speed, @Nullable ILivingEntityData groupData ) { + final BabySpiderEntity baby = BabySpiderEntity.ENTITY_TYPE.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( + (random.nextDouble() - 0.5) * speed, + 0.2 + 0.5 * random.nextDouble(), // Used to cause floor clip bug; remove if it happens again + (random.nextDouble() - 0.5) * speed ); + 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( "mother" ), + GET_TEXTURE_PATH( "mother_eyes" ) + }; + + /** @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/spider/PaleSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/PaleSpiderEntity.java new file mode 100644 index 0000000..e81cc6e --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/PaleSpiderEntity.java @@ -0,0 +1,84 @@ +package fathertoast.specialmobs.common.entity.spider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +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.potion.EffectInstance; +import net.minecraft.potion.Effects; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class PaleSpiderEntity extends _SpecialSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0xDED4C6 ); + //TODO theme - ice + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialSpiderEntity.createAttributes() ) + .addAttribute( Attributes.ARMOR, 15.0 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Pale Spider", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addSemicommonDrop( "semicommon", Items.FERMENTED_SPIDER_EYE ); + } + + @SpecialMob.Constructor + public PaleSpiderEntity( EntityType entityType, World world ) { + super( entityType, world ); + xpReward += 1; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + if( target instanceof LivingEntity ) { + final LivingEntity livingTarget = (LivingEntity) target; + final int duration = MobHelper.getDebuffDuration( level.getDifficulty() ); + + livingTarget.addEffect( new EffectInstance( Effects.WEAKNESS, duration, 0 ) ); + livingTarget.addEffect( new EffectInstance( Effects.DIG_SLOWDOWN, duration, 1 ) ); + } + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "pale" ), + GET_TEXTURE_PATH( "pale_eyes" ) + }; + + /** @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/spider/PoisonSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/PoisonSpiderEntity.java new file mode 100644 index 0000000..0c1dde2 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/PoisonSpiderEntity.java @@ -0,0 +1,79 @@ +package fathertoast.specialmobs.common.entity.spider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +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.AttributeModifierMap; +import net.minecraft.item.Items; +import net.minecraft.potion.EffectInstance; +import net.minecraft.potion.Effects; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class PoisonSpiderEntity extends _SpecialSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0x0C424E ); + //TODO theme - forest + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return _SpecialSpiderEntity.createAttributes(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Venomous Spider", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addUncommonDrop( "uncommon", Items.SPIDER_EYE ); + } + + @SpecialMob.Constructor + public PoisonSpiderEntity( EntityType entityType, World world ) { + super( entityType, world ); + xpReward += 1; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + if( target instanceof LivingEntity ) { + final LivingEntity livingTarget = (LivingEntity) target; + final int duration = MobHelper.getDebuffDuration( level.getDifficulty() ); + + livingTarget.addEffect( new EffectInstance( Effects.POISON, duration ) ); + } + } + + private static final ResourceLocation[] TEXTURES = { + new ResourceLocation( "textures/entity/spider/cave_spider.png" ), + new ResourceLocation( "textures/entity/spider_eyes.png" ) + }; + + /** @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/spider/WebSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/WebSpiderEntity.java new file mode 100644 index 0000000..dca3b87 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/WebSpiderEntity.java @@ -0,0 +1,119 @@ +package fathertoast.specialmobs.common.entity.spider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +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.block.Blocks; +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.monster.SpiderEntity; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.util.DamageSource; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.BlockPos; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class WebSpiderEntity extends _SpecialSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0xE7E7E7, BestiaryInfo.BaseWeight.LOW ); + //TODO theme - forest + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialSpiderEntity.createAttributes() ) + .addAttribute( Attributes.MAX_HEALTH, 4.0 ) + .multAttribute( Attributes.MOVEMENT_SPEED, 1.2 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Weaver", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addUncommonDrop( "uncommon", Blocks.COBWEB ); + } + + @SpecialMob.Constructor + public WebSpiderEntity( EntityType entityType, World world ) { + super( entityType, world ); + xpReward += 2; + webCount = 2 + random.nextInt( 5 ); + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** The number of cobwebs this spider can place. */ + private int webCount; + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + if( !level.isClientSide() && webCount > 0 && target instanceof LivingEntity && !(target instanceof SpiderEntity) ) { + final BlockPos pos = target.blockPosition(); + if( !tryPlaceWeb( pos ) && target.getBbHeight() > 1.0F ) { + tryPlaceWeb( pos.above() ); + } + } + } + + /** Called when this entity dies to add drops regardless of loot table. */ + @Override + protected void dropCustomDeathLoot( DamageSource source, int looting, boolean killedByPlayer ) { + super.dropCustomDeathLoot( source, looting, killedByPlayer ); + tryPlaceWeb( blockPosition() ); + } + + /** @return Attempts to place a cobweb at the given position and returns true if successful. */ + private boolean tryPlaceWeb( BlockPos pos ) { + if( level.getBlockState( pos ).getMaterial().isReplaceable() ) { + level.setBlock( pos, Blocks.COBWEB.defaultBlockState(), References.SET_BLOCK_FLAGS ); + webCount--; + return true; + } + return false; + } + + /** Override to save data to this entity's NBT data. */ + @Override + public void addVariantSaveData( CompoundNBT saveTag ) { + saveTag.putByte( References.TAG_AMMO, (byte) webCount ); + } + + /** Override to load data from this entity's NBT data. */ + @Override + public void readVariantSaveData( CompoundNBT saveTag ) { + if( saveTag.contains( References.TAG_AMMO, References.NBT_TYPE_NUMERICAL ) ) + webCount = saveTag.getByte( References.TAG_AMMO ); + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "web" ), + GET_TEXTURE_PATH( "web_eyes" ) + }; + + /** @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/spider/WitchSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/WitchSpiderEntity.java new file mode 100644 index 0000000..0d3d8cb --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/WitchSpiderEntity.java @@ -0,0 +1,87 @@ +package fathertoast.specialmobs.common.entity.spider; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +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.potion.EffectInstance; +import net.minecraft.potion.EffectType; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class WitchSpiderEntity extends _SpecialSpiderEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0xDD0E0E, BestiaryInfo.BaseWeight.LOW ); + //TODO theme - forest + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialSpiderEntity.createAttributes() ) + .addAttribute( Attributes.ARMOR, 15.0 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Witch Spider", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addLootTable( "common", EntityType.WITCH.getDefaultLootTable() ); + } + + @SpecialMob.Constructor + public WitchSpiderEntity( EntityType entityType, World world ) { + super( entityType, world ); + xpReward += 2; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + if( target instanceof LivingEntity ) { + ((LivingEntity) target).addEffect( MobHelper.nextWitchSpiderEffect( random, level, true ) ); + MobHelper.stealBuffEffect( this, (LivingEntity) target ); + } + } + + /** @return True if the effect can be applied to this entity. */ + @Override + public boolean canBeAffected( EffectInstance effect ) { + // Witch spider is immune to debuffs + return effect.getEffect().getCategory() != EffectType.HARMFUL && super.canBeAffected( effect ); + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "witch" ), + GET_TEXTURE_PATH( "witch_eyes" ) + }; + + /** @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/spider/_SpecialSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/_SpecialSpiderEntity.java new file mode 100644 index 0000000..2f37d4c --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/_SpecialSpiderEntity.java @@ -0,0 +1,231 @@ +package fathertoast.specialmobs.common.entity.spider; + +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.SpiderEntity; +import net.minecraft.entity.player.PlayerEntity; +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.ResourceLocation; +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 _SpecialSpiderEntity extends SpiderEntity implements ISpecialMob<_SpecialSpiderEntity> { + + //--------------- 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 SpiderEntity.createAttributes(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Spider", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void addBaseLoot( LootTableBuilder loot ) { + loot.addLootTable( "main", EntityType.SPIDER.getDefaultLootTable() ); + } + + @SpecialMob.Constructor + public _SpecialSpiderEntity( EntityType entityType, World world ) { + super( entityType, world ); + specialData.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 to melee attack the target. */ + @Override + public boolean doHurtTarget( Entity target ) { + if( super.doHurtTarget( target ) ) { + onVariantAttack( target ); + return true; + } + return false; + } + + /** 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( _SpecialSpiderEntity.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 ); + } + + /** 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<_SpecialSpiderEntity> specialData; + + /** @return This mob's special data. */ + @Override + public SpecialMobData<_SpecialSpiderEntity> 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 + "spider/" + type + ".png" ); + } + + private static final ResourceLocation[] TEXTURES = { + new ResourceLocation( "textures/entity/spider/spider.png" ), + new ResourceLocation( "textures/entity/spider_eyes.png" ) + }; + + /** @return All default textures for this entity. */ + @Override + public ResourceLocation[] getDefaultTextures() { return TEXTURES; } + + + //TODO--------------- 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 specialData.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( specialData.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 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 diff --git a/src/main/java/fathertoast/specialmobs/common/util/References.java b/src/main/java/fathertoast/specialmobs/common/util/References.java index 0a3906d..25da97a 100644 --- a/src/main/java/fathertoast/specialmobs/common/util/References.java +++ b/src/main/java/fathertoast/specialmobs/common/util/References.java @@ -62,7 +62,15 @@ public final class References { public static final String TAG_IS_BABY = "IsBaby"; // Spawner mobs TODO drowning creeper pufferfish cap? - public static final String TAG_EXTRA_BABIES = "ExtraBabies"; // Splitting Creepers + public static final String TAG_BABIES = "Babies"; // Mother Spd + public static final String TAG_EXTRA_BABIES = "ExtraBabies"; // Splitting Crp, Mother Spd + + // Growing mobs + public static final String TAG_GROWTH_LEVEL = "GrowthLevel"; // Hungry Spd + public static final String TAG_HEALTH_STACKS = "HealthStacks"; // Hungry Spd + + // Misc. + public static final String TAG_AMMO = "Ammo"; // Web Spd //--------------- INTERNATIONALIZATION ---------------- diff --git a/src/main/resources/META-INF/mods.toml b/src/main/resources/META-INF/mods.toml index a2aa2b4..7fe98c8 100644 --- a/src/main/resources/META-INF/mods.toml +++ b/src/main/resources/META-INF/mods.toml @@ -6,7 +6,7 @@ issueTrackerURL = "https://github.com/FatherToast/SpecialMobs/issues/" [[mods]] logoFile = "banner.png" authors = "Father Toast" -credits = "Sarinsa, Mother Toast & Baby Toast" +credits = "Sarinsa.instance().getCredits().toString(), Tutta (glorious textures), Mother Toast & Baby Toast" version = "${file.jarVersion}" displayName = "Special Mobs" modId = "specialmobs" @@ -21,7 +21,7 @@ Download: https://www.curseforge.com/members/fathertoast/projects/ Source: https://github.com/FatherToast/ -Discussion: https://discord.gg/GFTzTdX/ +Discussion: https://discord.gg/GFTzTdX ''' [[dependencies.deadlyworld]]