mirror of
https://github.com/FatherToast/SpecialMobs.git
synced 2025-04-25 06:45:11 +00:00
beamaroni
This commit is contained in:
parent
a4652d2e39
commit
5bae76072f
10 changed files with 323 additions and 293 deletions
|
@ -4,6 +4,7 @@ import fathertoast.specialmobs.client.renderer.entity.family.SpecialCreeperRende
|
|||
import fathertoast.specialmobs.common.entity.creeper.EnderCreeperEntity;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererManager;
|
||||
import net.minecraft.entity.monster.CreeperEntity;
|
||||
import net.minecraft.entity.monster.EndermanEntity;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
@ -18,6 +19,7 @@ public class EnderCreeperRenderer extends SpecialCreeperRenderer {
|
|||
|
||||
public EnderCreeperRenderer( EntityRendererManager rendererManager ) { super( rendererManager ); }
|
||||
|
||||
/** {@link net.minecraft.client.renderer.entity.EndermanRenderer#getRenderOffset(EndermanEntity, float)} */
|
||||
@Override
|
||||
public Vector3d getRenderOffset( CreeperEntity entity, float partialTicks ) {
|
||||
if( ((EnderCreeperEntity) entity).isCreepy() ) {
|
||||
|
@ -26,5 +28,4 @@ public class EnderCreeperRenderer extends SpecialCreeperRenderer {
|
|||
}
|
||||
return super.getRenderOffset( entity, partialTicks );
|
||||
}
|
||||
|
||||
}
|
|
@ -1,118 +0,0 @@
|
|||
package fathertoast.specialmobs.client.renderer.entity.species;
|
||||
|
||||
import fathertoast.specialmobs.common.entity.enderman.RunicEndermanEntity;
|
||||
import net.minecraft.client.renderer.entity.model.BipedModel;
|
||||
import net.minecraft.client.renderer.model.ModelRenderer;
|
||||
|
||||
/** Epic copy-paste of {@link net.minecraft.client.renderer.entity.model.EndermanModel} */
|
||||
public class RunicEndermanModel<T extends RunicEndermanEntity> extends BipedModel<T> {
|
||||
|
||||
public boolean carrying;
|
||||
public boolean creepy;
|
||||
|
||||
public RunicEndermanModel(float something) {
|
||||
super(0.0F, -14.0F, 64, 32);
|
||||
float f = -14.0F;
|
||||
this.hat = new ModelRenderer(this, 0, 16);
|
||||
this.hat.addBox(-4.0F, -8.0F, -4.0F, 8.0F, 8.0F, 8.0F, something - 0.5F);
|
||||
this.hat.setPos(0.0F, -14.0F, 0.0F);
|
||||
this.body = new ModelRenderer(this, 32, 16);
|
||||
this.body.addBox(-4.0F, 0.0F, -2.0F, 8.0F, 12.0F, 4.0F, something);
|
||||
this.body.setPos(0.0F, -14.0F, 0.0F);
|
||||
this.rightArm = new ModelRenderer(this, 56, 0);
|
||||
this.rightArm.addBox(-1.0F, -2.0F, -1.0F, 2.0F, 30.0F, 2.0F, something);
|
||||
this.rightArm.setPos(-3.0F, -12.0F, 0.0F);
|
||||
this.leftArm = new ModelRenderer(this, 56, 0);
|
||||
this.leftArm.mirror = true;
|
||||
this.leftArm.addBox(-1.0F, -2.0F, -1.0F, 2.0F, 30.0F, 2.0F, something);
|
||||
this.leftArm.setPos(5.0F, -12.0F, 0.0F);
|
||||
this.rightLeg = new ModelRenderer(this, 56, 0);
|
||||
this.rightLeg.addBox(-1.0F, 0.0F, -1.0F, 2.0F, 30.0F, 2.0F, something);
|
||||
this.rightLeg.setPos(-2.0F, -2.0F, 0.0F);
|
||||
this.leftLeg = new ModelRenderer(this, 56, 0);
|
||||
this.leftLeg.mirror = true;
|
||||
this.leftLeg.addBox(-1.0F, 0.0F, -1.0F, 2.0F, 30.0F, 2.0F, something);
|
||||
this.leftLeg.setPos(2.0F, -2.0F, 0.0F);
|
||||
}
|
||||
@Override
|
||||
public void setupAnim(T enderman, float limbSwing, float limbSwingAmount, float ageInTicks, float netHeadYaw, float headPitch) {
|
||||
super.setupAnim(enderman, limbSwing, limbSwingAmount, ageInTicks, netHeadYaw, headPitch);
|
||||
|
||||
this.head.visible = true;
|
||||
this.body.xRot = 0.0F;
|
||||
this.body.y = -14.0F;
|
||||
this.body.z = -0.0F;
|
||||
this.rightLeg.xRot -= 0.0F;
|
||||
this.leftLeg.xRot -= 0.0F;
|
||||
this.rightArm.xRot = (float)((double)this.rightArm.xRot * 0.5D);
|
||||
this.leftArm.xRot = (float)((double)this.leftArm.xRot * 0.5D);
|
||||
this.rightLeg.xRot = (float)((double)this.rightLeg.xRot * 0.5D);
|
||||
this.leftLeg.xRot = (float)((double)this.leftLeg.xRot * 0.5D);
|
||||
|
||||
if (this.rightArm.xRot > 0.4F) {
|
||||
this.rightArm.xRot = 0.4F;
|
||||
}
|
||||
|
||||
if (this.leftArm.xRot > 0.4F) {
|
||||
this.leftArm.xRot = 0.4F;
|
||||
}
|
||||
|
||||
if (this.rightArm.xRot < -0.4F) {
|
||||
this.rightArm.xRot = -0.4F;
|
||||
}
|
||||
|
||||
if (this.leftArm.xRot < -0.4F) {
|
||||
this.leftArm.xRot = -0.4F;
|
||||
}
|
||||
|
||||
if (this.rightLeg.xRot > 0.4F) {
|
||||
this.rightLeg.xRot = 0.4F;
|
||||
}
|
||||
|
||||
if (this.leftLeg.xRot > 0.4F) {
|
||||
this.leftLeg.xRot = 0.4F;
|
||||
}
|
||||
|
||||
if (this.rightLeg.xRot < -0.4F) {
|
||||
this.rightLeg.xRot = -0.4F;
|
||||
}
|
||||
|
||||
if (this.leftLeg.xRot < -0.4F) {
|
||||
this.leftLeg.xRot = -0.4F;
|
||||
}
|
||||
|
||||
if (this.carrying) {
|
||||
this.rightArm.xRot = -0.5F;
|
||||
this.leftArm.xRot = -0.5F;
|
||||
this.rightArm.zRot = 0.05F;
|
||||
this.leftArm.zRot = -0.05F;
|
||||
}
|
||||
this.rightArm.z = 0.0F;
|
||||
this.leftArm.z = 0.0F;
|
||||
this.rightLeg.z = 0.0F;
|
||||
this.leftLeg.z = 0.0F;
|
||||
this.rightLeg.y = -5.0F;
|
||||
this.leftLeg.y = -5.0F;
|
||||
this.head.z = -0.0F;
|
||||
this.head.y = -13.0F;
|
||||
this.hat.x = this.head.x;
|
||||
this.hat.y = this.head.y;
|
||||
this.hat.z = this.head.z;
|
||||
this.hat.xRot = this.head.xRot;
|
||||
this.hat.yRot = this.head.yRot;
|
||||
this.hat.zRot = this.head.zRot;
|
||||
|
||||
if (this.creepy) {
|
||||
this.head.y -= 5.0F;
|
||||
}
|
||||
this.rightArm.setPos(-5.0F, -12.0F, 0.0F);
|
||||
this.leftArm.setPos(5.0F, -12.0F, 0.0F);
|
||||
|
||||
if (enderman.getBeamTargetId().isPresent()) {
|
||||
leftArm.xRot = -0.5F;
|
||||
rightArm.xRot = -0.5F;
|
||||
leftArm.zRot = -0.25F;
|
||||
rightArm.zRot = 0.25F;
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,107 +2,146 @@ package fathertoast.specialmobs.client.renderer.entity.species;
|
|||
|
||||
import com.mojang.blaze3d.matrix.MatrixStack;
|
||||
import com.mojang.blaze3d.vertex.IVertexBuilder;
|
||||
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.client.renderer.entity.family.SpecialEndermanRenderer;
|
||||
import fathertoast.specialmobs.common.entity.enderman.RunicEndermanEntity;
|
||||
import net.minecraft.client.renderer.IRenderTypeBuffer;
|
||||
import net.minecraft.client.renderer.RenderType;
|
||||
import net.minecraft.client.renderer.entity.EnderDragonRenderer;
|
||||
import net.minecraft.client.renderer.entity.EntityRendererManager;
|
||||
import net.minecraft.client.renderer.entity.MobRenderer;
|
||||
import net.minecraft.client.renderer.entity.model.EndermanModel;
|
||||
import net.minecraft.client.renderer.texture.OverlayTexture;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.monster.EndermanEntity;
|
||||
import net.minecraft.util.ResourceLocation;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.RayTraceContext;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.vector.Matrix3f;
|
||||
import net.minecraft.util.math.vector.Matrix4f;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
import net.minecraft.util.math.vector.Vector3f;
|
||||
|
||||
public class RunicEndermanRenderer extends MobRenderer<RunicEndermanEntity, RunicEndermanModel<RunicEndermanEntity>> {
|
||||
public class RunicEndermanRenderer extends SpecialEndermanRenderer {
|
||||
public static final ResourceLocation BEAM_TEXTURE_LOCATION = new ResourceLocation( "textures/entity/end_crystal/end_crystal_beam.png" );
|
||||
private static final RenderType BEAM = RenderType.entitySmoothCutout( BEAM_TEXTURE_LOCATION );
|
||||
|
||||
public static final ResourceLocation CRYSTAL_BEAM_LOCATION = new ResourceLocation("textures/entity/end_crystal/end_crystal_beam.png");
|
||||
private static final RenderType BEAM = RenderType.entitySmoothCutout(CRYSTAL_BEAM_LOCATION);
|
||||
|
||||
private final float baseShadowRadius;
|
||||
|
||||
|
||||
public RunicEndermanRenderer(EntityRendererManager rendererManager) {
|
||||
super(rendererManager, new RunicEndermanModel<>(0.0F), 0.5F);
|
||||
|
||||
baseShadowRadius = shadowRadius;
|
||||
addLayer( new SpecialMobEyesLayer<>( this ) );
|
||||
addLayer( new SpecialMobOverlayLayer<>( this, new RunicEndermanModel<>( 0.25F ) ) );
|
||||
}
|
||||
public RunicEndermanRenderer( EntityRendererManager rendererManager ) { super( rendererManager ); }
|
||||
|
||||
@Override
|
||||
public void render(RunicEndermanEntity enderman, float rotation, float partialTicks, MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight) {
|
||||
if (enderman.getBeamTargetId().isPresent()) {
|
||||
Entity entity = enderman.level.getEntity(enderman.getBeamTargetId().getAsInt());
|
||||
public void render( EndermanEntity entity, float rotation, float partialTicks,
|
||||
MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight ) {
|
||||
// Beam attack
|
||||
final RunicEndermanEntity.BeamState beamState = ((RunicEndermanEntity) entity).getBeamState();
|
||||
if( beamState != RunicEndermanEntity.BeamState.OFF ) {
|
||||
matrixStack.pushPose();
|
||||
|
||||
if (entity != null) {
|
||||
matrixStack.pushPose();
|
||||
float x = (float)(entity.getX() - MathHelper.lerp(partialTicks, enderman.xo, enderman.getX()));
|
||||
float y = (float)(entity.getY() - MathHelper.lerp(partialTicks, enderman.yo, enderman.getY()));
|
||||
float z = (float)(entity.getZ() - MathHelper.lerp(partialTicks, enderman.zo, enderman.getZ()));
|
||||
renderBeamAttack(x, y, z, partialTicks, enderman.tickCount, matrixStack, buffer, packedLight);
|
||||
matrixStack.popPose();
|
||||
Vector3d beamVec = entity.getViewVector( partialTicks ).scale( RunicEndermanEntity.BEAM_MAX_RANGE );
|
||||
|
||||
final Vector3d beamStartPos = entity.getEyePosition( partialTicks );
|
||||
final Vector3d beamEndPos = beamStartPos.add( beamVec );
|
||||
|
||||
final RayTraceResult blockRayTrace = entity.level.clip(
|
||||
new RayTraceContext( beamStartPos, beamEndPos,
|
||||
RayTraceContext.BlockMode.COLLIDER, RayTraceContext.FluidMode.NONE, entity ) );
|
||||
if( blockRayTrace.getType() != RayTraceResult.Type.MISS ) {
|
||||
beamVec = blockRayTrace.getLocation().subtract( beamStartPos );
|
||||
}
|
||||
|
||||
renderBeamAttack( beamState, entity.getEyeHeight(), (float) beamVec.x, (float) beamVec.y, (float) beamVec.z,
|
||||
partialTicks, entity.tickCount, matrixStack, buffer, packedLight );
|
||||
|
||||
matrixStack.popPose();
|
||||
}
|
||||
|
||||
super.render(enderman, rotation, partialTicks, matrixStack, buffer, packedLight);
|
||||
super.render( entity, rotation, partialTicks, matrixStack, buffer, packedLight );
|
||||
}
|
||||
|
||||
@Override
|
||||
public ResourceLocation getTextureLocation(RunicEndermanEntity enderman) {
|
||||
return ((ISpecialMob<?>) enderman).getSpecialData().getTexture();
|
||||
protected boolean isBodyVisible( EndermanEntity entity ) {
|
||||
// This is called right after EntityModel#setupAnim, so we add on our own animation here as a hacky way to avoid a lot of extra stuff
|
||||
final RunicEndermanEntity.BeamState beamState = ((RunicEndermanEntity) entity).getBeamState();
|
||||
if( beamState != RunicEndermanEntity.BeamState.OFF ) {
|
||||
final float zRot = beamState == RunicEndermanEntity.BeamState.DAMAGING ? 0.25F : 0.6F;
|
||||
final EndermanModel<EndermanEntity> model = getModel();
|
||||
model.leftArm.xRot = -0.6F;
|
||||
model.rightArm.xRot = -0.6F;
|
||||
model.leftArm.zRot = -zRot;
|
||||
model.rightArm.zRot = zRot;
|
||||
}
|
||||
|
||||
return super.isBodyVisible( entity );
|
||||
}
|
||||
|
||||
@Override
|
||||
protected void scale(RunicEndermanEntity 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 );
|
||||
}
|
||||
|
||||
/** Copy-paste Ender Crystal render code. {@link EnderDragonRenderer#renderCrystalBeams(float, float, float, float, int, MatrixStack, IRenderTypeBuffer, int)} */
|
||||
private void renderBeamAttack(float x, float y, float z, float partialTicks, int tickCount, MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight) {
|
||||
float f = MathHelper.sqrt(x * x + z * z);
|
||||
float xyzMul = x * x + y * y + z * z;
|
||||
float f1 = MathHelper.sqrt(xyzMul);
|
||||
|
||||
/**
|
||||
* Copy-paste Ender Crystal render code.
|
||||
* {@link EnderDragonRenderer#renderCrystalBeams(float, float, float, float, int, MatrixStack, IRenderTypeBuffer, int)}
|
||||
*/
|
||||
private void renderBeamAttack( RunicEndermanEntity.BeamState beamState, float offsetY, float dX, float dY, float dZ,
|
||||
float partialTicks, int tickCount, MatrixStack matrixStack, IRenderTypeBuffer buffer, int packedLight ) {
|
||||
matrixStack.pushPose();
|
||||
matrixStack.translate(0.0D, 2.0D, 0.0D);
|
||||
matrixStack.mulPose(Vector3f.YP.rotation((float)(-Math.atan2(z, x)) - ((float)Math.PI / 2F)));
|
||||
matrixStack.mulPose(Vector3f.XP.rotation((float)(-Math.atan2(f, y)) - ((float)Math.PI / 2F)));
|
||||
|
||||
IVertexBuilder ivertexbuilder = buffer.getBuffer(BEAM);
|
||||
final float dH = MathHelper.sqrt( dX * dX + dZ * dZ );
|
||||
final float length = MathHelper.sqrt( dX * dX + dY * dY + dZ * dZ );
|
||||
|
||||
float f2 = 0.0F - ((float)tickCount + partialTicks) * 0.01F;
|
||||
float f3 = MathHelper.sqrt(xyzMul) / 32.0F - ((float)tickCount + partialTicks) * 0.01F;
|
||||
float f4 = 0.0F;
|
||||
float f5 = 0.75F;
|
||||
float f6 = 0.0F;
|
||||
matrixStack.translate( 0.0, offsetY, 0.0 );
|
||||
matrixStack.mulPose( Vector3f.YP.rotation( (float) -Math.atan2( dZ, dX ) - (float) Math.PI / 2.0F ) );
|
||||
matrixStack.mulPose( Vector3f.XP.rotation( (float) -Math.atan2( dH, dY ) - (float) Math.PI / 2.0F ) );
|
||||
|
||||
MatrixStack.Entry entry = matrixStack.last();
|
||||
Matrix4f matrix4f = entry.pose();
|
||||
Matrix3f matrix3f = entry.normal();
|
||||
final IVertexBuilder vertexBuilder = buffer.getBuffer( BEAM );
|
||||
final MatrixStack.Entry matrixEntry = matrixStack.last();
|
||||
final Matrix4f pose = matrixEntry.pose();
|
||||
final Matrix3f normal = matrixEntry.normal();
|
||||
|
||||
for(int j = 1; j <= 8; ++j) {
|
||||
float f7 = MathHelper.sin((float)j * ((float)Math.PI * 2F) / 8.0F) * 0.75F;
|
||||
float f8 = MathHelper.cos((float)j * ((float)Math.PI * 2F) / 8.0F) * 0.75F;
|
||||
float f9 = (float)j / 8.0F;
|
||||
|
||||
ivertexbuilder.vertex(matrix4f, f4 * 0.2F, f5 * 0.2F, 0.0F).color(0, 0, 0, 255).uv(f6, f2).overlayCoords(OverlayTexture.NO_OVERLAY).uv2(packedLight).normal(matrix3f, 0.0F, -1.0F, 0.0F).endVertex();
|
||||
ivertexbuilder.vertex(matrix4f, f4, f5, f1).color(255, 100, 255, 255).uv(f6, f3).overlayCoords(OverlayTexture.NO_OVERLAY).uv2(packedLight).normal(matrix3f, 0.0F, -1.0F, 0.0F).endVertex();
|
||||
ivertexbuilder.vertex(matrix4f, f7, f8, f1).color(255, 100, 255, 255).uv(f9, f3).overlayCoords(OverlayTexture.NO_OVERLAY).uv2(packedLight).normal(matrix3f, 0.0F, -1.0F, 0.0F).endVertex();
|
||||
ivertexbuilder.vertex(matrix4f, f7 * 0.2F, f8 * 0.2F, 0.0F).color(0, 0, 0, 255).uv(f9, f2).overlayCoords(OverlayTexture.NO_OVERLAY).uv2(packedLight).normal(matrix3f, 0.0F, -1.0F, 0.0F).endVertex();
|
||||
f4 = f7;
|
||||
f5 = f8;
|
||||
f6 = f9;
|
||||
final int eRB;
|
||||
final int alpha;
|
||||
final float eScale;
|
||||
final float v1;
|
||||
if( beamState == RunicEndermanEntity.BeamState.DAMAGING ) {
|
||||
eRB = 255;
|
||||
alpha = 255;
|
||||
eScale = 1.0F;
|
||||
v1 = (tickCount + partialTicks) * -0.01F;
|
||||
}
|
||||
else {
|
||||
eRB = 100;
|
||||
alpha = 170;
|
||||
eScale = 0.2F;
|
||||
v1 = (tickCount + partialTicks) * 0.006F;
|
||||
}
|
||||
final float v2 = length / 32.0F + v1;
|
||||
|
||||
float u1 = 0.0F;
|
||||
float x1 = 0.0F;
|
||||
float y1 = 0.75F;
|
||||
|
||||
final int resolution = 8;
|
||||
for( int n = 1; n <= resolution; n++ ) {
|
||||
final float u2 = (float) n / resolution;
|
||||
final float angle = u2 * (float) Math.PI * 2.0F;
|
||||
final float x2 = MathHelper.sin( angle ) * 0.75F;
|
||||
final float y2 = MathHelper.cos( angle ) * 0.75F;
|
||||
|
||||
vertexBuilder.vertex( pose, x1 * 0.2F, y1 * 0.2F, 0.0F )
|
||||
.color( 0, 0, 0, alpha )
|
||||
.uv( u1, v1 ).overlayCoords( OverlayTexture.NO_OVERLAY ).uv2( packedLight )
|
||||
.normal( normal, 0.0F, -1.0F, 0.0F ).endVertex();
|
||||
vertexBuilder.vertex( pose, x1 * eScale, y1 * eScale, length )
|
||||
.color( eRB, 100, eRB, alpha )
|
||||
.uv( u1, v2 ).overlayCoords( OverlayTexture.NO_OVERLAY ).uv2( packedLight )
|
||||
.normal( normal, 0.0F, -1.0F, 0.0F ).endVertex();
|
||||
vertexBuilder.vertex( pose, x2 * eScale, y2 * eScale, length )
|
||||
.color( eRB, 100, eRB, alpha )
|
||||
.uv( u2, v2 ).overlayCoords( OverlayTexture.NO_OVERLAY ).uv2( packedLight )
|
||||
.normal( normal, 0.0F, -1.0F, 0.0F ).endVertex();
|
||||
vertexBuilder.vertex( pose, x2 * 0.2F, y2 * 0.2F, 0.0F )
|
||||
.color( 0, 0, 0, alpha )
|
||||
.uv( u2, v1 ).overlayCoords( OverlayTexture.NO_OVERLAY ).uv2( packedLight )
|
||||
.normal( normal, 0.0F, -1.0F, 0.0F ).endVertex();
|
||||
|
||||
x1 = x2;
|
||||
y1 = y2;
|
||||
u1 = u2;
|
||||
}
|
||||
|
||||
matrixStack.popPose();
|
||||
}
|
||||
}
|
|
@ -276,12 +276,8 @@ public class BestiaryInfo {
|
|||
immuneToStickyBlocks.addAll( parent.immuneToStickyBlocks.getEntries() );
|
||||
immuneToPotions.addAll( parent.immuneToPotions.getEntries() );
|
||||
|
||||
rangedAttackDamage = parent.rangedAttackDamage;
|
||||
rangedAttackSpread = parent.rangedAttackSpread;
|
||||
rangedWalkSpeed = parent.rangedWalkSpeed;
|
||||
rangedAttackCooldown = parent.rangedAttackCooldown;
|
||||
rangedAttackMaxCooldown = parent.rangedAttackMaxCooldown;
|
||||
rangedAttackMaxRange = parent.rangedAttackMaxRange;
|
||||
setAllRangedStats( parent.rangedAttackDamage, parent.rangedAttackSpread, parent.rangedWalkSpeed,
|
||||
parent.rangedAttackCooldown, parent.rangedAttackMaxCooldown, parent.rangedAttackMaxRange );
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -593,18 +589,14 @@ public class BestiaryInfo {
|
|||
.multiplyRangedCooldown( cooldown ).multiplyRangedMaxCooldown( cooldown ).multiplyRangedMaxRange( range );
|
||||
}
|
||||
|
||||
/** Converts the entity to a fishing rod user by disabling unused ranged attack stats (for a bow user). */
|
||||
public Builder convertBowToFishing() { return rangedDamage( -1.0 ).rangedWalkSpeed( -1.0 ); }
|
||||
/** Converts the entity's ranged attack stats to those of a fishing rod user. */
|
||||
public Builder convertRangedAttackToFishing( double spread, int cooldown, double range ) {
|
||||
return setAllRangedStats( -1.0, spread, -1.0, cooldown, -1, range );
|
||||
}
|
||||
|
||||
/** Converts the entity to a fishing rod user by disabling unused ranged attack stats (for a throwing item user). */
|
||||
public Builder convertThrowToFishing() { return rangedWalkSpeed( -1.0 ); }
|
||||
|
||||
/** Converts the entity to a fishing rod user by disabling unused ranged attack stats (for a spit shooter). */
|
||||
public Builder convertSpitToFishing() { return rangedDamage( -1.0 ).rangedMaxCooldown( -1 ); }
|
||||
|
||||
/** Sets the species fishing rod user stats. */
|
||||
public Builder fishingAttack( double spread, int cooldown, double range ) {
|
||||
return rangedSpread( spread ).rangedCooldown( cooldown ).rangedMaxRange( range );
|
||||
/** Converts the entity's ranged attack stats to those of a beam attack user. */
|
||||
public Builder convertRangedAttackToBeam( double damage, double turnSpeed, int charge, int duration, double range ) {
|
||||
return setAllRangedStats( damage, -1.0, turnSpeed, charge, charge + duration, range );
|
||||
}
|
||||
|
||||
/** Sets the species as unable to use ranged attacks (for any ranged user). */
|
||||
|
@ -679,6 +671,17 @@ public class BestiaryInfo {
|
|||
return this;
|
||||
}
|
||||
|
||||
/** Converts ALL the entity's ranged attack stats. This method allows enabling/disabling stats on variants. */
|
||||
private Builder setAllRangedStats( double damage, double spread, double walkSpeed, int cooldown, int maxCooldown, double range ) {
|
||||
rangedAttackDamage = damage;
|
||||
rangedAttackSpread = spread;
|
||||
rangedWalkSpeed = walkSpeed;
|
||||
rangedAttackCooldown = cooldown;
|
||||
rangedAttackMaxCooldown = maxCooldown;
|
||||
rangedAttackMaxRange = range;
|
||||
return this;
|
||||
}
|
||||
|
||||
|
||||
//--------------- Attribute Changes ----------------
|
||||
|
||||
|
|
|
@ -1,68 +1,159 @@
|
|||
package fathertoast.specialmobs.common.entity.ai.goal;
|
||||
|
||||
import fathertoast.specialmobs.common.entity.MobHelper;
|
||||
import fathertoast.specialmobs.common.entity.enderman.RunicEndermanEntity;
|
||||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.entity.LivingEntity;
|
||||
import net.minecraft.entity.ai.goal.Goal;
|
||||
import net.minecraft.util.DamageSource;
|
||||
import net.minecraft.util.EntityDamageSource;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.RayTraceContext;
|
||||
import net.minecraft.util.math.RayTraceResult;
|
||||
import net.minecraft.util.math.vector.Vector3d;
|
||||
|
||||
import javax.annotation.Nullable;
|
||||
import java.util.ArrayList;
|
||||
import java.util.EnumSet;
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
|
||||
public class RunicEndermanBeamAttackGoal extends Goal {
|
||||
private final RunicEndermanEntity mob;
|
||||
private final DamageSource beamDamageSource;
|
||||
|
||||
private final RunicEndermanEntity runicEnderman;
|
||||
private final int maxAttackTime;
|
||||
private final double baseDamage;
|
||||
|
||||
private Vector3d targetPos;
|
||||
private int attackTime;
|
||||
|
||||
public RunicEndermanBeamAttackGoal(RunicEndermanEntity runicEnderman, int maxAttackTime, double baseDamage ) {
|
||||
this.runicEnderman = runicEnderman;
|
||||
this.maxAttackTime = maxAttackTime;
|
||||
this.baseDamage = baseDamage;
|
||||
public RunicEndermanBeamAttackGoal( RunicEndermanEntity entity ) {
|
||||
mob = entity;
|
||||
beamDamageSource = new EntityDamageSource( DamageSource.MAGIC.getMsgId(), entity );
|
||||
setFlags( EnumSet.of( Goal.Flag.MOVE, Flag.LOOK ) );
|
||||
}
|
||||
|
||||
/** @return Returns true if this AI can be activated. */
|
||||
@Override
|
||||
public boolean canUse() {
|
||||
if (runicEnderman.isVehicle())
|
||||
return false;
|
||||
if( mob.isVehicle() ) return false;
|
||||
|
||||
LivingEntity target = runicEnderman.getTarget();
|
||||
|
||||
return target != null
|
||||
&& target.isAlive()
|
||||
&& runicEnderman.getSensing().canSee(target)
|
||||
&& (runicEnderman.tickCount % 8 == 0 && runicEnderman.getRandom().nextInt(10) == 0);
|
||||
final LivingEntity target = mob.getTarget();
|
||||
if( target != null && target.isAlive() && mob.getSensing().canSee( target ) &&
|
||||
mob.tickCount % 8 == 0 && mob.getRandom().nextInt( 10 ) == 0 && target.distanceToSqr( mob ) <=
|
||||
mob.getSpecialData().getRangedAttackMaxRange() * mob.getSpecialData().getRangedAttackMaxRange() ) {
|
||||
setTargetPos( target );
|
||||
attackTime = 0;
|
||||
mob.setBeamState( RunicEndermanEntity.BeamState.CHARGING );
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/** @return Called each update while active and returns true if this AI can remain active. */
|
||||
@Override
|
||||
public boolean canContinueToUse() {
|
||||
LivingEntity target = runicEnderman.getTarget();
|
||||
|
||||
return attackTime > 0 && target != null && target.isAlive() && runicEnderman.getSensing().canSee(target);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start() {
|
||||
runicEnderman.setBeamTargetId(runicEnderman.getTarget().getId());
|
||||
attackTime = maxAttackTime;
|
||||
final LivingEntity target = mob.getTarget();
|
||||
return (mob.getBeamState() == RunicEndermanEntity.BeamState.DAMAGING || target != null && target.isAlive()) &&
|
||||
attackTime < mob.getSpecialData().getRangedAttackMaxCooldown();
|
||||
}
|
||||
|
||||
/** Called when this AI is deactivated. */
|
||||
@Override
|
||||
public void stop() {
|
||||
runicEnderman.clearBeamTargetId();
|
||||
attackTime = 0;
|
||||
targetPos = null;
|
||||
mob.setBeamState( RunicEndermanEntity.BeamState.OFF );
|
||||
}
|
||||
|
||||
@SuppressWarnings("ConstantConditions")
|
||||
/** Called each tick while this AI is active. */
|
||||
@Override
|
||||
public void tick() {
|
||||
--attackTime;
|
||||
|
||||
if (attackTime <= 0) {
|
||||
LivingEntity target = runicEnderman.getTarget();
|
||||
double xRatio = MathHelper.sin(runicEnderman.yRot * ((float)Math.PI / 180F));
|
||||
double zRatio = -MathHelper.cos(runicEnderman.yRot * ((float)Math.PI / 180F));
|
||||
target.knockback(5, xRatio, zRatio);
|
||||
target.hurt(DamageSource.mobAttack(runicEnderman), (float) baseDamage);
|
||||
attackTime++;
|
||||
switch( mob.getBeamState() ) {
|
||||
case CHARGING:
|
||||
chargingTick();
|
||||
break;
|
||||
case DAMAGING:
|
||||
damagingTick();
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/** Called each tick while this AI is active and in the charging phase. */
|
||||
private void chargingTick() {
|
||||
mob.getLookControl().setLookAt( targetPos.x, targetPos.y, targetPos.z,
|
||||
30.0F, mob.getMaxHeadXRot() );
|
||||
|
||||
if( attackTime >= mob.getSpecialData().getRangedAttackCooldown() ) {
|
||||
mob.setBeamState( RunicEndermanEntity.BeamState.DAMAGING );
|
||||
}
|
||||
}
|
||||
|
||||
/** Called each tick while this AI is active and in the damaging phase. */
|
||||
private void damagingTick() {
|
||||
final LivingEntity target = mob.getTarget();
|
||||
if( target != null ) setTargetPos( target );
|
||||
|
||||
lookTowardTarget( 1.666F * mob.getSpecialData().getRangedWalkSpeed() ); // Use move speed as a turn rate modifier
|
||||
final Vector3d viewVec = mob.getViewVector( 1.0F ).scale( RunicEndermanEntity.BEAM_MAX_RANGE );
|
||||
|
||||
final Vector3d beamStartPos = mob.getEyePosition( 1.0F );
|
||||
Vector3d beamEndPos = beamStartPos.add( viewVec );
|
||||
|
||||
final RayTraceResult blockRayTrace = mob.level.clip(
|
||||
new RayTraceContext( beamStartPos, beamEndPos,
|
||||
RayTraceContext.BlockMode.COLLIDER, RayTraceContext.FluidMode.NONE, mob ) );
|
||||
if( blockRayTrace.getType() != RayTraceResult.Type.MISS ) {
|
||||
beamEndPos = blockRayTrace.getLocation();
|
||||
}
|
||||
|
||||
final List<Entity> hitEntities = rayTraceEntities( beamStartPos, beamEndPos,
|
||||
mob.getBoundingBox().expandTowards( viewVec ).inflate( 1.0 ) );
|
||||
for( Entity entity : hitEntities ) {
|
||||
if( entity.hurt( beamDamageSource, mob.getSpecialData().getRangedAttackDamage() ) &&
|
||||
entity instanceof LivingEntity ) {
|
||||
MobHelper.knockback( (LivingEntity) entity, 1.0F, 1.0F,
|
||||
-viewVec.x, -viewVec.z, 1.0 );
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/** Updates the target position based on an entity we want to hit. */
|
||||
private void setTargetPos( LivingEntity target ) {
|
||||
targetPos = new Vector3d( target.getX(), target.getY( 0.5 ), target.getZ() );
|
||||
}
|
||||
|
||||
/** Sets the target look position. Modifies the target position to limit vertical look speed. */
|
||||
private void lookTowardTarget( float speed ) {
|
||||
final double dX = targetPos.x - mob.getX();
|
||||
final double dY = targetPos.y - mob.getEyeY();
|
||||
final double dZ = targetPos.z - mob.getZ();
|
||||
final double dH = MathHelper.sqrt( dX * dX + dZ * dZ );
|
||||
final float targetXRot = (float) MathHelper.atan2( dY, dH );
|
||||
|
||||
final float clampedXRot = (mob.xRot + MathHelper.clamp( MathHelper.degreesDifference( mob.xRot,
|
||||
targetXRot * -57.2957763671875F ), -speed, speed )) / -57.2957763671875F;
|
||||
final double clampedDY = dH * MathHelper.sin( clampedXRot ) / MathHelper.cos( clampedXRot );
|
||||
targetPos = targetPos.add( 0.0, clampedDY - dY, 0.0 );
|
||||
|
||||
mob.getLookControl().setLookAt( targetPos.x, targetPos.y, targetPos.z,
|
||||
speed, mob.getMaxHeadXRot() );
|
||||
}
|
||||
|
||||
/** @return A list of all entities between the two vector positions and within the search area, in no particular order. */
|
||||
private List<Entity> rayTraceEntities( Vector3d from, Vector3d to, AxisAlignedBB searchArea ) {
|
||||
final List<Entity> entitiesHit = new ArrayList<>();
|
||||
for( Entity entity : mob.level.getEntities( mob, searchArea, this::canBeamHitTarget ) ) {
|
||||
final Optional<Vector3d> hitPos = entity.getBoundingBox().inflate( 0.3 ).clip( from, to );
|
||||
if( hitPos.isPresent() ) {
|
||||
entitiesHit.add( entity );
|
||||
}
|
||||
}
|
||||
return entitiesHit;
|
||||
}
|
||||
|
||||
/** @return True if the beam can hit the target. */
|
||||
private boolean canBeamHitTarget( @Nullable Entity entity ) {
|
||||
return entity != null && !entity.isSpectator() && entity.isAlive() && entity.isPickable() &&
|
||||
!entity.isPassengerOfSameVehicle( mob );
|
||||
}
|
||||
}
|
|
@ -42,7 +42,7 @@ public class FishingDrownedEntity extends _SpecialDrownedEntity implements IAngl
|
|||
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
|
||||
bestiaryInfo.color( 0x2D41F4 ).weight( BestiaryInfo.DefaultWeight.LOW )
|
||||
.addExperience( 2 ).drownImmune().fluidPushImmune()
|
||||
.convertThrowToFishing().fishingAttack( 1.0, 40, 15.0 )
|
||||
.convertRangedAttackToFishing( 1.0, 40, 15.0 )
|
||||
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 0.8 );
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ 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.AIHelper;
|
||||
import fathertoast.specialmobs.common.entity.ai.goal.RunicEndermanBeamAttackGoal;
|
||||
import fathertoast.specialmobs.common.util.References;
|
||||
import fathertoast.specialmobs.datagen.loot.LootTableBuilder;
|
||||
|
@ -16,9 +17,10 @@ import net.minecraft.network.datasync.DataParameter;
|
|||
import net.minecraft.network.datasync.DataSerializers;
|
||||
import net.minecraft.network.datasync.EntityDataManager;
|
||||
import net.minecraft.potion.Effects;
|
||||
import net.minecraft.util.math.AxisAlignedBB;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
import java.util.OptionalInt;
|
||||
import net.minecraftforge.api.distmarker.Dist;
|
||||
import net.minecraftforge.api.distmarker.OnlyIn;
|
||||
|
||||
@SpecialMob
|
||||
public class RunicEndermanEntity extends _SpecialEndermanEntity {
|
||||
|
@ -33,6 +35,7 @@ public class RunicEndermanEntity extends _SpecialEndermanEntity {
|
|||
bestiaryInfo.color( 0xE42281 ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.MOUNTAIN )
|
||||
.uniqueTextureWithEyes()
|
||||
.addExperience( 2 ).fallImmune().burnImmune()
|
||||
.convertRangedAttackToBeam( 2.0, 1.0, 60, 100, 16.0 )
|
||||
.addToAttribute( Attributes.ARMOR, 10.0 )
|
||||
.addToAttribute( Attributes.ATTACK_DAMAGE, 1.0 )
|
||||
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 0.8 );
|
||||
|
@ -63,28 +66,24 @@ public class RunicEndermanEntity extends _SpecialEndermanEntity {
|
|||
|
||||
//--------------- Variant-Specific Implementations ----------------
|
||||
|
||||
public static final DataParameter<Boolean> BEAMING = EntityDataManager.defineId(RunicEndermanEntity.class, DataSerializers.BOOLEAN);
|
||||
public static final DataParameter<OptionalInt> TARGET_ID = EntityDataManager.defineId(RunicEndermanEntity.class, DataSerializers.OPTIONAL_UNSIGNED_INT);
|
||||
/** The actual range of the beam when fired. Roughly based on end crystal max range. */
|
||||
public static final double BEAM_MAX_RANGE = 32.0;
|
||||
/** The parameter for beam attack state. */
|
||||
private static final DataParameter<Byte> BEAM_STATE = EntityDataManager.defineId( RunicEndermanEntity.class, DataSerializers.BYTE );
|
||||
|
||||
public RunicEndermanEntity( EntityType<? extends _SpecialEndermanEntity> entityType, World world ) { super( entityType, world ); }
|
||||
|
||||
/** Called from the Entity.class constructor to define data watcher variables. */
|
||||
@Override
|
||||
protected void defineSynchedData() {
|
||||
super.defineSynchedData();
|
||||
entityData.define( BEAMING, false );
|
||||
entityData.define( TARGET_ID, OptionalInt.empty() );
|
||||
entityData.define( BEAM_STATE, BeamState.OFF.id() );
|
||||
}
|
||||
|
||||
public OptionalInt getBeamTargetId() {
|
||||
return entityData.get( TARGET_ID );
|
||||
}
|
||||
|
||||
public void setBeamTargetId(int targetId) {
|
||||
entityData.set( TARGET_ID, OptionalInt.of(targetId) );
|
||||
}
|
||||
|
||||
public void clearBeamTargetId() {
|
||||
entityData.set( TARGET_ID, OptionalInt.empty() );
|
||||
/** Override to change this entity's AI goals. */
|
||||
@Override
|
||||
protected void registerVariantGoals() {
|
||||
AIHelper.insertGoal( goalSelector, 2, new RunicEndermanBeamAttackGoal( this ) );
|
||||
}
|
||||
|
||||
/** Override to apply effects when this entity hits a target with a melee attack. */
|
||||
|
@ -94,10 +93,25 @@ public class RunicEndermanEntity extends _SpecialEndermanEntity {
|
|||
MobHelper.knockback( this, target, 2.0F, 0.0F );
|
||||
}
|
||||
|
||||
/** Override to change this entity's AI goals. */
|
||||
/** @return True if this enderman is using its beam attack. */
|
||||
public BeamState getBeamState() { return BeamState.of( entityData.get( BEAM_STATE ) ); }
|
||||
|
||||
/** Sets whether this enderman is using its beam attack. */
|
||||
public void setBeamState( BeamState value ) { entityData.set( BEAM_STATE, value.id() ); }
|
||||
|
||||
/** @return The bounding box to use for frustum culling. */
|
||||
@OnlyIn( Dist.CLIENT )
|
||||
@Override
|
||||
protected void registerVariantGoals() {
|
||||
super.registerVariantGoals();
|
||||
goalSelector.addGoal( 2, new RunicEndermanBeamAttackGoal(this, 50, 2.0D));
|
||||
public AxisAlignedBB getBoundingBoxForCulling() {
|
||||
return getBeamState() == BeamState.OFF ? super.getBoundingBoxForCulling() :
|
||||
super.getBoundingBoxForCulling().expandTowards( getViewVector( 1.0F ).scale( BEAM_MAX_RANGE ) );
|
||||
}
|
||||
|
||||
public enum BeamState {
|
||||
OFF, CHARGING, DAMAGING;
|
||||
|
||||
public byte id() { return (byte) ordinal(); }
|
||||
|
||||
public static BeamState of( byte id ) { return values()[id]; }
|
||||
}
|
||||
}
|
|
@ -35,7 +35,7 @@ public class FishingSilverfishEntity extends AmphibiousSilverfishEntity implemen
|
|||
.uniqueTextureBaseOnly()
|
||||
.size( 1.2F, 0.5F, 0.4F )
|
||||
.addExperience( 2 ).drownImmune().fluidPushImmune()
|
||||
.convertSpitToFishing().fishingAttack( 1.0, 40, 10.0 )
|
||||
.convertRangedAttackToFishing( 1.0, 40, 10.0 )
|
||||
.addToAttribute( Attributes.MAX_HEALTH, 4.0 )
|
||||
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 0.8 );
|
||||
}
|
||||
|
|
|
@ -46,7 +46,7 @@ public class FishingZombieEntity extends _SpecialZombieEntity implements IAngler
|
|||
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
|
||||
bestiaryInfo.color( 0x2D41F4 ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.FISHING )
|
||||
.addExperience( 2 ).drownImmune().fluidPushImmune()
|
||||
.convertBowToFishing().fishingAttack( 1.0, 40, 15.0 )
|
||||
.convertRangedAttackToFishing( 1.0, 40, 15.0 )
|
||||
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 0.8 );
|
||||
}
|
||||
|
||||
|
|
|
@ -44,7 +44,7 @@ public class FishingZombifiedPiglinEntity extends _SpecialZombifiedPiglinEntity
|
|||
public static void getBestiaryInfo( BestiaryInfo.Builder bestiaryInfo ) {
|
||||
bestiaryInfo.color( 0x2D41F4 ).weight( BestiaryInfo.DefaultWeight.LOW ).theme( BestiaryInfo.Theme.FISHING )
|
||||
.addExperience( 2 ).drownImmune().fluidPushImmune()
|
||||
.convertBowToFishing().fishingAttack( 1.0, 40, 15.0 )
|
||||
.convertRangedAttackToFishing( 1.0, 40, 15.0 )
|
||||
.multiplyAttribute( Attributes.MOVEMENT_SPEED, 0.8 );
|
||||
}
|
||||
|
||||
|
|
Loading…
Add table
Reference in a new issue