package com.thebrokenrail.energonrelics.block.entity; import com.thebrokenrail.energonrelics.Config; import com.thebrokenrail.energonrelics.EnergonRelics; import com.thebrokenrail.energonrelics.block.DefensiveLaserBlock; import com.thebrokenrail.energonrelics.energy.core.EnergyReceiverBlockEntity; import com.thebrokenrail.energonrelics.energy.core.util.Action; import com.thebrokenrail.energonrelics.util.MissingCaseException; import net.minecraft.block.BlockState; import net.minecraft.block.entity.BlockEntityType; import net.minecraft.entity.LivingEntity; import net.minecraft.nbt.CompoundTag; import net.minecraft.particle.ParticleTypes; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.hit.HitResult; import net.minecraft.util.math.Box; import net.minecraft.util.math.Direction; import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.Vec3d; import net.minecraft.world.RayTraceContext; import java.util.Collections; import java.util.List; import java.util.Optional; import java.util.function.Predicate; public class DefensiveLaserBlockEntity extends EnergyReceiverBlockEntity { private static final float MAX_PITCH = 0; private static final float MIN_PITCH = -70; private static final float ROTATION_INCREMENT = 2; private static final int COUNTDOWN = 6; public final Rotation rotation = new Rotation(0, 0, "Rotation"); private LivingEntity target; private int countdown = 0; private final Predicate predicate = entity -> { if (entity.getBlockPos().isWithinDistance(getPos(), Config.DEFENSIVE_LASER_RANGE)) { Vec3d start = getPosVec().add(rotation.getRayVector()); Vec3d end = entity.getPos(); HitResult result = entity.getEntityWorld().rayTrace(new RayTraceContext(start, end, RayTraceContext.ShapeType.COLLIDER, RayTraceContext.FluidHandling.ANY, entity)); return result.getType() == HitResult.Type.MISS; } else { return false; } }; private Vec3d getPosVec() { return new Vec3d(getPos().getX() + 0.5d, getPos().getY() + 0.5d, getPos().getZ() + 0.5d); } @Override public BlockState getCachedState() { if (hasWorld()) { return super.getCachedState(); } else { return EnergonRelics.DEFENSIVE_LASER_BLOCK.getDefaultState(); } } public class Rotation { private float yaw = 0; private float pitch = 0; private final String key; private Rotation(float yaw, float pitch, String key) { setRaw(yaw, pitch); this.key = key; } private void fromTag(CompoundTag tag) { CompoundTag rotation = tag.getCompound(key); setRaw(rotation.getInt("Yaw"), rotation.getInt("Pitch")); } private void toTag(CompoundTag tag) { CompoundTag rotation = new CompoundTag(); rotation.putFloat("Yaw", yaw); rotation.putFloat("Pitch", pitch); tag.put(key, rotation); } private void setRaw(float yaw, float pitch) { setYaw(yaw); setPitch(pitch); } private void setYaw(float yaw) { this.yaw = yaw % 360; } private void setPitch(float pitch) { this.pitch = pitch % 360; } public float getYaw() { return yaw; } public float getPitch() { return pitch; } private void change(float diffYaw, float diffPitch) { setRaw(yaw + diffYaw, pitch + diffPitch); markDirty(); } private Vec3d getRayVector() { float pitch = this.pitch * (float) DEG2RAD; float yaw = this.yaw * (float) DEG2RAD; float cosYaw = MathHelper.cos(yaw); float sinYaw = MathHelper.sin(yaw); float cosPitch = MathHelper.cos(pitch); float sinPitch = MathHelper.sin(pitch); return new Vec3d(sinYaw * cosPitch, -sinPitch, cosYaw * cosPitch); } private float getAngleChange(float from, float to) { float diff = MathHelper.subtractAngles(from, to); return MathHelper.clamp(diff, -ROTATION_INCREMENT, ROTATION_INCREMENT); } private void target(float targetYaw, float targetPitch) { float yawChange = getAngleChange(yaw, targetYaw); float pitchChange = getAngleChange(pitch, targetPitch); change(yawChange, pitchChange); } } public DefensiveLaserBlockEntity(BlockEntityType type) { super(type); } public static final double DEG2RAD = Math.PI / 180d; private static final double RAD2DEG = 180d / Math.PI; private float getTargetPitch(double x, double y, double z) { Vec3d pos = getPosVec(); double diffX = x - pos.getX(); double diffY = y - pos.getY(); double diffZ = z - pos.getZ(); double g = MathHelper.sqrt(diffX * diffX + diffZ * diffZ); return (float) -(MathHelper.atan2(diffY, g) * RAD2DEG); } private float getTargetYaw(double x, double z) { Vec3d pos = getPosVec(); double diffX = x - pos.getX(); double diffZ = z - pos.getZ(); return (float) (MathHelper.atan2(diffZ, diffX) * RAD2DEG) - 90.0F; } @Override protected void tickEnergy() { assert getWorld() != null; Vec3d vec = rotation.getRayVector().add(getPosVec()); ((ServerWorld) getWorld()).spawnParticles(ParticleTypes.END_ROD, vec.getX(), vec.getY(), vec.getZ(), 1, 0, 0, 0, 0); addAction(Action.createBlockStatePropertyAction(Config.DEFENSIVE_LASER_IDLE_ENERGY_REQUIRED, DefensiveLaserBlock.POWERED, true, false)); if (getCachedState().get(DefensiveLaserBlock.POWERED)) { if (countdown > 0) { countdown--; } if (countdown == 1) { // Fire! target = null; } else if (countdown < 1) { if (target == null) { List entities = getWorld().getEntities(LivingEntity.class, new Box(getPos()).expand(Config.DEFENSIVE_LASER_RANGE), predicate); if (entities.size() > 0) { Collections.shuffle(entities); target = entities.get(0); } } else { if (predicate.test(target)) { Optional optional = target.getBoundingBox().rayTrace(getPosVec(), getPosVec().add(rotation.getRayVector().multiply(Config.DEFENSIVE_LASER_RANGE))); if (optional.isPresent()) { countdown = COUNTDOWN; } else { Vec3d targetPos = target.getPos(); float targetYaw = getTargetYaw(targetPos.getX(), targetPos.getZ()); float targetPitch = getTargetPitch(targetPos.getX(), targetPos.getY(), targetPos.getZ()); rotation.target(targetYaw, targetPitch); } } else { target = null; } } } } } @Override public CompoundTag toTag(CompoundTag tag) { super.toTag(tag); rotation.toTag(tag); tag.putInt("Countdown", countdown); return tag; } @Override public void fromTag(BlockState state, CompoundTag tag) { super.fromTag(state, tag); rotation.fromTag(tag); countdown = tag.getInt("Countdown"); } }