package net.minecraft.world.entity.projectile; import net.minecraft.nbt.CompoundTag; 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.server.level.ServerLevel; import net.minecraft.server.level.ServerPlayer; import net.minecraft.sounds.SoundEvent; import net.minecraft.sounds.SoundEvents; import net.minecraft.util.Mth; import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.player.Player; import net.minecraft.world.item.ItemStack; import net.minecraft.world.item.Items; import net.minecraft.world.item.enchantment.EnchantmentHelper; import net.minecraft.world.level.Level; import net.minecraft.world.phys.BlockHitResult; import net.minecraft.world.phys.EntityHitResult; import net.minecraft.world.phys.Vec3; import org.jetbrains.annotations.Nullable; public class ThrownTrident extends AbstractArrow { private static final EntityDataAccessor ID_LOYALTY = SynchedEntityData.defineId(ThrownTrident.class, EntityDataSerializers.BYTE); private static final EntityDataAccessor ID_FOIL = SynchedEntityData.defineId(ThrownTrident.class, EntityDataSerializers.BOOLEAN); private static final float WATER_INERTIA = 0.99F; private static final boolean DEFAULT_DEALT_DAMAGE = false; private boolean dealtDamage = false; public int clientSideReturnTridentTickCount; public ThrownTrident(EntityType entityType, Level level) { super(entityType, level); } public ThrownTrident(Level level, LivingEntity shooter, ItemStack pickupItemStack) { super(EntityType.TRIDENT, shooter, level, pickupItemStack, null); this.entityData.set(ID_LOYALTY, this.getLoyaltyFromItem(pickupItemStack)); this.entityData.set(ID_FOIL, pickupItemStack.hasFoil()); } public ThrownTrident(Level level, double x, double y, double z, ItemStack pickupItemStack) { super(EntityType.TRIDENT, x, y, z, level, pickupItemStack, pickupItemStack); this.entityData.set(ID_LOYALTY, this.getLoyaltyFromItem(pickupItemStack)); this.entityData.set(ID_FOIL, pickupItemStack.hasFoil()); } @Override protected void defineSynchedData(Builder builder) { super.defineSynchedData(builder); builder.define(ID_LOYALTY, (byte)0); builder.define(ID_FOIL, false); } @Override public void tick() { if (this.inGroundTime > 4) { this.dealtDamage = true; } Entity entity = this.getOwner(); int i = this.entityData.get(ID_LOYALTY); if (i > 0 && (this.dealtDamage || this.isNoPhysics()) && entity != null) { if (!this.isAcceptibleReturnOwner()) { if (this.level() instanceof ServerLevel serverLevel && this.pickup == AbstractArrow.Pickup.ALLOWED) { this.spawnAtLocation(serverLevel, this.getPickupItem(), 0.1F); } this.discard(); } else { if (!(entity instanceof Player) && this.position().distanceTo(entity.getEyePosition()) < entity.getBbWidth() + 1.0) { this.discard(); return; } this.setNoPhysics(true); Vec3 vec3 = entity.getEyePosition().subtract(this.position()); this.setPosRaw(this.getX(), this.getY() + vec3.y * 0.015 * i, this.getZ()); double d = 0.05 * i; this.setDeltaMovement(this.getDeltaMovement().scale(0.95).add(vec3.normalize().scale(d))); if (this.clientSideReturnTridentTickCount == 0) { this.playSound(SoundEvents.TRIDENT_RETURN, 10.0F, 1.0F); } this.clientSideReturnTridentTickCount++; } } super.tick(); } private boolean isAcceptibleReturnOwner() { Entity entity = this.getOwner(); return entity == null || !entity.isAlive() ? false : !(entity instanceof ServerPlayer) || !entity.isSpectator(); } public boolean isFoil() { return this.entityData.get(ID_FOIL); } @Nullable @Override protected EntityHitResult findHitEntity(Vec3 startVec, Vec3 endVec) { return this.dealtDamage ? null : super.findHitEntity(startVec, endVec); } @Override protected void onHitEntity(EntityHitResult result) { Entity entity = result.getEntity(); float f = 8.0F; Entity entity2 = this.getOwner(); DamageSource damageSource = this.damageSources().trident(this, (Entity)(entity2 == null ? this : entity2)); if (this.level() instanceof ServerLevel serverLevel) { f = EnchantmentHelper.modifyDamage(serverLevel, this.getWeaponItem(), entity, damageSource, f); } this.dealtDamage = true; if (entity.hurtOrSimulate(damageSource, f)) { if (entity.getType() == EntityType.ENDERMAN) { return; } if (this.level() instanceof ServerLevel serverLevel) { EnchantmentHelper.doPostAttackEffectsWithItemSourceOnBreak(serverLevel, entity, damageSource, this.getWeaponItem(), item -> this.kill(serverLevel)); } if (entity instanceof LivingEntity livingEntity) { this.doKnockback(livingEntity, damageSource); this.doPostHurtEffects(livingEntity); } } this.deflect(ProjectileDeflection.REVERSE, entity, this.getOwner(), false); this.setDeltaMovement(this.getDeltaMovement().multiply(0.02, 0.2, 0.02)); this.playSound(SoundEvents.TRIDENT_HIT, 1.0F, 1.0F); } @Override protected void hitBlockEnchantmentEffects(ServerLevel level, BlockHitResult hitResult, ItemStack stack) { Vec3 vec3 = hitResult.getBlockPos().clampLocationWithin(hitResult.getLocation()); EnchantmentHelper.onHitBlock( level, stack, this.getOwner() instanceof LivingEntity livingEntity ? livingEntity : null, this, null, vec3, level.getBlockState(hitResult.getBlockPos()), item -> this.kill(level) ); } @Override public ItemStack getWeaponItem() { return this.getPickupItemStackOrigin(); } @Override protected boolean tryPickup(Player player) { return super.tryPickup(player) || this.isNoPhysics() && this.ownedBy(player) && player.getInventory().add(this.getPickupItem()); } @Override protected ItemStack getDefaultPickupItem() { return new ItemStack(Items.TRIDENT); } @Override protected SoundEvent getDefaultHitGroundSoundEvent() { return SoundEvents.TRIDENT_HIT_GROUND; } @Override public void playerTouch(Player player) { if (this.ownedBy(player) || this.getOwner() == null) { super.playerTouch(player); } } @Override public void readAdditionalSaveData(CompoundTag tag) { super.readAdditionalSaveData(tag); this.dealtDamage = tag.getBooleanOr("DealtDamage", false); this.entityData.set(ID_LOYALTY, this.getLoyaltyFromItem(this.getPickupItemStackOrigin())); } @Override public void addAdditionalSaveData(CompoundTag tag) { super.addAdditionalSaveData(tag); tag.putBoolean("DealtDamage", this.dealtDamage); } private byte getLoyaltyFromItem(ItemStack stack) { return this.level() instanceof ServerLevel serverLevel ? (byte)Mth.clamp(EnchantmentHelper.getTridentReturnToOwnerAcceleration(serverLevel, stack, this), 0, 127) : 0; } @Override public void tickDespawn() { int i = this.entityData.get(ID_LOYALTY); if (this.pickup != AbstractArrow.Pickup.ALLOWED || i <= 0) { super.tickDespawn(); } } @Override protected float getWaterInertia() { return 0.99F; } @Override public boolean shouldRender(double x, double y, double z) { return true; } }