411 lines
16 KiB
Java
411 lines
16 KiB
Java
package net.minecraft.world.entity.projectile;
|
|
|
|
import com.google.common.base.MoreObjects;
|
|
import it.unimi.dsi.fastutil.doubles.DoubleDoubleImmutablePair;
|
|
import java.util.Objects;
|
|
import java.util.UUID;
|
|
import java.util.function.Consumer;
|
|
import javax.annotation.Nullable;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.core.UUIDUtil;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.network.protocol.Packet;
|
|
import net.minecraft.network.protocol.game.ClientGamePacketListener;
|
|
import net.minecraft.network.protocol.game.ClientboundAddEntityPacket;
|
|
import net.minecraft.server.level.ServerEntity;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.tags.EntityTypeTags;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.world.damagesource.DamageSource;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.world.entity.EntitySelector;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.LivingEntity;
|
|
import net.minecraft.world.entity.TraceableEntity;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.item.Item;
|
|
import net.minecraft.world.item.ItemStack;
|
|
import net.minecraft.world.item.enchantment.EnchantmentHelper;
|
|
import net.minecraft.world.level.GameRules;
|
|
import net.minecraft.world.level.Level;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.gameevent.GameEvent;
|
|
import net.minecraft.world.phys.AABB;
|
|
import net.minecraft.world.phys.BlockHitResult;
|
|
import net.minecraft.world.phys.EntityHitResult;
|
|
import net.minecraft.world.phys.HitResult;
|
|
import net.minecraft.world.phys.Vec3;
|
|
|
|
public abstract class Projectile extends Entity implements TraceableEntity {
|
|
private static final boolean DEFAULT_LEFT_OWNER = false;
|
|
private static final boolean DEFAULT_HAS_BEEN_SHOT = false;
|
|
@Nullable
|
|
private UUID ownerUUID;
|
|
@Nullable
|
|
private Entity cachedOwner;
|
|
private boolean leftOwner = false;
|
|
private boolean hasBeenShot = false;
|
|
@Nullable
|
|
private Entity lastDeflectedBy;
|
|
|
|
Projectile(EntityType<? extends Projectile> p_37248_, Level p_37249_) {
|
|
super(p_37248_, p_37249_);
|
|
}
|
|
|
|
public void setOwner(@Nullable Entity p_37263_) {
|
|
if (p_37263_ != null) {
|
|
this.ownerUUID = p_37263_.getUUID();
|
|
this.cachedOwner = p_37263_;
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
@Override
|
|
public Entity getOwner() {
|
|
if (this.cachedOwner != null && !this.cachedOwner.isRemoved()) {
|
|
return this.cachedOwner;
|
|
} else if (this.ownerUUID != null) {
|
|
this.cachedOwner = this.findOwner(this.ownerUUID);
|
|
return this.cachedOwner;
|
|
} else {
|
|
return null;
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
protected Entity findOwner(UUID p_365545_) {
|
|
return this.level() instanceof ServerLevel serverlevel ? serverlevel.getEntity(p_365545_) : null;
|
|
}
|
|
|
|
public Entity getEffectSource() {
|
|
return MoreObjects.firstNonNull(this.getOwner(), this);
|
|
}
|
|
|
|
@Override
|
|
protected void addAdditionalSaveData(CompoundTag p_37265_) {
|
|
p_37265_.storeNullable("Owner", UUIDUtil.CODEC, this.ownerUUID);
|
|
if (this.leftOwner) {
|
|
p_37265_.putBoolean("LeftOwner", true);
|
|
}
|
|
|
|
p_37265_.putBoolean("HasBeenShot", this.hasBeenShot);
|
|
}
|
|
|
|
protected boolean ownedBy(Entity p_150172_) {
|
|
return p_150172_.getUUID().equals(this.ownerUUID);
|
|
}
|
|
|
|
@Override
|
|
protected void readAdditionalSaveData(CompoundTag p_37262_) {
|
|
this.setOwnerThroughUUID(p_37262_.read("Owner", UUIDUtil.CODEC).orElse(null));
|
|
this.leftOwner = p_37262_.getBooleanOr("LeftOwner", false);
|
|
this.hasBeenShot = p_37262_.getBooleanOr("HasBeenShot", false);
|
|
}
|
|
|
|
protected void setOwnerThroughUUID(@Nullable UUID p_369008_) {
|
|
if (!Objects.equals(this.ownerUUID, p_369008_)) {
|
|
this.ownerUUID = p_369008_;
|
|
this.cachedOwner = p_369008_ != null ? this.findOwner(p_369008_) : null;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void restoreFrom(Entity p_310133_) {
|
|
super.restoreFrom(p_310133_);
|
|
if (p_310133_ instanceof Projectile projectile) {
|
|
this.ownerUUID = projectile.ownerUUID;
|
|
this.cachedOwner = projectile.cachedOwner;
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public void tick() {
|
|
if (!this.hasBeenShot) {
|
|
this.gameEvent(GameEvent.PROJECTILE_SHOOT, this.getOwner());
|
|
this.hasBeenShot = true;
|
|
}
|
|
|
|
if (!this.leftOwner) {
|
|
this.leftOwner = this.checkLeftOwner();
|
|
}
|
|
|
|
super.tick();
|
|
}
|
|
|
|
private boolean checkLeftOwner() {
|
|
Entity entity = this.getOwner();
|
|
if (entity != null) {
|
|
AABB aabb = this.getBoundingBox().expandTowards(this.getDeltaMovement()).inflate(1.0);
|
|
return entity.getRootVehicle().getSelfAndPassengers().filter(EntitySelector.CAN_BE_PICKED).noneMatch(p_359340_ -> aabb.intersects(p_359340_.getBoundingBox()));
|
|
} else {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
public Vec3 getMovementToShoot(double p_335302_, double p_334829_, double p_334312_, float p_331363_, float p_330173_) {
|
|
return new Vec3(p_335302_, p_334829_, p_334312_)
|
|
.normalize()
|
|
.add(
|
|
this.random.triangle(0.0, 0.0172275 * p_330173_),
|
|
this.random.triangle(0.0, 0.0172275 * p_330173_),
|
|
this.random.triangle(0.0, 0.0172275 * p_330173_)
|
|
)
|
|
.scale(p_331363_);
|
|
}
|
|
|
|
public void shoot(double p_37266_, double p_37267_, double p_37268_, float p_37269_, float p_37270_) {
|
|
Vec3 vec3 = this.getMovementToShoot(p_37266_, p_37267_, p_37268_, p_37269_, p_37270_);
|
|
this.setDeltaMovement(vec3);
|
|
this.hasImpulse = true;
|
|
double d0 = vec3.horizontalDistance();
|
|
this.setYRot((float)(Mth.atan2(vec3.x, vec3.z) * 180.0F / (float)Math.PI));
|
|
this.setXRot((float)(Mth.atan2(vec3.y, d0) * 180.0F / (float)Math.PI));
|
|
this.yRotO = this.getYRot();
|
|
this.xRotO = this.getXRot();
|
|
}
|
|
|
|
public void shootFromRotation(Entity p_37252_, float p_37253_, float p_37254_, float p_37255_, float p_37256_, float p_37257_) {
|
|
float f = -Mth.sin(p_37254_ * (float) (Math.PI / 180.0)) * Mth.cos(p_37253_ * (float) (Math.PI / 180.0));
|
|
float f1 = -Mth.sin((p_37253_ + p_37255_) * (float) (Math.PI / 180.0));
|
|
float f2 = Mth.cos(p_37254_ * (float) (Math.PI / 180.0)) * Mth.cos(p_37253_ * (float) (Math.PI / 180.0));
|
|
this.shoot(f, f1, f2, p_37256_, p_37257_);
|
|
Vec3 vec3 = p_37252_.getKnownMovement();
|
|
this.setDeltaMovement(this.getDeltaMovement().add(vec3.x, p_37252_.onGround() ? 0.0 : vec3.y, vec3.z));
|
|
}
|
|
|
|
@Override
|
|
public void onAboveBubbleColumn(boolean p_395187_, BlockPos p_397623_) {
|
|
double d0 = p_395187_ ? -0.03 : 0.1;
|
|
this.setDeltaMovement(this.getDeltaMovement().add(0.0, d0, 0.0));
|
|
sendBubbleColumnParticles(this.level(), p_397623_);
|
|
}
|
|
|
|
@Override
|
|
public void onInsideBubbleColumn(boolean p_395217_) {
|
|
double d0 = p_395217_ ? -0.03 : 0.06;
|
|
this.setDeltaMovement(this.getDeltaMovement().add(0.0, d0, 0.0));
|
|
this.resetFallDistance();
|
|
}
|
|
|
|
public static <T extends Projectile> T spawnProjectileFromRotation(
|
|
Projectile.ProjectileFactory<T> p_364630_,
|
|
ServerLevel p_369390_,
|
|
ItemStack p_367599_,
|
|
LivingEntity p_361588_,
|
|
float p_367396_,
|
|
float p_363677_,
|
|
float p_365637_
|
|
) {
|
|
return spawnProjectile(
|
|
p_364630_.create(p_369390_, p_361588_, p_367599_),
|
|
p_369390_,
|
|
p_367599_,
|
|
p_375173_ -> p_375173_.shootFromRotation(p_361588_, p_361588_.getXRot(), p_361588_.getYRot(), p_367396_, p_363677_, p_365637_)
|
|
);
|
|
}
|
|
|
|
public static <T extends Projectile> T spawnProjectileUsingShoot(
|
|
Projectile.ProjectileFactory<T> p_362783_,
|
|
ServerLevel p_362807_,
|
|
ItemStack p_361126_,
|
|
LivingEntity p_368296_,
|
|
double p_367312_,
|
|
double p_361634_,
|
|
double p_367734_,
|
|
float p_361151_,
|
|
float p_368071_
|
|
) {
|
|
return spawnProjectile(
|
|
p_362783_.create(p_362807_, p_368296_, p_361126_),
|
|
p_362807_,
|
|
p_361126_,
|
|
p_359337_ -> p_359337_.shoot(p_367312_, p_361634_, p_367734_, p_361151_, p_368071_)
|
|
);
|
|
}
|
|
|
|
public static <T extends Projectile> T spawnProjectileUsingShoot(
|
|
T p_367886_, ServerLevel p_360818_, ItemStack p_364412_, double p_362828_, double p_361067_, double p_368213_, float p_366268_, float p_361310_
|
|
) {
|
|
return spawnProjectile(p_367886_, p_360818_, p_364412_, p_359347_ -> p_367886_.shoot(p_362828_, p_361067_, p_368213_, p_366268_, p_361310_));
|
|
}
|
|
|
|
public static <T extends Projectile> T spawnProjectile(T p_361503_, ServerLevel p_367711_, ItemStack p_361747_) {
|
|
return spawnProjectile(p_361503_, p_367711_, p_361747_, p_359326_ -> {});
|
|
}
|
|
|
|
public static <T extends Projectile> T spawnProjectile(T p_365177_, ServerLevel p_365242_, ItemStack p_366479_, Consumer<T> p_360962_) {
|
|
p_360962_.accept(p_365177_);
|
|
p_365242_.addFreshEntity(p_365177_);
|
|
p_365177_.applyOnProjectileSpawned(p_365242_, p_366479_);
|
|
return p_365177_;
|
|
}
|
|
|
|
public void applyOnProjectileSpawned(ServerLevel p_363701_, ItemStack p_365738_) {
|
|
EnchantmentHelper.onProjectileSpawned(p_363701_, p_365738_, this, p_359338_ -> {});
|
|
if (this instanceof AbstractArrow abstractarrow) {
|
|
ItemStack itemstack = abstractarrow.getWeaponItem();
|
|
if (itemstack != null && !itemstack.isEmpty() && !p_365738_.getItem().equals(itemstack.getItem())) {
|
|
EnchantmentHelper.onProjectileSpawned(p_363701_, itemstack, this, abstractarrow::onItemBreak);
|
|
}
|
|
}
|
|
}
|
|
|
|
protected ProjectileDeflection hitTargetOrDeflectSelf(HitResult p_329816_) {
|
|
if (p_329816_.getType() == HitResult.Type.ENTITY) {
|
|
EntityHitResult entityhitresult = (EntityHitResult)p_329816_;
|
|
Entity entity = entityhitresult.getEntity();
|
|
ProjectileDeflection projectiledeflection = entity.deflection(this);
|
|
if (projectiledeflection != ProjectileDeflection.NONE) {
|
|
if (entity != this.lastDeflectedBy && this.deflect(projectiledeflection, entity, this.getOwner(), false)) {
|
|
this.lastDeflectedBy = entity;
|
|
}
|
|
|
|
return projectiledeflection;
|
|
}
|
|
} else if (this.shouldBounceOnWorldBorder() && p_329816_ instanceof BlockHitResult blockhitresult && blockhitresult.isWorldBorderHit()) {
|
|
ProjectileDeflection projectiledeflection1 = ProjectileDeflection.REVERSE;
|
|
if (this.deflect(projectiledeflection1, null, this.getOwner(), false)) {
|
|
this.setDeltaMovement(this.getDeltaMovement().scale(0.2));
|
|
return projectiledeflection1;
|
|
}
|
|
}
|
|
|
|
this.onHit(p_329816_);
|
|
return ProjectileDeflection.NONE;
|
|
}
|
|
|
|
protected boolean shouldBounceOnWorldBorder() {
|
|
return false;
|
|
}
|
|
|
|
public boolean deflect(ProjectileDeflection p_328550_, @Nullable Entity p_330074_, @Nullable Entity p_333528_, boolean p_328333_) {
|
|
p_328550_.deflect(this, p_330074_, this.random);
|
|
if (!this.level().isClientSide) {
|
|
this.setOwner(p_333528_);
|
|
this.onDeflection(p_330074_, p_328333_);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
protected void onDeflection(@Nullable Entity p_327965_, boolean p_335911_) {
|
|
}
|
|
|
|
protected void onItemBreak(Item p_366262_) {
|
|
}
|
|
|
|
protected void onHit(HitResult p_37260_) {
|
|
HitResult.Type hitresult$type = p_37260_.getType();
|
|
if (hitresult$type == HitResult.Type.ENTITY) {
|
|
EntityHitResult entityhitresult = (EntityHitResult)p_37260_;
|
|
Entity entity = entityhitresult.getEntity();
|
|
if (entity.getType().is(EntityTypeTags.REDIRECTABLE_PROJECTILE) && entity instanceof Projectile projectile) {
|
|
projectile.deflect(ProjectileDeflection.AIM_DEFLECT, this.getOwner(), this.getOwner(), true);
|
|
}
|
|
|
|
this.onHitEntity(entityhitresult);
|
|
this.level().gameEvent(GameEvent.PROJECTILE_LAND, p_37260_.getLocation(), GameEvent.Context.of(this, null));
|
|
} else if (hitresult$type == HitResult.Type.BLOCK) {
|
|
BlockHitResult blockhitresult = (BlockHitResult)p_37260_;
|
|
this.onHitBlock(blockhitresult);
|
|
BlockPos blockpos = blockhitresult.getBlockPos();
|
|
this.level().gameEvent(GameEvent.PROJECTILE_LAND, blockpos, GameEvent.Context.of(this, this.level().getBlockState(blockpos)));
|
|
}
|
|
}
|
|
|
|
protected void onHitEntity(EntityHitResult p_37259_) {
|
|
}
|
|
|
|
protected void onHitBlock(BlockHitResult p_37258_) {
|
|
BlockState blockstate = this.level().getBlockState(p_37258_.getBlockPos());
|
|
blockstate.onProjectileHit(this.level(), blockstate, p_37258_, this);
|
|
}
|
|
|
|
protected boolean canHitEntity(Entity p_37250_) {
|
|
if (!p_37250_.canBeHitByProjectile()) {
|
|
return false;
|
|
} else {
|
|
Entity entity = this.getOwner();
|
|
return entity == null || this.leftOwner || !entity.isPassengerOfSameVehicle(p_37250_);
|
|
}
|
|
}
|
|
|
|
protected void updateRotation() {
|
|
Vec3 vec3 = this.getDeltaMovement();
|
|
double d0 = vec3.horizontalDistance();
|
|
this.setXRot(lerpRotation(this.xRotO, (float)(Mth.atan2(vec3.y, d0) * 180.0F / (float)Math.PI)));
|
|
this.setYRot(lerpRotation(this.yRotO, (float)(Mth.atan2(vec3.x, vec3.z) * 180.0F / (float)Math.PI)));
|
|
}
|
|
|
|
protected static float lerpRotation(float p_37274_, float p_37275_) {
|
|
while (p_37275_ - p_37274_ < -180.0F) {
|
|
p_37274_ -= 360.0F;
|
|
}
|
|
|
|
while (p_37275_ - p_37274_ >= 180.0F) {
|
|
p_37274_ += 360.0F;
|
|
}
|
|
|
|
return Mth.lerp(0.2F, p_37274_, p_37275_);
|
|
}
|
|
|
|
@Override
|
|
public Packet<ClientGamePacketListener> getAddEntityPacket(ServerEntity p_345233_) {
|
|
Entity entity = this.getOwner();
|
|
return new ClientboundAddEntityPacket(this, p_345233_, entity == null ? 0 : entity.getId());
|
|
}
|
|
|
|
@Override
|
|
public void recreateFromPacket(ClientboundAddEntityPacket p_150170_) {
|
|
super.recreateFromPacket(p_150170_);
|
|
Entity entity = this.level().getEntity(p_150170_.getData());
|
|
if (entity != null) {
|
|
this.setOwner(entity);
|
|
}
|
|
}
|
|
|
|
@Override
|
|
public boolean mayInteract(ServerLevel p_364907_, BlockPos p_150168_) {
|
|
Entity entity = this.getOwner();
|
|
return entity instanceof Player ? entity.mayInteract(p_364907_, p_150168_) : entity == null || p_364907_.getGameRules().getBoolean(GameRules.RULE_MOBGRIEFING);
|
|
}
|
|
|
|
public boolean mayBreak(ServerLevel p_361134_) {
|
|
return this.getType().is(EntityTypeTags.IMPACT_PROJECTILES) && p_361134_.getGameRules().getBoolean(GameRules.RULE_PROJECTILESCANBREAKBLOCKS);
|
|
}
|
|
|
|
@Override
|
|
public boolean isPickable() {
|
|
return this.getType().is(EntityTypeTags.REDIRECTABLE_PROJECTILE);
|
|
}
|
|
|
|
@Override
|
|
public float getPickRadius() {
|
|
return this.isPickable() ? 1.0F : 0.0F;
|
|
}
|
|
|
|
public DoubleDoubleImmutablePair calculateHorizontalHurtKnockbackDirection(LivingEntity p_343703_, DamageSource p_343506_) {
|
|
double d0 = this.getDeltaMovement().x;
|
|
double d1 = this.getDeltaMovement().z;
|
|
return DoubleDoubleImmutablePair.of(d0, d1);
|
|
}
|
|
|
|
@Override
|
|
public int getDimensionChangingDelay() {
|
|
return 2;
|
|
}
|
|
|
|
@Override
|
|
public boolean hurtServer(ServerLevel p_367356_, DamageSource p_368526_, float p_366624_) {
|
|
if (!this.isInvulnerableToBase(p_368526_)) {
|
|
this.markHurt();
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
@FunctionalInterface
|
|
public interface ProjectileFactory<T extends Projectile> {
|
|
T create(ServerLevel p_369109_, LivingEntity p_369221_, ItemStack p_366597_);
|
|
}
|
|
} |