Merge branch '1.16.5' of https://github.com/FatherToast/SpecialMobs into 1.16.5

This commit is contained in:
Sarinsa 2022-06-26 12:04:55 +02:00
commit c3665d3807
53 changed files with 2813 additions and 157 deletions

View file

@ -35,6 +35,7 @@ public class ClientRegister {
registerFamilyRenderers( MobFamily.ZOMBIE, SpecialZombieRenderer::new );
registerFamilyRenderers( MobFamily.SKELETON, SpecialSkeletonRenderer::new );
registerFamilyRenderers( MobFamily.WITHER_SKELETON, SpecialSkeletonRenderer::new );
registerFamilyRenderers( MobFamily.SLIME, SpecialSlimeRenderer::new );
registerFamilyRenderers( MobFamily.SPIDER, SpecialSpiderRenderer::new );
registerFamilyRenderers( MobFamily.CAVE_SPIDER, SpecialSpiderRenderer::new );
registerFamilyRenderers( MobFamily.SILVERFISH, SpecialSilverfishRenderer::new );

View file

@ -0,0 +1,34 @@
package fathertoast.specialmobs.client;
import fathertoast.specialmobs.common.entity.ai.IAngler;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.FishingRodItem;
import net.minecraft.item.IItemPropertyGetter;
import net.minecraft.item.ItemStack;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
/**
* Item property getter override that allows the fishing rod item animation to function for any entity implementing IAngler.
*/
@OnlyIn( Dist.CLIENT )
public class FishingRodItemPropertyGetter implements IItemPropertyGetter {
@Override
public float call( @Nonnull ItemStack stack, @Nullable ClientWorld world, @Nullable LivingEntity entity ) {
if( entity == null ) return 0.0F;
boolean inMainHand = entity.getMainHandItem() == stack;
boolean inOffhand = entity.getOffhandItem() == stack && !(entity.getMainHandItem().getItem() instanceof FishingRodItem);
return (inMainHand || inOffhand) && (
entity instanceof PlayerEntity && ((PlayerEntity) entity).fishing != null ||
entity instanceof IAngler && ((IAngler) entity).isLineOut() // Line added to vanilla logic
) ? 1.0F : 0.0F;
}
}

View file

@ -0,0 +1,44 @@
package fathertoast.specialmobs.client.renderer.entity;
import com.mojang.blaze3d.matrix.MatrixStack;
import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobEyesLayer;
import fathertoast.specialmobs.common.entity.ISpecialMob;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.client.renderer.entity.EntityRendererManager;
import net.minecraft.client.renderer.entity.SlimeRenderer;
import net.minecraft.entity.monster.SlimeEntity;
import net.minecraft.util.ResourceLocation;
import net.minecraftforge.api.distmarker.Dist;
import net.minecraftforge.api.distmarker.OnlyIn;
import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@OnlyIn( Dist.CLIENT )
public class SpecialSlimeRenderer extends SlimeRenderer {
private final float baseShadowRadius;
public SpecialSlimeRenderer( EntityRendererManager rendererManager ) {
super( rendererManager );
baseShadowRadius = shadowRadius;
addLayer( new SpecialMobEyesLayer<>( this ) );
// Model doesn't support size parameter
//addLayer( new SpecialMobOverlayLayer<>( this, new SlimeModel<>( 0.25F ) ) );
}
@Override
public ResourceLocation getTextureLocation( SlimeEntity entity ) {
return ((ISpecialMob<?>) entity).getSpecialData().getTexture();
}
@Override
protected void scale( SlimeEntity entity, MatrixStack matrixStack, float partialTick ) {
super.scale( entity, matrixStack, partialTick );
final float scale = ((ISpecialMob<?>) entity).getSpecialData().getRenderScale();
shadowRadius = baseShadowRadius * scale * entity.getSize(); // Factor slime size into shadow
matrixStack.scale( scale, scale, scale );
}
}

View file

@ -45,7 +45,7 @@ public class MobFamily<T extends LivingEntity> {
public static final MobFamily<ZombieEntity> 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<ZombieEntity> ZOMBIFIED_PIGLIN = new MobFamily<>(
// "ZombifiedPiglin", "zombie pigmen", 0xEA9393, new EntityType[] { EntityType.ZOMBIFIED_PIGLIN },
@ -61,10 +61,10 @@ public class MobFamily<T extends LivingEntity> {
"Brute", "Gatling", "Giant", "Knight", /*"Ninja",*/ "Sniper"//, "Spitfire"
);
// public static final MobFamily<SlimeEntity> SLIME = new MobFamily<>(
// "Slime", "slimes", 0x51A03E, new EntityType[] { EntityType.SLIME },
// "Blackberry", "Blueberry", "Caramel", "Grape", "Lemon", "Strawberry", "Watermelon"
// );
public static final MobFamily<SlimeEntity> SLIME = new MobFamily<>(
"Slime", "slimes", 0x51A03E, new EntityType[] { EntityType.SLIME },
"Blackberry", /*"Blueberry",*/ "Caramel", "Grape", "Lemon", "Strawberry", "Watermelon"
);
// public static final MobFamily<MagmaCubeEntity> MAGMA_CUBE = new MobFamily<>(
// "MagmaCube", "magma cubes", 0x340000, new EntityType[] { EntityType.MAGMA_CUBE },
// "Flying", "Hardened", "Sticky", "Volatile"

View file

@ -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,19 +55,20 @@ public class SpecialMobs {
* - skeletons
* - use shields
* - babies
* o wither skeletons
* o use shields
* o babies
* o slimes
* o use attack damage attribute
* - wither skeletons
* - use shields
* - babies
* - slimes
* o smallest size can deal damage
* o magma cubes
* o use attack damage attribute
* o smallest size can deal damage
* - spiders
* o ranged attack AI
* - cave spiders
* o ranged attack AI
* - silverfish
* ? ranged attack AI
* + puffer
* - endermen
* o witches
* o ability to equip held items
@ -87,8 +92,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 );

View file

@ -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() );
}

View file

@ -11,7 +11,7 @@ import java.util.ArrayList;
*/
public final class AIHelper {
/** Inserts an AI goal at the specified priority. Existing goals have their priority adjusted accordingly. */
/** Inserts an AI goal at the specified priority. Existing goals have their priority incremented accordingly. */
public static void insertGoal( GoalSelector ai, int priority, Goal goal ) {
for( PrioritizedGoal task : new ArrayList<>( ai.availableGoals ) ) {
if( task.getPriority() >= priority ) task.priority++;
@ -19,6 +19,14 @@ public final class AIHelper {
ai.addGoal( priority, goal );
}
/** Inserts an AI goal at the specified priority. Existing goals have their priority decremented accordingly. */
public static void insertGoalReverse( GoalSelector ai, int priority, Goal goal ) {
for( PrioritizedGoal task : new ArrayList<>( ai.availableGoals ) ) {
if( task.getPriority() <= priority ) task.priority--;
}
ai.addGoal( priority, goal );
}
/** Removes all goals with the specified priority. */
public static void removeGoals( GoalSelector ai, int priority ) {
for( PrioritizedGoal task : new ArrayList<>( ai.availableGoals ) ) {

View file

@ -0,0 +1,14 @@
package fathertoast.specialmobs.common.entity.ai;
/**
* Monsters must implement this interface to shoot fish hooks.
* This allows get and set methods for the fish hook so that the server can communicate rendering info to the client.
*/
public interface IAngler {
/** Sets this angler's line as out (or in). */
void setLineOut( boolean value );
/** @return Whether this angler's line is out. */
boolean isLineOut();
}

View file

@ -0,0 +1,25 @@
package fathertoast.specialmobs.common.entity.ai;
import net.minecraft.block.BlockState;
import javax.annotation.Nullable;
/**
* Monsters must implement this interface to use the ninja goal AI.
* This allows get and set methods for the disguise block and immobile state.
*/
public interface INinja {
/** @return Whether this ninja is currently immobile. */
boolean isImmobile();
/** Sets this ninja's immovable state. When activated, the entity is 'snapped' to the nearest block position. */
void setImmobile( boolean value );
/** @return The block being hidden (rendered) as, or null if not hiding. */
@Nullable
BlockState getDisguiseBlock();
/** Sets the block being hidden (rendered) as, set to null to cancel hiding. */
void setDisguiseBlock( @Nullable BlockState block );
}

View file

@ -12,11 +12,13 @@ import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.SpawnReason;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.monster.SlimeEntity;
import net.minecraft.item.Items;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundEvents;
import net.minecraft.util.text.ITextComponent;
import net.minecraft.world.IServerWorld;
import net.minecraft.world.World;
@ -32,6 +34,7 @@ public class MotherCaveSpiderEntity extends _SpecialCaveSpiderEntity {
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.9F, 0.6F );
return new BestiaryInfo( 0xB300B3 );
}
@ -59,6 +62,7 @@ public class MotherCaveSpiderEntity extends _SpecialCaveSpiderEntity {
@SpecialMob.Constructor
public MotherCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 0.8F );
getSpecialData().setRegenerationTime( 30 );
xpReward += 1;
@ -88,16 +92,18 @@ public class MotherCaveSpiderEntity extends _SpecialCaveSpiderEntity {
if( extraBabies > 0 && amount > 1.0F && level instanceof IServerWorld && random.nextFloat() < 0.33F ) {
extraBabies--;
spawnBaby( 0.66F, null );
playSound( SoundEvents.EGG_THROW, 1.0F, 2.0F / (random.nextFloat() * 0.4F + 0.8F) );
}
return true;
}
return false;
}
/** Called each tick to update this entity while it's dead. */
/** Called to remove this entity from the world. Includes death, unloading, interdimensional travel, etc. */
@Override
protected void tickDeath() {
if( deathTime == 19 && level instanceof IServerWorld ) { // At 19, the entity will be immediately removed upon call to super method
public void remove( boolean keepData ) {
//noinspection deprecation
if( isDeadOrDying() && !removed && level instanceof IServerWorld ) { // Same conditions as slime splitting
// Spawn babies on death
final int babiesToSpawn = babies + extraBabies;
ILivingEntityData groupData = null;
@ -106,8 +112,7 @@ public class MotherCaveSpiderEntity extends _SpecialCaveSpiderEntity {
}
playSound( SoundEvents.EGG_THROW, 1.0F, 2.0F / (random.nextFloat() * 0.4F + 0.8F) );
}
super.tickDeath();
super.remove( keepData );
}
/** Helper method to simplify spawning babies. */

View file

@ -60,7 +60,7 @@ public class _SpecialCaveSpiderEntity extends CaveSpiderEntity implements ISpeci
@SpecialMob.Constructor
public _SpecialCaveSpiderEntity( EntityType<? extends _SpecialCaveSpiderEntity> entityType, World world ) {
super( entityType, world );
specialData.initialize();
getSpecialData().initialize();
}
@ -82,14 +82,11 @@ public class _SpecialCaveSpiderEntity extends CaveSpiderEntity implements ISpeci
/** Override to change this entity's AI goals. */
protected void registerVariantGoals() { }
/** Called to melee attack the target. */
/** Called when this entity successfully damages a target to apply on-hit effects. */
@Override
public boolean doHurtTarget( Entity target ) {
if( super.doHurtTarget( target ) ) {
onVariantAttack( target );
return true;
}
return false;
public void doEnchantDamageEffects( LivingEntity attacker, Entity target ) {
onVariantAttack( target );
super.doEnchantDamageEffects( attacker, target );
}
/** Override to apply effects when this entity hits a target with a melee attack. */
@ -166,12 +163,12 @@ public class _SpecialCaveSpiderEntity extends CaveSpiderEntity implements ISpeci
/** @return The eye height of this entity when standing. */
@Override
protected float getStandingEyeHeight( Pose pose, EntitySize size ) {
return super.getStandingEyeHeight( pose, size ) * getSpecialData().getBaseScale() * (isBaby() ? 0.53448F : 1.0F);
return 0.65F * getSpecialData().getBaseScale() * (isBaby() ? 0.53448F : 1.0F); // Use base spider scale instead of super
}
/** @return Whether this entity is immune to fire damage. */
@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 +183,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. */

View file

@ -26,6 +26,7 @@ public class DeathCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.9F, 2.6F );
return new BestiaryInfo( 0xCD0000 );
}
@ -52,6 +53,7 @@ public class DeathCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.Constructor
public DeathCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 1.5F );
setExplodesWhileBurning( true );
xpReward += 2;
}

View file

@ -32,6 +32,7 @@ public class SplittingCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.7F, 1.99F );
return new BestiaryInfo( 0x5F9D22, BestiaryInfo.BaseWeight.LOW );
}
@ -57,6 +58,7 @@ public class SplittingCreeperEntity extends _SpecialCreeperEntity {
@SpecialMob.Constructor
public SplittingCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 1.2F );
getSpecialData().setImmuneToBurning( true );
setExplodesWhenShot( true );
xpReward += 2;

View file

@ -66,7 +66,7 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
@SpecialMob.Constructor
public _SpecialCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
super( entityType, world );
specialData.initialize();
getSpecialData().initialize();
}
@ -82,14 +82,11 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
/** Override to change this entity's AI goals. */
protected void registerVariantGoals() { }
/** Called to melee attack the target. */
/** Called when this entity successfully damages a target to apply on-hit effects. */
@Override
public boolean doHurtTarget( Entity target ) {
if( super.doHurtTarget( target ) ) {
onVariantAttack( target );
return true;
}
return false;
public void doEnchantDamageEffects( LivingEntity attacker, Entity target ) {
onVariantAttack( target );
super.doEnchantDamageEffects( attacker, target );
}
/** Override to apply effects when this entity hits a target with a melee attack. */
@ -293,15 +290,15 @@ public class _SpecialCreeperEntity extends CreeperEntity implements ISpecialMob<
getSpecialData().tick();
}
/** @return The eye height of this entity when standing. */
@Override
protected float getStandingEyeHeight( Pose pose, EntitySize size ) {
return super.getStandingEyeHeight( pose, size ) * getSpecialData().getBaseScale() * (isBaby() ? 0.53448F : 1.0F);
}
// /** @return The eye height of this entity when standing. */ - Creepers use auto-scaled eye height
// @Override
// protected float getStandingEyeHeight( Pose pose, EntitySize size ) {
// return super.getStandingEyeHeight( pose, size ) * getSpecialData().getBaseScale() * (isBaby() ? 0.53448F : 1.0F);
// }
/** @return Whether this entity is immune to fire damage. */
@Override
public boolean fireImmune() { return specialData.isImmuneToFire(); }
public boolean fireImmune() { return getSpecialData().isImmuneToFire(); }
/** Sets this entity on fire for a specific duration. */
@Override
@ -316,7 +313,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. */

