package net.mcreator.phayriosisreborn.meteor;

import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.function.Predicate;
import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.resources.ResourceLocation;
import net.minecraft.sounds.SoundEvent;
import net.minecraft.sounds.SoundSource;
import net.minecraft.world.damagesource.DamageSource;
import net.minecraft.server.network.ServerGamePacketListenerImpl;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.MobSpawnType;
import net.minecraft.world.entity.ai.attributes.AttributeInstance;
import net.minecraft.world.entity.ai.attributes.Attributes;
import net.minecraft.world.level.Level;
import net.minecraft.world.level.LevelAccessor;
import net.minecraft.world.level.block.Block;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.Heightmap;
import net.minecraft.world.phys.AABB;
import net.minecraft.world.phys.Vec3;
import net.minecraftforge.registries.ForgeRegistries;
import net.mcreator.phayriosisreborn.config.PhayriosisWorldSettings;

/**
 * Phase 6 expanded meteor impact.
 *
 * The vanilla/Forge Explosion stays authoritative so Explosion Overhaul, Sound
 * Physics and other explosion listeners receive a genuine Minecraft explosion.
 * Phayriosis-specific terrain, magma, chamber/ore content and infected spawns
 * are layered afterward in bounded batches.
 */
public final class PhayriosisMeteorImpactSystem {
    private static final float BLAST_POWER = 30.0F;
    private static final int CORE_RADIUS_XZ = 23;
    private static final int CORE_RADIUS_Y = 12;
    private static final int INFECTION_RADIUS = 60;
    private static final int MAGMA_RADIUS = 54;
    private static final double SHOCKWAVE_RADIUS = 512.0D;
    private static final double SHOCKWAVE_STEP = 5.0D;
    private static final int CHAMBER_RADIUS_XZ = 7;
    private static final int CHAMBER_RADIUS_Y = 5;
    private static final int MIN_SAFE_Y = -62;
    private static final int MAX_SAFE_Y = 318;

    private PhayriosisMeteorImpactSystem() {}

