From 753f953dfe91a1eb391c24af9498635c2d96a716 Mon Sep 17 00:00:00 2001 From: Voidsong Dragonfly Date: Sun, 30 Jun 2024 15:24:02 -0400 Subject: [PATCH 1/3] Basic implementation of gunpowder barrel explosions - Powder barrel explosion is now directional - Powder barrel explosion breaks more blocks - Powder barrel explosion keeps all blocks - Powder barrel explosion is instantaneous - Powder barrel explosion breaks more blocks when it is submerged in a rock face and not up top --- .../render/entity/IEExplosiveRenderer.java | 9 +- .../common/EventHandler.java | 14 -- .../blocks/wooden/GunpowderBarrelBlock.java | 11 +- ...Entity.java => GunpowderBarrelEntity.java} | 28 +-- .../common/register/IEEntityTypes.java | 5 +- ...n.java => DirectionalMiningExplosion.java} | 205 +++++++++++------- 6 files changed, 146 insertions(+), 126 deletions(-) rename src/main/java/blusunrize/immersiveengineering/common/entities/{IEExplosiveEntity.java => GunpowderBarrelEntity.java} (83%) rename src/main/java/blusunrize/immersiveengineering/common/util/{IEExplosion.java => DirectionalMiningExplosion.java} (54%) 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..2deaa943cb 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,7 +51,7 @@ 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); + 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); } @@ -62,15 +62,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 83% rename from src/main/java/blusunrize/immersiveengineering/common/entities/IEExplosiveEntity.java rename to src/main/java/blusunrize/immersiveengineering/common/entities/GunpowderBarrelEntity.java index e148e59869..89bf1cc1a5 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,24 +35,23 @@ 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); @@ -65,28 +64,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() { @@ -175,9 +167,7 @@ public void tick() if(newFuse <= 0) { this.discard(); - - Explosion explosion = new IEExplosion(level(), this, getX(), getY()+(getBbHeight()/16f), getZ(), size, isFlaming, mode) - .setDropChance(explosionDropChance); + Explosion explosion = new DirectionalMiningExplosion(level(), this, getX(), getY()+(getBbHeight()/16f), getZ(), size, isFlaming); if(!EventHooks.onExplosionStart(level(), explosion)) { explosion.explode(); 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/IEExplosion.java b/src/main/java/blusunrize/immersiveengineering/common/util/DirectionalMiningExplosion.java similarity index 54% rename from src/main/java/blusunrize/immersiveengineering/common/util/IEExplosion.java rename to src/main/java/blusunrize/immersiveengineering/common/util/DirectionalMiningExplosion.java index 8855bf9554..3f91aaf25d 100644 --- a/src/main/java/blusunrize/immersiveengineering/common/util/IEExplosion.java +++ b/src/main/java/blusunrize/immersiveengineering/common/util/DirectionalMiningExplosion.java @@ -15,6 +15,7 @@ import com.mojang.datafixers.util.Pair; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.core.BlockPos; +import net.minecraft.core.Vec3i; import net.minecraft.core.particles.ParticleTypes; import net.minecraft.server.level.ServerLevel; import net.minecraft.sounds.SoundEvents; @@ -28,6 +29,7 @@ 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; @@ -41,95 +43,27 @@ import java.util.List; import java.util.Set; -public class IEExplosion extends Explosion +public class DirectionalMiningExplosion extends Explosion { - public float dropChance = 1; - private int blockDestroyInt = 0; - public int blocksPerTick = 8; - public boolean isExplosionFinished = false; private final Level world; private final float size; - private final BlockInteraction damagesTerrain; + private final Set remove = new HashSet<>(); - public IEExplosion(Level world, Entity igniter, double x, double y, double z, float size, boolean isFlaming, BlockInteraction damageTerrain) + public DirectionalMiningExplosion(Level world, Entity igniter, double x, double y, double z, float size, boolean isFlaming) { - super(world, igniter, x, y, z, size, isFlaming, damageTerrain); - this.dropChance = 1/size; + super(world, igniter, x, y, z, size, isFlaming, BlockInteraction.KEEP); this.world = world; - damagesTerrain = damageTerrain; this.size = size; } - - public IEExplosion setDropChance(float chance) - { - this.dropChance = chance; - return this; - } - - public void doExplosionTick() - { - ObjectArrayList> 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) @@ -220,6 +154,122 @@ public void explode() } } } + }*/ + + @Override + public void explode() + { + // variables used for the rest of the explosion + int power = (int)size; + Vec3 center = center(); + BlockPos centerBlock = new BlockPos((int)(center.x)+(center.x<0?-1:0), (int)center.y, (int)(center.z)+(center.z<0?-1:0)); + + // variables collated during the iteration + Set area = new HashSet<>(); + double totalResistance = 0; + Vec3 weakestDirection = new Vec3(0,0, 0); + // to be used and reused in the iteration + BlockState cBlock; + FluidState cFluid; + // iterate over an area of size (2*power+1)^3 and collect the resistance of blocks in a sphere around our center + for (int x=-power;x<=power;x++) + for (int y=-power;y<=power;y++) + for (int z=-power;z<=power;z++) + { + BlockPos pos = centerBlock.offset(x, y, z); + area.add(pos); + if (new Vec3(x, y, z).length()<=power) + { + cBlock = world.getBlockState(pos); + cFluid = world.getFluidState(pos); + if(!cBlock.isAir()||!cFluid.isEmpty()) + { + double resistance = cBlock.getExplosionResistance(world, pos, this)+cFluid.getExplosionResistance(world, pos, this); + totalResistance += resistance; + weakestDirection = weakestDirection.add(x==0?0:resistance/x, y==0?0:resistance/y, z==0?0:resistance/z); + } + } + } + + // establish the weakest direction and the length of the explosive step we should be taking + weakestDirection = weakestDirection.reverse(); + Vec3 step = weakestDirection.scale(30*size/totalResistance); + + // offset center to be used in other calculations + BlockPos centerOffset; + // if step length is high we have a "surface burst" and the explosion dynamics should be different + if (step.length()>size*5) + { + // offset explosion by fixed distance proportional to size + step = step.normalize().scale(size/4); + centerOffset = centerBlock.offset((int)step.x, (int)step.y, (int)step.z); + int r = (int)(0.6f*power); + for(int x = -r; x <= r; x++) + for(int y = -r; y <= r; y++) + for(int z = -r; z <= r; z++) + addToRemoveRandom(centerOffset.offset(x, y, z), r-1, r, (new Vec3(x, y, z)).length(), 0.1f); + } + // otherwise proceed with embedded explosion dynamics + else + { + // if we have a step length greater than ~0.6 power we need to scale it down a little bit because we still have penetration + if (step.length()>size*0.6) step = step.scale(1.75/Math.pow(step.length(), 0.7f)); + // if we have a step length smaller than ~0.225 power but aren't on a surface explosion, we should enhance it + else if (step.length()radius&&radius>inner&&ApiUtils.RANDOM.nextFloat()>chance) remove.add(pos); + } + + private void removeExplodedBlock(BlockPos pos) + { + ObjectArrayList> objectarraylist = new ObjectArrayList<>(); + BlockState state = this.world.getBlockState(pos); + + 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); + 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()); } @Override @@ -229,12 +279,9 @@ public void finalizeExplosion(boolean spawnParticles) 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) + if(this.size >= 2.0F) 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 From 2090cb329c14035f03d5ea0f6f35f60b68e57107 Mon Sep 17 00:00:00 2001 From: Voidsong Dragonfly Date: Mon, 1 Jul 2024 23:21:39 -0400 Subject: [PATCH 2/3] Make sure powder barrel explosion fires only once --- .../common/entities/GunpowderBarrelEntity.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/blusunrize/immersiveengineering/common/entities/GunpowderBarrelEntity.java b/src/main/java/blusunrize/immersiveengineering/common/entities/GunpowderBarrelEntity.java index 89bf1cc1a5..76cba7ebb4 100644 --- a/src/main/java/blusunrize/immersiveengineering/common/entities/GunpowderBarrelEntity.java +++ b/src/main/java/blusunrize/immersiveengineering/common/entities/GunpowderBarrelEntity.java @@ -164,7 +164,7 @@ public void tick() } int newFuse = this.getFuse()-1; this.setFuse(newFuse); - if(newFuse <= 0) + if(newFuse < 0) { this.discard(); Explosion explosion = new DirectionalMiningExplosion(level(), this, getX(), getY()+(getBbHeight()/16f), getZ(), size, isFlaming); From 4caafb1da413119e5ed78329aa441ad19a351356 Mon Sep 17 00:00:00 2001 From: Voidsong Dragonfly Date: Thu, 4 Jul 2024 01:34:57 -0400 Subject: [PATCH 3/3] Theoretical final working order for the barrels - Shockwave now propagates appropriately - Three modes: surface, subsurface, blasting - Surface is mostly shockwave - Subsurface is a mix - Blasting is mostly block removal - Still many bugs to iron out! --- .../entities/GunpowderBarrelEntity.java | 2 +- .../util/DirectionalMiningExplosion.java | 297 +++++++++--------- 2 files changed, 154 insertions(+), 145 deletions(-) diff --git a/src/main/java/blusunrize/immersiveengineering/common/entities/GunpowderBarrelEntity.java b/src/main/java/blusunrize/immersiveengineering/common/entities/GunpowderBarrelEntity.java index 76cba7ebb4..5c40104081 100644 --- a/src/main/java/blusunrize/immersiveengineering/common/entities/GunpowderBarrelEntity.java +++ b/src/main/java/blusunrize/immersiveengineering/common/entities/GunpowderBarrelEntity.java @@ -167,7 +167,7 @@ public void tick() if(newFuse < 0) { this.discard(); - Explosion explosion = new DirectionalMiningExplosion(level(), this, getX(), getY()+(getBbHeight()/16f), getZ(), size, isFlaming); + Explosion explosion = new DirectionalMiningExplosion(level(), this, getX(), getY()+(getBbHeight()/16f), getZ(), isFlaming); if(!EventHooks.onExplosionStart(level(), explosion)) { explosion.explode(); diff --git a/src/main/java/blusunrize/immersiveengineering/common/util/DirectionalMiningExplosion.java b/src/main/java/blusunrize/immersiveengineering/common/util/DirectionalMiningExplosion.java index 3f91aaf25d..75ce95b676 100644 --- a/src/main/java/blusunrize/immersiveengineering/common/util/DirectionalMiningExplosion.java +++ b/src/main/java/blusunrize/immersiveengineering/common/util/DirectionalMiningExplosion.java @@ -9,27 +9,24 @@ package blusunrize.immersiveengineering.common.util; import blusunrize.immersiveengineering.api.ApiUtils; -import blusunrize.immersiveengineering.common.EventHandler; import blusunrize.immersiveengineering.mixin.accessors.ExplosionAccess; -import com.google.common.collect.Sets; import com.mojang.datafixers.util.Pair; import it.unimi.dsi.fastutil.objects.ObjectArrayList; import net.minecraft.core.BlockPos; -import net.minecraft.core.Vec3i; +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.util.Mth; +import net.minecraft.world.damagesource.DamageSource; import net.minecraft.world.entity.Entity; import net.minecraft.world.entity.LivingEntity; -import net.minecraft.world.entity.player.Player; +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; @@ -38,163 +35,90 @@ import net.minecraft.world.phys.AABB; import net.minecraft.world.phys.Vec3; -import java.util.Comparator; -import java.util.HashSet; -import java.util.List; -import java.util.Set; +import java.util.*; public class DirectionalMiningExplosion extends Explosion { + private static final int SIZE = 8; + private static final float BLASTING_LENGTH = 525; + private static final float SUBSURFACE_LENGTH = 700; + private static final float SUBSURFACE_RESISTANCE = 1500; + 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 float size; - private final Set remove = new HashSet<>(); + private final DamageSource damageSource; - public DirectionalMiningExplosion(Level world, Entity igniter, double x, double y, double z, float size, boolean isFlaming) + /** + * 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); + super(world, igniter, x, y, z, SIZE, isFlaming, BlockInteraction.KEEP); this.world = world; - this.size = size; + this.damageSource = world.damageSources().explosion(this); } -/* - @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 explode() { // variables used for the rest of the explosion - int power = (int)size; Vec3 center = center(); BlockPos centerBlock = new BlockPos((int)(center.x)+(center.x<0?-1:0), (int)center.y, (int)(center.z)+(center.z<0?-1:0)); - + // iteration to identify the basic characteristics of the explosion // variables collated during the iteration - Set area = new HashSet<>(); double totalResistance = 0; - Vec3 weakestDirection = new Vec3(0,0, 0); - // to be used and reused in the iteration + Vec3 weaknesses = new Vec3(0,0, 0); BlockState cBlock; FluidState cFluid; // iterate over an area of size (2*power+1)^3 and collect the resistance of blocks in a sphere around our center - for (int x=-power;x<=power;x++) - for (int y=-power;y<=power;y++) - for (int z=-power;z<=power;z++) + for (int x=-SIZE;x<=SIZE;x++) + for (int y=-SIZE;y<=SIZE;y++) + for (int z=-SIZE;z<=SIZE;z++) { BlockPos pos = centerBlock.offset(x, y, z); - area.add(pos); - if (new Vec3(x, y, z).length()<=power) + if (new Vec3(x, y, z).length()<=SIZE) { cBlock = world.getBlockState(pos); cFluid = world.getFluidState(pos); if(!cBlock.isAir()||!cFluid.isEmpty()) { - double resistance = cBlock.getExplosionResistance(world, pos, this)+cFluid.getExplosionResistance(world, pos, this); + float resistance = cBlock.getExplosionResistance(world, pos, this)+cFluid.getExplosionResistance(world, pos, this); totalResistance += resistance; - weakestDirection = weakestDirection.add(x==0?0:resistance/x, y==0?0:resistance/y, z==0?0:resistance/z); + weaknesses = weaknesses.add(x==0?0:resistance/x, y==0?0:resistance/y, z==0?0:resistance/z); } } } - // establish the weakest direction and the length of the explosive step we should be taking - weakestDirection = weakestDirection.reverse(); - Vec3 step = weakestDirection.scale(30*size/totalResistance); + weaknesses = weaknesses.reverse(); + // handle explosion based on criteria for explosions: either surface, subsurface, or blasting + int air = checkAir(centerBlock); + if(air=SUBSURFACE_RESISTANCE) + stagedExplosionDetonation(centerBlock, null, 3, SIZE*1.25f, MAX_SUBSURFACE_RESISTANCE, false); + else + stagedExplosionDetonation(centerBlock, null, 2, SIZE*2, MAX_SURFACE_RESISTANCE, false); + + + + + //Vec3 step = weaknesses.scale(30*size/totalResistance); +/* + // find entities in range and set them into the list to damage them + // TODO: this should scale with 1/explosion radius + double shock = 0.75f*size; + List damage = new ArrayList<>(world.getEntities(this.getDirectSourceEntity(), + new AABB(centerBlock.getX()-shock, centerBlock.getY()-shock, centerBlock.getZ()-shock, + centerBlock.getX()+shock, centerBlock.getY()+shock, centerBlock.getZ()+shock))); + // filter for radius, then filter out items + damage = damage.stream().filter(e -> center.distanceTo(e.position())<=shock).filter(e -> !(e instanceof ItemEntity)).toList(); // offset center to be used in other calculations BlockPos centerOffset; // if step length is high we have a "surface burst" and the explosion dynamics should be different @@ -212,11 +136,11 @@ public void explode() // otherwise proceed with embedded explosion dynamics else { - // if we have a step length greater than ~0.6 power we need to scale it down a little bit because we still have penetration += // if we have a step length greater than ~0.6 power we need to scale it down a little bit because we still have penetration if (step.length()>size*0.6) step = step.scale(1.75/Math.pow(step.length(), 0.7f)); // if we have a step length smaller than ~0.225 power but aren't on a surface explosion, we should enhance it else if (step.length()radius&&radius>inner&&ApiUtils.RANDOM.nextFloat()>chance) remove.add(pos); + // 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) + { + //scale weakness shatter vector into something to 'step' explosions by + if (step.length()>SIZE*0.6) + step = step.scale(1.75/Math.pow(step.length(), 0.7f)); + else if (step.length()> objectarraylist = new ObjectArrayList<>(); BlockState state = this.world.getBlockState(pos); - if(!state.isAir()) + if(!state.isAir()&&state.getExplosionResistance(world, pos, this)<=resistance&&ApiUtils.RANDOM.nextFloat()>chance) { if(this.world instanceof ServerLevel&&state.canDropFromExplosion(this.world, pos, this)) { @@ -272,16 +250,47 @@ private void removeExplodedBlock(BlockPos pos) 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)= 2.0F) - 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); + this.world.addParticle(ParticleTypes.EXPLOSION_EMITTER, pos.x, pos.y, pos.z, 1.0D, 0.0D, 0.0D); } } \ No newline at end of file