View file

@ -10,9 +10,11 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.effect.LightningBoltEntity;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import javax.annotation.ParametersAreNonnullByDefault;
@ -67,6 +69,10 @@ public class LightningEndermanEntity extends _SpecialEndermanEntity {
}
}
/** Called when this entity is struck by lightning. */
@Override
public void thunderHit( ServerWorld world, LightningBoltEntity lightningBolt ) { }
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "lightning" ),
GET_TEXTURE_PATH( "lightning_eyes" )

View file

@ -23,7 +23,7 @@ public class MiniEndermanEntity extends _SpecialEndermanEntity {
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.5F, 0.9F );
entityType.sized( 0.5F, 0.99F );
return new BestiaryInfo( 0xFFC0CB, BestiaryInfo.BaseWeight.LOW );
}

View file

@ -58,7 +58,7 @@ public class _SpecialEndermanEntity extends EndermanEntity implements ISpecialMo
@SpecialMob.Constructor
public _SpecialEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) {
super( entityType, world );
specialData.initialize();
getSpecialData().initialize();
getSpecialData().setDamagedByWater( true );
}
@ -75,14 +75,11 @@ public class _SpecialEndermanEntity extends EndermanEntity implements ISpecialMo
/** Override to change this entity's AI goals. */
protected void registerVariantGoals() { }
/** Called to melee attack the target. */
/** Called when this entity successfully damages a target to apply on-hit effects. */
@Override
public boolean doHurtTarget( Entity target ) {
if( super.doHurtTarget( target ) ) {
onVariantAttack( target );
return true;
}
return false;
public void doEnchantDamageEffects( LivingEntity attacker, Entity target ) {
onVariantAttack( target );
super.doEnchantDamageEffects( attacker, target );
}
/** Override to apply effects when this entity hits a target with a melee attack. */
@ -164,7 +161,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 +176,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. */

View file

@ -0,0 +1,14 @@
package fathertoast.specialmobs.common.entity.projectile;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.world.World;
public abstract class BugSpitEntity extends Entity {
public BugSpitEntity( EntityType<? extends BugSpitEntity> entityType, World world ) {
super( entityType, world );
}
//TODO
}

View file

@ -0,0 +1,14 @@
package fathertoast.specialmobs.common.entity.projectile;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.world.World;
public abstract class SpecialFishHookEntity extends Entity {
public SpecialFishHookEntity( EntityType<? extends SpecialFishHookEntity> entityType, World world ) {
super( entityType, world );
}
//TODO
}

View file

@ -0,0 +1,99 @@
package fathertoast.specialmobs.common.entity.silverfish;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.ai.IAngler;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootEntryItemBuilder;
import fathertoast.specialmobs.datagen.loot.LootPoolBuilder;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@SpecialMob
public class FishingSilverfishEntity extends _SpecialSilverfishEntity implements IAngler {
//--------------- Static Special Mob Hooks ----------------
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.5F, 0.4F );
return new BestiaryInfo( 0x2D41F4 );
//TODO theme - fishing
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialSilverfishEntity.createAttributes() )
.addAttribute( Attributes.MAX_HEALTH, 4.0 )
.multAttribute( Attributes.MOVEMENT_SPEED, 0.9 )
.build();
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Fishing Silverfish",
"", "", "", "", "", "" );//TODO
}
@SpecialMob.LootTableProvider
public static void buildLootTable( LootTableBuilder loot ) {
addBaseLoot( loot );
loot.addPool( new LootPoolBuilder( "common" )
.addEntry( new LootEntryItemBuilder( Items.COD ).setCount( 0, 2 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() )
.toLootPool() );
}
@SpecialMob.Constructor
public FishingSilverfishEntity( EntityType<? extends _SpecialSilverfishEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 1.2F );
getSpecialData().setCanBreatheInWater( true );
getSpecialData().setIgnoreWaterPush( true );
xpReward += 2;
}
//--------------- Variant-Specific Implementations ----------------
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
getSpecialData().rangedAttackSpread = 10.0F;
getSpecialData().rangedAttackCooldown = 32;
getSpecialData().rangedAttackMaxCooldown = 48;
getSpecialData().rangedAttackMaxRange = 10.0F;
//TODO add angler AI @ 4
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "fishing" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
//--------------- IAngler Implementations ----------------
/** Sets this angler's line as out (or in). */
@Override
public void setLineOut( boolean value ) { }
/** @return Whether this angler's line is out. */
@Override
public boolean isLineOut() { return false; }
}

