Angler & ninja boilerplates

This commit is contained in:
FatherToast 2022-06-25 18:28:35 -05:00
parent 5bf6e82a87
commit 2130486ff1
11 changed files with 795 additions and 1 deletions

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

@ -68,6 +68,7 @@ public class SpecialMobs {
* o ranged attack AI
* - silverfish
* ? ranged attack AI
* + puffer
* - endermen
* o witches
* o ability to equip held items

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

@ -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

@ -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

@ -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

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