package net.minecraft.network.protocol.game;

import net.minecraft.core.BlockPos;
import net.minecraft.core.particles.ParticleType;
import net.minecraft.core.particles.ParticleOptions;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.network.protocol.Packet;

import com.bergerkiller.bukkit.common.wrappers.ParticleRandomization;

import com.bergerkiller.generated.net.minecraft.network.protocol.game.ClientboundLevelParticlesPacketHandle;

class ClientboundLevelParticlesPacket extends Packet {

    // On older versions of Minecraft, the particle fields are all private and require reflection to access
    // There are no getter methods for these fields.
#if version < 1.17
  #if version >= 1.15
    #require ClientboundLevelParticlesPacket private double posX:a;
    #require ClientboundLevelParticlesPacket private double posY:b;
    #require ClientboundLevelParticlesPacket private double posZ:c;
    #require ClientboundLevelParticlesPacket private float randomX:d;
    #require ClientboundLevelParticlesPacket private float randomY:e;
    #require ClientboundLevelParticlesPacket private float randomZ:f;
    #require ClientboundLevelParticlesPacket private float speed:g;
    #require ClientboundLevelParticlesPacket private int count:h;
    #require ClientboundLevelParticlesPacket private boolean overrideLimiter:i;
    #require net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket private ParticleOptions particle:j;
  #elseif version >= 1.13
    #require ClientboundLevelParticlesPacket private float posX:a;
    #require ClientboundLevelParticlesPacket private float posY:b;
    #require ClientboundLevelParticlesPacket private float posZ:c;
    #require ClientboundLevelParticlesPacket private float randomX:d;
    #require ClientboundLevelParticlesPacket private float randomY:e;
    #require ClientboundLevelParticlesPacket private float randomZ:f;
    #require ClientboundLevelParticlesPacket private float speed:g;
    #require ClientboundLevelParticlesPacket private int count:h;
    #require ClientboundLevelParticlesPacket private boolean overrideLimiter:i;
    #require net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket private ParticleOptions particle:j;
  #else
    #require ClientboundLevelParticlesPacket private ParticleType particle:a;
    #require ClientboundLevelParticlesPacket private float posX:b;
    #require ClientboundLevelParticlesPacket private float posY:c;
    #require ClientboundLevelParticlesPacket private float posZ:d;
    #require ClientboundLevelParticlesPacket private float randomX:e;
    #require ClientboundLevelParticlesPacket private float randomY:f;
    #require ClientboundLevelParticlesPacket private float randomZ:g;
    #require ClientboundLevelParticlesPacket private float speed:h;
    #require ClientboundLevelParticlesPacket private int count:i;
    #require ClientboundLevelParticlesPacket private boolean overrideLimiter:j;
    #require ClientboundLevelParticlesPacket private int[] particleOptions:k;
  #endif
#endif

    // ParticleType (from options)
    // It is too difficult to expose the full particle options, but type is still useful.
#if version >= 1.13
    public (com.bergerkiller.bukkit.common.resources.ParticleType<?>) ParticleType<?> getParticleType() {
  #if version >= 26.3
        ParticleOptions param = instance.particle();
  #elseif version >= 1.18
        ParticleOptions param = instance.getParticle();
  #elseif version >= 1.17
        ParticleOptions param = instance.k();
  #else
        ParticleOptions param = instance#particle;
  #endif

        if (param == null) {
            return null;
        }

  #if version >= 1.18
        return param.getType();
  #elseif version >= 1.14.4
        return param.getParticle();
  #else
        return param.b();
  #endif
    }
#else
    public (com.bergerkiller.bukkit.common.resources.ParticleType<?>) ParticleType<?> getParticleType() {
        return instance#particle;
    }
#endif

