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