    public static void createImpact(ServerLevel level, double x, double y, double z, Entity source) {
        if (level == null) return;

        // Keep exactly one real Minecraft/Forge explosion as the authoritative blast.
        // Explosion Overhaul hooks the real Explosion object and Sound Physics receives
        // the normal positional sound path instead of a fake custom impact sound.
        // Use a generic/null explosion source deliberately. Explosion Overhaul classifies
        // unknown/generic explosions as DEFAULT and therefore applies its full crater,
        // concussion, distance-aware sound and Sound Physics compatibility pipeline.
        // Using the meteor entity itself as the source can be overridden by a source-mode
        // blacklist and silently fall back to vanilla handling.
        level.m_254849_(null, x, y, z, BLAST_POWER, Level.ExplosionInteraction.BLOCK);

        // Vanilla explosion packets have a relatively short practical hearing range.
        // Add one long-range positional layer at the actual crater. If Explosion Overhaul
        // is installed, use its own high-power far-distance sound asset; otherwise fall
        // back through Ballistix (if present) and finally vanilla. Sound Physics can process
        // all of these because they are emitted through Minecraft's normal positional path.
        playImpactSound(level, x, y, z);
        emitImpactFlash(level, x, y, z, 1.0D);
        net.mcreator.phayriosisreborn.PhayriosisrebornMod.queueServerWork(2,
                () -> emitImpactFlash(level, x, y, z, 0.72D));
        net.mcreator.phayriosisreborn.PhayriosisrebornMod.queueServerWork(5,
                () -> emitImpactFlash(level, x, y, z, 0.42D));

        // Expanding pressure front. Each annulus arrives later than the previous one,
        // making the wave visibly travel outward instead of applying one invisible push
        // inside a small radius. The outer edge is intentionally large enough to reach
        // a player watching from the normal meteor impact distance. 0.0.17.8 matches
        // Ballistix nuclear ground-wave timing: exactly 5 blocks per server tick, and
        // the same annulus applies damage/knockback when the visible front arrives.
        int shockwaveSteps = (int) Math.ceil(SHOCKWAVE_RADIUS / SHOCKWAVE_STEP);
        for (int i = 1; i <= shockwaveSteps; i++) {
            final double inner = (i - 1) * SHOCKWAVE_STEP;
            final double outer = Math.min(i * SHOCKWAVE_STEP, SHOCKWAVE_RADIUS);
            final int delay = 2 + (i - 1);
            net.mcreator.phayriosisreborn.PhayriosisrebornMod.queueServerWork(delay,
                    () -> {
                        applyShockwaveAnnulus(level, x, y, z, inner, outer);
                        emitShockwaveRing(level, x, y, z, outer);
                    });
        }

        PhayriosisWorldSettings settings = PhayriosisWorldSettings.get((net.minecraft.world.level.LevelAccessor) level);
        final boolean infectionEnabled = settings == null || settings.meteorInfectionEnabled;

        // Deposit the 50%-larger body after overhaul crater processing has had time to
        // begin.  Two-column slices keep block-update cost distributed across ticks.
        int group = 0;
        for (int startDx = -CORE_RADIUS_XZ; startDx <= CORE_RADIUS_XZ; startDx += 2) {
            final int from = startDx;
            final int to = Math.min(startDx + 1, CORE_RADIUS_XZ);
            final int delay = 40 + group;
            net.mcreator.phayriosisreborn.PhayriosisrebornMod.queueServerWork(delay,
                    () -> placeBuriedMeteorSlices(level, x, y, z, from, to));
            group++;
        }

        if (infectionEnabled) {
            int infectionGroup = 0;
            for (int startDx = -INFECTION_RADIUS; startDx <= INFECTION_RADIUS; startDx += 4) {
                final int from = startDx;
                final int to = Math.min(startDx + 3, INFECTION_RADIUS);
                final int delay = 52 + infectionGroup;
                net.mcreator.phayriosisreborn.PhayriosisrebornMod.queueServerWork(delay,
                        () -> infectImpactStrips(level, x, z, from, to));
                infectionGroup++;
            }
        }

        // Magma/scorching follows the infection pass and is sparse rather than carpeting
        // the whole crater.  It should read as superheated ground without becoming loot/terrain spam.
        int magmaGroup = 0;
        for (int startDx = -MAGMA_RADIUS; startDx <= MAGMA_RADIUS; startDx += 6) {
            final int from = startDx;
            final int to = Math.min(startDx + 5, MAGMA_RADIUS);
            final int delay = 72 + magmaGroup;
            net.mcreator.phayriosisreborn.PhayriosisrebornMod.queueServerWork(delay,
                    () -> placeMagmaStrips(level, x, z, from, to));
            magmaGroup++;
        }

        // Carve only a modest upper cavity/tunnel through the much larger body, then
        // expose a deliberately small number of ores on the chamber wall.
        net.mcreator.phayriosisreborn.PhayriosisrebornMod.queueServerWork(92,
                () -> carveUpperChamber(level, x, y, z));
        net.mcreator.phayriosisreborn.PhayriosisrebornMod.queueServerWork(94,
                () -> seedRareOres(level, x, y, z));

        // A controlled exterior wave: upgraded in 0.1.3 with six strong guards.
        net.mcreator.phayriosisreborn.PhayriosisrebornMod.queueServerWork(102,
                () -> spawnImpactInfected(level, x, z));

        // Unique chamber guardian: one Nucleus with a 500 HP base; world difficulty scales its final max HP.
        net.mcreator.phayriosisreborn.PhayriosisrebornMod.queueServerWork(108,
                () -> spawnNucleusGuardian(level, x, y, z));

        PhayriosisMeteorLaunchSystem.onMeteorImpact(level, x, y, z);
        System.out.println("[Phayriosis Meteor] Phase 6 expanded impact at (" + x + ", " + y + ", " + z
                + ") power=" + BLAST_POWER + " coreRadius=" + CORE_RADIUS_XZ
                + " infectionRadius=" + INFECTION_RADIUS + " shockwave=" + SHOCKWAVE_RADIUS);
    }

    private static int meteorCenterY(double y) {
        int desiredCenter = floor(y) - 8;
        return Math.max(desiredCenter, MIN_SAFE_Y + CORE_RADIUS_Y);
    }

