package net.minecraft.world.entity.item; import java.util.Optional; import net.minecraft.core.BlockPos; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.core.registries.Registries; import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.NbtUtils; import net.minecraft.network.syncher.EntityDataAccessor; import net.minecraft.network.syncher.EntityDataSerializers; import net.minecraft.network.syncher.SynchedEntityData; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.LivingEntity; import net.minecraft.world.entity.MoverType; import net.minecraft.world.entity.TraceableEntity; import net.minecraft.world.level.BlockGetter; import net.minecraft.world.level.Explosion; import net.minecraft.world.level.ExplosionDamageCalculator; import net.minecraft.world.level.Level; import net.minecraft.world.level.block.Blocks; import net.minecraft.world.level.block.state.BlockState; import net.minecraft.world.level.material.FluidState; import net.minecraft.world.level.portal.DimensionTransition; import org.jetbrains.annotations.Nullable; public class PrimedTnt extends Entity implements TraceableEntity { private static final EntityDataAccessor DATA_FUSE_ID = SynchedEntityData.defineId(PrimedTnt.class, EntityDataSerializers.INT); private static final EntityDataAccessor DATA_BLOCK_STATE_ID = SynchedEntityData.defineId(PrimedTnt.class, EntityDataSerializers.BLOCK_STATE); private static final int DEFAULT_FUSE_TIME = 80; private static final String TAG_BLOCK_STATE = "block_state"; public static final String TAG_FUSE = "fuse"; private static final ExplosionDamageCalculator USED_PORTAL_DAMAGE_CALCULATOR = new ExplosionDamageCalculator() { @Override public boolean shouldBlockExplode(Explosion explosion, BlockGetter reader, BlockPos pos, BlockState state, float power) { return state.is(Blocks.NETHER_PORTAL) ? false : super.shouldBlockExplode(explosion, reader, pos, state, power); } @Override public Optional getBlockExplosionResistance(Explosion explosion, BlockGetter reader, BlockPos pos, BlockState state, FluidState fluid) { return state.is(Blocks.NETHER_PORTAL) ? Optional.empty() : super.getBlockExplosionResistance(explosion, reader, pos, state, fluid); } }; @Nullable private LivingEntity owner; private boolean usedPortal; public PrimedTnt(EntityType entityType, Level level) { super(entityType, level); this.blocksBuilding = true; } public PrimedTnt(Level level, double x, double y, double z, @Nullable LivingEntity owner) { this(EntityType.TNT, level); this.setPos(x, y, z); double d = level.random.nextDouble() * (float) (Math.PI * 2); this.setDeltaMovement(-Math.sin(d) * 0.02, 0.2F, -Math.cos(d) * 0.02); this.setFuse(80); this.xo = x; this.yo = y; this.zo = z; this.owner = owner; } @Override protected void defineSynchedData(SynchedEntityData.Builder builder) { builder.define(DATA_FUSE_ID, 80); builder.define(DATA_BLOCK_STATE_ID, Blocks.TNT.defaultBlockState()); } @Override protected Entity.MovementEmission getMovementEmission() { return Entity.MovementEmission.NONE; } @Override public boolean isPickable() { return !this.isRemoved(); } @Override protected double getDefaultGravity() { return 0.04; } @Override public void tick() { this.handlePortal(); this.applyGravity(); this.move(MoverType.SELF, this.getDeltaMovement()); this.setDeltaMovement(this.getDeltaMovement().scale(0.98)); if (this.onGround()) { this.setDeltaMovement(this.getDeltaMovement().multiply(0.7, -0.5, 0.7)); } int i = this.getFuse() - 1; this.setFuse(i); if (i <= 0) { this.discard(); if (!this.level().isClientSide) { this.explode(); } } else { this.updateInWaterStateAndDoFluidPushing(); if (this.level().isClientSide) { this.level().addParticle(ParticleTypes.SMOKE, this.getX(), this.getY() + 0.5, this.getZ(), 0.0, 0.0, 0.0); } } } private void explode() { float f = 4.0F; this.level() .explode( this, Explosion.getDefaultDamageSource(this.level(), this), this.usedPortal ? USED_PORTAL_DAMAGE_CALCULATOR : null, this.getX(), this.getY(0.0625), this.getZ(), 4.0F, false, Level.ExplosionInteraction.TNT ); } @Override protected void addAdditionalSaveData(CompoundTag compound) { compound.putShort("fuse", (short)this.getFuse()); compound.put("block_state", NbtUtils.writeBlockState(this.getBlockState())); } @Override protected void readAdditionalSaveData(CompoundTag compound) { this.setFuse(compound.getShort("fuse")); if (compound.contains("block_state", 10)) { this.setBlockState(NbtUtils.readBlockState(this.level().holderLookup(Registries.BLOCK), compound.getCompound("block_state"))); } } /** * Returns null or the entityliving it was ignited by */ @Nullable public LivingEntity getOwner() { return this.owner; } @Override public void restoreFrom(Entity entity) { super.restoreFrom(entity); if (entity instanceof PrimedTnt primedTnt) { this.owner = primedTnt.owner; } } public void setFuse(int life) { this.entityData.set(DATA_FUSE_ID, life); } /** * Gets the fuse from the data manager */ public int getFuse() { return this.entityData.get(DATA_FUSE_ID); } public void setBlockState(BlockState blockState) { this.entityData.set(DATA_BLOCK_STATE_ID, blockState); } public BlockState getBlockState() { return this.entityData.get(DATA_BLOCK_STATE_ID); } private void setUsedPortal(boolean usedPortal) { this.usedPortal = usedPortal; } @Nullable @Override public Entity changeDimension(DimensionTransition transition) { Entity entity = super.changeDimension(transition); if (entity instanceof PrimedTnt primedTnt) { primedTnt.setUsedPortal(true); } return entity; } }