/* * 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> perExplosionEffects = new ConcurrentHashMap>(); 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 glassBlocks = BlockIndexManager.getNearby(level, center, (int)Math.ceil(radius), BlockIndexManager.BlockType.GLASS); if (glassBlocks.isEmpty()) { return; } HashSet foundGlassBlocks = new HashSet(); 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 groups = new ArrayList(); HashSet processed = new HashSet(); for (BlockPos glassPos : foundGlassBlocks) { List 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 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 findConnectedGlass(ServerLevel level, BlockPos start, Set processed) { ArrayList group = new ArrayList(); LinkedList queue = new LinkedList(); 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 effects : perExplosionEffects.values()) { int explosionBudget = budgetPerExplosion; Iterator 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 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; } } }