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
|
@ -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 );
|
||||
}
|
||||
}
|
|
@ -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"
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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 );
|
||||
}
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
|
|
|
@ -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) );
|
||||
}
|
||||
|
||||
|
|
|
@ -89,6 +89,7 @@ public class MirageEndermanEntity extends _SpecialEndermanEntity {
|
|||
@Override
|
||||
public void tick() {
|
||||
if( isFake && tickCount >= 200 ) {
|
||||
spawnAnim();
|
||||
remove();
|
||||
}
|
||||
super.tick();
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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 );
|
||||
|
|
|
@ -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();
|
||||
}
|
||||
}
|
|
@ -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;
|
||||
|
|
After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 244 B After Width: | Height: | Size: 261 B |
Before Width: | Height: | Size: 5.4 KiB After Width: | Height: | Size: 2.9 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 117 B |
Before Width: | Height: | Size: 6.1 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 7.9 KiB After Width: | Height: | Size: 2.6 KiB |
Before Width: | Height: | Size: 8.6 KiB After Width: | Height: | Size: 438 B |
After Width: | Height: | Size: 1.6 KiB |
Before Width: | Height: | Size: 2.4 KiB After Width: | Height: | Size: 2.4 KiB |
Before Width: | Height: | Size: 244 B After Width: | Height: | Size: 261 B |
After Width: | Height: | Size: 2.9 KiB |
After Width: | Height: | Size: 117 B |
After Width: | Height: | Size: 2.4 KiB |
After Width: | Height: | Size: 2.6 KiB |
After Width: | Height: | Size: 438 B |