This commit is contained in:
FatherToast 2022-06-25 15:39:05 -05:00
parent 6176f4aa42
commit 5bf6e82a87
18 changed files with 773 additions and 65 deletions

View file

@ -45,7 +45,7 @@ public class MobFamily<T extends LivingEntity> {
public static final MobFamily<ZombieEntity> ZOMBIE = new MobFamily<>( public static final MobFamily<ZombieEntity> ZOMBIE = new MobFamily<>(
"Zombie", "zombies", 0x00AFAF, new EntityType[] { EntityType.ZOMBIE, EntityType.HUSK }, "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<ZombieEntity> ZOMBIFIED_PIGLIN = new MobFamily<>( // public static final MobFamily<ZombieEntity> ZOMBIFIED_PIGLIN = new MobFamily<>(
// "ZombifiedPiglin", "zombie pigmen", 0xEA9393, new EntityType[] { EntityType.ZOMBIFIED_PIGLIN }, // "ZombifiedPiglin", "zombie pigmen", 0xEA9393, new EntityType[] { EntityType.ZOMBIFIED_PIGLIN },

View file

@ -32,17 +32,21 @@ public class SpecialMobs {
* o entity replacer * o entity replacer
* o dimension-sensitive configs * o dimension-sensitive configs
* o environment-sensitive configs * o environment-sensitive configs
* ? natural spawning
* o entities * o entities
* - nbt-driven capabilities (special mob data) * - nbt-driven capabilities (special mob data)
* o fish hook
* o bug projectile
* + bestiary * + bestiary
* ? configurable stats * ? configurable stats
* - monster families (see doc for specifics) * - monster families (see doc for specifics)
* - creepers * - creepers
* - chance to spawn charged during thunderstorms * - chance to spawn charged during thunderstorms
* + scope * + scope
* o zombies * - zombies
* o villager infection * o villager infection
* o ranged attack AI (using bow) * + transformations
* - ranged attack AI (using bow)
* - use shields * - use shields
* + drowned * + drowned
* o zombified piglins * o zombified piglins
@ -51,7 +55,7 @@ public class SpecialMobs {
* - skeletons * - skeletons
* - use shields * - use shields
* - babies * - babies
* o wither skeletons * - wither skeletons
* - use shields * - use shields
* - babies * - babies
* o slimes * o slimes
@ -87,8 +91,6 @@ public class SpecialMobs {
/** The path to the textures folder. */ /** The path to the textures folder. */
public static final String TEXTURE_PATH = "textures/entity/"; 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. */ /** Logger instance for the mod. */
public static final Logger LOG = LogManager.getLogger( MOD_ID ); public static final Logger LOG = LogManager.getLogger( MOD_ID );

View file

@ -176,8 +176,8 @@ public final class MobHelper {
public static EffectInstance nextPlagueEffect( Random random, World world ) { public static EffectInstance nextPlagueEffect( Random random, World world ) {
final int duration = MobHelper.getDebuffDuration( world.getDifficulty() ); 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 //final EffectInstance potion = PLAGUE_EFFECTS[random.nextInt( PLAGUE_EFFECTS.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 )];
return new EffectInstance( potion.getEffect(), duration * potion.getDuration(), potion.getAmplifier() ); 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 ) { public static EffectInstance nextWitchSpiderEffect( Random random, World world, boolean includePoison ) {
final int duration = MobHelper.getDebuffDuration( world.getDifficulty() ); 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() ); return new EffectInstance( potion.getEffect(), duration * potion.getDuration(), potion.getAmplifier() );
} }

View file

@ -60,7 +60,7 @@ public class _SpecialCaveSpiderEntity extends CaveSpiderEntity implements ISpeci
@SpecialMob.Constructor @SpecialMob.Constructor
public _SpecialCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) { public _SpecialCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) {
super( entityType, 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. */ /** @return Whether this entity is immune to fire damage. */
@Override @Override
public boolean fireImmune() { return specialData.isImmuneToFire(); } public boolean fireImmune() { return getSpecialData().isImmuneToFire(); }
/** Sets this entity on fire for a specific duration. */ /** Sets this entity on fire for a specific duration. */
@Override @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. */ /** 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 @Override
public void makeStuckInBlock( BlockState block, Vector3d speedMulti ) { 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. */ /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */

View file

@ -66,7 +66,7 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
@SpecialMob.Constructor @SpecialMob.Constructor
public _SpecialCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) { public _SpecialCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, 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. */ /** @return Whether this entity is immune to fire damage. */
@Override @Override
public boolean fireImmune() { return specialData.isImmuneToFire(); } public boolean fireImmune() { return getSpecialData().isImmuneToFire(); }
/** Sets this entity on fire for a specific duration. */ /** Sets this entity on fire for a specific duration. */
@Override @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. */ /** 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 @Override
public void makeStuckInBlock( BlockState block, Vector3d speedMulti ) { 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. */ /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */

View file

@ -58,7 +58,7 @@ public class _SpecialEndermanEntity extends EndermanEntity implements ISpecialMo
@SpecialMob.Constructor @SpecialMob.Constructor
public _SpecialEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) { public _SpecialEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) {
super( entityType, world ); super( entityType, world );
specialData.initialize(); getSpecialData().initialize();
getSpecialData().setDamagedByWater( true ); getSpecialData().setDamagedByWater( true );
} }
@ -164,7 +164,7 @@ public class _SpecialEndermanEntity extends EndermanEntity implements ISpecialMo
/** @return Whether this entity is immune to fire damage. */ /** @return Whether this entity is immune to fire damage. */
@Override @Override
public boolean fireImmune() { return specialData.isImmuneToFire(); } public boolean fireImmune() { return getSpecialData().isImmuneToFire(); }
/** Sets this entity on fire for a specific duration. */ /** Sets this entity on fire for a specific duration. */
@Override @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. */ /** 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 @Override
public void makeStuckInBlock( BlockState block, Vector3d speedMulti ) { 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. */ /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */

View file

@ -62,7 +62,7 @@ public class _SpecialSilverfishEntity extends SilverfishEntity implements ISpeci
@SpecialMob.Constructor @SpecialMob.Constructor
public _SpecialSilverfishEntity( EntityType<? extends _SpecialSilverfishEntity> entityType, World world ) { public _SpecialSilverfishEntity( EntityType<? extends _SpecialSilverfishEntity> entityType, World world ) {
super( entityType, 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. */ /** @return Whether this entity is immune to fire damage. */
@Override @Override
public boolean fireImmune() { return specialData.isImmuneToFire(); } public boolean fireImmune() { return getSpecialData().isImmuneToFire(); }
/** Sets this entity on fire for a specific duration. */ /** Sets this entity on fire for a specific duration. */
@Override @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. */ /** 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 @Override
public void makeStuckInBlock( BlockState block, Vector3d speedMulti ) { 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. */ /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */

View file

@ -79,7 +79,7 @@ public class _SpecialSkeletonEntity extends AbstractSkeletonEntity implements IS
@SpecialMob.Constructor @SpecialMob.Constructor
public _SpecialSkeletonEntity( EntityType<? extends _SpecialSkeletonEntity> entityType, World world ) { public _SpecialSkeletonEntity( EntityType<? extends _SpecialSkeletonEntity> entityType, World world ) {
super( entityType, world ); super( entityType, world );
specialData.initialize(); getSpecialData().initialize();
} }
@ -209,7 +209,7 @@ public class _SpecialSkeletonEntity extends AbstractSkeletonEntity implements IS
return groupData; return groupData;
} }
/** Called to change */ /** Called to set this entity's attack AI based on current equipment. */
@Override @Override
public void reassessWeaponGoal() { public void reassessWeaponGoal() {
if( level != null && !level.isClientSide ) { 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. */ /** @return Whether this entity is immune to fire damage. */
@Override @Override
public boolean fireImmune() { return specialData.isImmuneToFire(); } public boolean fireImmune() { return getSpecialData().isImmuneToFire(); }
/** Sets this entity on fire for a specific duration. */ /** Sets this entity on fire for a specific duration. */
@Override @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. */ /** 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 @Override
public void makeStuckInBlock( BlockState block, Vector3d speedMulti ) { 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. */ /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */

View file

@ -60,7 +60,7 @@ public class _SpecialSpiderEntity extends SpiderEntity implements ISpecialMob<_S
@SpecialMob.Constructor @SpecialMob.Constructor
public _SpecialSpiderEntity( EntityType<? extends _SpecialSpiderEntity> entityType, World world ) { public _SpecialSpiderEntity( EntityType<? extends _SpecialSpiderEntity> entityType, World world ) {
super( entityType, 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. */ /** @return Whether this entity is immune to fire damage. */
@Override @Override
public boolean fireImmune() { return specialData.isImmuneToFire(); } public boolean fireImmune() { return getSpecialData().isImmuneToFire(); }
/** Sets this entity on fire for a specific duration. */ /** Sets this entity on fire for a specific duration. */
@Override @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. */ /** 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 @Override
public void makeStuckInBlock( BlockState block, Vector3d speedMulti ) { 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. */ /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */

View file

@ -76,7 +76,7 @@ public class _SpecialWitherSkeletonEntity extends WitherSkeletonEntity implement
@SpecialMob.Constructor @SpecialMob.Constructor
public _SpecialWitherSkeletonEntity( EntityType<? extends _SpecialWitherSkeletonEntity> entityType, World world ) { public _SpecialWitherSkeletonEntity( EntityType<? extends _SpecialWitherSkeletonEntity> entityType, World world ) {
super( entityType, world ); super( entityType, world );
specialData.initialize(); getSpecialData().initialize();
getSpecialData().setImmuneToFire( true ); getSpecialData().setImmuneToFire( true );
} }
@ -207,7 +207,7 @@ public class _SpecialWitherSkeletonEntity extends WitherSkeletonEntity implement
return groupData; return groupData;
} }
/** Called to change */ /** Called to set this entity's attack AI based on current equipment. */
@Override @Override
public void reassessWeaponGoal() { public void reassessWeaponGoal() {
if( level != null && !level.isClientSide ) { if( level != null && !level.isClientSide ) {
@ -321,7 +321,7 @@ public class _SpecialWitherSkeletonEntity extends WitherSkeletonEntity implement
/** @return Whether this entity is immune to fire damage. */ /** @return Whether this entity is immune to fire damage. */
@Override @Override
public boolean fireImmune() { return specialData.isImmuneToFire(); } public boolean fireImmune() { return getSpecialData().isImmuneToFire(); }
/** Sets this entity on fire for a specific duration. */ /** Sets this entity on fire for a specific duration. */
@Override @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. */ /** 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 @Override
public void makeStuckInBlock( BlockState block, Vector3d speedMulti ) { 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. */ /** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */

View file

@ -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<LivingEntity> 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<? extends _SpecialZombieEntity> 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; }
}

View file

@ -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<LivingEntity> 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<? extends _SpecialZombieEntity> 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; }
}

View file

@ -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<LivingEntity> 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<? extends _SpecialZombieEntity> 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; }
}

View file

@ -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<LivingEntity> 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<? extends _SpecialZombieEntity> 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; }
}

View file

@ -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<LivingEntity> 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<? extends _SpecialZombieEntity> 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; }
}

View file

@ -10,20 +10,19 @@ import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References; import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder; import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault; 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.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes; import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.inventory.EquipmentSlotType; import net.minecraft.inventory.EquipmentSlotType;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.potion.EffectInstance; import net.minecraft.potion.EffectInstance;
import net.minecraft.potion.Effects; import net.minecraft.potion.Effects;
import net.minecraft.util.ResourceLocation; import net.minecraft.util.ResourceLocation;
import net.minecraft.world.DifficultyInstance; import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.IServerWorld;
import net.minecraft.world.World; import net.minecraft.world.World;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault; import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
@ -70,11 +69,30 @@ public class MadScientistZombieEntity extends _SpecialZombieEntity {
/** Override to change this entity's AI goals. */ /** Override to change this entity's AI goals. */
@Override @Override
protected void registerVariantGoals() { protected void registerVariantGoals() {
disableRangedAI();
AIHelper.insertGoal( goalSelector, 2, new SpecialInjectCreeperGoal<>( AIHelper.insertGoal( goalSelector, 2, new SpecialInjectCreeperGoal<>(
this, 1.0D, 20.0D, this, 1.0D, 20.0D,
( madman, creeper ) -> creeper.isAlive() && !creeper.isPowered() && madman.getSensing().canSee( creeper ) ) ); ( 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 to apply effects when this entity hits a target with a melee attack. */
@Override @Override
protected void onVariantAttack( Entity target ) { 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 = { private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "madscientist" ), GET_TEXTURE_PATH( "madscientist" ),
null, null,

View file

@ -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<LivingEntity> 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<? extends _SpecialZombieEntity> 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; }
}

View file

@ -6,15 +6,25 @@ import fathertoast.specialmobs.common.core.SpecialMobs;
import fathertoast.specialmobs.common.entity.ISpecialMob; import fathertoast.specialmobs.common.entity.ISpecialMob;
import fathertoast.specialmobs.common.entity.MobHelper; import fathertoast.specialmobs.common.entity.MobHelper;
import fathertoast.specialmobs.common.entity.SpecialMobData; import fathertoast.specialmobs.common.entity.SpecialMobData;
import fathertoast.specialmobs.common.entity.ai.AIHelper;
import fathertoast.specialmobs.common.util.References; import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder; import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault; import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.BlockState; import net.minecraft.block.BlockState;
import net.minecraft.entity.*; import net.minecraft.entity.*;
import net.minecraft.entity.ai.attributes.AttributeModifierMap; 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.monster.ZombieEntity;
import net.minecraft.entity.player.PlayerEntity; 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.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.nbt.CompoundNBT;
import net.minecraft.network.datasync.DataParameter; import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers; import net.minecraft.network.datasync.DataSerializers;
@ -22,6 +32,8 @@ import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.potion.EffectInstance; import net.minecraft.potion.EffectInstance;
import net.minecraft.util.DamageSource; import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation; 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.util.math.vector.Vector3d;
import net.minecraft.world.DifficultyInstance; import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.IServerWorld; import net.minecraft.world.IServerWorld;
@ -33,13 +45,13 @@ import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault @ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault @MethodsReturnNonnullByDefault
@SpecialMob @SpecialMob
public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_SpecialZombieEntity> { public class _SpecialZombieEntity extends ZombieEntity implements IRangedAttackMob, ISpecialMob<_SpecialZombieEntity> {
//--------------- Static Special Mob Hooks ---------------- //--------------- Static Special Mob Hooks ----------------
@SpecialMob.BestiaryInfoSupplier @SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) { public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x799C65 ); return new BestiaryInfo( 0x799C65 );//sized(0.6F, 1.95F)
} }
@SpecialMob.AttributeCreator @SpecialMob.AttributeCreator
@ -61,7 +73,8 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S
@SpecialMob.Constructor @SpecialMob.Constructor
public _SpecialZombieEntity( EntityType<? extends _SpecialZombieEntity> entityType, World world ) { public _SpecialZombieEntity( EntityType<? extends _SpecialZombieEntity> entityType, World world ) {
super( entityType, world ); super( entityType, world );
specialData.initialize(); reassessWeaponGoal();
getSpecialData().initialize();
} }
@ -71,9 +84,11 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S
@Override @Override
protected void registerGoals() { protected void registerGoals() {
super.registerGoals(); super.registerGoals();
AIHelper.removeGoals( goalSelector, ZombieAttackGoal.class );
getSpecialData().rangedAttackDamage = 2.0F; getSpecialData().rangedAttackDamage = 2.0F;
getSpecialData().rangedAttackSpread = 18.0F; getSpecialData().rangedAttackSpread = 20.0F;
getSpecialData().rangedWalkSpeed = 0.8F;
getSpecialData().rangedAttackCooldown = 30; getSpecialData().rangedAttackCooldown = 30;
getSpecialData().rangedAttackMaxCooldown = getSpecialData().rangedAttackCooldown; getSpecialData().rangedAttackMaxCooldown = getSpecialData().rangedAttackCooldown;
getSpecialData().rangedAttackMaxRange = 12.0F; getSpecialData().rangedAttackMaxRange = 12.0F;
@ -83,6 +98,68 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S
/** Override to change this entity's AI goals. */ /** Override to change this entity's AI goals. */
protected void registerVariantGoals() { } 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. */ /** Called to melee attack the target. */
@Override @Override
public boolean doHurtTarget( Entity target ) { public boolean doHurtTarget( Entity target ) {
@ -108,6 +185,9 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S
/** The parameter for special mob render scale. */ /** The parameter for special mob render scale. */
private static final DataParameter<Float> SCALE = EntityDataManager.defineId( _SpecialZombieEntity.class, DataSerializers.FLOAT ); private static final DataParameter<Float> 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. */ /** Called from the Entity.class constructor to define data watcher variables. */
@Override @Override
protected void defineSynchedData() { protected void defineSynchedData() {
@ -120,10 +200,36 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S
public ILivingEntityData finalizeSpawn( IServerWorld world, DifficultyInstance difficulty, SpawnReason spawnReason, public ILivingEntityData finalizeSpawn( IServerWorld world, DifficultyInstance difficulty, SpawnReason spawnReason,
@Nullable ILivingEntityData groupData, @Nullable CompoundNBT eggTag ) { @Nullable ILivingEntityData groupData, @Nullable CompoundNBT eggTag ) {
groupData = super.finalizeSpawn( world, difficulty, spawnReason, groupData, eggTag ); groupData = super.finalizeSpawn( world, difficulty, spawnReason, groupData, eggTag );
// TODO ranged attack reassessWeaponGoal();
return groupData; 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 ---------------- //--------------- ISpecialMob Implementation ----------------
@ -164,12 +270,12 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S
/** @return The eye height of this entity when standing. */ /** @return The eye height of this entity when standing. */
@Override @Override
protected float getStandingEyeHeight( Pose pose, EntitySize size ) { 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. */ /** @return Whether this entity is immune to fire damage. */
@Override @Override
public boolean fireImmune() { return specialData.isImmuneToFire(); } public boolean fireImmune() { return getSpecialData().isImmuneToFire(); }
/** Sets this entity on fire for a specific duration. */ /** Sets this entity on fire for a specific duration. */
@Override @Override
@ -177,6 +283,10 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S
if( !getSpecialData().isImmuneToBurning() ) super.setRemainingFireTicks( ticks ); 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. */ /** @return True if this entity can be leashed. */
@Override @Override
public boolean canBeLeashed( PlayerEntity player ) { return !isLeashed() && getSpecialData().allowLeashing(); } 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. */ /** 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 @Override
public void makeStuckInBlock( BlockState block, Vector3d speedMulti ) { 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. */ /** @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 ); getSpecialData().readFromNBT( saveTag );
readVariantSaveData( saveTag ); readVariantSaveData( saveTag );
reassessWeaponGoal();
} }
} }