    // Particle spawn configuration, such as position, speed and spread
#if version >= 26.3
    public boolean isOverrideLimiter:overrideLimiter();
    public double getPosX:x();
    public double getPosY:y();
    public double getPosZ:z();
    public float getRandomX:xDist();
    public float getRandomY:yDist();
    public float getRandomZ:zDist();
    public float getMaxSpeedX:xMaxSpeed();
    public float getMaxSpeedY:yMaxSpeed();
    public float getMaxSpeedZ:zMaxSpeed();
    public int getCount:count();
#elseif version >= 1.18
    public boolean isOverrideLimiter();
    public double getPosX:getX();
    public double getPosY:getY();
    public double getPosZ:getZ();
    public float getRandomX:getXDist();
    public float getRandomY:getYDist();
    public float getRandomZ:getZDist();
    public float getMaxSpeedX:getMaxSpeed();
    public float getMaxSpeedY:getMaxSpeed();
    public float getMaxSpeedZ:getMaxSpeed();
    public int getCount();
#elseif version >= 1.17
    public boolean isOverrideLimiter:b();
    public double getPosX:c();
    public double getPosY:d();
    public double getPosZ:e();
    public float getRandomX:f();
    public float getRandomY:g();
    public float getRandomZ:h();
    public float getMaxSpeedX:i();
    public float getMaxSpeedY:i();
    public float getMaxSpeedZ:i();
    public int getCount:j();
#else
    public boolean isOverrideLimiter() { return instance#overrideLimiter; }
    public double getPosX() { return (double) instance#posX; }
    public double getPosY() { return (double) instance#posY; }
    public double getPosZ() { return (double) instance#posZ; }
    public float getRandomX() { return instance#randomX; }
    public float getRandomY() { return instance#randomY; }
    public float getRandomZ() { return instance#randomZ; }
    public float getMaxSpeedX() { return instance#speed; }
    public float getMaxSpeedY() { return instance#speed; }
    public float getMaxSpeedZ() { return instance#speed; }
    public int getCount() { return instance#count; }
#endif

#if version >= 1.21.4
    public boolean isAlwaysShow:alwaysShow();
#else
    public boolean isAlwaysShow() { return false; }
#endif

#if version >= 26.3
    public (ParticleRandomization) net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket$RandomizationType getRandomization:randomizationType();
#else
    public (ParticleRandomization) net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket$RandomizationType getRandomization() {
        return net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket$RandomizationType.DEFAULT;
    }
#endif

    // Encodes ParticleType + options into a ParticleOptions object, which is then assigned to the packet
#if version >= 1.13
    #require ClientboundLevelParticlesPacketHandle public static ParticleOptions createParticleOptions(ParticleType<?> particleType, Object options) {
        // Particle type has no options (is its own options)
        if (particleType instanceof ParticleOptions) {
            return (ParticleOptions) particleType;
        }

        // No options were specified, but this particle type requires it
        if (options == null) {
            com.bergerkiller.bukkit.common.resources.ParticleType typeHandle;
            typeHandle = com.bergerkiller.bukkit.common.resources.ParticleType.byNMSParticleHandle(particleType);
            throw new IllegalArgumentException("Particle type " + typeHandle.toString() + " requires extra options, none specified");
        }

        // Block state option
        if (options instanceof com.bergerkiller.bukkit.common.wrappers.BlockData) {
            return new net.minecraft.core.particles.BlockParticleOption(particleType,
                    (BlockState) ((com.bergerkiller.bukkit.common.wrappers.BlockData) options).getData());
        }

        // ItemStack option
        if (options instanceof org.bukkit.inventory.ItemStack) {
            net.minecraft.world.item.ItemStack itemstack = (net.minecraft.world.item.ItemStack) com.bergerkiller.bukkit.common.conversion.type.HandleConversion
                    .toItemStackHandle((org.bukkit.inventory.ItemStack) options);
  #if version >= 26.1
            return new net.minecraft.core.particles.ItemParticleOption(particleType, itemstack.getItem());
  #else
            return new net.minecraft.core.particles.ItemParticleOption(particleType, itemstack);
  #endif
        }

  #if version >= 1.19
        // Sculk charge options
        if (options instanceof com.bergerkiller.bukkit.common.resources.ParticleType$SculkChargeOptions) {
            com.bergerkiller.bukkit.common.resources.ParticleType$SculkChargeOptions sc = (com.bergerkiller.bukkit.common.resources.ParticleType$SculkChargeOptions) options;
            return new net.minecraft.core.particles.SculkChargeParticleOptions(sc.roll);
        }

        // Shrieker options
        if (options instanceof com.bergerkiller.bukkit.common.resources.ParticleType$ShriekOptions) {
            com.bergerkiller.bukkit.common.resources.ParticleType$ShriekOptions sh = (com.bergerkiller.bukkit.common.resources.ParticleType$ShriekOptions) options;
            return new net.minecraft.core.particles.ShriekParticleOption(sh.delay);
        }
  #endif

        // Dust color transition options
  #if version >= 1.17
        if (options instanceof com.bergerkiller.bukkit.common.resources.ParticleType$DustColorTransitionOptions) {
            com.bergerkiller.bukkit.common.resources.ParticleType$DustColorTransitionOptions ts = (com.bergerkiller.bukkit.common.resources.ParticleType$DustColorTransitionOptions) options;
    #if version >= 1.21.2
            int color = ts.color.asRGB();
            int to_color = ts.endColor.asRGB();
    #elseif version >= 1.19.3
            org.joml.Vector3f color = new org.joml.Vector3f((float) ts.color.getRed() / 255.0f,
                                                            (float) ts.color.getGreen() / 255.0f,
                                                            (float) ts.color.getBlue() / 255.0f);
            org.joml.Vector3f to_color = new org.joml.Vector3f((float) ts.endColor.getRed() / 255.0f,
                                                               (float) ts.endColor.getGreen() / 255.0f,
                                                               (float) ts.endColor.getBlue() / 255.0f);
    #else
            com.mojang.math.Vector3fa color = new com.mojang.math.Vector3fa((float) ts.color.getRed() / 255.0f,
                                                                            (float) ts.color.getGreen() / 255.0f,
                                                                            (float) ts.color.getBlue() / 255.0f);
            com.mojang.math.Vector3fa to_color = new com.mojang.math.Vector3fa((float) ts.endColor.getRed() / 255.0f,
                                                                               (float) ts.endColor.getGreen() / 255.0f,
                                                                               (float) ts.endColor.getBlue() / 255.0f);
    #endif
            return new net.minecraft.core.particles.DustColorTransitionOptions(color, to_color, ts.scale);
        }
  #endif

        // Vibration options
  #if version >= 1.17
        if (options instanceof com.bergerkiller.bukkit.common.resources.ParticleType$VibrationOptions) {
    #if version >= 1.19
            #require net.minecraft.world.level.gameevent.EntityPositionSource static EntityPositionSource new_eps:<init>(com.mojang.datafixers.util.Either<net.minecraft.world.entity.Entity, com.mojang.datafixers.util.Either<java.util.UUID, Integer>> either, float f);
    #endif
            com.bergerkiller.bukkit.common.resources.ParticleType$VibrationOptions vo = (com.bergerkiller.bukkit.common.resources.ParticleType$VibrationOptions) options;
            net.minecraft.world.level.gameevent.PositionSource source;
            if (vo.destination instanceof com.bergerkiller.bukkit.common.resources.ParticleType$BlockPositionOption) {
                com.bergerkiller.bukkit.common.resources.ParticleType$BlockPositionOption dest = (com.bergerkiller.bukkit.common.resources.ParticleType$BlockPositionOption) vo.destination;
                source = new net.minecraft.world.level.gameevent.BlockPositionSource(new BlockPos(dest.x, dest.y, dest.z));
            } else if (vo.destination instanceof com.bergerkiller.bukkit.common.resources.ParticleType$EntityByIdPositionOption) {
                com.bergerkiller.bukkit.common.resources.ParticleType$EntityByIdPositionOption pos = (com.bergerkiller.bukkit.common.resources.ParticleType$EntityByIdPositionOption) vo.destination;
    #if version >= 1.19
                source = #new_eps(com.mojang.datafixers.util.Either.right((Object)com.mojang.datafixers.util.Either.right((Object)Integer.valueOf(pos.entityId))), pos.yOffset);
    #else
                source = new net.minecraft.world.level.gameevent.EntityPositionSource(pos.entityId);
    #endif
    #if version >= 1.19
            } else if (vo.destination instanceof com.bergerkiller.bukkit.common.resources.ParticleType$EntityByUUIDPositionOption) {
                com.bergerkiller.bukkit.common.resources.ParticleType$EntityByUUIDPositionOption pos = (com.bergerkiller.bukkit.common.resources.ParticleType$EntityByUUIDPositionOption) vo.destination;
                source = #new_eps(com.mojang.datafixers.util.Either.right((Object)com.mojang.datafixers.util.Either.left((Object)pos.entityUUID)), pos.yOffset);
    #endif
            } else if (vo.destination == null) {
                throw new IllegalArgumentException("Vibration option destination is null");
            } else {
                throw new IllegalArgumentException("Unknown Vibration position source: " + vo.destination);
            }

    #if version >= 1.19
            return new net.minecraft.core.particles.VibrationParticleOption(source, vo.arrivalInTicks);
    #else
            BlockPos origin = new BlockPos(vo.origin.x, vo.origin.y, vo.origin.z);
            net.minecraft.world.level.gameevent.vibrations.VibrationPath path = new net.minecraft.world.level.gameevent.vibrations.VibrationPath(origin, source, vo.arrivalInTicks);
            return new net.minecraft.core.particles.VibrationParticleOption(path);
    #endif
        }
  #endif

        // Color options
        if (options instanceof com.bergerkiller.bukkit.common.resources.ParticleType$ColorOptions) {
            com.bergerkiller.bukkit.common.resources.ParticleType$ColorOptions color = (com.bergerkiller.bukkit.common.resources.ParticleType$ColorOptions) options;
  #if version >= 1.20.5
            return net.minecraft.core.particles.ColorParticleOption.create(particleType, color.color.asARGB());
  #else
            throw new IllegalArgumentException("Incompatible particle options: " + options.getClass().getName());
  #endif
        }

        // Dust options
        if (options instanceof com.bergerkiller.bukkit.common.resources.ParticleType$DustOptions) {
            com.bergerkiller.bukkit.common.resources.ParticleType$DustOptions dust = (com.bergerkiller.bukkit.common.resources.ParticleType$DustOptions) options;

  #if version >= 1.21.2
            return new net.minecraft.core.particles.DustParticleOptions(dust.color.asRGB(), dust.scale);
  #else
            float r = (float) dust.color.getRed() / 255.0f;
            float g = (float) dust.color.getGreen() / 255.0f;
            float b = (float) dust.color.getBlue() / 255.0f;
    #if version >= 1.19.3
            return new net.minecraft.core.particles.DustParticleOptions(new org.joml.Vector3f(r,g,b), dust.scale);
    #elseif version >= 1.17
            return new net.minecraft.core.particles.DustParticleOptions(new com.mojang.math.Vector3fa(r,g,b), dust.scale);
    #else
            return new net.minecraft.core.particles.DustParticleOptions(r, g, b, dust.scale);
    #endif
  #endif
        }

        throw new IllegalArgumentException("Unknown particle options: " + options.getClass().getName());
    }
#endif

    // Marked optional so it is not visible by default
    public static optional (ClientboundLevelParticlesPacketHandle) ClientboundLevelParticlesPacket createNew((Object) ParticleType<?> particleType, Object options, double posX, double posY, double posZ, float randomX, float randomY, float randomZ, float maxSpeedX, float maxSpeedY, float maxSpeedZ, int count, boolean overrideLimiter, boolean alwaysShow, (ParticleRandomization) net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket$RandomizationType randomization) {
        // Particle type was not found (conversion error)
        if (particleType == null) {
            throw new IllegalArgumentException("Particle type is not supported");
        }

        // Only one maxSpeed option is available before Minecraft 26.3. Convert it to an average.
#if version < 26.3
        float averageMaxSpeed;
        if (maxSpeedX == maxSpeedY && maxSpeedY == maxSpeedZ) {
            averageMaxSpeed = maxSpeedX;
        } else {
            averageMaxSpeed = (maxSpeedX + maxSpeedY + maxSpeedZ) / 3.0f;
        }
#endif

#if version >= 1.13
        ParticleOptions particleParam = #createParticleOptions(particleType, options);

  #if version < 1.20.5
        // Set RGB fields when using the (ambient) entity effect particles and having colors specified
        if (options instanceof com.bergerkiller.bukkit.common.resources.ParticleType$ColorOptions) {
            com.bergerkiller.bukkit.common.resources.ParticleType$ColorOptions color = (com.bergerkiller.bukkit.common.resources.ParticleType$ColorOptions) options;
            //TODO: Implement this!
        }
  #endif

  #if version >= 26.3
        return new ClientboundLevelParticlesPacket(particleParam, overrideLimiter, alwaysShow, posX, posY, posZ, randomX, randomY, randomZ, maxSpeedX, maxSpeedY, maxSpeedZ, count, randomization);
  #elseif version >= 1.21.4
        return new ClientboundLevelParticlesPacket(particleParam, overrideLimiter, alwaysShow, posX, posY, posZ, randomX, randomY, randomZ, averageMaxSpeed, count);
  #elseif version >= 1.15
        return new ClientboundLevelParticlesPacket(particleParam, overrideLimiter, posX, posY, posZ, randomX, randomY, randomZ, averageMaxSpeed, count);
  #else
        return new ClientboundLevelParticlesPacket(particleParam, overrideLimiter, (float) posX, (float) posY, (float) posZ, randomX, randomY, randomZ, averageMaxSpeed, count);
  #endif

#else
        // Figure out the int[] data for the particle options.
        int[] data;

        if (options instanceof com.bergerkiller.bukkit.common.wrappers.BlockData) {
            data = new int[1];
            data[0] = net.minecraft.world.level.block.Block.getCombinedId(
                    (BlockState) ((com.bergerkiller.bukkit.common.wrappers.BlockData) options).getData());

        } else if (options instanceof org.bukkit.inventory.ItemStack) {
            net.minecraft.world.item.ItemStack itemstack = (net.minecraft.world.item.ItemStack) com.bergerkiller.bukkit.common.conversion.type.HandleConversion
                    .toItemStackHandle((org.bukkit.inventory.ItemStack) options);
            data = new int[1];
            data[0] = net.minecraft.world.item.Item.getId(itemstack.getItem());

        } else if (options instanceof com.bergerkiller.bukkit.common.resources.ParticleType$DustOptions) {
            com.bergerkiller.bukkit.common.resources.ParticleType$DustOptions dust = (com.bergerkiller.bukkit.common.resources.ParticleType$DustOptions) options;

            // rgb is encoded in the random x/y/z of this packet
            randomX = -1.0f + (float) dust.color.getRed() / 255.0f;
            randomY = (float) dust.color.getGreen() / 255.0f;
            randomZ = (float) dust.color.getBlue() / 255.0f;
            averageMaxSpeed = 1.0f;
            count = 0;
            data = new int[0];

        } else if (options instanceof com.bergerkiller.bukkit.common.resources.ParticleType$ColorOptions) {
            // Set RGB fields when using the (ambient) entity effect particles and having colors specified
            //TODO: Implement this!
            com.bergerkiller.bukkit.common.resources.ParticleType$ColorOptions color = (com.bergerkiller.bukkit.common.resources.ParticleType$ColorOptions) options;
            data = new int[0];

        } else {
            data = new int[0];
        }

        return new ClientboundLevelParticlesPacket(particleType, overrideLimiter, (float) posX, (float) posY, (float) posZ, randomX, randomY, randomZ, averageMaxSpeed, count, data);
#endif
    }

    // Clones this packet with the particle spawn configuration changed.
    // The spawned particle type and type options are not changed.
    public (ClientboundLevelParticlesPacketHandle) ClientboundLevelParticlesPacket withConfiguration(double posX, double posY, double posZ, float randomX, float randomY, float randomZ, float maxSpeedX, float maxSpeedY, float maxSpeedZ, int count, boolean overrideLimiter, boolean alwaysShow, (ParticleRandomization) net.minecraft.network.protocol.game.ClientboundLevelParticlesPacket$RandomizationType randomization) {
        // Only one maxSpeed option is available before Minecraft 26.3. Convert it to an average.
#if version < 26.3
        float averageMaxSpeed;
        if (maxSpeedX == maxSpeedY && maxSpeedY == maxSpeedZ) {
            averageMaxSpeed = maxSpeedX;
        } else {
            averageMaxSpeed = (maxSpeedX + maxSpeedY + maxSpeedZ) / 3.0f;
        }
#endif

        // Retrieve old particle options (1.13+)
#if version >= 26.3
        ParticleOptions particleParam = instance.particle();
#elseif version >= 1.18
        ParticleOptions particleParam = instance.getParticle();
#elseif version >= 1.17
        ParticleOptions particleParam = instance.k();
#elseif version >= 1.13
        ParticleOptions particleParam = instance#particle;
#else
        ParticleType particle = instance#particle;
        int[] data = instance#particleOptions;
#endif

#if version >= 26.3
        return new ClientboundLevelParticlesPacket(particleParam, overrideLimiter, alwaysShow, posX, posY, posZ, randomX, randomY, randomZ, maxSpeedX, maxSpeedY, maxSpeedZ, count, randomization);
#elseif version >= 1.21.4
        return new ClientboundLevelParticlesPacket(particleParam, overrideLimiter, alwaysShow, posX, posY, posZ, randomX, randomY, randomZ, averageMaxSpeed, count);
#elseif version >= 1.15
        return new ClientboundLevelParticlesPacket(particleParam, overrideLimiter, posX, posY, posZ, randomX, randomY, randomZ, averageMaxSpeed, count);
#elseif version >= 1.13
        return new ClientboundLevelParticlesPacket(particleParam, overrideLimiter, (float) posX, (float) posY, (float) posZ, randomX, randomY, randomZ, averageMaxSpeed, count);
#else
        return new ClientboundLevelParticlesPacket(particle, overrideLimiter, (float) posX, (float) posY, (float) posZ, randomX, randomY, randomZ, averageMaxSpeed, count, data);
#endif
    }

    <code>
    public static <T> ClientboundLevelParticlesPacketHandle createNew(com.bergerkiller.bukkit.common.resources.ParticleType<T> particleType, T value, double posX, double posY, double posZ, float randomX, float randomY, float randomZ, float maxSpeedX, float maxSpeedY, float maxSpeedZ, int count, boolean overrideLimiter, boolean alwaysShow, ParticleRandomization randomization) {
        return T.createNew.invokeVA(particleType.getRawHandle(), value, posX, posY, posZ, randomX, randomY, randomZ, maxSpeedX, maxSpeedY, maxSpeedZ, count, overrideLimiter, alwaysShow, randomization);
    }

    public static <T> ClientboundLevelParticlesPacketHandle createNew(com.bergerkiller.bukkit.common.resources.ParticleType<T> particleType, T value, double posX, double posY, double posZ, float randomX, float randomY, float randomZ, float maxSpeedX, float maxSpeedY, float maxSpeedZ, int count) {
        return createNew(particleType, value, posX, posY, posZ, randomX, randomY, randomZ, maxSpeedX, maxSpeedY, maxSpeedZ, count, false, false, ParticleRandomization.DEFAULT);
    }

    public static <T> ClientboundLevelParticlesPacketHandle createNew(com.bergerkiller.bukkit.common.resources.ParticleType<T> particleType, T value, double posX, double posY, double posZ) {
        return createNew(particleType, value, posX, posY, posZ, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1, false, false, ParticleRandomization.DEFAULT);
    }

    public static ClientboundLevelParticlesPacketHandle createNew(com.bergerkiller.bukkit.common.resources.ParticleType<Void> particleType, double posX, double posY, double posZ, float randomX, float randomY, float randomZ, float maxSpeedX, float maxSpeedY, float maxSpeedZ, int count, boolean overrideLimiter, boolean alwaysShow, ParticleRandomization randomization) {
        return createNew(particleType, null, posX, posY, posZ, randomX, randomY, randomZ, maxSpeedX, maxSpeedY, maxSpeedZ, count, overrideLimiter, alwaysShow, randomization);
    }

    public static ClientboundLevelParticlesPacketHandle createNew(com.bergerkiller.bukkit.common.resources.ParticleType<Void> particleType, double posX, double posY, double posZ, float randomX, float randomY, float randomZ, float maxSpeedX, float maxSpeedY, float maxSpeedZ, int count) {
        return createNew(particleType, posX, posY, posZ, randomX, randomY, randomZ, maxSpeedX, maxSpeedY, maxSpeedZ, count, false, false, ParticleRandomization.DEFAULT);
    }

    public static ClientboundLevelParticlesPacketHandle createNew(com.bergerkiller.bukkit.common.resources.ParticleType<Void> particleType, double posX, double posY, double posZ) {
        return createNew(particleType, posX, posY, posZ, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 1, false, false, ParticleRandomization.DEFAULT);
    }

    public ClientboundLevelParticlesPacketHandle withRandom(float uniformRandom) {
        return withRandom(uniformRandom, uniformRandom, uniformRandom);
    }

    public ClientboundLevelParticlesPacketHandle withRandom(float randomX, float randomY, float randomZ) {
        return withConfiguration(getPosX(), getPosY(), getPosZ(), randomX, randomY, randomZ, getMaxSpeedX(), getMaxSpeedY(), getMaxSpeedZ(), getCount(), isOverrideLimiter(), isAlwaysShow(), getRandomization());
    }

    public ClientboundLevelParticlesPacketHandle withMaxSpeed(float maxUniformSpeed) {
        return withMaxSpeed(maxUniformSpeed, maxUniformSpeed, maxUniformSpeed);
    }

    public ClientboundLevelParticlesPacketHandle withMaxSpeed(float maxSpeedX, float maxSpeedY, float maxSpeedZ) {
        return withConfiguration(getPosX(), getPosY(), getPosZ(), getRandomX(), getRandomY(), getRandomZ(), maxSpeedX, maxSpeedY, maxSpeedZ, getCount(), isOverrideLimiter(), isAlwaysShow(), getRandomization());
    }

    public ClientboundLevelParticlesPacketHandle withOverrideLimiter() {
        return withConfiguration(getPosX(), getPosY(), getPosZ(), getRandomX(), getRandomY(), getRandomZ(), getMaxSpeedX(), getMaxSpeedY(), getMaxSpeedZ(), getCount(), true, isAlwaysShow(), getRandomization());
    }

    public ClientboundLevelParticlesPacketHandle withAlwaysShow() {
        return withConfiguration(getPosX(), getPosY(), getPosZ(), getRandomX(), getRandomY(), getRandomZ(), getMaxSpeedX(), getMaxSpeedY(), getMaxSpeedZ(), getCount(), isOverrideLimiter(), true, getRandomization());
    }

    public ClientboundLevelParticlesPacketHandle withRandomization(ParticleRandomization randomization) {
        return withConfiguration(getPosX(), getPosY(), getPosZ(), getRandomX(), getRandomY(), getRandomZ(), getMaxSpeedX(), getMaxSpeedY(), getMaxSpeedZ(), getCount(), isOverrideLimiter(), isAlwaysShow(), randomization);
    }
    </code>
}
