mirror of
https://github.com/FatherToast/SpecialMobs.git
synced 2025-09-17 06:01:13 +00:00
ninja'd
This commit is contained in:
parent
a504a0c885
commit
bdb22925ae
5 changed files with 317 additions and 120 deletions
|
@ -1,22 +1,15 @@
|
||||||
package fathertoast.specialmobs.client.renderer.entity;
|
package fathertoast.specialmobs.client.renderer.entity;
|
||||||
|
|
||||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||||
import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobEyesLayer;
|
|
||||||
import fathertoast.specialmobs.client.renderer.entity.layers.SpecialMobOverlayLayer;
|
|
||||||
import fathertoast.specialmobs.common.entity.ISpecialMob;
|
|
||||||
import fathertoast.specialmobs.common.entity.ai.INinja;
|
import fathertoast.specialmobs.common.entity.ai.INinja;
|
||||||
import mcp.MethodsReturnNonnullByDefault;
|
import mcp.MethodsReturnNonnullByDefault;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.Blocks;
|
|
||||||
import net.minecraft.client.Minecraft;
|
import net.minecraft.client.Minecraft;
|
||||||
import net.minecraft.client.renderer.BlockRendererDispatcher;
|
import net.minecraft.client.renderer.BlockRendererDispatcher;
|
||||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
||||||
import net.minecraft.client.renderer.entity.EntityRendererManager;
|
import net.minecraft.client.renderer.entity.EntityRendererManager;
|
||||||
import net.minecraft.client.renderer.entity.SkeletonRenderer;
|
|
||||||
import net.minecraft.client.renderer.entity.model.SkeletonModel;
|
|
||||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||||
import net.minecraft.entity.monster.AbstractSkeletonEntity;
|
import net.minecraft.entity.monster.AbstractSkeletonEntity;
|
||||||
import net.minecraft.util.ResourceLocation;
|
|
||||||
import net.minecraftforge.api.distmarker.Dist;
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
import net.minecraftforge.client.model.data.EmptyModelData;
|
import net.minecraftforge.client.model.data.EmptyModelData;
|
||||||
|
@ -26,51 +19,35 @@ import javax.annotation.ParametersAreNonnullByDefault;
|
||||||
@ParametersAreNonnullByDefault
|
@ParametersAreNonnullByDefault
|
||||||
@MethodsReturnNonnullByDefault
|
@MethodsReturnNonnullByDefault
|
||||||
@OnlyIn( Dist.CLIENT )
|
@OnlyIn( Dist.CLIENT )
|
||||||
public class NinjaSkeletonRenderer extends SkeletonRenderer {
|
public class NinjaSkeletonRenderer extends SpecialSkeletonRenderer {
|
||||||
|
|
||||||
private final float baseShadowRadius;
|
|
||||||
private final BlockRendererDispatcher blockRenderer;
|
private final BlockRendererDispatcher blockRenderer;
|
||||||
|
|
||||||
public NinjaSkeletonRenderer( EntityRendererManager rendererManager ) {
|
public NinjaSkeletonRenderer( EntityRendererManager rendererManager ) {
|
||||||
super( rendererManager );
|
super( rendererManager );
|
||||||
baseShadowRadius = shadowRadius = shadowStrength = 0.0F;
|
|
||||||
blockRenderer = Minecraft.getInstance().getBlockRenderer();
|
blockRenderer = Minecraft.getInstance().getBlockRenderer();
|
||||||
addLayer( new SpecialMobEyesLayer<>( this ) );
|
|
||||||
addLayer( new SpecialMobOverlayLayer<>( this, new SkeletonModel<>() ) );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void render( AbstractSkeletonEntity skeletonEntity, float f1, float f2, MatrixStack matrixStack, IRenderTypeBuffer buffer, int i ) {
|
public void render( AbstractSkeletonEntity entity, float rotation, float partialTicks,
|
||||||
BlockState disguiseState = ((INinja) skeletonEntity).getDisguiseBlock();
|
MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight ) {
|
||||||
|
final BlockState disguiseBlock = ((INinja) entity).getHiddenDragon();
|
||||||
if( disguiseState == null ) {
|
if( disguiseBlock == null ) {
|
||||||
super.render( skeletonEntity, f1, f2, matrixStack, buffer, i );
|
super.render( entity, rotation, partialTicks, matrixStack, buffer, packedLight );
|
||||||
}
|
}
|
||||||
else {
|
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.pushPose();
|
||||||
|
|
||||||
matrixStack.translate( -0.5D, 0.0D, -0.5D );
|
matrixStack.translate( -0.5, 0.0, -0.5 );
|
||||||
this.blockRenderer.renderBlock( Blocks.LECTERN.defaultBlockState(), matrixStack, buffer, i, OverlayTexture.NO_OVERLAY, EmptyModelData.INSTANCE );
|
blockRenderer.renderBlock( block, matrixStack, buffer, packedLight, OverlayTexture.NO_OVERLAY, EmptyModelData.INSTANCE );
|
||||||
|
|
||||||
matrixStack.popPose();
|
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 );
|
|
||||||
}
|
|
||||||
}
|
}
|
|
@ -6,20 +6,20 @@ import javax.annotation.Nullable;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Monsters must implement this interface to use the ninja goal AI.
|
* 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 {
|
public interface INinja {
|
||||||
|
|
||||||
/** @return Whether this ninja is currently immobile. */
|
/** @return Whether this ninja is currently immovable. */
|
||||||
boolean isHiding();
|
boolean isCrouchingTiger();
|
||||||
|
|
||||||
/** Sets this ninja's immovable state. When activated, the entity is 'snapped' to the nearest block position. */
|
/** 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. */
|
/** @return The block being hidden (rendered) as, or null if not hiding. */
|
||||||
@Nullable
|
@Nullable
|
||||||
BlockState getDisguiseBlock();
|
BlockState getHiddenDragon();
|
||||||
|
|
||||||
/** Sets the block being hidden (rendered) as, set to null to cancel hiding. */
|
/** 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();
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,7 +3,9 @@ package fathertoast.specialmobs.common.entity.skeleton;
|
||||||
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
|
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
|
||||||
import fathertoast.specialmobs.common.bestiary.MobFamily;
|
import fathertoast.specialmobs.common.bestiary.MobFamily;
|
||||||
import fathertoast.specialmobs.common.bestiary.SpecialMob;
|
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.INinja;
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.NinjaGoal;
|
||||||
import fathertoast.specialmobs.common.util.AttributeHelper;
|
import fathertoast.specialmobs.common.util.AttributeHelper;
|
||||||
import fathertoast.specialmobs.common.util.References;
|
import fathertoast.specialmobs.common.util.References;
|
||||||
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
|
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.DataParameter;
|
||||||
import net.minecraft.network.datasync.DataSerializers;
|
import net.minecraft.network.datasync.DataSerializers;
|
||||||
import net.minecraft.network.datasync.EntityDataManager;
|
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.ActionResultType;
|
||||||
import net.minecraft.util.DamageSource;
|
import net.minecraft.util.DamageSource;
|
||||||
import net.minecraft.util.Hand;
|
import net.minecraft.util.Hand;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraft.util.math.vector.Vector3d;
|
||||||
import net.minecraft.world.DifficultyInstance;
|
import net.minecraft.world.DifficultyInstance;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
@ -78,8 +83,7 @@ public class NinjaSkeletonEntity extends _SpecialSkeletonEntity implements INinj
|
||||||
@Override
|
@Override
|
||||||
protected void registerVariantGoals() {
|
protected void registerVariantGoals() {
|
||||||
setRangedAI( 1.0, 10, 9.0F );
|
setRangedAI( 1.0, 10, 9.0F );
|
||||||
|
goalSelector.addGoal( -9, new NinjaGoal<>( this ) );
|
||||||
//TODO AIHelper.insertGoalReverse( goalSelector, getVariantAttackPriority() - 1, null );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called during spawn finalization to set starting equipment. */
|
/** 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 to apply effects when this entity hits a target with a melee attack. */
|
||||||
@Override
|
@Override
|
||||||
protected void onVariantAttack( Entity target ) {
|
protected void onVariantAttack( Entity target ) { revealTo( target, true ); }
|
||||||
revealTo( target );
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return Attempts to damage this entity; returns true if the hit was successful. */
|
/** @return Attempts to damage this entity; returns true if the hit was successful. */
|
||||||
@Override
|
@Override
|
||||||
public boolean hurt( DamageSource source, float amount ) {
|
public boolean hurt( DamageSource source, float amount ) {
|
||||||
if( super.hurt( source, amount ) ) {
|
if( super.hurt( source, amount ) ) {
|
||||||
revealTo( source.getEntity() );
|
revealTo( source.getEntity(), false );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -113,21 +115,25 @@ public class NinjaSkeletonEntity extends _SpecialSkeletonEntity implements INinj
|
||||||
@Override
|
@Override
|
||||||
public ActionResultType mobInteract( PlayerEntity player, Hand hand ) {
|
public ActionResultType mobInteract( PlayerEntity player, Hand hand ) {
|
||||||
// Attack if the player tries to right click the "block"
|
// 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 );
|
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. */
|
/** Called each tick to update this entity. */
|
||||||
@Override
|
@Override
|
||||||
public void tick() {
|
public void tick() {
|
||||||
// TODO can this be moved to the ninja AI?
|
|
||||||
if( !level.isClientSide() ) {
|
if( !level.isClientSide() ) {
|
||||||
if( canHide ) {
|
if( canHide ) {
|
||||||
//EntityAINinja.startHiding( this ); TODO
|
setHiddenDragon( NinjaGoal.pickDisguise( this ) );
|
||||||
this.setHiding( true );
|
|
||||||
this.setDisguiseBlock( Blocks.DIRT.defaultBlockState() );
|
|
||||||
}
|
}
|
||||||
else if( onGround && getDisguiseBlock() == null &&
|
else if( onGround && getHiddenDragon() == null &&
|
||||||
(getTarget() == null || getTarget() instanceof PlayerEntity && ((PlayerEntity) getTarget()).isCreative()) ) {
|
(getTarget() == null || getTarget() instanceof PlayerEntity && ((PlayerEntity) getTarget()).isCreative()) ) {
|
||||||
canHide = true;
|
canHide = true;
|
||||||
}
|
}
|
||||||
|
@ -135,33 +141,48 @@ public class NinjaSkeletonEntity extends _SpecialSkeletonEntity implements INinj
|
||||||
super.tick();
|
super.tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Moves this entity.
|
/** Moves this entity to a new position and rotation. */
|
||||||
// @Override TODO
|
@Override
|
||||||
// public void move( MoverType type, double x, double y, double z ) {
|
public void moveTo( double x, double y, double z, float yaw, float pitch ) {
|
||||||
// if( isHiding() && type != MoverType.PISTON ) {
|
if( !isCrouchingTiger() ) super.moveTo( x, y, z, yaw, pitch );
|
||||||
// motionY = 0.0;
|
}
|
||||||
// }
|
|
||||||
// else {
|
/** Sets this entity's movement. */
|
||||||
// super.move( type, x, y, z );
|
@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. */
|
/** Returns true if this entity should push and be pushed by other entities when colliding. */
|
||||||
@Override
|
@Override
|
||||||
public boolean isPushable() {
|
public boolean isPushable() {
|
||||||
return super.isPushable() && !isHiding();
|
return super.isPushable() && !isCrouchingTiger();
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets this entity on fire for a specific duration. */
|
/** Sets this entity on fire for a specific duration. */
|
||||||
@Override
|
@Override
|
||||||
public void setRemainingFireTicks( int ticks ) {
|
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. */
|
/** Reveals this ninja and sets its target so that it doesn't immediately re-disguise itself. */
|
||||||
public void revealTo( @Nullable Entity target ) {
|
public void revealTo( @Nullable Entity target, boolean ambush ) {
|
||||||
setDisguiseBlock( null );
|
if( getHiddenDragon() == null ) return;
|
||||||
if( target instanceof LivingEntity ) setTarget( (LivingEntity) target );
|
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 = {
|
private static final ResourceLocation[] TEXTURES = {
|
||||||
|
@ -194,37 +215,31 @@ public class NinjaSkeletonEntity extends _SpecialSkeletonEntity implements INinj
|
||||||
|
|
||||||
/** @return Whether this ninja is currently immobile. */
|
/** @return Whether this ninja is currently immobile. */
|
||||||
@Override
|
@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. */
|
/** Sets this ninja's immovable state. When activated, the entity is 'snapped' to the nearest block position. */
|
||||||
@Override
|
@Override
|
||||||
public void setHiding( boolean value ) {
|
public void setCrouchingTiger( boolean value ) {
|
||||||
if( value != isHiding() ) {
|
if( value != isCrouchingTiger() ) {
|
||||||
getEntityData().set( IS_HIDING, value );
|
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. */
|
/** @return The block being hidden (rendered) as, or null if not hiding. */
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public BlockState getDisguiseBlock() {
|
public BlockState getHiddenDragon() {
|
||||||
if( isAlive() ) return getEntityData().get( HIDING_BLOCK ).orElse( null );
|
if( isAlive() ) return getEntityData().get( HIDING_BLOCK ).orElse( null );
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the block being hidden (rendered) as, set to null to cancel hiding. */
|
/** Sets the block being hidden (rendered) as, set to null to cancel hiding. */
|
||||||
@Override
|
@Override
|
||||||
public void setDisguiseBlock( @Nullable BlockState block ) {
|
public void setHiddenDragon( @Nullable BlockState block ) {
|
||||||
getEntityData().set( HIDING_BLOCK, Optional.ofNullable( block ) );
|
getEntityData().set( HIDING_BLOCK, Optional.ofNullable( block ) );
|
||||||
canHide = false;
|
canHide = false;
|
||||||
|
|
||||||
// Smoke puff when emerging from disguise
|
// Smoke puff when emerging from disguise
|
||||||
if( block == null ) {
|
if( block == null ) spawnAnim();
|
||||||
//spawnExplosionParticle(); TODO
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
|
@ -3,7 +3,9 @@ package fathertoast.specialmobs.common.entity.witherskeleton;
|
||||||
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
|
import fathertoast.specialmobs.common.bestiary.BestiaryInfo;
|
||||||
import fathertoast.specialmobs.common.bestiary.MobFamily;
|
import fathertoast.specialmobs.common.bestiary.MobFamily;
|
||||||
import fathertoast.specialmobs.common.bestiary.SpecialMob;
|
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.INinja;
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.NinjaGoal;
|
||||||
import fathertoast.specialmobs.common.util.AttributeHelper;
|
import fathertoast.specialmobs.common.util.AttributeHelper;
|
||||||
import fathertoast.specialmobs.common.util.References;
|
import fathertoast.specialmobs.common.util.References;
|
||||||
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
|
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.DataParameter;
|
||||||
import net.minecraft.network.datasync.DataSerializers;
|
import net.minecraft.network.datasync.DataSerializers;
|
||||||
import net.minecraft.network.datasync.EntityDataManager;
|
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.ActionResultType;
|
||||||
import net.minecraft.util.DamageSource;
|
import net.minecraft.util.DamageSource;
|
||||||
import net.minecraft.util.Hand;
|
import net.minecraft.util.Hand;
|
||||||
import net.minecraft.util.ResourceLocation;
|
import net.minecraft.util.ResourceLocation;
|
||||||
|
import net.minecraft.util.math.vector.Vector3d;
|
||||||
import net.minecraft.world.DifficultyInstance;
|
import net.minecraft.world.DifficultyInstance;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
@ -78,8 +83,7 @@ public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity impl
|
||||||
@Override
|
@Override
|
||||||
protected void registerVariantGoals() {
|
protected void registerVariantGoals() {
|
||||||
setRangedAI( 1.0, 10, 9.0F );
|
setRangedAI( 1.0, 10, 9.0F );
|
||||||
|
goalSelector.addGoal( -9, new NinjaGoal<>( this ) );
|
||||||
//TODO AIHelper.insertGoalReverse( goalSelector, getVariantAttackPriority() - 1, null );
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Called during spawn finalization to set starting equipment. */
|
/** 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 to apply effects when this entity hits a target with a melee attack. */
|
||||||
@Override
|
@Override
|
||||||
protected void onVariantAttack( Entity target ) {
|
protected void onVariantAttack( Entity target ) { revealTo( target, true ); }
|
||||||
revealTo( target );
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return Attempts to damage this entity; returns true if the hit was successful. */
|
/** @return Attempts to damage this entity; returns true if the hit was successful. */
|
||||||
@Override
|
@Override
|
||||||
public boolean hurt( DamageSource source, float amount ) {
|
public boolean hurt( DamageSource source, float amount ) {
|
||||||
if( super.hurt( source, amount ) ) {
|
if( super.hurt( source, amount ) ) {
|
||||||
revealTo( source.getEntity() );
|
revealTo( source.getEntity(), false );
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
|
@ -113,19 +115,25 @@ public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity impl
|
||||||
@Override
|
@Override
|
||||||
public ActionResultType mobInteract( PlayerEntity player, Hand hand ) {
|
public ActionResultType mobInteract( PlayerEntity player, Hand hand ) {
|
||||||
// Attack if the player tries to right click the "block"
|
// 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 );
|
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. */
|
/** Called each tick to update this entity. */
|
||||||
@Override
|
@Override
|
||||||
public void tick() {
|
public void tick() {
|
||||||
// TODO can this be moved to the ninja AI?
|
|
||||||
if( !level.isClientSide() ) {
|
if( !level.isClientSide() ) {
|
||||||
if( canHide ) {
|
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()) ) {
|
(getTarget() == null || getTarget() instanceof PlayerEntity && ((PlayerEntity) getTarget()).isCreative()) ) {
|
||||||
canHide = true;
|
canHide = true;
|
||||||
}
|
}
|
||||||
|
@ -133,31 +141,48 @@ public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity impl
|
||||||
super.tick();
|
super.tick();
|
||||||
}
|
}
|
||||||
|
|
||||||
// // Moves this entity.
|
/** Moves this entity to a new position and rotation. */
|
||||||
// @Override TODO
|
@Override
|
||||||
// public void move( MoverType type, double x, double y, double z ) {
|
public void moveTo( double x, double y, double z, float yaw, float pitch ) {
|
||||||
// if( isHiding() && type != MoverType.PISTON ) {
|
if( !isCrouchingTiger() ) super.moveTo( x, y, z, yaw, pitch );
|
||||||
// motionY = 0.0;
|
}
|
||||||
// }
|
|
||||||
// else {
|
/** Sets this entity's movement. */
|
||||||
// super.move( type, x, y, z );
|
@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
|
/** Returns true if this entity should push and be pushed by other entities when colliding. */
|
||||||
// public boolean canBePushed() { return !isHiding(); }
|
@Override
|
||||||
|
public boolean isPushable() {
|
||||||
|
return super.isPushable() && !isCrouchingTiger();
|
||||||
|
}
|
||||||
|
|
||||||
/** Sets this entity on fire for a specific duration. */
|
/** Sets this entity on fire for a specific duration. */
|
||||||
@Override
|
@Override
|
||||||
public void setRemainingFireTicks( int ticks ) {
|
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. */
|
/** Reveals this ninja and sets its target so that it doesn't immediately re-disguise itself. */
|
||||||
public void revealTo( @Nullable Entity target ) {
|
public void revealTo( @Nullable Entity target, boolean ambush ) {
|
||||||
setDisguiseBlock( null );
|
if( getHiddenDragon() == null ) return;
|
||||||
if( target instanceof LivingEntity ) setTarget( (LivingEntity) target );
|
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 = {
|
private static final ResourceLocation[] TEXTURES = {
|
||||||
|
@ -190,37 +215,31 @@ public class NinjaWitherSkeletonEntity extends _SpecialWitherSkeletonEntity impl
|
||||||
|
|
||||||
/** @return Whether this ninja is currently immobile. */
|
/** @return Whether this ninja is currently immobile. */
|
||||||
@Override
|
@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. */
|
/** Sets this ninja's immovable state. When activated, the entity is 'snapped' to the nearest block position. */
|
||||||
@Override
|
@Override
|
||||||
public void setHiding( boolean value ) {
|
public void setCrouchingTiger( boolean value ) {
|
||||||
if( value != isHiding() ) {
|
if( value != isCrouchingTiger() ) {
|
||||||
getEntityData().set( IS_HIDING, value );
|
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. */
|
/** @return The block being hidden (rendered) as, or null if not hiding. */
|
||||||
@Nullable
|
@Nullable
|
||||||
@Override
|
@Override
|
||||||
public BlockState getDisguiseBlock() {
|
public BlockState getHiddenDragon() {
|
||||||
if( isAlive() ) return getEntityData().get( HIDING_BLOCK ).orElse( null );
|
if( isAlive() ) return getEntityData().get( HIDING_BLOCK ).orElse( null );
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Sets the block being hidden (rendered) as, set to null to cancel hiding. */
|
/** Sets the block being hidden (rendered) as, set to null to cancel hiding. */
|
||||||
@Override
|
@Override
|
||||||
public void setDisguiseBlock( @Nullable BlockState block ) {
|
public void setHiddenDragon( @Nullable BlockState block ) {
|
||||||
getEntityData().set( HIDING_BLOCK, Optional.ofNullable( block ) );
|
getEntityData().set( HIDING_BLOCK, Optional.ofNullable( block ) );
|
||||||
canHide = false;
|
canHide = false;
|
||||||
|
|
||||||
// Smoke puff when emerging from disguise
|
// Smoke puff when emerging from disguise
|
||||||
if( block == null ) {
|
if( block == null ) spawnAnim();
|
||||||
//spawnExplosionParticle(); TODO
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
Loading…
Add table
Reference in a new issue