323 lines
11 KiB
Java
323 lines
11 KiB
Java
package net.minecraft.world.entity.projectile;
|
|
|
|
import it.unimi.dsi.fastutil.doubles.DoubleDoubleImmutablePair;
|
|
import java.util.List;
|
|
import java.util.OptionalInt;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.component.DataComponents;
|
|
import net.minecraft.core.particles.ParticleTypes;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.nbt.NbtOps;
|
|
import net.minecraft.nbt.Tag;
|
|
import net.minecraft.network.syncher.EntityDataAccessor;
|
|
import net.minecraft.network.syncher.EntityDataSerializers;
|
|
import net.minecraft.network.syncher.SynchedEntityData;
|
|
import net.minecraft.network.syncher.SynchedEntityData.Builder;
|
|
import net.minecraft.resources.RegistryOps;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.sounds.SoundEvents;
|
|
import net.minecraft.sounds.SoundSource;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.InsideBlockEffectApplier;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.MoverType;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.Items;
|
|
import net.minecraft.world.item.component.FireworkExplosion;
|
|
import net.minecraft.world.item.component.Fireworks;
|
|
import net.minecraft.world.level.ClipContext;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.ClipContext.Block;
|
|
import net.minecraft.world.level.ClipContext.Fluid;
|
|
import net.minecraft.world.level.gameevent.GameEvent;
|
|
import net.minecraft.world.phys.BlockHitResult;
|
|
import net.minecraft.world.phys.EntityHitResult;
|
|
import net.minecraft.world.phys.HitResult;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.world.phys.HitResult.Type;
|
|
import org.jetbrains.annotations.Nullable;
|
|
|
|
public class FireworkRocketEntity extends Projectile implements ItemSupplier {
|
|
private static final EntityDataAccessor<ItemStack> DATA_ID_FIREWORKS_ITEM = SynchedEntityData.defineId(
|
|
FireworkRocketEntity.class, EntityDataSerializers.ITEM_STACK
|
|
);
|
|
private static final EntityDataAccessor<OptionalInt> DATA_ATTACHED_TO_TARGET = SynchedEntityData.defineId(
|
|
FireworkRocketEntity.class, EntityDataSerializers.OPTIONAL_UNSIGNED_INT
|
|
);
|
|
private static final EntityDataAccessor<Boolean> DATA_SHOT_AT_ANGLE = SynchedEntityData.defineId(FireworkRocketEntity.class, EntityDataSerializers.BOOLEAN);
|
|
private static final int DEFAULT_LIFE = 0;
|
|
private static final int DEFAULT_LIFE_TIME = 0;
|
|
private static final boolean DEFAULT_SHOT_AT_ANGLE = false;
|
|
private int life = 0;
|
|
private int lifetime = 0;
|
|
@Nullable
|
|
private LivingEntity attachedToEntity;
|
|
|
|
public FireworkRocketEntity(EntityType<? extends FireworkRocketEntity> entityType, Level level) {
|
|
super(entityType, level);
|
|
}
|
|
|
|
public FireworkRocketEntity(Level level, double x, double y, double z, ItemStack stack) {
|
|
super(EntityType.FIREWORK_ROCKET, level);
|
|
this.life = 0;
|
|
this.setPos(x, y, z);
|
|
this.entityData.set(DATA_ID_FIREWORKS_ITEM, stack.copy());
|
|
int i = 1;
|
|
Fireworks fireworks = stack.get(DataComponents.FIREWORKS);
|
|
if (fireworks != null) {
|
|
i += fireworks.flightDuration();
|
|
}
|
|
|
|
this.setDeltaMovement(this.random.triangle(0.0, 0.002297), 0.05, this.random.triangle(0.0, 0.002297));
|
|
this.lifetime = 10 * i + this.random.nextInt(6) + this.random.nextInt(7);
|
|
}
|
|
|
|
public FireworkRocketEntity(Level level, @Nullable Entity shooter, double x, double y, double z, ItemStack stack) {
|
|
this(level, x, y, z, stack);
|
|
this.setOwner(shooter);
|
|
}
|
|
|
|
public FireworkRocketEntity(Level level, ItemStack stack, LivingEntity shooter) {
|
|
this(level, shooter, shooter.getX(), shooter.getY(), shooter.getZ(), stack);
|
|
this.entityData.set(DATA_ATTACHED_TO_TARGET, OptionalInt.of(shooter.getId()));
|
|
this.attachedToEntity = shooter;
|
|
}
|
|
|
|
public FireworkRocketEntity(Level level, ItemStack stack, double x, double y, double z, boolean shotAtAngle) {
|
|
this(level, x, y, z, stack);
|
|
this.entityData.set(DATA_SHOT_AT_ANGLE, shotAtAngle);
|
|
}
|
|
|
|
public FireworkRocketEntity(Level level, ItemStack stack, Entity shooter, double x, double y, double z, boolean shotAtAngle) {
|
|
this(level, stack, x, y, z, shotAtAngle);
|
|
this.setOwner(shooter);
|
|
}
|
|
|
|
@Override
|
|
protected void defineSynchedData(Builder builder) {
|
|
builder.define(DATA_ID_FIREWORKS_ITEM, getDefaultItem());
|
|
builder.define(DATA_ATTACHED_TO_TARGET, OptionalInt.empty());
|
|
builder.define(DATA_SHOT_AT_ANGLE, false);
|
|
}
|
|
|
|
@Override
|
|
public boolean shouldRenderAtSqrDistance(double distance) {
|
|
return distance < 4096.0 && !this.isAttachedToEntity();
|
|
}
|
|
|
|
@Override
|
|
public boolean shouldRender(double x, double y, double z) {
|
|
return super.shouldRender(x, y, z) && !this.isAttachedToEntity();
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
super.tick();
|
|
HitResult hitResult;
|
|
if (this.isAttachedToEntity()) {
|
|
if (this.attachedToEntity == null) {
|
|
this.entityData.get(DATA_ATTACHED_TO_TARGET).ifPresent(i -> {
|
|
Entity entity = this.level().getEntity(i);
|
|
if (entity instanceof LivingEntity) {
|
|
this.attachedToEntity = (LivingEntity)entity;
|
|
}
|
|
});
|
|
}
|
|
|
|
if (this.attachedToEntity != null) {
|
|
Vec3 vec33;
|
|
if (this.attachedToEntity.isFallFlying()) {
|
|
Vec3 vec3 = this.attachedToEntity.getLookAngle();
|
|
double d = 1.5;
|
|
double e = 0.1;
|
|
Vec3 vec32 = this.attachedToEntity.getDeltaMovement();
|
|
this.attachedToEntity
|
|
.setDeltaMovement(
|
|
vec32.add(vec3.x * 0.1 + (vec3.x * 1.5 - vec32.x) * 0.5, vec3.y * 0.1 + (vec3.y * 1.5 - vec32.y) * 0.5, vec3.z * 0.1 + (vec3.z * 1.5 - vec32.z) * 0.5)
|
|
);
|
|
vec33 = this.attachedToEntity.getHandHoldingItemAngle(Items.FIREWORK_ROCKET);
|
|
} else {
|
|
vec33 = Vec3.ZERO;
|
|
}
|
|
|
|
this.setPos(this.attachedToEntity.getX() + vec33.x, this.attachedToEntity.getY() + vec33.y, this.attachedToEntity.getZ() + vec33.z);
|
|
this.setDeltaMovement(this.attachedToEntity.getDeltaMovement());
|
|
}
|
|
|
|
hitResult = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
|
|
} else {
|
|
if (!this.isShotAtAngle()) {
|
|
double f = this.horizontalCollision ? 1.0 : 1.15;
|
|
this.setDeltaMovement(this.getDeltaMovement().multiply(f, 1.0, f).add(0.0, 0.04, 0.0));
|
|
}
|
|
|
|
Vec3 vec33 = this.getDeltaMovement();
|
|
hitResult = ProjectileUtil.getHitResultOnMoveVector(this, this::canHitEntity);
|
|
this.move(MoverType.SELF, vec33);
|
|
this.applyEffectsFromBlocks();
|
|
this.setDeltaMovement(vec33);
|
|
}
|
|
|
|
if (!this.noPhysics && this.isAlive() && hitResult.getType() != Type.MISS) {
|
|
this.hitTargetOrDeflectSelf(hitResult);
|
|
this.hasImpulse = true;
|
|
}
|
|
|
|
this.updateRotation();
|
|
if (this.life == 0 && !this.isSilent()) {
|
|
this.level().playSound(null, this.getX(), this.getY(), this.getZ(), SoundEvents.FIREWORK_ROCKET_LAUNCH, SoundSource.AMBIENT, 3.0F, 1.0F);
|
|
}
|
|
|
|
this.life++;
|
|
if (this.level().isClientSide && this.life % 2 < 2) {
|
|
this.level()
|
|
.addParticle(
|
|
ParticleTypes.FIREWORK,
|
|
this.getX(),
|
|
this.getY(),
|
|
this.getZ(),
|
|
this.random.nextGaussian() * 0.05,
|
|
-this.getDeltaMovement().y * 0.5,
|
|
this.random.nextGaussian() * 0.05
|
|
);
|
|
}
|
|
|
|
if (this.life > this.lifetime && this.level() instanceof ServerLevel serverLevel) {
|
|
this.explode(serverLevel);
|
|
}
|
|
}
|
|
|
|
private void explode(ServerLevel level) {
|
|
level.broadcastEntityEvent(this, (byte)17);
|
|
this.gameEvent(GameEvent.EXPLODE, this.getOwner());
|
|
this.dealExplosionDamage(level);
|
|
this.discard();
|
|
}
|
|
|
|
@Override
|
|
protected void onHitEntity(EntityHitResult result) {
|
|
super.onHitEntity(result);
|
|
if (this.level() instanceof ServerLevel serverLevel) {
|
|
this.explode(serverLevel);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
protected void onHitBlock(BlockHitResult result) {
|
|
BlockPos blockPos = new BlockPos(result.getBlockPos());
|
|
this.level().getBlockState(blockPos).entityInside(this.level(), blockPos, this, InsideBlockEffectApplier.NOOP);
|
|
if (this.level() instanceof ServerLevel serverLevel && this.hasExplosion()) {
|
|
this.explode(serverLevel);
|
|
}
|
|
|
|
super.onHitBlock(result);
|
|
}
|
|
|
|
private boolean hasExplosion() {
|
|
return !this.getExplosions().isEmpty();
|
|
}
|
|
|
|
private void dealExplosionDamage(ServerLevel level) {
|
|
float f = 0.0F;
|
|
List<FireworkExplosion> list = this.getExplosions();
|
|
if (!list.isEmpty()) {
|
|
f = 5.0F + list.size() * 2;
|
|
}
|
|
|
|
if (f > 0.0F) {
|
|
if (this.attachedToEntity != null) {
|
|
this.attachedToEntity.hurtServer(level, this.damageSources().fireworks(this, this.getOwner()), 5.0F + list.size() * 2);
|
|
}
|
|
|
|
double d = 5.0;
|
|
Vec3 vec3 = this.position();
|
|
|
|
for (LivingEntity livingEntity : this.level().getEntitiesOfClass(LivingEntity.class, this.getBoundingBox().inflate(5.0))) {
|
|
if (livingEntity != this.attachedToEntity && !(this.distanceToSqr(livingEntity) > 25.0)) {
|
|
boolean bl = false;
|
|
|
|
for (int i = 0; i < 2; i++) {
|
|
Vec3 vec32 = new Vec3(livingEntity.getX(), livingEntity.getY(0.5 * i), livingEntity.getZ());
|
|
HitResult hitResult = this.level().clip(new ClipContext(vec3, vec32, Block.COLLIDER, Fluid.NONE, this));
|
|
if (hitResult.getType() == Type.MISS) {
|
|
bl = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (bl) {
|
|
float g = f * (float)Math.sqrt((5.0 - this.distanceTo(livingEntity)) / 5.0);
|
|
livingEntity.hurtServer(level, this.damageSources().fireworks(this, this.getOwner()), g);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private boolean isAttachedToEntity() {
|
|
return this.entityData.get(DATA_ATTACHED_TO_TARGET).isPresent();
|
|
}
|
|
|
|
public boolean isShotAtAngle() {
|
|
return this.entityData.get(DATA_SHOT_AT_ANGLE);
|
|
}
|
|
|
|
@Override
|
|
public void handleEntityEvent(byte id) {
|
|
if (id == 17 && this.level().isClientSide) {
|
|
Vec3 vec3 = this.getDeltaMovement();
|
|
this.level().createFireworks(this.getX(), this.getY(), this.getZ(), vec3.x, vec3.y, vec3.z, this.getExplosions());
|
|
}
|
|
|
|
super.handleEntityEvent(id);
|
|
}
|
|
|
|
@Override
|
|
public void addAdditionalSaveData(CompoundTag tag) {
|
|
super.addAdditionalSaveData(tag);
|
|
tag.putInt("Life", this.life);
|
|
tag.putInt("LifeTime", this.lifetime);
|
|
RegistryOps<Tag> registryOps = this.registryAccess().createSerializationContext(NbtOps.INSTANCE);
|
|
tag.store("FireworksItem", ItemStack.CODEC, registryOps, this.getItem());
|
|
tag.putBoolean("ShotAtAngle", this.entityData.get(DATA_SHOT_AT_ANGLE));
|
|
}
|
|
|
|
@Override
|
|
public void readAdditionalSaveData(CompoundTag tag) {
|
|
super.readAdditionalSaveData(tag);
|
|
this.life = tag.getIntOr("Life", 0);
|
|
this.lifetime = tag.getIntOr("LifeTime", 0);
|
|
RegistryOps<Tag> registryOps = this.registryAccess().createSerializationContext(NbtOps.INSTANCE);
|
|
this.entityData.set(DATA_ID_FIREWORKS_ITEM, (ItemStack)tag.read("FireworksItem", ItemStack.CODEC, registryOps).orElse(getDefaultItem()));
|
|
this.entityData.set(DATA_SHOT_AT_ANGLE, tag.getBooleanOr("ShotAtAngle", false));
|
|
}
|
|
|
|
private List<FireworkExplosion> getExplosions() {
|
|
ItemStack itemStack = this.entityData.get(DATA_ID_FIREWORKS_ITEM);
|
|
Fireworks fireworks = itemStack.get(DataComponents.FIREWORKS);
|
|
return fireworks != null ? fireworks.explosions() : List.of();
|
|
}
|
|
|
|
@Override
|
|
public ItemStack getItem() {
|
|
return this.entityData.get(DATA_ID_FIREWORKS_ITEM);
|
|
}
|
|
|
|
@Override
|
|
public boolean isAttackable() {
|
|
return false;
|
|
}
|
|
|
|
private static ItemStack getDefaultItem() {
|
|
return new ItemStack(Items.FIREWORK_ROCKET);
|
|
}
|
|
|
|
@Override
|
|
public DoubleDoubleImmutablePair calculateHorizontalHurtKnockbackDirection(LivingEntity entity, DamageSource damageSource) {
|
|
double d = entity.position().x - this.position().x;
|
|
double e = entity.position().z - this.position().z;
|
|
return DoubleDoubleImmutablePair.of(d, e);
|
|
}
|
|
}
|