Adds code for spawning customizable particle and trail effects
All checks were successful
EpicKnarvik97/KnarLib/pipeline/head This commit looks good
All checks were successful
EpicKnarvik97/KnarLib/pipeline/head This commit looks good
This commit is contained in:
parent
4fb4429f8b
commit
ad5dabc712
167
src/main/java/net/knarcraft/knarlib/particle/ParticleConfig.java
Normal file
167
src/main/java/net/knarcraft/knarlib/particle/ParticleConfig.java
Normal file
@ -0,0 +1,167 @@
|
||||
package net.knarcraft.knarlib.particle;
|
||||
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.configuration.ConfigurationSection;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
/**
|
||||
* A configuration describing a particle
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ParticleConfig {
|
||||
|
||||
private final ParticleMode particleMode;
|
||||
private final Particle particleType;
|
||||
private final int particleAmount;
|
||||
private final double particleDensity;
|
||||
private final double heightOffset;
|
||||
private final double offsetX;
|
||||
private final double offsetY;
|
||||
private final double offsetZ;
|
||||
private final double extra;
|
||||
|
||||
/**
|
||||
* Instantiates a new particle config
|
||||
*
|
||||
* @param particlesSection <p>The configuration section containing the particle's settings</p>
|
||||
*/
|
||||
public ParticleConfig(@NotNull ConfigurationSection particlesSection) {
|
||||
@NotNull Particle particleType;
|
||||
try {
|
||||
particleType = Particle.valueOf(particlesSection.getString("type"));
|
||||
} catch (IllegalArgumentException | NullPointerException exception) {
|
||||
particleType = Particle.ASH;
|
||||
}
|
||||
this.particleType = particleType;
|
||||
this.particleAmount = particlesSection.getInt("amount", 30);
|
||||
this.offsetX = particlesSection.getDouble("offsetX", 0.5);
|
||||
this.offsetY = particlesSection.getDouble("offsetY", 1);
|
||||
this.offsetZ = particlesSection.getDouble("offsetZ", 0.5);
|
||||
this.heightOffset = particlesSection.getDouble("heightOffset", 0.5);
|
||||
this.extra = particlesSection.getDouble("extra", 0);
|
||||
ParticleMode particleMode;
|
||||
try {
|
||||
particleMode = ParticleMode.valueOf(particlesSection.getString("mode"));
|
||||
} catch (IllegalArgumentException | NullPointerException exception) {
|
||||
particleMode = ParticleMode.SINGLE;
|
||||
}
|
||||
this.particleMode = particleMode;
|
||||
|
||||
// Make sure particle density is between 1 (inclusive) and 0 (exclusive)
|
||||
double particleDensity = particlesSection.getDouble("particleDensity", 0.1);
|
||||
if (particleDensity <= 0) {
|
||||
particleDensity = 0.1;
|
||||
} else if (particleDensity > 360) {
|
||||
particleDensity = 360;
|
||||
}
|
||||
this.particleDensity = particleDensity;
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new particle config
|
||||
*
|
||||
* @param particleMode <p>The mode to use when spawning the particle</p>
|
||||
* @param particleType <p>The type of particle to spawn</p>
|
||||
* @param particleAmount <p>The amount of particles to spawn at once</p>
|
||||
* @param particleDensity <p>The density of the particles, if spawning a shape</p>
|
||||
* @param heightOffset <p>The offset above the block to spawn the particle</p>
|
||||
* @param offsetX <p>The x-offset/spread of the spawned particles</p>
|
||||
* @param offsetY <p>The y-offset/spread of the spawned particles</p>
|
||||
* @param offsetZ <p>The z-offset/spread of the spawned particles</p>
|
||||
* @param extra <p>Extra data for the particle. Usage depends on the particle type.</p>
|
||||
*/
|
||||
public ParticleConfig(ParticleMode particleMode, Particle particleType, int particleAmount, double particleDensity,
|
||||
double heightOffset, double offsetX, double offsetY, double offsetZ, double extra) {
|
||||
this.particleMode = particleMode;
|
||||
this.particleType = particleType;
|
||||
this.particleAmount = particleAmount;
|
||||
this.particleDensity = particleDensity;
|
||||
this.heightOffset = heightOffset;
|
||||
this.offsetX = offsetX;
|
||||
this.offsetY = offsetY;
|
||||
this.offsetZ = offsetZ;
|
||||
this.extra = extra;
|
||||
}
|
||||
|
||||
/**
|
||||
* The mode to use when drawing/spawning the particle(s)
|
||||
*
|
||||
* @return <p>The particle mode</p>
|
||||
*/
|
||||
public ParticleMode getParticleMode() {
|
||||
return particleMode;
|
||||
}
|
||||
|
||||
/**
|
||||
* The type of particle to spawn
|
||||
*
|
||||
* @return <p>The particle type</p>
|
||||
*/
|
||||
public Particle getParticleType() {
|
||||
return particleType;
|
||||
}
|
||||
|
||||
/**
|
||||
* The amount of particles to spawn
|
||||
*
|
||||
* @return <p>The amount of particles</p>
|
||||
*/
|
||||
public int getParticleAmount() {
|
||||
return particleAmount;
|
||||
}
|
||||
|
||||
/**
|
||||
* The density of particles to use in shapes closer to 0 causes larger density
|
||||
*
|
||||
* @return <p>The particle density</p>
|
||||
*/
|
||||
public double getParticleDensity() {
|
||||
return particleDensity;
|
||||
}
|
||||
|
||||
/**
|
||||
* The number of blocks above the block the particle(s) should spawn
|
||||
*
|
||||
* @return <p>The y-offset</p>
|
||||
*/
|
||||
public double getHeightOffset() {
|
||||
return heightOffset;
|
||||
}
|
||||
|
||||
/**
|
||||
* The offset/spread of particles in the x-direction
|
||||
*
|
||||
* @return <p>The x-offset</p>
|
||||
*/
|
||||
public double getOffsetX() {
|
||||
return offsetX;
|
||||
}
|
||||
|
||||
/**
|
||||
* The offset/spread of particles in the y-direction
|
||||
*
|
||||
* @return <p>The y-offset</p>
|
||||
*/
|
||||
public double getOffsetY() {
|
||||
return offsetY;
|
||||
}
|
||||
|
||||
/**
|
||||
* The offset/spread of particles in the z-direction
|
||||
*
|
||||
* @return <p>The z-offset</p>
|
||||
*/
|
||||
public double getOffsetZ() {
|
||||
return offsetZ;
|
||||
}
|
||||
|
||||
/**
|
||||
* The extra value to set for the particle. Exactly what it does depends on the particle.
|
||||
*
|
||||
* @return <p>The particle's extra value</p>
|
||||
*/
|
||||
public double getExtra() {
|
||||
return extra;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,38 @@
|
||||
package net.knarcraft.knarlib.particle;
|
||||
|
||||
/**
|
||||
* The mode used for spawning one or more particle(s)
|
||||
*/
|
||||
public enum ParticleMode {
|
||||
|
||||
/**
|
||||
* Spawns the set amount of particles on a single point in the world
|
||||
*/
|
||||
SINGLE,
|
||||
|
||||
/**
|
||||
* Spawns the set amount of particles in a square around the block
|
||||
*/
|
||||
SQUARE,
|
||||
|
||||
/**
|
||||
* Spawns the set amount of particles in a circle around the block
|
||||
*/
|
||||
CIRCLE,
|
||||
|
||||
/**
|
||||
* Spawns the set amount of particles in a pyramid centered on the block
|
||||
*/
|
||||
PYRAMID,
|
||||
|
||||
/**
|
||||
* Spawns the set amount of particles in a sphere centered on the block
|
||||
*/
|
||||
SPHERE,
|
||||
|
||||
/**
|
||||
* Spawns the set amount of particles in a cube centered on the block
|
||||
*/
|
||||
CUBE,
|
||||
|
||||
}
|
@ -0,0 +1,142 @@
|
||||
package net.knarcraft.knarlib.particle;
|
||||
|
||||
import net.knarcraft.knarlib.util.ParticleHelper;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Material;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.Collection;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Supplier;
|
||||
|
||||
/**
|
||||
* A runnable tasks that spawns particles at the given blocks
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ParticleSpawner implements Runnable {
|
||||
|
||||
private final ParticleConfig particleConfig;
|
||||
private final Map<Material, ParticleConfig> materialConfigs;
|
||||
|
||||
private Block processingBlock;
|
||||
private final Collection<Block> blocks;
|
||||
private final Supplier<Collection<Block>> blockSupplier;
|
||||
private final UUID storedCalculationId;
|
||||
|
||||
/**
|
||||
* Instantiates a new particle spawner
|
||||
*
|
||||
* @param particleConfig <p>The configuration for the particle to spawn</p>
|
||||
* @param materialConfigs <p>Extra particle configurations for specific materials</p>
|
||||
* @param blocks <p>The blocks to spawn particles on</p>
|
||||
*/
|
||||
public ParticleSpawner(@NotNull ParticleConfig particleConfig,
|
||||
@NotNull Map<Material, ParticleConfig> materialConfigs, @NotNull Collection<Block> blocks) {
|
||||
this.particleConfig = particleConfig;
|
||||
this.materialConfigs = materialConfigs;
|
||||
this.blocks = blocks;
|
||||
this.blockSupplier = null;
|
||||
|
||||
this.storedCalculationId = UUID.randomUUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Instantiates a new particle spawner
|
||||
*
|
||||
* @param particleConfig <p>The configuration for the particle to spawn</p>
|
||||
* @param materialConfigs <p>Extra particle configurations for specific materials</p>
|
||||
* @param blockSupplier <p>The supplier supplying the blocks to spawn particles on</p>
|
||||
*/
|
||||
public ParticleSpawner(@NotNull ParticleConfig particleConfig,
|
||||
@NotNull Map<Material, ParticleConfig> materialConfigs,
|
||||
@NotNull Supplier<Collection<Block>> blockSupplier) {
|
||||
this.particleConfig = particleConfig;
|
||||
this.materialConfigs = materialConfigs;
|
||||
this.blocks = null;
|
||||
this.blockSupplier = blockSupplier;
|
||||
|
||||
this.storedCalculationId = UUID.randomUUID();
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the id used for stored calculations
|
||||
*
|
||||
* <p>You should run ParticleHelper.clearStoredCalculations(id) with this id when discarding this particle spawner
|
||||
* to prevent a memory leak.</p>
|
||||
*
|
||||
* @return <p>The id used for stored calculations</p>
|
||||
*/
|
||||
public UUID getStoredCalculationId() {
|
||||
return this.storedCalculationId;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
// Use the static collection of blocks, or a dynamically changing supplier
|
||||
Collection<Block> blocksToSpawnOn;
|
||||
if (blocks != null) {
|
||||
blocksToSpawnOn = blocks;
|
||||
} else if (blockSupplier != null) {
|
||||
blocksToSpawnOn = blockSupplier.get();
|
||||
} else {
|
||||
throw new RuntimeException("There is a bug in the plugin code. Please contact the developer!");
|
||||
}
|
||||
|
||||
for (Block block : blocksToSpawnOn) {
|
||||
// Ignore blocks in unloaded chunks
|
||||
if (!block.getChunk().isLoaded()) {
|
||||
continue;
|
||||
}
|
||||
|
||||
Location location = block.getLocation().clone();
|
||||
World world = location.getWorld();
|
||||
if (world == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
spawnParticleForBlock(block, world, location);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns the defined particle for the given block
|
||||
*
|
||||
* @param block <p>The block to spawn a particle effect for</p>
|
||||
* @param world <p>The world the block belongs to</p>
|
||||
* @param location <p>A clone of the block's location</p>
|
||||
*/
|
||||
private void spawnParticleForBlock(@NotNull Block block, @NotNull World world, @NotNull Location location) {
|
||||
// Store the currently processed block for height calculation
|
||||
processingBlock = block;
|
||||
|
||||
double blockHeight = ParticleHelper.getBlockHeight(block);
|
||||
ParticleConfig activeConfig = getParticleConfig();
|
||||
|
||||
switch (getParticleConfig().getParticleMode()) {
|
||||
case SINGLE -> ParticleHelper.spawnParticle(world, location.clone().add(0.5,
|
||||
activeConfig.getHeightOffset(), 0.5), activeConfig, blockHeight);
|
||||
case SQUARE -> ParticleHelper.drawSquare(world, location, activeConfig, blockHeight);
|
||||
case CIRCLE -> ParticleHelper.drawCircle(world, location, activeConfig, blockHeight, storedCalculationId);
|
||||
case PYRAMID -> ParticleHelper.drawPyramid(world, location, activeConfig, blockHeight, storedCalculationId);
|
||||
case SPHERE -> ParticleHelper.drawSphere(world, location, activeConfig, blockHeight, storedCalculationId);
|
||||
case CUBE -> ParticleHelper.drawCube(world, location, activeConfig, blockHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the particle config to use for the current block's material
|
||||
*
|
||||
* @return <p>The particle config to use</p>
|
||||
*/
|
||||
private ParticleConfig getParticleConfig() {
|
||||
ParticleConfig materialConfig = this.materialConfigs.get(processingBlock.getType());
|
||||
if (materialConfig != null) {
|
||||
return materialConfig;
|
||||
}
|
||||
return this.particleConfig;
|
||||
}
|
||||
|
||||
}
|
@ -0,0 +1,116 @@
|
||||
package net.knarcraft.knarlib.particle;
|
||||
|
||||
import net.knarcraft.knarlib.util.ParticleHelper;
|
||||
import org.bukkit.Bukkit;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.Particle;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.entity.Player;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A task for spawning trails behind players
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public class ParticleTrailSpawner implements Runnable {
|
||||
|
||||
private final Set<UUID> playersWithTrails = new HashSet<>();
|
||||
private final Map<UUID, Particle> playerParticles = new HashMap<>();
|
||||
private final Random random = new Random();
|
||||
private final Particle particle;
|
||||
private final boolean randomTrailType;
|
||||
private final List<Particle> randomTrailTypes;
|
||||
|
||||
/**
|
||||
* Instantiates a new particle trail spawner
|
||||
*
|
||||
* @param particle <p>The type of particle used for the trail</p>
|
||||
* @param randomTrailType <p>Whether to use a random trail type each time a player is launched</p>
|
||||
* @param randomTrailTypes <p>The types of particles to use for random trails</p>
|
||||
*/
|
||||
public ParticleTrailSpawner(@NotNull Particle particle, boolean randomTrailType,
|
||||
@NotNull List<Particle> randomTrailTypes) {
|
||||
this.particle = particle;
|
||||
this.randomTrailType = randomTrailType;
|
||||
this.randomTrailTypes = randomTrailTypes;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
Set<UUID> offlinePlayers = new HashSet<>();
|
||||
for (UUID playerId : playersWithTrails) {
|
||||
// Clear offline players from the list
|
||||
Player player = Bukkit.getPlayer(playerId);
|
||||
if (player == null) {
|
||||
offlinePlayers.add(playerId);
|
||||
continue;
|
||||
}
|
||||
|
||||
Location playerLocation = player.getLocation();
|
||||
World playerWorld = playerLocation.getWorld();
|
||||
if (playerWorld == null) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Decide on which type of particle to spawn
|
||||
Particle spawnParticle;
|
||||
if (randomTrailType) {
|
||||
spawnParticle = playerParticles.get(playerId);
|
||||
if (spawnParticle == null) {
|
||||
spawnParticle = this.particle;
|
||||
}
|
||||
} else {
|
||||
spawnParticle = this.particle;
|
||||
}
|
||||
|
||||
// Spawn a trail particle
|
||||
ParticleConfig particleConfig = new ParticleConfig(ParticleMode.SINGLE, spawnParticle,
|
||||
1, 1, 0, 0, 0, 0, 0);
|
||||
ParticleHelper.spawnParticle(playerWorld, playerLocation, particleConfig, 0);
|
||||
}
|
||||
playersWithTrails.removeAll(offlinePlayers);
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the trail behind the player with the given id
|
||||
*
|
||||
* @param playerId <p>The id of the player to remove the trail for</p>
|
||||
*/
|
||||
public void removeTrail(UUID playerId) {
|
||||
this.playersWithTrails.remove(playerId);
|
||||
this.playerParticles.remove(playerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a trail behind the player with the given id
|
||||
*
|
||||
* @param playerId <p>The id of the player to add the trail to</p>
|
||||
*/
|
||||
public void startTrail(UUID playerId) {
|
||||
this.playerParticles.put(playerId, randomParticle());
|
||||
this.playersWithTrails.add(playerId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets a random particle in the whitelist
|
||||
*
|
||||
* @return <p>A random particle</p>
|
||||
*/
|
||||
private Particle randomParticle() {
|
||||
Particle spawnParticle = null;
|
||||
while (spawnParticle == null || spawnParticle.getDataType() != Void.class) {
|
||||
spawnParticle = randomTrailTypes.get(random.nextInt(randomTrailTypes.size()));
|
||||
}
|
||||
return spawnParticle;
|
||||
}
|
||||
|
||||
}
|
242
src/main/java/net/knarcraft/knarlib/util/ParticleHelper.java
Normal file
242
src/main/java/net/knarcraft/knarlib/util/ParticleHelper.java
Normal file
@ -0,0 +1,242 @@
|
||||
package net.knarcraft.knarlib.util;
|
||||
|
||||
import net.knarcraft.knarlib.particle.ParticleConfig;
|
||||
import org.bukkit.Location;
|
||||
import org.bukkit.World;
|
||||
import org.bukkit.block.Block;
|
||||
import org.bukkit.util.BoundingBox;
|
||||
import org.bukkit.util.Vector;
|
||||
import org.jetbrains.annotations.NotNull;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* A helper class for spawning particle effects
|
||||
*/
|
||||
@SuppressWarnings("unused")
|
||||
public final class ParticleHelper {
|
||||
|
||||
private static final Map<UUID, Vector[]> pyramidVectors = new HashMap<>();
|
||||
private static final Map<UUID, Double[][]> circleCoordinates = new HashMap<>();
|
||||
private static final Map<UUID, Double[][]> sphereCoordinates = new HashMap<>();
|
||||
|
||||
private ParticleHelper() {
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes any stored calculations for the given id
|
||||
*
|
||||
* <p>If you frequently change the id without clearing, you'll create a memory leak!</p>
|
||||
*
|
||||
* @param id <p>The id specified when generating a shape</p>
|
||||
*/
|
||||
public static void clearStoredCalculations(UUID id) {
|
||||
pyramidVectors.remove(id);
|
||||
circleCoordinates.remove(id);
|
||||
sphereCoordinates.remove(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a cube of particles at the given location
|
||||
*
|
||||
* <p>It is recommended to only spawn one particle with no spread, and a density between 1 and 0.01</p>
|
||||
*
|
||||
* @param world <p>The world to spawn the particles in</p>
|
||||
* @param location <p>The location of the block to spawn the particles at</p>
|
||||
* @param particleConfig <p>The configuration describing the particle to spawn</p>
|
||||
* @param blockHeight <p>The height of the block to spawn the particle above</p>
|
||||
*/
|
||||
public static void drawCube(@NotNull World world, @NotNull Location location, @NotNull ParticleConfig particleConfig,
|
||||
double blockHeight) {
|
||||
// Draw the top and bottom of the cube
|
||||
drawSquare(world, location, particleConfig, blockHeight);
|
||||
drawSquare(world, location.clone().add(0, 1, 0), particleConfig, blockHeight);
|
||||
|
||||
for (float y = 0; y <= 1; y += particleConfig.getParticleDensity()) {
|
||||
double height = particleConfig.getHeightOffset() + y;
|
||||
spawnParticle(world, location.clone().add(0, height, 0), particleConfig, blockHeight);
|
||||
spawnParticle(world, location.clone().add(0, height, 1), particleConfig, blockHeight);
|
||||
spawnParticle(world, location.clone().add(1, height, 0), particleConfig, blockHeight);
|
||||
spawnParticle(world, location.clone().add(1, height, 1), particleConfig, blockHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a sphere of particles at the given location
|
||||
*
|
||||
* <p>It is recommended to only spawn one particle with no spread, and a density between 4 and 0.1</p>
|
||||
*
|
||||
* @param world <p>The world to spawn the particles in</p>
|
||||
* @param location <p>The location of the block to spawn the particles at</p>
|
||||
* @param particleConfig <p>The configuration describing the particle to spawn</p>
|
||||
* @param blockHeight <p>The height of the block to spawn the particle above</p>
|
||||
* @param storedCalculationsId <p>An id for stored calculations. Use clearStoredCalculations with this id later.</p>
|
||||
*/
|
||||
public static void drawSphere(@NotNull World world, @NotNull Location location, @NotNull ParticleConfig particleConfig,
|
||||
double blockHeight, UUID storedCalculationsId) {
|
||||
// For spheres, densities below 0.1 has weird bugs such as blinking in and out of existence, and floating point
|
||||
// errors when calculating the length of circleCoordinates
|
||||
double density = Math.max(1, particleConfig.getParticleDensity());
|
||||
// Store calculations for improved efficiency
|
||||
if (sphereCoordinates.get(storedCalculationsId) == null) {
|
||||
int length = (int) Math.ceil((180 / density));
|
||||
Double[][] coordinates = new Double[length * 3][];
|
||||
double height = particleConfig.getHeightOffset() + 0.5;
|
||||
int i = 0;
|
||||
for (float x = 0; x < 180; x += density) {
|
||||
if (i >= coordinates.length) {
|
||||
continue;
|
||||
}
|
||||
|
||||
double cos = 0.5 * Math.cos(x);
|
||||
double sin = 0.5 * Math.sin(x);
|
||||
coordinates[i++] = new Double[]{sin + 0.5, height, cos + 0.5};
|
||||
coordinates[i++] = new Double[]{sin + 0.5, height + cos, 0.5};
|
||||
coordinates[i++] = new Double[]{0.5, height + sin, cos + 0.5};
|
||||
}
|
||||
sphereCoordinates.put(storedCalculationsId, coordinates);
|
||||
}
|
||||
|
||||
// Spawn particles on the stored locations, relative to the launchpad
|
||||
for (Double[] sphereCoordinate : sphereCoordinates.get(storedCalculationsId)) {
|
||||
spawnParticle(world, location.clone().add(sphereCoordinate[0], sphereCoordinate[1], sphereCoordinate[2]),
|
||||
particleConfig, blockHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a pyramid of particles at the given location
|
||||
*
|
||||
* <p>It is recommended to only spawn one particle with no spread, and a density between 1 and 0.01</p>
|
||||
*
|
||||
* @param world <p>The world to spawn the particles in</p>
|
||||
* @param location <p>The location of the block to spawn the particles at</p>
|
||||
* @param particleConfig <p>The configuration describing the particle to spawn</p>
|
||||
* @param blockHeight <p>The height of the block to spawn the particle above</p>
|
||||
* @param storedCalculationsId <p>An id for stored calculations. Use clearStoredCalculations with this id later.</p>
|
||||
*/
|
||||
public static void drawPyramid(@NotNull World world, @NotNull Location location, @NotNull ParticleConfig particleConfig,
|
||||
double blockHeight, UUID storedCalculationsId) {
|
||||
// Draw the bottom of the pyramid
|
||||
drawSquare(world, location, particleConfig, blockHeight);
|
||||
|
||||
// Store calculations for improved efficiency
|
||||
if (pyramidVectors.get(storedCalculationsId) == null) {
|
||||
// The 0.5 offsets are required for the angle of the pyramid's 4 lines to be correct
|
||||
double height = particleConfig.getHeightOffset();
|
||||
double coordinateMin = -0.5 * height;
|
||||
double coordinateMax = 1 + (0.5 * height);
|
||||
|
||||
Vector[] vectors = new Vector[5];
|
||||
// The vector from the origin to the top of the pyramid
|
||||
vectors[0] = new Vector(0.5, 1 + height, 0.5);
|
||||
// The vectors from the top of the pyramid towards each corner
|
||||
vectors[1] = new Vector(coordinateMin, 0, coordinateMin).subtract(vectors[0]).normalize();
|
||||
vectors[2] = new Vector(coordinateMax, 0, coordinateMin).subtract(vectors[0]).normalize();
|
||||
vectors[3] = new Vector(coordinateMin, 0, coordinateMax).subtract(vectors[0]).normalize();
|
||||
vectors[4] = new Vector(coordinateMax, 0, coordinateMax).subtract(vectors[0]).normalize();
|
||||
pyramidVectors.put(storedCalculationsId, vectors);
|
||||
}
|
||||
|
||||
Vector[] storedVectors = pyramidVectors.get(storedCalculationsId);
|
||||
Location topLocation = location.clone().add(storedVectors[0]);
|
||||
for (double x = 0; x <= 1.2; x += particleConfig.getParticleDensity()) {
|
||||
spawnParticle(world, topLocation.clone().add(storedVectors[1].clone().multiply(x)), particleConfig, blockHeight);
|
||||
spawnParticle(world, topLocation.clone().add(storedVectors[2].clone().multiply(x)), particleConfig, blockHeight);
|
||||
spawnParticle(world, topLocation.clone().add(storedVectors[3].clone().multiply(x)), particleConfig, blockHeight);
|
||||
spawnParticle(world, topLocation.clone().add(storedVectors[4].clone().multiply(x)), particleConfig, blockHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a circle of particles at the given location
|
||||
*
|
||||
* <p>It is recommended to only spawn one particle with no spread, and a density between 4 and 0.1</p>
|
||||
*
|
||||
* @param world <p>The world to spawn the particles in</p>
|
||||
* @param location <p>The location of the block to spawn the particles at</p>
|
||||
* @param particleConfig <p>The configuration describing the particle to spawn</p>
|
||||
* @param blockHeight <p>The height of the block to spawn the particle above</p>
|
||||
* @param storedCalculationsId <p>An id for stored calculations. Use clearStoredCalculations with this id later.</p>
|
||||
*/
|
||||
public static void drawCircle(@NotNull World world, @NotNull Location location, @NotNull ParticleConfig particleConfig,
|
||||
double blockHeight, UUID storedCalculationsId) {
|
||||
// For circles, densities below 0.1 has weird bugs such as blinking in and out of existence, and floating point
|
||||
// errors when calculating the length of circleCoordinates
|
||||
double density = Math.max(1, particleConfig.getParticleDensity());
|
||||
// Store calculations for improved efficiency
|
||||
if (circleCoordinates.get(storedCalculationsId) == null) {
|
||||
Double[][] coordinates = new Double[(int) Math.ceil((180 / density))][];
|
||||
int i = 0;
|
||||
for (float x = 0; x < 180; x += density) {
|
||||
if (i >= coordinates.length) {
|
||||
continue;
|
||||
}
|
||||
coordinates[i++] = new Double[]{(0.5 * Math.sin(x)) + 0.5, (0.5 * Math.cos(x)) + 0.5};
|
||||
}
|
||||
circleCoordinates.put(storedCalculationsId, coordinates);
|
||||
}
|
||||
|
||||
// Spawn particles on the stored locations, relative to the launchpad
|
||||
for (Double[] circleCoordinate : circleCoordinates.get(storedCalculationsId)) {
|
||||
spawnParticle(world, location.clone().add(circleCoordinate[0], particleConfig.getHeightOffset(),
|
||||
circleCoordinate[1]), particleConfig, blockHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns a square of particles at the given location
|
||||
*
|
||||
* <p>It is recommended to only spawn one particle with no spread, and a density between 1 and 0.01</p>
|
||||
*
|
||||
* @param world <p>The world to spawn the particles in</p>
|
||||
* @param location <p>The location of the block to spawn the particles at</p>
|
||||
* @param particleConfig <p>The configuration describing the particle to spawn</p>
|
||||
* @param blockHeight <p>The height of the block to spawn the particle above</p>
|
||||
*/
|
||||
public static void drawSquare(@NotNull World world, @NotNull Location location, @NotNull ParticleConfig particleConfig,
|
||||
double blockHeight) {
|
||||
for (float x = 0; x <= 1; x += particleConfig.getParticleDensity()) {
|
||||
spawnParticle(world, location.clone().add(x, particleConfig.getHeightOffset(), 0), particleConfig, blockHeight);
|
||||
spawnParticle(world, location.clone().add(x, particleConfig.getHeightOffset(), 1), particleConfig, blockHeight);
|
||||
spawnParticle(world, location.clone().add(0, particleConfig.getHeightOffset(), x), particleConfig, blockHeight);
|
||||
spawnParticle(world, location.clone().add(1, particleConfig.getHeightOffset(), x), particleConfig, blockHeight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Spawns the specified particle at the given location
|
||||
*
|
||||
* @param world <p>The world to spawn the particle in</p>
|
||||
* @param location <p>The location to spawn the particle at</p>
|
||||
* @param particleConfig <p>The configuration describing the particle to spawn</p>
|
||||
* @param blockHeight <p>The height of the block to spawn the particle above</p>
|
||||
*/
|
||||
public static void spawnParticle(@NotNull World world, @NotNull Location location,
|
||||
@NotNull ParticleConfig particleConfig, double blockHeight) {
|
||||
world.spawnParticle(particleConfig.getParticleType(),
|
||||
location.add(0, blockHeight, 0), particleConfig.getParticleAmount(),
|
||||
particleConfig.getOffsetX(), particleConfig.getOffsetY(), particleConfig.getOffsetZ(),
|
||||
particleConfig.getExtra());
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the height of the block at the given location
|
||||
*
|
||||
* @param block <p>The block to check</p>
|
||||
* @return <p>The height of the block</p>
|
||||
*/
|
||||
public static double getBlockHeight(Block block) {
|
||||
double maxY = 0;
|
||||
for (BoundingBox boundingBox : block.getCollisionShape().getBoundingBoxes()) {
|
||||
if (boundingBox.getMaxY() > maxY) {
|
||||
maxY = boundingBox.getMaxY();
|
||||
}
|
||||
}
|
||||
return maxY;
|
||||
}
|
||||
|
||||
}
|
Loading…
Reference in New Issue
Block a user