View file

@ -25,7 +25,7 @@ public class ToughSilverfishEntity extends _SpecialSilverfishEntity {
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.6F, 0.9F );
entityType.sized( 0.6F, 0.45F );
return new BestiaryInfo( 0xDD0E0E, BestiaryInfo.BaseWeight.LOW );
}

View file

@ -62,7 +62,7 @@ public class _SpecialSilverfishEntity extends SilverfishEntity implements ISpeci
@SpecialMob.Constructor
public _SpecialSilverfishEntity( EntityType<? extends _SpecialSilverfishEntity> entityType, World world ) {
super( entityType, world );
specialData.initialize();
getSpecialData().initialize();
}
@ -84,14 +84,11 @@ public class _SpecialSilverfishEntity extends SilverfishEntity implements ISpeci
/** Override to change this entity's AI goals. */
protected void registerVariantGoals() { }
/** Called to melee attack the target. */
/** Called when this entity successfully damages a target to apply on-hit effects. */
@Override
public boolean doHurtTarget( Entity target ) {
if( super.doHurtTarget( target ) ) {
onVariantAttack( target );
return true;
}
return false;
public void doEnchantDamageEffects( LivingEntity attacker, Entity target ) {
onVariantAttack( target );
super.doEnchantDamageEffects( attacker, target );
}
/** Override to apply effects when this entity hits a target with a melee attack. */
@ -191,7 +188,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 +203,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. */

View file

@ -32,6 +32,7 @@ public class BruteSkeletonEntity extends _SpecialSkeletonEntity {
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.7F, 2.4F );
return new BestiaryInfo( 0xFFF87E );
}
@ -59,6 +60,7 @@ public class BruteSkeletonEntity extends _SpecialSkeletonEntity {
@SpecialMob.Constructor
public BruteSkeletonEntity( EntityType<? extends _SpecialSkeletonEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 1.2F );
xpReward += 2;
}

View file

@ -24,7 +24,7 @@ public class GiantSkeletonEntity extends _SpecialSkeletonEntity {
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.9F, 2.7F );
entityType.sized( 0.9F, 2.99F );
return new BestiaryInfo( 0x494949 );
}

View file

@ -0,0 +1,222 @@
package fathertoast.specialmobs.common.entity.skeleton;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.ai.INinja;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Hand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Optional;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@SpecialMob
public class NinjaSkeletonEntity extends _SpecialSkeletonEntity implements INinja {
//--------------- Static Special Mob Hooks ----------------
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x333366 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialSkeletonEntity.createAttributes() )
.multAttribute( Attributes.MOVEMENT_SPEED, 1.2 )
.build();
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Skeleton Ninja",
"", "", "", "", "", "" );//TODO
}
@SpecialMob.LootTableProvider
public static void buildLootTable( LootTableBuilder loot ) {
addBaseLoot( loot );
loot.addUncommonDrop( "uncommon", Blocks.INFESTED_STONE, Blocks.INFESTED_COBBLESTONE, Blocks.INFESTED_STONE_BRICKS,
Blocks.INFESTED_CRACKED_STONE_BRICKS, Blocks.INFESTED_MOSSY_STONE_BRICKS, Blocks.INFESTED_CHISELED_STONE_BRICKS );
}
@SpecialMob.Constructor
public NinjaSkeletonEntity( EntityType<? extends _SpecialSkeletonEntity> entityType, World world ) {
super( entityType, world );
xpReward += 2;
}
//--------------- Variant-Specific Implementations ----------------
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
setRangedAI( 1.0, 10, 9.0F );
//TODO AIHelper.insertGoalReverse( goalSelector, getVariantAttackPriority() - 1, null );
}
/** Called during spawn finalization to set starting equipment. */
@Override
protected void populateDefaultEquipmentSlots( DifficultyInstance difficulty ) {
super.populateDefaultEquipmentSlots( difficulty );
setCanPickUpLoot( true );
}
/** Override to change this entity's chance to spawn with a melee weapon. */
@Override
protected double getVariantMeleeChance() { return 0.5; }
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
revealTo( target );
}
/** @return Attempts to damage this entity; returns true if the hit was successful. */
@Override
public boolean hurt( DamageSource source, float amount ) {
if( super.hurt( source, amount ) ) {
revealTo( source.getEntity() );
return true;
}
return false;
}
/** @return Interacts (right click) with this entity and returns the result. */
@Override
public ActionResultType mobInteract( PlayerEntity player, Hand hand ) {
// Attack if the player tries to right click the "block"
if( !level.isClientSide() && getDisguiseBlock() != null ) revealTo( player );
return super.mobInteract( player, hand );
}
/** Called each tick to update this entity. */
@Override
public void tick() {
// TODO can this be moved to the ninja AI?
if( !level.isClientSide() ) {
if( canHide ) {
//EntityAINinja.startHiding( this ); TODO
}
else if( onGround && getDisguiseBlock() == null &&
(getTarget() == null || getTarget() instanceof PlayerEntity && ((PlayerEntity) getTarget()).isCreative()) ) {
canHide = true;
}
}
super.tick();
}
// // Moves this entity.
// @Override TODO
// public void move( MoverType type, double x, double y, double z ) {
// if( isImmobile() && type != MoverType.PISTON ) {
// motionY = 0.0;
// }
// else {
// super.move( type, x, y, z );
// }
// }
//
// // Returns true if this entity should push and be pushed by other entities when colliding.
// @Override
// public boolean canBePushed() { return !isImmobile(); }
/** Sets this entity on fire for a specific duration. */
@Override
public void setRemainingFireTicks( int ticks ) {
if( !isImmobile() ) super.setRemainingFireTicks( ticks );
}
/** Reveals this ninja and sets its target so that it doesn't immediately re-disguise itself. */
public void revealTo( @Nullable Entity target ) {
setDisguiseBlock( null );
if( target instanceof LivingEntity ) setTarget( (LivingEntity) target );
}
private static final ResourceLocation[] TEXTURES = {
new ResourceLocation( "textures/entity/skeleton/skeleton.png" ),
null,
GET_TEXTURE_PATH( "ninja_overlay" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
//--------------- INinja Implementations ----------------
/** The parameter for the ninja immobile state. */
private static final DataParameter<Boolean> IS_HIDING = EntityDataManager.defineId( NinjaSkeletonEntity.class, DataSerializers.BOOLEAN );
/** The parameter for the ninja disguise block. */
private static final DataParameter<Optional<BlockState>> HIDING_BLOCK = EntityDataManager.defineId( NinjaSkeletonEntity.class, DataSerializers.BLOCK_STATE );
private boolean canHide = true;
/** Called from the Entity.class constructor to define data watcher variables. */
@Override
protected void defineSynchedData() {
super.defineSynchedData();
entityData.define( IS_HIDING, false );
entityData.define( HIDING_BLOCK, Optional.empty() );
}
/** @return Whether this ninja is currently immobile. */
@Override
public boolean isImmobile() { return getEntityData().get( IS_HIDING ); }
/** Sets this ninja's immovable state. When activated, the entity is 'snapped' to the nearest block position. */
@Override
public void setImmobile( boolean value ) {
if( value != isImmobile() ) {
getEntityData().set( IS_HIDING, value );
if( value ) {
clearFire();
moveTo( Math.floor( getX() ) + 0.5, Math.floor( getY() ), Math.floor( getZ() ) + 0.5 );
}
}
}
/** @return The block being hidden (rendered) as, or null if not hiding. */
@Nullable
@Override
public BlockState getDisguiseBlock() {
if( isAlive() ) return getEntityData().get( HIDING_BLOCK ).orElse( null );
return null;
}
/** Sets the block being hidden (rendered) as, set to null to cancel hiding. */
@Override
public void setDisguiseBlock( @Nullable BlockState block ) {
getEntityData().set( HIDING_BLOCK, Optional.ofNullable( block ) );
canHide = false;
// Smoke puff when emerging from disguise
if( block == null ) {
//spawnExplosionParticle(); TODO
}
}
}

View file

@ -26,7 +26,7 @@ public class SpitfireSkeletonEntity extends _SpecialSkeletonEntity {
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.9F, 2.7F ).fireImmune();
entityType.sized( 0.9F, 2.99F ).fireImmune();
return new BestiaryInfo( 0xDC1A00, BestiaryInfo.BaseWeight.LOW );
//TODO theme - fire
}

View file

