mirror of
https://github.com/FatherToast/SpecialMobs.git
synced 2025-04-25 06:45:11 +00:00
Amphibious mobs :O
This commit is contained in:
parent
653a122408
commit
43fe9c89d4
17 changed files with 646 additions and 30 deletions
|
@ -3,15 +3,13 @@ package fathertoast.specialmobs.client;
|
||||||
import fathertoast.specialmobs.client.renderer.entity.family.*;
|
import fathertoast.specialmobs.client.renderer.entity.family.*;
|
||||||
import fathertoast.specialmobs.client.renderer.entity.projectile.BugSpitRenderer;
|
import fathertoast.specialmobs.client.renderer.entity.projectile.BugSpitRenderer;
|
||||||
import fathertoast.specialmobs.client.renderer.entity.projectile.SpecialFishingBobberRenderer;
|
import fathertoast.specialmobs.client.renderer.entity.projectile.SpecialFishingBobberRenderer;
|
||||||
import fathertoast.specialmobs.client.renderer.entity.species.CorporealShiftGhastRenderer;
|
import fathertoast.specialmobs.client.renderer.entity.species.*;
|
||||||
import fathertoast.specialmobs.client.renderer.entity.species.NinjaSkeletonRenderer;
|
|
||||||
import fathertoast.specialmobs.client.renderer.entity.species.PotionSlimeRenderer;
|
|
||||||
import fathertoast.specialmobs.client.renderer.entity.species.SpecialZombieVillagerRenderer;
|
|
||||||
import fathertoast.specialmobs.common.bestiary.MobFamily;
|
import fathertoast.specialmobs.common.bestiary.MobFamily;
|
||||||
import fathertoast.specialmobs.common.config.Config;
|
import fathertoast.specialmobs.common.config.Config;
|
||||||
import fathertoast.specialmobs.common.core.SpecialMobs;
|
import fathertoast.specialmobs.common.core.SpecialMobs;
|
||||||
import fathertoast.specialmobs.common.core.register.SMEntities;
|
import fathertoast.specialmobs.common.core.register.SMEntities;
|
||||||
import fathertoast.specialmobs.common.entity.ghast.CorporealShiftGhastEntity;
|
import fathertoast.specialmobs.common.entity.ghast.CorporealShiftGhastEntity;
|
||||||
|
import fathertoast.specialmobs.common.entity.silverfish.PufferSilverfishEntity;
|
||||||
import fathertoast.specialmobs.common.entity.skeleton.NinjaSkeletonEntity;
|
import fathertoast.specialmobs.common.entity.skeleton.NinjaSkeletonEntity;
|
||||||
import fathertoast.specialmobs.common.entity.slime.PotionSlimeEntity;
|
import fathertoast.specialmobs.common.entity.slime.PotionSlimeEntity;
|
||||||
import fathertoast.specialmobs.common.entity.witherskeleton.NinjaWitherSkeletonEntity;
|
import fathertoast.specialmobs.common.entity.witherskeleton.NinjaWitherSkeletonEntity;
|
||||||
|
@ -77,6 +75,8 @@ public class ClientRegister {
|
||||||
|
|
||||||
registerSpeciesRenderer( PotionSlimeEntity.SPECIES, PotionSlimeRenderer::new );
|
registerSpeciesRenderer( PotionSlimeEntity.SPECIES, PotionSlimeRenderer::new );
|
||||||
|
|
||||||
|
registerSpeciesRenderer( PufferSilverfishEntity.SPECIES, ShortSilverfishRenderer::new );
|
||||||
|
|
||||||
registerSpeciesRenderer( CorporealShiftGhastEntity.SPECIES, CorporealShiftGhastRenderer::new );
|
registerSpeciesRenderer( CorporealShiftGhastEntity.SPECIES, CorporealShiftGhastRenderer::new );
|
||||||
|
|
||||||
// Other
|
// Other
|
||||||
|
|
|
@ -0,0 +1,27 @@
|
||||||
|
package fathertoast.specialmobs.client.renderer.entity.species;
|
||||||
|
|
||||||
|
import fathertoast.specialmobs.client.renderer.entity.family.SpecialSilverfishRenderer;
|
||||||
|
import net.minecraft.client.renderer.entity.EntityRendererManager;
|
||||||
|
import net.minecraft.entity.monster.SilverfishEntity;
|
||||||
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
import net.minecraft.util.math.vector.Vector3d;
|
||||||
|
import net.minecraftforge.api.distmarker.Dist;
|
||||||
|
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||||
|
|
||||||
|
@OnlyIn( Dist.CLIENT )
|
||||||
|
public class ShortSilverfishRenderer extends SpecialSilverfishRenderer {
|
||||||
|
|
||||||
|
private static final float FORWARD_OFFSET = 0.4F;
|
||||||
|
|
||||||
|
public ShortSilverfishRenderer( EntityRendererManager rendererManager ) {
|
||||||
|
super( rendererManager );
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Vector3d getRenderOffset( SilverfishEntity entity, float partialTicks ) {
|
||||||
|
final float angle = MathHelper.lerp( partialTicks, entity.yRotO, entity.yRot ) * (float) Math.PI / 180.0F;
|
||||||
|
final float forwardX = -MathHelper.sin( angle );
|
||||||
|
final float forwardZ = MathHelper.cos( angle );
|
||||||
|
return new Vector3d( forwardX * FORWARD_OFFSET, 0.0, forwardZ * FORWARD_OFFSET );
|
||||||
|
}
|
||||||
|
}
|
|
@ -86,7 +86,7 @@ public class MobFamily<T extends LivingEntity, V extends FamilyConfig> {
|
||||||
public static final MobFamily<SilverfishEntity, SilverfishFamilyConfig> SILVERFISH = new MobFamily<>( SilverfishFamilyConfig::new,
|
public static final MobFamily<SilverfishEntity, SilverfishFamilyConfig> SILVERFISH = new MobFamily<>( SilverfishFamilyConfig::new,
|
||||||
"Silverfish", "silverfish", 0x6E6E6E, new EntityType[] { EntityType.SILVERFISH },
|
"Silverfish", "silverfish", 0x6E6E6E, new EntityType[] { EntityType.SILVERFISH },
|
||||||
"Albino", "Blinding", "Desiccated", "Fire", "Fishing", "Flying", "Poison", "Puffer", "Tough"
|
"Albino", "Blinding", "Desiccated", "Fire", "Fishing", "Flying", "Poison", "Puffer", "Tough"
|
||||||
);//TODO puffer
|
);
|
||||||
|
|
||||||
public static final MobFamily<EndermanEntity, FamilyConfig> ENDERMAN = new MobFamily<>( FamilyConfig::new,
|
public static final MobFamily<EndermanEntity, FamilyConfig> ENDERMAN = new MobFamily<>( FamilyConfig::new,
|
||||||
"Enderman", "endermen", 0x161616, new EntityType[] { EntityType.ENDERMAN },
|
"Enderman", "endermen", 0x161616, new EntityType[] { EntityType.ENDERMAN },
|
||||||
|
|
|
@ -82,7 +82,6 @@ public class SpecialMobs {
|
||||||
* + natural spawning
|
* + natural spawning
|
||||||
* - silverfish
|
* - silverfish
|
||||||
* - ranged attack AI (spitter)
|
* - ranged attack AI (spitter)
|
||||||
* + puffer
|
|
||||||
* - endermen
|
* - endermen
|
||||||
* - witches
|
* - witches
|
||||||
* - ability to equip held items (wonky)
|
* - ability to equip held items (wonky)
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
package fathertoast.specialmobs.common.entity.ai;
|
||||||
|
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.MobEntity;
|
||||||
|
import net.minecraft.entity.ai.attributes.Attributes;
|
||||||
|
import net.minecraft.entity.ai.controller.MovementController;
|
||||||
|
import net.minecraft.util.math.MathHelper;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The drowned movement controller repurposed for use on other mobs.
|
||||||
|
* <p>
|
||||||
|
* {@link net.minecraft.entity.monster.DrownedEntity.MoveHelperController}
|
||||||
|
*/
|
||||||
|
public class AmphibiousMovementController<T extends MobEntity & IAmphibiousMob> extends MovementController {
|
||||||
|
|
||||||
|
private final T owner;
|
||||||
|
|
||||||
|
public AmphibiousMovementController( T entity ) {
|
||||||
|
super( entity );
|
||||||
|
owner = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
public void tick() {
|
||||||
|
final LivingEntity target = owner.getTarget();
|
||||||
|
if( owner.shouldSwim() && owner.isInWater() ) {
|
||||||
|
if( target != null && target.getY() > owner.getY() || owner.isSwimmingUp() ) {
|
||||||
|
owner.setDeltaMovement( owner.getDeltaMovement().add( 0.0, 0.002, 0.0 ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
if( operation != MovementController.Action.MOVE_TO || owner.getNavigation().isDone() ) {
|
||||||
|
owner.setSpeed( 0.0F );
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final double dX = wantedX - owner.getX();
|
||||||
|
final double dY = wantedY - owner.getY();
|
||||||
|
final double dZ = wantedZ - owner.getZ();
|
||||||
|
final double distance = MathHelper.sqrt( dX * dX + dY * dY + dZ * dZ );
|
||||||
|
|
||||||
|
final float targetYRot = (float) MathHelper.atan2( dZ, dX ) * 180.0F / (float) Math.PI - 90.0F;
|
||||||
|
owner.yRot = rotlerp( owner.yRot, targetYRot, 90.0F );
|
||||||
|
owner.yBodyRot = owner.yRot;
|
||||||
|
|
||||||
|
final float maxSpeed = (float) (speedModifier * owner.getAttributeValue( Attributes.MOVEMENT_SPEED ));
|
||||||
|
final float speed = MathHelper.lerp( 0.125F, owner.getSpeed(), maxSpeed );
|
||||||
|
owner.setSpeed( speed );
|
||||||
|
owner.setDeltaMovement( owner.getDeltaMovement().add(
|
||||||
|
speed * dX * 0.005, speed * dY / distance * 0.1, speed * dZ * 0.005 ) );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if( !owner.isOnGround() ) {
|
||||||
|
owner.setDeltaMovement( owner.getDeltaMovement().add( 0.0, -0.008, 0.0 ) );
|
||||||
|
}
|
||||||
|
super.tick();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,21 @@
|
||||||
|
package fathertoast.specialmobs.common.entity.ai;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Monsters must implement this interface to use the amphibious AI.
|
||||||
|
*/
|
||||||
|
public interface IAmphibiousMob {
|
||||||
|
/** @return True if this mob should use its swimming navigator for its current goal. */
|
||||||
|
default boolean shouldSwim() { return isSwimmingUp(); }
|
||||||
|
|
||||||
|
/** Sets whether this mob should swim upward. */
|
||||||
|
void setSwimmingUp( boolean value );
|
||||||
|
|
||||||
|
/** @return True if this mob should swim upward. */
|
||||||
|
boolean isSwimmingUp();
|
||||||
|
|
||||||
|
/** Sets this mob's current navigator to swimming mode. */
|
||||||
|
void setNavigatorToSwim();
|
||||||
|
|
||||||
|
/** Sets this mob's current navigator to ground mode. */
|
||||||
|
void setNavigatorToGround();
|
||||||
|
}
|
|
@ -0,0 +1,51 @@
|
||||||
|
package fathertoast.specialmobs.common.entity.ai.goal;
|
||||||
|
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.IAmphibiousMob;
|
||||||
|
import net.minecraft.entity.CreatureEntity;
|
||||||
|
import net.minecraft.entity.ai.goal.MoveToBlockGoal;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.world.IWorldReader;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The drowned "go to beach" goal repurposed for use on other mobs.
|
||||||
|
* <p>
|
||||||
|
* {@link net.minecraft.entity.monster.DrownedEntity.GoToBeachGoal}
|
||||||
|
*/
|
||||||
|
public class AmphibiousGoToShoreGoal<T extends CreatureEntity & IAmphibiousMob> extends MoveToBlockGoal {
|
||||||
|
|
||||||
|
private final T amphibiousMob;
|
||||||
|
|
||||||
|
private boolean disableAtDay = true;
|
||||||
|
|
||||||
|
public AmphibiousGoToShoreGoal( T entity, double speed ) {
|
||||||
|
super( entity, speed, 8, 2 );
|
||||||
|
amphibiousMob = entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Builder that allows this goal to run during the day. */
|
||||||
|
public AmphibiousGoToShoreGoal<T> alwaysEnabled() {
|
||||||
|
disableAtDay = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return Returns true if this AI can be activated. */
|
||||||
|
@Override
|
||||||
|
public boolean canUse() {
|
||||||
|
return super.canUse() && !(disableAtDay && mob.level.isDay()) && mob.isInWater() && mob.getY() >= mob.level.getSeaLevel() - 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return True if the position is valid to move to. */
|
||||||
|
@Override
|
||||||
|
protected boolean isValidTarget( IWorldReader world, BlockPos targetPos ) {
|
||||||
|
return world.isEmptyBlock( targetPos.above() ) && world.isEmptyBlock( targetPos.above( 2 ) ) &&
|
||||||
|
world.getBlockState( targetPos ).entityCanStandOn( world, targetPos, mob );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Called when this AI is activated. */
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
amphibiousMob.setSwimmingUp( false );
|
||||||
|
amphibiousMob.setNavigatorToGround();
|
||||||
|
super.start();
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,81 @@
|
||||||
|
package fathertoast.specialmobs.common.entity.ai.goal;
|
||||||
|
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.IAmphibiousMob;
|
||||||
|
import net.minecraft.block.Blocks;
|
||||||
|
import net.minecraft.entity.MobEntity;
|
||||||
|
import net.minecraft.entity.ai.goal.Goal;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.vector.Vector3d;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
import java.util.EnumSet;
|
||||||
|
import java.util.Random;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The drowned "go to water" goal repurposed for use on other mobs.
|
||||||
|
* <p>
|
||||||
|
* {@link net.minecraft.entity.monster.DrownedEntity.GoToWaterGoal}
|
||||||
|
*/
|
||||||
|
public class AmphibiousGoToWaterGoal<T extends MobEntity & IAmphibiousMob> extends Goal {
|
||||||
|
|
||||||
|
private final T mob;
|
||||||
|
private final double speedModifier;
|
||||||
|
|
||||||
|
private boolean disableAtNight = true;
|
||||||
|
|
||||||
|
private double wantedX;
|
||||||
|
private double wantedY;
|
||||||
|
private double wantedZ;
|
||||||
|
|
||||||
|
public AmphibiousGoToWaterGoal( T entity, double speed ) {
|
||||||
|
mob = entity;
|
||||||
|
speedModifier = speed;
|
||||||
|
setFlags( EnumSet.of( Goal.Flag.MOVE ) );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Builder that allows this goal to run during the night. */
|
||||||
|
public AmphibiousGoToWaterGoal<T> alwaysEnabled() {
|
||||||
|
disableAtNight = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return Returns true if this AI can be activated. */
|
||||||
|
@Override
|
||||||
|
public boolean canUse() {
|
||||||
|
if( disableAtNight && !mob.level.isDay() || mob.isInWater() ) return false;
|
||||||
|
|
||||||
|
final Vector3d targetPos = findWaterPos();
|
||||||
|
if( targetPos == null ) return false;
|
||||||
|
|
||||||
|
wantedX = targetPos.x;
|
||||||
|
wantedY = targetPos.y;
|
||||||
|
wantedZ = targetPos.z;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return Called each update while active and returns true if this AI can remain active. */
|
||||||
|
@Override
|
||||||
|
public boolean canContinueToUse() { return !mob.getNavigation().isDone(); }
|
||||||
|
|
||||||
|
/** Called when this AI is activated. */
|
||||||
|
@Override
|
||||||
|
public void start() { mob.getNavigation().moveTo( wantedX, wantedY, wantedZ, speedModifier ); }
|
||||||
|
|
||||||
|
/** @return A random nearby position of water, or null if none is found after a few tries. */
|
||||||
|
@Nullable
|
||||||
|
private Vector3d findWaterPos() {
|
||||||
|
final Random random = mob.getRandom();
|
||||||
|
final BlockPos origin = mob.blockPosition();
|
||||||
|
|
||||||
|
for( int i = 0; i < 10; i++ ) {
|
||||||
|
final BlockPos target = origin.offset(
|
||||||
|
random.nextInt( 20 ) - 10,
|
||||||
|
2 - random.nextInt( 8 ),
|
||||||
|
random.nextInt( 20 ) - 10 );
|
||||||
|
if( mob.level.getBlockState( target ).is( Blocks.WATER ) ) {
|
||||||
|
return Vector3d.atBottomCenterOf( target );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
}
|
|
@ -0,0 +1,47 @@
|
||||||
|
package fathertoast.specialmobs.common.entity.ai.goal;
|
||||||
|
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.IAmphibiousMob;
|
||||||
|
import net.minecraft.entity.CreatureEntity;
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.ai.goal.MeleeAttackGoal;
|
||||||
|
|
||||||
|
import javax.annotation.Nullable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The drowned "attack" goal repurposed for use on other mobs.
|
||||||
|
* Prevents mobs from attacking targets outside of water during the day.
|
||||||
|
* <p>
|
||||||
|
* {@link net.minecraft.entity.monster.DrownedEntity.AttackGoal}
|
||||||
|
*/
|
||||||
|
public class AmphibiousMeleeAttackGoal<T extends CreatureEntity & IAmphibiousMob> extends MeleeAttackGoal {
|
||||||
|
|
||||||
|
/** @return True if the target is valid. */
|
||||||
|
public static boolean isValidTarget( @Nullable LivingEntity target ) {
|
||||||
|
return target != null && (!target.level.isDay() || target.isInWater());
|
||||||
|
}
|
||||||
|
|
||||||
|
public AmphibiousMeleeAttackGoal( T entity, double speed, boolean longMemory ) { super( entity, speed, longMemory ); }
|
||||||
|
|
||||||
|
/** @return Returns true if this AI can be activated. */
|
||||||
|
@Override
|
||||||
|
public boolean canUse() { return super.canUse() && isValidTarget( mob.getTarget() ); }
|
||||||
|
|
||||||
|
/** @return Called each update while active and returns true if this AI can remain active. */
|
||||||
|
@Override
|
||||||
|
public boolean canContinueToUse() { return super.canContinueToUse() && isValidTarget( mob.getTarget() ); }
|
||||||
|
|
||||||
|
// Uncomment if ever needed
|
||||||
|
// /** A zombie attack version of the normal amphibious melee goal. */
|
||||||
|
// public static class Zombie<T extends ZombieEntity & IAmphibiousMob> extends ZombieAttackGoal {
|
||||||
|
//
|
||||||
|
// public Zombie( T entity, double speed, boolean longMemory ) { super( entity, speed, longMemory ); }
|
||||||
|
//
|
||||||
|
// /** @return Returns true if this AI can be activated. */
|
||||||
|
// @Override
|
||||||
|
// public boolean canUse() { return super.canUse() && isValidTarget( mob.getTarget() ); }
|
||||||
|
//
|
||||||
|
// /** @return Called each update while active and returns true if this AI can remain active. */
|
||||||
|
// @Override
|
||||||
|
// public boolean canContinueToUse() { return super.canContinueToUse() && isValidTarget( mob.getTarget() ); }
|
||||||
|
// }
|
||||||
|
}
|
|
@ -0,0 +1,82 @@
|
||||||
|
package fathertoast.specialmobs.common.entity.ai.goal;
|
||||||
|
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.IAmphibiousMob;
|
||||||
|
import net.minecraft.entity.CreatureEntity;
|
||||||
|
import net.minecraft.entity.ai.RandomPositionGenerator;
|
||||||
|
import net.minecraft.entity.ai.goal.Goal;
|
||||||
|
import net.minecraft.pathfinding.Path;
|
||||||
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.vector.Vector3d;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The drowned "swim up" goal repurposed for use on other mobs.
|
||||||
|
* <p>
|
||||||
|
* {@link net.minecraft.entity.monster.DrownedEntity.SwimUpGoal}
|
||||||
|
*/
|
||||||
|
public class AmphibiousSwimUpGoal<T extends CreatureEntity & IAmphibiousMob> extends Goal {
|
||||||
|
|
||||||
|
private final T mob;
|
||||||
|
private final double speedModifier;
|
||||||
|
private final int seaLevel;
|
||||||
|
|
||||||
|
private boolean disableAtDay = true;
|
||||||
|
|
||||||
|
private boolean stuck;
|
||||||
|
|
||||||
|
public AmphibiousSwimUpGoal( T entity, double speed ) {
|
||||||
|
mob = entity;
|
||||||
|
speedModifier = speed;
|
||||||
|
seaLevel = entity.level.getSeaLevel() - 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Builder that allows this goal to run during the day. */
|
||||||
|
public AmphibiousSwimUpGoal<T> alwaysEnabled() {
|
||||||
|
disableAtDay = false;
|
||||||
|
return this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return Returns true if this AI can be activated. */
|
||||||
|
@Override
|
||||||
|
public boolean canUse() { return !(disableAtDay && mob.level.isDay()) && mob.isInWater() && mob.getY() < seaLevel - 1; }
|
||||||
|
|
||||||
|
/** @return Called each update while active and returns true if this AI can remain active. */
|
||||||
|
@Override
|
||||||
|
public boolean canContinueToUse() { return canUse() && !stuck; }
|
||||||
|
|
||||||
|
/** Called when this AI is activated. */
|
||||||
|
@Override
|
||||||
|
public void start() {
|
||||||
|
mob.setSwimmingUp( true );
|
||||||
|
stuck = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Called when this AI is deactivated. */
|
||||||
|
@Override
|
||||||
|
public void stop() { mob.setSwimmingUp( false ); }
|
||||||
|
|
||||||
|
/** Called each tick while this AI is active. */
|
||||||
|
@Override
|
||||||
|
public void tick() {
|
||||||
|
if( mob.getY() < seaLevel && (mob.getNavigation().isDone() || closeToNextPos()) ) {
|
||||||
|
final Vector3d pos = RandomPositionGenerator.getPosTowards( mob, 4, 8,
|
||||||
|
new Vector3d( mob.getX(), seaLevel, mob.getZ() ) );
|
||||||
|
if( pos == null ) {
|
||||||
|
stuck = true;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
mob.getNavigation().moveTo( pos.x, pos.y, pos.z, speedModifier );
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return True if the entity is within 2 blocks of its pathing target. */
|
||||||
|
private boolean closeToNextPos() {
|
||||||
|
final Path path = mob.getNavigation().getPath();
|
||||||
|
if( path != null ) {
|
||||||
|
final BlockPos pos = path.getTarget();
|
||||||
|
return mob.distanceToSqr( pos.getX() + 0.5, pos.getY(), pos.getZ() + 0.5 ) < 4.0;
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
|
@ -3,6 +3,11 @@ package fathertoast.specialmobs.common.entity.creeper;
|
||||||
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.ai.AIHelper;
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.AmphibiousMovementController;
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.IAmphibiousMob;
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.goal.AmphibiousGoToWaterGoal;
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.goal.AmphibiousSwimUpGoal;
|
||||||
import fathertoast.specialmobs.common.util.ExplosionHelper;
|
import fathertoast.specialmobs.common.util.ExplosionHelper;
|
||||||
import fathertoast.specialmobs.common.util.References;
|
import fathertoast.specialmobs.common.util.References;
|
||||||
import fathertoast.specialmobs.datagen.loot.LootEntryItemBuilder;
|
import fathertoast.specialmobs.datagen.loot.LootEntryItemBuilder;
|
||||||
|
@ -11,18 +16,26 @@ import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
|
||||||
import net.minecraft.block.BlockState;
|
import net.minecraft.block.BlockState;
|
||||||
import net.minecraft.block.Blocks;
|
import net.minecraft.block.Blocks;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.MoverType;
|
||||||
import net.minecraft.entity.ai.attributes.Attributes;
|
import net.minecraft.entity.ai.attributes.Attributes;
|
||||||
|
import net.minecraft.entity.ai.goal.SwimGoal;
|
||||||
import net.minecraft.entity.passive.fish.PufferfishEntity;
|
import net.minecraft.entity.passive.fish.PufferfishEntity;
|
||||||
import net.minecraft.item.Items;
|
import net.minecraft.item.Items;
|
||||||
|
import net.minecraft.nbt.CompoundNBT;
|
||||||
|
import net.minecraft.pathfinding.GroundPathNavigator;
|
||||||
|
import net.minecraft.pathfinding.PathNodeType;
|
||||||
|
import net.minecraft.pathfinding.SwimmerPathNavigator;
|
||||||
import net.minecraft.state.properties.BlockStateProperties;
|
import net.minecraft.state.properties.BlockStateProperties;
|
||||||
import net.minecraft.tags.BlockTags;
|
import net.minecraft.tags.BlockTags;
|
||||||
import net.minecraft.util.math.BlockPos;
|
import net.minecraft.util.math.BlockPos;
|
||||||
|
import net.minecraft.util.math.vector.Vector3d;
|
||||||
import net.minecraft.world.Explosion;
|
import net.minecraft.world.Explosion;
|
||||||
import net.minecraft.world.IServerWorld;
|
import net.minecraft.world.IServerWorld;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
@SpecialMob
|
@SpecialMob
|
||||||
public class DrowningCreeperEntity extends _SpecialCreeperEntity {
|
public class DrowningCreeperEntity extends _SpecialCreeperEntity implements IAmphibiousMob {
|
||||||
|
|
||||||
//--------------- Static Special Mob Hooks ----------------
|
//--------------- Static Special Mob Hooks ----------------
|
||||||
|
|
||||||
|
@ -66,7 +79,23 @@ public class DrowningCreeperEntity extends _SpecialCreeperEntity {
|
||||||
|
|
||||||
//--------------- Variant-Specific Implementations ----------------
|
//--------------- Variant-Specific Implementations ----------------
|
||||||
|
|
||||||
public DrowningCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) { super( entityType, world ); }
|
public DrowningCreeperEntity( EntityType<? extends _SpecialCreeperEntity> entityType, World world ) {
|
||||||
|
super( entityType, world );
|
||||||
|
moveControl = new AmphibiousMovementController<>( this );
|
||||||
|
waterNavigation = new SwimmerPathNavigator( this, world );
|
||||||
|
groundNavigation = new GroundPathNavigator( this, world );
|
||||||
|
maxUpStep = 1.0F;
|
||||||
|
setPathfindingMalus( PathNodeType.WATER, PathNodeType.WALKABLE.getMalus() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Override to change this entity's AI goals. */
|
||||||
|
@Override
|
||||||
|
protected void registerVariantGoals() {
|
||||||
|
AIHelper.removeGoals( goalSelector, SwimGoal.class );
|
||||||
|
AIHelper.insertGoal( goalSelector, 5, new AmphibiousGoToWaterGoal<>( this, 1.0 ).alwaysEnabled() );
|
||||||
|
AIHelper.insertGoal( goalSelector, 6, new AmphibiousSwimUpGoal<>( this, 1.0 ) );
|
||||||
|
AIHelper.replaceWaterAvoidingRandomWalking( this, 0.8 );
|
||||||
|
}
|
||||||
|
|
||||||
/** Override to change this creeper's explosion power multiplier. */
|
/** Override to change this creeper's explosion power multiplier. */
|
||||||
@Override
|
@Override
|
||||||
|
@ -92,7 +121,7 @@ public class DrowningCreeperEntity extends _SpecialCreeperEntity {
|
||||||
final int rMinusOneSq = (radius - 1) * (radius - 1);
|
final int rMinusOneSq = (radius - 1) * (radius - 1);
|
||||||
final BlockPos center = new BlockPos( explosion.getPos() );
|
final BlockPos center = new BlockPos( explosion.getPos() );
|
||||||
|
|
||||||
// Track how many pufferfish have been spawned
|
// Track how many pufferfish have been spawned so we don't spawn a bunch of them
|
||||||
spawnPufferfish( center.above( 1 ) );
|
spawnPufferfish( center.above( 1 ) );
|
||||||
int pufferCount = 1;
|
int pufferCount = 1;
|
||||||
|
|
||||||
|
@ -123,8 +152,7 @@ public class DrowningCreeperEntity extends _SpecialCreeperEntity {
|
||||||
// Water fill
|
// Water fill
|
||||||
level.setBlock( pos, water, References.SetBlockFlags.DEFAULTS );
|
level.setBlock( pos, water, References.SetBlockFlags.DEFAULTS );
|
||||||
|
|
||||||
// Prevent greater radiuses from spawning a bazillion pufferfish
|
if( random.nextFloat() < 0.0075F && pufferCount < 5 ) {
|
||||||
if( random.nextFloat() < 0.01F && pufferCount < 10 ) {
|
|
||||||
spawnPufferfish( pos );
|
spawnPufferfish( pos );
|
||||||
pufferCount++;
|
pufferCount++;
|
||||||
}
|
}
|
||||||
|
@ -153,4 +181,72 @@ public class DrowningCreeperEntity extends _SpecialCreeperEntity {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isInWaterRainOrBubble() { return true; }
|
public boolean isInWaterRainOrBubble() { return true; }
|
||||||
|
|
||||||
|
/** Override to load data from this entity's NBT data. */
|
||||||
|
@Override
|
||||||
|
public void readVariantSaveData( CompoundNBT saveTag ) {
|
||||||
|
setPathfindingMalus( PathNodeType.WATER, PathNodeType.WALKABLE.getMalus() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--------------- IAmphibiousMob Implementation ----------------
|
||||||
|
|
||||||
|
private final SwimmerPathNavigator waterNavigation;
|
||||||
|
private final GroundPathNavigator groundNavigation;
|
||||||
|
|
||||||
|
private boolean swimmingUp;
|
||||||
|
|
||||||
|
/** Called each tick to update this entity's swimming state. */
|
||||||
|
@Override
|
||||||
|
public void updateSwimming() {
|
||||||
|
if( !level.isClientSide ) {
|
||||||
|
if( isEffectiveAi() && isUnderWater() && shouldSwim() ) {
|
||||||
|
setNavigatorToSwim();
|
||||||
|
setSwimming( true );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setNavigatorToGround();
|
||||||
|
setSwimming( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Moves this entity in the desired direction. Input magnitude of < 1 scales down movement speed. */
|
||||||
|
@Override
|
||||||
|
public void travel( Vector3d input ) {
|
||||||
|
if( isEffectiveAi() && isUnderWater() && shouldSwim() ) {
|
||||||
|
moveRelative( 0.01F, input );
|
||||||
|
move( MoverType.SELF, getDeltaMovement() );
|
||||||
|
setDeltaMovement( getDeltaMovement().scale( 0.9 ) );
|
||||||
|
}
|
||||||
|
else super.travel( input );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return Water drag coefficient. */
|
||||||
|
@Override
|
||||||
|
protected float getWaterSlowDown() { return 0.9F; }
|
||||||
|
|
||||||
|
/** @return True if this mob should use its swimming navigator for its current goal. */
|
||||||
|
@Override
|
||||||
|
public boolean shouldSwim() {
|
||||||
|
if( swimmingUp ) return true;
|
||||||
|
final LivingEntity target = getTarget();
|
||||||
|
return target != null && target.isInWater();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets whether this mob should swim upward. */
|
||||||
|
@Override
|
||||||
|
public void setSwimmingUp( boolean value ) { swimmingUp = value; }
|
||||||
|
|
||||||
|
/** @return True if this mob should swim upward. */
|
||||||
|
@Override
|
||||||
|
public boolean isSwimmingUp() { return swimmingUp; }
|
||||||
|
|
||||||
|
/** Sets this mob's current navigator to swimming mode. */
|
||||||
|
@Override
|
||||||
|
public void setNavigatorToSwim() { navigation = waterNavigation; }
|
||||||
|
|
||||||
|
/** Sets this mob's current navigator to ground mode. */
|
||||||
|
@Override
|
||||||
|
public void setNavigatorToGround() { navigation = groundNavigation; }
|
||||||
}
|
}
|
|
@ -131,6 +131,8 @@ public class _SpecialDrownedEntity extends DrownedEntity implements ISpecialMob<
|
||||||
/** The parameter for special mob render scale. */
|
/** The parameter for special mob render scale. */
|
||||||
private static final DataParameter<Float> SCALE = EntityDataManager.defineId( _SpecialDrownedEntity.class, DataSerializers.FLOAT );
|
private static final DataParameter<Float> SCALE = EntityDataManager.defineId( _SpecialDrownedEntity.class, DataSerializers.FLOAT );
|
||||||
|
|
||||||
|
private boolean needsToBeDeeper;
|
||||||
|
|
||||||
public _SpecialDrownedEntity( EntityType<? extends _SpecialDrownedEntity> entityType, World world ) {
|
public _SpecialDrownedEntity( EntityType<? extends _SpecialDrownedEntity> entityType, World world ) {
|
||||||
super( entityType, world );
|
super( entityType, world );
|
||||||
recalculateAttackGoal();
|
recalculateAttackGoal();
|
||||||
|
@ -172,6 +174,35 @@ public class _SpecialDrownedEntity extends DrownedEntity implements ISpecialMob<
|
||||||
level.addFreshEntity( trident );
|
level.addFreshEntity( trident );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Called each tick to update this entity's swimming state. */
|
||||||
|
@Override
|
||||||
|
public void updateSwimming() {
|
||||||
|
if( !level.isClientSide && isEffectiveAi() ) needsToBeDeeper = true;
|
||||||
|
super.updateSwimming();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Moves this entity in the desired direction. Input magnitude of < 1 scales down movement speed. */
|
||||||
|
@Override
|
||||||
|
public void travel( Vector3d input ) {
|
||||||
|
if( isEffectiveAi() ) needsToBeDeeper = true;
|
||||||
|
super.travel( input );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return Water drag coefficient. */
|
||||||
|
@Override
|
||||||
|
protected float getWaterSlowDown() { return 0.9F; } // Improve mobility in shallow water
|
||||||
|
|
||||||
|
/** @return True if this entity is in water. */
|
||||||
|
@Override
|
||||||
|
public boolean isInWater() {
|
||||||
|
// Hacky way to fix vanilla drowned AI breaking in shallow water
|
||||||
|
if( needsToBeDeeper ) {
|
||||||
|
needsToBeDeeper = false;
|
||||||
|
return isUnderWater();
|
||||||
|
}
|
||||||
|
return super.isInWater();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//--------------- ISpecialMob Implementation ----------------
|
//--------------- ISpecialMob Implementation ----------------
|
||||||
|
|
||||||
|
|
|
@ -0,0 +1,97 @@
|
||||||
|
package fathertoast.specialmobs.common.entity.silverfish;
|
||||||
|
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.AmphibiousMovementController;
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.IAmphibiousMob;
|
||||||
|
import net.minecraft.entity.EntityType;
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.MoverType;
|
||||||
|
import net.minecraft.nbt.CompoundNBT;
|
||||||
|
import net.minecraft.pathfinding.GroundPathNavigator;
|
||||||
|
import net.minecraft.pathfinding.PathNodeType;
|
||||||
|
import net.minecraft.pathfinding.SwimmerPathNavigator;
|
||||||
|
import net.minecraft.util.math.vector.Vector3d;
|
||||||
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* A bare-bones implementation of an amphibious silverfish. Just fix the AI and it's good to go.
|
||||||
|
*/
|
||||||
|
public abstract class AmphibiousSilverfishEntity extends _SpecialSilverfishEntity implements IAmphibiousMob {
|
||||||
|
|
||||||
|
private final SwimmerPathNavigator waterNavigation;
|
||||||
|
private final GroundPathNavigator groundNavigation;
|
||||||
|
|
||||||
|
private boolean swimmingUp;
|
||||||
|
|
||||||
|
public AmphibiousSilverfishEntity( EntityType<? extends _SpecialSilverfishEntity> entityType, World world ) {
|
||||||
|
super( entityType, world );
|
||||||
|
moveControl = new AmphibiousMovementController<>( this );
|
||||||
|
waterNavigation = new SwimmerPathNavigator( this, world );
|
||||||
|
groundNavigation = new GroundPathNavigator( this, world );
|
||||||
|
maxUpStep = 1.0F;
|
||||||
|
setPathfindingMalus( PathNodeType.WATER, PathNodeType.WALKABLE.getMalus() );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Loads data from this entity's base NBT compound that is specific to its subclass. */
|
||||||
|
@Override
|
||||||
|
public void readAdditionalSaveData( CompoundNBT tag ) {
|
||||||
|
super.readAdditionalSaveData( tag );
|
||||||
|
setPathfindingMalus( PathNodeType.WATER, PathNodeType.WALKABLE.getMalus() );
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//--------------- IAmphibiousMob Implementation ----------------
|
||||||
|
|
||||||
|
/** Called each tick to update this entity's swimming state. */
|
||||||
|
@Override
|
||||||
|
public void updateSwimming() {
|
||||||
|
if( !level.isClientSide ) {
|
||||||
|
if( isEffectiveAi() && isUnderWater() && shouldSwim() ) {
|
||||||
|
setNavigatorToSwim();
|
||||||
|
setSwimming( true );
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
setNavigatorToGround();
|
||||||
|
setSwimming( false );
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Moves this entity in the desired direction. Input magnitude of < 1 scales down movement speed. */
|
||||||
|
@Override
|
||||||
|
public void travel( Vector3d input ) {
|
||||||
|
if( isEffectiveAi() && isUnderWater() && shouldSwim() ) {
|
||||||
|
moveRelative( 0.01F, input );
|
||||||
|
move( MoverType.SELF, getDeltaMovement() );
|
||||||
|
setDeltaMovement( getDeltaMovement().scale( 0.9 ) );
|
||||||
|
}
|
||||||
|
else super.travel( input );
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @return Water drag coefficient. */
|
||||||
|
@Override
|
||||||
|
protected float getWaterSlowDown() { return 0.9F; }
|
||||||
|
|
||||||
|
/** @return True if this mob should use its swimming navigator for its current goal. */
|
||||||
|
@Override
|
||||||
|
public boolean shouldSwim() {
|
||||||
|
if( swimmingUp ) return true;
|
||||||
|
final LivingEntity target = getTarget();
|
||||||
|
return target != null && target.isInWater();
|
||||||
|
}
|
||||||
|
|
||||||
|
/** Sets whether this mob should swim upward. */
|
||||||
|
@Override
|
||||||
|
public void setSwimmingUp( boolean value ) { swimmingUp = value; }
|
||||||
|
|
||||||
|
/** @return True if this mob should swim upward. */
|
||||||
|
@Override
|
||||||
|
public boolean isSwimmingUp() { return swimmingUp; }
|
||||||
|
|
||||||
|
/** Sets this mob's current navigator to swimming mode. */
|
||||||
|
@Override
|
||||||
|
public void setNavigatorToSwim() { navigation = waterNavigation; }
|
||||||
|
|
||||||
|
/** Sets this mob's current navigator to ground mode. */
|
||||||
|
@Override
|
||||||
|
public void setNavigatorToGround() { navigation = groundNavigation; }
|
||||||
|
}
|
|
@ -7,6 +7,8 @@ import fathertoast.specialmobs.common.config.species.SilverfishSpeciesConfig;
|
||||||
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
|
import fathertoast.specialmobs.common.config.species.SpeciesConfig;
|
||||||
import fathertoast.specialmobs.common.entity.ai.AIHelper;
|
import fathertoast.specialmobs.common.entity.ai.AIHelper;
|
||||||
import fathertoast.specialmobs.common.entity.ai.IAngler;
|
import fathertoast.specialmobs.common.entity.ai.IAngler;
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.goal.AmphibiousGoToShoreGoal;
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.goal.AmphibiousSwimUpGoal;
|
||||||
import fathertoast.specialmobs.common.entity.ai.goal.AnglerGoal;
|
import fathertoast.specialmobs.common.entity.ai.goal.AnglerGoal;
|
||||||
import fathertoast.specialmobs.common.entity.ai.goal.PassiveRangedAttackGoal;
|
import fathertoast.specialmobs.common.entity.ai.goal.PassiveRangedAttackGoal;
|
||||||
import fathertoast.specialmobs.common.util.References;
|
import fathertoast.specialmobs.common.util.References;
|
||||||
|
@ -15,11 +17,12 @@ import fathertoast.specialmobs.datagen.loot.LootPoolBuilder;
|
||||||
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
|
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
import net.minecraft.entity.ai.attributes.Attributes;
|
import net.minecraft.entity.ai.attributes.Attributes;
|
||||||
|
import net.minecraft.entity.ai.goal.SwimGoal;
|
||||||
import net.minecraft.item.Items;
|
import net.minecraft.item.Items;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
@SpecialMob
|
@SpecialMob
|
||||||
public class FishingSilverfishEntity extends _SpecialSilverfishEntity implements IAngler {
|
public class FishingSilverfishEntity extends AmphibiousSilverfishEntity implements IAngler {
|
||||||
|
|
||||||
//--------------- Static Special Mob Hooks ----------------
|
//--------------- Static Special Mob Hooks ----------------
|
||||||
|
|
||||||
|
@ -77,6 +80,10 @@ public class FishingSilverfishEntity extends _SpecialSilverfishEntity implements
|
||||||
protected void registerVariantGoals() {
|
protected void registerVariantGoals() {
|
||||||
AIHelper.removeGoals( goalSelector, PassiveRangedAttackGoal.class ); // Disable spit attack use
|
AIHelper.removeGoals( goalSelector, PassiveRangedAttackGoal.class ); // Disable spit attack use
|
||||||
goalSelector.addGoal( 4, new AnglerGoal<>( this ) );
|
goalSelector.addGoal( 4, new AnglerGoal<>( this ) );
|
||||||
|
|
||||||
|
AIHelper.removeGoals( goalSelector, SwimGoal.class );
|
||||||
|
AIHelper.insertGoal( goalSelector, 5, new AmphibiousGoToShoreGoal<>( this, 1.0 ).alwaysEnabled() );
|
||||||
|
AIHelper.insertGoal( goalSelector, 6, new AmphibiousSwimUpGoal<>( this, 1.0 ).alwaysEnabled() );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -4,16 +4,19 @@ 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.MobHelper;
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.AIHelper;
|
||||||
|
import fathertoast.specialmobs.common.entity.ai.goal.AmphibiousGoToWaterGoal;
|
||||||
import fathertoast.specialmobs.common.util.References;
|
import fathertoast.specialmobs.common.util.References;
|
||||||
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
|
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
import net.minecraft.entity.LivingEntity;
|
import net.minecraft.entity.LivingEntity;
|
||||||
|
import net.minecraft.entity.ai.goal.SwimGoal;
|
||||||
import net.minecraft.item.Items;
|
import net.minecraft.item.Items;
|
||||||
import net.minecraft.potion.Effects;
|
import net.minecraft.potion.Effects;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
@SpecialMob
|
@SpecialMob
|
||||||
public class PufferSilverfishEntity extends _SpecialSilverfishEntity {
|
public class PufferSilverfishEntity extends AmphibiousSilverfishEntity {
|
||||||
|
|
||||||
//--------------- Static Special Mob Hooks ----------------
|
//--------------- Static Special Mob Hooks ----------------
|
||||||
|
|
||||||
|
@ -23,7 +26,7 @@ public class PufferSilverfishEntity extends _SpecialSilverfishEntity {
|
||||||
@SpecialMob.BestiaryInfoSupplier
|
@SpecialMob.BestiaryInfoSupplier
|
||||||
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
|
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
|
||||||
bestiaryInfo.color( 0xE6E861 ).theme( BestiaryInfo.Theme.WATER )
|
bestiaryInfo.color( 0xE6E861 ).theme( BestiaryInfo.Theme.WATER )
|
||||||
.uniqueTextureBaseOnly()//TODO Change texture or renderer to fix offset
|
.uniqueTextureBaseOnly()
|
||||||
.addExperience( 1 ).drownImmune().effectImmune( Effects.POISON );
|
.addExperience( 1 ).drownImmune().effectImmune( Effects.POISON );
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -52,7 +55,12 @@ public class PufferSilverfishEntity extends _SpecialSilverfishEntity {
|
||||||
|
|
||||||
public PufferSilverfishEntity( EntityType<? extends _SpecialSilverfishEntity> entityType, World world ) { super( entityType, world ); }
|
public PufferSilverfishEntity( EntityType<? extends _SpecialSilverfishEntity> entityType, World world ) { super( entityType, world ); }
|
||||||
|
|
||||||
//TODO swim behavior
|
/** Override to change this entity's AI goals. */
|
||||||
|
@Override
|
||||||
|
protected void registerVariantGoals() {
|
||||||
|
AIHelper.removeGoals( goalSelector, SwimGoal.class );
|
||||||
|
AIHelper.insertGoal( goalSelector, 5, new AmphibiousGoToWaterGoal<>( this, 1.0 ).alwaysEnabled() );
|
||||||
|
}
|
||||||
|
|
||||||
/** Override to change the color of this entity's spit attack. */
|
/** Override to change the color of this entity's spit attack. */
|
||||||
@Override
|
@Override
|
||||||
|
|
|
@ -89,6 +89,8 @@ public class _SpecialSilverfishEntity extends SilverfishEntity implements IRange
|
||||||
super.registerGoals();
|
super.registerGoals();
|
||||||
goalSelector.addGoal( 4, new PassiveRangedAttackGoal<>( this ) );
|
goalSelector.addGoal( 4, new PassiveRangedAttackGoal<>( this ) );
|
||||||
AIHelper.replaceHurtByTarget( this, new SpecialHurtByTargetGoal( this, SilverfishEntity.class ).setAlertOthers() );
|
AIHelper.replaceHurtByTarget( this, new SpecialHurtByTargetGoal( this, SilverfishEntity.class ).setAlertOthers() );
|
||||||
|
// Someday, it would be nice to replace SilverfishEntity.HideInStoneGoal with one that
|
||||||
|
// expands the allowed stone types and preserves species on hide/reveal
|
||||||
|
|
||||||
registerVariantGoals();
|
registerVariantGoals();
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,21 +3,20 @@ package fathertoast.specialmobs.common.entity.slime;
|
||||||
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.AIHelper;
|
import fathertoast.specialmobs.common.entity.ai.AIHelper;
|
||||||
import fathertoast.specialmobs.common.entity.ai.FluidPathNavigator;
|
|
||||||
import fathertoast.specialmobs.common.util.References;
|
import fathertoast.specialmobs.common.util.References;
|
||||||
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
|
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
|
||||||
|
import net.minecraft.block.FlowingFluidBlock;
|
||||||
import net.minecraft.entity.EntityType;
|
import net.minecraft.entity.EntityType;
|
||||||
|
import net.minecraft.entity.LivingEntity;
|
||||||
import net.minecraft.entity.ai.attributes.Attributes;
|
import net.minecraft.entity.ai.attributes.Attributes;
|
||||||
import net.minecraft.fluid.Fluid;
|
|
||||||
import net.minecraft.item.Items;
|
import net.minecraft.item.Items;
|
||||||
import net.minecraft.nbt.CompoundNBT;
|
import net.minecraft.nbt.CompoundNBT;
|
||||||
import net.minecraft.particles.IParticleData;
|
import net.minecraft.particles.IParticleData;
|
||||||
import net.minecraft.particles.ParticleTypes;
|
import net.minecraft.particles.ParticleTypes;
|
||||||
import net.minecraft.pathfinding.PathNavigator;
|
|
||||||
import net.minecraft.pathfinding.PathNodeType;
|
import net.minecraft.pathfinding.PathNodeType;
|
||||||
import net.minecraft.tags.FluidTags;
|
import net.minecraft.tags.FluidTags;
|
||||||
|
import net.minecraft.util.math.shapes.ISelectionContext;
|
||||||
import net.minecraft.world.World;
|
import net.minecraft.world.World;
|
||||||
|
|
||||||
@SpecialMob
|
@SpecialMob
|
||||||
|
@ -71,21 +70,28 @@ public class BlueberrySlimeEntity extends _SpecialSlimeEntity {
|
||||||
AIHelper.removeGoals( goalSelector, 1 ); // SlimeEntity.FloatGoal
|
AIHelper.removeGoals( goalSelector, 1 ); // SlimeEntity.FloatGoal
|
||||||
}
|
}
|
||||||
|
|
||||||
/** @return A new path navigator for this entity to use. */
|
|
||||||
@Override
|
|
||||||
protected PathNavigator createNavigation( World world ) {
|
|
||||||
return new FluidPathNavigator( this, world, true, false );
|
|
||||||
}
|
|
||||||
|
|
||||||
/** @return Whether this entity can stand on a particular type of fluid. */
|
|
||||||
@Override
|
|
||||||
public boolean canStandOnFluid( Fluid fluid ) { return fluid.is( FluidTags.WATER ); }
|
|
||||||
|
|
||||||
/** Called each tick to update this entity. */
|
/** Called each tick to update this entity. */
|
||||||
@Override
|
@Override
|
||||||
public void tick() {
|
public void tick() {
|
||||||
super.tick();
|
super.tick();
|
||||||
MobHelper.floatInFluid( this, 0.06, FluidTags.WATER );
|
|
||||||
|
// Hacky way of attacking submerged targets; drop down on them when directly above
|
||||||
|
double floatAccel = 0.06;
|
||||||
|
final LivingEntity target = getTarget();
|
||||||
|
if( target != null && target.getY( 0.5 ) < getY( 0.5 ) ) {
|
||||||
|
//
|
||||||
|
final double dX = target.getX() - getX();
|
||||||
|
final double dZ = target.getZ() - getZ();
|
||||||
|
final float range = (target.getBbWidth() + getBbWidth() + 0.1F) / 2.0F;
|
||||||
|
if( dX * dX + dZ * dZ < range * range )
|
||||||
|
floatAccel = -0.12;
|
||||||
|
}
|
||||||
|
if( tickCount > 1 && getFluidHeight( FluidTags.WATER ) > 0.0 ) {
|
||||||
|
if( !ISelectionContext.of( this ).isAbove( FlowingFluidBlock.STABLE_SHAPE, blockPosition(), true ) ||
|
||||||
|
level.getFluidState( blockPosition().above() ).is( FluidTags.WATER ) ) {
|
||||||
|
setDeltaMovement( getDeltaMovement().scale( 0.5 ).add( 0.0, floatAccel, 0.0 ) );
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// The below two methods are here to effectively override the private Entity#isInRain to always return true (always wet)
|
// The below two methods are here to effectively override the private Entity#isInRain to always return true (always wet)
|
||||||
|
@ -95,6 +101,10 @@ public class BlueberrySlimeEntity extends _SpecialSlimeEntity {
|
||||||
@Override
|
@Override
|
||||||
public boolean isInWaterRainOrBubble() { return true; }
|
public boolean isInWaterRainOrBubble() { return true; }
|
||||||
|
|
||||||
|
/** @return Water drag coefficient. */
|
||||||
|
@Override
|
||||||
|
protected float getWaterSlowDown() { return 0.9F; }
|
||||||
|
|
||||||
/** Override to load data from this entity's NBT data. */
|
/** Override to load data from this entity's NBT data. */
|
||||||
@Override
|
@Override
|
||||||
public void readVariantSaveData( CompoundNBT saveTag ) {
|
public void readVariantSaveData( CompoundNBT saveTag ) {
|
||||||
|
|
Loading…
Add table
Reference in a new issue