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

232 lines
11 KiB
Java

/*
* Decompiled with CFR 0.152.
*/
package com.vinlanx.explosionoverhaul;
import com.vinlanx.explosionoverhaul.BlockIndexManager;
import com.vinlanx.explosionoverhaul.Config;
import com.vinlanx.explosionoverhaul.ExplosionOverhaul;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.HashSet;
import java.util.Iterator;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentHashMap;
import net.minecraft.core.BlockPos;
import net.minecraft.core.Direction;
import net.minecraft.core.Position;
import net.minecraft.core.Vec3i;
import net.minecraft.core.particles.BlockParticleOption;
import net.minecraft.core.particles.ParticleOptions;
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.tags.BlockTags;
import net.minecraft.util.Mth;
import net.minecraft.util.RandomSource;
import net.minecraft.world.level.BlockGetter;
import net.minecraft.world.level.ClipContext;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.HitResult;
import net.minecraft.world.phys.Vec3;
public class GlassBreakingEffects {
private static final Map<UUID, List<DelayedGlassEffect>> perExplosionEffects = new ConcurrentHashMap<UUID, List<DelayedGlassEffect>>();
private static final int MAX_GROUPS_PER_EXPLOSION = 250;
private static int tickCounter = 0;
public static void trigger(ServerLevel level, Vec3 explosionPos, float power) {
if (!((Boolean)Config.COMMON.enableGlassBreaking.get()).booleanValue()) {
return;
}
RandomSource random = level.m_213780_();
double radius = GlassBreakingEffects.calculateRadius(power);
BlockPos center = BlockPos.m_274446_((Position)explosionPos);
List<BlockPos> glassBlocks = BlockIndexManager.getNearby(level, center, (int)Math.ceil(radius), BlockIndexManager.BlockType.GLASS);
if (glassBlocks.isEmpty()) {
return;
}
HashSet<BlockPos> foundGlassBlocks = new HashSet<BlockPos>();
float initialRayEnergy = power * 2.5f;
block0: for (BlockPos glassPos : glassBlocks) {
Vec3 glassCenter = Vec3.m_82512_((Vec3i)glassPos);
Vec3 rayDirection = glassCenter.m_82546_(explosionPos).m_82541_();
double distanceToGlass = explosionPos.m_82554_(glassCenter);
if (distanceToGlass > radius) continue;
float currentEnergy = initialRayEnergy * (0.8f + random.m_188501_() * 0.4f);
BlockPos lastPos = null;
for (double step = 0.5; step < distanceToGlass + 1.0; step += 0.4) {
float resistance;
BlockPos currentPos = BlockPos.m_274446_((Position)explosionPos.m_82549_(rayDirection.m_82490_(step)));
if (currentPos.equals(lastPos)) continue;
lastPos = currentPos;
if (!level.m_46749_(currentPos) || currentPos.m_123342_() < level.m_141937_() || currentPos.m_123342_() >= level.m_151558_()) continue block0;
BlockState state = level.m_8055_(currentPos);
if (state.m_60795_()) continue;
if (GlassBreakingEffects.isGlass(state)) {
foundGlassBlocks.add(currentPos);
continue;
}
if (BlockIndexManager.isReinforcedGlass(state) || ExplosionOverhaul.isBlockStateBlacklisted(state) || (currentEnergy -= (resistance = state.getExplosionResistance((BlockGetter)level, currentPos, null)) + 0.3f) <= 0.0f) continue block0;
}
}
if (foundGlassBlocks.isEmpty()) {
return;
}
ArrayList<GlassGroup> groups = new ArrayList<GlassGroup>();
HashSet<BlockPos> processed = new HashSet<BlockPos>();
for (BlockPos glassPos : foundGlassBlocks) {
List<BlockPos> groupPositions;
if (processed.contains(glassPos) || (groupPositions = GlassBreakingEffects.findConnectedGlass(level, glassPos, processed)).isEmpty()) continue;
groups.add(new GlassGroup(groupPositions, glassPos, glassPos.m_123331_((Vec3i)center)));
}
if (groups.isEmpty()) {
return;
}
groups.sort(Comparator.comparingDouble(g -> g.distanceSq));
List<DelayedGlassEffect> effects = Collections.synchronizedList(new ArrayList());
UUID explosionId = UUID.randomUUID();
int groupsToProcess = Math.min(groups.size(), 250);
for (int i = 0; i < groupsToProcess; ++i) {
GlassGroup group = (GlassGroup)groups.get(i);
for (BlockPos pos : group.positions()) {
if (!GlassBreakingEffects.shouldBreak(level, explosionPos, pos, power, random, radius)) continue;
double distance = Math.sqrt(pos.m_123331_((Vec3i)BlockPos.m_274446_((Position)explosionPos)));
long delayInTicks = (long)(distance / 3.0);
int processingInterval = Math.max(1, (Integer)Config.COMMON.glassBreakingIntervalTicks.get());
long delayInProcessingCycles = Math.max(1L, delayInTicks / (long)processingInterval);
effects.add(new DelayedGlassEffect(level, pos, delayInProcessingCycles += (long)random.m_188503_(2)));
}
}
if (!effects.isEmpty()) {
perExplosionEffects.put(explosionId, effects);
}
}
private static boolean shouldBreak(ServerLevel level, Vec3 explosionPos, BlockPos glassPos, float power, RandomSource random, double maxRadius) {
ClipContext context;
double distance = Math.sqrt(glassPos.m_123331_((Vec3i)BlockPos.m_274446_((Position)explosionPos)));
float distanceFraction = (float)(distance / maxRadius);
if (distanceFraction > 1.0f) {
return false;
}
float baseChance = distanceFraction <= 0.25f ? Mth.m_14179_((float)(distanceFraction / 0.25f), (float)1.0f, (float)0.95f) : (distanceFraction <= 0.5f ? Mth.m_14179_((float)((distanceFraction - 0.25f) / 0.25f), (float)0.95f, (float)0.68f) : (distanceFraction <= 0.85f ? Mth.m_14179_((float)((distanceFraction - 0.5f) / 0.35f), (float)0.68f, (float)0.2f) : Mth.m_14179_((float)((distanceFraction - 0.85f) / 0.15f), (float)0.2f, (float)0.0f)));
baseChance *= Mth.m_14036_((float)(power / 25.0f), (float)0.8f, (float)1.5f);
BlockState state = level.m_8055_(glassPos);
if (state.m_60734_().toString().contains("pane")) {
baseChance += 0.2f;
}
if (level.m_45547_(context = new ClipContext(explosionPos, Vec3.m_82512_((Vec3i)glassPos), ClipContext.Block.COLLIDER, ClipContext.Fluid.NONE, null)).m_6662_() != HitResult.Type.MISS) {
baseChance *= 0.5f;
}
return random.m_188501_() < Mth.m_14036_((float)baseChance, (float)0.0f, (float)1.0f);
}
private static double calculateRadius(float power) {
if (power <= 5.0f) {
return Mth.m_14179_((float)Mth.m_184655_((float)power, (float)1.0f, (float)4.0f), (float)20.0f, (float)48.0f);
}
float dustRadius = power * GlassBreakingEffects.calculateRadiusMultiplier(power);
return dustRadius * 1.75f;
}
private static float calculateRadiusMultiplier(float power) {
if (power <= 5.0f) {
return 2.0f;
}
if (power <= 40.0f) {
return Mth.m_14179_((float)((power - 5.0f) / 35.0f), (float)2.0f, (float)4.0f);
}
if (power <= 80.0f) {
return Mth.m_14179_((float)((power - 40.0f) / 40.0f), (float)4.0f, (float)5.0f);
}
if (power <= 100.0f) {
return Mth.m_14179_((float)((power - 80.0f) / 20.0f), (float)5.0f, (float)7.0f);
}
return 7.0f;
}
private static List<BlockPos> findConnectedGlass(ServerLevel level, BlockPos start, Set<BlockPos> processed) {
ArrayList<BlockPos> group = new ArrayList<BlockPos>();
LinkedList<BlockPos> queue = new LinkedList<BlockPos>();
queue.add(start);
processed.add(start);
while (!queue.isEmpty()) {
BlockPos current = (BlockPos)queue.poll();
group.add(current);
if (group.size() > 200) break;
for (Direction dir : Direction.values()) {
BlockPos neighbor = current.m_5484_(dir, 1);
if (processed.contains(neighbor) || !GlassBreakingEffects.isGlass(level.m_8055_(neighbor))) continue;
processed.add(neighbor);
queue.add(neighbor);
}
}
return group;
}
private static boolean isGlass(BlockState state) {
if (BlockIndexManager.isReinforcedGlass(state)) {
return false;
}
return state.m_204336_(BlockTags.f_13049_) || state.m_60734_().toString().contains("glass");
}
public static void onServerTick() {
int processingInterval = (Integer)Config.COMMON.glassBreakingIntervalTicks.get();
if (++tickCounter < processingInterval) {
return;
}
tickCounter = 0;
if (perExplosionEffects.isEmpty()) {
return;
}
int budgetPerExplosion = (Integer)Config.COMMON.glassBlocksPerCycle.get();
for (List<DelayedGlassEffect> effects : perExplosionEffects.values()) {
int explosionBudget = budgetPerExplosion;
Iterator<DelayedGlassEffect> iterator = effects.iterator();
while (iterator.hasNext() && explosionBudget > 0) {
DelayedGlassEffect effect = iterator.next();
if (!effect.tick()) continue;
iterator.remove();
--explosionBudget;
}
}
perExplosionEffects.values().removeIf(List::isEmpty);
}
private record GlassGroup(List<BlockPos> positions, BlockPos representativePos, double distanceSq) {
}
private static class DelayedGlassEffect {
private final ServerLevel level;
private final BlockPos glassPos;
private final BlockState glassState;
private long delayCycles;
public DelayedGlassEffect(ServerLevel level, BlockPos glassPos, long delayCycles) {
this.level = level;
this.glassPos = glassPos;
this.glassState = level.m_8055_(glassPos);
this.delayCycles = delayCycles;
}
public boolean tick() {
if (--this.delayCycles <= 0L) {
this.level.m_46961_(this.glassPos, false);
this.level.m_8767_((ParticleOptions)new BlockParticleOption(ParticleTypes.f_123794_, this.glassState), (double)this.glassPos.m_123341_() + 0.5, (double)this.glassPos.m_123342_() + 0.5, (double)this.glassPos.m_123343_() + 0.5, 20, 0.3, 0.3, 0.3, 0.1);
this.level.m_5594_(null, this.glassPos, SoundEvents.f_11983_, SoundSource.BLOCKS, 0.7f, 1.0f);
return true;
}
return false;
}
}
}