    private static void placeBuriedMeteorSlices(ServerLevel level, double x, double y, double z, int fromDx, int toDx) {
        BlockState meteor = state("phayriosisreborn", "meteoric_rock");
        BlockState buried = state("phayriosisreborn", "burrowed_meteoric_rock");
        if (meteor == null && buried == null) return;
        if (meteor == null) meteor = buried;
        if (buried == null) buried = meteor;

        int cx = floor(x);
        int cy = meteorCenterY(y);
        int cz = floor(z);

        for (int dx = fromDx; dx <= toDx; dx++) {
            for (int dy = -CORE_RADIUS_Y; dy <= CORE_RADIUS_Y; dy++) {
                for (int dz = -CORE_RADIUS_XZ; dz <= CORE_RADIUS_XZ; dz++) {
                    double nx = (double) dx / (double) CORE_RADIUS_XZ;
                    double ny = (double) dy / (double) CORE_RADIUS_Y;
                    double nz = (double) dz / (double) CORE_RADIUS_XZ;
                    double shell = nx * nx + ny * ny + nz * nz;
                    if (shell > 1.0D) continue;
                    int py = cy + dy;
                    if (py < MIN_SAFE_Y || py > MAX_SAFE_Y) continue;
                    BlockState chosen = shell < 0.48D ? buried : meteor;
                    level.m_7731_(new BlockPos(cx + dx, py, cz + dz), chosen, 2);
                }
            }
        }
    }

    private static void infectImpactStrips(ServerLevel level, double x, double z, int fromDx, int toDx) {
        BlockState flesh = state("phayriosisreborn", "phayrilesh");
        BlockState buriedFlesh = state("phayriosisreborn", "burrowed_phayrilesh");
        BlockState infestation = state("phayriosisreborn", "infestationactive");
        if (flesh == null && buriedFlesh == null && infestation == null) return;
        if (flesh == null) flesh = buriedFlesh != null ? buriedFlesh : infestation;
        if (buriedFlesh == null) buriedFlesh = flesh;

        int cx = floor(x);
        int cz = floor(z);
        double radiusSq = (double) INFECTION_RADIUS * (double) INFECTION_RADIUS;

        for (int dx = fromDx; dx <= toDx; dx++) {
            for (int dz = -INFECTION_RADIUS; dz <= INFECTION_RADIUS; dz++) {
                double distSq = (double) dx * dx + (double) dz * dz;
                if (distSq > radiusSq) continue;
                double normalized = Math.sqrt(distSq) / (double) INFECTION_RADIUS;
                double chance = 0.70D - normalized * 0.48D;
                if (Math.random() > chance) continue;

                int wx = cx + dx;
                int wz = cz + dz;
                int top = level.m_6924_(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, wx, wz);
                int surfaceY = top - 1;
                if (surfaceY < MIN_SAFE_Y || surfaceY > MAX_SAFE_Y) continue;

                BlockState chosen = Math.random() < 0.30D ? buriedFlesh : flesh;
                level.m_7731_(new BlockPos(wx, surfaceY, wz), chosen, 2);
                if (infestation != null && Math.sqrt(distSq) > 14.0D && Math.random() < 0.045D && surfaceY + 1 <= MAX_SAFE_Y) {
                    level.m_7731_(new BlockPos(wx, surfaceY + 1, wz), infestation, 3);
                }
            }
        }
    }

    private static void placeMagmaStrips(ServerLevel level, double x, double z, int fromDx, int toDx) {
        BlockState magma = state("minecraft", "magma_block");
        if (magma == null) return;
        int cx = floor(x);
        int cz = floor(z);
        double radiusSq = (double) MAGMA_RADIUS * (double) MAGMA_RADIUS;
        for (int dx = fromDx; dx <= toDx; dx++) {
            for (int dz = -MAGMA_RADIUS; dz <= MAGMA_RADIUS; dz++) {
                double distSq = (double) dx * dx + (double) dz * dz;
                if (distSq > radiusSq || distSq < 100.0D) continue;
                double normalized = Math.sqrt(distSq) / (double) MAGMA_RADIUS;
                double chance = 0.13D - normalized * 0.08D;
                if (Math.random() > chance) continue;
                int wx = cx + dx;
                int wz = cz + dz;
                int top = level.m_6924_(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, wx, wz);
                int surfaceY = top - 1;
                if (surfaceY < MIN_SAFE_Y || surfaceY > MAX_SAFE_Y) continue;
                level.m_7731_(new BlockPos(wx, surfaceY, wz), magma, 3);
            }
        }
    }

