Files
Explosion-Overhaul/com/vinlanx/explosionoverhaul/client/ClientEffects.java
2026-05-04 10:03:58 +00:00

483 lines
21 KiB
Java

/*
* Decompiled with CFR 0.152.
*/
package com.vinlanx.explosionoverhaul.client;
import com.mojang.blaze3d.systems.RenderSystem;
import com.vinlanx.explosionoverhaul.Config;
import com.vinlanx.explosionoverhaul.ModParticles;
import com.vinlanx.explosionoverhaul.PlayTrackedSoundPacket;
import com.vinlanx.explosionoverhaul.client.Blur;
import com.vinlanx.explosionoverhaul.client.CameraShakeConcussionEffect;
import com.vinlanx.explosionoverhaul.client.ExplosionWindController;
import com.vinlanx.explosionoverhaul.client.GroundDustEffect;
import com.vinlanx.explosionoverhaul.client.GroundMistEffect;
import com.vinlanx.explosionoverhaul.client.PhysicsBasedExplosionEffect;
import com.vinlanx.explosionoverhaul.client.PositionalSoundInstance;
import com.vinlanx.explosionoverhaul.client.ShockwaveEffect;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Random;
import net.minecraft.client.Minecraft;
import net.minecraft.client.gui.GuiGraphics;
import net.minecraft.client.multiplayer.ClientLevel;
import net.minecraft.client.player.LocalPlayer;
import net.minecraft.client.resources.sounds.SoundInstance;
import net.minecraft.client.sounds.SoundManager;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.BlockParticleOption;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.core.particles.SimpleParticleType;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.BlockHitResult;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.client.event.RenderGuiOverlayEvent;
import net.minecraftforge.registries.ForgeRegistries;
public class ClientEffects {
private static float currentShakeIntensity = 0.0f;
private static int shakeDurationTicks = 0;
private static float currentPushIntensity = 0.0f;
private static float lastYawOffset = 0.0f;
private static float lastPitchOffset = 0.0f;
private static final Random random = new Random();
private static final List<PhysicsBasedExplosionEffect> activeExplosions = new ArrayList<PhysicsBasedExplosionEffect>();
private static final List<TrackedSound> activeTrackedSounds = new ArrayList<TrackedSound>();
private static final List<ShockwaveEffect> activeShockwaves = new ArrayList<ShockwaveEffect>();
private static final List<GroundDustEffect> activeDustClouds = new ArrayList<GroundDustEffect>();
private static final List<GroundMistEffect> activeMistClouds = new ArrayList<GroundMistEffect>();
private static final List<FlashEffect> activeFlashEffects = new ArrayList<FlashEffect>();
private static final List<PendingShake> pendingShakes = new ArrayList<PendingShake>();
public static void addTrackedSound(PlayTrackedSoundPacket msg) {
SoundEvent sound = (SoundEvent)ForgeRegistries.SOUND_EVENTS.getValue(msg.getSoundId());
if (sound != null) {
activeTrackedSounds.add(new TrackedSound(msg.getExplosionPos(), sound, msg.getVolume(), msg.getPitch(), msg.getDelayTicks(), msg.isPlayerInHouse()));
}
}
private static Vec3 calculateSoundPosition(Player player, Vec3 explosionPos, boolean isPlayerInHouse) {
float FIXED_DISTANCE = 45.0f;
Vec3 playerPos = player.m_146892_();
Vec3 startPos = isPlayerInHouse ? explosionPos : playerPos;
Vec3 endPos = isPlayerInHouse ? playerPos : explosionPos;
ClipContext context = new ClipContext(startPos, endPos, ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, (Entity)player);
BlockHitResult hitResult = player.m_9236_().m_45547_(context);
if (hitResult.m_6662_() == HitResult.Type.MISS) {
Vec3 direction = explosionPos.m_82546_(playerPos).m_82541_();
if (direction.m_82556_() < 0.001) {
direction = player.m_20154_();
}
return playerPos.m_82549_(direction.m_82490_(45.0));
}
Vec3 hitPos = hitResult.m_82450_();
Vec3 rayDir = endPos.m_82546_(startPos).m_82541_();
return hitPos.m_82546_(rayDir.m_82490_(0.1));
}
private static void handleTrackedSoundsTick() {
Minecraft mc = Minecraft.m_91087_();
if (mc.f_91074_ == null || mc.m_91106_() == null) {
return;
}
Iterator<TrackedSound> iterator = activeTrackedSounds.iterator();
while (iterator.hasNext()) {
TrackedSound tracked = iterator.next();
if (tracked.isFinished()) {
iterator.remove();
continue;
}
tracked.tick((Player)mc.f_91074_, mc.m_91106_(), mc.f_91074_.m_217043_());
}
}
public static void triggerLocalCameraShake(float intensity, int durationTicks, float pushIntensity) {
if (!((Boolean)Config.CLIENT.enableCameraShake.get()).booleanValue()) {
return;
}
float amplifiedIntensity = intensity * (((Double)Config.CLIENT.cameraShakeAmplifier.get()).floatValue() * 10.0f);
float amplifiedPush = 0.0f;
if (((Boolean)Config.COMMON.enablePlayerShake.get()).booleanValue()) {
amplifiedPush = pushIntensity * (((Double)Config.COMMON.playerShakeAmplifier.get()).floatValue() * 5.0f);
}
if (amplifiedIntensity > currentShakeIntensity || shakeDurationTicks == 0 || durationTicks > shakeDurationTicks) {
currentShakeIntensity = amplifiedIntensity;
shakeDurationTicks = durationTicks;
currentPushIntensity = amplifiedPush;
}
}
public static void triggerDelayedCameraShake(float intensity, int durationTicks, float pushIntensity, int delayTicks) {
if (!((Boolean)Config.CLIENT.enableCameraShake.get()).booleanValue()) {
return;
}
if (delayTicks <= 0) {
ClientEffects.triggerLocalCameraShake(intensity, durationTicks, pushIntensity);
return;
}
pendingShakes.add(new PendingShake(delayTicks, intensity, durationTicks, pushIntensity));
}
public static void triggerRealisticExplosion(Vec3 position, float power) {
activeExplosions.add(new PhysicsBasedExplosionEffect(position, power));
}
public static void addFlashEffect(Vec3 explosionPos, float power) {
activeFlashEffects.add(new FlashEffect(explosionPos, power));
}
public static void onClientTick() {
if (Minecraft.m_91087_().m_91104_()) {
return;
}
Blur.onClientTick();
CameraShakeConcussionEffect.onClientTick();
ExplosionWindController.tick();
ClientEffects.handleCameraShakeTick();
ClientEffects.handlePendingShakesTick();
ClientEffects.handleTrackedSoundsTick();
activeExplosions.removeIf(effect -> {
effect.tick();
return effect.isFinished();
});
activeShockwaves.removeIf(effect -> {
effect.tick();
return effect.isFinished();
});
activeDustClouds.removeIf(effect -> {
effect.tick();
return effect.isFinished();
});
activeMistClouds.removeIf(effect -> {
effect.tick();
return effect.isFinished();
});
activeFlashEffects.removeIf(effect -> effect.isFinished());
}
public static void renderFlash(RenderGuiOverlayEvent.Post event) {
if (!((Boolean)Config.CLIENT.enableFlashEffect.get()).booleanValue()) {
return;
}
Minecraft mc = Minecraft.m_91087_();
if (mc.f_91074_ == null || activeFlashEffects.isEmpty()) {
return;
}
float maxOpacity = 0.0f;
for (FlashEffect effect : activeFlashEffects) {
float opacity = effect.getCurrentOpacity((Player)mc.f_91074_);
if (!(opacity > maxOpacity)) continue;
maxOpacity = opacity;
}
if (maxOpacity <= 0.0f) {
return;
}
GuiGraphics guiGraphics = event.getGuiGraphics();
int screenWidth = mc.m_91268_().m_85445_();
int screenHeight = mc.m_91268_().m_85446_();
ResourceLocation flashTexture = ResourceLocation.fromNamespaceAndPath((String)"explosionoverhaul", (String)"textures/effects/flash.png");
RenderSystem.enableBlend();
RenderSystem.defaultBlendFunc();
RenderSystem.setShaderColor((float)1.0f, (float)1.0f, (float)1.0f, (float)maxOpacity);
guiGraphics.m_280163_(flashTexture, 0, 0, 0.0f, 0.0f, screenWidth, screenHeight, screenWidth, screenHeight);
RenderSystem.disableBlend();
RenderSystem.setShaderColor((float)1.0f, (float)1.0f, (float)1.0f, (float)1.0f);
}
private static void handlePendingShakesTick() {
if (pendingShakes.isEmpty()) {
return;
}
Iterator<PendingShake> it = pendingShakes.iterator();
while (it.hasNext()) {
PendingShake ps = it.next();
--ps.delayTicks;
if (ps.delayTicks > 0) continue;
ClientEffects.triggerLocalCameraShake(ps.intensity, ps.durationTicks, ps.pushIntensity);
it.remove();
}
}
public static void triggerShockwave(Vec3 position, float power) {
activeShockwaves.add(new ShockwaveEffect(position, power));
}
public static void triggerDustCloud(Vec3 position, float power) {
if (!((Boolean)Config.CLIENT.enableGroundDustEffect.get()).booleanValue()) {
return;
}
activeDustClouds.add(new GroundDustEffect(position, power));
}
public static void triggerMistCloud(Vec3 position, float power) {
if (!((Boolean)Config.CLIENT.enableGroundMistEffect.get()).booleanValue()) {
return;
}
activeMistClouds.add(new GroundMistEffect(position, power));
}
public static void triggerAmbientCaveDust(float power) {
int i;
Minecraft mc = Minecraft.m_91087_();
LocalPlayer player = mc.f_91074_;
ClientLevel level = mc.f_91073_;
if (player == null || level == null) {
return;
}
int particleCount = 15 + (int)((double)(power / 10.0f) * 3.5);
particleCount = Mth.m_14045_((int)particleCount, (int)10, (int)70);
BlockPos playerPos = player.m_20183_();
BlockState ceilingState = Blocks.f_50069_.m_49966_();
for (i = 3; i < 10; ++i) {
BlockState state = level.m_8055_(playerPos.m_6630_(i));
if (state.m_60795_()) continue;
ceilingState = state;
break;
}
for (i = 0; i < particleCount; ++i) {
double x = player.m_20185_() + (random.nextDouble() - 0.5) * 16.0;
double z = player.m_20189_() + (random.nextDouble() - 0.5) * 16.0;
double y = player.m_20186_() + 4.0 + random.nextDouble() * 6.0;
level.m_7106_((ParticleOptions)new BlockParticleOption(ParticleTypes.f_123814_, ceilingState), x, y, z, 0.0, -0.15, 0.0);
}
}
private static void handleCameraShakeTick() {
Minecraft mc = Minecraft.m_91087_();
LocalPlayer player = mc.f_91074_;
if (player == null) {
if (shakeDurationTicks > 0 || lastYawOffset != 0.0f || lastPitchOffset != 0.0f) {
lastYawOffset = 0.0f;
lastPitchOffset = 0.0f;
shakeDurationTicks = 0;
currentShakeIntensity = 0.0f;
currentPushIntensity = 0.0f;
}
return;
}
player.m_19884_((double)(-lastYawOffset), (double)(-lastPitchOffset));
lastYawOffset = 0.0f;
lastPitchOffset = 0.0f;
if (shakeDurationTicks > 0) {
float progress = (float)shakeDurationTicks / 30.0f;
float cameraIntensity = currentShakeIntensity * Mth.m_14031_((float)(progress * (float)Math.PI));
float yawChange = (random.nextFloat() - 0.5f) * 2.0f * cameraIntensity;
float pitchChange = (random.nextFloat() - 0.5f) * 1.0f * cameraIntensity;
player.m_19884_((double)yawChange, (double)pitchChange);
lastYawOffset = yawChange;
lastPitchOffset = pitchChange;
if (currentPushIntensity > 0.0f) {
float pushFactor = currentPushIntensity * Mth.m_14031_((float)(progress * (float)Math.PI)) * 0.1f;
player.m_5997_((random.nextDouble() - 0.5) * (double)pushFactor, 0.0, (random.nextDouble() - 0.5) * (double)pushFactor);
}
--shakeDurationTicks;
} else {
currentShakeIntensity = 0.0f;
currentPushIntensity = 0.0f;
}
}
public static void triggerLineSparks(Vec3 position, float power) {
if (!((Boolean)Config.CLIENT.enableLineSparks.get()).booleanValue()) {
return;
}
Minecraft mc = Minecraft.m_91087_();
ClientLevel level = mc.f_91073_;
if (level == null) {
return;
}
double amountMultiplier = (Double)Config.CLIENT.lineSparkAmountMultiplier.get();
if (amountMultiplier <= 0.0) {
return;
}
int sparkCount = (int)((double)(power * 15.0f) * amountMultiplier);
sparkCount = Mth.m_14045_((int)sparkCount, (int)15, (int)500);
for (int i = 0; i < sparkCount; ++i) {
Vec3 motion = new Vec3(random.nextDouble() - 0.5, random.nextDouble() - 0.5, random.nextDouble() - 0.5).m_82541_();
float powerFraction = power / 100.0f;
float force = random.nextFloat() < 0.25f ? Mth.m_14179_((float)powerFraction, (float)0.5f, (float)8.0f) * (0.9f + random.nextFloat() * 0.4f) : Mth.m_14179_((float)powerFraction, (float)0.3f, (float)4.5f) * (0.7f + random.nextFloat() * 0.4f);
motion = motion.m_82490_((double)force);
level.m_7106_((ParticleOptions)((SimpleParticleType)ModParticles.LINE_SPARK.get()), position.f_82479_, position.f_82480_, position.f_82481_, motion.f_82479_, motion.f_82480_, motion.f_82481_);
}
}
private static class TrackedSound {
final Vec3 explosionPos;
final SoundEvent sound;
final float volume;
final float pitch;
long delayTicks;
PositionalSoundInstance soundInstance;
boolean started = false;
private int updateCooldown = 0;
private final boolean isPlayerInHouse;
TrackedSound(Vec3 explosionPos, SoundEvent sound, float volume, float pitch, long delayTicks, boolean isPlayerInHouse) {
this.explosionPos = explosionPos;
this.sound = sound;
this.volume = volume;
this.pitch = pitch;
this.delayTicks = delayTicks;
this.isPlayerInHouse = isPlayerInHouse;
}
public void tick(Player player, SoundManager soundManager, RandomSource random) {
if (this.soundInstance != null && this.soundInstance.m_7801_()) {
return;
}
if (!this.started) {
--this.delayTicks;
if (this.delayTicks <= 0L) {
Vec3 soundPos = ClientEffects.calculateSoundPosition(player, this.explosionPos, this.isPlayerInHouse);
this.soundInstance = new PositionalSoundInstance(this.sound, SoundSource.BLOCKS, this.volume, this.pitch, random, soundPos.f_82479_, soundPos.f_82480_, soundPos.f_82481_);
soundManager.m_120367_((SoundInstance)this.soundInstance);
this.started = true;
}
} else {
--this.updateCooldown;
if (this.updateCooldown <= 0) {
this.updateCooldown = 10;
Vec3 newSoundPos = ClientEffects.calculateSoundPosition(player, this.explosionPos, this.isPlayerInHouse);
this.soundInstance.updatePosition(newSoundPos);
}
}
}
public boolean isFinished() {
return this.soundInstance != null && this.soundInstance.m_7801_();
}
}
private static class PendingShake {
int delayTicks;
final float intensity;
final int durationTicks;
final float pushIntensity;
PendingShake(int delayTicks, float intensity, int durationTicks, float pushIntensity) {
this.delayTicks = delayTicks;
this.intensity = intensity;
this.durationTicks = durationTicks;
this.pushIntensity = pushIntensity;
}
}
private static class FlashEffect {
final Vec3 explosionPos;
final float power;
final long startTime;
final float maxDurationTicks;
final float maxOpacity;
final float maxDistance;
FlashEffect(Vec3 explosionPos, float power) {
this.explosionPos = explosionPos;
this.power = power;
this.startTime = System.currentTimeMillis();
this.maxDurationTicks = this.calculateDuration(power) * 20.0f;
this.maxOpacity = this.calculateMaxOpacity(power);
this.maxDistance = this.calculateMaxDistance(power);
}
private float calculateDuration(float power) {
if (power <= 1.0f) {
return 0.5f;
}
if (power <= 5.0f) {
return 1.0f;
}
if (power <= 10.0f) {
return 2.5f;
}
if (power <= 25.0f) {
return 3.5f;
}
return 5.5f;
}
private float calculateMaxDistance(float power) {
if (power <= 1.0f) {
return 30.0f;
}
if (power <= 5.0f) {
return 30.0f + (power - 1.0f) * 70.0f / 4.0f;
}
if (power <= 10.0f) {
return 100.0f + (power - 5.0f) * 100.0f / 5.0f;
}
if (power <= 25.0f) {
return 200.0f + (power - 10.0f) * 200.0f / 15.0f;
}
if (power <= 40.0f) {
return 400.0f + (power - 25.0f) * 300.0f / 15.0f;
}
return 700.0f + (power - 40.0f) * 50.0f;
}
private float calculateMaxOpacity(float power) {
float baseOpacity = power <= 1.0f ? 0.5f : (power <= 5.0f ? 0.7f : (power <= 10.0f ? 0.9f : 1.0f));
return baseOpacity * ((Double)Config.CLIENT.flashMaxOpacity.get()).floatValue();
}
public float getCurrentOpacity(Player player) {
long elapsed = System.currentTimeMillis() - this.startTime;
float elapsedTicks = (float)elapsed / 50.0f;
if (elapsedTicks > this.maxDurationTicks) {
return 0.0f;
}
float progress = elapsedTicks / this.maxDurationTicks;
float fadeOpacity = this.maxOpacity * (1.0f - progress * progress);
double distance = player.m_146892_().m_82554_(this.explosionPos);
float distanceFactor = Math.max(0.0f, 1.0f - (float)distance / this.maxDistance);
Vec3 viewVec = player.m_20154_();
Vec3 dirToExplosion = this.explosionPos.m_82546_(player.m_146892_()).m_82541_();
float dot = (float)viewVec.m_82526_(dirToExplosion);
float angleFactor = Math.max(0.0f, dot);
if (!this.isVisible(player, this.explosionPos)) {
return 0.0f;
}
return fadeOpacity * distanceFactor * angleFactor;
}
private boolean isVisible(Player player, Vec3 explosionPos) {
Vec3 start = player.m_146892_();
Vec3 end = explosionPos;
ClipContext context = new ClipContext(start, end, ClipContext.Block.VISUAL, ClipContext.Fluid.NONE, (Entity)player);
BlockHitResult hit = player.m_9236_().m_45547_(context);
if (hit.m_6662_() == HitResult.Type.MISS) {
return true;
}
if (this.power > 10.0f) {
int numChecks = Math.max(1, (int)(this.power / 10.0f));
for (int i = 1; i <= numChecks; ++i) {
Vec3 elevatedStart = start.m_82520_(0.0, (double)(5 * i), 0.0);
Vec3 elevatedEnd = end.m_82520_(0.0, (double)(5 * i), 0.0);
ClipContext elevatedContext = new ClipContext(elevatedStart, elevatedEnd, ClipContext.Block.VISUAL, ClipContext.Fluid.NONE, (Entity)player);
BlockHitResult elevatedHit = player.m_9236_().m_45547_(elevatedContext);
if (elevatedHit.m_6662_() != HitResult.Type.MISS) continue;
return true;
}
}
return false;
}
public boolean isFinished() {
long elapsed = System.currentTimeMillis() - this.startTime;
return (float)elapsed / 50.0f > this.maxDurationTicks;
}
}
}