diff --git a/src/main/java/blusunrize/immersiveengineering/client/render/entity/IEExplosiveRenderer.java b/src/main/java/blusunrize/immersiveengineering/client/render/entity/IEExplosiveRenderer.java index 26a8d8dcae..eb0b083c23 100644 --- a/src/main/java/blusunrize/immersiveengineering/client/render/entity/IEExplosiveRenderer.java +++ b/src/main/java/blusunrize/immersiveengineering/client/render/entity/IEExplosiveRenderer.java @@ -8,10 +8,9 @@ package blusunrize.immersiveengineering.client.render.entity; -import blusunrize.immersiveengineering.common.entities.IEExplosiveEntity; +import blusunrize.immersiveengineering.common.entities.GunpowderBarrelEntity; import com.mojang.blaze3d.vertex.PoseStack; import org.joml.Quaternionf; -import org.joml.Vector3f; import net.minecraft.client.Minecraft; import net.minecraft.client.renderer.MultiBufferSource; import net.minecraft.client.renderer.block.BlockRenderDispatcher; @@ -22,7 +21,7 @@ import net.minecraft.util.Mth; import net.minecraft.world.inventory.InventoryMenu; -public class IEExplosiveRenderer extends EntityRenderer +public class IEExplosiveRenderer extends EntityRenderer { public IEExplosiveRenderer(Context renderManager) { @@ -31,7 +30,7 @@ public IEExplosiveRenderer(Context renderManager) } @Override - public void render(IEExplosiveEntity entity, float entityYaw, float partialTicks, PoseStack matrixStackIn, MultiBufferSource bufferIn, int packedLightIn) + public void render(GunpowderBarrelEntity entity, float entityYaw, float partialTicks, PoseStack matrixStackIn, MultiBufferSource bufferIn, int packedLightIn) { if(entity.block==null) return; @@ -63,7 +62,7 @@ public void render(IEExplosiveEntity entity, float entityYaw, float partialTicks } @Override - public ResourceLocation getTextureLocation(IEExplosiveEntity entity) + public ResourceLocation getTextureLocation(GunpowderBarrelEntity entity) { return InventoryMenu.BLOCK_ATLAS; } diff --git a/src/main/java/blusunrize/immersiveengineering/common/EventHandler.java b/src/main/java/blusunrize/immersiveengineering/common/EventHandler.java index a135977b3f..acfda662d1 100644 --- a/src/main/java/blusunrize/immersiveengineering/common/EventHandler.java +++ b/src/main/java/blusunrize/immersiveengineering/common/EventHandler.java @@ -96,7 +96,6 @@ public class EventHandler { - public static Map> currentExplosions = new WeakHashMap<>(); public static final Queue SERVER_TASKS = new ArrayDeque<>(); @SubscribeEvent @@ -175,19 +174,6 @@ public void onWorldTick(LevelTickEvent event) if(next!=null) next.run(); } - - final Set explosionsInLevel = currentExplosions.get(event.level); - if(explosionsInLevel!=null) - { - Iterator itExplosion = explosionsInLevel.iterator(); - while(itExplosion.hasNext()) - { - IEExplosion ex = itExplosion.next(); - ex.doExplosionTick(); - if(ex.isExplosionFinished) - itExplosion.remove(); - } - } } public static Map> crusherMap = new HashMap<>(); diff --git a/src/main/java/blusunrize/immersiveengineering/common/blocks/wooden/GunpowderBarrelBlock.java b/src/main/java/blusunrize/immersiveengineering/common/blocks/wooden/GunpowderBarrelBlock.java index 69dc812c25..a544f6b2cd 100644 --- a/src/main/java/blusunrize/immersiveengineering/common/blocks/wooden/GunpowderBarrelBlock.java +++ b/src/main/java/blusunrize/immersiveengineering/common/blocks/wooden/GunpowderBarrelBlock.java @@ -8,7 +8,7 @@ package blusunrize.immersiveengineering.common.blocks.wooden; -import blusunrize.immersiveengineering.common.entities.IEExplosiveEntity; +import blusunrize.immersiveengineering.common.entities.GunpowderBarrelEntity; import net.minecraft.core.BlockPos; import net.minecraft.core.Direction; import net.minecraft.sounds.SoundEvents; @@ -51,9 +51,12 @@ public int getFlammability(BlockState state, BlockGetter world, BlockPos pos, Di @Override public void onCaughtFire(BlockState state, Level world, BlockPos pos, @org.jetbrains.annotations.Nullable Direction face, @org.jetbrains.annotations.Nullable LivingEntity igniter) { - IEExplosiveEntity explosive = spawnExplosive(world, pos, state, igniter); - world.playSound(null, explosive.getX(), explosive.getY(), explosive.getZ(), SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1.0F, 1.0F); - world.removeBlock(pos, false); + if(!world.isClientSide) + { + GunpowderBarrelEntity explosive = spawnExplosive(world, pos, state, igniter); + world.playSound(null, explosive.getX(), explosive.getY(), explosive.getZ(), SoundEvents.TNT_PRIMED, SoundSource.BLOCKS, 1.0F, 1.0F); + world.removeBlock(pos, false); + } } @Override @@ -62,15 +65,14 @@ public void onBlockExploded(BlockState state, Level world, BlockPos pos, Explosi super.onBlockExploded(state, world, pos, explosion); if(!world.isClientSide) { - IEExplosiveEntity explosive = spawnExplosive(world, pos, state, explosion.getIndirectSourceEntity()); + GunpowderBarrelEntity explosive = spawnExplosive(world, pos, state, explosion.getIndirectSourceEntity()); explosive.setFuse((short)(world.random.nextInt(explosive.getFuse()/4)+explosive.getFuse()/8)); } } - private IEExplosiveEntity spawnExplosive(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity igniter) + private GunpowderBarrelEntity spawnExplosive(Level world, BlockPos pos, BlockState state, @Nullable LivingEntity igniter) { - IEExplosiveEntity explosive = new IEExplosiveEntity(world, pos, igniter, state, 5); - explosive.setDropChance(1); + GunpowderBarrelEntity explosive = new GunpowderBarrelEntity(world, pos, igniter, state, 8); world.addFreshEntity(explosive); return explosive; } diff --git a/src/main/java/blusunrize/immersiveengineering/common/entities/IEExplosiveEntity.java b/src/main/java/blusunrize/immersiveengineering/common/entities/GunpowderBarrelEntity.java similarity index 78% rename from src/main/java/blusunrize/immersiveengineering/common/entities/IEExplosiveEntity.java rename to src/main/java/blusunrize/immersiveengineering/common/entities/GunpowderBarrelEntity.java index e148e59869..5c9da07b71 100644 --- a/src/main/java/blusunrize/immersiveengineering/common/entities/IEExplosiveEntity.java +++ b/src/main/java/blusunrize/immersiveengineering/common/entities/GunpowderBarrelEntity.java @@ -9,7 +9,7 @@ package blusunrize.immersiveengineering.common.entities; import blusunrize.immersiveengineering.common.register.IEEntityTypes; -import blusunrize.immersiveengineering.common.util.IEExplosion; +import blusunrize.immersiveengineering.common.util.DirectionalMiningExplosion; import blusunrize.immersiveengineering.mixin.accessors.TNTEntityAccess; import net.minecraft.core.BlockPos; import net.minecraft.core.particles.ParticleTypes; @@ -35,29 +35,26 @@ import javax.annotation.Nonnull; -public class IEExplosiveEntity extends PrimedTnt +public class GunpowderBarrelEntity extends PrimedTnt { private float size; private Explosion.BlockInteraction mode = BlockInteraction.DESTROY; private boolean isFlaming = false; - private float explosionDropChance; public BlockState block; private Component name; - private static final EntityDataAccessor dataMarker_block = SynchedEntityData.defineId(IEExplosiveEntity.class, EntityDataSerializers.BLOCK_STATE); - private static final EntityDataAccessor dataMarker_fuse = SynchedEntityData.defineId(IEExplosiveEntity.class, EntityDataSerializers.INT); + private static final EntityDataAccessor dataMarker_block = SynchedEntityData.defineId(GunpowderBarrelEntity.class, EntityDataSerializers.BLOCK_STATE); + private static final EntityDataAccessor dataMarker_fuse = SynchedEntityData.defineId(GunpowderBarrelEntity.class, EntityDataSerializers.INT); - public IEExplosiveEntity(EntityType type, Level world) + public GunpowderBarrelEntity(EntityType type, Level world) { super(type, world); } - public IEExplosiveEntity(Level world, BlockPos pos, LivingEntity igniter, BlockState blockstate, float size) + public GunpowderBarrelEntity(Level world, BlockPos pos, LivingEntity igniter, BlockState blockstate, float size) { super(IEEntityTypes.EXPLOSIVE.get(), world); this.setPos(pos.getX()+.5, pos.getY()+.5, pos.getZ()+.5); - double jumpingDirection = world.random.nextDouble()*2*Math.PI; - this.setDeltaMovement(-Math.sin(jumpingDirection)*0.02D, 0.2, -Math.cos(jumpingDirection)*0.02D); this.setFuse(80); this.xo = getX(); this.yo = getY(); @@ -65,28 +62,21 @@ public IEExplosiveEntity(Level world, BlockPos pos, LivingEntity igniter, BlockS ((TNTEntityAccess)this).setOwner(igniter); this.size = size; this.block = blockstate; - this.explosionDropChance = 1/size; this.setBlockSynced(); } - public IEExplosiveEntity setMode(BlockInteraction smoke) + public GunpowderBarrelEntity setMode(BlockInteraction smoke) { this.mode = smoke; return this; } - public IEExplosiveEntity setFlaming(boolean fire) + public GunpowderBarrelEntity setFlaming(boolean fire) { this.isFlaming = fire; return this; } - public IEExplosiveEntity setDropChance(float chance) - { - this.explosionDropChance = chance; - return this; - } - @Override protected void defineSynchedData() { @@ -172,16 +162,15 @@ public void tick() } int newFuse = this.getFuse()-1; this.setFuse(newFuse); - if(newFuse <= 0) + if(newFuse < 0) { this.discard(); - - Explosion explosion = new IEExplosion(level(), this, getX(), getY()+(getBbHeight()/16f), getZ(), size, isFlaming, mode) - .setDropChance(explosionDropChance); - if(!EventHooks.onExplosionStart(level(), explosion)) - { - explosion.explode(); - explosion.finalizeExplosion(true); + if(!this.level().isClientSide()) { + Explosion explosion = new DirectionalMiningExplosion(level(), this, getX(), getY(), getZ(), isFlaming); + if (!EventHooks.onExplosionStart(level(), explosion)) { + explosion.explode(); + explosion.finalizeExplosion(true); + } } } else diff --git a/src/main/java/blusunrize/immersiveengineering/common/register/IEEntityTypes.java b/src/main/java/blusunrize/immersiveengineering/common/register/IEEntityTypes.java index 4676e803d8..37be07326c 100644 --- a/src/main/java/blusunrize/immersiveengineering/common/register/IEEntityTypes.java +++ b/src/main/java/blusunrize/immersiveengineering/common/register/IEEntityTypes.java @@ -21,7 +21,6 @@ import net.neoforged.neoforge.registries.DeferredHolder; import net.neoforged.neoforge.registries.DeferredRegister; import net.minecraft.core.registries.BuiltInRegistries; -import net.minecraft.core.Holder; import java.util.function.Supplier; @@ -40,9 +39,9 @@ public class IEEntityTypes () -> Builder.of(FluorescentTubeEntity::new, MobCategory.MISC) .sized(FluorescentTubeEntity.TUBE_LENGTH/2, 1+FluorescentTubeEntity.TUBE_LENGTH/2) ); - public static final DeferredHolder, EntityType> EXPLOSIVE = register( + public static final DeferredHolder, EntityType> EXPLOSIVE = register( "explosive", - () -> Builder.of(IEExplosiveEntity::new, MobCategory.MISC) + () -> Builder.of(GunpowderBarrelEntity::new, MobCategory.MISC) .fireImmune() .sized(0.98F, 0.98F) ); diff --git a/src/main/java/blusunrize/immersiveengineering/common/util/DirectionalMiningExplosion.java b/src/main/java/blusunrize/immersiveengineering/common/util/DirectionalMiningExplosion.java new file mode 100644 index 0000000000..ec5539fd29 --- /dev/null +++ b/src/main/java/blusunrize/immersiveengineering/common/util/DirectionalMiningExplosion.java @@ -0,0 +1,258 @@ +/* + * BluSunrize + * Copyright (c) 2017 + * + * This code is licensed under "Blu's License of Common Sense" + * Details can be found in the license file in the root folder of this project + */ + +package blusunrize.immersiveengineering.common.util; + +import blusunrize.immersiveengineering.api.ApiUtils; +import blusunrize.immersiveengineering.mixin.accessors.ExplosionAccess; +import com.mojang.datafixers.util.Pair; +import it.unimi.dsi.fastutil.objects.ObjectArrayList; +import net.minecraft.core.BlockPos; +import net.minecraft.core.Direction; +import net.minecraft.core.particles.ParticleTypes; +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.LivingEntity; +import net.minecraft.world.entity.item.ItemEntity; +import net.minecraft.world.item.ItemStack; +import net.minecraft.world.item.enchantment.ProtectionEnchantment; +import net.minecraft.world.level.Explosion; +import net.minecraft.world.level.Level; +import net.minecraft.world.level.block.Block; +import net.minecraft.world.level.block.Blocks; +import net.minecraft.world.level.block.entity.BlockEntity; +import net.minecraft.world.level.block.state.BlockState; +import net.minecraft.world.level.material.FluidState; +import net.minecraft.world.level.storage.loot.LootParams; +import net.minecraft.world.level.storage.loot.parameters.LootContextParams; +import net.minecraft.world.phys.AABB; +import net.minecraft.world.phys.Vec3; + +import java.util.*; + +public class DirectionalMiningExplosion extends Explosion +{ + private static final int SIZE = 8; + private static final int SCAN = SIZE-1; + private static final float BLASTING_LENGTH = 150; + private static final float SUBSURFACE_LENGTH = 300; + private static final int THIRD_VOLUME = (int)((1.0/3)*(4.0/3)*Math.PI*SIZE*SIZE*SIZE); + private static final int MIN_AIR = 3; + private static final float MAX_SHOCKWAVE_RESISTANCE = 0.4f; + private static final float MAX_SURFACE_RESISTANCE = 1.75f; + private static final float MAX_SUBSURFACE_RESISTANCE = 6f; + private static final float MAX_BLASTING_RESISTANCE = 25f; + + private final Level world; + private final DamageSource damageSource; + + /** + * This explosion type is a bit special because it has a constant, tuned size to behave like a mining explosive. + * It is NOT INTENDED for any other use than with the gunpowder barrel. + * It WILL behave unpredictably with larger sizes, so user beware if they decide to customize it! + */ + public DirectionalMiningExplosion(Level world, Entity igniter, double x, double y, double z, boolean isFlaming) + { + super(world, igniter, x, y, z, SIZE, isFlaming, BlockInteraction.KEEP); + this.world = world; + this.damageSource = world.damageSources().explosion(this); + } + + @Override + /** + * This method is the entry method for starting the explosion, and does most of the pre-explosion calculation to assess explosion dynamics. + * First, a spherical area is scanned around the entity, and four properties are collected: + * 1. The total number of blocks in the area + * 2. The total blast resistances of blocks in the area + * 3. The sum of the vectors of all blocks in area of the form blast resistance divided by the distance from the center + * 4. The sum of the vectors of all blocks in area of the form one divided by the distance from the center + * These properties are then composed into a vector in which the explosion should propagate + * Finally, a subtype (surface, subsurface, blasting) of explosion is selected based on these parameters, and DirectionalMiningExplosion#stagedExplosionDetonation() is called + */ + public void explode() + { + // variables used for the rest of the explosion + Vec3 center = center(); + BlockPos centerBlock = new BlockPos((int)(center.x-0.5f), (int)(center.y-0.5f), (int)(center.z-0.5f)); + // iteration to identify the basic characteristics of the explosion + // variables collated during the iteration + int totalBlocks = 0; + Vec3 openSpace = new Vec3(0,0, 0); + BlockState cBlock; + FluidState cFluid; + // iterate over an area of size (2*(power-1)+1)^3 and collect the resistance of blocks in a sphere around our center + for (int x=-SCAN;x<=SCAN;x++) + for (int y=-SCAN;y<=SCAN;y++) + for (int z=-SCAN;z<=SCAN;z++) + { + BlockPos pos = centerBlock.offset(x, y, z); + if (new Vec3(x, y, z).length()<=SCAN) + { + cBlock = world.getBlockState(pos); + cFluid = world.getFluidState(pos); + if(!cBlock.isAir()||!cFluid.isEmpty()) + totalBlocks+=1; + if(cBlock.canBeReplaced()&&cFluid.isEmpty()) + openSpace = openSpace.add(x == 0 ? 0 : 1.0 / x, y == 0 ? 0 : 1.0 / y, z == 0 ? 0 : 1.0 / z); + } + } + // establish the weakest direction and the length of the explosive step we should be taking + Vec3 step = openSpace.scale((0.5*SIZE+1-Math.sqrt(openSpace.length()/SIZE))/openSpace.length()); + // handle explosion based on criteria for explosions: either surface, subsurface, or blasting + int air = checkAir(centerBlock); + if(air=THIRD_VOLUME) + stagedExplosionDetonation(centerBlock, step, 0.4f * SIZE, 0.4f * SIZE, MAX_BLASTING_RESISTANCE, true); + else if(air<=MIN_AIR&&openSpace.length()=THIRD_VOLUME) + stagedExplosionDetonation(centerBlock, null, 3, SIZE*1.25f, MAX_SUBSURFACE_RESISTANCE, false); + else + stagedExplosionDetonation(centerBlock, null, 2, SIZE*2, MAX_SURFACE_RESISTANCE, false); + } + + /** + * Actuates a directional explosion detonation using parameters calculated in DirectionalMiningExplosion#explode(). + * Multiple spheres of blocks are broken to correspond to different parts of the explosion, including a shockwave. + * Blasting explosions create a 'cone' of removed material via two separate removal spheres + * @param center BlockPos position that the center of the explosion is at + * @param step Vec3 vector that the staged block break steps should move each iteration + * @param crater float radius that the crater block break step should break out to + * @param shockwave float radius of the shockwave step + * @param resistance float maximum blast resistance the explosion can remove blocks of + * @param blasting boolean whether the explosion should treat itself as a blasting explosion or a shockwave + */ + private void stagedExplosionDetonation(BlockPos center, Vec3 step, float crater, float shockwave, float resistance, boolean blasting) + { + // handle shockwave and crater block damage that come with any explosion + int shock = (int)shockwave; + for(int x = -shock; x <= shock; x++) + for(int y = -shock; y <= shock; y++) + for(int z = -shock; z <= shock; z++) + { + double length = Math.sqrt(x*x+y*y+z*z); + if (length damage = new ArrayList<>(world.getEntities(this.getDirectSourceEntity(), + new AABB(center.getX()-shock, center.getY()-shock, center.getZ()-shock, + center.getX()+shock, center.getY()+shock, center.getZ()+shock))); + damage = damage.stream().filter(e -> center().distanceTo(e.position())<=shock).filter(e -> !(e instanceof ItemEntity)).toList(); + damageEntities(damage, shockwave/SIZE); + // handle directional explosions that come with a buried explosive barrel + if(blasting) + { + // first explosion propagation sphere + BlockPos centerOffset = center.offset((int)step.x(), (int)step.y(), (int)step.z()); + int blast1 = (int)(crater*1.25f); + for(int x = -blast1; x <= blast1; x++) + for(int y = -blast1; y <= blast1; y++) + for(int z = -blast1; z <= blast1; z++) + { + int length = (int)Math.sqrt(x*x+y*y+z*z); + if (length> objectarraylist = new ObjectArrayList<>(); + BlockState state = this.world.getBlockState(pos); + + if(!state.isAir()&&state.getExplosionResistance(world, pos, this)<=resistance&&ApiUtils.RANDOM.nextFloat()>chance) + { + if(this.world instanceof ServerLevel&&state.canDropFromExplosion(this.world, pos, this)) + { + BlockEntity tile = this.world.getBlockEntity(pos); + LootParams.Builder lootCtx = new LootParams.Builder((ServerLevel)this.world) + .withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos)) + .withParameter(LootContextParams.TOOL, ItemStack.EMPTY) + .withOptionalParameter(LootContextParams.BLOCK_ENTITY, tile); + state.getDrops(lootCtx).forEach((stack) -> { + ExplosionAccess.callAddOrAppendStack(objectarraylist, stack, pos); + }); + state.onBlockExploded(world, pos, this); + } + } + for(Pair pair : objectarraylist) + Block.popResource(this.world, pair.getSecond(), pair.getFirst()); + } + + /* + * This code is copied and modified from the base explosion class because I don't care about fine-tuning it for a mining explosive + */ + private void damageEntities(List list, float intensity) + { + net.neoforged.neoforge.event.EventHooks.onExplosionDetonate(this.world, this, list, SIZE*2); + for(Entity entity : list) + if(!entity.ignoreExplosion(this)) + { + // relative distance + double x = entity.getX()-center().x(); + double y = entity.getY()+entity.getBbHeight()/2-center().y(); + double z = entity.getZ()-center().z(); + float length = (float)Math.sqrt(x*x+y*y+z*z); + x = (x/length)*(x/length); + y = (y/length)*(y/length); + z = (z/length)*(z/length); + // other useful variables + float exposed = getSeenPercent(center(), entity); + float damage = exposed*(float)((SIZE*SIZE*SIZE)/(4*Math.PI*length*length))*intensity; + double knockback = (entity instanceof LivingEntity living)?ProtectionEnchantment.getExplosionKnockbackAfterDampener(living, damage): damage; + // actually do damage & knockback + entity.hurt(damageSource, damage); + entity.setDeltaMovement(entity.getDeltaMovement().add(knockback/(x*x), knockback/(y*y), knockback/(z*z))); + } + } + + private int checkAir(BlockPos pos) + { + int air = 0; + for (Direction direction : Direction.values()) + air += world.getBlockState(pos.relative(direction)).getExplosionResistance(world, pos.relative(direction), this)> objectarraylist = new ObjectArrayList<>(); - int max = Math.min(blockDestroyInt+blocksPerTick, this.getToBlow().size()); - for(; blockDestroyInt < max; blockDestroyInt++) - { - BlockPos pos = this.getToBlow().get(blockDestroyInt); - BlockState state = this.world.getBlockState(pos); - Block block = state.getBlock(); - -// if(spawnParticles) - { - var center = center(); - double d0 = (float)pos.getX()+ApiUtils.RANDOM.nextFloat(); - double d1 = (float)pos.getY()+ApiUtils.RANDOM.nextFloat(); - double d2 = (float)pos.getZ()+ApiUtils.RANDOM.nextFloat(); - double d3 = d0-center.x; - double d4 = d1-center.y; - double d5 = d2-center.z; - double d6 = Mth.sqrt((float)(d3*d3+d4*d4+d5*d5)); - d3 = d3/d6; - d4 = d4/d6; - d5 = d5/d6; - double d7 = 0.5D/(d6/(double)this.size+0.1D); - d7 = d7*(double)(ApiUtils.RANDOM.nextFloat()*ApiUtils.RANDOM.nextFloat()+0.3F); - d3 = d3*d7; - d4 = d4*d7; - d5 = d5*d7; - this.world.addParticle(ParticleTypes.EXPLOSION, (d0+center.x*1.0D)/2.0D, (d1+center.y*1.0D)/2.0D, (d2+center.z*1.0D)/2.0D, d3, d4, d5); - this.world.addParticle(ParticleTypes.SMOKE, d0, d1, d2, d3, d4, d5); - } - - if(!state.isAir()) - { - if(this.world instanceof ServerLevel&&state.canDropFromExplosion(this.world, pos, this)) - { - BlockEntity tile = this.world.getBlockEntity(pos); - LootParams.Builder lootCtx = new LootParams.Builder((ServerLevel)this.world) - .withParameter(LootContextParams.ORIGIN, Vec3.atCenterOf(pos)) - .withParameter(LootContextParams.TOOL, ItemStack.EMPTY) - .withOptionalParameter(LootContextParams.BLOCK_ENTITY, tile); - if(damagesTerrain==Explosion.BlockInteraction.DESTROY) - lootCtx.withParameter(LootContextParams.EXPLOSION_RADIUS, this.size); - state.getDrops(lootCtx).forEach((stack) -> { - ExplosionAccess.callAddOrAppendStack(objectarraylist, stack, pos); - }); - state.onBlockExploded(world, pos, this); - } - } - } - for(Pair pair : objectarraylist) - { - Block.popResource(this.world, pair.getSecond(), pair.getFirst()); - } - if(blockDestroyInt >= this.getToBlow().size()) - this.isExplosionFinished = true; - } - - @Override - public void explode() - { - Set set = Sets.newHashSet(); - int i = 16; - - for(int j = 0; j < 16; ++j) - for(int k = 0; k < 16; ++k) - for(int l = 0; l < 16; ++l) - if(j==0||j==15||k==0||k==15||l==0||l==15) - { - double d0 = (float)j/15.0F*2.0F-1.0F; - double d1 = (float)k/15.0F*2.0F-1.0F; - double d2 = (float)l/15.0F*2.0F-1.0F; - double d3 = Math.sqrt(d0*d0+d1*d1+d2*d2); - d0 = d0/d3; - d1 = d1/d3; - d2 = d2/d3; - float f = this.size*(0.7F+ApiUtils.RANDOM.nextFloat()*0.6F); - double d4 = center().x; - double d6 = center().y; - double d8 = center().z; - - for(float f1 = 0.3F; f > 0.0F; f -= 0.22500001F) - { - BlockPos blockpos = BlockPos.containing(d4, d6, d8); - BlockState iblockstate = this.world.getBlockState(blockpos); - FluidState ifluidstate = this.world.getFluidState(blockpos); - if(!iblockstate.isAir()||!ifluidstate.isEmpty()) - { - float f2 = Math.max(iblockstate.getExplosionResistance(world, blockpos, this), ifluidstate.getExplosionResistance(world, blockpos, this)); - if(this.getDirectSourceEntity()!=null) - { - f2 = this.getDirectSourceEntity().getBlockExplosionResistance(this, this.world, blockpos, iblockstate, ifluidstate, f2); - } - - f -= (f2+0.3F)*0.3F; - } - - if(f > 0.0F&&(this.getDirectSourceEntity()==null||this.getDirectSourceEntity().shouldBlockExplode(this, this.world, blockpos, iblockstate, f))) - { - set.add(blockpos); - } - - d4 += d0*(double)0.3F; - d6 += d1*(double)0.3F; - d8 += d2*(double)0.3F; - } - } - - this.getToBlow().addAll(set); - Vec3 center = center(); - this.getToBlow().sort(Comparator.comparingDouble(pos -> pos.distToCenterSqr(center))); - - float f3 = this.size*2.0F; - int k1 = Mth.floor(center.x-(double)f3-1.0D); - int l1 = Mth.floor(center.x+(double)f3+1.0D); - int i2 = Mth.floor(center.y-(double)f3-1.0D); - int i1 = Mth.floor(center.y+(double)f3+1.0D); - int j2 = Mth.floor(center.z-(double)f3-1.0D); - int j1 = Mth.floor(center.z+(double)f3+1.0D); - List list = this.world.getEntities(this.getDirectSourceEntity(), new AABB(k1, i2, j2, l1, i1, j1)); - net.neoforged.neoforge.event.EventHooks.onExplosionDetonate(this.world, this, list, f3); - Vec3 vec3 = new Vec3(center.x, center.y, center.z); - - for(int k2 = 0; k2 < list.size(); ++k2) - { - Entity entity = list.get(k2); - if(!entity.ignoreExplosion(this)) - { - double d12 = entity.position() - .distanceToSqr(center.x, center.y, center.z)/(double)f3; - if(d12 <= 1.0D) - { - double d5 = entity.getX()-center.x; - double d7 = entity.getY()+(double)entity.getEyeHeight()-center.y; - double d9 = entity.getZ()-center.z; - double d13 = Mth.sqrt((float)(d5*d5+d7*d7+d9*d9)); - if(d13!=0.0D) - { - d5 = d5/d13; - d7 = d7/d13; - d9 = d9/d13; - double d14 = getSeenPercent(vec3, entity); - double d10 = (1.0D-d12)*d14; - entity.hurt(entity.damageSources().explosion(this), (float)((int)((d10*d10+d10)/2.0D*8.0D*(double)f3+1.0D))); - double d11 = entity instanceof LivingEntity?ProtectionEnchantment.getExplosionKnockbackAfterDampener((LivingEntity)entity, d10): d10; - entity.setDeltaMovement(entity.getDeltaMovement().add(d5*d11, - d7*d11, - d9*d11)); - if(entity instanceof Player&&!((Player)entity).getAbilities().invulnerable) - this.getHitPlayers().put((Player)entity, new Vec3(d5*d10, d7*d10, d9*d10)); - } - } - } - } - } - - @Override - public void finalizeExplosion(boolean spawnParticles) - { - Vec3 pos = center(); - if(this.world.isClientSide) - this.world.playLocalSound(pos.x, pos.y, pos.z, SoundEvents.GENERIC_EXPLODE, SoundSource.NEUTRAL, 4.0F, (1.0F+(ApiUtils.RANDOM.nextFloat()-ApiUtils.RANDOM.nextFloat())*0.2F)*0.7F, true); - - if(this.size >= 2.0F&&this.damagesTerrain!=BlockInteraction.KEEP) - this.world.addParticle(ParticleTypes.EXPLOSION_EMITTER, pos.x, pos.y, pos.z, 1.0D, 0.0D, 0.0D); - else - this.world.addParticle(ParticleTypes.EXPLOSION, pos.x, pos.y, pos.z, 1.0D, 0.0D, 0.0D); - - if(!this.world.isClientSide) - EventHandler.currentExplosions.computeIfAbsent(this.world, $ -> new HashSet<>()).add(this); - } -} \ No newline at end of file