From 55fde4c062466248aa8e2f691c1b8929cb2007b8 Mon Sep 17 00:00:00 2001 From: MrSphay Date: Sat, 9 May 2026 17:50:33 +0200 Subject: [PATCH] Restore cinematic explosion impact --- .../ServerExplosionHandler.java | 12 +- .../client/ClientEffects.java | 93 ++++++++++-- .../client/ConcussionAudioEffect.java | 22 +-- .../client/DeafnessConcussionEffect.java | 4 +- .../client/LowPassConcussionEffect.java | 4 +- .../client/PhysicsBasedExplosionEffect.java | 136 ++++++++++++++---- .../client/ShockwaveEffect.java | 47 +++++- 7 files changed, 255 insertions(+), 63 deletions(-) diff --git a/src/main/java/com/vinlanx/explosionoverhaul/ServerExplosionHandler.java b/src/main/java/com/vinlanx/explosionoverhaul/ServerExplosionHandler.java index 2af9f47..c504e67 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/ServerExplosionHandler.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/ServerExplosionHandler.java @@ -195,12 +195,12 @@ public class ServerExplosionHandler { if (!((Boolean)Config.CLIENT.enableCameraShake.get()).booleanValue() && !((Boolean)Config.COMMON.enablePlayerShake.get()).booleanValue()) { return new CameraShakeProfile(0.0f, 0, 0.0f); } - double maxDistance = Math.max(18.0, power * (playerInCave ? 8.0 : 5.0)); + double maxDistance = Math.max(22.0, power * (playerInCave ? 10.5 : 7.0)); double percent = Math.max(0.0, 1.0 - distance / maxDistance); float amplifier = ((Double)Config.COMMON.playerShakeAmplifier.get()).floatValue(); - float intensity = (float)Mth.clamp(percent * (0.45 + power * 0.035) * amplifier, 0.0, 4.0); - int duration = intensity <= 0.02f ? 0 : Mth.clamp((int)Math.round(10 + power * 1.4 + percent * 45.0), 6, 140); - float push = (float)Mth.clamp(percent * power * 0.07, 0.0, 2.0); + float intensity = (float)Mth.clamp(percent * (0.55 + power * 0.052) * amplifier, 0.0, 5.0); + int duration = intensity <= 0.02f ? 0 : Mth.clamp((int)Math.round(14 + power * 1.9 + percent * 58.0), 8, 190); + float push = (float)Mth.clamp(percent * power * 0.092, 0.0, 2.8); return new CameraShakeProfile(intensity, duration, push); } @@ -209,8 +209,8 @@ public class ServerExplosionHandler { if (direction.lengthSqr() < 0.0001) { direction = new Vec3(0.0, 1.0, 0.0); } - double strength = Mth.clamp((1.0 - distance / Math.max(12.0, power * 4.0)) * power * 0.06, 0.0, 1.4); - player.setDeltaMovement(player.getDeltaMovement().add(direction.normalize().scale(strength).add(0.0, Math.min(0.45, strength * 0.35), 0.0))); + double strength = Mth.clamp((1.0 - distance / Math.max(14.0, power * 5.0)) * power * 0.08, 0.0, 1.85); + player.setDeltaMovement(player.getDeltaMovement().add(direction.normalize().scale(strength).add(0.0, Math.min(0.58, strength * 0.38), 0.0))); } private static boolean hasLineOfSight(ServerLevel level, Vec3 from, Vec3 to) { diff --git a/src/main/java/com/vinlanx/explosionoverhaul/client/ClientEffects.java b/src/main/java/com/vinlanx/explosionoverhaul/client/ClientEffects.java index 56732bf..f306b49 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/client/ClientEffects.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/client/ClientEffects.java @@ -23,6 +23,8 @@ public class ClientEffects { private static final List TRACKED_SOUNDS = new ArrayList<>(); private static final List DELAYED_SHAKES = new ArrayList<>(); private static final List EXPLOSION_EFFECTS = new ArrayList<>(); + private static final List SHOCKWAVE_EFFECTS = new ArrayList<>(); + private static final List LINGERING_FOGS = new ArrayList<>(); private static int shakeTicks; private static float shakeIntensity; private static float pushIntensity; @@ -71,7 +73,7 @@ public class ClientEffects { if (!((Boolean)Config.CLIENT.enableFlashEffect.get()).booleanValue()) { return; } - flashTicks = Math.max(flashTicks, 8 + Mth.floor(power * 0.5f)); + flashTicks = Math.max(flashTicks, Mth.clamp(9 + Mth.floor(power * 0.85f), 10, 42)); flashPower = Math.max(flashPower, power); } @@ -93,6 +95,25 @@ public class ClientEffects { } } } + synchronized (SHOCKWAVE_EFFECTS) { + Iterator iterator = SHOCKWAVE_EFFECTS.iterator(); + while (iterator.hasNext()) { + ShockwaveEffect effect = iterator.next(); + effect.tick(); + if (effect.isFinished()) { + iterator.remove(); + } + } + } + synchronized (LINGERING_FOGS) { + Iterator iterator = LINGERING_FOGS.iterator(); + while (iterator.hasNext()) { + LingeringFog fog = iterator.next(); + if (fog.tick(mc)) { + iterator.remove(); + } + } + } if (shakeTicks > 0 && mc.player != null) { float fade = shakeTicks / Math.max(1.0f, shakeTicks + 8.0f); float yaw = (mc.player.getRandom().nextFloat() - 0.5f) * shakeIntensity * 0.8f * fade; @@ -122,8 +143,8 @@ public class ClientEffects { return; } Minecraft mc = Minecraft.getInstance(); - float alpha = Mth.clamp(flashTicks / 12.0f, 0.0f, 1.0f) * ((Double)Config.CLIENT.flashMaxOpacity.get()).floatValue(); - alpha = Mth.clamp(alpha * (0.6f + flashPower * 0.018f), 0.0f, 0.92f); + float alpha = Mth.clamp(flashTicks / 18.0f, 0.0f, 1.0f) * ((Double)Config.CLIENT.flashMaxOpacity.get()).floatValue(); + alpha = Mth.clamp(alpha * (0.7f + flashPower * 0.026f), 0.0f, 0.92f); int color = ((int)(alpha * 255.0f) << 24) | 0xFFFFD7; graphics.fill(0, 0, mc.getWindow().getGuiScaledWidth(), mc.getWindow().getGuiScaledHeight(), color); } @@ -133,11 +154,8 @@ public class ClientEffects { if (mc.level == null || !((Boolean)Config.CLIENT.enableShockwaveEffect.get()).booleanValue()) { return; } - int count = Math.min(120, Math.max(24, Math.round(power * 5.0f))); - for (int i = 0; i < count; ++i) { - double angle = Math.PI * 2.0 * i / count; - double speed = 0.18 + Math.min(0.75, power * 0.018); - mc.level.addParticle(ModParticles.LINE_SPARK.get(), position.x, position.y + 0.12, position.z, Math.cos(angle) * speed, 0.01, Math.sin(angle) * speed); + synchronized (SHOCKWAVE_EFFECTS) { + SHOCKWAVE_EFFECTS.add(new ShockwaveEffect(position, power)); } } @@ -146,11 +164,13 @@ public class ClientEffects { if (mc.level == null || !((Boolean)Config.CLIENT.enableGroundDustEffect.get()).booleanValue()) { return; } - int count = Math.min(180, Math.max(30, Math.round(power * 8.0f * ((Double)Config.CLIENT.groundDustQuality.get()).floatValue()))); - mc.level.addParticle(new SmokeParticleOptions(Math.max(1.4f, power * 0.35f), 80, 0.2f, 0.17f, 0.14f, 0.68f, true, 0.0f, 0.2f, null), position.x, position.y + 0.2, position.z, 0.0, 0.04, 0.0); + float quality = ((Double)Config.CLIENT.groundDustQuality.get()).floatValue(); + int count = Math.min(260, Math.max(36, Math.round(power * 9.5f * quality))); + float coreScale = Mth.clamp(Math.max(1.6f, power * 0.42f), 1.6f, 10.5f); + mc.level.addParticle(new SmokeParticleOptions(coreScale, 110, 0.2f, 0.17f, 0.14f, 0.64f, true, 0.0f, 0.2f, null), position.x, position.y + 0.2, position.z, 0.0, 0.045, 0.0); for (int i = 0; i < count; ++i) { - Vec3 velocity = randomHorizontal(mc.player == null ? 0 : mc.player.getRandom().nextLong()).scale(0.04 + mc.level.random.nextDouble() * 0.16); - mc.level.addParticle(new SmokeParticleOptions(0.9f + mc.level.random.nextFloat() * 1.7f, 45 + mc.level.random.nextInt(55), 0.18f, 0.16f, 0.13f, 0.58f, true, 0.0f, 0.2f, null), position.x, position.y + 0.1, position.z, velocity.x, 0.03 + mc.level.random.nextDouble() * 0.05, velocity.z); + Vec3 velocity = randomHorizontal(mc.level.random.nextLong()).scale(0.04 + mc.level.random.nextDouble() * 0.16); + mc.level.addParticle(new SmokeParticleOptions(1.0f + mc.level.random.nextFloat() * 2.1f, 58 + mc.level.random.nextInt(62), 0.18f, 0.16f, 0.13f, 0.52f, true, 0.0f, 0.2f, null), position.x, position.y + 0.1, position.z, velocity.x, 0.025 + mc.level.random.nextDouble() * 0.05, velocity.z); } } @@ -159,11 +179,14 @@ public class ClientEffects { if (mc.level == null || !((Boolean)Config.CLIENT.enableGroundMistEffect.get()).booleanValue()) { return; } - int count = Math.min(120, Math.max(20, Math.round(power * 5.0f * ((Double)Config.CLIENT.groundMistQuality.get()).floatValue()))); + int count = Math.min(160, Math.max(24, Math.round(power * 6.0f * ((Double)Config.CLIENT.groundMistQuality.get()).floatValue()))); for (int i = 0; i < count; ++i) { Vec3 velocity = randomHorizontal(mc.level.random.nextLong()).scale(0.025 + mc.level.random.nextDouble() * 0.11); mc.level.addParticle(new SmokeParticleOptions(1.2f + mc.level.random.nextFloat() * 2.2f, 65 + mc.level.random.nextInt(60), 0.32f, 0.31f, 0.29f, 0.34f, false, 0.0f, 0.1f, null), position.x, position.y + 0.08, position.z, velocity.x, 0.01, velocity.z); } + synchronized (LINGERING_FOGS) { + LINGERING_FOGS.add(new LingeringFog(position, power)); + } } public static void triggerLineSparks(Vec3 position, float power) { @@ -198,6 +221,50 @@ public class ClientEffects { return new Vec3(Math.cos(angle), 0.0, Math.sin(angle)); } + private static final class LingeringFog { + private final Vec3 position; + private final float power; + private final int lifetime; + private final float radius; + private int age; + + private LingeringFog(Vec3 position, float power) { + this.position = position; + this.power = power; + this.lifetime = Mth.clamp(80 + Math.round(power * 4.0f), 90, 260); + this.radius = Mth.clamp(5.0f + power * 1.8f, 7.0f, 72.0f); + } + + private boolean tick(Minecraft mc) { + if (mc.level == null) { + return true; + } + if (++this.age >= this.lifetime) { + return true; + } + if (!((Boolean)Config.CLIENT.enableGroundMistEffect.get()).booleanValue() && !((Boolean)Config.CLIENT.enableGroundDustEffect.get()).booleanValue()) { + return false; + } + if (this.age % 2 != 0) { + return false; + } + float progress = this.age / (float)this.lifetime; + float fade = Mth.clamp(1.0f - progress, 0.0f, 1.0f); + int count = Mth.clamp(Math.round(this.power * 0.26f * fade), 1, 10); + for (int i = 0; i < count; ++i) { + double angle = mc.level.random.nextDouble() * Math.PI * 2.0; + double distance = this.radius * (0.18 + mc.level.random.nextDouble() * (0.82 + progress * 0.35)); + double x = this.position.x + Math.cos(angle) * distance; + double z = this.position.z + Math.sin(angle) * distance; + double y = this.position.y + 0.04 + mc.level.random.nextDouble() * 0.55; + float scale = Mth.clamp(1.1f + this.power * 0.075f + mc.level.random.nextFloat() * 1.6f, 1.0f, 4.8f); + float alpha = Mth.clamp(0.30f * fade, 0.03f, 0.30f); + mc.level.addParticle(new SmokeParticleOptions(scale, 80 + mc.level.random.nextInt(70), 0.29f, 0.28f, 0.26f, alpha, false, 0.0f, 0.04f, null), x, y, z, Math.cos(angle) * 0.018, 0.004, Math.sin(angle) * 0.018); + } + return false; + } + } + private static final class DelayedSound { private final PlayTrackedSoundPacket packet; private long delayTicks; diff --git a/src/main/java/com/vinlanx/explosionoverhaul/client/ConcussionAudioEffect.java b/src/main/java/com/vinlanx/explosionoverhaul/client/ConcussionAudioEffect.java index 0910e82..dc33972 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/client/ConcussionAudioEffect.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/client/ConcussionAudioEffect.java @@ -25,9 +25,11 @@ public class ConcussionAudioEffect { return; } float chance = ((Double)Config.CLIENT.concussionChanceMultiplier.get()).floatValue(); - currentIntensity = Math.max(currentIntensity, (float)(computeBaseIntensityPercent(effectivePercent) / 100.0 * chance)); - ticksRemaining = Math.max(ticksRemaining, (int)Math.round((computeBaseSilentSeconds(effectivePercent, explosionInCave) + power * 0.08) * 20.0 * ((Double)Config.CLIENT.concussionDurationMultiplier.get()).floatValue())); - ticksRemaining = Math.min(ticksRemaining, 20 * 18); + float powerBoost = (float)Math.min(1.55, 0.76 + power / 34.0); + currentIntensity = Math.max(currentIntensity, (float)(computeBaseIntensityPercent(effectivePercent) / 100.0 * chance * powerBoost)); + currentIntensity = Math.min(currentIntensity, explosionInCave ? 1.35f : 1.15f); + ticksRemaining = Math.max(ticksRemaining, (int)Math.round((computeBaseSilentSeconds(effectivePercent, explosionInCave) + power * 0.13) * 20.0 * ((Double)Config.CLIENT.concussionDurationMultiplier.get()).floatValue())); + ticksRemaining = Math.min(ticksRemaining, 20 * 24); if (((Boolean)Config.CLIENT.enableCameraSway.get()).booleanValue()) { CameraShakeConcussionEffect.start(Math.max(1, ticksRemaining / 20), currentIntensity * ((Double)Config.CLIENT.cameraSwayIntensity.get()).floatValue()); } @@ -59,7 +61,7 @@ public class ConcussionAudioEffect { updateHeartbeat(mc.player, currentIntensity); } if (tinnitusCooldown > 0 && --tinnitusCooldown == 0) { - mc.level.playLocalSound(mc.player.getX(), mc.player.getY(), mc.player.getZ(), ModSounds.LOW_SOUND.get(), SoundSource.AMBIENT, Math.min(0.8f, currentIntensity), 1.25f, false); + mc.level.playLocalSound(mc.player.getX(), mc.player.getY(), mc.player.getZ(), ModSounds.LOW_SOUND.get(), SoundSource.AMBIENT, Math.min(1.0f, 0.18f + currentIntensity), 1.18f, false); } } @@ -68,10 +70,10 @@ public class ConcussionAudioEffect { if (mc.level == null || heartbeatTicks-- > 0) { return; } - heartbeatTicks = Math.max(10, (int)(32 - currentIntensity * 16.0f)); - mc.level.playLocalSound(player.getX(), player.getY(), player.getZ(), ModSounds.HEART_LAB.get(), SoundSource.PLAYERS, 0.28f + currentIntensity * 0.35f, 1.0f, false); + heartbeatTicks = Math.max(7, (int)(30 - currentIntensity * 18.0f)); + mc.level.playLocalSound(player.getX(), player.getY(), player.getZ(), ModSounds.HEART_LAB.get(), SoundSource.PLAYERS, 0.3f + currentIntensity * 0.48f, 0.96f + currentIntensity * 0.05f, false); if (heartbeatTicks > 4) { - mc.level.playLocalSound(player.getX(), player.getY(), player.getZ(), ModSounds.HEART_DAB.get(), SoundSource.PLAYERS, 0.22f + currentIntensity * 0.25f, 1.0f, false); + mc.level.playLocalSound(player.getX(), player.getY(), player.getZ(), ModSounds.HEART_DAB.get(), SoundSource.PLAYERS, 0.24f + currentIntensity * 0.34f, 0.98f + currentIntensity * 0.05f, false); } } @@ -103,15 +105,15 @@ public class ConcussionAudioEffect { } public static double computeMaxDistance(float power, boolean explosionInCave) { - return explosionInCave ? Math.max(18.0, power * 8.0) : Math.max(10.0, power * 5.0); + return explosionInCave ? Math.max(24.0, power * 11.0) : Math.max(16.0, power * 7.5); } public static double applyOcclusion(double percent) { - return Math.max(0.0, percent * 0.5); + return Math.max(0.0, percent * 0.62); } public static double computeBaseSilentSeconds(double effectivePercent, boolean explosionInCave) { - return Math.max(0.0, effectivePercent / (explosionInCave ? 16.0 : 24.0)); + return Math.max(0.0, effectivePercent / (explosionInCave ? 13.5 : 19.0)); } public static double computeBaseIntensityPercent(double effectivePercent) { diff --git a/src/main/java/com/vinlanx/explosionoverhaul/client/DeafnessConcussionEffect.java b/src/main/java/com/vinlanx/explosionoverhaul/client/DeafnessConcussionEffect.java index 5b6e0c6..7483b07 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/client/DeafnessConcussionEffect.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/client/DeafnessConcussionEffect.java @@ -16,7 +16,7 @@ public class DeafnessConcussionEffect { DeafnessConcussionEffect.intensity = Math.max(DeafnessConcussionEffect.intensity, intensity); totalTicks = Math.max(1, (int)Math.round(Math.max(1.0, silentSeconds) * 20.0)); ticksRemaining = Math.max(ticksRemaining, totalTicks); - LowPassConcussionEffect.setDeafnessGain((float)Math.max(0.18, 1.0 - intensity * 0.75)); + LowPassConcussionEffect.setDeafnessGain((float)Math.max(0.12, 1.0 - intensity * 0.82)); return true; } @@ -44,7 +44,7 @@ public class DeafnessConcussionEffect { } --ticksRemaining; double remaining = ticksRemaining / Math.max(1.0, totalTicks); - LowPassConcussionEffect.setDeafnessGain((float)Math.max(0.18, lerp(1.0, 1.0 - intensity * 0.75, easeOutQuad(remaining)))); + LowPassConcussionEffect.setDeafnessGain((float)Math.max(0.12, lerp(1.0, 1.0 - intensity * 0.82, easeOutQuad(remaining)))); } public static double lerp(double a, double b, double t) { diff --git a/src/main/java/com/vinlanx/explosionoverhaul/client/LowPassConcussionEffect.java b/src/main/java/com/vinlanx/explosionoverhaul/client/LowPassConcussionEffect.java index 0a797ac..d568486 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/client/LowPassConcussionEffect.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/client/LowPassConcussionEffect.java @@ -90,7 +90,7 @@ public class LowPassConcussionEffect { double remaining = ticksRemaining / Math.max(1.0, totalTicks); double curve = easeOutQuad(Math.max(0.0, Math.min(1.0, remaining))); float amount = (float)(targetIntensity * curve); - currentHfMultiplier = lerp(1.0f, compatibilityMode ? 0.45f : 0.18f, amount); - currentGainMultiplier = lerp(1.0f, 0.55f, amount); + currentHfMultiplier = lerp(1.0f, compatibilityMode ? 0.38f : 0.12f, Math.min(1.0f, amount)); + currentGainMultiplier = lerp(1.0f, 0.46f, Math.min(1.0f, amount)); } } diff --git a/src/main/java/com/vinlanx/explosionoverhaul/client/PhysicsBasedExplosionEffect.java b/src/main/java/com/vinlanx/explosionoverhaul/client/PhysicsBasedExplosionEffect.java index b1ec6f0..26671aa 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/client/PhysicsBasedExplosionEffect.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/client/PhysicsBasedExplosionEffect.java @@ -14,12 +14,18 @@ public class PhysicsBasedExplosionEffect { private final Vec3 position; private final float power; private final int lifetime; + private final float stemHeight; + private final float capRadius; + private final float stemRadius; private int age; public PhysicsBasedExplosionEffect(Vec3 position, float power) { this.position = position; this.power = power; - this.lifetime = Mth.clamp(18 + Math.round(power * 1.4f), 22, 90); + this.lifetime = Mth.clamp(64 + Math.round(power * 4.2f), 78, 210); + this.stemHeight = Mth.clamp(3.6f + power * 0.42f, 4.5f, 28.0f); + this.capRadius = Mth.clamp(3.0f + power * 0.46f, 4.0f, 26.0f); + this.stemRadius = Mth.clamp(0.75f + power * 0.055f, 0.9f, 4.0f); this.spawnInitialBurst(); } @@ -30,19 +36,17 @@ public class PhysicsBasedExplosionEffect { return; } float progress = this.age / (float)this.lifetime; - if (progress < 0.45f) { - int count = Math.max(2, Math.round(this.power * 0.7f)); - for (int i = 0; i < count; ++i) { - Vec3 velocity = randomDirection(level).scale(0.05 + level.random.nextDouble() * 0.18); - level.addParticle(new CustomGlowParticleOptions(i % 2, this.power, 0.75f + level.random.nextFloat() * 0.75f, i % 3, (float)this.position.y, this.power * 2.2f, progress), this.position.x, this.position.y, this.position.z, velocity.x, Math.abs(velocity.y) * 0.45, velocity.z); - } + if (progress < 0.62f) { + this.spawnRisingStem(level, progress); } - if (progress < 0.8f && ((Boolean)Config.CLIENT.enablePlasmaParticles.get()).booleanValue()) { - int count = Math.max(1, Math.round(this.power * 0.28f)); - for (int i = 0; i < count; ++i) { - Vec3 velocity = randomDirection(level).scale(0.18 + level.random.nextDouble() * 0.7); - level.addParticle(new PlasmaParticleOptions(this.power), this.position.x, this.position.y, this.position.z, velocity.x, velocity.y, velocity.z); - } + if (progress > 0.06f && progress < 0.86f) { + this.spawnMushroomCap(level, progress); + } + if (progress < 0.58f && ((Boolean)Config.CLIENT.enablePlasmaParticles.get()).booleanValue()) { + this.spawnPlasmaCore(level, progress); + } + if (progress > 0.34f && progress < 0.94f) { + this.spawnTrailingSmoke(level, progress); } } @@ -55,33 +59,107 @@ public class PhysicsBasedExplosionEffect { if (level == null) { return; } - Config.Client.ParticleRenderMode mode = Config.CLIENT.particleRenderMode.get(); - int glowCount = switch (mode) { - case REALISTIC -> Math.round(this.power * 10.0f); - case REALISTIC_2 -> Math.round(this.power * 12.0f); - case VANILA -> Math.round(this.power * 6.0f); - }; - glowCount = Mth.clamp(glowCount, 24, 260); + float modeScale = modeScale(); + int glowCount = Mth.clamp(Math.round(this.power * 9.0f * modeScale), 28, 220); for (int i = 0; i < glowCount; ++i) { - Vec3 velocity = randomDirection(level).scale(0.04 + level.random.nextDouble() * 0.22); - level.addParticle(new CustomGlowParticleOptions(i % 2, this.power, 0.75f + level.random.nextFloat() * 0.9f, i % 3, (float)this.position.y, this.power * 2.2f, 0.0f), this.position.x, this.position.y, this.position.z, velocity.x, velocity.y * 0.35, velocity.z); + Vec3 velocity = randomDirection(level).scale(0.08 + level.random.nextDouble() * 0.42); + level.addParticle(new CustomGlowParticleOptions(i % 2, this.power, 0.8f + level.random.nextFloat() * 0.9f, i % 3, (float)this.position.y, this.capRadius, 0.0f), this.position.x, this.position.y + 0.2, this.position.z, velocity.x, Math.abs(velocity.y) * 0.45 + 0.04, velocity.z); } - int smokeCount = Mth.clamp(Math.round(this.power * 7.0f), 18, 180); + int smokeCount = Mth.clamp(Math.round(this.power * 5.5f * modeScale), 26, 150); for (int i = 0; i < smokeCount; ++i) { - Vec3 velocity = randomDirection(level).scale(0.02 + level.random.nextDouble() * 0.12); - level.addParticle(new SmokeParticleOptions(0.8f + level.random.nextFloat() * Math.max(1.6f, this.power * 0.28f), 60 + level.random.nextInt(60), 0.18f, 0.16f, 0.13f, 0.62f, true, 0.0f, 0.2f, null), this.position.x, this.position.y + 0.1, this.position.z, velocity.x, Math.abs(velocity.y) * 0.25 + 0.02, velocity.z); + Vec3 velocity = randomDirection(level).scale(0.03 + level.random.nextDouble() * 0.18); + float scale = 1.0f + level.random.nextFloat() * Math.max(1.8f, this.power * 0.22f); + level.addParticle(new SmokeParticleOptions(scale, 70 + level.random.nextInt(65), 0.22f, 0.19f, 0.15f, 0.64f, true, 0.0f, 0.12f, null), this.position.x, this.position.y + 0.15, this.position.z, velocity.x, Math.abs(velocity.y) * 0.22 + 0.03, velocity.z); } - int sparks = Mth.clamp(Math.round(this.power * 5.0f), 10, 140); + int sparks = Mth.clamp(Math.round(this.power * 4.2f * modeScale), 12, 120); for (int i = 0; i < sparks; ++i) { - Vec3 velocity = randomDirection(level).scale(0.28 + level.random.nextDouble() * 1.1); - level.addParticle(ModParticles.LINE_SPARK.get(), this.position.x, this.position.y + 0.15, this.position.z, velocity.x, Math.abs(velocity.y) * 0.5, velocity.z); + Vec3 velocity = randomDirection(level).scale(0.45 + level.random.nextDouble() * 1.35); + level.addParticle(ModParticles.LINE_SPARK.get(), this.position.x, this.position.y + 0.25, this.position.z, velocity.x, Math.abs(velocity.y) * 0.52, velocity.z); + } + } + + private void spawnRisingStem(ClientLevel level, float progress) { + float modeScale = modeScale(); + int count = Mth.clamp(Math.round(this.power * 0.28f * modeScale), 2, 10); + float lift = this.stemHeight * Mth.clamp(progress / 0.62f, 0.0f, 1.0f); + for (int i = 0; i < count; ++i) { + double angle = level.random.nextDouble() * Math.PI * 2.0; + double radius = this.stemRadius * Math.sqrt(level.random.nextDouble()); + double x = this.position.x + Math.cos(angle) * radius; + double z = this.position.z + Math.sin(angle) * radius; + double y = this.position.y + 0.4 + level.random.nextDouble() * Math.max(1.4, lift); + float fade = 1.0f - progress * 0.45f; + float scale = Mth.clamp(1.3f + this.power * 0.16f + level.random.nextFloat() * 1.4f, 1.2f, 7.2f); + level.addParticle(new SmokeParticleOptions(scale, 92 + level.random.nextInt(76), 0.17f, 0.15f, 0.13f, 0.58f * fade, true, 0.0f, progress, null), x, y, z, Math.cos(angle) * 0.025, 0.075 + level.random.nextDouble() * 0.065, Math.sin(angle) * 0.025); + } + } + + private void spawnMushroomCap(ClientLevel level, float progress) { + float capProgress = Mth.clamp((progress - 0.06f) / 0.58f, 0.0f, 1.0f); + float spread = this.capRadius * easeOutCubic(capProgress); + float capY = (float)this.position.y + this.stemHeight * (0.72f + capProgress * 0.28f); + int count = Mth.clamp(Math.round(this.power * 0.34f * modeScale()), 2, 14); + for (int i = 0; i < count; ++i) { + double angle = level.random.nextDouble() * Math.PI * 2.0; + double radius = spread * (0.35 + level.random.nextDouble() * 0.78); + double x = this.position.x + Math.cos(angle) * radius; + double z = this.position.z + Math.sin(angle) * radius; + double y = capY + (level.random.nextDouble() - 0.35) * Math.max(1.0, this.stemHeight * 0.18); + float alpha = Mth.clamp(0.64f * (1.0f - progress * 0.55f), 0.16f, 0.64f); + float scale = Mth.clamp(1.6f + this.power * 0.18f + level.random.nextFloat() * 2.0f, 1.4f, 8.4f); + Vec3 outward = new Vec3(x - this.position.x, 0.0, z - this.position.z); + if (outward.lengthSqr() < 0.0001) { + outward = new Vec3(Math.cos(angle), 0.0, Math.sin(angle)); + } + Vec3 velocity = outward.normalize().scale(0.035 + capProgress * 0.055).add(0.0, 0.02 + level.random.nextDouble() * 0.035, 0.0); + level.addParticle(new SmokeParticleOptions(scale, 100 + level.random.nextInt(92), 0.18f, 0.16f, 0.14f, alpha, true, 0.0f, capProgress, null), x, y, z, velocity.x, velocity.y, velocity.z); + } + } + + private void spawnPlasmaCore(ClientLevel level, float progress) { + int count = Mth.clamp(Math.round(this.power * 0.16f * modeScale()), 1, 8); + for (int i = 0; i < count; ++i) { + Vec3 velocity = randomDirection(level).scale(0.18 + level.random.nextDouble() * 0.78); + double y = this.position.y + 0.25 + level.random.nextDouble() * this.stemHeight * Mth.clamp(progress * 1.6f, 0.12f, 1.0f); + level.addParticle(new PlasmaParticleOptions(this.power), this.position.x, y, this.position.z, velocity.x, Math.abs(velocity.y) * 0.45, velocity.z); + } + } + + private void spawnTrailingSmoke(ClientLevel level, float progress) { + if (!((Boolean)Config.CLIENT.enableGroundMistEffect.get()).booleanValue()) { + return; + } + float fade = 1.0f - progress; + int count = Mth.clamp(Math.round(this.power * 0.14f * modeScale()), 1, 6); + for (int i = 0; i < count; ++i) { + double angle = level.random.nextDouble() * Math.PI * 2.0; + double radius = this.capRadius * (0.15 + level.random.nextDouble() * 0.95); + double x = this.position.x + Math.cos(angle) * radius; + double z = this.position.z + Math.sin(angle) * radius; + double y = this.position.y + 0.1 + level.random.nextDouble() * 1.2; + float scale = Mth.clamp(1.0f + this.power * 0.1f + level.random.nextFloat() * 1.7f, 1.0f, 5.8f); + level.addParticle(new SmokeParticleOptions(scale, 84 + level.random.nextInt(70), 0.26f, 0.25f, 0.23f, 0.28f * fade, false, 0.0f, 0.05f, null), x, y, z, Math.cos(angle) * 0.025, 0.006, Math.sin(angle) * 0.025); } } private static Vec3 randomDirection(ClientLevel level) { double yaw = level.random.nextDouble() * Math.PI * 2.0; - double y = level.random.nextDouble() * 0.9 - 0.25; + double y = level.random.nextDouble() * 1.15 - 0.25; double horizontal = Math.sqrt(Math.max(0.0, 1.0 - y * y)); return new Vec3(Math.cos(yaw) * horizontal, y, Math.sin(yaw) * horizontal); } + + private static float modeScale() { + return switch (Config.CLIENT.particleRenderMode.get()) { + case REALISTIC -> 1.0f; + case REALISTIC_2 -> 1.12f; + case VANILA -> 0.68f; + }; + } + + private static float easeOutCubic(float t) { + float clamped = Mth.clamp(t, 0.0f, 1.0f); + float inv = 1.0f - clamped; + return 1.0f - inv * inv * inv; + } } diff --git a/src/main/java/com/vinlanx/explosionoverhaul/client/ShockwaveEffect.java b/src/main/java/com/vinlanx/explosionoverhaul/client/ShockwaveEffect.java index c9d1296..57af489 100644 --- a/src/main/java/com/vinlanx/explosionoverhaul/client/ShockwaveEffect.java +++ b/src/main/java/com/vinlanx/explosionoverhaul/client/ShockwaveEffect.java @@ -1,15 +1,60 @@ package com.vinlanx.explosionoverhaul.client; +import com.vinlanx.explosionoverhaul.Config; +import com.vinlanx.explosionoverhaul.ModParticles; +import com.vinlanx.explosionoverhaul.SmokeParticleOptions; +import net.minecraft.client.Minecraft; +import net.minecraft.client.multiplayer.ClientLevel; +import net.minecraft.util.Mth; import net.minecraft.world.phys.Vec3; public class ShockwaveEffect { + private final Vec3 position; + private final float power; + private final int lifetime; + private final float maxRadius; + private int age; + public ShockwaveEffect(Vec3 position, float power) { + this.position = position; + this.power = power; + this.lifetime = Mth.clamp(18 + Math.round(power * 0.9f), 24, 72); + this.maxRadius = Mth.clamp(8.0f + power * 2.9f, 10.0f, 96.0f); } public void tick() { + ++this.age; + ClientLevel level = Minecraft.getInstance().level; + if (level == null || !((Boolean)Config.CLIENT.enableShockwaveEffect.get()).booleanValue()) { + return; + } + float progress = this.age / (float)this.lifetime; + float radius = this.maxRadius * easeOutCubic(progress); + float fade = Mth.clamp(1.0f - progress, 0.0f, 1.0f); + int points = Mth.clamp(Math.round(8.0f + this.power * 1.15f), 12, 44); + for (int i = 0; i < points; ++i) { + double angle = (Math.PI * 2.0 * i / points) + level.random.nextDouble() * 0.22; + double x = this.position.x + Math.cos(angle) * radius; + double z = this.position.z + Math.sin(angle) * radius; + double y = this.position.y + 0.08 + level.random.nextDouble() * 0.35; + double outwardSpeed = 0.18 + Math.min(0.9, this.power * 0.025); + if (progress < 0.72f) { + level.addParticle(ModParticles.LINE_SPARK.get(), x, y + 0.16, z, Math.cos(angle) * outwardSpeed, 0.015 + level.random.nextDouble() * 0.04, Math.sin(angle) * outwardSpeed); + } + if (((Boolean)Config.CLIENT.enableGroundDustEffect.get()).booleanValue()) { + float scale = Mth.clamp(0.8f + this.power * 0.055f + level.random.nextFloat() * 0.8f, 0.8f, 4.2f); + level.addParticle(new SmokeParticleOptions(scale, 42 + level.random.nextInt(36), 0.28f, 0.25f, 0.21f, 0.34f * fade, true, 0.0f, 0.05f, null), x, y, z, Math.cos(angle) * 0.055, 0.012, Math.sin(angle) * 0.055); + } + } } public boolean isFinished() { - return true; + return this.age >= this.lifetime; + } + + private static float easeOutCubic(float t) { + float clamped = Mth.clamp(t, 0.0f, 1.0f); + float inv = 1.0f - clamped; + return 1.0f - inv * inv * inv; } }