@ -79,7 +79,7 @@ public class _SpecialSkeletonEntity extends AbstractSkeletonEntity implements IS
@SpecialMob.Constructor
public _SpecialSkeletonEntity( EntityType<? extends _SpecialSkeletonEntity> entityType, World world ) {
super( entityType, world );
specialData.initialize();
getSpecialData().initialize();
}
@ -164,14 +164,11 @@ public class _SpecialSkeletonEntity extends AbstractSkeletonEntity implements IS
return arrow;
}
/** Called to melee attack the target. */
/** Called when this entity successfully damages a target to apply on-hit effects. */
@Override
public boolean doHurtTarget( Entity target ) {
if( super.doHurtTarget( target ) ) {
onVariantAttack( target );
return true;
}
return false;
public void doEnchantDamageEffects( LivingEntity attacker, Entity target ) {
onVariantAttack( target );
super.doEnchantDamageEffects( attacker, target );
}
/** Override to apply effects when this entity hits a target with a melee attack. */
@ -209,7 +206,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 +355,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 +370,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. */

View file

@ -0,0 +1,238 @@
package fathertoast.specialmobs.common.entity.slime;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.ExplosionHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.AreaEffectCloudEntity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.ai.goal.Goal;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.particles.IParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.potion.EffectInstance;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.Hand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.SoundEvents;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@SpecialMob
public class BlackberrySlimeEntity extends _SpecialSlimeEntity {
//--------------- Static Special Mob Hooks ----------------
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x331133 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return _SpecialSlimeEntity.createAttributes(); // Slimes define their attributes elsewhere based on size
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Blackberry Slime",
"", "", "", "", "", "" );//TODO
}
@SpecialMob.LootTableProvider
public static void buildLootTable( LootTableBuilder loot ) {
addBaseLoot( loot );
loot.addCommonDrop( "common", Items.GUNPOWDER );
loot.addUncommonDrop( "uncommon", Items.BLACK_DYE );
}
@SpecialMob.Constructor
public BlackberrySlimeEntity( EntityType<? extends _SpecialSlimeEntity> entityType, World world ) {
super( entityType, world );
slimeExperienceValue += 2;
}
//--------------- Variant-Specific Implementations ----------------
private static final byte MAX_FUSE = 30;
private int fuse = 0;
private int swellDir = 0;
private boolean ignited = false;
/** Override to modify this slime's base attributes by size. */
@Override
protected void modifyVariantAttributes( int size ) {
addAttribute( Attributes.MAX_HEALTH, 2.0 * size );
}
/** @return This slime's particle type for jump effects. */
@Override
protected IParticleData getParticleType() { return ParticleTypes.SMOKE; }
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
goalSelector.addGoal( 0, new SlimeSwellGoal( this ) );
}
/** Called each tick to update this entity. */
@Override
public void tick() {
if( isAlive() && !level.isClientSide() ) {
if( ignited ) swellDir = 1;
if( swellDir > 0 ) {
if( fuse == 0 ) {
playSound( SoundEvents.CREEPER_PRIMED, 1.0F, 0.5F );
}
else if( fuse >= MAX_FUSE ) {
dead = true;
ExplosionHelper.explode( this, getSize() + 0.5F, true, false );
remove();
spawnLingeringCloud();
}
else {
changeFuse( +1 );
}
}
else if( swellDir < 0 && fuse > 0 ) {
changeFuse( -1 );
if( fuse <= 0 ) swellDir = 0;
}
}
super.tick();
}
/** Changes the fuse by a specific amount. The fuse must always be changed in step with render scale, so they stay in sync. */
private void changeFuse( int change ) {
fuse += change;
getSpecialData().setRenderScale( getSpecialData().getRenderScale() + change * 0.014F );
}
/** Called to create a lingering effect cloud as part of this slime's explosion 'attack'. */
protected void spawnLingeringCloud() {
final List<EffectInstance> effects = new ArrayList<>( getActiveEffects() );
if( !effects.isEmpty() ) {
final AreaEffectCloudEntity potionCloud = new AreaEffectCloudEntity( level, getX(), getY(), getZ() );
potionCloud.setRadius( getSize() + 0.5F );
potionCloud.setRadiusOnUse( -0.5F );
potionCloud.setWaitTime( 10 );
potionCloud.setDuration( potionCloud.getDuration() / 2 );
potionCloud.setRadiusPerTick( -potionCloud.getRadius() / (float) potionCloud.getDuration() );
for( EffectInstance effect : effects ) {
potionCloud.addEffect( new EffectInstance( effect ) );
}
level.addFreshEntity( potionCloud );
}
}
/** @return This entity's max fall distance. */
@Override
public int getMaxFallDistance() { return getTarget() == null ? 3 : 3 + (int) (getHealth() - 1.0F); }
/** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */
@Override
public boolean causeFallDamage( float distance, float damageMultiplier ) {
final boolean success = super.causeFallDamage( distance, damageMultiplier );
// Speed up fuse from falling like creepers
changeFuse( (int) (distance * 1.5F) );
if( fuse > MAX_FUSE - 5 ) changeFuse( MAX_FUSE - 5 - fuse );
return success;
}
/** @return Interacts (right click) with this entity and returns the result. */
@Override
public ActionResultType mobInteract( PlayerEntity player, Hand hand ) {
final ItemStack item = player.getItemInHand( hand );
if( item.getItem() == Items.FLINT_AND_STEEL ) {
// Allow players to ignite blackberry slimes like creepers
level.playSound( player, getX(), getY(), getZ(), SoundEvents.FLINTANDSTEEL_USE, getSoundSource(),
1.0F, random.nextFloat() * 0.4F + 0.8F );
if( !level.isClientSide ) {
ignited = true;
item.hurtAndBreak( 1, player, ( entity ) -> entity.broadcastBreakEvent( hand ) );
}
return ActionResultType.sidedSuccess( level.isClientSide );
}
return super.mobInteract( player, hand );
}
/** Override to save data to this entity's NBT data. */
@Override
public void addVariantSaveData( CompoundNBT saveTag ) {
saveTag.putByte( References.TAG_FUSE_TIME, (byte) fuse );
}
/** Override to load data from this entity's NBT data. */
@Override
public void readVariantSaveData( CompoundNBT saveTag ) {
if( saveTag.contains( References.TAG_FUSE_TIME, References.NBT_TYPE_NUMERICAL ) )
fuse = saveTag.getByte( References.TAG_FUSE_TIME );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "blackberry" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
//--------------- Nested Classes ----------------
/** The "creeper swell" goal repurposed for use on a slime. */
private static class SlimeSwellGoal extends Goal {
private final BlackberrySlimeEntity slime;
private LivingEntity target;
public SlimeSwellGoal( BlackberrySlimeEntity entity ) {
slime = entity;
setFlags( EnumSet.of( Flag.MOVE ) );
}
public boolean canUse() {
final LivingEntity target = slime.getTarget();
return slime.swellDir > 0 || target != null && slime.distanceToSqr( target ) < 9.0F + (slime.getSize() - 1) * 2.0F;
}
public void start() {
slime.getNavigation().stop();
target = slime.getTarget();
}
public void stop() {
slime.swellDir = -1;
target = null;
}
public void tick() {
if( target == null || slime.distanceToSqr( target ) > 49.0 || !slime.getSensing().canSee( target ) ) {
slime.swellDir = -1;
}
else {
slime.swellDir = 1;
}
}
}
}

View file

