From 6176f4aa42368231cfdb7a8dd080e67d4f0ef559 Mon Sep 17 00:00:00 2001 From: FatherToast Date: Sat, 25 Jun 2022 12:58:35 -0500 Subject: [PATCH 1/4] Resizing --- .../specialmobs/common/core/SpecialMobs.java | 4 ++-- .../entity/cavespider/MotherCaveSpiderEntity.java | 2 ++ .../entity/cavespider/_SpecialCaveSpiderEntity.java | 2 +- .../common/entity/creeper/DeathCreeperEntity.java | 2 ++ .../common/entity/creeper/SplittingCreeperEntity.java | 2 ++ .../common/entity/creeper/_SpecialCreeperEntity.java | 10 +++++----- .../common/entity/enderman/MiniEndermanEntity.java | 2 +- .../entity/silverfish/ToughSilverfishEntity.java | 2 +- .../common/entity/skeleton/BruteSkeletonEntity.java | 2 ++ .../common/entity/skeleton/GiantSkeletonEntity.java | 2 +- .../common/entity/skeleton/SpitfireSkeletonEntity.java | 2 +- .../common/entity/spider/DesertSpiderEntity.java | 2 +- .../common/entity/spider/MotherSpiderEntity.java | 4 ++-- .../witherskeleton/BruteWitherSkeletonEntity.java | 2 ++ .../witherskeleton/GiantWitherSkeletonEntity.java | 2 +- .../witherskeleton/SpitfireWitherSkeletonEntity.java | 2 +- .../witherskeleton/_SpecialWitherSkeletonEntity.java | 4 ++-- 17 files changed, 29 insertions(+), 19 deletions(-) diff --git a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java index f8ceefe..a821aef 100644 --- a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java +++ b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java @@ -52,8 +52,8 @@ public class SpecialMobs { * - use shields * - babies * o wither skeletons - * o use shields - * o babies + * - use shields + * - babies * o slimes * o use attack damage attribute * o magma cubes diff --git a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/MotherCaveSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/MotherCaveSpiderEntity.java index 775054f..a661101 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/MotherCaveSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/MotherCaveSpiderEntity.java @@ -32,6 +32,7 @@ public class MotherCaveSpiderEntity extends _SpecialCaveSpiderEntity { @SpecialMob.BestiaryInfoSupplier public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 0.9F, 0.6F ); return new BestiaryInfo( 0xB300B3 ); } @@ -59,6 +60,7 @@ public class MotherCaveSpiderEntity extends _SpecialCaveSpiderEntity { @SpecialMob.Constructor public MotherCaveSpiderEntity( EntityType entityType, World world ) { super( entityType, world ); + getSpecialData().setBaseScale( 0.8F ); getSpecialData().setRegenerationTime( 30 ); xpReward += 1; diff --git a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java index abfe1f1..27917be 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java @@ -166,7 +166,7 @@ public class _SpecialCaveSpiderEntity extends CaveSpiderEntity implements ISpeci /** @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 0.65F * getSpecialData().getBaseScale() * (isBaby() ? 0.53448F : 1.0F); // Use base spider scale instead of super } /** @return Whether this entity is immune to fire damage. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/creeper/DeathCreeperEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/creeper/DeathCreeperEntity.java index 140fd3b..722ce7b 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/DeathCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/DeathCreeperEntity.java @@ -26,6 +26,7 @@ public class DeathCreeperEntity extends _SpecialCreeperEntity { @SpecialMob.BestiaryInfoSupplier public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 0.9F, 2.6F ); return new BestiaryInfo( 0xCD0000 ); } @@ -52,6 +53,7 @@ public class DeathCreeperEntity extends _SpecialCreeperEntity { @SpecialMob.Constructor public DeathCreeperEntity( EntityType entityType, World world ) { super( entityType, world ); + getSpecialData().setBaseScale( 1.5F ); setExplodesWhileBurning( true ); xpReward += 2; } 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 796b0a7..f11a5cf 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/SplittingCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/SplittingCreeperEntity.java @@ -32,6 +32,7 @@ public class SplittingCreeperEntity extends _SpecialCreeperEntity { @SpecialMob.BestiaryInfoSupplier public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 0.7F, 1.99F ); return new BestiaryInfo( 0x5F9D22, BestiaryInfo.BaseWeight.LOW ); } @@ -57,6 +58,7 @@ public class SplittingCreeperEntity extends _SpecialCreeperEntity { @SpecialMob.Constructor public SplittingCreeperEntity( EntityType entityType, World world ) { super( entityType, world ); + getSpecialData().setBaseScale( 1.2F ); getSpecialData().setImmuneToBurning( true ); setExplodesWhenShot( true ); xpReward += 2; 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 2a8bf63..55665cc 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java @@ -293,11 +293,11 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob< 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 The eye height of this entity when standing. */ - Creepers use auto-scaled eye height + // @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 diff --git a/src/main/java/fathertoast/specialmobs/common/entity/enderman/MiniEndermanEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/enderman/MiniEndermanEntity.java index eb57edb..a7f8e36 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/enderman/MiniEndermanEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/enderman/MiniEndermanEntity.java @@ -23,7 +23,7 @@ public class MiniEndermanEntity extends _SpecialEndermanEntity { @SpecialMob.BestiaryInfoSupplier public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { - entityType.sized( 0.5F, 0.9F ); + entityType.sized( 0.5F, 0.99F ); return new BestiaryInfo( 0xFFC0CB, BestiaryInfo.BaseWeight.LOW ); } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/ToughSilverfishEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/ToughSilverfishEntity.java index 5e189c5..7c46ed6 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/ToughSilverfishEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/ToughSilverfishEntity.java @@ -25,7 +25,7 @@ public class ToughSilverfishEntity extends _SpecialSilverfishEntity { @SpecialMob.BestiaryInfoSupplier public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { - entityType.sized( 0.6F, 0.9F ); + entityType.sized( 0.6F, 0.45F ); return new BestiaryInfo( 0xDD0E0E, BestiaryInfo.BaseWeight.LOW ); } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/skeleton/BruteSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/BruteSkeletonEntity.java index a2f9f72..ebab200 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/skeleton/BruteSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/BruteSkeletonEntity.java @@ -32,6 +32,7 @@ public class BruteSkeletonEntity extends _SpecialSkeletonEntity { @SpecialMob.BestiaryInfoSupplier public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 0.7F, 2.4F ); return new BestiaryInfo( 0xFFF87E ); } @@ -59,6 +60,7 @@ public class BruteSkeletonEntity extends _SpecialSkeletonEntity { @SpecialMob.Constructor public BruteSkeletonEntity( EntityType entityType, World world ) { super( entityType, world ); + getSpecialData().setBaseScale( 1.2F ); xpReward += 2; } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/skeleton/GiantSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/GiantSkeletonEntity.java index ccba5ba..6c1a980 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/skeleton/GiantSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/GiantSkeletonEntity.java @@ -24,7 +24,7 @@ public class GiantSkeletonEntity extends _SpecialSkeletonEntity { @SpecialMob.BestiaryInfoSupplier public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { - entityType.sized( 0.9F, 2.7F ); + entityType.sized( 0.9F, 2.99F ); return new BestiaryInfo( 0x494949 ); } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/skeleton/SpitfireSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/SpitfireSkeletonEntity.java index 7b2ab6c..2270ed1 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/skeleton/SpitfireSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/SpitfireSkeletonEntity.java @@ -26,7 +26,7 @@ public class SpitfireSkeletonEntity extends _SpecialSkeletonEntity { @SpecialMob.BestiaryInfoSupplier public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { - entityType.sized( 0.9F, 2.7F ).fireImmune(); + entityType.sized( 0.9F, 2.99F ).fireImmune(); return new BestiaryInfo( 0xDC1A00, BestiaryInfo.BaseWeight.LOW ); //TODO theme - fire } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/spider/DesertSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/DesertSpiderEntity.java index 0ed5573..c85ce38 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/spider/DesertSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/DesertSpiderEntity.java @@ -29,7 +29,7 @@ public class DesertSpiderEntity extends _SpecialSpiderEntity { @SpecialMob.BestiaryInfoSupplier public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { - entityType.sized( 0.95F, 0.8F ); + entityType.sized( 0.95F, 0.7F ); return new BestiaryInfo( 0xE6DDAC ); //TODO theme - desert } 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 5557b9c..cf59312 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/spider/MotherSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/MotherSpiderEntity.java @@ -32,7 +32,7 @@ public class MotherSpiderEntity extends _SpecialSpiderEntity { @SpecialMob.BestiaryInfoSupplier public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { - entityType.sized( 1.8F, 1.2F ); + entityType.sized( 1.7F, 1.0F ); return new BestiaryInfo( 0xB300B3 ); } @@ -60,7 +60,7 @@ public class MotherSpiderEntity extends _SpecialSpiderEntity { @SpecialMob.Constructor public MotherSpiderEntity( EntityType entityType, World world ) { super( entityType, world ); - getSpecialData().setBaseScale( 1.4F ); + getSpecialData().setBaseScale( 1.2F ); getSpecialData().setRegenerationTime( 30 ); xpReward += 1; diff --git a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/BruteWitherSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/BruteWitherSkeletonEntity.java index 2bd2c0e..e84c51c 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/BruteWitherSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/BruteWitherSkeletonEntity.java @@ -32,6 +32,7 @@ public class BruteWitherSkeletonEntity extends _SpecialWitherSkeletonEntity { @SpecialMob.BestiaryInfoSupplier public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 0.85F, 2.9F ); return new BestiaryInfo( 0xFFF87E ); } @@ -59,6 +60,7 @@ public class BruteWitherSkeletonEntity extends _SpecialWitherSkeletonEntity { @SpecialMob.Constructor public BruteWitherSkeletonEntity( EntityType entityType, World world ) { super( entityType, world ); + getSpecialData().setBaseScale( 1.44F ); xpReward += 2; } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/GiantWitherSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/GiantWitherSkeletonEntity.java index f849dd5..6c299d2 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/GiantWitherSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/GiantWitherSkeletonEntity.java @@ -24,7 +24,7 @@ public class GiantWitherSkeletonEntity extends _SpecialWitherSkeletonEntity { @SpecialMob.BestiaryInfoSupplier public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { - entityType.sized( 0.95F, 3.24F ); + entityType.sized( 0.95F, 3.6F ); return new BestiaryInfo( 0x474D4D ); } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/SpitfireWitherSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/SpitfireWitherSkeletonEntity.java index 08ab6a1..87eee42 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/SpitfireWitherSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/SpitfireWitherSkeletonEntity.java @@ -26,7 +26,7 @@ public class SpitfireWitherSkeletonEntity extends _SpecialWitherSkeletonEntity { @SpecialMob.BestiaryInfoSupplier public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { - entityType.sized( 0.95F, 3.24F ); + entityType.sized( 0.95F, 3.6F ); return new BestiaryInfo( 0xDC1A00, BestiaryInfo.BaseWeight.LOW ); } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java index ce0f8b2..2510052 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java @@ -315,8 +315,8 @@ public class _SpecialWitherSkeletonEntity extends WitherSkeletonEntity implement /** @return The eye height of this entity when standing. */ @Override - protected float getStandingEyeHeight( Pose pose, EntitySize size ) {//TODO fix this - return super.getStandingEyeHeight( pose, size ) * getSpecialData().getBaseScale() * (isBaby() ? 0.53448F : 1.0F); + protected float getStandingEyeHeight( Pose pose, EntitySize size ) { + return 1.74F * getSpecialData().getBaseScale() * (isBaby() ? 0.53448F : 1.0F); // Use base skeleton scale instead of super } /** @return Whether this entity is immune to fire damage. */ From 5bf6e82a872a059d827d61c9c0c575bf428c429c Mon Sep 17 00:00:00 2001 From: FatherToast Date: Sat, 25 Jun 2022 15:39:05 -0500 Subject: [PATCH 2/4] Zombies --- .../common/bestiary/MobFamily.java | 2 +- .../specialmobs/common/core/SpecialMobs.java | 12 +- .../specialmobs/common/entity/MobHelper.java | 6 +- .../cavespider/_SpecialCaveSpiderEntity.java | 6 +- .../entity/creeper/_SpecialCreeperEntity.java | 6 +- .../enderman/_SpecialEndermanEntity.java | 6 +- .../silverfish/_SpecialSilverfishEntity.java | 6 +- .../skeleton/_SpecialSkeletonEntity.java | 8 +- .../entity/spider/_SpecialSpiderEntity.java | 6 +- .../_SpecialWitherSkeletonEntity.java | 8 +- .../entity/zombie/BruteZombieEntity.java | 96 +++++++++++++ .../entity/zombie/FireZombieEntity.java | 102 ++++++++++++++ .../entity/zombie/GiantZombieEntity.java | 75 ++++++++++ .../entity/zombie/HungryZombieEntity.java | 114 ++++++++++++++++ .../entity/zombie/HuskZombieEntity.java | 117 ++++++++++++++++ .../zombie/MadScientistZombieEntity.java | 47 +++---- .../entity/zombie/PlagueZombieEntity.java | 93 +++++++++++++ .../entity/zombie/_SpecialZombieEntity.java | 128 ++++++++++++++++-- 18 files changed, 773 insertions(+), 65 deletions(-) create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/zombie/BruteZombieEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/zombie/FireZombieEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/zombie/GiantZombieEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/zombie/HungryZombieEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/zombie/HuskZombieEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/zombie/PlagueZombieEntity.java diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java index 08a7648..4c4674d 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java @@ -45,7 +45,7 @@ public class MobFamily { public static final MobFamily ZOMBIE = new MobFamily<>( "Zombie", "zombies", 0x00AFAF, new EntityType[] { EntityType.ZOMBIE, EntityType.HUSK }, - /*"Brute", "Fire", "Fishing", "Giant", "Hungry", "Husk",*/ "MadScientist"//, "Plague" + "Brute", "Fire", /*"Fishing",*/ "Giant", "Hungry", "Husk", "MadScientist", "Plague" ); // public static final MobFamily ZOMBIFIED_PIGLIN = new MobFamily<>( // "ZombifiedPiglin", "zombie pigmen", 0xEA9393, new EntityType[] { EntityType.ZOMBIFIED_PIGLIN }, diff --git a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java index a821aef..81ad2a3 100644 --- a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java +++ b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java @@ -32,17 +32,21 @@ public class SpecialMobs { * o entity replacer * o dimension-sensitive configs * o environment-sensitive configs + * ? natural spawning * o entities * - nbt-driven capabilities (special mob data) + * o fish hook + * o bug projectile * + bestiary * ? configurable stats * - monster families (see doc for specifics) * - creepers * - chance to spawn charged during thunderstorms * + scope - * o zombies + * - zombies * o villager infection - * o ranged attack AI (using bow) + * + transformations + * - ranged attack AI (using bow) * - use shields * + drowned * o zombified piglins @@ -51,7 +55,7 @@ public class SpecialMobs { * - skeletons * - use shields * - babies - * o wither skeletons + * - wither skeletons * - use shields * - babies * o slimes @@ -87,8 +91,6 @@ public class SpecialMobs { /** The path to the textures folder. */ public static final String TEXTURE_PATH = "textures/entity/"; - /** The path to the loot tables folder. */ - public static final String LOOT_TABLE_PATH = MOD_ID + ":entities/"; /** Logger instance for the mod. */ public static final Logger LOG = LogManager.getLogger( MOD_ID ); diff --git a/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java b/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java index 5420300..7eed2ad 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/MobHelper.java @@ -176,8 +176,8 @@ public final class MobHelper { 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 )]; + //final EffectInstance potion = PLAGUE_EFFECTS[random.nextInt( PLAGUE_EFFECTS.length - (Config.get().GENERAL.DISABLE_NAUSEA ? 1 : 0) )]; TODO config + final EffectInstance potion = PLAGUE_EFFECTS[random.nextInt( PLAGUE_EFFECTS.length )]; return new EffectInstance( potion.getEffect(), duration * potion.getDuration(), potion.getAmplifier() ); } @@ -195,7 +195,7 @@ public final class MobHelper { 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) )]; + final EffectInstance potion = WITCH_EFFECTS[random.nextInt( WITCH_EFFECTS.length - (includePoison ? 0 : 1) )]; return new EffectInstance( potion.getEffect(), duration * potion.getDuration(), potion.getAmplifier() ); } diff --git a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java index 27917be..4a729a0 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java @@ -60,7 +60,7 @@ public class _SpecialCaveSpiderEntity extends CaveSpiderEntity implements ISpeci @SpecialMob.Constructor public _SpecialCaveSpiderEntity( EntityType entityType, World world ) { super( entityType, world ); - specialData.initialize(); + getSpecialData().initialize(); } @@ -171,7 +171,7 @@ public class _SpecialCaveSpiderEntity extends CaveSpiderEntity implements ISpeci /** @return Whether this entity is immune to fire damage. */ @Override - public boolean fireImmune() { return specialData.isImmuneToFire(); } + public boolean fireImmune() { return getSpecialData().isImmuneToFire(); } /** Sets this entity on fire for a specific duration. */ @Override @@ -186,7 +186,7 @@ public class _SpecialCaveSpiderEntity extends CaveSpiderEntity implements ISpeci /** 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 ); + if( getSpecialData().canBeStuckIn( block ) ) super.makeStuckInBlock( block, speedMulti ); } /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */ 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 55665cc..eab11ff 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java @@ -66,7 +66,7 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob< @SpecialMob.Constructor public _SpecialCreeperEntity( EntityType entityType, World world ) { super( entityType, world ); - specialData.initialize(); + getSpecialData().initialize(); } @@ -301,7 +301,7 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob< /** @return Whether this entity is immune to fire damage. */ @Override - public boolean fireImmune() { return specialData.isImmuneToFire(); } + public boolean fireImmune() { return getSpecialData().isImmuneToFire(); } /** Sets this entity on fire for a specific duration. */ @Override @@ -316,7 +316,7 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob< /** 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 ); + if( getSpecialData().canBeStuckIn( block ) ) super.makeStuckInBlock( block, speedMulti ); } /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/enderman/_SpecialEndermanEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/enderman/_SpecialEndermanEntity.java index 520b6cf..10b6a29 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/enderman/_SpecialEndermanEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/enderman/_SpecialEndermanEntity.java @@ -58,7 +58,7 @@ public class _SpecialEndermanEntity extends EndermanEntity implements ISpecialMo @SpecialMob.Constructor public _SpecialEndermanEntity( EntityType entityType, World world ) { super( entityType, world ); - specialData.initialize(); + getSpecialData().initialize(); getSpecialData().setDamagedByWater( true ); } @@ -164,7 +164,7 @@ public class _SpecialEndermanEntity extends EndermanEntity implements ISpecialMo /** @return Whether this entity is immune to fire damage. */ @Override - public boolean fireImmune() { return specialData.isImmuneToFire(); } + public boolean fireImmune() { return getSpecialData().isImmuneToFire(); } /** Sets this entity on fire for a specific duration. */ @Override @@ -179,7 +179,7 @@ public class _SpecialEndermanEntity extends EndermanEntity implements ISpecialMo /** 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 ); + if( getSpecialData().canBeStuckIn( block ) ) super.makeStuckInBlock( block, speedMulti ); } /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/_SpecialSilverfishEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/_SpecialSilverfishEntity.java index ed6d00a..8bebf76 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/_SpecialSilverfishEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/_SpecialSilverfishEntity.java @@ -62,7 +62,7 @@ public class _SpecialSilverfishEntity extends SilverfishEntity implements ISpeci @SpecialMob.Constructor public _SpecialSilverfishEntity( EntityType entityType, World world ) { super( entityType, world ); - specialData.initialize(); + getSpecialData().initialize(); } @@ -191,7 +191,7 @@ public class _SpecialSilverfishEntity extends SilverfishEntity implements ISpeci /** @return Whether this entity is immune to fire damage. */ @Override - public boolean fireImmune() { return specialData.isImmuneToFire(); } + public boolean fireImmune() { return getSpecialData().isImmuneToFire(); } /** Sets this entity on fire for a specific duration. */ @Override @@ -206,7 +206,7 @@ public class _SpecialSilverfishEntity extends SilverfishEntity implements ISpeci /** 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 ); + if( getSpecialData().canBeStuckIn( block ) ) super.makeStuckInBlock( block, speedMulti ); } /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */ 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 2524ba3..10e6aef 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/skeleton/_SpecialSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/_SpecialSkeletonEntity.java @@ -79,7 +79,7 @@ public class _SpecialSkeletonEntity extends AbstractSkeletonEntity implements IS @SpecialMob.Constructor public _SpecialSkeletonEntity( EntityType entityType, World world ) { super( entityType, world ); - specialData.initialize(); + getSpecialData().initialize(); } @@ -209,7 +209,7 @@ public class _SpecialSkeletonEntity extends AbstractSkeletonEntity implements IS return groupData; } - /** Called to change */ + /** Called to set this entity's attack AI based on current equipment. */ @Override public void reassessWeaponGoal() { if( level != null && !level.isClientSide ) { @@ -358,7 +358,7 @@ public class _SpecialSkeletonEntity extends AbstractSkeletonEntity implements IS /** @return Whether this entity is immune to fire damage. */ @Override - public boolean fireImmune() { return specialData.isImmuneToFire(); } + public boolean fireImmune() { return getSpecialData().isImmuneToFire(); } /** Sets this entity on fire for a specific duration. */ @Override @@ -373,7 +373,7 @@ public class _SpecialSkeletonEntity extends AbstractSkeletonEntity implements IS /** 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 ); + if( getSpecialData().canBeStuckIn( block ) ) super.makeStuckInBlock( block, speedMulti ); } /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/spider/_SpecialSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/_SpecialSpiderEntity.java index e17ed80..6e23c08 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/spider/_SpecialSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/_SpecialSpiderEntity.java @@ -60,7 +60,7 @@ public class _SpecialSpiderEntity extends SpiderEntity implements ISpecialMob<_S @SpecialMob.Constructor public _SpecialSpiderEntity( EntityType entityType, World world ) { super( entityType, world ); - specialData.initialize(); + getSpecialData().initialize(); } @@ -171,7 +171,7 @@ public class _SpecialSpiderEntity extends SpiderEntity implements ISpecialMob<_S /** @return Whether this entity is immune to fire damage. */ @Override - public boolean fireImmune() { return specialData.isImmuneToFire(); } + public boolean fireImmune() { return getSpecialData().isImmuneToFire(); } /** Sets this entity on fire for a specific duration. */ @Override @@ -186,7 +186,7 @@ public class _SpecialSpiderEntity extends SpiderEntity implements ISpecialMob<_S /** 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 ); + if( getSpecialData().canBeStuckIn( block ) ) super.makeStuckInBlock( block, speedMulti ); } /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java index 2510052..4690e9c 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java @@ -76,7 +76,7 @@ public class _SpecialWitherSkeletonEntity extends WitherSkeletonEntity implement @SpecialMob.Constructor public _SpecialWitherSkeletonEntity( EntityType entityType, World world ) { super( entityType, world ); - specialData.initialize(); + getSpecialData().initialize(); getSpecialData().setImmuneToFire( true ); } @@ -207,7 +207,7 @@ public class _SpecialWitherSkeletonEntity extends WitherSkeletonEntity implement return groupData; } - /** Called to change */ + /** Called to set this entity's attack AI based on current equipment. */ @Override public void reassessWeaponGoal() { if( level != null && !level.isClientSide ) { @@ -321,7 +321,7 @@ public class _SpecialWitherSkeletonEntity extends WitherSkeletonEntity implement /** @return Whether this entity is immune to fire damage. */ @Override - public boolean fireImmune() { return specialData.isImmuneToFire(); } + public boolean fireImmune() { return getSpecialData().isImmuneToFire(); } /** Sets this entity on fire for a specific duration. */ @Override @@ -336,7 +336,7 @@ public class _SpecialWitherSkeletonEntity extends WitherSkeletonEntity implement /** 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 ); + if( getSpecialData().canBeStuckIn( block ) ) super.makeStuckInBlock( block, speedMulti ); } /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/zombie/BruteZombieEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombie/BruteZombieEntity.java new file mode 100644 index 0000000..6c0b546 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombie/BruteZombieEntity.java @@ -0,0 +1,96 @@ +package fathertoast.specialmobs.common.entity.zombie; + +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.entity.projectile.AbstractArrowEntity; +import net.minecraft.entity.projectile.ArrowEntity; +import net.minecraft.item.ItemStack; +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 BruteZombieEntity extends _SpecialZombieEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 0.7F, 2.35F ); + return new BestiaryInfo( 0xFFF87E ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialZombieEntity.createAttributes() ) + .addAttribute( Attributes.MAX_HEALTH, 10.0 ) + .addAttribute( Attributes.ARMOR, 10.0 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Zombie Brute", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.FLINT, 1 ); + loot.addRareDrop( "rare", Items.IRON_INGOT ); + } + + @SpecialMob.Constructor + public BruteZombieEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setBaseScale( 1.2F ); + 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 ) { + MobHelper.causeLifeLoss( (LivingEntity) target, 2.0F ); + } + } + + /** Override to modify this entity's ranged attack projectile. */ + @Override + protected AbstractArrowEntity getVariantArrow( AbstractArrowEntity arrow, ItemStack arrowItem, float damageMulti ) { + if( arrow instanceof ArrowEntity ) { + ((ArrowEntity) arrow).addEffect( new EffectInstance( Effects.HARM, 1 ) ); + } + return arrow; + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "brute" ), + null, + GET_TEXTURE_PATH( "brute_overlay" ) + }; + + /** @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/zombie/FireZombieEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombie/FireZombieEntity.java new file mode 100644 index 0000000..5915a33 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombie/FireZombieEntity.java @@ -0,0 +1,102 @@ +package fathertoast.specialmobs.common.entity.zombie; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +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.projectile.AbstractArrowEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.util.DamageSource; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundEvent; +import net.minecraft.util.SoundEvents; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class FireZombieEntity extends _SpecialZombieEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.fireImmune(); + return new BestiaryInfo( 0xDC1A00 ); + //TODO theme - fire + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return _SpecialZombieEntity.createAttributes(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Fire Zombie", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.FIRE_CHARGE ); + loot.addUncommonDrop( "uncommon", Items.COAL ); + } + + @SpecialMob.Constructor + public FireZombieEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setImmuneToFire( true ); + getSpecialData().setDamagedByWater( true ); + 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 ) { + target.setSecondsOnFire( 10 ); + } + + /** Override to modify this entity's ranged attack projectile. */ + @Override + protected AbstractArrowEntity getVariantArrow( AbstractArrowEntity arrow, ItemStack arrowItem, float damageMulti ) { + arrow.setSecondsOnFire( 100 ); + return arrow; + } + + /** @return The sound this entity makes idly. */ + @Override + protected SoundEvent getAmbientSound() { return SoundEvents.HUSK_AMBIENT; } + + /** @return The sound this entity makes when damaged. */ + @Override + protected SoundEvent getHurtSound( DamageSource source ) { return SoundEvents.HUSK_HURT; } + + /** @return The sound this entity makes when killed. */ + @Override + protected SoundEvent getDeathSound() { return SoundEvents.HUSK_DEATH; } + + /** @return The sound this entity makes while walking. */ + @Override + protected SoundEvent getStepSound() { return SoundEvents.HUSK_STEP; } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "fire" ) + }; + + /** @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/zombie/GiantZombieEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombie/GiantZombieEntity.java new file mode 100644 index 0000000..2d9fe26 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombie/GiantZombieEntity.java @@ -0,0 +1,75 @@ +package fathertoast.specialmobs.common.entity.zombie; + +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 GiantZombieEntity extends _SpecialZombieEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 0.9F, 2.95F ); + return new BestiaryInfo( 0x799C65 ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialZombieEntity.createAttributes() ) + .addAttribute( Attributes.MAX_HEALTH, 20.0 ) + .addAttribute( Attributes.ATTACK_DAMAGE, 2.0 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Giant Zombie", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addGuaranteedDrop( "base", Items.ROTTEN_FLESH, 2 ); + } + + @SpecialMob.Constructor + public GiantZombieEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setBaseScale( 1.5F ); + maxUpStep = 1.0F; + xpReward += 1; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + getSpecialData().rangedAttackDamage += 2.0F; + } + + /** Sets this entity as a baby. */ + @Override + public void setBaby( boolean value ) { } + + /** @return True if this entity is a baby. */ + @Override + public boolean isBaby() { return false; } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/zombie/HungryZombieEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombie/HungryZombieEntity.java new file mode 100644 index 0000000..15dd647 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombie/HungryZombieEntity.java @@ -0,0 +1,114 @@ +package fathertoast.specialmobs.common.entity.zombie; + +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.entity.player.PlayerEntity; +import net.minecraft.item.Food; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundEvents; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.World; +import net.minecraftforge.event.ForgeEventFactory; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class HungryZombieEntity extends _SpecialZombieEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0xAB1518 ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialZombieEntity.createAttributes() ) + .addAttribute( Attributes.MAX_HEALTH, 10.0 ) + .multAttribute( Attributes.MOVEMENT_SPEED, 1.3 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Hungry Zombie", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.BONE ); + loot.addUncommonDrop( "uncommon", Items.BEEF, Items.CHICKEN, Items.MUTTON, Items.PORKCHOP, Items.RABBIT, Items.COOKIE ); + } + + @SpecialMob.Constructor + public HungryZombieEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setRegenerationTime( 30 ); + xpReward += 2; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + disableRangedAI(); + } + + /** Called during spawn finalization to set starting equipment. */ + @Override + protected void populateDefaultEquipmentSlots( DifficultyInstance difficulty ) { + super.populateDefaultEquipmentSlots( difficulty ); + setCanPickUpLoot( false ); + } + + /** Override to change this entity's chance to spawn with a bow. */ + @Override + protected double getVariantBowChance() { return 0.0; } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + if( level.isClientSide() ) return; + + if( target instanceof PlayerEntity && ForgeEventFactory.getMobGriefingEvent( level, this ) ) { + final ItemStack food = MobHelper.stealRandomFood( (PlayerEntity) target ); + if( !food.isEmpty() ) { + 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 ); + return; + } + } + // Take a bite out of the target if they have no food to eat + if( target instanceof LivingEntity ) { + MobHelper.stealLife( this, (LivingEntity) target, 2.0F ); + } + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "hungry" ) + }; + + /** @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/zombie/HuskZombieEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombie/HuskZombieEntity.java new file mode 100644 index 0000000..eae4d39 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombie/HuskZombieEntity.java @@ -0,0 +1,117 @@ +package fathertoast.specialmobs.common.entity.zombie; + +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.entity.projectile.AbstractArrowEntity; +import net.minecraft.entity.projectile.ArrowEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.potion.EffectInstance; +import net.minecraft.potion.Effects; +import net.minecraft.util.DamageSource; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundEvent; +import net.minecraft.util.SoundEvents; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class HuskZombieEntity extends _SpecialZombieEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0xE6CC94, BestiaryInfo.BaseWeight.LOW ); + //TODO theme - desert + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return _SpecialZombieEntity.createAttributes(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Husk", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + loot.addLootTable( "main", EntityType.HUSK.getDefaultLootTable() ); + } + + @SpecialMob.Constructor + public HuskZombieEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setBaseScale( 1.0625F ); + 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.HUNGER, duration ) ); + } + } + + /** Override to modify this entity's ranged attack projectile. */ + @Override + protected AbstractArrowEntity getVariantArrow( AbstractArrowEntity arrow, ItemStack arrowItem, float damageMulti ) { + if( arrow instanceof ArrowEntity ) { + final int duration = MobHelper.getDebuffDuration( level.getDifficulty() ); + + ((ArrowEntity) arrow).addEffect( new EffectInstance( Effects.HUNGER, duration ) ); + } + return arrow; + } + + private static final ResourceLocation[] TEXTURES = { + new ResourceLocation( "textures/entity/zombie/husk.png" ) + }; + + /** @return All default textures for this entity. */ + @Override + public ResourceLocation[] getDefaultTextures() { return TEXTURES; } + + + //--------------- Husk Implementations ---------------- + + /** @return True if this zombie burns in sunlight. */ + @Override + protected boolean isSunSensitive() { return false; } + + /** @return The sound this entity makes idly. */ + @Override + protected SoundEvent getAmbientSound() { return SoundEvents.HUSK_AMBIENT; } + + /** @return The sound this entity makes when damaged. */ + @Override + protected SoundEvent getHurtSound( DamageSource source ) { return SoundEvents.HUSK_HURT; } + + /** @return The sound this entity makes when killed. */ + @Override + protected SoundEvent getDeathSound() { return SoundEvents.HUSK_DEATH; } + + /** @return The sound this entity makes while walking. */ + @Override + protected SoundEvent getStepSound() { return SoundEvents.HUSK_STEP; } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/zombie/MadScientistZombieEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombie/MadScientistZombieEntity.java index 46461a0..3c083b2 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/zombie/MadScientistZombieEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombie/MadScientistZombieEntity.java @@ -10,20 +10,19 @@ 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.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.inventory.EquipmentSlotType; import net.minecraft.item.ItemStack; -import net.minecraft.nbt.CompoundNBT; import net.minecraft.potion.EffectInstance; import net.minecraft.potion.Effects; import net.minecraft.util.ResourceLocation; import net.minecraft.world.DifficultyInstance; -import net.minecraft.world.IServerWorld; import net.minecraft.world.World; -import javax.annotation.Nullable; import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault @@ -70,11 +69,30 @@ public class MadScientistZombieEntity extends _SpecialZombieEntity { /** Override to change this entity's AI goals. */ @Override protected void registerVariantGoals() { + disableRangedAI(); + AIHelper.insertGoal( goalSelector, 2, new SpecialInjectCreeperGoal<>( this, 1.0D, 20.0D, ( madman, creeper ) -> creeper.isAlive() && !creeper.isPowered() && madman.getSensing().canSee( creeper ) ) ); } + /** Override to change this entity's attack goal priority. */ + @Override + protected int getVariantAttackPriority() { return super.getVariantAttackPriority() + 1; } + + /** Called during spawn finalization to set starting equipment. */ + @Override + protected void populateDefaultEquipmentSlots( DifficultyInstance difficulty ) { + super.populateDefaultEquipmentSlots( difficulty ); + + setItemSlot( EquipmentSlotType.MAINHAND, new ItemStack( SMItems.SYRINGE.get() ) ); + setDropChance( EquipmentSlotType.MAINHAND, 0.0F ); + } + + /** Override to change this entity's chance to spawn with a bow. */ + @Override + protected double getVariantBowChance() { return 0.0; } + /** Override to apply effects when this entity hits a target with a melee attack. */ @Override protected void onVariantAttack( Entity target ) { @@ -86,27 +104,6 @@ public class MadScientistZombieEntity extends _SpecialZombieEntity { } } - /** Called on spawn to initialize properties based on the world, difficulty, and the group it spawns with. */ - @Nullable - @Override - public ILivingEntityData finalizeSpawn( IServerWorld world, DifficultyInstance difficulty, SpawnReason spawnReason, - @Nullable ILivingEntityData groupData, @Nullable CompoundNBT eggTag ) { - this.populateDefaultEquipmentSlots( difficulty ); - return super.finalizeSpawn( world, difficulty, spawnReason, groupData, eggTag ); - } - - /** Only drop armor. The syringe item should be dropped from the loot table, and not from the hand item. **/ - @Override - protected float getEquipmentDropChance( EquipmentSlotType slotType ) { - return slotType.getType() == EquipmentSlotType.Group.ARMOR ? this.armorDropChances[slotType.getIndex()] : 0.0F; - } - - @Override - protected void populateDefaultEquipmentSlots( DifficultyInstance difficultyInstance ) { - super.populateDefaultEquipmentSlots( difficultyInstance ); - this.setItemSlot( EquipmentSlotType.MAINHAND, new ItemStack( SMItems.SYRINGE.get() ) ); - } - private static final ResourceLocation[] TEXTURES = { GET_TEXTURE_PATH( "madscientist" ), null, diff --git a/src/main/java/fathertoast/specialmobs/common/entity/zombie/PlagueZombieEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombie/PlagueZombieEntity.java new file mode 100644 index 0000000..6719686 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombie/PlagueZombieEntity.java @@ -0,0 +1,93 @@ +package fathertoast.specialmobs.common.entity.zombie; + +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.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.projectile.AbstractArrowEntity; +import net.minecraft.entity.projectile.ArrowEntity; +import net.minecraft.item.ItemStack; +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 PlagueZombieEntity extends _SpecialZombieEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0x8AA838 ); + //TODO theme - forest + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialZombieEntity.createAttributes() ) + .multAttribute( Attributes.MOVEMENT_SPEED, 1.1 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Plague Zombie", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addUncommonDrop( "uncommon", Items.POISONOUS_POTATO, Items.SPIDER_EYE, Items.FERMENTED_SPIDER_EYE, + Blocks.RED_MUSHROOM, Blocks.BROWN_MUSHROOM ); + } + + @SpecialMob.Constructor + public PlagueZombieEntity( 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 ) { + ((LivingEntity) target).addEffect( MobHelper.nextPlagueEffect( random, level ) ); + } + } + + /** Override to modify this entity's ranged attack projectile. */ + @Override + protected AbstractArrowEntity getVariantArrow( AbstractArrowEntity arrow, ItemStack arrowItem, float damageMulti ) { + if( arrow instanceof ArrowEntity ) { + ((ArrowEntity) arrow).addEffect( MobHelper.nextPlagueEffect( random, level ) ); + } + return arrow; + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "plague" ) + }; + + /** @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/zombie/_SpecialZombieEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombie/_SpecialZombieEntity.java index 4b464de..d7f00a2 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/zombie/_SpecialZombieEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombie/_SpecialZombieEntity.java @@ -6,15 +6,25 @@ import fathertoast.specialmobs.common.core.SpecialMobs; import fathertoast.specialmobs.common.entity.ISpecialMob; import fathertoast.specialmobs.common.entity.MobHelper; import fathertoast.specialmobs.common.entity.SpecialMobData; +import fathertoast.specialmobs.common.entity.ai.AIHelper; 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.ai.goal.Goal; +import net.minecraft.entity.ai.goal.RangedBowAttackGoal; +import net.minecraft.entity.ai.goal.ZombieAttackGoal; import net.minecraft.entity.monster.ZombieEntity; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.projectile.AbstractArrowEntity; +import net.minecraft.entity.projectile.ProjectileHelper; import net.minecraft.entity.projectile.SnowballEntity; +import net.minecraft.inventory.EquipmentSlotType; +import net.minecraft.item.BowItem; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; import net.minecraft.nbt.CompoundNBT; import net.minecraft.network.datasync.DataParameter; import net.minecraft.network.datasync.DataSerializers; @@ -22,6 +32,8 @@ 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.SoundEvents; +import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.vector.Vector3d; import net.minecraft.world.DifficultyInstance; import net.minecraft.world.IServerWorld; @@ -33,13 +45,13 @@ import javax.annotation.ParametersAreNonnullByDefault; @ParametersAreNonnullByDefault @MethodsReturnNonnullByDefault @SpecialMob -public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_SpecialZombieEntity> { +public class _SpecialZombieEntity extends ZombieEntity implements IRangedAttackMob, ISpecialMob<_SpecialZombieEntity> { //--------------- Static Special Mob Hooks ---------------- @SpecialMob.BestiaryInfoSupplier public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { - return new BestiaryInfo( 0x799C65 ); + return new BestiaryInfo( 0x799C65 );//sized(0.6F, 1.95F) } @SpecialMob.AttributeCreator @@ -61,7 +73,8 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S @SpecialMob.Constructor public _SpecialZombieEntity( EntityType entityType, World world ) { super( entityType, world ); - specialData.initialize(); + reassessWeaponGoal(); + getSpecialData().initialize(); } @@ -71,9 +84,11 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S @Override protected void registerGoals() { super.registerGoals(); + AIHelper.removeGoals( goalSelector, ZombieAttackGoal.class ); getSpecialData().rangedAttackDamage = 2.0F; - getSpecialData().rangedAttackSpread = 18.0F; + getSpecialData().rangedAttackSpread = 20.0F; + getSpecialData().rangedWalkSpeed = 0.8F; getSpecialData().rangedAttackCooldown = 30; getSpecialData().rangedAttackMaxCooldown = getSpecialData().rangedAttackCooldown; getSpecialData().rangedAttackMaxRange = 12.0F; @@ -83,6 +98,68 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S /** 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( 1.0, 20, 0.0F ); } + + /** Helper method to set the ranged attack AI more easily. */ + protected void setRangedAI( double walkSpeed, int cooldownTime ) { + getSpecialData().rangedWalkSpeed = (float) walkSpeed; + getSpecialData().rangedAttackCooldown = cooldownTime; + getSpecialData().rangedAttackMaxCooldown = cooldownTime; + } + + /** Helper method to set the ranged attack AI more easily. */ + protected void setRangedAI( double walkSpeed, int cooldownTime, float range ) { + setRangedAI( walkSpeed, cooldownTime ); + getSpecialData().rangedAttackMaxRange = range; + } + + /** Override to change this entity's attack goal priority. */ + protected int getVariantAttackPriority() { return 2; } + + /** Called during spawn finalization to set starting equipment. */ + @Override + protected void populateDefaultEquipmentSlots( DifficultyInstance difficulty ) { + super.populateDefaultEquipmentSlots( difficulty ); + if( random.nextDouble() < getVariantBowChance() ) { //TODO config the default 5% chance + setItemSlot( EquipmentSlotType.MAINHAND, new ItemStack( Items.BOW ) ); + } + } + + /** Override to change this entity's chance to spawn with a bow. */ + protected double getVariantBowChance() { return getSpecialData().rangedAttackMaxRange > 0.0F ? 0.05 : 0.0; } + + /** Called to attack the target with a ranged attack. */ + @Override + public void performRangedAttack( LivingEntity target, float damageMulti ) { + final ItemStack arrowItem = getProjectile( getItemInHand( ProjectileHelper.getWeaponHoldingHand( + this, item -> item instanceof BowItem ) ) ); + AbstractArrowEntity arrow = getArrow( arrowItem, damageMulti ); + if( getMainHandItem().getItem() instanceof BowItem ) + arrow = ((BowItem) getMainHandItem().getItem()).customArrow( arrow ); + + final double dX = target.getX() - getX(); + final double dY = target.getY( 1.0 / 3.0 ) - arrow.getY(); + final double dZ = target.getZ() - getZ(); + final double dH = MathHelper.sqrt( dX * dX + dZ * dZ ); + arrow.shoot( dX, dY + dH * 0.2, dZ, 1.6F, + getSpecialData().rangedAttackSpread * (1.0F - 0.2858F * level.getDifficulty().getId()) ); + + playSound( SoundEvents.SKELETON_SHOOT, 1.0F, 1.0F / (random.nextFloat() * 0.4F + 0.8F) ); + level.addFreshEntity( arrow ); + } + + /** @return The arrow for this zombie to shoot. */ + protected AbstractArrowEntity getArrow( ItemStack arrowItem, float damageMulti ) { + return getVariantArrow( ProjectileHelper.getMobArrow( this, arrowItem, + damageMulti * getSpecialData().rangedAttackDamage ), arrowItem, damageMulti ); + } + + /** Override to modify this entity's ranged attack projectile. */ + protected AbstractArrowEntity getVariantArrow( AbstractArrowEntity arrow, ItemStack arrowItem, float damageMulti ) { + return arrow; + } + /** Called to melee attack the target. */ @Override public boolean doHurtTarget( Entity target ) { @@ -108,6 +185,9 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S /** The parameter for special mob render scale. */ private static final DataParameter SCALE = EntityDataManager.defineId( _SpecialZombieEntity.class, DataSerializers.FLOAT ); + /** This entity's attack AI. */ + private Goal currentAttackAI; + /** Called from the Entity.class constructor to define data watcher variables. */ @Override protected void defineSynchedData() { @@ -120,10 +200,36 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S 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 + reassessWeaponGoal(); return groupData; } + /** Called to set the item equipped in a particular slot. */ + @Override + public void setItemSlot( EquipmentSlotType slot, ItemStack item ) { + super.setItemSlot( slot, item ); + if( !level.isClientSide ) reassessWeaponGoal(); + } + + /** Called to set this entity's attack AI based on current equipment. */ + public void reassessWeaponGoal() { + if( level != null && !level.isClientSide ) { + if( currentAttackAI != null ) goalSelector.removeGoal( currentAttackAI ); + + final SpecialMobData<_SpecialZombieEntity> data = getSpecialData(); + final ItemStack weapon = getItemInHand( ProjectileHelper.getWeaponHoldingHand( + this, item -> item instanceof BowItem ) ); + if( data.rangedAttackMaxRange > 0.0F && weapon.getItem() == Items.BOW ) { + currentAttackAI = new RangedBowAttackGoal<>( this, data.rangedWalkSpeed, + data.rangedAttackCooldown, data.rangedAttackMaxRange ); + } + else { + currentAttackAI = new ZombieAttackGoal( this, 1.0, false ); + } + goalSelector.addGoal( getVariantAttackPriority(), currentAttackAI ); + } + } + //--------------- ISpecialMob Implementation ---------------- @@ -164,12 +270,12 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S /** @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 super.getStandingEyeHeight( pose, size ) * getSpecialData().getBaseScale();// * (isBaby() ? 0.53448F : 1.0F); - Handled in super } /** @return Whether this entity is immune to fire damage. */ @Override - public boolean fireImmune() { return specialData.isImmuneToFire(); } + public boolean fireImmune() { return getSpecialData().isImmuneToFire(); } /** Sets this entity on fire for a specific duration. */ @Override @@ -177,6 +283,10 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S if( !getSpecialData().isImmuneToBurning() ) super.setRemainingFireTicks( ticks ); } + /** @return True if this zombie burns in sunlight. */ + @Override + protected boolean isSunSensitive() { return !getSpecialData().isImmuneToFire() && !getSpecialData().isImmuneToBurning(); } + /** @return True if this entity can be leashed. */ @Override public boolean canBeLeashed( PlayerEntity player ) { return !isLeashed() && getSpecialData().allowLeashing(); } @@ -184,7 +294,7 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S /** 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 ); + if( getSpecialData().canBeStuckIn( block ) ) super.makeStuckInBlock( block, speedMulti ); } /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */ @@ -246,5 +356,7 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S getSpecialData().readFromNBT( saveTag ); readVariantSaveData( saveTag ); + + reassessWeaponGoal(); } } \ No newline at end of file From 2130486ff15ec7267be08cae01f54c310a3670a6 Mon Sep 17 00:00:00 2001 From: FatherToast Date: Sat, 25 Jun 2022 18:28:35 -0500 Subject: [PATCH 3/4] Angler & ninja boilerplates --- .../client/FishingRodItemPropertyGetter.java | 34 +++ .../specialmobs/common/core/SpecialMobs.java | 1 + .../common/entity/ai/AIHelper.java | 10 +- .../specialmobs/common/entity/ai/IAngler.java | 14 ++ .../specialmobs/common/entity/ai/INinja.java | 25 ++ .../entity/projectile/BugSpitEntity.java | 14 ++ .../projectile/SpecialFishHookEntity.java | 14 ++ .../silverfish/FishingSilverfishEntity.java | 99 ++++++++ .../entity/skeleton/NinjaSkeletonEntity.java | 222 ++++++++++++++++++ .../NinjaWitherSkeletonEntity.java | 222 ++++++++++++++++++ .../entity/zombie/FishingZombieEntity.java | 141 +++++++++++ 11 files changed, 795 insertions(+), 1 deletion(-) create mode 100644 src/main/java/fathertoast/specialmobs/client/FishingRodItemPropertyGetter.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/ai/IAngler.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/ai/INinja.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/projectile/BugSpitEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/projectile/SpecialFishHookEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/silverfish/FishingSilverfishEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/skeleton/NinjaSkeletonEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/NinjaWitherSkeletonEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/zombie/FishingZombieEntity.java diff --git a/src/main/java/fathertoast/specialmobs/client/FishingRodItemPropertyGetter.java b/src/main/java/fathertoast/specialmobs/client/FishingRodItemPropertyGetter.java new file mode 100644 index 0000000..c0fa0c0 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/client/FishingRodItemPropertyGetter.java @@ -0,0 +1,34 @@ +package fathertoast.specialmobs.client; + +import fathertoast.specialmobs.common.entity.ai.IAngler; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.LivingEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.FishingRodItem; +import net.minecraft.item.IItemPropertyGetter; +import net.minecraft.item.ItemStack; +import net.minecraftforge.api.distmarker.Dist; +import net.minecraftforge.api.distmarker.OnlyIn; + +import javax.annotation.Nonnull; +import javax.annotation.Nullable; + +/** + * Item property getter override that allows the fishing rod item animation to function for any entity implementing IAngler. + */ +@OnlyIn( Dist.CLIENT ) +public class FishingRodItemPropertyGetter implements IItemPropertyGetter { + + @Override + public float call( @Nonnull ItemStack stack, @Nullable ClientWorld world, @Nullable LivingEntity entity ) { + if( entity == null ) return 0.0F; + + boolean inMainHand = entity.getMainHandItem() == stack; + boolean inOffhand = entity.getOffhandItem() == stack && !(entity.getMainHandItem().getItem() instanceof FishingRodItem); + + return (inMainHand || inOffhand) && ( + entity instanceof PlayerEntity && ((PlayerEntity) entity).fishing != null || + entity instanceof IAngler && ((IAngler) entity).isLineOut() // Line added to vanilla logic + ) ? 1.0F : 0.0F; + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java index 81ad2a3..7ac023b 100644 --- a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java +++ b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java @@ -68,6 +68,7 @@ public class SpecialMobs { * o ranged attack AI * - silverfish * ? ranged attack AI + * + puffer * - endermen * o witches * o ability to equip held items diff --git a/src/main/java/fathertoast/specialmobs/common/entity/ai/AIHelper.java b/src/main/java/fathertoast/specialmobs/common/entity/ai/AIHelper.java index 3322dcb..7a335d3 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/ai/AIHelper.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/ai/AIHelper.java @@ -11,7 +11,7 @@ import java.util.ArrayList; */ public final class AIHelper { - /** Inserts an AI goal at the specified priority. Existing goals have their priority adjusted accordingly. */ + /** Inserts an AI goal at the specified priority. Existing goals have their priority incremented accordingly. */ public static void insertGoal( GoalSelector ai, int priority, Goal goal ) { for( PrioritizedGoal task : new ArrayList<>( ai.availableGoals ) ) { if( task.getPriority() >= priority ) task.priority++; @@ -19,6 +19,14 @@ public final class AIHelper { ai.addGoal( priority, goal ); } + /** Inserts an AI goal at the specified priority. Existing goals have their priority decremented accordingly. */ + public static void insertGoalReverse( GoalSelector ai, int priority, Goal goal ) { + for( PrioritizedGoal task : new ArrayList<>( ai.availableGoals ) ) { + if( task.getPriority() <= priority ) task.priority--; + } + ai.addGoal( priority, goal ); + } + /** Removes all goals with the specified priority. */ public static void removeGoals( GoalSelector ai, int priority ) { for( PrioritizedGoal task : new ArrayList<>( ai.availableGoals ) ) { diff --git a/src/main/java/fathertoast/specialmobs/common/entity/ai/IAngler.java b/src/main/java/fathertoast/specialmobs/common/entity/ai/IAngler.java new file mode 100644 index 0000000..2dd1f1e --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/ai/IAngler.java @@ -0,0 +1,14 @@ +package fathertoast.specialmobs.common.entity.ai; + +/** + * Monsters must implement this interface to shoot fish hooks. + * This allows get and set methods for the fish hook so that the server can communicate rendering info to the client. + */ +public interface IAngler { + + /** Sets this angler's line as out (or in). */ + void setLineOut( boolean value ); + + /** @return Whether this angler's line is out. */ + boolean isLineOut(); +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/ai/INinja.java b/src/main/java/fathertoast/specialmobs/common/entity/ai/INinja.java new file mode 100644 index 0000000..529149a --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/ai/INinja.java @@ -0,0 +1,25 @@ +package fathertoast.specialmobs.common.entity.ai; + +import net.minecraft.block.BlockState; + +import javax.annotation.Nullable; + +/** + * Monsters must implement this interface to use the ninja goal AI. + * This allows get and set methods for the disguise block and immobile state. + */ +public interface INinja { + + /** @return Whether this ninja is currently immobile. */ + boolean isImmobile(); + + /** Sets this ninja's immovable state. When activated, the entity is 'snapped' to the nearest block position. */ + void setImmobile( boolean value ); + + /** @return The block being hidden (rendered) as, or null if not hiding. */ + @Nullable + BlockState getDisguiseBlock(); + + /** Sets the block being hidden (rendered) as, set to null to cancel hiding. */ + void setDisguiseBlock( @Nullable BlockState block ); +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/projectile/BugSpitEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/projectile/BugSpitEntity.java new file mode 100644 index 0000000..7ca4d83 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/projectile/BugSpitEntity.java @@ -0,0 +1,14 @@ +package fathertoast.specialmobs.common.entity.projectile; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.world.World; + +public abstract class BugSpitEntity extends Entity { + + public BugSpitEntity( EntityType entityType, World world ) { + super( entityType, world ); + } + + //TODO +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/projectile/SpecialFishHookEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/projectile/SpecialFishHookEntity.java new file mode 100644 index 0000000..df85c43 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/projectile/SpecialFishHookEntity.java @@ -0,0 +1,14 @@ +package fathertoast.specialmobs.common.entity.projectile; + +import net.minecraft.entity.Entity; +import net.minecraft.entity.EntityType; +import net.minecraft.world.World; + +public abstract class SpecialFishHookEntity extends Entity { + + public SpecialFishHookEntity( EntityType entityType, World world ) { + super( entityType, world ); + } + + //TODO +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/FishingSilverfishEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/FishingSilverfishEntity.java new file mode 100644 index 0000000..8bf7f15 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/FishingSilverfishEntity.java @@ -0,0 +1,99 @@ +package fathertoast.specialmobs.common.entity.silverfish; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.ai.IAngler; +import fathertoast.specialmobs.common.util.AttributeHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootEntryItemBuilder; +import fathertoast.specialmobs.datagen.loot.LootPoolBuilder; +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 FishingSilverfishEntity extends _SpecialSilverfishEntity implements IAngler { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 0.5F, 0.4F ); + return new BestiaryInfo( 0x2D41F4 ); + //TODO theme - fishing + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialSilverfishEntity.createAttributes() ) + .addAttribute( Attributes.MAX_HEALTH, 4.0 ) + .multAttribute( Attributes.MOVEMENT_SPEED, 0.9 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Fishing Silverfish", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addPool( new LootPoolBuilder( "common" ) + .addEntry( new LootEntryItemBuilder( Items.COD ).setCount( 0, 2 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() ) + .toLootPool() ); + } + + @SpecialMob.Constructor + public FishingSilverfishEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setBaseScale( 1.2F ); + getSpecialData().setCanBreatheInWater( true ); + getSpecialData().setIgnoreWaterPush( true ); + xpReward += 2; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + getSpecialData().rangedAttackSpread = 10.0F; + getSpecialData().rangedAttackCooldown = 32; + getSpecialData().rangedAttackMaxCooldown = 48; + getSpecialData().rangedAttackMaxRange = 10.0F; + + //TODO add angler AI @ 4 + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "fishing" ) + }; + + /** @return All default textures for this entity. */ + @Override + public ResourceLocation[] getDefaultTextures() { return TEXTURES; } + + + //--------------- IAngler Implementations ---------------- + + /** Sets this angler's line as out (or in). */ + @Override + public void setLineOut( boolean value ) { } + + /** @return Whether this angler's line is out. */ + @Override + public boolean isLineOut() { return false; } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/skeleton/NinjaSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/NinjaSkeletonEntity.java new file mode 100644 index 0000000..1813c6d --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/NinjaSkeletonEntity.java @@ -0,0 +1,222 @@ +package fathertoast.specialmobs.common.entity.skeleton; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.ai.INinja; +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.BlockState; +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.player.PlayerEntity; +import net.minecraft.network.datasync.DataParameter; +import net.minecraft.network.datasync.DataSerializers; +import net.minecraft.network.datasync.EntityDataManager; +import net.minecraft.util.ActionResultType; +import net.minecraft.util.DamageSource; +import net.minecraft.util.Hand; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.World; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Optional; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class NinjaSkeletonEntity extends _SpecialSkeletonEntity implements INinja { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0x333366 ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialSkeletonEntity.createAttributes() ) + .multAttribute( Attributes.MOVEMENT_SPEED, 1.2 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Skeleton Ninja", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addUncommonDrop( "uncommon", Blocks.INFESTED_STONE, Blocks.INFESTED_COBBLESTONE, Blocks.INFESTED_STONE_BRICKS, + Blocks.INFESTED_CRACKED_STONE_BRICKS, Blocks.INFESTED_MOSSY_STONE_BRICKS, Blocks.INFESTED_CHISELED_STONE_BRICKS ); + } + + @SpecialMob.Constructor + public NinjaSkeletonEntity( EntityType entityType, World world ) { + super( entityType, world ); + xpReward += 2; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + setRangedAI( 1.0, 10, 9.0F ); + + //TODO AIHelper.insertGoalReverse( goalSelector, getVariantAttackPriority() - 1, null ); + } + + /** Called during spawn finalization to set starting equipment. */ + @Override + protected void populateDefaultEquipmentSlots( DifficultyInstance difficulty ) { + super.populateDefaultEquipmentSlots( difficulty ); + setCanPickUpLoot( true ); + } + + /** Override to change this entity's chance to spawn with a melee weapon. */ + @Override + protected double getVariantMeleeChance() { return 0.5; } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + revealTo( target ); + } + + /** @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 ) ) { + revealTo( source.getEntity() ); + return true; + } + return false; + } + + /** @return Interacts (right click) with this entity and returns the result. */ + @Override + public ActionResultType mobInteract( PlayerEntity player, Hand hand ) { + // Attack if the player tries to right click the "block" + if( !level.isClientSide() && getDisguiseBlock() != null ) revealTo( player ); + return super.mobInteract( player, hand ); + } + + /** Called each tick to update this entity. */ + @Override + public void tick() { + // TODO can this be moved to the ninja AI? + if( !level.isClientSide() ) { + if( canHide ) { + //EntityAINinja.startHiding( this ); TODO + } + else if( onGround && getDisguiseBlock() == null && + (getTarget() == null || getTarget() instanceof PlayerEntity && ((PlayerEntity) getTarget()).isCreative()) ) { + canHide = true; + } + } + super.tick(); + } + + // // Moves this entity. + // @Override TODO + // public void move( MoverType type, double x, double y, double z ) { + // if( isImmobile() && type != MoverType.PISTON ) { + // motionY = 0.0; + // } + // else { + // super.move( type, x, y, z ); + // } + // } + // + // // Returns true if this entity should push and be pushed by other entities when colliding. + // @Override + // public boolean canBePushed() { return !isImmobile(); } + + /** Sets this entity on fire for a specific duration. */ + @Override + public void setRemainingFireTicks( int ticks ) { + if( !isImmobile() ) super.setRemainingFireTicks( ticks ); + } + + /** Reveals this ninja and sets its target so that it doesn't immediately re-disguise itself. */ + public void revealTo( @Nullable Entity target ) { + setDisguiseBlock( null ); + if( target instanceof LivingEntity ) setTarget( (LivingEntity) target ); + } + + private static final ResourceLocation[] TEXTURES = { + new ResourceLocation( "textures/entity/skeleton/skeleton.png" ), + null, + GET_TEXTURE_PATH( "ninja_overlay" ) + }; + + /** @return All default textures for this entity. */ + @Override + public ResourceLocation[] getDefaultTextures() { return TEXTURES; } + + + //--------------- INinja Implementations ---------------- + + /** The parameter for the ninja immobile state. */ + private static final DataParameter IS_HIDING = EntityDataManager.defineId( NinjaSkeletonEntity.class, DataSerializers.BOOLEAN ); + /** The parameter for the ninja disguise block. */ + private static final DataParameter> HIDING_BLOCK = EntityDataManager.defineId( NinjaSkeletonEntity.class, DataSerializers.BLOCK_STATE ); + + private boolean canHide = true; + + /** Called from the Entity.class constructor to define data watcher variables. */ + @Override + protected void defineSynchedData() { + super.defineSynchedData(); + entityData.define( IS_HIDING, false ); + entityData.define( HIDING_BLOCK, Optional.empty() ); + } + + /** @return Whether this ninja is currently immobile. */ + @Override + public boolean isImmobile() { return getEntityData().get( IS_HIDING ); } + + /** Sets this ninja's immovable state. When activated, the entity is 'snapped' to the nearest block position. */ + @Override + public void setImmobile( boolean value ) { + if( value != isImmobile() ) { + getEntityData().set( IS_HIDING, value ); + if( value ) { + clearFire(); + moveTo( Math.floor( getX() ) + 0.5, Math.floor( getY() ), Math.floor( getZ() ) + 0.5 ); + } + } + } + + /** @return The block being hidden (rendered) as, or null if not hiding. */ + @Nullable + @Override + public BlockState getDisguiseBlock() { + if( isAlive() ) return getEntityData().get( HIDING_BLOCK ).orElse( null ); + return null; + } + + /** Sets the block being hidden (rendered) as, set to null to cancel hiding. */ + @Override + public void setDisguiseBlock( @Nullable BlockState block ) { + getEntityData().set( HIDING_BLOCK, Optional.ofNullable( block ) ); + canHide = false; + + // Smoke puff when emerging from disguise + if( block == null ) { + //spawnExplosionParticle(); TODO + } + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/NinjaWitherSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/NinjaWitherSkeletonEntity.java new file mode 100644 index 0000000..e2b72bf --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/NinjaWitherSkeletonEntity.java @@ -0,0 +1,222 @@ +package fathertoast.specialmobs.common.entity.witherskeleton; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.ai.INinja; +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.BlockState; +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.player.PlayerEntity; +import net.minecraft.network.datasync.DataParameter; +import net.minecraft.network.datasync.DataSerializers; +import net.minecraft.network.datasync.EntityDataManager; +import net.minecraft.util.ActionResultType; +import net.minecraft.util.DamageSource; +import net.minecraft.util.Hand; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.World; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.Optional; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity implements INinja { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0x333366 ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialWitherSkeletonEntity.createAttributes() ) + .multAttribute( Attributes.MOVEMENT_SPEED, 1.2 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Wither Skeleton Ninja", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addUncommonDrop( "uncommon", Blocks.INFESTED_STONE, Blocks.INFESTED_COBBLESTONE, Blocks.INFESTED_STONE_BRICKS, + Blocks.INFESTED_CRACKED_STONE_BRICKS, Blocks.INFESTED_MOSSY_STONE_BRICKS, Blocks.INFESTED_CHISELED_STONE_BRICKS ); + } + + @SpecialMob.Constructor + public NinjaWitherSkeletonEntity( EntityType entityType, World world ) { + super( entityType, world ); + xpReward += 2; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + setRangedAI( 1.0, 10, 9.0F ); + + //TODO AIHelper.insertGoalReverse( goalSelector, getVariantAttackPriority() - 1, null ); + } + + /** Called during spawn finalization to set starting equipment. */ + @Override + protected void populateDefaultEquipmentSlots( DifficultyInstance difficulty ) { + super.populateDefaultEquipmentSlots( difficulty ); + setCanPickUpLoot( true ); + } + + /** Override to change this entity's chance to spawn with a bow. */ + @Override + protected double getVariantBowChance() { return 0.5; } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + revealTo( target ); + } + + /** @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 ) ) { + revealTo( source.getEntity() ); + return true; + } + return false; + } + + /** @return Interacts (right click) with this entity and returns the result. */ + @Override + public ActionResultType mobInteract( PlayerEntity player, Hand hand ) { + // Attack if the player tries to right click the "block" + if( !level.isClientSide() && getDisguiseBlock() != null ) revealTo( player ); + return super.mobInteract( player, hand ); + } + + /** Called each tick to update this entity. */ + @Override + public void tick() { + // TODO can this be moved to the ninja AI? + if( !level.isClientSide() ) { + if( canHide ) { + //EntityAINinja.startHiding( this ); TODO + } + else if( onGround && getDisguiseBlock() == null && + (getTarget() == null || getTarget() instanceof PlayerEntity && ((PlayerEntity) getTarget()).isCreative()) ) { + canHide = true; + } + } + super.tick(); + } + + // // Moves this entity. + // @Override TODO + // public void move( MoverType type, double x, double y, double z ) { + // if( isImmobile() && type != MoverType.PISTON ) { + // motionY = 0.0; + // } + // else { + // super.move( type, x, y, z ); + // } + // } + // + // // Returns true if this entity should push and be pushed by other entities when colliding. + // @Override + // public boolean canBePushed() { return !isImmobile(); } + + /** Sets this entity on fire for a specific duration. */ + @Override + public void setRemainingFireTicks( int ticks ) { + if( !isImmobile() ) super.setRemainingFireTicks( ticks ); + } + + /** Reveals this ninja and sets its target so that it doesn't immediately re-disguise itself. */ + public void revealTo( @Nullable Entity target ) { + setDisguiseBlock( null ); + if( target instanceof LivingEntity ) setTarget( (LivingEntity) target ); + } + + private static final ResourceLocation[] TEXTURES = { + new ResourceLocation( "textures/entity/skeleton/skeleton.png" ), + null, + GET_TEXTURE_PATH( "ninja_overlay" ) + }; + + /** @return All default textures for this entity. */ + @Override + public ResourceLocation[] getDefaultTextures() { return TEXTURES; } + + + //--------------- INinja Implementations ---------------- + + /** The parameter for the ninja immobile state. */ + private static final DataParameter IS_HIDING = EntityDataManager.defineId( NinjaWitherSkeletonEntity.class, DataSerializers.BOOLEAN ); + /** The parameter for the ninja disguise block. */ + private static final DataParameter> HIDING_BLOCK = EntityDataManager.defineId( NinjaWitherSkeletonEntity.class, DataSerializers.BLOCK_STATE ); + + private boolean canHide = true; + + /** Called from the Entity.class constructor to define data watcher variables. */ + @Override + protected void defineSynchedData() { + super.defineSynchedData(); + entityData.define( IS_HIDING, false ); + entityData.define( HIDING_BLOCK, Optional.empty() ); + } + + /** @return Whether this ninja is currently immobile. */ + @Override + public boolean isImmobile() { return getEntityData().get( IS_HIDING ); } + + /** Sets this ninja's immovable state. When activated, the entity is 'snapped' to the nearest block position. */ + @Override + public void setImmobile( boolean value ) { + if( value != isImmobile() ) { + getEntityData().set( IS_HIDING, value ); + if( value ) { + clearFire(); + moveTo( Math.floor( getX() ) + 0.5, Math.floor( getY() ), Math.floor( getZ() ) + 0.5 ); + } + } + } + + /** @return The block being hidden (rendered) as, or null if not hiding. */ + @Nullable + @Override + public BlockState getDisguiseBlock() { + if( isAlive() ) return getEntityData().get( HIDING_BLOCK ).orElse( null ); + return null; + } + + /** Sets the block being hidden (rendered) as, set to null to cancel hiding. */ + @Override + public void setDisguiseBlock( @Nullable BlockState block ) { + getEntityData().set( HIDING_BLOCK, Optional.ofNullable( block ) ); + canHide = false; + + // Smoke puff when emerging from disguise + if( block == null ) { + //spawnExplosionParticle(); TODO + } + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/zombie/FishingZombieEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombie/FishingZombieEntity.java new file mode 100644 index 0000000..b349ba5 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombie/FishingZombieEntity.java @@ -0,0 +1,141 @@ +package fathertoast.specialmobs.common.entity.zombie; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.ai.IAngler; +import fathertoast.specialmobs.common.util.AttributeHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootEntryItemBuilder; +import fathertoast.specialmobs.datagen.loot.LootHelper; +import fathertoast.specialmobs.datagen.loot.LootPoolBuilder; +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.inventory.EquipmentSlotType; +import net.minecraft.item.FishingRodItem; +import net.minecraft.item.IDyeableArmorItem; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.network.datasync.DataParameter; +import net.minecraft.network.datasync.DataSerializers; +import net.minecraft.network.datasync.EntityDataManager; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class FishingZombieEntity extends _SpecialZombieEntity implements IAngler { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0x2D41F4 ); + //TODO theme - fishing + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return AttributeHelper.of( _SpecialZombieEntity.createAttributes() ) + .multAttribute( Attributes.MOVEMENT_SPEED, 0.8 ) + .build(); + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Fishing Zombie", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addPool( new LootPoolBuilder( "common" ) + .addEntry( new LootEntryItemBuilder( Items.COD ).setCount( 0, 2 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() ) + .toLootPool() ); + loot.addPool( new LootPoolBuilder( "rare" ).addConditions( LootHelper.RARE_CONDITIONS ) + .addEntry( new LootEntryItemBuilder( Items.FISHING_ROD ).enchant( 30, true ).toLootEntry() ) + .toLootPool() ); + } + + @SpecialMob.Constructor + public FishingZombieEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setCanBreatheInWater( true ); + getSpecialData().setIgnoreWaterPush( true ); + xpReward += 2; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + getSpecialData().rangedAttackSpread = 10.0F; + getSpecialData().rangedAttackCooldown = 32; + getSpecialData().rangedAttackMaxCooldown = 48; + getSpecialData().rangedAttackMaxRange = 10.0F; + + //TODO add angler AI @ attack priority + } + + /** Called during spawn finalization to set starting equipment. */ + @Override + protected void populateDefaultEquipmentSlots( DifficultyInstance difficulty ) { + super.populateDefaultEquipmentSlots( difficulty ); + + setItemSlot( EquipmentSlotType.MAINHAND, new ItemStack( Items.FISHING_ROD ) ); + if( getItemBySlot( EquipmentSlotType.FEET ).isEmpty() ) { + ItemStack booties = new ItemStack( Items.LEATHER_BOOTS ); + ((IDyeableArmorItem) booties.getItem()).setColor( booties, 0xFFFF00 ); + setItemSlot( EquipmentSlotType.FEET, booties ); + } + setCanPickUpLoot( false ); + } + + /** Override to change this entity's chance to spawn with a bow. */ + @Override + protected double getVariantBowChance() { return 0.0; } + + + //--------------- IAngler Implementations ---------------- + + /** The parameter for baby status. */ + private static final DataParameter IS_LINE_OUT = EntityDataManager.defineId( FishingZombieEntity.class, DataSerializers.BOOLEAN ); + + /** Called from the Entity.class constructor to define data watcher variables. */ + @Override + protected void defineSynchedData() { + super.defineSynchedData(); + entityData.define( IS_LINE_OUT, false ); + } + + /** Sets this angler's line as out (or in). */ + @Override + public void setLineOut( boolean value ) { getEntityData().set( IS_LINE_OUT, value ); } + + /** @return Whether this angler's line is out. */ + @Override + public boolean isLineOut() { return getEntityData().get( IS_LINE_OUT ); } + + /** @return The item equipped in a particular slot. */ + @Override + public ItemStack getItemBySlot( EquipmentSlotType slot ) { + // Display a stick in place of the "cast fishing rod" when the fancy render is disabled + if( level.isClientSide() && /*!Config.get().GENERAL.FANCY_FISHING_MOBS &&*/ EquipmentSlotType.MAINHAND.equals( slot ) ) { + final ItemStack held = super.getItemBySlot( slot ); + if( !held.isEmpty() && held.getItem() instanceof FishingRodItem && isLineOut() ) { + return new ItemStack( Items.STICK ); + } + return held; + } + return super.getItemBySlot( slot ); + } +} \ No newline at end of file From a73a317fd70b4d5f57b3d8b562fb248d95669af0 Mon Sep 17 00:00:00 2001 From: FatherToast Date: Sat, 25 Jun 2022 23:46:48 -0500 Subject: [PATCH 4/4] Slimes! --- .../specialmobs/client/ClientRegister.java | 1 + .../renderer/entity/SpecialSlimeRenderer.java | 44 +++ .../common/bestiary/MobFamily.java | 8 +- .../specialmobs/common/core/SpecialMobs.java | 6 +- .../cavespider/MotherCaveSpiderEntity.java | 13 +- .../cavespider/_SpecialCaveSpiderEntity.java | 11 +- .../entity/creeper/_SpecialCreeperEntity.java | 11 +- .../enderman/LightningEndermanEntity.java | 6 + .../enderman/_SpecialEndermanEntity.java | 11 +- .../silverfish/_SpecialSilverfishEntity.java | 11 +- .../skeleton/_SpecialSkeletonEntity.java | 11 +- .../entity/slime/BlackberrySlimeEntity.java | 238 ++++++++++++++ .../entity/slime/BlueberrySlimeEntity.java | 83 +++++ .../entity/slime/CaramelSlimeEntity.java | 108 +++++++ .../common/entity/slime/GrapeSlimeEntity.java | 87 +++++ .../common/entity/slime/LemonSlimeEntity.java | 102 ++++++ .../entity/slime/StrawberrySlimeEntity.java | 86 +++++ .../entity/slime/WatermelonSlimeEntity.java | 105 ++++++ .../entity/slime/_SpecialSlimeEntity.java | 303 ++++++++++++++++++ .../entity/spider/MotherSpiderEntity.java | 11 +- .../entity/spider/_SpecialSpiderEntity.java | 11 +- .../_SpecialWitherSkeletonEntity.java | 11 +- .../entity/zombie/_SpecialZombieEntity.java | 11 +- .../specialmobs/common/util/References.java | 1 + 24 files changed, 1217 insertions(+), 73 deletions(-) create mode 100644 src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSlimeRenderer.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/slime/BlackberrySlimeEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/slime/BlueberrySlimeEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/slime/CaramelSlimeEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/slime/GrapeSlimeEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/slime/LemonSlimeEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/slime/StrawberrySlimeEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/slime/WatermelonSlimeEntity.java create mode 100644 src/main/java/fathertoast/specialmobs/common/entity/slime/_SpecialSlimeEntity.java diff --git a/src/main/java/fathertoast/specialmobs/client/ClientRegister.java b/src/main/java/fathertoast/specialmobs/client/ClientRegister.java index 5be252e..d713382 100644 --- a/src/main/java/fathertoast/specialmobs/client/ClientRegister.java +++ b/src/main/java/fathertoast/specialmobs/client/ClientRegister.java @@ -35,6 +35,7 @@ public class ClientRegister { registerFamilyRenderers( MobFamily.ZOMBIE, SpecialZombieRenderer::new ); registerFamilyRenderers( MobFamily.SKELETON, SpecialSkeletonRenderer::new ); registerFamilyRenderers( MobFamily.WITHER_SKELETON, SpecialSkeletonRenderer::new ); + registerFamilyRenderers( MobFamily.SLIME, SpecialSlimeRenderer::new ); registerFamilyRenderers( MobFamily.SPIDER, SpecialSpiderRenderer::new ); registerFamilyRenderers( MobFamily.CAVE_SPIDER, SpecialSpiderRenderer::new ); registerFamilyRenderers( MobFamily.SILVERFISH, SpecialSilverfishRenderer::new ); diff --git a/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSlimeRenderer.java b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSlimeRenderer.java new file mode 100644 index 0000000..4b4f521 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/client/renderer/entity/SpecialSlimeRenderer.java @@ -0,0 +1,44 @@ +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; +import net.minecraft.client.renderer.entity.SlimeRenderer; +import net.minecraft.entity.monster.SlimeEntity; +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 SpecialSlimeRenderer extends SlimeRenderer { + + private final float baseShadowRadius; + + public SpecialSlimeRenderer( EntityRendererManager rendererManager ) { + super( rendererManager ); + baseShadowRadius = shadowRadius; + addLayer( new SpecialMobEyesLayer<>( this ) ); + // Model doesn't support size parameter + //addLayer( new SpecialMobOverlayLayer<>( this, new SlimeModel<>( 0.25F ) ) ); + } + + @Override + public ResourceLocation getTextureLocation( SlimeEntity entity ) { + return ((ISpecialMob) entity).getSpecialData().getTexture(); + } + + @Override + protected void scale( SlimeEntity entity, MatrixStack matrixStack, float partialTick ) { + super.scale( entity, matrixStack, partialTick ); + + final float scale = ((ISpecialMob) entity).getSpecialData().getRenderScale(); + shadowRadius = baseShadowRadius * scale * entity.getSize(); // Factor slime size into shadow + matrixStack.scale( scale, scale, scale ); + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java index 4c4674d..4b20497 100644 --- a/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java +++ b/src/main/java/fathertoast/specialmobs/common/bestiary/MobFamily.java @@ -61,10 +61,10 @@ public class MobFamily { "Brute", "Gatling", "Giant", "Knight", /*"Ninja",*/ "Sniper"//, "Spitfire" ); - // public static final MobFamily SLIME = new MobFamily<>( - // "Slime", "slimes", 0x51A03E, new EntityType[] { EntityType.SLIME }, - // "Blackberry", "Blueberry", "Caramel", "Grape", "Lemon", "Strawberry", "Watermelon" - // ); + public static final MobFamily SLIME = new MobFamily<>( + "Slime", "slimes", 0x51A03E, new EntityType[] { EntityType.SLIME }, + "Blackberry", /*"Blueberry",*/ "Caramel", "Grape", "Lemon", "Strawberry", "Watermelon" + ); // public static final MobFamily MAGMA_CUBE = new MobFamily<>( // "MagmaCube", "magma cubes", 0x340000, new EntityType[] { EntityType.MAGMA_CUBE }, // "Flying", "Hardened", "Sticky", "Volatile" diff --git a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java index 7ac023b..d99711a 100644 --- a/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java +++ b/src/main/java/fathertoast/specialmobs/common/core/SpecialMobs.java @@ -58,10 +58,10 @@ public class SpecialMobs { * - wither skeletons * - use shields * - babies - * o slimes - * o use attack damage attribute + * - slimes + * o smallest size can deal damage * o magma cubes - * o use attack damage attribute + * o smallest size can deal damage * - spiders * o ranged attack AI * - cave spiders diff --git a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/MotherCaveSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/MotherCaveSpiderEntity.java index a661101..af7b9f1 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/MotherCaveSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/MotherCaveSpiderEntity.java @@ -12,11 +12,13 @@ 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.entity.monster.SlimeEntity; 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.util.text.ITextComponent; import net.minecraft.world.IServerWorld; import net.minecraft.world.World; @@ -90,16 +92,18 @@ public class MotherCaveSpiderEntity extends _SpecialCaveSpiderEntity { if( extraBabies > 0 && amount > 1.0F && level instanceof IServerWorld && random.nextFloat() < 0.33F ) { extraBabies--; spawnBaby( 0.66F, null ); + playSound( SoundEvents.EGG_THROW, 1.0F, 2.0F / (random.nextFloat() * 0.4F + 0.8F) ); } return true; } return false; } - /** Called each tick to update this entity while it's dead. */ + /** Called to remove this entity from the world. Includes death, unloading, interdimensional travel, etc. */ @Override - protected void tickDeath() { - if( deathTime == 19 && level instanceof IServerWorld ) { // At 19, the entity will be immediately removed upon call to super method + 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; @@ -108,8 +112,7 @@ public class MotherCaveSpiderEntity extends _SpecialCaveSpiderEntity { } playSound( SoundEvents.EGG_THROW, 1.0F, 2.0F / (random.nextFloat() * 0.4F + 0.8F) ); } - - super.tickDeath(); + super.remove( keepData ); } /** Helper method to simplify spawning babies. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java index 4a729a0..14cbf3c 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/cavespider/_SpecialCaveSpiderEntity.java @@ -82,14 +82,11 @@ public class _SpecialCaveSpiderEntity extends CaveSpiderEntity implements ISpeci /** Override to change this entity's AI goals. */ protected void registerVariantGoals() { } - /** Called to melee attack the target. */ + /** Called when this entity successfully damages a target to apply on-hit effects. */ @Override - public boolean doHurtTarget( Entity target ) { - if( super.doHurtTarget( target ) ) { - onVariantAttack( target ); - return true; - } - return false; + 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. */ 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 eab11ff..06a4bd8 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/creeper/_SpecialCreeperEntity.java @@ -82,14 +82,11 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob< /** Override to change this entity's AI goals. */ protected void registerVariantGoals() { } - /** Called to melee attack the target. */ + /** Called when this entity successfully damages a target to apply on-hit effects. */ @Override - public boolean doHurtTarget( Entity target ) { - if( super.doHurtTarget( target ) ) { - onVariantAttack( target ); - return true; - } - return false; + 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. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/enderman/LightningEndermanEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/enderman/LightningEndermanEntity.java index 9c96f76..c87d186 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/enderman/LightningEndermanEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/enderman/LightningEndermanEntity.java @@ -10,9 +10,11 @@ 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.effect.LightningBoltEntity; import net.minecraft.item.Items; import net.minecraft.util.ResourceLocation; import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; import javax.annotation.ParametersAreNonnullByDefault; @@ -67,6 +69,10 @@ public class LightningEndermanEntity extends _SpecialEndermanEntity { } } + /** Called when this entity is struck by lightning. */ + @Override + public void thunderHit( ServerWorld world, LightningBoltEntity lightningBolt ) { } + private static final ResourceLocation[] TEXTURES = { GET_TEXTURE_PATH( "lightning" ), GET_TEXTURE_PATH( "lightning_eyes" ) diff --git a/src/main/java/fathertoast/specialmobs/common/entity/enderman/_SpecialEndermanEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/enderman/_SpecialEndermanEntity.java index 10b6a29..907b498 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/enderman/_SpecialEndermanEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/enderman/_SpecialEndermanEntity.java @@ -75,14 +75,11 @@ public class _SpecialEndermanEntity extends EndermanEntity implements ISpecialMo /** Override to change this entity's AI goals. */ protected void registerVariantGoals() { } - /** Called to melee attack the target. */ + /** Called when this entity successfully damages a target to apply on-hit effects. */ @Override - public boolean doHurtTarget( Entity target ) { - if( super.doHurtTarget( target ) ) { - onVariantAttack( target ); - return true; - } - return false; + 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. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/_SpecialSilverfishEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/_SpecialSilverfishEntity.java index 8bebf76..25cc590 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/silverfish/_SpecialSilverfishEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/silverfish/_SpecialSilverfishEntity.java @@ -84,14 +84,11 @@ public class _SpecialSilverfishEntity extends SilverfishEntity implements ISpeci /** Override to change this entity's AI goals. */ protected void registerVariantGoals() { } - /** Called to melee attack the target. */ + /** Called when this entity successfully damages a target to apply on-hit effects. */ @Override - public boolean doHurtTarget( Entity target ) { - if( super.doHurtTarget( target ) ) { - onVariantAttack( target ); - return true; - } - return false; + 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. */ 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 10e6aef..d4f82cc 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/skeleton/_SpecialSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/skeleton/_SpecialSkeletonEntity.java @@ -164,14 +164,11 @@ public class _SpecialSkeletonEntity extends AbstractSkeletonEntity implements IS return arrow; } - /** Called to melee attack the target. */ + /** Called when this entity successfully damages a target to apply on-hit effects. */ @Override - public boolean doHurtTarget( Entity target ) { - if( super.doHurtTarget( target ) ) { - onVariantAttack( target ); - return true; - } - return false; + 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. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/slime/BlackberrySlimeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/slime/BlackberrySlimeEntity.java new file mode 100644 index 0000000..f26d097 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/slime/BlackberrySlimeEntity.java @@ -0,0 +1,238 @@ +package fathertoast.specialmobs.common.entity.slime; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.util.ExplosionHelper; +import fathertoast.specialmobs.common.util.References; +import fathertoast.specialmobs.datagen.loot.LootTableBuilder; +import mcp.MethodsReturnNonnullByDefault; +import net.minecraft.entity.AreaEffectCloudEntity; +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.ai.goal.Goal; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.item.ItemStack; +import net.minecraft.item.Items; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.particles.IParticleData; +import net.minecraft.particles.ParticleTypes; +import net.minecraft.potion.EffectInstance; +import net.minecraft.util.ActionResultType; +import net.minecraft.util.Hand; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.SoundEvents; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.ArrayList; +import java.util.EnumSet; +import java.util.List; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class BlackberrySlimeEntity extends _SpecialSlimeEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0x331133 ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return _SpecialSlimeEntity.createAttributes(); // Slimes define their attributes elsewhere based on size + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Blackberry Slime", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.GUNPOWDER ); + loot.addUncommonDrop( "uncommon", Items.BLACK_DYE ); + } + + @SpecialMob.Constructor + public BlackberrySlimeEntity( EntityType entityType, World world ) { + super( entityType, world ); + slimeExperienceValue += 2; + } + + + //--------------- Variant-Specific Implementations ---------------- + + private static final byte MAX_FUSE = 30; + + private int fuse = 0; + private int swellDir = 0; + private boolean ignited = false; + + /** Override to modify this slime's base attributes by size. */ + @Override + protected void modifyVariantAttributes( int size ) { + addAttribute( Attributes.MAX_HEALTH, 2.0 * size ); + } + + /** @return This slime's particle type for jump effects. */ + @Override + protected IParticleData getParticleType() { return ParticleTypes.SMOKE; } + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + goalSelector.addGoal( 0, new SlimeSwellGoal( this ) ); + } + + /** Called each tick to update this entity. */ + @Override + public void tick() { + if( isAlive() && !level.isClientSide() ) { + if( ignited ) swellDir = 1; + + if( swellDir > 0 ) { + if( fuse == 0 ) { + playSound( SoundEvents.CREEPER_PRIMED, 1.0F, 0.5F ); + } + else if( fuse >= MAX_FUSE ) { + dead = true; + ExplosionHelper.explode( this, getSize() + 0.5F, true, false ); + remove(); + spawnLingeringCloud(); + } + else { + changeFuse( +1 ); + } + } + else if( swellDir < 0 && fuse > 0 ) { + changeFuse( -1 ); + if( fuse <= 0 ) swellDir = 0; + } + } + super.tick(); + } + + /** Changes the fuse by a specific amount. The fuse must always be changed in step with render scale, so they stay in sync. */ + private void changeFuse( int change ) { + fuse += change; + getSpecialData().setRenderScale( getSpecialData().getRenderScale() + change * 0.014F ); + } + + /** Called to create a lingering effect cloud as part of this slime's explosion 'attack'. */ + protected void spawnLingeringCloud() { + final List effects = new ArrayList<>( getActiveEffects() ); + + if( !effects.isEmpty() ) { + final AreaEffectCloudEntity potionCloud = new AreaEffectCloudEntity( level, getX(), getY(), getZ() ); + potionCloud.setRadius( getSize() + 0.5F ); + potionCloud.setRadiusOnUse( -0.5F ); + potionCloud.setWaitTime( 10 ); + potionCloud.setDuration( potionCloud.getDuration() / 2 ); + potionCloud.setRadiusPerTick( -potionCloud.getRadius() / (float) potionCloud.getDuration() ); + for( EffectInstance effect : effects ) { + potionCloud.addEffect( new EffectInstance( effect ) ); + } + level.addFreshEntity( potionCloud ); + } + } + + /** @return This entity's max fall distance. */ + @Override + public int getMaxFallDistance() { return getTarget() == null ? 3 : 3 + (int) (getHealth() - 1.0F); } + + /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */ + @Override + public boolean causeFallDamage( float distance, float damageMultiplier ) { + final boolean success = super.causeFallDamage( distance, damageMultiplier ); + + // Speed up fuse from falling like creepers + changeFuse( (int) (distance * 1.5F) ); + if( fuse > MAX_FUSE - 5 ) changeFuse( MAX_FUSE - 5 - fuse ); + return success; + } + + /** @return Interacts (right click) with this entity and returns the result. */ + @Override + public ActionResultType mobInteract( PlayerEntity player, Hand hand ) { + final ItemStack item = player.getItemInHand( hand ); + if( item.getItem() == Items.FLINT_AND_STEEL ) { + // Allow players to ignite blackberry slimes like creepers + level.playSound( player, getX(), getY(), getZ(), SoundEvents.FLINTANDSTEEL_USE, getSoundSource(), + 1.0F, random.nextFloat() * 0.4F + 0.8F ); + if( !level.isClientSide ) { + ignited = true; + item.hurtAndBreak( 1, player, ( entity ) -> entity.broadcastBreakEvent( hand ) ); + } + return ActionResultType.sidedSuccess( level.isClientSide ); + } + return super.mobInteract( player, hand ); + } + + /** Override to save data to this entity's NBT data. */ + @Override + public void addVariantSaveData( CompoundNBT saveTag ) { + saveTag.putByte( References.TAG_FUSE_TIME, (byte) fuse ); + } + + /** Override to load data from this entity's NBT data. */ + @Override + public void readVariantSaveData( CompoundNBT saveTag ) { + if( saveTag.contains( References.TAG_FUSE_TIME, References.NBT_TYPE_NUMERICAL ) ) + fuse = saveTag.getByte( References.TAG_FUSE_TIME ); + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "blackberry" ) + }; + + /** @return All default textures for this entity. */ + @Override + public ResourceLocation[] getDefaultTextures() { return TEXTURES; } + + + //--------------- Nested Classes ---------------- + + /** The "creeper swell" goal repurposed for use on a slime. */ + private static class SlimeSwellGoal extends Goal { + + private final BlackberrySlimeEntity slime; + + private LivingEntity target; + + public SlimeSwellGoal( BlackberrySlimeEntity entity ) { + slime = entity; + setFlags( EnumSet.of( Flag.MOVE ) ); + } + + public boolean canUse() { + final LivingEntity target = slime.getTarget(); + return slime.swellDir > 0 || target != null && slime.distanceToSqr( target ) < 9.0F + (slime.getSize() - 1) * 2.0F; + } + + public void start() { + slime.getNavigation().stop(); + target = slime.getTarget(); + } + + public void stop() { + slime.swellDir = -1; + target = null; + } + + public void tick() { + if( target == null || slime.distanceToSqr( target ) > 49.0 || !slime.getSensing().canSee( target ) ) { + slime.swellDir = -1; + } + else { + slime.swellDir = 1; + } + } + } +} \ No newline at end of file diff --git a/src/main/java/fathertoast/specialmobs/common/entity/slime/BlueberrySlimeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/slime/BlueberrySlimeEntity.java new file mode 100644 index 0000000..e203ae4 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/slime/BlueberrySlimeEntity.java @@ -0,0 +1,83 @@ +package fathertoast.specialmobs.common.entity.slime; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +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.particles.IParticleData; +import net.minecraft.particles.ParticleTypes; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class BlueberrySlimeEntity extends _SpecialSlimeEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0x766BBC ); + //TODO theme - water + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return _SpecialSlimeEntity.createAttributes(); // Slimes define their attributes elsewhere based on size + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Blueberry Slime", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addRareDrop( "rare", Items.GOLD_NUGGET, Items.PRISMARINE_SHARD, Items.PRISMARINE_CRYSTALS ); + loot.addUncommonDrop( "uncommon", Items.BLUE_DYE ); + } + + @SpecialMob.Constructor + public BlueberrySlimeEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setImmuneToBurning( true ); + getSpecialData().setCanBreatheInWater( true ); + slimeExperienceValue += 1; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** Override to modify this slime's base attributes by size. */ + @Override + protected void modifyVariantAttributes( int size ) { + addAttribute( Attributes.ATTACK_DAMAGE, 1.0 * size ); + } + + /** @return This slime's particle type for jump effects. */ + @Override + protected IParticleData getParticleType() { return ParticleTypes.SPLASH; } + + /** Override to change this entity's AI goals. */ + @Override + protected boolean isAffectedByFluids() { return false; } // TODO figure out the right way to do this + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "blueberry" ) + }; + + /** @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/slime/CaramelSlimeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/slime/CaramelSlimeEntity.java new file mode 100644 index 0000000..24d9344 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/slime/CaramelSlimeEntity.java @@ -0,0 +1,108 @@ +package fathertoast.specialmobs.common.entity.slime; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +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.particles.IParticleData; +import net.minecraft.particles.ParticleTypes; +import net.minecraft.util.DamageSource; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; +import java.util.List; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class CaramelSlimeEntity extends _SpecialSlimeEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0x9D733F ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return _SpecialSlimeEntity.createAttributes(); // Slimes define their attributes elsewhere based on size + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Caramel Slime", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.SUGAR ); + loot.addUncommonDrop( "uncommon", Items.ORANGE_DYE ); + } + + @SpecialMob.Constructor + public CaramelSlimeEntity( EntityType entityType, World world ) { + super( entityType, world ); + slimeExperienceValue += 2; + } + + + //--------------- Variant-Specific Implementations ---------------- + + private final DamageSource grabDamageSource = DamageSource.mobAttack( this ).bypassArmor().bypassMagic(); + + private int grabTime; + + /** Override to modify this slime's base attributes by size. */ + @Override + protected void modifyVariantAttributes( int size ) { + addAttribute( Attributes.MAX_HEALTH, 4.0 * size ); + } + + /** @return This slime's particle type for jump effects. */ + @Override + protected IParticleData getParticleType() { return ParticleTypes.SPLASH; } //TODO + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + if( grabTime <= -20 && getPassengers().isEmpty() ) { + if( target.startRiding( this, true ) ) grabTime = 20; + } + } + + /** Called each tick to update this entity. */ + @Override + public void tick() { + super.tick(); + + grabTime--; + final List riders = getPassengers(); + if( grabTime <= 0 && !riders.isEmpty() ) { + for( Entity rider : riders ) { + if( rider instanceof LivingEntity ) { + rider.hurt( grabDamageSource, 1.0F ); + grabTime = 10; + } + } + } + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "caramel" ) + }; + + /** @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/slime/GrapeSlimeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/slime/GrapeSlimeEntity.java new file mode 100644 index 0000000..c45ed69 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/slime/GrapeSlimeEntity.java @@ -0,0 +1,87 @@ +package fathertoast.specialmobs.common.entity.slime; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.ai.SpecialLeapAtTargetGoal; +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.particles.IParticleData; +import net.minecraft.particles.ParticleTypes; +import net.minecraft.util.ResourceLocation; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class GrapeSlimeEntity extends _SpecialSlimeEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0xB333B3 ); + //TODO theme - mountain + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return _SpecialSlimeEntity.createAttributes(); // Slimes define their attributes elsewhere based on size + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Grape Slime", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addGuaranteedDrop( "base", Items.SLIME_BALL, 1 ); + loot.addUncommonDrop( "uncommon", Items.PURPLE_DYE ); + } + + @SpecialMob.Constructor + public GrapeSlimeEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setFallDamageMultiplier( 0.0F ); + slimeExperienceValue += 1; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** Override to modify this slime's base attributes by size. */ + @Override + protected void modifyVariantAttributes( int size ) { + addAttribute( Attributes.MAX_HEALTH, 4.0 * size ); + multAttribute( Attributes.MOVEMENT_SPEED, 1.2 ); + } + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + goalSelector.addGoal( 0, new SpecialLeapAtTargetGoal( + this, 10, 6.0F, 12.0F, 1.1F, 2.6F ) ); + } + + /** @return This slime's particle type for jump effects. */ + @Override + protected IParticleData getParticleType() { return ParticleTypes.SPLASH; } //TODO + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "grape" ) + }; + + /** @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/slime/LemonSlimeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/slime/LemonSlimeEntity.java new file mode 100644 index 0000000..4eecf23 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/slime/LemonSlimeEntity.java @@ -0,0 +1,102 @@ +package fathertoast.specialmobs.common.entity.slime; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.util.ExplosionHelper; +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.entity.effect.LightningBoltEntity; +import net.minecraft.item.Items; +import net.minecraft.particles.IParticleData; +import net.minecraft.particles.ParticleTypes; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.vector.Vector3d; +import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class LemonSlimeEntity extends _SpecialSlimeEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.fireImmune(); + return new BestiaryInfo( 0xE6E861 ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return _SpecialSlimeEntity.createAttributes(); // Slimes define their attributes elsewhere based on size + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Lemon Slime", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.REDSTONE, 1 ); + loot.addUncommonDrop( "uncommon", Items.YELLOW_DYE ); + } + + @SpecialMob.Constructor + public LemonSlimeEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setImmuneToFire( true ); + slimeExperienceValue += 2; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** Override to modify this slime's base attributes by size. */ + @Override + protected void modifyVariantAttributes( int size ) { + addAttribute( Attributes.MAX_HEALTH, 2.0 * size ); + } + + /** @return This slime's particle type for jump effects. */ + @Override + protected IParticleData getParticleType() { return ParticleTypes.SPLASH; } //TODO + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + if( target instanceof LivingEntity ) { + ExplosionHelper.spawnLightning( level, target.getX(), target.getY(), target.getZ() ); + + // Knock self back + final float forwardPower = 1.1F; + final float upwardPower = 1.0F; + final Vector3d vKnockback = new Vector3d( target.getX() - getX(), 0.0, target.getZ() - getZ() ) + .normalize().scale( -forwardPower ).add( getDeltaMovement().scale( 0.2F ) ); + setDeltaMovement( vKnockback.x, 0.4 * upwardPower, vKnockback.z ); + } + } + + /** Called when this entity is struck by lightning. */ + @Override + public void thunderHit( ServerWorld world, LightningBoltEntity lightningBolt ) { } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "lemon" ) + }; + + /** @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/slime/StrawberrySlimeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/slime/StrawberrySlimeEntity.java new file mode 100644 index 0000000..77ea722 --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/slime/StrawberrySlimeEntity.java @@ -0,0 +1,86 @@ +package fathertoast.specialmobs.common.entity.slime; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.ai.SpecialLeapAtTargetGoal; +import fathertoast.specialmobs.common.util.ExplosionHelper; +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.entity.effect.LightningBoltEntity; +import net.minecraft.item.Items; +import net.minecraft.particles.IParticleData; +import net.minecraft.particles.ParticleTypes; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.vector.Vector3d; +import net.minecraft.world.World; +import net.minecraft.world.server.ServerWorld; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class StrawberrySlimeEntity extends _SpecialSlimeEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.fireImmune(); + return new BestiaryInfo( 0xBE696B ); + //TODO theme - fire + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return _SpecialSlimeEntity.createAttributes(); // Slimes define their attributes elsewhere based on size + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Strawberry Slime", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.FIRE_CHARGE, 1 ); + loot.addUncommonDrop( "uncommon", Items.RED_DYE ); + } + + @SpecialMob.Constructor + public StrawberrySlimeEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setImmuneToFire( true ); + getSpecialData().setDamagedByWater( true ); + slimeExperienceValue += 1; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** @return This slime's particle type for jump effects. */ + @Override + protected IParticleData getParticleType() { return ParticleTypes.SPLASH; } //TODO + + /** Override to apply effects when this entity hits a target with a melee attack. */ + @Override + protected void onVariantAttack( Entity target ) { + target.setSecondsOnFire( getSize() * 3 ); + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "strawberry" ) + }; + + /** @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/slime/WatermelonSlimeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/slime/WatermelonSlimeEntity.java new file mode 100644 index 0000000..0540d4e --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/slime/WatermelonSlimeEntity.java @@ -0,0 +1,105 @@ +package fathertoast.specialmobs.common.entity.slime; + +import fathertoast.specialmobs.common.bestiary.BestiaryInfo; +import fathertoast.specialmobs.common.bestiary.SpecialMob; +import fathertoast.specialmobs.common.entity.ai.SpecialLeapAtTargetGoal; +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.particles.IParticleData; +import net.minecraft.particles.ParticleTypes; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.vector.Vector3d; +import net.minecraft.world.World; + +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class WatermelonSlimeEntity extends _SpecialSlimeEntity { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + entityType.sized( 3.06F, 3.06F ); + return new BestiaryInfo( 0xDF7679, BestiaryInfo.BaseWeight.LOW ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return _SpecialSlimeEntity.createAttributes(); // Slimes define their attributes elsewhere based on size + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Watermelon Slime", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void buildLootTable( LootTableBuilder loot ) { + addBaseLoot( loot ); + loot.addCommonDrop( "common", Items.MELON_SLICE ); + loot.addRareDrop( "rare", Items.GLISTERING_MELON_SLICE ); + loot.addUncommonDrop( "uncommon", Items.PINK_DYE, Items.LIME_DYE ); + } + + @SpecialMob.Constructor + public WatermelonSlimeEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().setBaseScale( 1.5F ); + slimeExperienceValue += 2; + } + + + //--------------- Variant-Specific Implementations ---------------- + + /** Override to modify this slime's base attributes by size. */ + @Override + protected void modifyVariantAttributes( int size ) { + addAttribute( Attributes.MAX_HEALTH, 2.0 * size + 8.0 ); + setAttribute( Attributes.ARMOR, 15.0 ); + } + + /** Override to change this entity's AI goals. */ + @Override + protected void registerVariantGoals() { + goalSelector.addGoal( 0, new SpecialLeapAtTargetGoal( + this, 10, 0.0F, 5.0F, 1.16F, 1.0F ) ); + } + + /** @return This slime's particle type for jump effects. */ + @Override + protected IParticleData getParticleType() { return ParticleTypes.SPLASH; } //TODO + + /** 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 float forwardPower = 0.8F; + final float upwardPower = 0.5F; + final Vector3d vKnockback = new Vector3d( target.getX() - getX(), 0.0, target.getZ() - getZ() ) + .normalize().scale( forwardPower ).add( getDeltaMovement().scale( 0.2F ) ); + target.setDeltaMovement( vKnockback.x, 0.4 * upwardPower, vKnockback.z ); + target.hurtMarked = true; + + setDeltaMovement( getDeltaMovement().multiply( 0.2, 1.0, 0.2 ) ); + } + } + + private static final ResourceLocation[] TEXTURES = { + GET_TEXTURE_PATH( "watermelon" ) + }; + + /** @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/slime/_SpecialSlimeEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/slime/_SpecialSlimeEntity.java new file mode 100644 index 0000000..92aeb9b --- /dev/null +++ b/src/main/java/fathertoast/specialmobs/common/entity/slime/_SpecialSlimeEntity.java @@ -0,0 +1,303 @@ +package fathertoast.specialmobs.common.entity.slime; + +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.Attribute; +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.monster.MonsterEntity; +import net.minecraft.entity.monster.SlimeEntity; +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.entity.projectile.SnowballEntity; +import net.minecraft.nbt.CompoundNBT; +import net.minecraft.network.datasync.DataParameter; +import net.minecraft.network.datasync.DataSerializers; +import net.minecraft.network.datasync.EntityDataManager; +import net.minecraft.potion.EffectInstance; +import net.minecraft.util.DamageSource; +import net.minecraft.util.ResourceLocation; +import net.minecraft.util.math.vector.Vector3d; +import net.minecraft.world.DifficultyInstance; +import net.minecraft.world.IServerWorld; +import net.minecraft.world.World; + +import javax.annotation.Nullable; +import javax.annotation.ParametersAreNonnullByDefault; + +@ParametersAreNonnullByDefault +@MethodsReturnNonnullByDefault +@SpecialMob +public class _SpecialSlimeEntity extends SlimeEntity implements ISpecialMob<_SpecialSlimeEntity> { + + //--------------- Static Special Mob Hooks ---------------- + + @SpecialMob.BestiaryInfoSupplier + public static BestiaryInfo bestiaryInfo( EntityType.Builder entityType ) { + return new BestiaryInfo( 0x51A03E ); + } + + @SpecialMob.AttributeCreator + public static AttributeModifierMap.MutableAttribute createAttributes() { + return MonsterEntity.createMonsterAttributes(); // Slimes define their attributes elsewhere based on size + } + + @SpecialMob.LanguageProvider + public static String[] getTranslations( String langKey ) { + return References.translations( langKey, "Slime", + "", "", "", "", "", "" );//TODO + } + + @SpecialMob.LootTableProvider + public static void addBaseLoot( LootTableBuilder loot ) { + loot.addLootTable( "main", EntityType.SLIME.getDefaultLootTable() ); + } + + @SpecialMob.Constructor + public _SpecialSlimeEntity( EntityType entityType, World world ) { + super( entityType, world ); + getSpecialData().initialize(); + } + + + //--------------- Variant-Specific Breakouts ---------------- + + /** Override to modify this slime's base attributes by size. */ + protected void modifyVariantAttributes( int size ) { } + + /** Called in the MobEntity.class constructor to initialize AI goals. */ + @Override + protected void registerGoals() { + super.registerGoals(); + registerVariantGoals(); + } + + /** Override to change this entity's AI goals. */ + protected void registerVariantGoals() { } + + /** Called when this entity successfully damages a target to apply on-hit effects. */ + @Override + public void doEnchantDamageEffects( LivingEntity attacker, Entity target ) { + onVariantAttack( target ); + super.doEnchantDamageEffects( attacker, target ); + } + + /** Override to apply effects when this entity hits a target with a melee attack. */ + protected void onVariantAttack( Entity target ) { } + + /** 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( _SpecialSlimeEntity.class, DataSerializers.FLOAT ); + + protected int slimeExperienceValue = 0; + + /** Called from the Entity.class constructor to define data watcher variables. */ + @Override + protected void defineSynchedData() { + super.defineSynchedData(); + specialData = new SpecialMobData<>( this, SCALE, 1.0F ); + } + + /** Returns true if this slime can deal damage. */ + @Override + protected boolean isDealsDamage() { return getSize() > 0 && isEffectiveAi(); } // TODO config for allow tiny slimes to hit + + /** Sets this slime's size, optionally resetting its health to max. */ + @Override + protected void setSize( int size, boolean resetHealth ) { + super.setSize( size, resetHealth ); + + modifyVariantAttributes( size ); + if( resetHealth ) setHealth( getMaxHealth() ); + xpReward = size + slimeExperienceValue; + } + + /** + * Alters this slime's base attribute by adding an amount to it. + * Do NOT use this for move speed, instead use {@link #multAttribute(Attribute, double)} + */ + protected void addAttribute( Attribute attribute, double amount ) { + if( attribute != Attributes.MAX_HEALTH && attribute != Attributes.ATTACK_DAMAGE && attribute != Attributes.MOVEMENT_SPEED ) + throw new IllegalArgumentException( "Slime relative attributes are only health, damage, and speed!" ); + + final ModifiableAttributeInstance attributeInstance = getAttribute( attribute ); + if( attributeInstance == null ) + throw new IllegalStateException( "Attempted to modify non-registered attribute " + attribute.getDescriptionId() ); + attributeInstance.setBaseValue( attributeInstance.getBaseValue() + amount ); + } + + /** + * Alters this slime's base attribute by multiplying it by an amount. + * Mainly use this for move speed, for other attributes use {@link #addAttribute(Attribute, double)} + */ + protected void multAttribute( Attribute attribute, double amount ) { + if( attribute != Attributes.MAX_HEALTH && attribute != Attributes.ATTACK_DAMAGE && attribute != Attributes.MOVEMENT_SPEED ) + throw new IllegalArgumentException( "Slime relative attributes are only health, damage, and speed!" ); + + final ModifiableAttributeInstance attributeInstance = getAttribute( attribute ); + if( attributeInstance == null ) + throw new IllegalStateException( "Attempted to modify non-registered attribute " + attribute.getDescriptionId() ); + attributeInstance.setBaseValue( attributeInstance.getBaseValue() * amount ); + } + + /** Sets this slime's base attribute. */ + protected void setAttribute( Attribute attribute, double amount ) { + if( attribute == Attributes.MAX_HEALTH || attribute == Attributes.ATTACK_DAMAGE || attribute == Attributes.MOVEMENT_SPEED ) + throw new IllegalArgumentException( "Use slime relative attribute!" ); + + final ModifiableAttributeInstance attributeInstance = getAttribute( attribute ); + if( attributeInstance == null ) + throw new IllegalStateException( "Attempted to modify non-registered attribute " + attribute.getDescriptionId() ); + attributeInstance.setBaseValue( amount ); + } + + /** 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<_SpecialSlimeEntity> specialData; + + /** @return This mob's special data. */ + @Override + public SpecialMobData<_SpecialSlimeEntity> getSpecialData() { return specialData; } + + /** @return The experience that should be dropped by this entity. */ + @Override + public final int getExperience() { return slimeExperienceValue; } // Slime base xp + + /** Sets the experience that should be dropped by this entity. */ + @Override + public final void setExperience( int xp ) { + slimeExperienceValue = xp; + xpReward = getSize() + xp; + } + + static ResourceLocation GET_TEXTURE_PATH( String type ) { + return SpecialMobs.resourceLoc( SpecialMobs.TEXTURE_PATH + "slime/" + type + ".png" ); + } + + private static final ResourceLocation[] TEXTURES = { + new ResourceLocation( "textures/entity/slime/slime.png" ) + }; + + /** @return All default textures for this entity. */ + @Override + public ResourceLocation[] getDefaultTextures() { return TEXTURES; } + + + //--------------- SpecialMobData Hooks ---------------- + + /** Called each tick to update this entity's movement. */ + @Override + public void aiStep() { + super.aiStep(); + getSpecialData().tick(); + } + + /** @return The eye height of this entity when standing. */ + @Override + protected float getStandingEyeHeight( Pose pose, EntitySize size ) { + return super.getStandingEyeHeight( pose, size ) * getSpecialData().getBaseScale() * (isBaby() ? 0.53448F : 1.0F); + } + + /** @return Whether this entity is immune to fire damage. */ + @Override + public boolean fireImmune() { return getSpecialData().isImmuneToFire(); } + + /** Sets this entity on fire for a specific duration. */ + @Override + public void setRemainingFireTicks( int ticks ) { + if( !getSpecialData().isImmuneToBurning() ) super.setRemainingFireTicks( ticks ); + } + + /** @return True if this entity can be leashed. */ + @Override + public boolean canBeLeashed( PlayerEntity player ) { return !isLeashed() && getSpecialData().allowLeashing(); } + + /** Sets this entity 'stuck' inside a block, such as a cobweb or sweet berry bush. Mod blocks could use this as a speed boost. */ + @Override + public void makeStuckInBlock( BlockState block, Vector3d speedMulti ) { + if( getSpecialData().canBeStuckIn( block ) ) super.makeStuckInBlock( block, speedMulti ); + } + + /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */ + @Override + public boolean causeFallDamage( float distance, float damageMultiplier ) { + return super.causeFallDamage( distance, damageMultiplier * getSpecialData().getFallDamageMultiplier() ); + } + + /** @return True if this entity should NOT trigger pressure plates or tripwires. */ + @Override + public boolean isIgnoringBlockTriggers() { return getSpecialData().ignorePressurePlates(); } + + /** @return True if this entity can breathe underwater. */ + @Override + public boolean canBreatheUnderwater() { return getSpecialData().canBreatheInWater(); } + + /** @return True if this entity can be pushed by (flowing) fluids. */ + @Override + public boolean isPushedByFluid() { return !getSpecialData().ignoreWaterPush(); } + + /** @return True if this entity takes damage while wet. */ + @Override + public boolean isSensitiveToWater() { return getSpecialData().isDamagedByWater(); } + + /** @return Attempts to damage this entity; returns true if the hit was successful. */ + @Override + public boolean hurt( DamageSource source, float amount ) { + if( isSensitiveToWater() && source.getDirectEntity() instanceof SnowballEntity ) { + amount = Math.max( 3.0F, amount ); + } + return super.hurt( source, amount ); + } + + /** @return True if the effect can be applied to this entity. */ + @Override + public boolean canBeAffected( EffectInstance effect ) { return getSpecialData().isPotionApplicable( effect ); } + + /** Saves data to this entity's base NBT compound that is specific to its subclass. */ + @Override + public void addAdditionalSaveData( CompoundNBT tag ) { + super.addAdditionalSaveData( tag ); + + final CompoundNBT saveTag = SpecialMobData.getSaveLocation( tag ); + + getSpecialData().writeToNBT( saveTag ); + addVariantSaveData( saveTag ); + } + + /** Loads data from this entity's base NBT compound that is specific to its subclass. */ + @Override + public void readAdditionalSaveData( CompoundNBT tag ) { + super.readAdditionalSaveData( tag ); + + final CompoundNBT saveTag = SpecialMobData.getSaveLocation( tag ); + + getSpecialData().readFromNBT( saveTag ); + readVariantSaveData( saveTag ); + } +} \ No newline at end of file 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 cf59312..a9e9950 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/spider/MotherSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/MotherSpiderEntity.java @@ -90,16 +90,18 @@ public class MotherSpiderEntity extends _SpecialSpiderEntity { if( extraBabies > 0 && amount > 1.0F && level instanceof IServerWorld && random.nextFloat() < 0.33F ) { extraBabies--; spawnBaby( 0.66F, null ); + playSound( SoundEvents.EGG_THROW, 1.0F, 2.0F / (random.nextFloat() * 0.4F + 0.8F) ); } return true; } return false; } - /** Called each tick to update this entity while it's dead. */ + /** Called to remove this entity from the world. Includes death, unloading, interdimensional travel, etc. */ @Override - protected void tickDeath() { - if( deathTime == 19 && level instanceof IServerWorld ) { // At 19, the entity will be immediately removed upon call to super method + 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; @@ -108,8 +110,7 @@ public class MotherSpiderEntity extends _SpecialSpiderEntity { } playSound( SoundEvents.EGG_THROW, 1.0F, 2.0F / (random.nextFloat() * 0.4F + 0.8F) ); } - - super.tickDeath(); + super.remove( keepData ); } /** Helper method to simplify spawning babies. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/spider/_SpecialSpiderEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/spider/_SpecialSpiderEntity.java index 6e23c08..0dbe4fd 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/spider/_SpecialSpiderEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/spider/_SpecialSpiderEntity.java @@ -82,14 +82,11 @@ public class _SpecialSpiderEntity extends SpiderEntity implements ISpecialMob<_S /** Override to change this entity's AI goals. */ protected void registerVariantGoals() { } - /** Called to melee attack the target. */ + /** Called when this entity successfully damages a target to apply on-hit effects. */ @Override - public boolean doHurtTarget( Entity target ) { - if( super.doHurtTarget( target ) ) { - onVariantAttack( target ); - return true; - } - return false; + 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. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java index 4690e9c..4beaedc 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/witherskeleton/_SpecialWitherSkeletonEntity.java @@ -162,14 +162,11 @@ public class _SpecialWitherSkeletonEntity extends WitherSkeletonEntity implement return arrow; } - /** Called to melee attack the target. */ + /** Called when this entity successfully damages a target to apply on-hit effects. */ @Override - public boolean doHurtTarget( Entity target ) { - if( super.doHurtTarget( target ) ) { - onVariantAttack( target ); - return true; - } - return false; + 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. */ diff --git a/src/main/java/fathertoast/specialmobs/common/entity/zombie/_SpecialZombieEntity.java b/src/main/java/fathertoast/specialmobs/common/entity/zombie/_SpecialZombieEntity.java index d7f00a2..49a7b52 100644 --- a/src/main/java/fathertoast/specialmobs/common/entity/zombie/_SpecialZombieEntity.java +++ b/src/main/java/fathertoast/specialmobs/common/entity/zombie/_SpecialZombieEntity.java @@ -160,14 +160,11 @@ public class _SpecialZombieEntity extends ZombieEntity implements IRangedAttackM return arrow; } - /** Called to melee attack the target. */ + /** Called when this entity successfully damages a target to apply on-hit effects. */ @Override - public boolean doHurtTarget( Entity target ) { - if( super.doHurtTarget( target ) ) { - onVariantAttack( target ); - return true; - } - return false; + 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. */ diff --git a/src/main/java/fathertoast/specialmobs/common/util/References.java b/src/main/java/fathertoast/specialmobs/common/util/References.java index a3f604c..ad9fcb8 100644 --- a/src/main/java/fathertoast/specialmobs/common/util/References.java +++ b/src/main/java/fathertoast/specialmobs/common/util/References.java @@ -77,6 +77,7 @@ public final class References { 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