Offer nearly every vanilla particle in the slot builder

Add the remaining particles, including colour, colour transition, block
and item particles, which take their colours or a new block setting from
the entry. Only particles that cover the screen, show only under water or
need a target stay out.
This commit is contained in:
2026-10-26 09:48:36 +01:00
parent 54d30f4a42
commit 6b5441bb04
7 changed files with 296 additions and 45 deletions
@@ -132,6 +132,7 @@ PARTICLE_PARAM_SPREAD_Y = §fSpread Y
PARTICLE_PARAM_SPREAD_Z = §fSpread Z
PARTICLE_PARAM_SPEED = §fSpeed
PARTICLE_PARAM_COLORS = §fColours
PARTICLE_PARAM_BLOCK = §fBlock
PARTICLE_PARAM_OFFSET_X = §fOffset X
PARTICLE_PARAM_OFFSET_Y = §fOffset Y
PARTICLE_PARAM_OFFSET_Z = §fOffset Z
@@ -132,6 +132,7 @@ PARTICLE_PARAM_SPREAD_Y = §fStreuung Y
PARTICLE_PARAM_SPREAD_Z = §fStreuung Z
PARTICLE_PARAM_SPEED = §fGeschwindigkeit
PARTICLE_PARAM_COLORS = §fFarben
PARTICLE_PARAM_BLOCK = §fBlock
PARTICLE_PARAM_OFFSET_X = §fVersatz X
PARTICLE_PARAM_OFFSET_Y = §fVersatz Y
PARTICLE_PARAM_OFFSET_Z = §fVersatz Z
@@ -23,6 +23,7 @@ import de.steamwar.lobby.particle.ParticleElement;
import de.steamwar.lobby.particle.elements.*;
import de.steamwar.lobby.particle.elements.custom.NonMoving;
import lombok.experimental.UtilityClass;
import org.bukkit.Material;
import java.util.List;
import java.util.function.UnaryOperator;
@@ -132,6 +133,52 @@ public class EntryParameters {
public final DoubleParameter SPREAD_Z = new DoubleParameter("spreadZ", "PARTICLE_PARAM_SPREAD_Z", 0, 0, 2, 0.05);
public final DoubleParameter SPEED = new DoubleParameter("speed", "PARTICLE_PARAM_SPEED", 0.01, 0, 1, 0.01);
public final GradientParameter COLORS = new GradientParameter("colors", "PARTICLE_PARAM_COLORS", 8);
// Blocks that are also items, so they work for block and item particles alike
public final ChoiceParameter<Material> BLOCK = new ChoiceParameter<>("block", "PARTICLE_PARAM_BLOCK", Material.STONE, List.of(
new ChoiceParameter.Choice<>("stone", Material.STONE, false),
new ChoiceParameter.Choice<>("cobblestone", Material.COBBLESTONE, false),
new ChoiceParameter.Choice<>("dirt", Material.DIRT, false),
new ChoiceParameter.Choice<>("grass_block", Material.GRASS_BLOCK, false),
new ChoiceParameter.Choice<>("sand", Material.SAND, false),
new ChoiceParameter.Choice<>("red_sand", Material.RED_SAND, false),
new ChoiceParameter.Choice<>("gravel", Material.GRAVEL, false),
new ChoiceParameter.Choice<>("oak_planks", Material.OAK_PLANKS, false),
new ChoiceParameter.Choice<>("oak_leaves", Material.OAK_LEAVES, false),
new ChoiceParameter.Choice<>("cherry_leaves", Material.CHERRY_LEAVES, false),
new ChoiceParameter.Choice<>("snow_block", Material.SNOW_BLOCK, false),
new ChoiceParameter.Choice<>("ice", Material.ICE, false),
new ChoiceParameter.Choice<>("glass", Material.GLASS, false),
new ChoiceParameter.Choice<>("tnt", Material.TNT, false),
new ChoiceParameter.Choice<>("pumpkin", Material.PUMPKIN, false),
new ChoiceParameter.Choice<>("melon", Material.MELON, false),
new ChoiceParameter.Choice<>("honey_block", Material.HONEY_BLOCK, false),
new ChoiceParameter.Choice<>("slime_block", Material.SLIME_BLOCK, false),
new ChoiceParameter.Choice<>("netherrack", Material.NETHERRACK, false),
new ChoiceParameter.Choice<>("soul_sand", Material.SOUL_SAND, false),
new ChoiceParameter.Choice<>("magma_block", Material.MAGMA_BLOCK, false),
new ChoiceParameter.Choice<>("end_stone", Material.END_STONE, false),
new ChoiceParameter.Choice<>("obsidian", Material.OBSIDIAN, false),
new ChoiceParameter.Choice<>("prismarine", Material.PRISMARINE, false),
new ChoiceParameter.Choice<>("sculk", Material.SCULK, false),
new ChoiceParameter.Choice<>("amethyst_block", Material.AMETHYST_BLOCK, false),
new ChoiceParameter.Choice<>("coal_block", Material.COAL_BLOCK, false),
new ChoiceParameter.Choice<>("iron_block", Material.IRON_BLOCK, false),
new ChoiceParameter.Choice<>("gold_block", Material.GOLD_BLOCK, false),
new ChoiceParameter.Choice<>("redstone_block", Material.REDSTONE_BLOCK, false),
new ChoiceParameter.Choice<>("lapis_block", Material.LAPIS_BLOCK, false),
new ChoiceParameter.Choice<>("emerald_block", Material.EMERALD_BLOCK, false),
new ChoiceParameter.Choice<>("diamond_block", Material.DIAMOND_BLOCK, false),
new ChoiceParameter.Choice<>("white_wool", Material.WHITE_WOOL, false),
new ChoiceParameter.Choice<>("red_wool", Material.RED_WOOL, false),
new ChoiceParameter.Choice<>("orange_wool", Material.ORANGE_WOOL, false),
new ChoiceParameter.Choice<>("yellow_wool", Material.YELLOW_WOOL, false),
new ChoiceParameter.Choice<>("lime_wool", Material.LIME_WOOL, false),
new ChoiceParameter.Choice<>("light_blue_wool", Material.LIGHT_BLUE_WOOL, false),
new ChoiceParameter.Choice<>("blue_wool", Material.BLUE_WOOL, false),
new ChoiceParameter.Choice<>("purple_wool", Material.PURPLE_WOOL, false),
new ChoiceParameter.Choice<>("pink_wool", Material.PINK_WOOL, false),
new ChoiceParameter.Choice<>("black_wool", Material.BLACK_WOOL, false)
));
public final DoubleParameter OFFSET_X = new DoubleParameter("offsetX", "PARTICLE_PARAM_OFFSET_X", 0, -3, 3, 0.1);
public final DoubleParameter OFFSET_Y = new DoubleParameter("offsetY", "PARTICLE_PARAM_OFFSET_Y", 0, -3, 3, 0.1);
public final DoubleParameter OFFSET_Z = new DoubleParameter("offsetZ", "PARTICLE_PARAM_OFFSET_Z", 0, -3, 3, 0.1);
@@ -144,7 +191,7 @@ public class EntryParameters {
public final ChoiceParameter<Visibility> VISIBILITY = ChoiceParameter.ofEnum("visibility", "PARTICLE_PARAM_VISIBILITY", Visibility.EVERYONE);
public final List<Parameter<?>> ALL = List.of(
PARTICLE, COUNT, SPREAD_X, SPREAD_Y, SPREAD_Z, SPEED, COLORS,
PARTICLE, COUNT, SPREAD_X, SPREAD_Y, SPREAD_Z, SPEED, COLORS, BLOCK,
OFFSET_X, OFFSET_Y, OFFSET_Z, INTERVAL, TICK_MODE,
MOVEMENT, GLIDING, GROUND, SNEAK, VISIBILITY
);
@@ -24,54 +24,142 @@ import lombok.Getter;
import org.bukkit.Material;
import org.bukkit.Particle;
// Particles that need extra data (BLOCK, ITEM, VIBRATION, DUST_COLOR_TRANSITION) or disturb other players are left out
// Left out: ELDER_GUARDIAN, FLASH and the explosion/gust emitters cover other players' screens,
// BUBBLE, BUBBLE_COLUMN_UP and CURRENT_DOWN only show under water, VIBRATION and TRAIL need a target
@AllArgsConstructor
@Getter
public enum ParticleType {
DUST(Material.REDSTONE, Particle.DUST, true),
FLAME(Material.FLINT_AND_STEEL, Particle.FLAME, true),
SOUL(Material.ZOMBIE_HEAD, Particle.SOUL, true),
SMOKE(Material.COBWEB, Particle.SMOKE, true),
LARGE_SMOKE(Material.GRAY_WOOL, Particle.LARGE_SMOKE, true),
CAMPFIRE_COSY_SMOKE(Material.CAMPFIRE, Particle.CAMPFIRE_COSY_SMOKE, true),
CLOUD(Material.WHITE_WOOL, Particle.CLOUD, true),
SNEEZE(Material.SLIME_BLOCK, Particle.SNEEZE, true),
DRIPPING_LAVA(Material.LAVA_BUCKET, Particle.DRIPPING_LAVA, true),
DRIPPING_WATER(Material.WATER_BUCKET, Particle.DRIPPING_WATER, true),
DRIPPING_HONEY(Material.HONEY_BOTTLE, Particle.DRIPPING_HONEY, true),
FALLING_HONEY(Material.HONEY_BLOCK, Particle.FALLING_HONEY, true),
FALLING_NECTAR(Material.HONEYCOMB, Particle.FALLING_NECTAR, true),
FALLING_SPORE_BLOSSOM(Material.SPORE_BLOSSOM, Particle.FALLING_SPORE_BLOSSOM, true),
FALLING_OBSIDIAN_TEAR(Material.CRYING_OBSIDIAN, Particle.FALLING_OBSIDIAN_TEAR, true),
RAIN(Material.CYAN_DYE, Particle.RAIN, true),
UNDERWATER(Material.PRISMARINE_SHARD, Particle.UNDERWATER, true),
BUBBLE_POP(Material.TUBE_CORAL, Particle.BUBBLE_POP, true),
DOLPHIN(Material.BLUE_DYE, Particle.DOLPHIN, true),
NAUTILUS(Material.NAUTILUS_SHELL, Particle.NAUTILUS, true),
SQUID_INK(Material.INK_SAC, Particle.SQUID_INK, true),
GLOW_SQUID_INK(Material.GLOW_INK_SAC, Particle.GLOW_SQUID_INK, true),
SNOWFLAKE(Material.SNOWBALL, Particle.SNOWFLAKE, true),
ITEM_SNOWBALL(Material.SNOW_BLOCK, Particle.ITEM_SNOWBALL, true),
ITEM_SLIME(Material.SLIME_BALL, Particle.ITEM_SLIME, true),
HEART(Material.RED_DYE, Particle.HEART, true),
NOTE(Material.NOTE_BLOCK, Particle.NOTE, true),
ANGRY_VILLAGER(Material.REDSTONE_BLOCK, Particle.ANGRY_VILLAGER, true),
DAMAGE_INDICATOR(Material.SPIDER_EYE, Particle.DAMAGE_INDICATOR, true),
CRIT(Material.CAULDRON, Particle.CRIT, true),
ENCHANTED_HIT(Material.GLOWSTONE_DUST, Particle.ENCHANTED_HIT, true),
ENCHANT(Material.ENCHANTING_TABLE, Particle.ENCHANT, true),
WITCH(Material.EXPERIENCE_BOTTLE, Particle.WITCH, true),
END_ROD(Material.END_ROD, Particle.END_ROD, true),
DRAGON_BREATH(Material.DRAGON_BREATH, Particle.DRAGON_BREATH, true),
FIREWORK(Material.FIRE_CHARGE, Particle.FIREWORK, true),
TOTEM_OF_UNDYING(Material.TOTEM_OF_UNDYING, Particle.TOTEM_OF_UNDYING, true),
RAID_OMEN(Material.FIREWORK_STAR, Particle.RAID_OMEN, true),
EXPLOSION(Material.TNT, Particle.EXPLOSION, false),
DUST(Material.REDSTONE, Particle.DUST, Data.DUST, true),
DUST_COLOR_TRANSITION(Material.REDSTONE_TORCH, Particle.DUST_COLOR_TRANSITION, Data.COLOR_TRANSITION, true),
ENTITY_EFFECT(Material.BREWING_STAND, Particle.ENTITY_EFFECT, Data.COLOR, true),
TINTED_LEAVES(Material.OAK_LEAVES, Particle.TINTED_LEAVES, Data.COLOR, true),
BLOCK(Material.STONE, Particle.BLOCK, Data.BLOCK, true),
BLOCK_CRUMBLE(Material.CRACKED_STONE_BRICKS, Particle.BLOCK_CRUMBLE, Data.BLOCK, true),
BLOCK_MARKER(Material.GLASS, Particle.BLOCK_MARKER, Data.BLOCK, true),
DUST_PILLAR(Material.MUD, Particle.DUST_PILLAR, Data.BLOCK, true),
FALLING_DUST(Material.SAND, Particle.FALLING_DUST, Data.BLOCK, true),
ITEM(Material.ITEM_FRAME, Particle.ITEM, Data.ITEM, true),
FLAME(Material.FLINT_AND_STEEL, Particle.FLAME, Data.NONE, true),
SMALL_FLAME(Material.TORCH, Particle.SMALL_FLAME, Data.NONE, true),
SOUL_FIRE_FLAME(Material.SOUL_TORCH, Particle.SOUL_FIRE_FLAME, Data.NONE, true),
SOUL(Material.ZOMBIE_HEAD, Particle.SOUL, Data.NONE, true),
LAVA(Material.MAGMA_CREAM, Particle.LAVA, Data.NONE, true),
SMOKE(Material.COBWEB, Particle.SMOKE, Data.NONE, true),
LARGE_SMOKE(Material.GRAY_WOOL, Particle.LARGE_SMOKE, Data.NONE, true),
WHITE_SMOKE(Material.LIGHT_GRAY_DYE, Particle.WHITE_SMOKE, Data.NONE, true),
CAMPFIRE_COSY_SMOKE(Material.CAMPFIRE, Particle.CAMPFIRE_COSY_SMOKE, Data.NONE, true),
CAMPFIRE_SIGNAL_SMOKE(Material.HAY_BLOCK, Particle.CAMPFIRE_SIGNAL_SMOKE, Data.NONE, true),
ASH(Material.BASALT, Particle.ASH, Data.NONE, true),
WHITE_ASH(Material.BONE_MEAL, Particle.WHITE_ASH, Data.NONE, true),
POOF(Material.WHITE_DYE, Particle.POOF, Data.NONE, true),
CLOUD(Material.WHITE_WOOL, Particle.CLOUD, Data.NONE, true),
SNEEZE(Material.SLIME_BLOCK, Particle.SNEEZE, Data.NONE, true),
SPIT(Material.LLAMA_SPAWN_EGG, Particle.SPIT, Data.NONE, true),
EXPLOSION(Material.TNT, Particle.EXPLOSION, Data.NONE, true),
SONIC_BOOM(Material.ECHO_SHARD, Particle.SONIC_BOOM, Data.NONE, true),
SWEEP_ATTACK(Material.IRON_SWORD, Particle.SWEEP_ATTACK, Data.NONE, true),
GUST(Material.WIND_CHARGE, Particle.GUST, Data.NONE, true),
SMALL_GUST(Material.BREEZE_ROD, Particle.SMALL_GUST, Data.NONE, true),
DRIPPING_WATER(Material.WATER_BUCKET, Particle.DRIPPING_WATER, Data.NONE, true),
FALLING_WATER(Material.LIGHT_BLUE_DYE, Particle.FALLING_WATER, Data.NONE, true),
DRIPPING_LAVA(Material.LAVA_BUCKET, Particle.DRIPPING_LAVA, Data.NONE, true),
FALLING_LAVA(Material.ORANGE_DYE, Particle.FALLING_LAVA, Data.NONE, true),
LANDING_LAVA(Material.FIRE_CORAL, Particle.LANDING_LAVA, Data.NONE, true),
DRIPPING_HONEY(Material.HONEY_BOTTLE, Particle.DRIPPING_HONEY, Data.NONE, true),
FALLING_HONEY(Material.HONEY_BLOCK, Particle.FALLING_HONEY, Data.NONE, true),
LANDING_HONEY(Material.HONEYCOMB_BLOCK, Particle.LANDING_HONEY, Data.NONE, true),
FALLING_NECTAR(Material.HONEYCOMB, Particle.FALLING_NECTAR, Data.NONE, true),
DRIPPING_OBSIDIAN_TEAR(Material.OBSIDIAN, Particle.DRIPPING_OBSIDIAN_TEAR, Data.NONE, true),
FALLING_OBSIDIAN_TEAR(Material.CRYING_OBSIDIAN, Particle.FALLING_OBSIDIAN_TEAR, Data.NONE, true),
LANDING_OBSIDIAN_TEAR(Material.RESPAWN_ANCHOR, Particle.LANDING_OBSIDIAN_TEAR, Data.NONE, true),
DRIPPING_DRIPSTONE_WATER(Material.DRIPSTONE_BLOCK, Particle.DRIPPING_DRIPSTONE_WATER, Data.NONE, true),
FALLING_DRIPSTONE_WATER(Material.BLUE_ICE, Particle.FALLING_DRIPSTONE_WATER, Data.NONE, true),
DRIPPING_DRIPSTONE_LAVA(Material.POINTED_DRIPSTONE, Particle.DRIPPING_DRIPSTONE_LAVA, Data.NONE, true),
FALLING_DRIPSTONE_LAVA(Material.MAGMA_BLOCK, Particle.FALLING_DRIPSTONE_LAVA, Data.NONE, true),
RAIN(Material.CYAN_DYE, Particle.RAIN, Data.NONE, true),
SPLASH(Material.KELP, Particle.SPLASH, Data.NONE, true),
FISHING(Material.FISHING_ROD, Particle.FISHING, Data.NONE, true),
UNDERWATER(Material.PRISMARINE_SHARD, Particle.UNDERWATER, Data.NONE, true),
BUBBLE_POP(Material.TUBE_CORAL, Particle.BUBBLE_POP, Data.NONE, true),
DOLPHIN(Material.BLUE_DYE, Particle.DOLPHIN, Data.NONE, true),
NAUTILUS(Material.NAUTILUS_SHELL, Particle.NAUTILUS, Data.NONE, true),
SQUID_INK(Material.INK_SAC, Particle.SQUID_INK, Data.NONE, true),
GLOW_SQUID_INK(Material.GLOW_INK_SAC, Particle.GLOW_SQUID_INK, Data.NONE, true),
GLOW(Material.GLOW_BERRIES, Particle.GLOW, Data.NONE, true),
FALLING_SPORE_BLOSSOM(Material.SPORE_BLOSSOM, Particle.FALLING_SPORE_BLOSSOM, Data.NONE, true),
SPORE_BLOSSOM_AIR(Material.PINK_PETALS, Particle.SPORE_BLOSSOM_AIR, Data.NONE, true),
CHERRY_LEAVES(Material.CHERRY_LEAVES, Particle.CHERRY_LEAVES, Data.NONE, true),
PALE_OAK_LEAVES(Material.PALE_OAK_LEAVES, Particle.PALE_OAK_LEAVES, Data.NONE, true),
CRIMSON_SPORE(Material.CRIMSON_FUNGUS, Particle.CRIMSON_SPORE, Data.NONE, true),
WARPED_SPORE(Material.WARPED_FUNGUS, Particle.WARPED_SPORE, Data.NONE, true),
MYCELIUM(Material.MYCELIUM, Particle.MYCELIUM, Data.NONE, true),
COMPOSTER(Material.COMPOSTER, Particle.COMPOSTER, Data.NONE, true),
FIREFLY(Material.FIREFLY_BUSH, Particle.FIREFLY, Data.NONE, true),
SNOWFLAKE(Material.SNOWBALL, Particle.SNOWFLAKE, Data.NONE, true),
ITEM_SNOWBALL(Material.SNOW_BLOCK, Particle.ITEM_SNOWBALL, Data.NONE, true),
ITEM_SLIME(Material.SLIME_BALL, Particle.ITEM_SLIME, Data.NONE, true),
ITEM_COBWEB(Material.STRING, Particle.ITEM_COBWEB, Data.NONE, true),
EGG_CRACK(Material.TURTLE_EGG, Particle.EGG_CRACK, Data.NONE, true),
DUST_PLUME(Material.SUSPICIOUS_SAND, Particle.DUST_PLUME, Data.NONE, true),
INFESTED(Material.INFESTED_STONE, Particle.INFESTED, Data.NONE, true),
HEART(Material.RED_DYE, Particle.HEART, Data.NONE, true),
NOTE(Material.NOTE_BLOCK, Particle.NOTE, Data.NONE, true),
HAPPY_VILLAGER(Material.EMERALD, Particle.HAPPY_VILLAGER, Data.NONE, true),
ANGRY_VILLAGER(Material.REDSTONE_BLOCK, Particle.ANGRY_VILLAGER, Data.NONE, true),
DAMAGE_INDICATOR(Material.SPIDER_EYE, Particle.DAMAGE_INDICATOR, Data.NONE, true),
CRIT(Material.CAULDRON, Particle.CRIT, Data.NONE, true),
ENCHANTED_HIT(Material.GLOWSTONE_DUST, Particle.ENCHANTED_HIT, Data.NONE, true),
ENCHANT(Material.ENCHANTING_TABLE, Particle.ENCHANT, Data.NONE, true),
EFFECT(Material.POTION, Particle.EFFECT, Data.NONE, true),
INSTANT_EFFECT(Material.SPLASH_POTION, Particle.INSTANT_EFFECT, Data.NONE, true),
WITCH(Material.EXPERIENCE_BOTTLE, Particle.WITCH, Data.NONE, true),
END_ROD(Material.END_ROD, Particle.END_ROD, Data.NONE, true),
PORTAL(Material.ENDER_PEARL, Particle.PORTAL, Data.NONE, true),
REVERSE_PORTAL(Material.ENDER_EYE, Particle.REVERSE_PORTAL, Data.NONE, true),
DRAGON_BREATH(Material.DRAGON_BREATH, Particle.DRAGON_BREATH, Data.NONE, true),
FIREWORK(Material.FIRE_CHARGE, Particle.FIREWORK, Data.NONE, true),
TOTEM_OF_UNDYING(Material.TOTEM_OF_UNDYING, Particle.TOTEM_OF_UNDYING, Data.NONE, true),
ELECTRIC_SPARK(Material.LIGHTNING_ROD, Particle.ELECTRIC_SPARK, Data.NONE, true),
SCRAPE(Material.COPPER_BLOCK, Particle.SCRAPE, Data.NONE, true),
WAX_ON(Material.WAXED_COPPER_BLOCK, Particle.WAX_ON, Data.NONE, true),
WAX_OFF(Material.WAXED_EXPOSED_COPPER, Particle.WAX_OFF, Data.NONE, true),
SCULK_SOUL(Material.SCULK_CATALYST, Particle.SCULK_SOUL, Data.NONE, true),
SCULK_CHARGE(Material.SCULK_SENSOR, Particle.SCULK_CHARGE, Data.ZERO_FLOAT, true),
SCULK_CHARGE_POP(Material.SCULK_VEIN, Particle.SCULK_CHARGE_POP, Data.NONE, true),
SHRIEK(Material.SCULK_SHRIEKER, Particle.SHRIEK, Data.ZERO_INT, true),
RAID_OMEN(Material.FIREWORK_STAR, Particle.RAID_OMEN, Data.NONE, true),
TRIAL_OMEN(Material.TRIAL_KEY, Particle.TRIAL_OMEN, Data.NONE, true),
OMINOUS_SPAWNING(Material.OMINOUS_TRIAL_KEY, Particle.OMINOUS_SPAWNING, Data.NONE, true),
TRIAL_SPAWNER_DETECTION(Material.TRIAL_SPAWNER, Particle.TRIAL_SPAWNER_DETECTION, Data.NONE, true),
TRIAL_SPAWNER_DETECTION_OMINOUS(Material.VAULT, Particle.TRIAL_SPAWNER_DETECTION_OMINOUS, Data.NONE, true),
VAULT_CONNECTION(Material.HEAVY_CORE, Particle.VAULT_CONNECTION, Data.NONE, true),
;
// The extra data a particle needs when it is spawned
public enum Data {
NONE,
// Colour and size, handled by the dust element
DUST,
// A colour from the entry's colours
COLOR,
// Fades between the entry's first two colours
COLOR_TRANSITION,
// Block data of the entry's block
BLOCK,
// An item of the entry's block
ITEM,
ZERO_INT,
ZERO_FLOAT,
}
private final Material icon;
private final Particle particle;
private final Data data;
private final boolean buildable;
public String getNameKey() {
@@ -79,6 +167,14 @@ public enum ParticleType {
}
public boolean isDust() {
return particle == Particle.DUST;
return data == Data.DUST;
}
public boolean usesColors() {
return data == Data.DUST || data == Data.COLOR || data == Data.COLOR_TRANSITION;
}
public boolean usesBlock() {
return data == Data.BLOCK || data == Data.ITEM;
}
}
@@ -0,0 +1,67 @@
/*
* This file is a part of the SteamWar software.
*
* Copyright (C) 2026 SteamWar.de-Serverteam
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Affero General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU Affero General Public License for more details.
*
* You should have received a copy of the GNU Affero General Public License
* along with this program. If not, see <https://www.gnu.org/licenses/>.
*/
package de.steamwar.lobby.particle.elements;
import de.steamwar.lobby.particle.Gradient;
import de.steamwar.lobby.particle.ParticleElement;
import de.steamwar.lobby.particle.ParticleTickData;
import org.bukkit.Color;
import org.bukkit.Location;
import org.bukkit.Particle;
import java.util.function.Function;
// A particle that needs extra data, computed per tick so colours can follow a gradient
public class DataParticle implements ParticleElement {
private final Particle particle;
private final float vx;
private final float vy;
private final float vz;
private final double speed;
private final int count;
private final Function<ParticleTickData, Object> data;
public DataParticle(Particle particle, float vx, float vy, float vz, double speed, int count, Function<ParticleTickData, Object> data) {
this.particle = particle;
this.vx = vx;
this.vy = vy;
this.vz = vz;
this.speed = speed;
this.count = count;
this.data = data;
}
// The gradient's colour at this tick, the same way dust particles pick it; a random colour without gradient
public static Color color(Gradient gradient, ParticleTickData particleTickData) {
if (gradient == null) {
return Color.fromRGB(RANDOM.nextInt(256), RANDOM.nextInt(256), RANDOM.nextInt(256));
}
java.awt.Color color = gradient.getColor((particleTickData.getDeg() * 10) % 360, 360);
return Color.fromRGB(color.getRed(), color.getGreen(), color.getBlue());
}
@Override
public void tick(ParticleTickData particleTickData) {
Location location = particleTickData.getLocation().add(0.0, 0.2, 0.0);
Object value = data.apply(particleTickData);
display(particleTickData, player -> player.spawnParticle(particle, location, count, vx, vy, vz, speed, value));
}
}
@@ -109,8 +109,11 @@ class EntryEditor {
clickType -> Pickers.design(player, design, picked -> changeDesign(context, picked), context::reopen)));
if (design.usesEntryParticle()) {
inventory.setItem(ROW_DESIGN * 9 + 2, parameterItem(context, PARTICLE));
if (entry.getValues().get(PARTICLE).isDust()) {
ParticleType type = entry.getValues().get(PARTICLE);
if (type.usesColors()) {
inventory.setItem(ROW_DESIGN * 9 + 3, parameterItem(context, COLORS));
} else if (type.usesBlock()) {
inventory.setItem(ROW_DESIGN * 9 + 3, parameterItem(context, BLOCK));
}
row(inventory, context, ROW_PARTICLE, List.of(COUNT, SPEED, SPREAD_X, SPREAD_Y, SPREAD_Z));
} else {
@@ -159,6 +162,7 @@ class EntryEditor {
if (parameter instanceof BooleanParameter) icon = (Boolean) value ? Material.LIME_DYE : Material.GRAY_DYE;
if (parameter == PARTICLE) icon = ((ParticleType) value).getIcon();
if (parameter == COLORS) icon = Material.RED_DYE;
if (parameter == BLOCK) icon = (Material) value;
return new SWItem(icon, name(parameter.getNameKey(), player), lore, false, clickType -> {
if (parameter instanceof BooleanParameter toggle) {
@@ -191,6 +195,7 @@ class EntryEditor {
private Material choiceIcon(Object value) {
if (value instanceof ParticleType type) return type.getIcon();
if (value instanceof WingDesign) return Material.PHANTOM_MEMBRANE;
if (value instanceof Material material) return material;
return Material.LEVER;
}
@@ -23,13 +23,19 @@ import com.google.gson.JsonElement;
import com.google.gson.JsonObject;
import de.steamwar.lobby.particle.Gradient;
import de.steamwar.lobby.particle.ParticleElement;
import de.steamwar.lobby.particle.ParticleTickData;
import de.steamwar.lobby.particle.design.*;
import de.steamwar.lobby.particle.elements.*;
import lombok.Getter;
import org.bukkit.Material;
import org.bukkit.Particle;
import org.bukkit.block.data.BlockData;
import org.bukkit.inventory.ItemStack;
import java.awt.Color;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Function;
import static de.steamwar.lobby.particle.design.EntryParameters.*;
@@ -97,14 +103,42 @@ public class ParticleEntry {
double speed = values.get(SPEED);
int count = values.get(COUNT);
List<Color> colors = values.get(COLORS);
Gradient gradient = colors.isEmpty() ? null : new Gradient(colors.toArray(new Color[0]));
Material block = values.get(BLOCK);
Function<ParticleTickData, Object> data = switch (type.getData()) {
// Dust keeps its own element below, so presets look exactly as before
case NONE, DUST -> null;
case COLOR -> tick -> DataParticle.color(gradient, tick);
case COLOR_TRANSITION -> tick -> new Particle.DustTransition(
colors.isEmpty() ? DataParticle.color(null, tick) : bukkitColor(colors.get(0)),
colors.size() < 2 ? DataParticle.color(null, tick) : bukkitColor(colors.get(1)),
1);
case BLOCK -> {
BlockData blockData = block.createBlockData();
yield tick -> blockData;
}
case ITEM -> {
ItemStack item = new ItemStack(block);
yield tick -> item;
}
case ZERO_INT -> tick -> 0;
case ZERO_FLOAT -> tick -> 0f;
};
if (type.isDust()) {
List<Color> colors = values.get(COLORS);
Gradient gradient = colors.isEmpty() ? null : new Gradient(colors.toArray(new Color[0]));
return new DustParticle(type.getParticle(), spreadX, spreadY, spreadZ, speed, count, gradient);
}
if (data != null) {
return new DataParticle(type.getParticle(), spreadX, spreadY, spreadZ, speed, count, data);
}
return new SimpleParticle(type.getParticle(), spreadX, spreadY, spreadZ, speed, count);
}
private static org.bukkit.Color bukkitColor(Color color) {
return org.bukkit.Color.fromRGB(color.getRed(), color.getGreen(), color.getBlue());
}
private ParticleElement withOffset(ParticleElement element) {
double offsetX = values.get(OFFSET_X);
double offsetY = values.get(OFFSET_Y);