@ -0,0 +1,83 @@
package fathertoast.specialmobs.common.entity.slime;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.particles.IParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@SpecialMob
public class BlueberrySlimeEntity extends _SpecialSlimeEntity {
//--------------- Static Special Mob Hooks ----------------
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x766BBC );
//TODO theme - water
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return _SpecialSlimeEntity.createAttributes(); // Slimes define their attributes elsewhere based on size
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Blueberry Slime",
"", "", "", "", "", "" );//TODO
}
@SpecialMob.LootTableProvider
public static void buildLootTable( LootTableBuilder loot ) {
addBaseLoot( loot );
loot.addRareDrop( "rare", Items.GOLD_NUGGET, Items.PRISMARINE_SHARD, Items.PRISMARINE_CRYSTALS );
loot.addUncommonDrop( "uncommon", Items.BLUE_DYE );
}
@SpecialMob.Constructor
public BlueberrySlimeEntity( EntityType<? extends _SpecialSlimeEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setImmuneToBurning( true );
getSpecialData().setCanBreatheInWater( true );
slimeExperienceValue += 1;
}
//--------------- Variant-Specific Implementations ----------------
/** Override to modify this slime's base attributes by size. */
@Override
protected void modifyVariantAttributes( int size ) {
addAttribute( Attributes.ATTACK_DAMAGE, 1.0 * size );
}
/** @return This slime's particle type for jump effects. */
@Override
protected IParticleData getParticleType() { return ParticleTypes.SPLASH; }
/** Override to change this entity's AI goals. */
@Override
protected boolean isAffectedByFluids() { return false; } // TODO figure out the right way to do this
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "blueberry" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -0,0 +1,108 @@
package fathertoast.specialmobs.common.entity.slime;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.particles.IParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.List;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@SpecialMob
public class CaramelSlimeEntity extends _SpecialSlimeEntity {
//--------------- Static Special Mob Hooks ----------------
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x9D733F );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return _SpecialSlimeEntity.createAttributes(); // Slimes define their attributes elsewhere based on size
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Caramel Slime",
"", "", "", "", "", "" );//TODO
}
@SpecialMob.LootTableProvider
public static void buildLootTable( LootTableBuilder loot ) {
addBaseLoot( loot );
loot.addCommonDrop( "common", Items.SUGAR );
loot.addUncommonDrop( "uncommon", Items.ORANGE_DYE );
}
@SpecialMob.Constructor
public CaramelSlimeEntity( EntityType<? extends _SpecialSlimeEntity> entityType, World world ) {
super( entityType, world );
slimeExperienceValue += 2;
}
//--------------- Variant-Specific Implementations ----------------
private final DamageSource grabDamageSource = DamageSource.mobAttack( this ).bypassArmor().bypassMagic();
private int grabTime;
/** Override to modify this slime's base attributes by size. */
@Override
protected void modifyVariantAttributes( int size ) {
addAttribute( Attributes.MAX_HEALTH, 4.0 * size );
}
/** @return This slime's particle type for jump effects. */
@Override
protected IParticleData getParticleType() { return ParticleTypes.SPLASH; } //TODO
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
if( grabTime <= -20 && getPassengers().isEmpty() ) {
if( target.startRiding( this, true ) ) grabTime = 20;
}
}
/** Called each tick to update this entity. */
@Override
public void tick() {
super.tick();
grabTime--;
final List<Entity> riders = getPassengers();
if( grabTime <= 0 && !riders.isEmpty() ) {
for( Entity rider : riders ) {
if( rider instanceof LivingEntity ) {
rider.hurt( grabDamageSource, 1.0F );
grabTime = 10;
}
}
}
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "caramel" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -0,0 +1,87 @@
package fathertoast.specialmobs.common.entity.slime;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.ai.SpecialLeapAtTargetGoal;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.particles.IParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@SpecialMob
public class GrapeSlimeEntity extends _SpecialSlimeEntity {
//--------------- Static Special Mob Hooks ----------------
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0xB333B3 );
//TODO theme - mountain
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return _SpecialSlimeEntity.createAttributes(); // Slimes define their attributes elsewhere based on size
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Grape Slime",
"", "", "", "", "", "" );//TODO
}
@SpecialMob.LootTableProvider
public static void buildLootTable( LootTableBuilder loot ) {
addBaseLoot( loot );
loot.addGuaranteedDrop( "base", Items.SLIME_BALL, 1 );
loot.addUncommonDrop( "uncommon", Items.PURPLE_DYE );
}
@SpecialMob.Constructor
public GrapeSlimeEntity( EntityType<? extends _SpecialSlimeEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setFallDamageMultiplier( 0.0F );
slimeExperienceValue += 1;
}
//--------------- Variant-Specific Implementations ----------------
/** Override to modify this slime's base attributes by size. */
@Override
protected void modifyVariantAttributes( int size ) {
addAttribute( Attributes.MAX_HEALTH, 4.0 * size );
multAttribute( Attributes.MOVEMENT_SPEED, 1.2 );
}
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
goalSelector.addGoal( 0, new SpecialLeapAtTargetGoal(
this, 10, 6.0F, 12.0F, 1.1F, 2.6F ) );
}
/** @return This slime's particle type for jump effects. */
@Override
protected IParticleData getParticleType() { return ParticleTypes.SPLASH; } //TODO
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "grape" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -0,0 +1,102 @@
package fathertoast.specialmobs.common.entity.slime;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.util.ExplosionHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.effect.LightningBoltEntity;
import net.minecraft.item.Items;
import net.minecraft.particles.IParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@SpecialMob
public class LemonSlimeEntity extends _SpecialSlimeEntity {
//--------------- Static Special Mob Hooks ----------------
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.fireImmune();
return new BestiaryInfo( 0xE6E861 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return _SpecialSlimeEntity.createAttributes(); // Slimes define their attributes elsewhere based on size
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Lemon Slime",
"", "", "", "", "", "" );//TODO
}
@SpecialMob.LootTableProvider
public static void buildLootTable( LootTableBuilder loot ) {
addBaseLoot( loot );
loot.addCommonDrop( "common", Items.REDSTONE, 1 );
loot.addUncommonDrop( "uncommon", Items.YELLOW_DYE );
}
@SpecialMob.Constructor
public LemonSlimeEntity( EntityType<? extends _SpecialSlimeEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setImmuneToFire( true );
slimeExperienceValue += 2;
}
//--------------- Variant-Specific Implementations ----------------
/** Override to modify this slime's base attributes by size. */
@Override
protected void modifyVariantAttributes( int size ) {
addAttribute( Attributes.MAX_HEALTH, 2.0 * size );
}
/** @return This slime's particle type for jump effects. */
@Override
protected IParticleData getParticleType() { return ParticleTypes.SPLASH; } //TODO
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
if( target instanceof LivingEntity ) {
ExplosionHelper.spawnLightning( level, target.getX(), target.getY(), target.getZ() );
// Knock self back
final float forwardPower = 1.1F;
final float upwardPower = 1.0F;
final Vector3d vKnockback = new Vector3d( target.getX() - getX(), 0.0, target.getZ() - getZ() )
.normalize().scale( -forwardPower ).add( getDeltaMovement().scale( 0.2F ) );
setDeltaMovement( vKnockback.x, 0.4 * upwardPower, vKnockback.z );
}
}
/** Called when this entity is struck by lightning. */
@Override
public void thunderHit( ServerWorld world, LightningBoltEntity lightningBolt ) { }
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "lemon" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -0,0 +1,86 @@
package fathertoast.specialmobs.common.entity.slime;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.ai.SpecialLeapAtTargetGoal;
import fathertoast.specialmobs.common.util.ExplosionHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.effect.LightningBoltEntity;
import net.minecraft.item.Items;
import net.minecraft.particles.IParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import net.minecraft.world.server.ServerWorld;
import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@SpecialMob
public class StrawberrySlimeEntity extends _SpecialSlimeEntity {
//--------------- Static Special Mob Hooks ----------------
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.fireImmune();
return new BestiaryInfo( 0xBE696B );
//TODO theme - fire
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return _SpecialSlimeEntity.createAttributes(); // Slimes define their attributes elsewhere based on size
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Strawberry Slime",
"", "", "", "", "", "" );//TODO
}
@SpecialMob.LootTableProvider
public static void buildLootTable( LootTableBuilder loot ) {
addBaseLoot( loot );
loot.addCommonDrop( "common", Items.FIRE_CHARGE, 1 );
loot.addUncommonDrop( "uncommon", Items.RED_DYE );
}
@SpecialMob.Constructor
public StrawberrySlimeEntity( EntityType<? extends _SpecialSlimeEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setImmuneToFire( true );
getSpecialData().setDamagedByWater( true );
slimeExperienceValue += 1;
}
//--------------- Variant-Specific Implementations ----------------
/** @return This slime's particle type for jump effects. */
@Override
protected IParticleData getParticleType() { return ParticleTypes.SPLASH; } //TODO
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
target.setSecondsOnFire( getSize() * 3 );
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "strawberry" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -0,0 +1,105 @@
package fathertoast.specialmobs.common.entity.slime;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.ai.SpecialLeapAtTargetGoal;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.item.Items;
import net.minecraft.particles.IParticleData;
import net.minecraft.particles.ParticleTypes;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@SpecialMob
public class WatermelonSlimeEntity extends _SpecialSlimeEntity {
//--------------- Static Special Mob Hooks ----------------
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 3.06F, 3.06F );
return new BestiaryInfo( 0xDF7679, BestiaryInfo.BaseWeight.LOW );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return _SpecialSlimeEntity.createAttributes(); // Slimes define their attributes elsewhere based on size
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Watermelon Slime",
"", "", "", "", "", "" );//TODO
}
@SpecialMob.LootTableProvider
public static void buildLootTable( LootTableBuilder loot ) {
addBaseLoot( loot );
loot.addCommonDrop( "common", Items.MELON_SLICE );
loot.addRareDrop( "rare", Items.GLISTERING_MELON_SLICE );
loot.addUncommonDrop( "uncommon", Items.PINK_DYE, Items.LIME_DYE );
}
@SpecialMob.Constructor
public WatermelonSlimeEntity( EntityType<? extends _SpecialSlimeEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 1.5F );
slimeExperienceValue += 2;
}
//--------------- Variant-Specific Implementations ----------------
/** Override to modify this slime's base attributes by size. */
@Override
protected void modifyVariantAttributes( int size ) {
addAttribute( Attributes.MAX_HEALTH, 2.0 * size + 8.0 );
setAttribute( Attributes.ARMOR, 15.0 );
}
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
goalSelector.addGoal( 0, new SpecialLeapAtTargetGoal(
this, 10, 0.0F, 5.0F, 1.16F, 1.0F ) );
}
/** @return This slime's particle type for jump effects. */
@Override
protected IParticleData getParticleType() { return ParticleTypes.SPLASH; } //TODO
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
if( target instanceof LivingEntity ) {
final float forwardPower = 0.8F;
final float upwardPower = 0.5F;
final Vector3d vKnockback = new Vector3d( target.getX() - getX(), 0.0, target.getZ() - getZ() )
.normalize().scale( forwardPower ).add( getDeltaMovement().scale( 0.2F ) );
target.setDeltaMovement( vKnockback.x, 0.4 * upwardPower, vKnockback.z );
target.hurtMarked = true;
setDeltaMovement( getDeltaMovement().multiply( 0.2, 1.0, 0.2 ) );
}
}
private static final ResourceLocation[] TEXTURES = {
GET_TEXTURE_PATH( "watermelon" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
}

View file

@ -0,0 +1,303 @@
package fathertoast.specialmobs.common.entity.slime;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.core.SpecialMobs;
import fathertoast.specialmobs.common.entity.ISpecialMob;
import fathertoast.specialmobs.common.entity.SpecialMobData;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.BlockState;
import net.minecraft.entity.*;
import net.minecraft.entity.ai.attributes.Attribute;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.ai.attributes.ModifiableAttributeInstance;
import net.minecraft.entity.monster.MonsterEntity;
import net.minecraft.entity.monster.SlimeEntity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.entity.projectile.SnowballEntity;
import net.minecraft.nbt.CompoundNBT;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.potion.EffectInstance;
import net.minecraft.util.DamageSource;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.IServerWorld;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@SpecialMob
public class _SpecialSlimeEntity extends SlimeEntity implements ISpecialMob<_SpecialSlimeEntity> {
//--------------- Static Special Mob Hooks ----------------
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x51A03E );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return MonsterEntity.createMonsterAttributes(); // Slimes define their attributes elsewhere based on size
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Slime",
"", "", "", "", "", "" );//TODO
}
@SpecialMob.LootTableProvider
public static void addBaseLoot( LootTableBuilder loot ) {
loot.addLootTable( "main", EntityType.SLIME.getDefaultLootTable() );
}
@SpecialMob.Constructor
public _SpecialSlimeEntity( EntityType<? extends _SpecialSlimeEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().initialize();
}
//--------------- Variant-Specific Breakouts ----------------
/** Override to modify this slime's base attributes by size. */
protected void modifyVariantAttributes( int size ) { }
/** Called in the MobEntity.class constructor to initialize AI goals. */
@Override
protected void registerGoals() {
super.registerGoals();
registerVariantGoals();
}
/** Override to change this entity's AI goals. */
protected void registerVariantGoals() { }
/** Called when this entity successfully damages a target to apply on-hit effects. */
@Override
public void doEnchantDamageEffects( LivingEntity attacker, Entity target ) {
onVariantAttack( target );
super.doEnchantDamageEffects( attacker, target );
}
/** Override to apply effects when this entity hits a target with a melee attack. */
protected void onVariantAttack( Entity target ) { }
/** Override to save data to this entity's NBT data. */
public void addVariantSaveData( CompoundNBT saveTag ) { }
/** Override to load data from this entity's NBT data. */
public void readVariantSaveData( CompoundNBT saveTag ) { }
//--------------- Family-Specific Implementations ----------------
/** The parameter for special mob render scale. */
private static final DataParameter<Float> SCALE = EntityDataManager.defineId( _SpecialSlimeEntity.class, DataSerializers.FLOAT );
protected int slimeExperienceValue = 0;
/** Called from the Entity.class constructor to define data watcher variables. */
@Override
protected void defineSynchedData() {
super.defineSynchedData();
specialData = new SpecialMobData<>( this, SCALE, 1.0F );
}
/** Returns true if this slime can deal damage. */
@Override
protected boolean isDealsDamage() { return getSize() > 0 && isEffectiveAi(); } // TODO config for allow tiny slimes to hit
/** Sets this slime's size, optionally resetting its health to max. */
@Override
protected void setSize( int size, boolean resetHealth ) {
super.setSize( size, resetHealth );
modifyVariantAttributes( size );
if( resetHealth ) setHealth( getMaxHealth() );
xpReward = size + slimeExperienceValue;
}
/**
* Alters this slime's base attribute by adding an amount to it.
* Do NOT use this for move speed, instead use {@link #multAttribute(Attribute, double)}
*/
protected void addAttribute( Attribute attribute, double amount ) {
if( attribute != Attributes.MAX_HEALTH && attribute != Attributes.ATTACK_DAMAGE && attribute != Attributes.MOVEMENT_SPEED )
throw new IllegalArgumentException( "Slime relative attributes are only health, damage, and speed!" );
final ModifiableAttributeInstance attributeInstance = getAttribute( attribute );
if( attributeInstance == null )
throw new IllegalStateException( "Attempted to modify non-registered attribute " + attribute.getDescriptionId() );
attributeInstance.setBaseValue( attributeInstance.getBaseValue() + amount );
}
/**
* Alters this slime's base attribute by multiplying it by an amount.
* Mainly use this for move speed, for other attributes use {@link #addAttribute(Attribute, double)}
*/
protected void multAttribute( Attribute attribute, double amount ) {
if( attribute != Attributes.MAX_HEALTH && attribute != Attributes.ATTACK_DAMAGE && attribute != Attributes.MOVEMENT_SPEED )
throw new IllegalArgumentException( "Slime relative attributes are only health, damage, and speed!" );
final ModifiableAttributeInstance attributeInstance = getAttribute( attribute );
if( attributeInstance == null )
throw new IllegalStateException( "Attempted to modify non-registered attribute " + attribute.getDescriptionId() );
attributeInstance.setBaseValue( attributeInstance.getBaseValue() * amount );
}
/** Sets this slime's base attribute. */
protected void setAttribute( Attribute attribute, double amount ) {
if( attribute == Attributes.MAX_HEALTH || attribute == Attributes.ATTACK_DAMAGE || attribute == Attributes.MOVEMENT_SPEED )
throw new IllegalArgumentException( "Use slime relative attribute!" );
final ModifiableAttributeInstance attributeInstance = getAttribute( attribute );
if( attributeInstance == null )
throw new IllegalStateException( "Attempted to modify non-registered attribute " + attribute.getDescriptionId() );
attributeInstance.setBaseValue( amount );
}
/** Called on spawn to initialize properties based on the world, difficulty, and the group it spawns with. */
@Nullable
public ILivingEntityData finalizeSpawn( IServerWorld world, DifficultyInstance difficulty, SpawnReason spawnReason,
@Nullable ILivingEntityData groupData, @Nullable CompoundNBT eggTag ) {
groupData = super.finalizeSpawn( world, difficulty, spawnReason, groupData, eggTag );
// TODO ranged attack
return groupData;
}
//--------------- ISpecialMob Implementation ----------------
private SpecialMobData<_SpecialSlimeEntity> specialData;
/** @return This mob's special data. */
@Override
public SpecialMobData<_SpecialSlimeEntity> getSpecialData() { return specialData; }
/** @return The experience that should be dropped by this entity. */
@Override
public final int getExperience() { return slimeExperienceValue; } // Slime base xp
/** Sets the experience that should be dropped by this entity. */
@Override
public final void setExperience( int xp ) {
slimeExperienceValue = xp;
xpReward = getSize() + xp;
}
static ResourceLocation GET_TEXTURE_PATH( String type ) {
return SpecialMobs.resourceLoc( SpecialMobs.TEXTURE_PATH + "slime/" + type + ".png" );
}
private static final ResourceLocation[] TEXTURES = {
new ResourceLocation( "textures/entity/slime/slime.png" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
//--------------- SpecialMobData Hooks ----------------
/** Called each tick to update this entity's movement. */
@Override
public void aiStep() {
super.aiStep();
getSpecialData().tick();
}
/** @return The eye height of this entity when standing. */
@Override
protected float getStandingEyeHeight( Pose pose, EntitySize size ) {
return super.getStandingEyeHeight( pose, size ) * getSpecialData().getBaseScale() * (isBaby() ? 0.53448F : 1.0F);
}
/** @return Whether this entity is immune to fire damage. */
@Override
public boolean fireImmune() { return getSpecialData().isImmuneToFire(); }
/** Sets this entity on fire for a specific duration. */
@Override
public void setRemainingFireTicks( int ticks ) {
if( !getSpecialData().isImmuneToBurning() ) super.setRemainingFireTicks( ticks );
}
/** @return True if this entity can be leashed. */
@Override
public boolean canBeLeashed( PlayerEntity player ) { return !isLeashed() && getSpecialData().allowLeashing(); }
/** Sets this entity 'stuck' inside a block, such as a cobweb or sweet berry bush. Mod blocks could use this as a speed boost. */
@Override
public void makeStuckInBlock( BlockState block, Vector3d speedMulti ) {
if( getSpecialData().canBeStuckIn( block ) ) super.makeStuckInBlock( block, speedMulti );
}
/** @return Called when this mob falls. Calculates and applies fall damage. Returns false if canceled. */
@Override
public boolean causeFallDamage( float distance, float damageMultiplier ) {
return super.causeFallDamage( distance, damageMultiplier * getSpecialData().getFallDamageMultiplier() );
}
/** @return True if this entity should NOT trigger pressure plates or tripwires. */
@Override
public boolean isIgnoringBlockTriggers() { return getSpecialData().ignorePressurePlates(); }
/** @return True if this entity can breathe underwater. */
@Override
public boolean canBreatheUnderwater() { return getSpecialData().canBreatheInWater(); }
/** @return True if this entity can be pushed by (flowing) fluids. */
@Override
public boolean isPushedByFluid() { return !getSpecialData().ignoreWaterPush(); }
/** @return True if this entity takes damage while wet. */
@Override
public boolean isSensitiveToWater() { return getSpecialData().isDamagedByWater(); }
/** @return Attempts to damage this entity; returns true if the hit was successful. */
@Override
public boolean hurt( DamageSource source, float amount ) {
if( isSensitiveToWater() && source.getDirectEntity() instanceof SnowballEntity ) {
amount = Math.max( 3.0F, amount );
}
return super.hurt( source, amount );
}
/** @return True if the effect can be applied to this entity. */
@Override
public boolean canBeAffected( EffectInstance effect ) { return getSpecialData().isPotionApplicable( effect ); }
/** Saves data to this entity's base NBT compound that is specific to its subclass. */
@Override
public void addAdditionalSaveData( CompoundNBT tag ) {
super.addAdditionalSaveData( tag );
final CompoundNBT saveTag = SpecialMobData.getSaveLocation( tag );
getSpecialData().writeToNBT( saveTag );
addVariantSaveData( saveTag );
}
/** Loads data from this entity's base NBT compound that is specific to its subclass. */
@Override
public void readAdditionalSaveData( CompoundNBT tag ) {
super.readAdditionalSaveData( tag );
final CompoundNBT saveTag = SpecialMobData.getSaveLocation( tag );
getSpecialData().readFromNBT( saveTag );
readVariantSaveData( saveTag );
}
}

View file

@ -29,7 +29,7 @@ public class DesertSpiderEntity extends _SpecialSpiderEntity {
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.95F, 0.8F );
entityType.sized( 0.95F, 0.7F );
return new BestiaryInfo( 0xE6DDAC );
//TODO theme - desert
}