    private static void carveUpperChamber(ServerLevel level, double x, double y, double z) {
        BlockState air = state("minecraft", "air");
        if (air == null) return;
        int cx = floor(x);
        int cy = meteorCenterY(y);
        int cz = floor(z);
        int chamberY = cy + 2;

        for (int dx = -CHAMBER_RADIUS_XZ; dx <= CHAMBER_RADIUS_XZ; dx++) {
            for (int dy = -CHAMBER_RADIUS_Y; dy <= CHAMBER_RADIUS_Y; dy++) {
                for (int dz = -CHAMBER_RADIUS_XZ; dz <= CHAMBER_RADIUS_XZ; dz++) {
                    double nx = (double) dx / (double) CHAMBER_RADIUS_XZ;
                    double ny = (double) dy / (double) CHAMBER_RADIUS_Y;
                    double nz = (double) dz / (double) CHAMBER_RADIUS_XZ;
                    if (nx * nx + ny * ny + nz * nz <= 1.0D) {
                        level.m_7731_(new BlockPos(cx + dx, chamberY + dy, cz + dz), air, 2);
                    }
                }
            }
        }

        // Narrow top entry.  Slightly offset toward +X so the cavity isn't a perfectly
        // symmetrical hollow sphere and only opens through the top portion.
        int topY = cy + CORE_RADIUS_Y + 1;
        for (int py = chamberY + 2; py <= topY; py++) {
            double t = (double) (py - (chamberY + 2)) / Math.max(1.0D, (double) (topY - chamberY - 2));
            int tx = cx + (int) Math.round(t * 3.0D);
            for (int dx = -2; dx <= 2; dx++) {
                for (int dz = -2; dz <= 2; dz++) {
                    if (dx * dx + dz * dz <= 5) {
                        level.m_7731_(new BlockPos(tx + dx, py, cz + dz), air, 2);
                    }
                }
            }
        }
    }

    private static void seedRareOres(ServerLevel level, double x, double y, double z) {
        int cx = floor(x);
        int cy = meteorCenterY(y) + 2;
        int cz = floor(z);
        String[][] pool = new String[][] {
            // Common material still dominates, but 0.0.18.9 makes the world-event chamber
            // materially richer and gives rare ores a real chance to appear.
            {"minecraft", "iron_ore"}, {"minecraft", "iron_ore"}, {"minecraft", "iron_ore"},
            {"minecraft", "iron_ore"}, {"minecraft", "iron_ore"}, {"minecraft", "iron_ore"},
            {"minecraft", "iron_ore"}, {"minecraft", "iron_ore"}, {"minecraft", "iron_ore"},
            {"minecraft", "iron_ore"}, {"minecraft", "iron_ore"}, {"minecraft", "iron_ore"},
            {"minecraft", "iron_ore"}, {"minecraft", "emerald_ore"}, {"minecraft", "diamond_ore"},
            {"minecraft", "gold_ore"},
            {"phayriosisreborn", "biometal_ore"}, {"phayriosisreborn", "biometal_ore"},
            {"phayriosisreborn", "biometal_ore"}, {"phayriosisreborn", "biometal_ore"},
            {"phayriosisreborn", "biometal_ore"}, {"phayriosisreborn", "biometal_ore"},
            {"phayriosisreborn", "biometal_ore"}, {"phayriosisreborn", "biometal_ore"},
            {"phayriosisreborn", "biometal_ore"}, {"phayriosisreborn", "biometal_ore"},
            {"phayriosisreborn", "plutonium_ore"}, {"phayriosisreborn", "crimsonscale_ore"},
            {"minecraft", "copper_ore"}, {"minecraft", "copper_ore"},
            {"minecraft", "copper_ore"}, {"minecraft", "copper_ore"},
            {"minecraft", "copper_ore"}, {"minecraft", "copper_ore"},
            {"minecraft", "copper_ore"}, {"minecraft", "gold_ore"},
            {"minecraft", "gold_ore"}, {"minecraft", "gold_ore"}, {"minecraft", "gold_ore"},
            {"minecraft", "gold_ore"}, {"minecraft", "gold_ore"}, {"minecraft", "gold_ore"},
            {"phayriosisreborn", "crimsonscale_ore"}, {"phayriosisreborn", "crimsonscale_ore"},
            {"phayriosisreborn", "crimsonscale_ore"}, {"phayriosisreborn", "crimsonscale_ore"},
            {"minecraft", "emerald_ore"}, {"minecraft", "diamond_ore"},
            {"phayriosisreborn", "plutonium_ore"}
        };

        // 0.0.18.9: major world-event reward pass. Target 70-80 unique exposed blocks.
        // Rare ores are more visible than before but remain a minority of the 49-entry pool.
        int targetCount = 70 + (int) (Math.random() * 11.0D);
        Set<String> used = new HashSet<>();
        int placed = 0;
        int attempts = 0;
        while (placed < targetCount && attempts++ < targetCount * 12) {
            double vertical = -3.0D + Math.random() * 7.0D;
            double normalizedY = vertical / (double) CHAMBER_RADIUS_Y;
            double wallRadius = (double) CHAMBER_RADIUS_XZ
                    * Math.sqrt(Math.max(0.05D, 1.0D - normalizedY * normalizedY));
            double angle = Math.random() * Math.PI * 2.0D;
            double radial = wallRadius + 0.25D + Math.random() * 0.55D;
            int ox = (int) Math.round(Math.cos(angle) * radial);
            int oz = (int) Math.round(Math.sin(angle) * radial);
            int oy = (int) Math.round(vertical);
            String key = ox + ":" + oy + ":" + oz;
            if (!used.add(key)) continue;

            String[] id = pool[(int) (Math.random() * pool.length)];
            BlockState ore = state(id[0], id[1]);
            if (ore == null) continue;
            level.m_7731_(new BlockPos(cx + ox, cy + oy, cz + oz), ore, 2);
            placed++;
        }
        System.out.println("[Phayriosis Meteor] seeded " + placed + " exposed chamber ores");
    }

