Skip to content
◆ TTS-STUDIO docs Discord

Plugins / ParticleForge

Developer API

Plugins can play any ParticleForge effect, or build one in code. Add softdepend: [ParticleForge] to your plugin.yml.

Fluent API

import com.ttsstudio.particleforge.api.Effects;
import com.ttsstudio.particleforge.api.EffectHandle;

EffectHandle h = Effects.burst()
    .at(player.getLocation())
    .particle("FLAME")
    .count(64)
    .speed(0.7)
    .duration(10)
    .play();

// Later
if (h.isActive()) h.cancel();

For pre-registered effects (loaded from YAML), use Effects.named(...):

Effects.named("crate/legendary-win")
    .at(crateLocation)
    .param("color", "#ffaa00")
    .play();

Turn and move an effect

Effects are built pointing up. Point one along an axis, tilt and turn it by degrees, or shift it by blocks, on either builder:

Effects.named("teleport/portal-arcano")
    .at(wallLocation)
    .axis("+z")              // +x -x +y -y +z -z, or east/west/up/down/south/north
    .offset(0, 1, 0)         // blocks along the world axes, applied after turning
    .spin(30)                // keep turning around the vertical, degrees per second
    .play();

Effects.helix()
    .at(loc)
    .rotate(90, player.getLocation().getYaw())   // pitch, then yaw (Minecraft's yaw)
    .play();

rotate(pitch, yaw) tilts by pitch (90 lays the effect down pointing south, 180 flips it) and then turns by yaw around the vertical, counted like a player's yaw. EffectTransform.facing(playerPitch, playerYaw) builds the rotation that points where a player looks. The service object takes the same thing as a last argument: pf.play(name, at, overrides, viewers, owner, follow, transform). The turn and move apply to every step of the effect, so bundled and hand-written effects behave the same.

Service lookup

When your plugin soft-depends on ParticleForge, fetch the API service from Bukkit:

RegisteredServiceProvider<ParticleForgeApi> rsp =
    Bukkit.getServicesManager().getRegistration(ParticleForgeApi.class);
if (rsp != null) {
    ParticleForgeApi pf = rsp.getProvider();
    pf.play("crate/legendary-win", location);
}

Integration snippet

package your.plugin.integration;

import com.ttsstudio.particleforge.api.EffectHandle;
import com.ttsstudio.particleforge.api.ParticleForgeApi;
import org.bukkit.Bukkit;
import org.bukkit.Location;
import org.bukkit.plugin.RegisteredServiceProvider;
import org.bukkit.plugin.java.JavaPlugin;

public final class ParticleForgeHook {
    private final JavaPlugin owner;
    private volatile ParticleForgeApi api;

    public ParticleForgeHook(JavaPlugin owner) { this.owner = owner; }

    public boolean isAvailable() {
        if (api != null) return true;
        if (!Bukkit.getPluginManager().isPluginEnabled("ParticleForge")) return false;
        RegisteredServiceProvider<ParticleForgeApi> rsp =
            Bukkit.getServicesManager().getRegistration(ParticleForgeApi.class);
        if (rsp == null) return false;
        this.api = rsp.getProvider();
        return api != null;
    }

    public EffectHandle play(String effect, Location at) {
        return isAvailable() ? api.play(effect, at) : null;
    }
}

Don't forget softdepend: [ParticleForge] in your plugin.yml, and gate runtime access with isPluginEnabled("ParticleForge") — not getPlugin() != null — so disabled plugins don't trip you up.

Keep reading