View file

@ -32,7 +32,7 @@ public class MotherSpiderEntity extends _SpecialSpiderEntity {
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 1.8F, 1.2F );
entityType.sized( 1.7F, 1.0F );
return new BestiaryInfo( 0xB300B3 );
}
@ -60,7 +60,7 @@ public class MotherSpiderEntity extends _SpecialSpiderEntity {
@SpecialMob.Constructor
public MotherSpiderEntity( EntityType<? extends _SpecialSpiderEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 1.4F );
getSpecialData().setBaseScale( 1.2F );
getSpecialData().setRegenerationTime( 30 );
xpReward += 1;
@ -90,16 +90,18 @@ public class MotherSpiderEntity extends _SpecialSpiderEntity {
if( extraBabies > 0 && amount > 1.0F && level instanceof IServerWorld && random.nextFloat() < 0.33F ) {
extraBabies--;
spawnBaby( 0.66F, null );
playSound( SoundEvents.EGG_THROW, 1.0F, 2.0F / (random.nextFloat() * 0.4F + 0.8F) );
}
return true;
}
return false;
}
/** Called each tick to update this entity while it's dead. */
/** Called to remove this entity from the world. Includes death, unloading, interdimensional travel, etc. */
@Override
protected void tickDeath() {
if( deathTime == 19 && level instanceof IServerWorld ) { // At 19, the entity will be immediately removed upon call to super method
public void remove( boolean keepData ) {
//noinspection deprecation
if( isDeadOrDying() && !removed && level instanceof IServerWorld ) { // Same conditions as slime splitting
// Spawn babies on death
final int babiesToSpawn = babies + extraBabies;
ILivingEntityData groupData = null;
@ -108,8 +110,7 @@ public class MotherSpiderEntity extends _SpecialSpiderEntity {
}
playSound( SoundEvents.EGG_THROW, 1.0F, 2.0F / (random.nextFloat() * 0.4F + 0.8F) );
}
super.tickDeath();
super.remove( keepData );
}
/** Helper method to simplify spawning babies. */

View file

@ -60,7 +60,7 @@ public class _SpecialSpiderEntity extends SpiderEntity implements ISpecialMob<_S
@SpecialMob.Constructor
public _SpecialSpiderEntity( EntityType<? extends _SpecialSpiderEntity> entityType, World world ) {
super( entityType, world );
specialData.initialize();
getSpecialData().initialize();
}
@ -82,14 +82,11 @@ public class _SpecialSpiderEntity extends SpiderEntity implements ISpecialMob<_S
/** Override to change this entity's AI goals. */
protected void registerVariantGoals() { }
/** Called to melee attack the target. */
/** Called when this entity successfully damages a target to apply on-hit effects. */
@Override
public boolean doHurtTarget( Entity target ) {
if( super.doHurtTarget( target ) ) {
onVariantAttack( target );
return true;
}
return false;
public void doEnchantDamageEffects( LivingEntity attacker, Entity target ) {
onVariantAttack( target );
super.doEnchantDamageEffects( attacker, target );
}
/** Override to apply effects when this entity hits a target with a melee attack. */
@ -171,7 +168,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 +183,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. */

View file

@ -32,6 +32,7 @@ public class BruteWitherSkeletonEntity extends _SpecialWitherSkeletonEntity {
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.85F, 2.9F );
return new BestiaryInfo( 0xFFF87E );
}
@ -59,6 +60,7 @@ public class BruteWitherSkeletonEntity extends _SpecialWitherSkeletonEntity {
@SpecialMob.Constructor
public BruteWitherSkeletonEntity( EntityType<? extends _SpecialWitherSkeletonEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setBaseScale( 1.44F );
xpReward += 2;
}

View file

@ -24,7 +24,7 @@ public class GiantWitherSkeletonEntity extends _SpecialWitherSkeletonEntity {
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.95F, 3.24F );
entityType.sized( 0.95F, 3.6F );
return new BestiaryInfo( 0x474D4D );
}

View file

@ -0,0 +1,222 @@
package fathertoast.specialmobs.common.entity.witherskeleton;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.ai.INinja;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.Entity;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Hand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.World;
import javax.annotation.Nullable;
import javax.annotation.ParametersAreNonnullByDefault;
import java.util.Optional;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@SpecialMob
public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity implements INinja {
//--------------- Static Special Mob Hooks ----------------
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x333366 );
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialWitherSkeletonEntity.createAttributes() )
.multAttribute( Attributes.MOVEMENT_SPEED, 1.2 )
.build();
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Wither Skeleton Ninja",
"", "", "", "", "", "" );//TODO
}
@SpecialMob.LootTableProvider
public static void buildLootTable( LootTableBuilder loot ) {
addBaseLoot( loot );
loot.addUncommonDrop( "uncommon", Blocks.INFESTED_STONE, Blocks.INFESTED_COBBLESTONE, Blocks.INFESTED_STONE_BRICKS,
Blocks.INFESTED_CRACKED_STONE_BRICKS, Blocks.INFESTED_MOSSY_STONE_BRICKS, Blocks.INFESTED_CHISELED_STONE_BRICKS );
}
@SpecialMob.Constructor
public NinjaWitherSkeletonEntity( EntityType<? extends _SpecialWitherSkeletonEntity> entityType, World world ) {
super( entityType, world );
xpReward += 2;
}
//--------------- Variant-Specific Implementations ----------------
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
setRangedAI( 1.0, 10, 9.0F );
//TODO AIHelper.insertGoalReverse( goalSelector, getVariantAttackPriority() - 1, null );
}
/** Called during spawn finalization to set starting equipment. */
@Override
protected void populateDefaultEquipmentSlots( DifficultyInstance difficulty ) {
super.populateDefaultEquipmentSlots( difficulty );
setCanPickUpLoot( true );
}
/** Override to change this entity's chance to spawn with a bow. */
@Override
protected double getVariantBowChance() { return 0.5; }
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
revealTo( target );
}
/** @return Attempts to damage this entity; returns true if the hit was successful. */
@Override
public boolean hurt( DamageSource source, float amount ) {
if( super.hurt( source, amount ) ) {
revealTo( source.getEntity() );
return true;
}
return false;
}
/** @return Interacts (right click) with this entity and returns the result. */
@Override
public ActionResultType mobInteract( PlayerEntity player, Hand hand ) {
// Attack if the player tries to right click the "block"
if( !level.isClientSide() && getDisguiseBlock() != null ) revealTo( player );
return super.mobInteract( player, hand );
}
/** Called each tick to update this entity. */
@Override
public void tick() {
// TODO can this be moved to the ninja AI?
if( !level.isClientSide() ) {
if( canHide ) {
//EntityAINinja.startHiding( this ); TODO
}
else if( onGround && getDisguiseBlock() == null &&
(getTarget() == null || getTarget() instanceof PlayerEntity && ((PlayerEntity) getTarget()).isCreative()) ) {
canHide = true;
}
}
super.tick();
}
// // Moves this entity.
// @Override TODO
// public void move( MoverType type, double x, double y, double z ) {
// if( isImmobile() && type != MoverType.PISTON ) {
// motionY = 0.0;
// }
// else {
// super.move( type, x, y, z );
// }
// }
//
// // Returns true if this entity should push and be pushed by other entities when colliding.
// @Override
// public boolean canBePushed() { return !isImmobile(); }
/** Sets this entity on fire for a specific duration. */
@Override
public void setRemainingFireTicks( int ticks ) {
if( !isImmobile() ) super.setRemainingFireTicks( ticks );
}
/** Reveals this ninja and sets its target so that it doesn't immediately re-disguise itself. */
public void revealTo( @Nullable Entity target ) {
setDisguiseBlock( null );
if( target instanceof LivingEntity ) setTarget( (LivingEntity) target );
}
private static final ResourceLocation[] TEXTURES = {
new ResourceLocation( "textures/entity/skeleton/skeleton.png" ),
null,
GET_TEXTURE_PATH( "ninja_overlay" )
};
/** @return All default textures for this entity. */
@Override
public ResourceLocation[] getDefaultTextures() { return TEXTURES; }
//--------------- INinja Implementations ----------------
/** The parameter for the ninja immobile state. */
private static final DataParameter<Boolean> IS_HIDING = EntityDataManager.defineId( NinjaWitherSkeletonEntity.class, DataSerializers.BOOLEAN );
/** The parameter for the ninja disguise block. */
private static final DataParameter<Optional<BlockState>> HIDING_BLOCK = EntityDataManager.defineId( NinjaWitherSkeletonEntity.class, DataSerializers.BLOCK_STATE );
private boolean canHide = true;
/** Called from the Entity.class constructor to define data watcher variables. */
@Override
protected void defineSynchedData() {
super.defineSynchedData();
entityData.define( IS_HIDING, false );
entityData.define( HIDING_BLOCK, Optional.empty() );
}
/** @return Whether this ninja is currently immobile. */
@Override
public boolean isImmobile() { return getEntityData().get( IS_HIDING ); }
/** Sets this ninja's immovable state. When activated, the entity is 'snapped' to the nearest block position. */
@Override
public void setImmobile( boolean value ) {
if( value != isImmobile() ) {
getEntityData().set( IS_HIDING, value );
if( value ) {
clearFire();
moveTo( Math.floor( getX() ) + 0.5, Math.floor( getY() ), Math.floor( getZ() ) + 0.5 );
}
}
}
/** @return The block being hidden (rendered) as, or null if not hiding. */
@Nullable
@Override
public BlockState getDisguiseBlock() {
if( isAlive() ) return getEntityData().get( HIDING_BLOCK ).orElse( null );
return null;
}
/** Sets the block being hidden (rendered) as, set to null to cancel hiding. */
@Override
public void setDisguiseBlock( @Nullable BlockState block ) {
getEntityData().set( HIDING_BLOCK, Optional.ofNullable( block ) );
canHide = false;
// Smoke puff when emerging from disguise
if( block == null ) {
//spawnExplosionParticle(); TODO
}
}
}