    private static void spawnImpactInfected(ServerLevel level, double x, double z) {
        String[] ids = new String[] {
            "converted_human", "converted_human", "converted_human", "converted_human",
            "converted_human", "converted_human",
            "patroller", "patroller", "patroller", "patroller",
            "infester", "infester", "infester",
            "gory_mass", "gory_mass",
            "assaulter", "assaulter",
            "spitter", "spitter",
            "carrier",
            // 0.1.3: six stronger outer guards, using the same protected meteor spawn path.
            "primal_hunter", "primal_hunter",
            "primal_assaulter", "primal_assaulter",
            "primal_tank", "gory_beast"
        };
        int cx = floor(x);
        int cz = floor(z);
        for (String id : ids) {
            double angle = Math.random() * Math.PI * 2.0D;
            double distance = 20.0D + Math.random() * 28.0D;
            int sx = cx + (int) Math.round(Math.cos(angle) * distance);
            int sz = cz + (int) Math.round(Math.sin(angle) * distance);
            int sy = level.m_6924_(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, sx, sz);
            spawnEntity(level, "phayriosisreborn", id, new BlockPos(sx, sy, sz));
        }
    }

    private static void spawnNucleusGuardian(ServerLevel level, double x, double y, double z) {
        int cx = floor(x);
        int cy = meteorCenterY(y) + 2;
        int cz = floor(z);
        Entity entity = spawnEntity(level, "phayriosisreborn", "nucleus", new BlockPos(cx, cy - 3, cz));
        if (entity instanceof LivingEntity living) {
            AttributeInstance maxHealth = living.m_21051_(Attributes.f_22276_);
            if (maxHealth != null) maxHealth.m_22100_(500.0D);
            living.m_21153_(living.m_21233_());
            System.out.println("[Phayriosis Meteor] spawned 500-base-HP Nucleus guardian inside meteor chamber");
        }
    }

    private static Entity spawnEntity(ServerLevel level, String namespace, String path, BlockPos pos) {
        EntityType<?> type = ForgeRegistries.ENTITY_TYPES.getValue(new ResourceLocation(namespace, path));
        if (type == null) return null;
        return MeteorSpawnScope.spawn(type, level, pos, MobSpawnType.MOB_SUMMONED);
    }

    private static void applyShockwaveAnnulus(ServerLevel level, double x, double y, double z,
                                               double innerRadius, double outerRadius) {
        Vec3 center = new Vec3(x, y, z);
        AABB box = new AABB(center, center).m_82400_(outerRadius + 5.0D);
        Predicate<Entity> all = e -> true;
        List<Entity> entities = ((LevelAccessor) level).m_6443_(Entity.class, box, all);
        DamageSource damageSource = level.m_269111_().m_269036_(null, null);
        for (Entity entity : entities) {
            double dx = entity.m_20185_() - x;
            double dy = entity.m_20186_() - y;
            double dz = entity.m_20189_() - z;
            if (Math.abs(dy) > 96.0D) continue;
            double horizontal = Math.sqrt(dx * dx + dz * dz);
            if (horizontal < 0.25D || horizontal <= innerRadius || horizontal > outerRadius + 5.0D) continue;

            double normalized = Math.min(1.0D, horizontal / SHOCKWAVE_RADIUS);
            double falloff = 1.0D - normalized;
            double strength = 0.38D + falloff * 2.75D;
            double vertical = 0.12D + falloff * 0.72D;
            double dirX = dx / horizontal;
            double dirZ = dz / horizontal;
            entity.m_5997_(dirX * strength, vertical, dirZ * strength);

            // Damage is deliberately distance-scaled: severe near the crater, survivable
            // near the outer edge. This is separate from the initial power-30 blast.
            if (entity instanceof LivingEntity living) {
                float damage = (float) (4.0D + falloff * 30.0D);
                living.m_6469_(damageSource, damage);
            }

            if (entity instanceof ServerPlayer player) {
                // ServerPlayer movement is partly client-authoritative. A small positional
                // displacement guarantees that the player actually feels the pressure front
                // instead of rubber-banding away the server-side impulse.
                double displacement = 0.45D + falloff * 1.35D;
                player.f_8906_.m_9774_(
                        player.m_20185_() + dirX * displacement,
                        player.m_20186_() + Math.min(0.35D, vertical * 0.35D),
                        player.m_20189_() + dirZ * displacement,
                        player.m_146908_(), player.m_146909_());
                emitLocalShockwaveHit(level, player, falloff);
            }
        }
    }

