diff --git a/src/main/java/com/vinlanx/explosionoverhaul/ExplosionClusterHandler.java b/src/main/java/com/vinlanx/explosionoverhaul/ExplosionClusterHandler.java index a61ff52..b43db33 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/ExplosionClusterHandler.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/ExplosionClusterHandler.java @@ -1,9 +1,15 @@ package com.vinlanx.explosionoverhaul; +import java.util.ArrayList; +import java.util.List; import net.minecraft.core.BlockPos; +import net.minecraft.world.entity.EntityType; import net.minecraft.world.entity.item.PrimedTnt; +import net.minecraft.world.entity.monster.Creeper; +import net.minecraft.world.entity.vehicle.AbstractMinecart; 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.phys.AABB; import net.minecraft.world.phys.Vec3; @@ -12,18 +18,109 @@ public class ExplosionClusterHandler { if (!((Boolean)Config.COMMON.enableExplosionClustering.get()).booleanValue()) { return initialPower; } - int nearbyExplosives = 0; + double searchRadius = getSearchRadius(initialPower); + AABB searchBox = new AABB(pos.x - searchRadius, pos.y - searchRadius, pos.z - searchRadius, pos.x + searchRadius, pos.y + searchRadius, pos.z + searchRadius); + List tntEntities = level.getEntitiesOfClass(PrimedTnt.class, searchBox); + List creepers = level.getEntitiesOfClass(Creeper.class, searchBox); + List minecarts = level.getEntitiesOfClass(AbstractMinecart.class, searchBox); + List tntBlocks = new ArrayList<>(); BlockPos origin = BlockPos.containing(pos); - for (BlockPos blockPos : BlockPos.betweenClosed(origin.offset(-3, -2, -3), origin.offset(3, 2, 3))) { - if (level.getBlockState(blockPos).is(Blocks.TNT)) { - ++nearbyExplosives; + int blockRadius = (int)Math.ceil(searchRadius); + for (BlockPos blockPos : BlockPos.betweenClosed(origin.offset(-blockRadius, -blockRadius, -blockRadius), origin.offset(blockRadius, blockRadius, blockRadius))) { + BlockState state = level.getBlockState(blockPos); + if (state.is(Blocks.TNT)) { + tntBlocks.add(blockPos.immutable()); } } - nearbyExplosives += level.getEntitiesOfClass(PrimedTnt.class, new AABB(pos.x - 4.0, pos.y - 4.0, pos.z - 4.0, pos.x + 4.0, pos.y + 4.0, pos.z + 4.0)).size(); - if (nearbyExplosives <= 1) { + boolean hasTntMinecart = minecarts.stream().anyMatch(cart -> cart.getType() == EntityType.TNT_MINECART); + if (tntEntities.isEmpty() && creepers.isEmpty() && !hasTntMinecart && tntBlocks.isEmpty()) { return initialPower; } - float clusteredPower = initialPower + (nearbyExplosives - 1) * 2.75f; - return Math.min(clusteredPower, ((Integer)Config.COMMON.maxClusterPower.get()).floatValue()); + List sources = new ArrayList<>(); + sources.add(new ExplosionSource(pos, initialPower)); + for (PrimedTnt tnt : tntEntities) { + if (!tnt.position().equals(pos)) { + sources.add(new ExplosionSource(tnt.position(), 4.0f)); + } + } + for (Creeper creeper : creepers) { + if (!creeper.position().equals(pos)) { + sources.add(new ExplosionSource(creeper.position(), 3.0f)); + } + } + for (AbstractMinecart cart : minecarts) { + if (cart.getType() == EntityType.TNT_MINECART && !cart.position().equals(pos)) { + sources.add(new ExplosionSource(cart.position(), 4.0f)); + } + } + for (BlockPos blockPos : tntBlocks) { + Vec3 blockCenter = Vec3.atCenterOf(blockPos); + if (!blockCenter.equals(pos)) { + sources.add(new ExplosionSource(blockCenter, 4.0f)); + } + } + ExplosionSource mainSource = sources.stream().max((a, b) -> Float.compare(a.power, b.power)).orElse(sources.get(0)); + float totalPower = mainSource.power; + for (ExplosionSource source : sources) { + if (source != mainSource) { + totalPower += getPowerAddition(source.power); + } + } + for (PrimedTnt tnt : tntEntities) { + if (!tnt.position().equals(pos)) { + tnt.discard(); + } + } + for (Creeper creeper : creepers) { + if (!creeper.position().equals(pos)) { + creeper.discard(); + } + } + for (AbstractMinecart cart : minecarts) { + if (cart.getType() == EntityType.TNT_MINECART && !cart.position().equals(pos)) { + cart.discard(); + } + } + for (BlockPos blockPos : tntBlocks) { + if (!Vec3.atCenterOf(blockPos).equals(pos)) { + level.setBlock(blockPos, Blocks.AIR.defaultBlockState(), 3); + } + } + return Math.min(totalPower, ((Integer)Config.COMMON.maxClusterPower.get()).floatValue()); + } + + private static double getSearchRadius(float power) { + if (power <= 4.0f) { + return 3.0; + } + if (power <= 10.0f) { + return 5.0; + } + if (power <= 25.0f) { + return 10.0; + } + if (power <= 35.0f) { + return 14.0; + } + return 20.0; + } + + private static float getPowerAddition(float power) { + if (power <= 4.0f) { + return 2.0f; + } + if (power <= 10.0f) { + return 5.0f; + } + if (power <= 25.0f) { + return 10.0f; + } + if (power <= 35.0f) { + return 15.0f; + } + return 15.0f; + } + + private record ExplosionSource(Vec3 position, float power) { } } diff --git a/src/main/java/com/vinlanx/explosionoverhaul/ServerExplosionHandler.java b/src/main/java/com/vinlanx/explosionoverhaul/ServerExplosionHandler.java index 6b7ff12..18ae79d 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/ServerExplosionHandler.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/ServerExplosionHandler.java @@ -55,6 +55,21 @@ public class ServerExplosionHandler { level.sendParticles(ParticleTypes.EXPLOSION_EMITTER, center.x(), center.y(), center.z(), Math.max(2, radius / 3), 0.5, 0.5, 0.5, 0.0); level.sendParticles(ParticleTypes.LARGE_SMOKE, center.x(), center.y(), center.z(), radius * 8, radius * 0.35, radius * 0.25, radius * 0.35, 0.08); level.sendParticles(ParticleTypes.FLAME, center.x(), center.y(), center.z(), radius * 4, radius * 0.25, radius * 0.2, radius * 0.25, 0.05); + spawnCustomExplosionParticles(level, center, power, radius); + } + + private static void spawnCustomExplosionParticles(ServerLevel level, Vec3 center, float power, int radius) { + int glowCount = Math.min(220, Math.max(24, radius * 9)); + int smokeCount = Math.min(260, Math.max(32, radius * 11)); + int plasmaCount = Math.min(120, Math.max(12, radius * 5)); + int sparkCount = Math.min(80, Math.max(10, radius * 3)); + float maxRadius = Math.max(4.0f, radius); + for (int i = 0; i < 3; ++i) { + level.sendParticles(new CustomGlowParticleOptions(i % 2, power, 0.95f + i * 0.2f, i, (float)center.y(), maxRadius, 0.5f), center.x(), center.y(), center.z(), glowCount / 3, radius * 0.18, radius * 0.12, radius * 0.18, 0.08); + } + level.sendParticles(new SmokeParticleOptions(Math.max(1.2f, radius * 0.28f), 58 + radius * 3, 0.18f, 0.16f, 0.14f, 0.7f, true, 0.0f, 0.35f, null), center.x(), center.y() + 0.2, center.z(), smokeCount, radius * 0.35, radius * 0.22, radius * 0.35, 0.05); + level.sendParticles(new PlasmaParticleOptions(power), center.x(), center.y(), center.z(), plasmaCount, radius * 0.16, radius * 0.1, radius * 0.16, 0.45); + level.sendParticles(ModParticles.LINE_SPARK.get(), center.x(), center.y(), center.z(), sparkCount, radius * 0.22, radius * 0.16, radius * 0.22, 0.55); } public static void register() { diff --git a/src/main/java/com/vinlanx/explosionoverhaul/client/ClientParticleEvents.java b/src/main/java/com/vinlanx/explosionoverhaul/client/ClientParticleEvents.java new file mode 100644 index 0000000..247810f --- /dev/null +++ b/src/main/java/com/vinlanx/explosionoverhaul/client/ClientParticleEvents.java @@ -0,0 +1,23 @@ +package com.vinlanx.explosionoverhaul.client; + +import com.vinlanx.explosionoverhaul.ExplosionOverhaul; +import com.vinlanx.explosionoverhaul.ModParticles; +import net.neoforged.api.distmarker.Dist; +import net.neoforged.bus.api.SubscribeEvent; +import net.neoforged.fml.common.EventBusSubscriber; +import net.neoforged.neoforge.client.event.RegisterParticleProvidersEvent; + +@EventBusSubscriber(modid = ExplosionOverhaul.MODID, value = Dist.CLIENT, bus = EventBusSubscriber.Bus.MOD) +public final class ClientParticleEvents { + private ClientParticleEvents() { + } + + @SubscribeEvent + public static void registerParticleProviders(RegisterParticleProvidersEvent event) { + event.registerSpriteSet(ModParticles.CUSTOM_GLOW.get(), CustomGlowParticleProvider::new); + event.registerSpriteSet(ModParticles.PLASMA.get(), PlasmaParticle.Provider::new); + event.registerSpriteSet(ModParticles.CUSTOM_SMOKE.get(), SmokeParticle.Provider::new); + event.registerSpriteSet(ModParticles.LINE_SPARK.get(), LineSparkParticleProvider::new); + ExplosionOverhaul.LOGGER.info("Registered Explosion Overhaul particle providers."); + } +} diff --git a/src/main/java/com/vinlanx/explosionoverhaul/client/CustomGlowParticle.java b/src/main/java/com/vinlanx/explosionoverhaul/client/CustomGlowParticle.java index 5a67dce..862b5b5 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/client/CustomGlowParticle.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/client/CustomGlowParticle.java @@ -1,18 +1,77 @@ package com.vinlanx.explosionoverhaul.client; +import com.vinlanx.explosionoverhaul.Config; import com.vinlanx.explosionoverhaul.CustomGlowParticleOptions; import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.particle.ParticleRenderType; +import net.minecraft.client.particle.SpriteSet; import net.minecraft.client.particle.TextureSheetParticle; +import net.minecraft.util.Mth; public class CustomGlowParticle extends TextureSheetParticle { - public CustomGlowParticle(ClientLevel level, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, CustomGlowParticleOptions options) { + private final SpriteSet sprites; + private final float baseSize; + + public CustomGlowParticle(ClientLevel level, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, CustomGlowParticleOptions options, SpriteSet sprites) { super(level, x, y, z, xSpeed, ySpeed, zSpeed); - this.lifetime = 1; + this.sprites = sprites; + Config.Client.ParticleRenderMode mode = Config.CLIENT.particleRenderMode.get(); + float configScale = ((Double)Config.CLIENT.particleSizeScale.get()).floatValue(); + float powerScale = Mth.clamp(options.getPower() / 4.0f, 0.8f, 4.0f); + this.baseSize = options.getScale() * configScale * powerScale * (0.85f + this.random.nextFloat() * 0.35f); + this.lifetime = switch (mode) { + case REALISTIC -> 34 + this.random.nextInt(15); + case REALISTIC_2 -> 44 + this.random.nextInt(22); + case VANILA -> 22 + this.random.nextInt(12); + }; + this.quadSize = this.baseSize; + this.friction = 0.88f; + this.gravity = mode == Config.Client.ParticleRenderMode.VANILA ? -0.01f : -0.018f; + this.hasPhysics = false; + this.xd = xSpeed * 0.85; + this.yd = ySpeed * 0.85 + 0.01; + this.zd = zSpeed * 0.85; + this.alpha = 0.92f; + this.pickSprite(sprites); + applyModeColor(mode, options.getZone()); } @Override public ParticleRenderType getRenderType() { return ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT; } + + @Override + public void tick() { + super.tick(); + if (this.age >= this.lifetime) { + return; + } + float progress = this.age / (float)this.lifetime; + this.setSpriteFromAge(this.sprites); + this.alpha = Mth.clamp(1.0f - progress, 0.0f, 1.0f) * 0.92f; + this.quadSize = this.baseSize * (0.8f + progress * 1.8f); + } + + public boolean shouldCull() { + return false; + } + + private void applyModeColor(Config.Client.ParticleRenderMode mode, int zone) { + if (mode == Config.Client.ParticleRenderMode.VANILA) { + this.rCol = zone == 0 ? 1.0f : 0.95f; + this.gCol = zone == 0 ? 0.55f : 0.72f; + this.bCol = zone == 0 ? 0.15f : 0.3f; + return; + } + if (mode == Config.Client.ParticleRenderMode.REALISTIC_2) { + this.rCol = zone == 0 ? 1.0f : 0.82f; + this.gCol = zone == 0 ? 0.76f : 0.92f; + this.bCol = zone == 0 ? 0.42f : 1.0f; + return; + } + this.rCol = zone == 0 ? 1.0f : 1.0f; + this.gCol = zone == 0 ? 0.48f : 0.72f; + this.bCol = zone == 0 ? 0.08f : 0.2f; + } } diff --git a/src/main/java/com/vinlanx/explosionoverhaul/client/CustomGlowParticleProvider.java b/src/main/java/com/vinlanx/explosionoverhaul/client/CustomGlowParticleProvider.java index bda7fb2..0e2f937 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/client/CustomGlowParticleProvider.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/client/CustomGlowParticleProvider.java @@ -12,10 +12,13 @@ import net.minecraft.client.particle.SpriteSet; public class CustomGlowParticleProvider implements ParticleProvider { + private final SpriteSet sprites; + public CustomGlowParticleProvider(SpriteSet pSprites) { + this.sprites = pSprites; } public Particle createParticle(CustomGlowParticleOptions options, ClientLevel pLevel, double pX, double pY, double pZ, double pXSpeed, double pYSpeed, double pZSpeed) { - return new CustomGlowParticle(pLevel, pX, pY, pZ, pXSpeed, pYSpeed, pZSpeed, options); + return new CustomGlowParticle(pLevel, pX, pY, pZ, pXSpeed, pYSpeed, pZSpeed, options, this.sprites); } } diff --git a/src/main/java/com/vinlanx/explosionoverhaul/client/LineSparkParticle.java b/src/main/java/com/vinlanx/explosionoverhaul/client/LineSparkParticle.java index 58a7812..5839e27 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/client/LineSparkParticle.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/client/LineSparkParticle.java @@ -4,15 +4,40 @@ import net.minecraft.client.multiplayer.ClientLevel; import net.minecraft.client.particle.ParticleRenderType; import net.minecraft.client.particle.SpriteSet; import net.minecraft.client.particle.TextureSheetParticle; +import net.minecraft.util.Mth; public class LineSparkParticle extends TextureSheetParticle { + private final SpriteSet sprites; + public LineSparkParticle(ClientLevel level, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, SpriteSet sprites) { super(level, x, y, z, xSpeed, ySpeed, zSpeed); - this.setSpriteFromAge(sprites); + this.sprites = sprites; + this.lifetime = 6 + this.random.nextInt(8); + this.quadSize = 0.12f + this.random.nextFloat() * 0.18f; + this.rCol = 1.0f; + this.gCol = 0.8f; + this.bCol = 0.35f; + this.alpha = 1.0f; + this.friction = 0.76f; + this.gravity = 0.08f; + this.xd = xSpeed; + this.yd = ySpeed; + this.zd = zSpeed; + this.setSpriteFromAge(this.sprites); } @Override public ParticleRenderType getRenderType() { return ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT; } + + @Override + public void tick() { + super.tick(); + if (this.age >= this.lifetime) { + return; + } + this.setSpriteFromAge(this.sprites); + this.alpha = Mth.clamp(1.0f - this.age / (float)this.lifetime, 0.0f, 1.0f); + } } diff --git a/src/main/java/com/vinlanx/explosionoverhaul/client/PlasmaParticle.java b/src/main/java/com/vinlanx/explosionoverhaul/client/PlasmaParticle.java index 00ddedd..ce8fdaf 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/client/PlasmaParticle.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/client/PlasmaParticle.java @@ -7,11 +7,28 @@ import net.minecraft.client.particle.ParticleProvider; import net.minecraft.client.particle.ParticleRenderType; import net.minecraft.client.particle.SpriteSet; import net.minecraft.client.particle.TextureSheetParticle; +import net.minecraft.util.Mth; public class PlasmaParticle extends TextureSheetParticle { + private final SpriteSet sprites; + private final float startSize; + protected PlasmaParticle(ClientLevel level, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, PlasmaParticleOptions options, SpriteSet sprites) { super(level, x, y, z, xSpeed, ySpeed, zSpeed); - this.lifetime = 1; + this.sprites = sprites; + this.lifetime = 10 + this.random.nextInt(10); + this.startSize = Mth.clamp(options.getPower() / 8.0f, 0.45f, 2.8f); + this.quadSize = this.startSize; + this.rCol = 1.0f; + this.gCol = 0.74f + this.random.nextFloat() * 0.22f; + this.bCol = 0.18f; + this.alpha = 1.0f; + this.friction = 0.82f; + this.gravity = 0.02f; + this.hasPhysics = true; + this.xd = xSpeed; + this.yd = ySpeed; + this.zd = zSpeed; this.pickSprite(sprites); } @@ -20,6 +37,18 @@ public class PlasmaParticle extends TextureSheetParticle { return ParticleRenderType.PARTICLE_SHEET_TRANSLUCENT; } + @Override + public void tick() { + super.tick(); + if (this.age >= this.lifetime) { + return; + } + float progress = this.age / (float)this.lifetime; + this.setSpriteFromAge(this.sprites); + this.quadSize = this.startSize * (1.0f - progress * 0.65f); + this.alpha = Mth.clamp(1.0f - progress, 0.0f, 1.0f); + } + public static class Provider implements ParticleProvider { private final SpriteSet sprites; diff --git a/src/main/java/com/vinlanx/explosionoverhaul/client/SmokeParticle.java b/src/main/java/com/vinlanx/explosionoverhaul/client/SmokeParticle.java index 8e84dbb..23b5784 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/client/SmokeParticle.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/client/SmokeParticle.java @@ -7,11 +7,30 @@ import net.minecraft.client.particle.ParticleProvider; import net.minecraft.client.particle.ParticleRenderType; import net.minecraft.client.particle.SpriteSet; import net.minecraft.client.particle.TextureSheetParticle; +import net.minecraft.util.Mth; public class SmokeParticle extends TextureSheetParticle { + private final SpriteSet sprites; + private final float startSize; + private final float startAlpha; + protected SmokeParticle(ClientLevel level, double x, double y, double z, double xSpeed, double ySpeed, double zSpeed, SmokeParticleOptions options, SpriteSet sprites) { super(level, x, y, z, xSpeed, ySpeed, zSpeed); - this.lifetime = 1; + this.sprites = sprites; + this.lifetime = Math.max(6, options.getLifetime()); + this.startSize = options.getScale() * (options.isHeavy() ? 1.45f : 1.0f); + this.startAlpha = options.getAlpha(); + this.quadSize = this.startSize; + this.rCol = options.getRed(); + this.gCol = options.getGreen(); + this.bCol = options.getBlue(); + this.alpha = this.startAlpha; + this.friction = options.isHeavy() ? 0.94f : 0.9f; + this.gravity = options.isHeavy() ? -0.004f : -0.012f; + this.hasPhysics = false; + this.xd = xSpeed + options.getWindSpeed() * 0.02; + this.yd = ySpeed + 0.008 + options.getHeightPercent() * 0.01; + this.zd = zSpeed; this.pickSprite(sprites); } @@ -24,6 +43,18 @@ public class SmokeParticle extends TextureSheetParticle { return false; } + @Override + public void tick() { + super.tick(); + if (this.age >= this.lifetime) { + return; + } + float progress = this.age / (float)this.lifetime; + this.setSpriteFromAge(this.sprites); + this.quadSize = this.startSize * (1.0f + progress * 1.6f); + this.alpha = this.startAlpha * Mth.clamp(1.0f - progress, 0.0f, 1.0f); + } + public static class Provider implements ParticleProvider { private final SpriteSet sprites;