View file

@ -26,7 +26,7 @@ public class SpitfireWitherSkeletonEntity extends _SpecialWitherSkeletonEntity {
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
entityType.sized( 0.95F, 3.24F );
entityType.sized( 0.95F, 3.6F );
return new BestiaryInfo( 0xDC1A00, BestiaryInfo.BaseWeight.LOW );
}

View file

@ -76,7 +76,7 @@ public class _SpecialWitherSkeletonEntity extends WitherSkeletonEntity implement
@SpecialMob.Constructor
public _SpecialWitherSkeletonEntity( EntityType<? extends _SpecialWitherSkeletonEntity> entityType, World world ) {
super( entityType, world );
specialData.initialize();
getSpecialData().initialize();
getSpecialData().setImmuneToFire( true );
}
@ -162,14 +162,11 @@ public class _SpecialWitherSkeletonEntity extends WitherSkeletonEntity implement
return arrow;
}
/** Called to melee attack the target. */
/** Called when this entity successfully damages a target to apply on-hit effects. */
@Override
public boolean doHurtTarget( Entity target ) {
if( super.doHurtTarget( target ) ) {
onVariantAttack( target );
return true;
}
return false;
public void doEnchantDamageEffects( LivingEntity attacker, Entity target ) {
onVariantAttack( target );
super.doEnchantDamageEffects( attacker, target );
}
/** Override to apply effects when this entity hits a target with a melee attack. */
@ -207,7 +204,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 ) {
@ -315,13 +312,13 @@ public class _SpecialWitherSkeletonEntity extends WitherSkeletonEntity implement
/** @return The eye height of this entity when standing. */
@Override
protected float getStandingEyeHeight( Pose pose, EntitySize size ) {//TODO fix this
return super.getStandingEyeHeight( pose, size ) * getSpecialData().getBaseScale() * (isBaby() ? 0.53448F : 1.0F);
protected float getStandingEyeHeight( Pose pose, EntitySize size ) {
return 1.74F * getSpecialData().getBaseScale() * (isBaby() ? 0.53448F : 1.0F); // Use base skeleton scale instead of super
}
/** @return Whether this entity is immune to fire damage. */
@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 +333,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. */

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,141 @@
package fathertoast.specialmobs.common.entity.zombie;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.ai.IAngler;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootEntryItemBuilder;
import fathertoast.specialmobs.datagen.loot.LootHelper;
import fathertoast.specialmobs.datagen.loot.LootPoolBuilder;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
import mcp.MethodsReturnNonnullByDefault;
import net.minecraft.entity.EntityType;
import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.ai.attributes.AttributeModifierMap;
import net.minecraft.entity.ai.attributes.Attributes;
import net.minecraft.inventory.EquipmentSlotType;
import net.minecraft.item.FishingRodItem;
import net.minecraft.item.IDyeableArmorItem;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.network.datasync.DataParameter;
import net.minecraft.network.datasync.DataSerializers;
import net.minecraft.network.datasync.EntityDataManager;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.World;
import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@SpecialMob
public class FishingZombieEntity extends _SpecialZombieEntity implements IAngler {
//--------------- Static Special Mob Hooks ----------------
@SpecialMob.BestiaryInfoSupplier
public static BestiaryInfo bestiaryInfo( EntityType.Builder<LivingEntity> entityType ) {
return new BestiaryInfo( 0x2D41F4 );
//TODO theme - fishing
}
@SpecialMob.AttributeCreator
public static AttributeModifierMap.MutableAttribute createAttributes() {
return AttributeHelper.of( _SpecialZombieEntity.createAttributes() )
.multAttribute( Attributes.MOVEMENT_SPEED, 0.8 )
.build();
}
@SpecialMob.LanguageProvider
public static String[] getTranslations( String langKey ) {
return References.translations( langKey, "Fishing Zombie",
"", "", "", "", "", "" );//TODO
}
@SpecialMob.LootTableProvider
public static void buildLootTable( LootTableBuilder loot ) {
addBaseLoot( loot );
loot.addPool( new LootPoolBuilder( "common" )
.addEntry( new LootEntryItemBuilder( Items.COD ).setCount( 0, 2 ).addLootingBonus( 0, 1 ).smeltIfBurning().toLootEntry() )
.toLootPool() );
loot.addPool( new LootPoolBuilder( "rare" ).addConditions( LootHelper.RARE_CONDITIONS )
.addEntry( new LootEntryItemBuilder( Items.FISHING_ROD ).enchant( 30, true ).toLootEntry() )
.toLootPool() );
}
@SpecialMob.Constructor
public FishingZombieEntity( EntityType<? extends _SpecialZombieEntity> entityType, World world ) {
super( entityType, world );
getSpecialData().setCanBreatheInWater( true );
getSpecialData().setIgnoreWaterPush( true );
xpReward += 2;
}
//--------------- Variant-Specific Implementations ----------------
/** Override to change this entity's AI goals. */
@Override
protected void registerVariantGoals() {
getSpecialData().rangedAttackSpread = 10.0F;
getSpecialData().rangedAttackCooldown = 32;
getSpecialData().rangedAttackMaxCooldown = 48;
getSpecialData().rangedAttackMaxRange = 10.0F;
//TODO add angler AI @ attack priority
}
/** Called during spawn finalization to set starting equipment. */
@Override
protected void populateDefaultEquipmentSlots( DifficultyInstance difficulty ) {
super.populateDefaultEquipmentSlots( difficulty );
setItemSlot( EquipmentSlotType.MAINHAND, new ItemStack( Items.FISHING_ROD ) );
if( getItemBySlot( EquipmentSlotType.FEET ).isEmpty() ) {
ItemStack booties = new ItemStack( Items.LEATHER_BOOTS );
((IDyeableArmorItem) booties.getItem()).setColor( booties, 0xFFFF00 );
setItemSlot( EquipmentSlotType.FEET, booties );
}
setCanPickUpLoot( false );
}
/** Override to change this entity's chance to spawn with a bow. */
@Override
protected double getVariantBowChance() { return 0.0; }
//--------------- IAngler Implementations ----------------
/** The parameter for baby status. */
private static final DataParameter<Boolean> IS_LINE_OUT = EntityDataManager.defineId( FishingZombieEntity.class, DataSerializers.BOOLEAN );
/** Called from the Entity.class constructor to define data watcher variables. */
@Override
protected void defineSynchedData() {
super.defineSynchedData();
entityData.define( IS_LINE_OUT, false );
}
/** Sets this angler's line as out (or in). */
@Override
public void setLineOut( boolean value ) { getEntityData().set( IS_LINE_OUT, value ); }
/** @return Whether this angler's line is out. */
@Override
public boolean isLineOut() { return getEntityData().get( IS_LINE_OUT ); }
/** @return The item equipped in a particular slot. */
@Override
public ItemStack getItemBySlot( EquipmentSlotType slot ) {
// Display a stick in place of the "cast fishing rod" when the fancy render is disabled
if( level.isClientSide() && /*!Config.get().GENERAL.FANCY_FISHING_MOBS &&*/ EquipmentSlotType.MAINHAND.equals( slot ) ) {
final ItemStack held = super.getItemBySlot( slot );
if( !held.isEmpty() && held.getItem() instanceof FishingRodItem && isLineOut() ) {
return new ItemStack( Items.STICK );
}
return held;
}
return super.getItemBySlot( slot );
}
}

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.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,

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.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<LivingEntity> 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<? extends _SpecialZombieEntity> 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,14 +98,73 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S
/** Override to change this entity's AI goals. */
protected void registerVariantGoals() { }
/** Called to melee attack the target. */
/** 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
public boolean doHurtTarget( Entity target ) {
if( super.doHurtTarget( target ) ) {
onVariantAttack( target );
return true;
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 ) );
}
return false;
}
/** 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 when this entity successfully damages a target to apply on-hit effects. */
@Override
public void doEnchantDamageEffects( LivingEntity attacker, Entity target ) {
onVariantAttack( target );
super.doEnchantDamageEffects( attacker, target );
}
/** Override to apply effects when this entity hits a target with a melee attack. */
@ -108,6 +182,9 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S
/** The parameter for special mob render scale. */
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. */
@Override
protected void defineSynchedData() {
@ -120,10 +197,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 +267,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 +280,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 +291,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 +353,7 @@ public class _SpecialZombieEntity extends ZombieEntity implements ISpecialMob<_S
getSpecialData().readFromNBT( saveTag );
readVariantSaveData( saveTag );
reassessWeaponGoal();
}
}

View file

@ -77,6 +77,7 @@ public final class References {
public static final String TAG_HEALTH_STACKS = "HealthStacks"; // Hungry Spider
// Misc.
public static final String TAG_FUSE_TIME = "FuseTime"; // Blackberry Slime, Volatile Magma Cube
public static final String TAG_AMMO = "Ammo"; // Web (Cave) Spider
public static final String TAG_IS_FAKE = "IsFake"; // Mirage Enderman