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

 Conflicts:
	src/main/java/fathertoast/specialmobs/client/renderer/entity/NinjaSkeletonRenderer.java
This commit is contained in:
Sarinsa 2022-06-29 00:22:53 +02:00
commit eab57d3dd9
28 changed files with 332 additions and 120 deletions

View file

@ -26,51 +26,35 @@ import javax.annotation.ParametersAreNonnullByDefault;
@ParametersAreNonnullByDefault
@MethodsReturnNonnullByDefault
@OnlyIn( Dist.CLIENT )
public class NinjaSkeletonRenderer extends SkeletonRenderer {
private final float baseShadowRadius;
public class NinjaSkeletonRenderer extends SpecialSkeletonRenderer {
private final BlockRendererDispatcher blockRenderer;
public NinjaSkeletonRenderer( EntityRendererManager rendererManager ) {
super( rendererManager );
baseShadowRadius = shadowRadius = shadowStrength = 0.0F;
blockRenderer = Minecraft.getInstance().getBlockRenderer();
addLayer( new SpecialMobEyesLayer<>( this ) );
addLayer( new SpecialMobOverlayLayer<>( this, new SkeletonModel<>() ) );
}
@Override
public void render( AbstractSkeletonEntity skeletonEntity, float f1, float f2, MatrixStack matrixStack, IRenderTypeBuffer buffer, int i ) {
BlockState disguiseState = ((INinja) skeletonEntity).getDisguiseBlock();
if( disguiseState == null ) {
super.render( skeletonEntity, f1, f2, matrixStack, buffer, i );
public void render( AbstractSkeletonEntity entity, float rotation, float partialTicks,
MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight ) {
final BlockState disguiseBlock = ((INinja) entity).getHiddenDragon();
if( disguiseBlock == null ) {
super.render( entity, rotation, partialTicks, matrixStack, buffer, packedLight );
}
else {
renderBlockDisguise( (AbstractSkeletonEntity & INinja) skeletonEntity, disguiseState, f1, f2, matrixStack, buffer, i );
shadowRadius = 0.0F;
renderBlockDisguise( entity, disguiseBlock, rotation, partialTicks, matrixStack, buffer, packedLight );
}
}
private <T extends AbstractSkeletonEntity & INinja> void renderBlockDisguise( T ninja, BlockState state, float f1, float f2, MatrixStack matrixStack, IRenderTypeBuffer buffer, int i ) {
private void renderBlockDisguise( AbstractSkeletonEntity ninja, BlockState block, float rotation, float partialTicks,
MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight ) {
matrixStack.pushPose();
matrixStack.translate( -0.5D, 0.0D, -0.5D );
this.blockRenderer.renderBlock( state, matrixStack, buffer, i, OverlayTexture.NO_OVERLAY, EmptyModelData.INSTANCE );
matrixStack.translate( -0.5, 0.0, -0.5 );
blockRenderer.renderBlock( block, matrixStack, buffer, packedLight, OverlayTexture.NO_OVERLAY, EmptyModelData.INSTANCE );
matrixStack.popPose();
}
@Override
public ResourceLocation getTextureLocation( AbstractSkeletonEntity entity ) {
return ((ISpecialMob<?>) entity).getSpecialData().getTexture();
}
@Override
protected void scale( AbstractSkeletonEntity entity, MatrixStack matrixStack, float partialTick ) {
super.scale( entity, matrixStack, partialTick );
final float scale = ((ISpecialMob<?>) entity).getSpecialData().getRenderScale();
shadowRadius = baseShadowRadius * scale;
matrixStack.scale( scale, scale, scale );
}
}

View file

@ -47,6 +47,7 @@ public class MobFamily<T extends LivingEntity> {
"Zombie", "zombies", 0x00AFAF, new EntityType[] { EntityType.ZOMBIE, EntityType.HUSK },
"Brute", "Fire", /*"Fishing",*/ "Giant", "Hungry", "Husk", "MadScientist", "Plague"
);
// TODO Drowned family and zombie transform mechanic
public static final MobFamily<ZombifiedPiglinEntity> ZOMBIFIED_PIGLIN = new MobFamily<>(//TODO figure out crossbows
"ZombifiedPiglin", "zombified piglins", 0xEA9393, new EntityType[] { EntityType.ZOMBIFIED_PIGLIN },
"Brute", /*"Fishing",*/ "Giant", "Hungry", "Knight", "Plague", "Vampire"

View file

@ -52,10 +52,10 @@ public class SpecialMobs {
* + transformations
* - ranged attack AI (using bow)
* - use shields
* + drowned
* o zombified piglins
* o ranged attack AI (using bow)
* o use shields
* + drowned
* - zombified piglins
* - ranged attack AI (using bow)
* - use shields
* - skeletons
* - use shields
* - babies
@ -64,7 +64,7 @@ public class SpecialMobs {
* - babies
* - slimes
* - smallest size can deal damage
* o magma cubes
* - magma cubes
* - spiders
* o ranged attack AI
* - cave spiders
@ -84,6 +84,7 @@ public class SpecialMobs {
* ? zoglins
* ? endermites
* ? guardians
* ? vortex
* ? shulkers
* ? phantoms
* + the goat

View file

@ -6,20 +6,20 @@ 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.
* This allows get and set methods for the disguise block and immovable state.
*/
public interface INinja {
/** @return Whether this ninja is currently immobile. */
boolean isHiding();
/** @return Whether this ninja is currently immovable. */
boolean isCrouchingTiger();
/** Sets this ninja's immovable state. When activated, the entity is 'snapped' to the nearest block position. */
void setHiding( boolean value );
void setCrouchingTiger( boolean value );
/** @return The block being hidden (rendered) as, or null if not hiding. */
@Nullable
BlockState getDisguiseBlock();
BlockState getHiddenDragon();
/** Sets the block being hidden (rendered) as, set to null to cancel hiding. */
void setDisguiseBlock( @Nullable BlockState block );
void setHiddenDragon( @Nullable BlockState block );
}

View file

@ -0,0 +1,186 @@
package fathertoast.specialmobs.common.entity.ai;
import net.minecraft.block.BlockRenderType;
import net.minecraft.block.BlockState;
import net.minecraft.block.Blocks;
import net.minecraft.entity.MobEntity;
import net.minecraft.entity.ai.goal.Goal;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.state.properties.BlockStateProperties;
import net.minecraft.util.Direction;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import java.util.ArrayList;
import java.util.EnumSet;
import java.util.List;
import java.util.Random;
/**
* This AI goal tells the ninja when players are looking in its direction so it can 'freeze' in place and act like a block.
* <p>
* It also contains static methods to help ninjas choose a block to disguise as.
*/
public class NinjaGoal<T extends MobEntity & INinja> extends Goal {
private final T ninja;
public NinjaGoal( T entity ) {
ninja = entity;
setFlags( EnumSet.of( Goal.Flag.MOVE, Flag.LOOK, Goal.Flag.JUMP ) );
}
/** @return Returns true if this AI can be activated. */
@Override
public boolean canUse() {
if( ninja.getHiddenDragon() == null || ninja.getHiddenDragon().getRenderShape() != BlockRenderType.MODEL )
return false;
final List<PlayerEntity> players = new ArrayList<>( ninja.level.players() );
for( PlayerEntity player : players ) {
final float dX = (float) (ninja.getX() - player.getX());
final float dZ = (float) (ninja.getZ() - player.getZ());
final float angleFromPlayer = (float) Math.atan2( dX, -dZ ) * 180.0F / (float) Math.PI;
if( Math.abs( angleFromPlayer - MathHelper.wrapDegrees( player.yHeadRot ) ) > 90.0F ) {
return true; // A player is looking in the ninja's general direction!
}
}
return false;
}
/** Called when this AI is activated. */
@Override
public void start() {
ninja.getNavigation().stop();
ninja.clearFire();
ninja.moveTo( Math.floor( ninja.getX() ) + 0.5, Math.floor( ninja.getY() ), Math.floor( ninja.getZ() ) + 0.5 );
ninja.setDeltaMovement( 0.0, 0.0, 0.0 );
ninja.setCrouchingTiger( true );
}
/** @return Called each update while active and returns true if this AI can remain active. */
@Override
public boolean canContinueToUse() { return canUse(); }
/** @return Whether this AI can be interrupted by a lower priority AI while running. */
@Override
public boolean isInterruptable() { return false; }
/** Called when this AI is deactivated. */
@Override
public void stop() { ninja.setCrouchingTiger( false ); }
//--------------- Static Disguise Helper ----------------
/** Finds a nearby block for the entity to hide as and flags it to start hiding. */
public static <T extends MobEntity & INinja> BlockState pickDisguise( T entity ) {
final Random random = entity.getRandom();
// Options available regardless of position
switch( random.nextInt( 200 ) ) {
case 0: return Blocks.CHEST.defaultBlockState().setValue( BlockStateProperties.HORIZONTAL_FACING,
Direction.Plane.HORIZONTAL.getRandomDirection( random ) );
case 1: return Blocks.OAK_LOG.defaultBlockState();
case 2: return Blocks.SPONGE.defaultBlockState();
case 3: return Blocks.DEAD_BUSH.defaultBlockState();
case 4: return Blocks.OAK_LEAVES.defaultBlockState();
case 5: return Blocks.BEE_NEST.defaultBlockState();
case 6: return Blocks.CAKE.defaultBlockState();
case 7: return Blocks.CRAFTING_TABLE.defaultBlockState();
case 8: return Blocks.FURNACE.defaultBlockState();
case 9: return Blocks.ANVIL.defaultBlockState();
case 10: return Blocks.BREWING_STAND.defaultBlockState();
}
final BlockPos posUnderFeet = entity.blockPosition().below();
final BlockState blockUnderFeet = entity.level.getBlockState( posUnderFeet );
if( !blockUnderFeet.getBlock().isAir( blockUnderFeet, entity.level, posUnderFeet ) ) {
// Options available based on the block we are standing on
if( blockUnderFeet.is( Blocks.STONE ) || blockUnderFeet.is( Blocks.STONE_BRICKS ) ) {
// Cave theme
switch( random.nextInt( 30 ) ) {
case 0: return blockUnderFeet;
case 1: return Blocks.MOSSY_COBBLESTONE.defaultBlockState();
case 2: return Blocks.CLAY.defaultBlockState();
case 3: return Blocks.GRAVEL.defaultBlockState();
case 4: return Blocks.COAL_ORE.defaultBlockState();
case 5: return Blocks.IRON_ORE.defaultBlockState();
case 6: return Blocks.LAPIS_ORE.defaultBlockState();
case 7: return Blocks.GOLD_ORE.defaultBlockState();
case 8: return Blocks.REDSTONE_ORE.defaultBlockState();
case 9: return Blocks.DIAMOND_ORE.defaultBlockState();
case 10: return Blocks.EMERALD_ORE.defaultBlockState();
}
}
else if( blockUnderFeet.is( Blocks.GRASS ) || blockUnderFeet.is( Blocks.DIRT ) || blockUnderFeet.is( Blocks.PODZOL ) ) {
// Field theme
switch( random.nextInt( 15 ) ) {
case 0: return blockUnderFeet;
case 1: return Blocks.OAK_LOG.defaultBlockState();
case 2: return Blocks.OAK_LEAVES.defaultBlockState();
case 3: return Blocks.PUMPKIN.defaultBlockState();
case 4: return Blocks.MELON.defaultBlockState();
case 5: return Blocks.TALL_GRASS.defaultBlockState();
case 6: return Blocks.FERN.defaultBlockState();
case 7: return Blocks.BEE_NEST.defaultBlockState();
}
}
else if( blockUnderFeet.is( Blocks.SAND ) || blockUnderFeet.is( Blocks.RED_SAND ) ||
blockUnderFeet.is( Blocks.SANDSTONE ) || blockUnderFeet.is( Blocks.RED_SANDSTONE ) ) {
// Desert theme
switch( random.nextInt( 5 ) ) {
case 0: return blockUnderFeet;
case 1: return Blocks.CACTUS.defaultBlockState();
case 2: return Blocks.DEAD_BUSH.defaultBlockState();
}
}
else if( blockUnderFeet.is( Blocks.NETHERRACK ) || blockUnderFeet.is( Blocks.NETHER_BRICKS ) ||
blockUnderFeet.is( Blocks.SOUL_SAND ) || blockUnderFeet.is( Blocks.SOUL_SOIL ) ||
blockUnderFeet.is( Blocks.CRIMSON_NYLIUM ) || blockUnderFeet.is( Blocks.WARPED_NYLIUM ) ) {
// Nether theme
switch( random.nextInt( 25 ) ) {
case 0: return blockUnderFeet;
case 1: return Blocks.GRAVEL.defaultBlockState();
case 2: return Blocks.SOUL_SAND.defaultBlockState();
case 3: return Blocks.GLOWSTONE.defaultBlockState();
case 4: return Blocks.NETHER_QUARTZ_ORE.defaultBlockState();
case 5: return Blocks.NETHER_GOLD_ORE.defaultBlockState();
case 6: return Blocks.ANCIENT_DEBRIS.defaultBlockState();
case 7: return Blocks.BROWN_MUSHROOM.defaultBlockState();
case 8: return Blocks.RED_MUSHROOM.defaultBlockState();
case 9: return Blocks.CRIMSON_STEM.defaultBlockState();
case 10: return Blocks.WARPED_STEM.defaultBlockState();
}
}
else if( blockUnderFeet.is( Blocks.END_STONE ) || blockUnderFeet.is( Blocks.END_STONE_BRICKS ) ||
blockUnderFeet.is( Blocks.PURPUR_BLOCK ) ) {
// End theme
switch( random.nextInt( 10 ) ) {
case 0: return blockUnderFeet;
case 1: return Blocks.CHORUS_PLANT.defaultBlockState();
case 2: return Blocks.PURPUR_PILLAR.defaultBlockState();
case 3: return Blocks.END_ROD.defaultBlockState();
}
}
}
// Pick a random nearby render-able block
final BlockPos.Mutable randPos = new BlockPos.Mutable();
for( int i = 0; i < 16; i++ ) {
randPos.set(
posUnderFeet.getX() + random.nextInt( 17 ) - 8,
posUnderFeet.getY() + random.nextInt( 5 ) - 2,
posUnderFeet.getZ() + random.nextInt( 17 ) - 8 );
final BlockState randBlock = entity.level.getBlockState( randPos );
if( randBlock.getRenderShape() == BlockRenderType.MODEL ) return randBlock;
}
// Hide as a log if none of the other options are chosen
return Blocks.OAK_LOG.defaultBlockState();
}
}

View file

@ -94,6 +94,7 @@ public class MotherCaveSpiderEntity extends _SpecialCaveSpiderEntity {
if( extraBabies > 0 && amount > 1.0F && level instanceof IServerWorld && random.nextFloat() < 0.33F ) {
extraBabies--;
spawnBaby( 0.66F, null );
spawnAnim();
playSound( SoundEvents.EGG_THROW, 1.0F, 2.0F / (random.nextFloat() * 0.4F + 0.8F) );
}
return true;
@ -112,6 +113,7 @@ public class MotherCaveSpiderEntity extends _SpecialCaveSpiderEntity {
for( int i = 0; i < babiesToSpawn; i++ ) {
groupData = spawnBaby( 0.33F, groupData );
}
spawnAnim();
playSound( SoundEvents.EGG_THROW, 1.0F, 2.0F / (random.nextFloat() * 0.4F + 0.8F) );
}
super.remove( keepData );

View file

@ -87,6 +87,7 @@ public class SplittingCreeperEntity extends _SpecialCreeperEntity {
for( int i = 0; i < babiesToSpawn; i++ ) {
groupData = spawnBaby( explosionPower / 3.0F, groupData );
}
spawnAnim();
playSound( SoundEvents.EGG_THROW, 1.0F, 2.0F / (random.nextFloat() * 0.4F + 0.8F) );
}

View file

@ -89,6 +89,7 @@ public class MirageEndermanEntity extends _SpecialEndermanEntity {
@Override
public void tick() {
if( isFake && tickCount >= 200 ) {
spawnAnim();
remove();
}
super.tick();

View file

@ -3,7 +3,9 @@ package fathertoast.specialmobs.common.entity.skeleton;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.MobHelper;
import fathertoast.specialmobs.common.entity.ai.INinja;
import fathertoast.specialmobs.common.entity.ai.NinjaGoal;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
@ -19,10 +21,13 @@ 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.potion.EffectInstance;
import net.minecraft.potion.Effects;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Hand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.World;
@ -78,8 +83,7 @@ public class NinjaSkeletonEntity extends _SpecialSkeletonEntity implements INinj
@Override
protected void registerVariantGoals() {
setRangedAI( 1.0, 10, 9.0F );
//TODO AIHelper.insertGoalReverse( goalSelector, getVariantAttackPriority() - 1, null );
goalSelector.addGoal( -9, new NinjaGoal<>( this ) );
}
/** Called during spawn finalization to set starting equipment. */
@ -95,15 +99,13 @@ public class NinjaSkeletonEntity extends _SpecialSkeletonEntity implements INinj
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
revealTo( target );
}
protected void onVariantAttack( Entity target ) { revealTo( target, true ); }
/** @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() );
revealTo( source.getEntity(), false );
return true;
}
return false;
@ -113,21 +115,25 @@ public class NinjaSkeletonEntity extends _SpecialSkeletonEntity implements INinj
@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 );
if( !level.isClientSide() && getHiddenDragon() != null ) revealTo( player, true );
return super.mobInteract( player, hand );
}
/** Called by the player when it touches this entity. */
@Override
public void playerTouch( PlayerEntity player ) {
if( !level.isClientSide() && getHiddenDragon() != null && !player.isCreative() ) revealTo( player, true );
super.playerTouch( player );
}
/** 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
this.setHiding( true );
this.setDisguiseBlock( Blocks.DIRT.defaultBlockState() );
setHiddenDragon( NinjaGoal.pickDisguise( this ) );
}
else if( onGround && getDisguiseBlock() == null &&
else if( onGround && getHiddenDragon() == null &&
(getTarget() == null || getTarget() instanceof PlayerEntity && ((PlayerEntity) getTarget()).isCreative()) ) {
canHide = true;
}
@ -135,33 +141,48 @@ public class NinjaSkeletonEntity extends _SpecialSkeletonEntity implements INinj
super.tick();
}
// // Moves this entity.
// @Override TODO
// public void move( MoverType type, double x, double y, double z ) {
// if( isHiding() && type != MoverType.PISTON ) {
// motionY = 0.0;
// }
// else {
// super.move( type, x, y, z );
// }
// }
/** Moves this entity to a new position and rotation. */
@Override
public void moveTo( double x, double y, double z, float yaw, float pitch ) {
if( !isCrouchingTiger() ) super.moveTo( x, y, z, yaw, pitch );
}
/** Sets this entity's movement. */
@Override
public void setDeltaMovement( Vector3d vec ) {
if( !isCrouchingTiger() ) super.setDeltaMovement( vec );
}
/** Returns true if this entity should push and be pushed by other entities when colliding. */
@Override
public boolean isPushable() {
return super.isPushable() && !isHiding();
return super.isPushable() && !isCrouchingTiger();
}
/** Sets this entity on fire for a specific duration. */
@Override
public void setRemainingFireTicks( int ticks ) {
if( !isHiding() ) super.setRemainingFireTicks( ticks );
if( !isCrouchingTiger() ) 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 );
public void revealTo( @Nullable Entity target, boolean ambush ) {
if( getHiddenDragon() == null ) return;
setHiddenDragon( null );
if( target instanceof LivingEntity && !(target instanceof PlayerEntity && ((PlayerEntity) target).isCreative()) ) {
final LivingEntity livingTarget = (LivingEntity) target;
setTarget( livingTarget );
if( ambush ) {
final int duration = MobHelper.getDebuffDuration( level.getDifficulty() );
livingTarget.addEffect( new EffectInstance( Effects.POISON, duration ) );
livingTarget.addEffect( new EffectInstance( Effects.MOVEMENT_SLOWDOWN, duration ) );
livingTarget.addEffect( new EffectInstance( Effects.BLINDNESS, duration ) );
livingTarget.removeEffect( Effects.NIGHT_VISION ); // Prevent blind + night vision combo (black screen)
}
}
}
private static final ResourceLocation[] TEXTURES = {
@ -194,37 +215,31 @@ public class NinjaSkeletonEntity extends _SpecialSkeletonEntity implements INinj
/** @return Whether this ninja is currently immobile. */
@Override
public boolean isHiding() { return getEntityData().get( IS_HIDING ); }
public boolean isCrouchingTiger() { 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 setHiding( boolean value ) {
if( value != isHiding() ) {
public void setCrouchingTiger( boolean value ) {
if( value != isCrouchingTiger() ) {
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() {
public BlockState getHiddenDragon() {
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 ) {
public void setHiddenDragon( @Nullable BlockState block ) {
getEntityData().set( HIDING_BLOCK, Optional.ofNullable( block ) );
canHide = false;
// Smoke puff when emerging from disguise
if( block == null ) {
//spawnExplosionParticle(); TODO
}
if( block == null ) spawnAnim();
}
}

View file

@ -94,6 +94,7 @@ public class MotherSpiderEntity extends _SpecialSpiderEntity {
if( extraBabies > 0 && amount > 1.0F && level instanceof IServerWorld && random.nextFloat() < 0.33F ) {
extraBabies--;
spawnBaby( 0.66F, null );
spawnAnim();
playSound( SoundEvents.EGG_THROW, 1.0F, 2.0F / (random.nextFloat() * 0.4F + 0.8F) );
}
return true;
@ -112,6 +113,7 @@ public class MotherSpiderEntity extends _SpecialSpiderEntity {
for( int i = 0; i < babiesToSpawn; i++ ) {
groupData = spawnBaby( 0.33F, groupData );
}
spawnAnim();
playSound( SoundEvents.EGG_THROW, 1.0F, 2.0F / (random.nextFloat() * 0.4F + 0.8F) );
}
super.remove( keepData );

View file

@ -3,7 +3,9 @@ package fathertoast.specialmobs.common.entity.witherskeleton;
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
import fathertoast.specialmobs.common.bestiary.MobFamily;
import fathertoast.specialmobs.common.bestiary.SpecialMob;
import fathertoast.specialmobs.common.entity.MobHelper;
import fathertoast.specialmobs.common.entity.ai.INinja;
import fathertoast.specialmobs.common.entity.ai.NinjaGoal;
import fathertoast.specialmobs.common.util.AttributeHelper;
import fathertoast.specialmobs.common.util.References;
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
@ -19,10 +21,13 @@ 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.potion.EffectInstance;
import net.minecraft.potion.Effects;
import net.minecraft.util.ActionResultType;
import net.minecraft.util.DamageSource;
import net.minecraft.util.Hand;
import net.minecraft.util.ResourceLocation;
import net.minecraft.util.math.vector.Vector3d;
import net.minecraft.world.DifficultyInstance;
import net.minecraft.world.World;
@ -78,8 +83,7 @@ public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity impl
@Override
protected void registerVariantGoals() {
setRangedAI( 1.0, 10, 9.0F );
//TODO AIHelper.insertGoalReverse( goalSelector, getVariantAttackPriority() - 1, null );
goalSelector.addGoal( -9, new NinjaGoal<>( this ) );
}
/** Called during spawn finalization to set starting equipment. */
@ -95,15 +99,13 @@ public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity impl
/** Override to apply effects when this entity hits a target with a melee attack. */
@Override
protected void onVariantAttack( Entity target ) {
revealTo( target );
}
protected void onVariantAttack( Entity target ) { revealTo( target, true ); }
/** @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() );
revealTo( source.getEntity(), false );
return true;
}
return false;
@ -113,19 +115,25 @@ public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity impl
@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 );
if( !level.isClientSide() && getHiddenDragon() != null ) revealTo( player, true );
return super.mobInteract( player, hand );
}
/** Called by the player when it touches this entity. */
@Override
public void playerTouch( PlayerEntity player ) {
if( !level.isClientSide() && getHiddenDragon() != null && !player.isCreative() ) revealTo( player, true );
super.playerTouch( player );
}
/** 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
setHiddenDragon( NinjaGoal.pickDisguise( this ) );
}
else if( onGround && getDisguiseBlock() == null &&
else if( onGround && getHiddenDragon() == null &&
(getTarget() == null || getTarget() instanceof PlayerEntity && ((PlayerEntity) getTarget()).isCreative()) ) {
canHide = true;
}
@ -133,31 +141,48 @@ public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity impl
super.tick();
}
// // Moves this entity.
// @Override TODO
// public void move( MoverType type, double x, double y, double z ) {
// if( isHiding() && 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 !isHiding(); }
/** Moves this entity to a new position and rotation. */
@Override
public void moveTo( double x, double y, double z, float yaw, float pitch ) {
if( !isCrouchingTiger() ) super.moveTo( x, y, z, yaw, pitch );
}
/** Sets this entity's movement. */
@Override
public void setDeltaMovement( Vector3d vec ) {
if( !isCrouchingTiger() ) super.setDeltaMovement( vec );
}
/** Returns true if this entity should push and be pushed by other entities when colliding. */
@Override
public boolean isPushable() {
return super.isPushable() && !isCrouchingTiger();
}
/** Sets this entity on fire for a specific duration. */
@Override
public void setRemainingFireTicks( int ticks ) {
if( !isHiding() ) super.setRemainingFireTicks( ticks );
if( !isCrouchingTiger() ) 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 );
public void revealTo( @Nullable Entity target, boolean ambush ) {
if( getHiddenDragon() == null ) return;
setHiddenDragon( null );
if( target instanceof LivingEntity && !(target instanceof PlayerEntity && ((PlayerEntity) target).isCreative()) ) {
final LivingEntity livingTarget = (LivingEntity) target;
setTarget( livingTarget );
if( ambush ) {
final int duration = MobHelper.getDebuffDuration( level.getDifficulty() );
livingTarget.addEffect( new EffectInstance( Effects.POISON, duration ) );
livingTarget.addEffect( new EffectInstance( Effects.MOVEMENT_SLOWDOWN, duration ) );
livingTarget.addEffect( new EffectInstance( Effects.BLINDNESS, duration ) );
livingTarget.removeEffect( Effects.NIGHT_VISION ); // Prevent blind + night vision combo (black screen)
}
}
}
private static final ResourceLocation[] TEXTURES = {
@ -190,37 +215,31 @@ public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity impl
/** @return Whether this ninja is currently immobile. */
@Override
public boolean isHiding() { return getEntityData().get( IS_HIDING ); }
public boolean isCrouchingTiger() { 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 setHiding( boolean value ) {
if( value != isHiding() ) {
public void setCrouchingTiger( boolean value ) {
if( value != isCrouchingTiger() ) {
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() {
public BlockState getHiddenDragon() {
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 ) {
public void setHiddenDragon( @Nullable BlockState block ) {
getEntityData().set( HIDING_BLOCK, Optional.ofNullable( block ) );
canHide = false;
// Smoke puff when emerging from disguise
if( block == null ) {
//spawnExplosionParticle(); TODO
}
if( block == null ) spawnAnim();
}
}

View file

@ -172,7 +172,7 @@ public final class AnnotationHelper {
* @throws NoSuchMethodException if the constructor does not exist.
*/
private static <T> Constructor<T> getConstructor( Class<T> type, Class<? extends Annotation> annotation ) throws NoSuchMethodException {
for( Constructor<?> constructor : type.getConstructors() ) {
for( Constructor<?> constructor : type.getDeclaredConstructors() ) {
if( constructor.isAnnotationPresent( annotation ) )
//noinspection unchecked
return (Constructor<T>) constructor;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 B

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 5.4 KiB

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 6.1 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 7.9 KiB

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 8.6 KiB

After

Width:  |  Height:  |  Size: 438 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.6 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 KiB

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 244 B

After

Width:  |  Height:  |  Size: 261 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.9 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 117 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.6 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B