mirror of
https://github.com/FatherToast/SpecialMobs.git
synced 2025-04-25 06:45:11 +00:00
Angler & ninja boilerplates
This commit is contained in:
parent
5bf6e82a87
commit
2130486ff1
11 changed files with 795 additions and 1 deletions
|
@ -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;
|
||||||
|
}
|
||||||
|
}
|
|
@ -68,6 +68,7 @@ public class SpecialMobs {
|
||||||
* o ranged attack AI
|
* o ranged attack AI
|
||||||
* - silverfish
|
* - silverfish
|
||||||
* ? ranged attack AI
|
* ? ranged attack AI
|
||||||
|
* + puffer
|
||||||
* - endermen
|
* - endermen
|
||||||
* o witches
|
* o witches
|
||||||
* o ability to equip held items
|
* o ability to equip held items
|
||||||
|
|
|
@ -11,7 +11,7 @@ import java.util.ArrayList;
|
||||||
*/
|
*/
|
||||||
public final class AIHelper {
|
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 ) {
|
public static void insertGoal( GoalSelector ai, int priority, Goal goal ) {
|
||||||
for( PrioritizedGoal task : new ArrayList<>( ai.availableGoals ) ) {
|
for( PrioritizedGoal task : new ArrayList<>( ai.availableGoals ) ) {
|
||||||
if( task.getPriority() >= priority ) task.priority++;
|
if( task.getPriority() >= priority ) task.priority++;
|
||||||
|
@ -19,6 +19,14 @@ public final class AIHelper {
|
||||||
ai.addGoal( priority, goal );
|
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. */
|
/** Removes all goals with the specified priority. */
|
||||||
public static void removeGoals( GoalSelector ai, int priority ) {
|
public static void removeGoals( GoalSelector ai, int priority ) {
|
||||||
for( PrioritizedGoal task : new ArrayList<>( ai.availableGoals ) ) {
|
for( PrioritizedGoal task : new ArrayList<>( ai.availableGoals ) ) {
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
|
@ -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; }
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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 );
|
||||||
|
}
|
||||||
|
}
|
Loading…
Add table
Reference in a new issue