    private static void emitShockwaveRing(ServerLevel level, double x, double y, double z, double radius) {
        // 0.0.17.8: Ballistix nuclear reference pass. The optional bridge uses the
        // Ballistix shockwave particle when Ballistix is present and a forced vanilla
        // ground-ring fallback otherwise. The wavefront is scheduled at exactly 5 blocks/tick.
        PhayriosisMeteorBallistixVisualBridge.emitShockwaveRing(level, x, y, z, radius);
    }

    private static void emitLocalShockwaveHit(ServerLevel level, ServerPlayer player, double strength) {
        ParticleOptions cloud = particle("minecraft", "cloud");
        ParticleOptions poof = particle("minecraft", "poof");
        ParticleOptions flash = particle("minecraft", "flash");
        double px = player.m_20185_();
        double py = player.m_20186_() + 0.8D;
        double pz = player.m_20189_();
        if (cloud != null) level.m_8624_(player, cloud, true, px, py, pz, 24, 2.5D, 1.1D, 2.5D, 0.22D);
        if (poof != null) level.m_8624_(player, poof, true, px, py, pz, 12, 1.8D, 0.8D, 1.8D, 0.18D);
        if (flash != null && strength > 0.35D) level.m_8624_(player, flash, true, px, py + 0.4D, pz, 1, 0.0D, 0.0D, 0.0D, 0.0D);
    }

    private static void playImpactSound(ServerLevel level, double x, double y, double z) {
        // Mirrors Ballistix nuclear delivery behavior without a hard dependency: a loud
        // source at the crater plus a distant directional helper source toward the impact.
        // Explosion Overhaul power-7 close/far events are preferred when installed;
        // Ballistix nuclear or Phayriosis largeexplosion are the optional fallbacks.
        PhayriosisMeteorBallistixVisualBridge.playImpactSound(level, x, y, z);
    }

    private static void emitImpactFlash(ServerLevel level, double x, double y, double z, double scale) {
        // Ballistix' nuclear flash is not a normal vanilla FLASH particle. The bridge
        // reproduces its large white-hot blast-smoke bloom when Ballistix is installed
        // and uses a shader-safe white/yellow/red fallback bloom otherwise.
        PhayriosisMeteorBallistixVisualBridge.emitImpactFlash(level, x, y, z, scale);
    }

    private static ParticleOptions particle(String namespace, String path) {
        Object value = ForgeRegistries.PARTICLE_TYPES.getValue(new ResourceLocation(namespace, path));
        return value instanceof ParticleOptions ? (ParticleOptions) value : null;
    }

    private static SoundEvent sound(String namespace, String path) {
        return ForgeRegistries.SOUND_EVENTS.getValue(new ResourceLocation(namespace, path));
    }

    private static void sendParticleToNearbyPlayers(ServerLevel level, ParticleOptions particle,
                                                     double x, double y, double z, int count,
                                                     double spreadX, double spreadY, double spreadZ, double speed) {
        for (ServerPlayer player : level.m_6907_()) {
            double dx = player.m_20185_() - x;
            double dy = player.m_20186_() - y;
            double dz = player.m_20189_() - z;
            if (dx * dx + dy * dy + dz * dz <= 262144.0D) {
                level.m_8624_(player, particle, true, x, y, z, count, spreadX, spreadY, spreadZ, speed);
            }
        }
    }

    private static BlockState state(String namespace, String path) {
        Block block = ForgeRegistries.BLOCKS.getValue(new ResourceLocation(namespace, path));
        return block == null ? null : block.m_49966_();
    }

    private static int floor(double value) {
        int i = (int) value;
        return value < (double) i ? i - 1 : i;
    }
}
