That's one heck of a commit you've got there...

I may have got a bit behind with version control. A lot behind, in fact. Maybe I'll go back and split this sometime - then again, I probably won't. But hey, at least it's here!
This commit is contained in:
Electroblob77
2019-08-18 00:04:18 +01:00
parent 2680d02304
commit f37812be3e
1564 changed files with 47652 additions and 12984 deletions
@@ -1,12 +1,8 @@
package electroblob.wizardry.spell;
import javax.annotation.Nullable;
import electroblob.wizardry.Wizardry;
import electroblob.wizardry.constants.Element;
import electroblob.wizardry.constants.SpellType;
import electroblob.wizardry.constants.Tier;
import electroblob.wizardry.registry.WizardryItems;
import electroblob.wizardry.util.RayTracer;
import electroblob.wizardry.util.SpellModifiers;
import electroblob.wizardry.util.WizardryUtilities;
import net.minecraft.entity.Entity;
@@ -16,13 +12,14 @@ import net.minecraft.entity.player.EntityPlayer;
import net.minecraft.item.EnumAction;
import net.minecraft.util.EnumFacing;
import net.minecraft.util.EnumHand;
import net.minecraft.util.SoundEvent;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.RayTraceResult;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
import javax.annotation.Nullable;
/**
* Generic superclass for all spells which use a raytrace to do something and (optionally) spawn particles along that
* trajectory. This is for both continuous ('stream') spells and non-continuous ('bolt') spells This allows all the
@@ -30,111 +27,95 @@ import net.minecraft.world.World;
* such must be subclassed to define what the spell actually does. This is because ray-like spells do a wider variety of
* different things, so it does not make sense to define more specific functions in this class since they would be
* redundant in the majority of cases.
* <p>
* <p></p>
* <i>N.B. The three abstract methods in this class have a {@link Nullable} caster parameter (the caster is null when
* the spell is cast by a dispenser). When implementing these methods, be sure to check whether the caster is
* {@code null} and deal with it appropriately.</i>
* <p></p>
* Properties added by this type of spell: {@link Spell#RANGE}
* <p></p>
* By default, this type of spell can be cast by NPCs. {@link Spell#canBeCastByNPCs()}
* <p>
* By default, this type of spell requires a packet to be sent. {@link Spell#doesSpellRequirePacket()}
* <p></p>
* By default, this type of spell can be cast by dispensers. {@link Spell#canBeCastByDispensers()}
* <p></p>
* By default, this type of spell requires a packet to be sent. {@link Spell#requiresPacket()}
*
* @author Electroblob
* @since Wizardry 4.2
* @see SpellStream
*/
public abstract class SpellRay extends Spell {
// TODO: Implement the 'aim assist' (borderSize) effect for rayTracing
/** The distance below the caster's eyes that the bolt particles start from. */
private static final double Y_OFFSET = 0.4;
/** The base range of this spell. */
protected final double baseRange;
/** The sound that gets played when this spell is cast. */
@Nullable
protected final SoundEvent sound;
/** The volume of the sound played when this spell is cast. Defaults to 1. */
protected float volume = 1;
/** The pitch of the sound played when this spell is cast. Defaults to 1. */
protected float pitch = 1;
/** The pitch variation of the sound played when this spell is cast. Defaults to 0. */
protected float pitchVariation = 0;
protected static final double Y_OFFSET = 0.25;
/** The distance between spawned particles. Defaults to 0.85. */
// 0.85 was chosen to keep it similar to the most common method used previously, which gave an effective spacing of
// 10/12 = 0.8333 when the spell did not hit anything.
protected double particleSpacing = 0.85;
/** The maximum dither (random position offset) for spawned particles. Defaults to 0.1. */
protected double particleDither = 0.1;
/** The maximum jitter (random position offset) for spawned particles. Defaults to 0.1. */
protected double particleJitter = 0.1;
/** The velocity of spawned particles in the direction the caster is aiming, can be negative. Defaults to 0. */
protected double particleVelocity = 0;
/** Whether entities are ignored when raytracing. Defaults to false. */
protected boolean ignoreEntities = false;
/** Whether living entities are ignored when raytracing. Defaults to false. */
protected boolean ignoreLivingEntities = false;
/** Whether liquids count as blocks when raytracing. Defaults to false. */
protected boolean hitLiquids = false;
/** Whether to ignore uncollidable blocks when raytracing. Defaults to true. */
protected boolean ignoreUncollidables = true;
/** The aim assist to use when raytracing. Defaults to 0. */
protected float aimAssist = 0;
public SpellRay(String name, Tier tier, Element element, SpellType type, int cost, int cooldown, boolean isContinuous, double baseRange, SoundEvent sound){
this(Wizardry.MODID, name, tier, element, type, cost, cooldown, isContinuous, baseRange, sound);
public SpellRay(String name, boolean isContinuous, EnumAction action){
this(Wizardry.MODID, name, isContinuous, action);
}
public SpellRay(String modID, String name, Tier tier, Element element, SpellType type, int cost, int cooldown, boolean isContinuous, double baseRange, SoundEvent sound){
super(modID, name, tier, element, type, cost, cooldown, EnumAction.NONE, isContinuous);
this.baseRange = baseRange;
this.sound = sound;
public SpellRay(String modID, String name, boolean isContinuous, EnumAction action){
super(modID, name, action, isContinuous);
this.addProperties(RANGE);
}
/**
* Sets the sound parameters for this spell.
* @param volume
* @param pitch
* @param pitchVariation
* @return The spell instance, allowing this method to be chained onto the constructor.
*/
public SpellRay soundValues(float volume, float pitch, float pitchVariation) {
this.volume = volume;
this.pitch = pitch;
this.pitchVariation = pitchVariation;
return this;
}
// Although this class is abstract, someone might instantiate one of its subclasses more than once to make two
// different spells, which may require different parameters. These methods allow such instances to neatly set any
// relevant parameters by chaining them onto the constructor.
/**
* Sets the distance between spawned particles.
* @param particleSpacing The distance between particles in the ray effect.
* @return The spell instance, allowing this method to be chained onto the constructor.
*/
public SpellRay particleSpacing(double particleSpacing){
public Spell particleSpacing(double particleSpacing){
this.particleSpacing = particleSpacing;
return this;
}
/**
* Sets the maximum dither (random position offset) for spawned particles.
* @param particleDither The maximum dither for particles in the ray effect.
* Sets the maximum jitter (random position offset) for spawned particles.
* @param particleJitter The maximum jitter for particles in the ray effect.
* @return The spell instance, allowing this method to be chained onto the constructor.
*/
public SpellRay particleDither(double particleDither){
this.particleDither = particleDither;
public Spell particleJitter(double particleJitter){
this.particleJitter = particleJitter;
return this;
}
/**
* Sets the velocity of spawned particles.
* Sets the velocity of spawned particles; usually used for continuous spells.
* @param particleVelocity The velocity of spawned particles in the direction the caster is aiming, can be negative.
* @return The spell instance, allowing this method to be chained onto the constructor.
*/
public SpellRay particleVelocity(double particleVelocity){
public Spell particleVelocity(double particleVelocity){
this.particleVelocity = particleVelocity;
return this;
}
/**
* Sets whether entities are ignored when raytracing.
* @param ignoreEntities Whether to ignore entities when raytracing. If this is true, the spell will pass through
* entities as if they weren't there.
* @param ignoreLivingEntities Whether to ignore living entities when raytracing. If this is true, the spell
* will pass through living entities as if they weren't there.
* @return The spell instance, allowing this method to be chained onto the constructor.
*/
public SpellRay ignoreEntities(boolean ignoreEntities){
this.ignoreEntities = ignoreEntities;
public Spell ignoreLivingEntities(boolean ignoreLivingEntities){
this.ignoreLivingEntities = ignoreLivingEntities;
return this;
}
@@ -144,36 +125,47 @@ public abstract class SpellRay extends Spell {
* liquids as if they weren't there.
* @return The spell instance, allowing this method to be chained onto the constructor.
*/
public SpellRay hitLiquids(boolean hitLiquids){
public Spell hitLiquids(boolean hitLiquids){
this.hitLiquids = hitLiquids;
return this;
}
/**
* Sets whether uncollidable blocks are ignored when raytracing.
* @param ignoreUncollidables Whether to hit uncollidable blocks when raytracing. If this is true, the spell will
* pass through uncollidable blocks as if they weren't there.
* @return The spell instance, allowing this method to be chained onto the constructor.
*/
public Spell ignoreUncollidables(boolean ignoreUncollidables){
this.ignoreUncollidables = ignoreUncollidables;
return this;
}
/**
* Sets the aim assist to use when raytracing.
* @param particleVelocity The aim assist to use when raytracing. See {@link WizardryUtilities#rayTrace(World, Vec3d,
* Vec3d, float, boolean, Class, java.util.function.Predicate)} for more details.
* @param aimAssist The aim assist to use when raytracing. See {@link RayTracer#rayTrace(World, Vec3d, Vec3d, float, boolean, boolean, boolean, Class, java.util.function.Predicate)} for more details.
* @return The spell instance, allowing this method to be chained onto the constructor.
*/
public SpellRay aimAssist(float aimAssist){
public Spell aimAssist(float aimAssist){
this.aimAssist = aimAssist;
return this;
}
@Override public boolean canBeCastByNPCs(){ return true; }
@Override public boolean canBeCastByDispensers() { return true; }
// Finally everything in here is standardised and written in a form that's actually readable - it was long overdue!
@Override
public boolean cast(World world, EntityPlayer caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
Vec3d look = caster.getLookVec();
Vec3d origin = new Vec3d(caster.posX, caster.getEntityBoundingBox().minY + caster.getEyeHeight() - Y_OFFSET,
caster.posZ);
Vec3d origin = new Vec3d(caster.posX, caster.getEntityBoundingBox().minY + caster.getEyeHeight() - Y_OFFSET, caster.posZ);
if(!shootSpell(world, origin, look, caster, ticksInUse, modifiers)) return false;
if(!isContinuous) caster.swingArm(hand); // Bit of a dirty fix but I don't think it'll be a problem!
if(sound != null) WizardryUtilities.playSoundAtPlayer(caster, sound, volume, pitch + pitchVariation * (world.rand.nextFloat() - 0.5f));
if(casterSwingsArm(world, caster, hand, ticksInUse, modifiers)) caster.swingArm(hand);
this.playSound(world, caster, ticksInUse, -1, modifiers);
return true;
}
@@ -187,7 +179,8 @@ public abstract class SpellRay extends Spell {
if(target != null){
if(!ignoreEntities && onEntityHit(world, target, caster, ticksInUse, modifiers)){
if((!ignoreLivingEntities || !WizardryUtilities.isLiving(target))
&& onEntityHit(world, target, null, caster, origin, ticksInUse, modifiers)){
direction = new Vec3d(target.posX, target.getEntityBoundingBox().minY + target.height/2, target.posZ)
.subtract(origin);
@@ -203,7 +196,7 @@ public abstract class SpellRay extends Spell {
// This works as if the NPC had actually aimed at the floor beneath the target, so it needs to check
// that the block is not air and (optionally) not a liquid.
if(!world.isAirBlock(pos) && (!world.getBlockState(pos).getMaterial().isLiquid() || hitLiquids)
&& onBlockHit(world, pos, EnumFacing.UP, caster, ticksInUse, modifiers)){
&& onBlockHit(world, pos, EnumFacing.UP, null, caster, origin, ticksInUse, modifiers)){
direction = new Vec3d(x + 0.5, y + 1, z + 0.5).subtract(origin);
flag = true;
@@ -214,118 +207,196 @@ public abstract class SpellRay extends Spell {
// Wizards don't miss... yet
if(!flag) return false;
// Particle spawning (direction should never be null at this point but no harm in checking)
if(world.isRemote && direction != null){
spawnParticleRay(world, origin, direction.normalize(), direction.lengthVector());
// Particle spawning
if(world.isRemote){
spawnParticleRay(world, origin, direction.normalize(), caster, direction.length());
}
if(!isContinuous) caster.swingArm(hand); // Bit of a dirty fix but I don't think it'll be a problem!
if(sound != null) caster.playSound(sound, volume, pitch + pitchVariation * (world.rand.nextFloat() - 0.5f));
if(casterSwingsArm(world, caster, hand, ticksInUse, modifiers)) caster.swingArm(hand);
this.playSound(world, caster, ticksInUse, -1, modifiers);
return true;
}
/** Player and dispenser casting are almost identical so this takes care of the shared stuff. */
private boolean shootSpell(World world, Vec3d origin, Vec3d direction, @Nullable EntityPlayer caster, int ticksInUse, SpellModifiers modifiers){
@Override
public boolean cast(World world, double x, double y, double z, EnumFacing direction, int ticksInUse, int duration, SpellModifiers modifiers){
double range = baseRange * modifiers.get(WizardryItems.range_upgrade);
Vec3d vec = new Vec3d(direction.getDirectionVec());
Vec3d origin = new Vec3d(x, y, z);
if(!shootSpell(world, origin, vec, null, ticksInUse, modifiers)) return false;
// This MUST be the coordinates of the actual dispenser, so we need to offset it
this.playSound(world, x - direction.getXOffset(), y - direction.getYOffset(), z - direction.getZOffset(), ticksInUse, duration, modifiers);
return true;
}
/**
* Hook allowing subclasses to override the default range calculation on a per-cast basis. For example, grapple
* overrides this to change the range based on casting time so that its vine attaches to entities/blocks at the
* correct point and moves them accordingly.
*
* @param world The world in which the spell is being cast.
* @param origin A vector representing the coordinates of the origin point of the spell.
* @param direction A normalised vector representing the direction in which the spell is being cast.
* @param caster The entity casting the spell, or null if the spell is being cast from a dispenser.
* @param ticksInUse The number of ticks the spell has already been cast for. For all non-continuous spells,
* this is 0 and is not used.
* @param modifiers The SpellModifiers object with which this spell is being cast.
* @return The range to be used for this particular casting of the spell.
*/
// Technically you could alter the range in the SpellModifiers object by overriding the cast method but that
// would be a bit of a hack since it's not really what spell modifiers are for.
protected double getRange(World world, Vec3d origin, Vec3d direction, @Nullable EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers){
return getProperty(RANGE).doubleValue() * modifiers.get(WizardryItems.range_upgrade);
}
/**
* Hook allowing subclasses to determine whether the caster swings their arm when casting the spell. By default,
* returns false for continuous spells and true for all others.
*
* @param world A reference to the world object. This is for convenience, you can also use caster.world.
* @param caster The EntityLivingBase that cast the spell.
* @param hand The hand that is holding the item used to cast the spell. If no item was used, this will be the
* main hand.
* @param ticksInUse The number of ticks the spell has already been cast for. For all non-continuous spells, this is
* 0 and is not used. For continuous spells, it is passed in as the maximum use duration of the item minus
* the count parameter in onUsingItemTick and therefore it increases by 1 each tick.
* @return True if the caster should swing their arm when casting this spell, false if not.
*/
protected boolean casterSwingsArm(World world, EntityLivingBase caster, EnumHand hand, int ticksInUse, SpellModifiers modifiers){
return !this.isContinuous;
}
/** Player and dispenser casting are almost identical so this takes care of the shared stuff. This is mainly for internal use. */
protected boolean shootSpell(World world, Vec3d origin, Vec3d direction, @Nullable EntityPlayer caster, int ticksInUse, SpellModifiers modifiers){
double range = getRange(world, origin, direction, caster, ticksInUse, modifiers);
Vec3d endpoint = origin.add(direction.scale(range));
// The first method will hit the first block it touches, passing through entities as if they weren't there.
// The second method will hit the first thing it touches, whether that's a block or an entity. This method now
// returns the exact hit position for entities as well as blocks.
RayTraceResult rayTrace = WizardryUtilities.rayTrace(world, origin, endpoint, aimAssist, hitLiquids, Entity.class, e -> e == caster);
// Change the filter depending on whether living entities are ignored or not
RayTraceResult rayTrace = RayTracer.rayTrace(world, origin, endpoint, aimAssist, hitLiquids,
ignoreUncollidables, false, Entity.class, ignoreLivingEntities ? WizardryUtilities::isLiving
: RayTracer.ignoreEntityFilter(caster));
boolean flag = false;
if(rayTrace != null){
// Doesn't matter which way round these are, they're mutually exclusive
if(rayTrace.typeOfHit == RayTraceResult.Type.ENTITY){
// Do whatever the spell does when it hits an entity
flag = onEntityHit(world, rayTrace.entityHit, caster, ticksInUse, modifiers);
flag = onEntityHit(world, rayTrace.entityHit, rayTrace.hitVec, caster, origin, ticksInUse, modifiers);
// If the spell succeeded, clip the particles to the correct distance so they don't go through the entity
if(flag) range = origin.distanceTo(rayTrace.hitVec);
}else if(rayTrace.typeOfHit == RayTraceResult.Type.BLOCK){
// Do whatever the spell does when it hits an block
flag = onBlockHit(world, rayTrace.getBlockPos(), rayTrace.sideHit, caster, ticksInUse, modifiers);
// If the spell succeeded, clip the particles to the correct distance so they don't go through the block
if(flag) range = origin.distanceTo(rayTrace.hitVec);
flag = onBlockHit(world, rayTrace.getBlockPos(), rayTrace.sideHit, rayTrace.hitVec, caster, origin, ticksInUse, modifiers);
// Clip the particles to the correct distance so they don't go through the block
// Unlike with entities, this is done regardless of whether the spell succeeded, since no spells go
// through blocks (and in fact, even the ray tracer itself doesn't do that)
range = origin.distanceTo(rayTrace.hitVec);
}
}
// If flag is false, either the spell missed or the relevant entity/block hit method returned false
if(!flag && !onMiss(world, caster, ticksInUse, modifiers)) return false;
if(!flag && !onMiss(world, caster, origin, direction, ticksInUse, modifiers)) return false;
// Particle spawning
if(world.isRemote){
spawnParticleRay(world, origin, direction, range);
spawnParticleRay(world, origin, direction, caster, range);
}
return true;
}
// Private helper method, no-one will need to override it since it's pretty much the whole point of this class.
private void spawnParticleRay(World world, Vec3d origin, Vec3d direction, double distance){
Vec3d velocity = direction.scale(particleVelocity);
for(double d = particleSpacing; d <= distance; d += particleSpacing){
double x = origin.x + d*direction.x + particleDither * (world.rand.nextDouble()*2 - 1);
double y = origin.y + d*direction.y + particleDither * (world.rand.nextDouble()*2 - 1);
double z = origin.z + d*direction.z + particleDither * (world.rand.nextDouble()*2 - 1);
spawnParticle(world, x, y, z, velocity.x, velocity.y, velocity.z);
}
}
// The exact behaviour of the returned values of the following three methods can be a little confusing. Normally,
// either onEntityHit or onBlockHit (or both) will return true when the spell succeeded in hitting the block or
// entity, and false if not (note that those two methods are mutually exclusive). If false is returned, onMiss will
// be called - onMiss will never be called if either of the other methods returns true.
// be called - if either of the other methods returns true, onMiss will only be called for a complete miss.
/**
* Called when the spell hits an entity. Will never be called if ignoreEntities is true.
* Called when the spell hits an entity. Will never be called if ignoreLivingEntities is true.
* @param world The world the entity is in.
* @param target The entity that was hit.
* @param caster The caster of this spell, or null if this spell was cast from a dispenser.
* @param hit A vector representing the exact position at which the spell first hit the entity. Usually used for
* particle spawning.
* @param caster The caster of this spell, or null if this spell was cast from a dispenser. <i> N.B. It is strongly
* recommended that the origin parameter is used instead of taking the caster's position directly.</i>
* @param origin The position at which this spell originated. If the caster is not null, this will be at the caster's
* eyes.
* @param ticksInUse The number of ticks the spell has already been cast for (used only for continuous spells).
* @param modifiers The modifiers this spell was cast with.
* @return True to continue with spell casting and spawn particles, false to trigger a miss (N.B. you will need to
* return false from {@link SpellRay#onMiss(World, EntityLivingBase, int, SpellModifiers)} if a miss should not consume
* mana).
* return false from {@link SpellRay#onMiss(World, EntityLivingBase, Vec3d, Vec3d, int, SpellModifiers)} if a miss
* should not consume mana). Returning false from this method will make it look as if the spell passed right
* through it, so if a spell spawns particles when it misses this method should return true even for non-living
* entities.
*/
protected abstract boolean onEntityHit(World world, Entity target, @Nullable EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers);
protected abstract boolean onEntityHit(World world, Entity target, Vec3d hit, @Nullable EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers);
/**
* Called when the spell hits a block.
* @param world The world the block is in.
* @param pos The BlockPos of the block that was hit.
* @param side The side of the block that was hit.
* @param hit A vector representing the exact position at which the spell first hit the block. Usually used for
* particle spawning.
* @param caster The caster of this spell, or null if this spell was cast from a dispenser.
* @param origin The position at which this spell originated. If the caster is not null, this will be at the caster's
* eyes.
* @param ticksInUse The number of ticks the spell has already been cast for (used only for continuous spells).
* @param modifiers The modifiers this spell was cast with.
* @return True to continue with spell casting and spawn particles, false to trigger a miss (N.B. you will need to
* return false from {@link SpellRay#onMiss(World, EntityLivingBase, int, SpellModifiers)} if a miss should not consume
* return false from {@link SpellRay#onMiss(World, EntityLivingBase, Vec3d, Vec3d, int, SpellModifiers)} if a miss should not consume
* mana).
*/
protected abstract boolean onBlockHit(World world, BlockPos pos, EnumFacing side, @Nullable EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers);
protected abstract boolean onBlockHit(World world, BlockPos pos, EnumFacing side, Vec3d hit, @Nullable EntityLivingBase caster, Vec3d origin, int ticksInUse, SpellModifiers modifiers);
/**
* Called when the spell does not hit anything or when the spell hits something it has no effect on. Most of the time
* this will just return true or false, but some spells may, for example, display a chat readout.
* this will just return true or false, but some spells may, for example, display a chat readout or spawn custom
* particles. It is worth noting that this can affect how easy the spell is to identify.
* @param world The world the spell is in.
* @param caster The caster of this spell, or null if this spell was cast from a dispenser.
* @param origin The position at which this spell originated. If the caster is not null, this will be at the caster's
* eyes.
* @param direction A normalised vector in the direction this spell was cast (useful for custom particle effects).
* @param ticksInUse The number of ticks the spell has already been cast for (used only for continuous spells).
* @param modifiers The modifiers this spell was cast with.
* @return True to continue with spell casting and spawn particles, false to cause the spell to fail.
*/
protected abstract boolean onMiss(World world, @Nullable EntityLivingBase caster, int ticksInUse, SpellModifiers modifiers);
protected abstract boolean onMiss(World world, @Nullable EntityLivingBase caster, Vec3d origin, Vec3d direction, int ticksInUse, SpellModifiers modifiers);
/**
* Highest-level particle spawning method, only called client-side. 'Normal' subclasses should not need to override
* this method; by default it spawns a line of particles, applying jitter and then calling
* {@link SpellRay#spawnParticle(World, double, double, double, double, double, double)} at each point. Override to replace this with
* an entirely custom particle effect - this is done by a few spells in the main mod to spawn beam-type particles.
* @param world The world in which to spawn the particles.
* @param origin A vector representing the start point of the line of particles.
* @param direction A normalised vector representing the direction of the line of particles.
* @param caster The entity that cast this spell, or null if it was cast by a dispenser.
* @param distance The length of the line of particles, already set to the appropriate distance based on the spell's
*/
// The caster argument is only really useful for spawning targeted particles continuously
protected void spawnParticleRay(World world, Vec3d origin, Vec3d direction, EntityLivingBase caster, double distance){
Vec3d velocity = direction.scale(particleVelocity);
for(double d = particleSpacing; d <= distance; d += particleSpacing){
double x = origin.x + d*direction.x + particleJitter * (world.rand.nextDouble()*2 - 1);
double y = origin.y + d*direction.y + particleJitter * (world.rand.nextDouble()*2 - 1);
double z = origin.z + d*direction.z + particleJitter * (world.rand.nextDouble()*2 - 1);
spawnParticle(world, x, y, z, velocity.x, velocity.y, velocity.z);
}
}
/**
* Called at each point along the spell trajectory to spawn one or more particles at that point. Only called
* client-side. Does nothing by default.
* @param world The world in which to spawn the particle.
* @param x The x-coordinate to spawn the particle at, with dither already applied.
* @param y The y-coordinate to spawn the particle at, with dither already applied.
* @param z The z-coordinate to spawn the particle at, with dither already applied.
* @param x The x-coordinate to spawn the particle at, with jitter already applied.
* @param y The y-coordinate to spawn the particle at, with jitter already applied.
* @param z The z-coordinate to spawn the particle at, with jitter already applied.
* @param vx The x velocity to spawn the particle with. Usually this is only non-zero for continuous spells.
* @param vy The y velocity to spawn the particle with. Usually this is only non-zero for continuous spells.
* @param vz The z velocity to spawn the particle with. Usually this is only non-zero for continuous spells.